Completed
Push — master ( 016a0b...25fc98 )
by Franco
12s
created

EmailRecipient::getEmailBodyContent()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

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