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

EmailRecipient::canEdit()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 1
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\ORM\FieldType\DBField;
30
use SilverStripe\UserForms\Model\EditableFormField\EditableEmailField;
31
use SilverStripe\UserForms\Model\EditableFormField;
32
use SilverStripe\UserForms\Model\EditableFormField\EditableMultipleOptionField;
33
use SilverStripe\UserForms\Model\EditableFormField\EditableTextField;
34
use SilverStripe\UserForms\Model\Recipient\EmailRecipientCondition;
35
use SilverStripe\UserForms\Model\UserDefinedForm;
36
use SilverStripe\View\Requirements;
37
use Symbiote\GridFieldExtensions\GridFieldAddNewInlineButton;
38
use Symbiote\GridFieldExtensions\GridFieldEditableColumns;
39
40
/**
41
 * A Form can have multiply members / emails to email the submission
42
 * to and custom subjects
43
 *
44
 * @package userforms
45
 */
46
class EmailRecipient extends DataObject
47
{
48
    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...
49
        'EmailAddress' => 'Varchar(200)',
50
        'EmailSubject' => 'Varchar(200)',
51
        'EmailFrom' => 'Varchar(200)',
52
        'EmailReplyTo' => 'Varchar(200)',
53
        'EmailBody' => 'Text',
54
        'EmailBodyHtml' => 'HTMLText',
55
        'EmailTemplate' => 'Varchar',
56
        'SendPlain' => 'Boolean',
57
        'HideFormData' => 'Boolean',
58
        'CustomRulesCondition' => 'Enum("And,Or")'
59
    ];
60
61
    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...
62
        'Form' => UserDefinedForm::class,
63
        'SendEmailFromField' => EditableFormField::class,
64
        'SendEmailToField' => EditableFormField::class,
65
        'SendEmailSubjectField' => EditableFormField::class
66
    ];
67
68
    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...
69
        'CustomRules' => EmailRecipientCondition::class,
70
    ];
71
72
    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...
73
        'CustomRules',
74
    ];
75
76
    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...
77
        'CustomRules',
78
    ];
79
80
    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...
81
        'EmailAddress',
82
        'EmailSubject',
83
        'EmailFrom'
84
    ];
85
86
    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...
87
88
    /**
89
     * Setting this to true will allow you to select "risky" fields as
90
     * email recipient, such as free-text entry fields.
91
     *
92
     * It's advisable to leave this off.
93
     *
94
     * @config
95
     * @var bool
96
     */
97
    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...
98
99
    public function summaryFields()
100
    {
101
        $fields = parent::summaryFields();
102
        if (isset($fields['EmailAddress'])) {
103
            $fields['EmailAddress'] = _t('SilverStripe\\UserForms\\Model\\UserDefinedForm.EMAILADDRESS', Email::class);
104
        }
105
        if (isset($fields['EmailSubject'])) {
106
            $fields['EmailSubject'] = _t('SilverStripe\\UserForms\\Model\\UserDefinedForm.EMAILSUBJECT', 'Subject');
107
        }
108
        if (isset($fields['EmailFrom'])) {
109
            $fields['EmailFrom'] = _t('SilverStripe\\UserForms\\Model\\UserDefinedForm.EMAILFROM', 'From');
110
        }
111
        return $fields;
112
    }
113
114
    /**
115
     * Get instance of UserDefinedForm when editing in getCMSFields
116
     *
117
     * @return UserDefinedFrom
118
     */
119
    protected function getFormParent()
120
    {
121
        $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...
122
            ? $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...
123
            : Session::get('CMSMain.currentPage');
124
        return UserDefinedForm::get()->byID($formID);
125
    }
126
127
    public function getTitle()
128
    {
129
        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...
130
            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...
131
        }
132
        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...
133
            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...
134
        }
135
        return parent::getTitle();
136
    }
137
138
    /**
139
     * Generate a gridfield config for editing filter rules
140
     *
141
     * @return GridFieldConfig
142
     */
143
    protected function getRulesConfig()
