Completed
Pull Request — master (#647)
by Robbie
02:01
created

EmailRecipient::getRulesConfig()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 28
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 28
rs 8.8571
c 0
b 0
f 0
cc 1
eloc 18
nc 1
nop 0
1
<?php
2
3
namespace SilverStripe\UserForms\Model\Recipient;
4
5
use SilverStripe\Assets\FileFinder;
6
use SilverStripe\CMS\Controllers\CMSMain;
7
use SilverStripe\CMS\Controllers\CMSPageEditController;
8
use SilverStripe\Control\Controller;
9
use SilverStripe\Control\Email\Email;
10
use SilverStripe\Control\Session;
11
use SilverStripe\Core\Manifest\ModuleLoader;
12
use SilverStripe\Forms\CheckboxField;
13
use SilverStripe\Forms\DropdownField;
14
use SilverStripe\Forms\FieldGroup;
15
use SilverStripe\Forms\FieldList;
16
use SilverStripe\Forms\Form;
17
use SilverStripe\Forms\GridField\GridField;
18
use SilverStripe\Forms\GridField\GridFieldButtonRow;
19
use SilverStripe\Forms\GridField\GridFieldConfig;
20
use SilverStripe\Forms\GridField\GridFieldDeleteAction;
21
use SilverStripe\Forms\GridField\GridFieldToolbarHeader;
22
use SilverStripe\Forms\HTMLEditor\HTMLEditorField;
23
use SilverStripe\Forms\LiteralField;
24
use SilverStripe\Forms\TabSet;
25
use SilverStripe\Forms\TextareaField;
26
use SilverStripe\Forms\TextField;
27
use SilverStripe\ORM\ArrayList;
28
use SilverStripe\ORM\DataObject;
29
use SilverStripe\UserForms\Model\EditableFormField\EditableEmailField;
30
use SilverStripe\UserForms\Model\EditableFormField;
31
use SilverStripe\UserForms\Model\EditableFormField\EditableMultipleOptionField;
32
use SilverStripe\UserForms\Model\EditableFormField\EditableTextField;
33
use SilverStripe\UserForms\Model\Recipient\EmailRecipientCondition;
34
use SilverStripe\UserForms\Model\UserDefinedForm;
35
use SilverStripe\View\Requirements;
36
use Symbiote\GridFieldExtensions\GridFieldAddNewInlineButton;
37
use Symbiote\GridFieldExtensions\GridFieldEditableColumns;
38
39
/**
40
 * A Form can have multiply members / emails to email the submission
41
 * to and custom subjects
42
 *
43
 * @package userforms
44
 */
45
class EmailRecipient extends DataObject
46
{
47
    private static $db = [
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $db is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
48
        'EmailAddress' => 'Varchar(200)',
49
        'EmailSubject' => 'Varchar(200)',
50
        'EmailFrom' => 'Varchar(200)',
51
        'EmailReplyTo' => 'Varchar(200)',
52
        'EmailBody' => 'Text',
53
        'EmailBodyHtml' => 'HTMLText',
54
        'EmailTemplate' => 'Varchar',
55
        'SendPlain' => 'Boolean',
56
        'HideFormData' => 'Boolean',
57
        'CustomRulesCondition' => 'Enum("And,Or")'
58
    ];
59
60
    private static $has_one = [
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $has_one is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
61
        'Form' => UserDefinedForm::class,
62
        'SendEmailFromField' => EditableFormField::class,
63
        'SendEmailToField' => EditableFormField::class,
64
        'SendEmailSubjectField' => EditableFormField::class
65
    ];
66
67
    private static $has_many = [
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $has_many is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
68
        'CustomRules' => EmailRecipientCondition::class,
69
    ];
70
71
    private static $owns = [
0 ignored issues
show
Unused Code introduced by
The property $owns is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
72
        'CustomRules',
73
    ];
74
75
    private static $cascade_deetes = [
0 ignored issues
show
Unused Code introduced by
The property $cascade_deetes is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
76
        'CustomRules',
77
    ];
78
79
    private static $summary_fields = [
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $summary_fields is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
80
        'EmailAddress',
81
        'EmailSubject',
82
        'EmailFrom'
83
    ];
84
85
    private static $table_name = 'UserDefinedForm_EmailRecipient';
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $table_name is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
86
87
    /**
88
     * Setting this to true will allow you to select "risky" fields as
89
     * email recipient, such as free-text entry fields.
90
     *
91
     * It's advisable to leave this off.
92
     *
93
     * @config
94
     * @var bool
95
     */
96
    private static $allow_unbound_recipient_fields = false;
0 ignored issues
show
Unused Code introduced by
The property $allow_unbound_recipient_fields is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
97
98
    public function summaryFields()
99
    {
100
        $fields = parent::summaryFields();
101
        if (isset($fields['EmailAddress'])) {
102
            $fields['EmailAddress'] = _t('SilverStripe\\UserForms\\Model\\UserDefinedForm.EMAILADDRESS', Email::class);
103
        }
104
        if (isset($fields['EmailSubject'])) {
105
            $fields['EmailSubject'] = _t('SilverStripe\\UserForms\\Model\\UserDefinedForm.EMAILSUBJECT', 'Subject');
106
        }
107
        if (isset($fields['EmailFrom'])) {
108
            $fields['EmailFrom'] = _t('SilverStripe\\UserForms\\Model\\UserDefinedForm.EMAILFROM', 'From');
109
        }
110
        return $fields;
111
    }
112
113
    /**
114
     * Get instance of UserDefinedForm when editing in getCMSFields
115
     *
116
     * @return UserDefinedFrom
117
     */
118
    protected function getFormParent()
119
    {
120
        $formID = $this->FormID
0 ignored issues
show
Documentation introduced by
The property FormID does not exist on object<SilverStripe\User...cipient\EmailRecipient>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
121
            ? $this->FormID
0 ignored issues
show
Documentation introduced by
The property FormID does not exist on object<SilverStripe\User...cipient\EmailRecipient>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
122
            : Session::get('CMSMain.currentPage');
123
        return UserDefinedForm::get()->byID($formID);
124
    }
125
126
    public function getTitle()
127
    {
128
        if ($this->EmailAddress) {
0 ignored issues
show
Documentation introduced by
The property EmailAddress does not exist on object<SilverStripe\User...cipient\EmailRecipient>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
129
            return $this->EmailAddress;
0 ignored issues
show
Documentation introduced by
The property EmailAddress does not exist on object<SilverStripe\User...cipient\EmailRecipient>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
130
        }
131
        if ($this->EmailSubject) {
0 ignored issues
show
Documentation introduced by
The property EmailSubject does not exist on object<SilverStripe\User...cipient\EmailRecipient>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
132
            return $this->EmailSubject;
0 ignored issues
show
Documentation introduced by
The property EmailSubject does not exist on object<SilverStripe\User...cipient\EmailRecipient>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
133
        }
134
        return parent::getTitle();
135
    }
136
137
    /**
138
     * Generate a gridfield config for editing filter rules
139
     *
140
     * @return GridFieldConfig
141
     */
142
    protected function getRulesConfig()
143
    {
144
        $formFields = $this->getFormParent()->Fields();
145
146
        $config = GridFieldConfig::create()
147
            ->addComponents(
148
                new GridFieldButtonRow('before'),
149
                new GridFieldToolbarHeader(),
150
                new GridFieldAddNewInlineButton(),
151
                new GridFieldDeleteAction(),
152
                $columns = new GridFieldEditableColumns()
153
            );
154
155
        $columns->setDisplayFields(array(
156
            'ConditionFieldID' => function ($record, $column, $grid) use ($formFields) {
0 ignored issues
show
Unused Code introduced by
The parameter $grid is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
157
                return DropdownField::create($column, false, $formFields->map('ID', 'Title'));
158
            },
159
            'ConditionOption' => function ($record, $column, $grid) {
0 ignored issues
show
Unused Code introduced by
The parameter $grid is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
160
                $options = EmailRecipientCondition::config()->condition_options;
161
                return DropdownField::create($column, false, $options);
162
            },
163
            'ConditionValue' => function ($record, $column, $grid) {
0 ignored issues
show
Unused Code introduced by
The parameter $grid is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
164
                return TextField::create($column);
165
            }
166
        ));
167
168
        return $config;
169
    }
170
171
    /**
172
     * @return FieldList
173
     */
174
    public function getCMSFields()
175
    {
176
        Requirements::javascript(
177
            ModuleLoader::getModule('silverstripe/userforms')->getRelativeResourcePath('javascript/Recipient.js')
178
        );
179
180
        // Determine optional field values
181
        $form = $this->getFormParent();
182
183
        // predefined choices are also candidates
184
        $multiOptionFields = EditableMultipleOptionField::get()->filter('ParentID', $form->ID);
185
186
        // if they have email fields then we could send from it
187
        $validEmailFromFields = EditableEmailField::get()->filter('ParentID', $form->ID);
188
189
        // For the subject, only one-line entry boxes make sense
190
        $validSubjectFields = ArrayList::create(
191
            EditableTextField::get()
192
                ->filter('ParentID', $form->ID)
193
                ->exclude('Rows:GreaterThan', 1)
194
                ->toArray()
195
        );
196
        $validSubjectFields->merge($multiOptionFields);
197
198
199
        // Check valid email-recipient fields
200
        if ($this->config()->get('allow_unbound_recipient_fields')) {
201
            // To address can only be email fields or multi option fields
202
            $validEmailToFields = ArrayList::create($validEmailFromFields->toArray());
203
            $validEmailToFields->merge($multiOptionFields);
204
        } else {
205
            // To address cannot be unbound, so restrict to pre-defined lists
206
            $validEmailToFields = $multiOptionFields;
207
        }
208
209
        // Build fieldlist
210
        $fields = FieldList::create(Tabset::create('Root')->addExtraClass('EmailRecipientForm'));
211
212
        // Configuration fields
213
        $fields->addFieldsToTab('Root.EmailDetails', [
214
            // Subject
215
            FieldGroup::create(
216
                TextField::create(
217
                    'EmailSubject',
218
                    _t('SilverStripe\\UserForms\\Model\\UserDefinedForm.TYPESUBJECT', 'Type subject')
219
                )
220
                    ->setAttribute('style', 'min-width: 400px;'),
221
                DropdownField::create(
222
                    'SendEmailSubjectFieldID',
223
                    _t(
224
                        'SilverStripe\\UserForms\\Model\\UserDefinedForm.SELECTAFIELDTOSETSUBJECT',
225
                        '.. or select a field to use as the subject'
226
                    ),
227
                    $validSubjectFields->map('ID', 'Title')
228
                )->setEmptyString('')
229
            )
230
                ->setTitle(_t('SilverStripe\\UserForms\\Model\\UserDefinedForm.EMAILSUBJECT', 'Email subject')),
231
232
            // To
233
            FieldGroup::create(
234
                TextField::create(
235
                    'EmailAddress',
236
                    _t('SilverStripe\\UserForms\\Model\\UserDefinedForm.TYPETO', 'Type to address')
237
                )
238
                    ->setAttribute('style', 'min-width: 400px;'),
239
                DropdownField::create(
240
                    'SendEmailToFieldID',
241
                    _t(
242
                        'SilverStripe\\UserForms\\Model\\UserDefinedForm.ORSELECTAFIELDTOUSEASTO',
243
                        '.. or select a field to use as the to address'
244
                    ),
245
                    $validEmailToFields->map('ID', 'Title')
246
                )->setEmptyString(' ')
247
            )
248
                ->setTitle(_t('SilverStripe\\UserForms\\Model\\UserDefinedForm.SENDEMAILTO', 'Send email to'))
249
                ->setDescription(_t(
250
                    'SilverStripe\\UserForms\\Model\\UserDefinedForm.SENDEMAILTO_DESCRIPTION',
251
                    'You may enter multiple email addresses as a comma separated list.'
252
                )),
253
254
255
            // From
256
            TextField::create(
257
                'EmailFrom',
258
                _t('SilverStripe\\UserForms\\Model\\UserDefinedForm.FROMADDRESS', 'Send email from')
259
            )
260
                ->setDescription(_t(
261
                    'SilverStripe\\UserForms\\Model\\UserDefinedForm.EmailFromContent',
262
                    "The from address allows you to set who the email comes from. On most servers this ".
263
                    "will need to be set to an email address on the same domain name as your site. ".
264
                    "For example on yoursite.com the from address may need to be [email protected]. ".
265
                    "You can however, set any email address you wish as the reply to address."
266
                )),
267
268
269
            // Reply-To
270
            FieldGroup::create(
271
                TextField::create('EmailReplyTo', _t(
272
                    'SilverStripe\\UserForms\\Model\\UserDefinedForm.TYPEREPLY',
273
                    'Type reply address'
274
                ))
275
                    ->setAttribute('style', 'min-width: 400px;'),
276
                DropdownField::create(
277
                    'SendEmailFromFieldID',
278
                    _t(
279
                        'SilverStripe\\UserForms\\Model\\UserDefinedForm.ORSELECTAFIELDTOUSEASFROM',
280
                        '.. or select a field to use as reply to address'
281
                    ),
282
                    $validEmailFromFields->map('ID', 'Title')
283
                )->setEmptyString(' ')
284
            )
285
                ->setTitle(_t(
286
                    'SilverStripe\\UserForms\\Model\\UserDefinedForm.REPLYADDRESS',
287
                    'Email for reply to'
288
                ))
289
                ->setDescription(_t(
290
                    'SilverStripe\\UserForms\\Model\\UserDefinedForm.REPLYADDRESS_DESCRIPTION',
291
                    'The email address which the recipient is able to \'reply\' to.'
292
                ))
293
        ]);
294
295
        $fields->fieldByName('Root.EmailDetails')->setTitle(_t(__CLASS__.'.EMAILDETAILSTAB', 'Email Details'));
296
297
        // Only show the preview link if the recipient has been saved.
298
        if (!empty($this->EmailTemplate)) {
0 ignored issues
show
Documentation introduced by
The property EmailTemplate does not exist on object<SilverStripe\User...cipient\EmailRecipient>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
299
            $preview = sprintf(
300
                '<p><a href="%s" target="_blank" class="ss-ui-button">%s</a></p><em>%s</em>',
301
                Controller::join_links(
302
                    singleton(CMSPageEditController::class)->getEditForm()->FormAction(),
303
                    "field/EmailRecipients/item/{$this->ID}/preview"
304
                ),
305
                _t('SilverStripe\\UserForms\\Model\\UserDefinedForm.PREVIEW_EMAIL', 'Preview email'),
306
                _t(
307
                    'SilverStripe\\UserForms\\Model\\UserDefinedForm.PREVIEW_EMAIL_DESCRIPTION',
308
                    'Note: Unsaved changes will not appear in the preview.'
309
                )
310
            );
311
        } else {
312
            $preview = sprintf(
313
                '<em>%s</em>',
314
                _t(
315
                    'SilverStripe\\UserForms\\Model\\UserDefinedForm.PREVIEW_EMAIL_UNAVAILABLE',
316
                    'You can preview this email once you have saved the Recipient.'
317
                )
318
            );
319
        }
320
321
        // Email templates
322
        $fields->addFieldsToTab('Root.EmailContent', [
323
            CheckboxField::create(
324
                'HideFormData',
325
                _t('SilverStripe\\UserForms\\Model\\UserDefinedForm.HIDEFORMDATA', 'Hide form data from email?')
326
            ),
327
            CheckboxField::create(
328
                'SendPlain',
329
                _t(
330
                    'SilverStripe\\UserForms\\Model\\UserDefinedForm.SENDPLAIN',
331
                    'Send email as plain text? (HTML will be stripped)'
332
                )
333
            ),
334
            DropdownField::create(
335
                'EmailTemplate',
336
                _t('SilverStripe\\UserForms\\Model\\UserDefinedForm.EMAILTEMPLATE', 'Email template'),
337
                $this->getEmailTemplateDropdownValues()
338
            )->addExtraClass('toggle-html-only'),
339
            HTMLEditorField::create(
340
                'EmailBodyHtml',
341
                _t('SilverStripe\\UserForms\\Model\\UserDefinedForm.EMAILBODYHTML', 'Body')
342
            )
343
                ->addExtraClass('toggle-html-only'),
344
            TextareaField::create(
345
                'EmailBody',
346
                _t('SilverStripe\\UserForms\\Model\\UserDefinedForm.EMAILBODY', 'Body')
347
            )
348
                ->addExtraClass('toggle-plain-only'),
349
            LiteralField::create(
350
                'EmailPreview',
351
                '<div id="EmailPreview" class="field toggle-html-only">' . $preview . '</div>'
352
            )
353
        ]);
354
355
        $fields->fieldByName('Root.EmailContent')->setTitle(_t(__CLASS__.'.EMAILCONTENTTAB', 'Email Content'));
356
357
        // Custom rules for sending this field
358
        $grid = GridField::create(
359
            'CustomRules',
360
            _t('SilverStripe\\UserForms\\Model\\EditableFormField.CUSTOMRULES', 'Custom Rules'),
361
            $this->CustomRules(),
0 ignored issues
show
Documentation Bug introduced by
The method CustomRules does not exist on object<SilverStripe\User...cipient\EmailRecipient>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
362
            $this->getRulesConfig()
363
        );
364
        $grid->setDescription(_t(
365
            'SilverStripe\\UserForms\\Model\\UserDefinedForm.RulesDescription',
366
            'Emails will only be sent to the recipient if the custom rules are met. If no rules are defined, this receipient will receive notifications for every submission.'
367
        ));
368
        $fields->addFieldsToTab('Root.CustomRules', [
369
            DropdownField::create(
370
                'CustomRulesCondition',
371
                _t('SilverStripe\\UserForms\\Model\\UserDefinedForm.SENDIF', 'Send condition'),
372
                [
373
                    'Or' => _t('SilverStripe\\UserForms\\Model\\UserDefinedForm.SENDIFOR', 'Any conditions are true'),
374
                    'And' => _t('SilverStripe\\UserForms\\Model\\UserDefinedForm.SENDIFAND', 'All conditions are true')
375
                ]
376
            ),
377
            $grid
378
        ]);
379
380
        $fields->fieldByName('Root.CustomRules')->setTitle(_t(__CLASS__.'.CUSTOMRULESTAB', 'Custom Rules'));
381
382
        $this->extend('updateCMSFields', $fields);
383
        return $fields;
384
    }
385
386
    /**
387
     * Return whether a user can create an object of this type
388
     *
389
     * @param Member $member
390
     * @param array $context Virtual parameter to allow context to be passed in to check
391
     * @return bool
392
     */
393 View Code Duplication
    public function canCreate($member = null, $context = [])
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
394
    {
395
        // Check parent page
396
        $parent = $this->getCanCreateContext(func_get_args());
397
        if ($parent) {
398
            return $parent->canEdit($member);
399
        }
400
401
        // Fall back to secure admin permissions
402
        return parent::canCreate($member);
0 ignored issues
show
Bug Compatibility introduced by
The expression parent::canCreate($member); of type boolean|string adds the type string to the return on line 402 which is incompatible with the return type documented by SilverStripe\UserForms\M...ailRecipient::canCreate of type boolean.
Loading history...
403
    }
404
405
    /**
406
     * Helper method to check the parent for this object
407
     *
408
     * @param array $args List of arguments passed to canCreate
409
     * @return SiteTree Parent page instance
410
     */
411
    protected function getCanCreateContext($args)
412
    {
413
        // Inspect second parameter to canCreate for a 'Parent' context
414
        if (isset($args[1][Form::class])) {
415
            return $args[1][Form::class];
416
        }
417
        // Hack in currently edited page if context is missing
418
        if (Controller::has_curr() && Controller::curr() instanceof CMSMain) {
419
            return Controller::curr()->currentPage();
420
        }
421
422
        // No page being edited
423
        return null;
424
    }
425
426
    /**
427
     * @param Member
428
     *
429
     * @return boolean
430
     */
431
    public function canView($member = null)
432
    {
433
        return $this->Form()->canView($member);
0 ignored issues
show
Documentation Bug introduced by
The method Form does not exist on object<SilverStripe\User...cipient\EmailRecipient>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
434
    }
435
436
    /**
437
     * @param Member
438
     *
439
     * @return boolean
440
     */
441
    public function canEdit($member = null)
442
    {
443
        return $this->Form()->canEdit($member);
0 ignored issues
show
Documentation Bug introduced by
The method Form does not exist on object<SilverStripe\User...cipient\EmailRecipient>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
444
    }
445
446
    /**
447
     * @param Member
448
     *
449
     * @return boolean
450
     */
451
    public function canDelete($member = null)
452
    {
453
        return $this->canEdit($member);
454
    }
455
456
    /*
457
     * Determine if this recipient may receive notifications for this submission
458
     *
459
     * @param array $data
460
     * @param Form $form
461
     * @return bool
462
     */
463
    public function canSend($data, $form)
0 ignored issues
show
Unused Code introduced by
The parameter $form is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
464
    {
465
        // Skip if no rules configured
466
        $customRules = $this->CustomRules();
0 ignored issues
show
Documentation Bug introduced by
The method CustomRules does not exist on object<SilverStripe\User...cipient\EmailRecipient>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
467
        if (!$customRules->count()) {
468
            return true;
469
        }
470
471
        // Check all rules
472
        $isAnd = $this->CustomRulesCondition === 'And';
0 ignored issues
show
Documentation introduced by
The property CustomRulesCondition does not exist on object<SilverStripe\User...cipient\EmailRecipient>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
473
        foreach ($customRules as $customRule) {
474
            /** @var EmailRecipientCondition  $customRule */
475
            $matches = $customRule->matches($data);
476
            if ($isAnd && !$matches) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $matches of type boolean|null is loosely compared to false; this is ambiguous if the boolean can be false. You might want to explicitly use !== null instead.

If an expression can have both false, and null as possible values. It is generally a good practice to always use strict comparison to clearly distinguish between those two values.

$a = canBeFalseAndNull();

// Instead of
if ( ! $a) { }

// Better use one of the explicit versions:
if ($a !== null) { }
if ($a !== false) { }
if ($a !== null && $a !== false) { }
Loading history...
477
                return false;
478
            }
479
            if (!$isAnd && $matches) {
480
                return true;
481
            }
482
        }
483
484
        // Once all rules are checked
485
        return $isAnd;
486
    }
487
488
    /**
489
     * Make sure the email template saved against the recipient exists on the file system.
490
     *
491
     * @param string
492
     *
493
     * @return boolean
494
     */
495
    public function emailTemplateExists($template = '')
496
    {
497
        $t = ($template ? $template : $this->EmailTemplate);
0 ignored issues
show
Documentation introduced by
The property EmailTemplate does not exist on object<SilverStripe\User...cipient\EmailRecipient>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
498
499
        return in_array($t, $this->getEmailTemplateDropdownValues());
500
    }
501
502
    /**
503
     * Get the email body for the current email format
504
     *
505
     * @return string
506
     */
507
    public function getEmailBodyContent()
508
    {
509
        return $this->SendPlain ? $this->EmailBody : $this->EmailBodyHtml;
0 ignored issues
show
Documentation introduced by
The property SendPlain does not exist on object<SilverStripe\User...cipient\EmailRecipient>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
Documentation introduced by
The property EmailBody does not exist on object<SilverStripe\User...cipient\EmailRecipient>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
Documentation introduced by
The property EmailBodyHtml does not exist on object<SilverStripe\User...cipient\EmailRecipient>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
510
    }
511
512
    /**
513
     * Gets a list of email templates suitable for populating the email template dropdown.
514
     *
515
     * @return array
516
     */
517
    public function getEmailTemplateDropdownValues()
518
    {
519
        $templates = [];
520
521
        $finder = new FileFinder();
522
        $finder->setOption('name_regex', '/^.*\.ss$/');
523
524
        $templateDirectory = UserDefinedForm::config()->get('email_template_directory');
525
        // Handle cases where "userforms" might not be the base module directory, e.g. in a Travis build
526
        if (!file_exists($templateDirectory) && substr($templateDirectory, 0, 10) === 'userforms/') {
527
            $templateDirectory = substr($templateDirectory, 10);
528
        }
529
        $found = $finder->find(BASE_PATH . '/' . $templateDirectory);
530
531
        foreach ($found as $key => $value) {
532
            $template = pathinfo($value);
533
534
            $templates[$template['filename']] = $template['filename'];
535
        }
536
537
        return $templates;
538
    }
539
540
    /**
541
     * Validate that valid email addresses are being used
542
     *
543
     * @return ValidationResult
544
     */
545
    public function validate()
546
    {
547
        $result = parent::validate();
548
        $checkEmail = [
549
            'EmailAddress' => 'EMAILADDRESSINVALID',
550
            'EmailFrom' => 'EMAILFROMINVALID',
551
            'EmailReplyTo' => 'EMAILREPLYTOINVALID',
552
        ];
553
        foreach ($checkEmail as $check => $translation) {
554
            if ($this->$check) {
555
                //may be a comma separated list of emails
556
                $addresses = explode(',', $this->$check);
557
                foreach ($addresses as $address) {
558
                    $trimAddress = trim($address);
559
                    if ($trimAddress && !Email::is_valid_address($trimAddress)) {
560
                        $error = _t(
561
                            __CLASS__.".$translation",
562
                            "Invalid email address $trimAddress"
563
                        );
564
                        $result->addError($error . " ($trimAddress)");
565
                    }
566
                }
567
            }
568
        }
569
        return $result;
570
    }
571
}
572