144
    {
145
        $formFields = $this->getFormParent()->Fields();
146
147
        $config = GridFieldConfig::create()
148
            ->addComponents(
149
                new GridFieldButtonRow('before'),
150
                new GridFieldToolbarHeader(),
151
                new GridFieldAddNewInlineButton(),
152
                new GridFieldDeleteAction(),
153
                $columns = new GridFieldEditableColumns()
154
            );
155
156
        $columns->setDisplayFields(array(
157
            '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...
158
                return DropdownField::create($column, false, $formFields->map('ID', 'Title'));
159
            },
160
            '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...
161
                $options = EmailRecipientCondition::config()->condition_options;
162
                return DropdownField::create($column, false, $options);
163
            },
164
            '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...
165
                return TextField::create($column);
166
            }
167
        ));
168
169
        return $config;
170
    }
171
172
    /**
173
     * @return FieldList
174
     */
175
    public function getCMSFields()
176
    {
177
        Requirements::javascript(
178
            ModuleLoader::getModule('silverstripe/userforms')->getRelativeResourcePath('javascript/Recipient.js')
179
        );
180
181
        // Determine optional field values
182
        $form = $this->getFormParent();
183
184
        // predefined choices are also candidates
185
        $multiOptionFields = EditableMultipleOptionField::get()->filter('ParentID', $form->ID);
186
187
        // if they have email fields then we could send from it
188
        $validEmailFromFields = EditableEmailField::get()->filter('ParentID', $form->ID);
189
190
        // For the subject, only one-line entry boxes make sense
191
        $validSubjectFields = ArrayList::create(
192
            EditableTextField::get()
193
                ->filter('ParentID', $form->ID)
194
                ->exclude('Rows:GreaterThan', 1)
195
                ->toArray()
196
        );
197
        $validSubjectFields->merge($multiOptionFields);
198
199
200
        // Check valid email-recipient fields
201
        if ($this->config()->get('allow_unbound_recipient_fields')) {
202
            // To address can only be email fields or multi option fields
203
            $validEmailToFields = ArrayList::create($validEmailFromFields->toArray());
204
            $validEmailToFields->merge($multiOptionFields);
205
        } else {
206
            // To address cannot be unbound, so restrict to pre-defined lists
207
            $validEmailToFields = $multiOptionFields;
208
        }
209
210
        // Build fieldlist
211
        $fields = FieldList::create(Tabset::create('Root')->addExtraClass('EmailRecipientForm'));
212
213
        // Configuration fields
214
        $fields->addFieldsToTab('Root.EmailDetails', [
215
            // Subject
216
            FieldGroup::create(
217
                TextField::create(
218
                    'EmailSubject',
219
                    _t('SilverStripe\\UserForms\\Model\\UserDefinedForm.TYPESUBJECT', 'Type subject')
220
                )
221
                    ->setAttribute('style', 'min-width: 400px;'),
222
                DropdownField::create(
223
                    'SendEmailSubjectFieldID',
224
                    _t(
225
                        'SilverStripe\\UserForms\\Model\\UserDefinedForm.SELECTAFIELDTOSETSUBJECT',
226
                        '.. or select a field to use as the subject'
227
                    ),
228
                    $validSubjectFields->map('ID', 'Title')
229
                )->setEmptyString('')
230
            )
231
                ->setTitle(_t('SilverStripe\\UserForms\\Model\\UserDefinedForm.EMAILSUBJECT', 'Email subject')),
232
233
            // To
234
            FieldGroup::create(
235
                TextField::create(
236
                    'EmailAddress',
237
                    _t('SilverStripe\\UserForms\\Model\\UserDefinedForm.TYPETO', 'Type to address')
238
                )
239
                    ->setAttribute('style', 'min-width: 400px;'),
240
                DropdownField::create(
241
                    'SendEmailToFieldID',
242
                    _t(
243
                        'SilverStripe\\UserForms\\Model\\UserDefinedForm.ORSELECTAFIELDTOUSEASTO',
244
                        '.. or select a field to use as the to address'
245
                    ),
246
                    $validEmailToFields->map('ID', 'Title')
247
                )->setEmptyString(' ')
248
            )
249
                ->setTitle(_t('SilverStripe\\UserForms\\Model\\UserDefinedForm.SENDEMAILTO', 'Send email to'))
250
                ->setDescription(_t(
251
                    'SilverStripe\\UserForms\\Model\\UserDefinedForm.SENDEMAILTO_DESCRIPTION',
252
                    'You may enter multiple email addresses as a comma separated list.'
253
                )),
254
255
256
            // From
257
            TextField::create(
258
                'EmailFrom',
259
                _t('SilverStripe\\UserForms\\Model\\UserDefinedForm.FROMADDRESS', 'Send email from')
260
            )
261
                ->setDescription(_t(
262
                    'SilverStripe\\UserForms\\Model\\UserDefinedForm.EmailFromContent',
263
                    "The from address allows you to set who the email comes from. On most servers this ".
264
                    "will need to be set to an email address on the same domain name as your site. ".
265
                    "For example on yoursite.com the from address may need to be [email protected]. ".
266
                    "You can however, set any email address you wish as the reply to address."
267
                )),
268
269
270
            // Reply-To
271
            FieldGroup::create(
272
                TextField::create('EmailReplyTo', _t(
273
                    'SilverStripe\\UserForms\\Model\\UserDefinedForm.TYPEREPLY',
274
                    'Type reply address'
275
                ))
276
                    ->setAttribute('style', 'min-width: 400px;'),
277
                DropdownField::create(
278
                    'SendEmailFromFieldID',
279
                    _t(
280
                        'SilverStripe\\UserForms\\Model\\UserDefinedForm.ORSELECTAFIELDTOUSEASFROM',
281
                        '.. or select a field to use as reply to address'
282
                    ),
283
                    $validEmailFromFields->map('ID', 'Title')
284
                )->setEmptyString(' ')
285
            )
286
                ->setTitle(_t(
287
                    'SilverStripe\\UserForms\\Model\\UserDefinedForm.REPLYADDRESS',
288
                    'Email for reply to'
289
                ))
290
                ->setDescription(_t(
291
                    'SilverStripe\\UserForms\\Model\\UserDefinedForm.REPLYADDRESS_DESCRIPTION',
292
                    'The email address which the recipient is able to \'reply\' to.'
293
                ))
294
        ]);
295
296
        $fields->fieldByName('Root.EmailDetails')->setTitle(_t(__CLASS__.'.EMAILDETAILSTAB', 'Email Details'));
297
298
        // Only show the preview link if the recipient has been saved.
299
        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...
300
            $preview = sprintf(
301
                '<p><a href="%s" target="_blank" class="ss-ui-button">%s</a></p><em>%s</em>',
302
                Controller::join_links(
303
                    singleton(CMSPageEditController::class)->getEditForm()->FormAction(),
304
                    "field/EmailRecipients/item/{$this->ID}/preview"
305
                ),
306
                _t('SilverStripe\\UserForms\\Model\\UserDefinedForm.PREVIEW_EMAIL', 'Preview email'),
307
                _t(
308
                    'SilverStripe\\UserForms\\Model\\UserDefinedForm.PREVIEW_EMAIL_DESCRIPTION',
309
                    'Note: Unsaved changes will not appear in the preview.'
310
                )
311
            );
312
        } else {
313
            $preview = sprintf(
314
                '<em>%s</em>',
315
                _t(
316
                    'SilverStripe\\UserForms\\Model\\UserDefinedForm.PREVIEW_EMAIL_UNAVAILABLE',
317
                    'You can preview this email once you have saved the Recipient.'
318
                )
319
            );
320
        }
321
322
        // Email templates
323
        $fields->addFieldsToTab('Root.EmailContent', [
324
            CheckboxField::create(
325
                'HideFormData',
326
                _t('SilverStripe\\UserForms\\Model\\UserDefinedForm.HIDEFORMDATA', 'Hide form data from email?')
327
            ),
328
            CheckboxField::create(
329
                'SendPlain',
330
                _t(
331
                    'SilverStripe\\UserForms\\Model\\UserDefinedForm.SENDPLAIN',
332
                    'Send email as plain text? (HTML will be stripped)'
333
                )
334
            ),
335
            DropdownField::create(
336
                'EmailTemplate',
337
                _t('SilverStripe\\UserForms\\Model\\UserDefinedForm.EMAILTEMPLATE', 'Email template'),
338
                $this->getEmailTemplateDropdownValues()
339
            )->addExtraClass('toggle-html-only'),
340
            HTMLEditorField::create(
341
                'EmailBodyHtml',
342
                _t('SilverStripe\\UserForms\\Model\\UserDefinedForm.EMAILBODYHTML', 'Body')
343
            )
344
                ->addExtraClass('toggle-html-only'),
345
            TextareaField::create(
346
                'EmailBody',
347
                _t('SilverStripe\\UserForms\\Model\\UserDefinedForm.EMAILBODY', 'Body')
348
            )
349
                ->addExtraClass('toggle-plain-only'),
350
            LiteralField::create('EmailPreview', $preview)
351
        ]);
352
353
        $fields->fieldByName('Root.EmailContent')->setTitle(_t(__CLASS__.'.EMAILCONTENTTAB', 'Email Content'));
354
355
        // Custom rules for sending this field
356
        $grid = GridField::create(
357
            'CustomRules',
358
            _t('SilverStripe\\UserForms\\Model\\EditableFormField.CUSTOMRULES', 'Custom Rules'),
359
            $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...
360
            $this->getRulesConfig()
361
        );
362
        $grid->setDescription(_t(
363
            'SilverStripe\\UserForms\\Model\\UserDefinedForm.RulesDescription',
364
            '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.'
365
        ));
366
        $fields->addFieldsToTab('Root.CustomRules', [
367
            DropdownField::create(
368
                'CustomRulesCondition',
369
                _t('SilverStripe\\UserForms\\Model\\UserDefinedForm.SENDIF', 'Send condition'),
370
                [
371
                    'Or' => _t('SilverStripe\\UserForms\\Model\\UserDefinedForm.SENDIFOR', 'Any conditions are true'),
372
                    'And' => _t('SilverStripe\\UserForms\\Model\\UserDefinedForm.SENDIFAND', 'All conditions are true')
373
                ]
374
            ),
375
            $grid
376
        ]);
377
378
        $fields->fieldByName('Root.CustomRules')->setTitle(_t(__CLASS__.'.CUSTOMRULESTAB', 'Custom Rules'));
379
380
        $this->extend('updateCMSFields', $fields);
381
        return $fields;
382
    }
383
384
    /**
385
     * Return whether a user can create an object of this type
386
     *
387
     * @param Member $member
388
     * @param array $context Virtual parameter to allow context to be passed in to check
389
     * @return bool
390
     */
391 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...
392
    {
393
        // Check parent page
394
        $parent = $this->getCanCreateContext(func_get_args());
395
        if ($parent) {
396
            return $parent->canEdit($member);
397
        }
398
399
        // Fall back to secure admin permissions
400
        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 400 which is incompatible with the return type documented by SilverStripe\UserForms\M...ailRecipient::canCreate of type boolean.
Loading history...
401
    }
402
403
    /**
404
     * Helper method to check the parent for this object
405
     *
406
     * @param array $args List of arguments passed to canCreate
407
     * @return SiteTree Parent page instance
408
     */
409
    protected function getCanCreateContext($args)
410
    {
411
        // Inspect second parameter to canCreate for a 'Parent' context
412
        if (isset($args[1][Form::class])) {
413
            return $args[1][Form::class];
414
        }
415
        // Hack in currently edited page if context is missing
416
        if (Controller::has_curr() && Controller::curr() instanceof CMSMain) {
417
            return Controller::curr()->currentPage();
418
        }
419
420
        // No page being edited
421
        return null;
422
    }
423
424
    /**
425
     * @param Member
426
     *
427
     * @return boolean
428
     */
429
    public function canView($member = null)
430
    {
431
        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...
432
    }
433
434
    /**
435
     * @param Member
436
     *
437
     * @return boolean
438
     */
439
    public function canEdit($member = null)
440
    {
441
        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...
442
    }
443
444
    /**
445
     * @param Member
446
     *
447
     * @return boolean
448
     */
449
    public function canDelete($member = null)
450
    {
451
        return $this->canEdit($member);
452
    }
453
454
    /*
455
     * Determine if this recipient may receive notifications for this submission
456
     *
457
     * @param array $data
458
     * @param Form $form
459
     * @return bool
460
     */
461
    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...
462
    {
463
        // Skip if no rules configured
464
        $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...
465
        if (!$customRules->count()) {
466
            return true;
467
        }
468
469
        // Check all rules
470
        $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...
471
        foreach ($customRules as $customRule) {
472
            /** @var EmailRecipientCondition  $customRule */
473
            $matches = $customRule->matches($data);
474
            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...
475
                return false;
476
            }
477
            if (!$isAnd && $matches) {
478
                return true;
479
            }
480
        }
481
482
        // Once all rules are checked
483
        return $isAnd;
484
    }
485
486
    /**
487
     * Make sure the email template saved against the recipient exists on the file system.
488
     *
489
     * @param string
490
     *
491
     * @return boolean
492
     */
493
    public function emailTemplateExists($template = '')
494
    {
495
        $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...
496
497
        return in_array($t, $this->getEmailTemplateDropdownValues());
498
    }
499
500
    /**
501
     * Get the email body for the current email format
502
     *
503
     * @return string
504
     */
505
    public function getEmailBodyContent()
506
    {
507
        if ($this->SendPlain) {
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...
508
            return DBField::create_field('HTMLText', $this->EmailBody)->Plain();
0 ignored issues
show
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...
509
        }
510
        return DBField::create_field('HTMLText', $this->EmailBodyHtml)->RAW();
0 ignored issues
show
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...
511
    }
512
513
    /**
514
     * Gets a list of email templates suitable for populating the email template dropdown.
515
     *
516
     * @return array
517
     */
518
    public function getEmailTemplateDropdownValues()
519
    {
520
        $templates = [];
521
522
        $finder = new FileFinder();
523
        $finder->setOption('name_regex', '/^.*\.ss$/');
524
525
        $templateDirectory = UserDefinedForm::config()->get('email_template_directory');
526
        // Handle cases where "userforms" might not be the base module directory, e.g. in a Travis build
527
        if (!file_exists($templateDirectory) && substr($templateDirectory, 0, 10) === 'userforms/') {
528
            $templateDirectory = substr($templateDirectory, 10);
529
        }
530
        $found = $finder->find(BASE_PATH . '/' . $templateDirectory);
531
532
        foreach ($found as $key => $value) {
533
            $template = pathinfo($value);
534
535
            $templates[$template['filename']] = $template['filename'];
536
        }
537
538
        return $templates;
539
    }
540
541
    /**
542
     * Validate that valid email addresses are being used
543
     *
544
     * @return ValidationResult
545
     */
546
    public function validate()
547
    {
548
        $result = parent::validate();
549
        $checkEmail = [
550
            'EmailAddress' => 'EMAILADDRESSINVALID',
551
            'EmailFrom' => 'EMAILFROMINVALID',
552
            'EmailReplyTo' => 'EMAILREPLYTOINVALID',
553
        ];
554
        foreach ($checkEmail as $check => $translation) {
555
            if ($this->$check) {
556
                //may be a comma separated list of emails
557
                $addresses = explode(',', $this->$check);
558
                foreach ($addresses as $address) {
559
                    $trimAddress = trim($address);
560
                    if ($trimAddress && !Email::is_valid_address($trimAddress)) {
561
                        $error = _t(
562
                            __CLASS__.".$translation",
563
                            "Invalid email address $trimAddress"
564
                        );
565
                        $result->addError($error . " ($trimAddress)");
566
                    }
567
                }
568
            }
569
        }
570
        return $result;
571
    }
572
}
573