Passed
Pull Request — master (#722)
by Andrew
03:15
created

EmailRecipient::canSend()   C

Complexity

Conditions 7
Paths 5

Size

Total Lines 23
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 11
nc 5
nop 2
dl 0
loc 23
rs 6.7272
c 0
b 0
f 0
1
<?php
2
3
namespace SilverStripe\UserForms\Model\Recipient;
4
5
use SilverStripe\Assets\FileFinder;
6
use SilverStripe\CMS\Controllers\CMSMain;
7
use SilverStripe\CMS\Controllers\CMSPageEditController;
8
use SilverStripe\CMS\Model\SiteTree;
9
use SilverStripe\Control\Controller;
10
use SilverStripe\Control\Email\Email;
11
use SilverStripe\Core\Manifest\ModuleResource;
12
use SilverStripe\Core\Manifest\ModuleResourceLoader;
13
use SilverStripe\Forms\CheckboxField;
14
use SilverStripe\Forms\DropdownField;
15
use SilverStripe\Forms\FieldGroup;
16
use SilverStripe\Forms\FieldList;
17
use SilverStripe\Forms\Form;
18
use SilverStripe\Forms\GridField\GridField;
19
use SilverStripe\Forms\GridField\GridFieldButtonRow;
20
use SilverStripe\Forms\GridField\GridFieldConfig;
21
use SilverStripe\Forms\GridField\GridFieldDeleteAction;
22
use SilverStripe\Forms\GridField\GridFieldToolbarHeader;
23
use SilverStripe\Forms\HTMLEditor\HTMLEditorField;
24
use SilverStripe\Forms\LiteralField;
25
use SilverStripe\Forms\TabSet;
26
use SilverStripe\Forms\TextareaField;
27
use SilverStripe\Forms\TextField;
28
use SilverStripe\ORM\ArrayList;
29
use SilverStripe\ORM\DataList;
30
use SilverStripe\ORM\DataObject;
31
use SilverStripe\ORM\DB;
32
use SilverStripe\ORM\FieldType\DBField;
33
use SilverStripe\ORM\ValidationResult;
34
use SilverStripe\Security\Member;
35
use SilverStripe\UserForms\Model\EditableFormField;
36
use SilverStripe\UserForms\Model\EditableFormField\EditableEmailField;
37
use SilverStripe\UserForms\Model\EditableFormField\EditableMultipleOptionField;
38
use SilverStripe\UserForms\Model\EditableFormField\EditableTextField;
39
use SilverStripe\UserForms\Model\UserDefinedForm;
40
use SilverStripe\UserForms\UserForm;
41
use SilverStripe\View\Requirements;
42
use Symbiote\GridFieldExtensions\GridFieldAddNewInlineButton;
43
use Symbiote\GridFieldExtensions\GridFieldEditableColumns;
44
45
/**
46
 * A Form can have multiply members / emails to email the submission
47
 * to and custom subjects
48
 *
49
 * @package userforms
50
 */
51
class EmailRecipient extends DataObject
52
{
53
    private static $db = [
54
        'EmailAddress' => 'Varchar(200)',
55
        'EmailSubject' => 'Varchar(200)',
56
        'EmailFrom' => 'Varchar(200)',
57
        'EmailReplyTo' => 'Varchar(200)',
58
        'EmailBody' => 'Text',
59
        'EmailBodyHtml' => 'HTMLText',
60
        'EmailTemplate' => 'Varchar',
61
        'SendPlain' => 'Boolean',
62
        'HideFormData' => 'Boolean',
63
        'CustomRulesCondition' => 'Enum("And,Or")'
64
    ];
65
66
    private static $has_one = [
67
        'Form' => DataObject::class,
68
        'SendEmailFromField' => EditableFormField::class,
69
        'SendEmailToField' => EditableFormField::class,
70
        'SendEmailSubjectField' => EditableFormField::class
71
    ];
72
73
    private static $has_many = [
74
        'CustomRules' => EmailRecipientCondition::class,
75
    ];
76
77
    private static $owns = [
78
        'CustomRules',
79
    ];
80
81
    private static $cascade_deletes = [
82
        'CustomRules',
83
    ];
84
85
    private static $summary_fields = [
86
        'EmailAddress',
87
        'EmailSubject',
88
        'EmailFrom'
89
    ];
90
91
    private static $table_name = 'UserDefinedForm_EmailRecipient';
92
93
    /**
94
     * Setting this to true will allow you to select "risky" fields as
95
     * email recipient, such as free-text entry fields.
96
     *
97
     * It's advisable to leave this off.
98
     *
99
     * @config
100
     * @var bool
101
     */
102
    private static $allow_unbound_recipient_fields = false;
103
104
    public function requireDefaultRecords()
105
    {
106
        parent::requireDefaultRecords();
107
108
        // make sure to migrate the class across (prior to v5.x)
109
        DB::query("UPDATE UserDefinedForm_EmailRecipient SET FormClass = 'Page' WHERE FormClass IS NULL");
110
    }
111
112
    public function summaryFields()
113
    {
114
        $fields = parent::summaryFields();
115
        if (isset($fields['EmailAddress'])) {
116
            /** @skipUpgrade */
117
            $fields['EmailAddress'] = _t('SilverStripe\\UserForms\\Model\\UserDefinedForm.EMAILADDRESS', 'Email');
118
        }
119
        if (isset($fields['EmailSubject'])) {
120
            $fields['EmailSubject'] = _t('SilverStripe\\UserForms\\Model\\UserDefinedForm.EMAILSUBJECT', 'Subject');
121
        }
122
        if (isset($fields['EmailFrom'])) {
123
            $fields['EmailFrom'] = _t('SilverStripe\\UserForms\\Model\\UserDefinedForm.EMAILFROM', 'From');
124
        }
125
        return $fields;
126
    }
127
128
    /**
129
     * Get instance of UserForm when editing in getCMSFields
130
     *
131
     * @return UserDefinedForm|UserForm
132
     */
133
    protected function getFormParent()
134
    {
135
        // LeftAndMain::sessionNamespace is protected. @todo replace this with a non-deprecated equivalent.
136
        $sessionNamespace = $this->config()->get('session_namespace') ?: CMSMain::class;
137
138
        $formID = $this->FormID ?: Controller::curr()->getRequest()->getSession()->get($sessionNamespace . '.currentPage');
0 ignored issues
show
Bug Best Practice introduced by
The property FormID does not exist on SilverStripe\UserForms\M...ecipient\EmailRecipient. Since you implemented __get, consider adding a @property annotation.
Loading history...
139
        $formClass = $this->FormClass ?: UserDefinedForm::class;
0 ignored issues
show
Bug Best Practice introduced by
The property FormClass does not exist on SilverStripe\UserForms\M...ecipient\EmailRecipient. Since you implemented __get, consider adding a @property annotation.
Loading history...
140
141
        return $formClass::get()->byID($formID);
142
    }
143
144
    public function getTitle()
145
    {
146
        if ($this->EmailAddress) {
0 ignored issues
show
Bug Best Practice introduced by
The property EmailAddress does not exist on SilverStripe\UserForms\M...ecipient\EmailRecipient. Since you implemented __get, consider adding a @property annotation.
Loading history...
147
            return $this->EmailAddress;
148
        }
149
        if ($this->EmailSubject) {
0 ignored issues
show
Bug Best Practice introduced by
The property EmailSubject does not exist on SilverStripe\UserForms\M...ecipient\EmailRecipient. Since you implemented __get, consider adding a @property annotation.
Loading history...
150
            return $this->EmailSubject;
151
        }
152
        return parent::getTitle();
153
    }
154
155
    /**
156
     * Generate a gridfield config for editing filter rules
157
     *
158
     * @return GridFieldConfig
159
     */
160
    protected function getRulesConfig()
161
    {
162
        if (!$this->getFormParent()) {
163
            return null;
164
        }
165
        $formFields = $this->getFormParent()->Fields();
0 ignored issues
show
Bug introduced by
The method Fields() does not exist on SilverStripe\UserForms\UserForm. Did you maybe mean getCMSFields()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

165
        $formFields = $this->getFormParent()->/** @scrutinizer ignore-call */ Fields();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
166
167
        $config = GridFieldConfig::create()
168
            ->addComponents(
169
                new GridFieldButtonRow('before'),
170
                new GridFieldToolbarHeader(),
171
                new GridFieldAddNewInlineButton(),
172
                new GridFieldDeleteAction(),
173
                $columns = new GridFieldEditableColumns()
174
            );
175
176
        $columns->setDisplayFields(array(
177
            '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. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

177
            'ConditionFieldID' => function ($record, $column, /** @scrutinizer ignore-unused */ $grid) use ($formFields) {

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

Loading history...
178
                return DropdownField::create($column, false, $formFields->map('ID', 'Title'));
0 ignored issues
show
Bug introduced by
false of type false is incompatible with the type array expected by parameter $args of SilverStripe\View\ViewableData::create(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

178
                return DropdownField::create($column, /** @scrutinizer ignore-type */ false, $formFields->map('ID', 'Title'));
Loading history...
179
            },
180
            'ConditionOption' => function ($record, $column, $grid) {
0 ignored issues
show
Unused Code introduced by
The parameter $grid is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

180
            'ConditionOption' => function ($record, $column, /** @scrutinizer ignore-unused */ $grid) {

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

Loading history...
181
                $options = EmailRecipientCondition::config()->condition_options;
182
                return DropdownField::create($column, false, $options);
0 ignored issues
show
Bug introduced by
false of type false is incompatible with the type array expected by parameter $args of SilverStripe\View\ViewableData::create(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

182
                return DropdownField::create($column, /** @scrutinizer ignore-type */ false, $options);
Loading history...
183
            },
184
            'ConditionValue' => function ($record, $column, $grid) {
0 ignored issues
show
Unused Code introduced by
The parameter $grid is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

184
            'ConditionValue' => function ($record, $column, /** @scrutinizer ignore-unused */ $grid) {

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

Loading history...
185
                return TextField::create($column);
186
            }
187
        ));
188
189
        return $config;
190
    }
191
192
    /**
193
     * @return FieldList
194
     */
195
    public function getCMSFields()
196
    {
197
        Requirements::javascript('silverstripe/userforms:client/dist/js/userforms-cms.js');
198
199
        // Build fieldlist
200
        $fields = FieldList::create(Tabset::create('Root')->addExtraClass('EmailRecipientForm'));
0 ignored issues
show
Bug introduced by
'Root' of type string is incompatible with the type array expected by parameter $args of SilverStripe\View\ViewableData::create(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

200
        $fields = FieldList::create(Tabset::create(/** @scrutinizer ignore-type */ 'Root')->addExtraClass('EmailRecipientForm'));
Loading history...
201
202
        if (!$this->getFormParent()) {
203
            $fields->addFieldToTab('Root.EmailDetails', $this->getUnsavedFormLiteralField());
204
        }
205
206
        // Configuration fields
207
        $fields->addFieldsToTab('Root.EmailDetails', [
208
            $this->getSubjectCMSFields(),
209
            $this->getEmailToCMSFields(),
210
            $this->getEmailFromCMSFields(),
211
            $this->getEmailReplyToCMSFields(),
212
        ]);
213
214
        $fields->fieldByName('Root.EmailDetails')->setTitle(_t(__CLASS__ . '.EMAILDETAILSTAB', 'Email Details'));
215
216
        // Only show the preview link if the recipient has been saved.
217
        if (!empty($this->EmailTemplate)) {
0 ignored issues
show
Bug Best Practice introduced by
The property EmailTemplate does not exist on SilverStripe\UserForms\M...ecipient\EmailRecipient. Since you implemented __get, consider adding a @property annotation.
Loading history...
218
            $pageEditController = singleton(CMSPageEditController::class);
219
            $pageEditController
220
                ->getRequest()
221
                ->setSession(Controller::curr()->getRequest()->getSession());
222
223
            $preview = sprintf(
224
                '<p><a href="%s" target="_blank" class="btn btn-outline-secondary">%s</a></p><em>%s</em>',
225
                Controller::join_links(
226
                    $pageEditController->getEditForm()->FormAction(),
227
                    "field/EmailRecipients/item/{$this->ID}/preview"
228
                ),
229
                _t('SilverStripe\\UserForms\\Model\\UserDefinedForm.PREVIEW_EMAIL', 'Preview email'),
230
                _t(
231
                    'SilverStripe\\UserForms\\Model\\UserDefinedForm.PREVIEW_EMAIL_DESCRIPTION',
232
                    'Note: Unsaved changes will not appear in the preview.'
233
                )
234
            );
235
        } else {
236
            $preview = sprintf(
237
                '<p class="alert alert-warning">%s</p>',
238
                _t(
239
                    'SilverStripe\\UserForms\\Model\\UserDefinedForm.PREVIEW_EMAIL_UNAVAILABLE',
240
                    'You can preview this email once you have saved the Recipient.'
241
                )
242
            );
243
        }
244
245
        // Email templates
246
        $fields->addFieldsToTab('Root.EmailContent', [
247
            CheckboxField::create(
248
                'HideFormData',
249
                _t('SilverStripe\\UserForms\\Model\\UserDefinedForm.HIDEFORMDATA', 'Hide form data from email?')
250
            ),
251
            CheckboxField::create(
252
                'SendPlain',
253
                _t(
254
                    'SilverStripe\\UserForms\\Model\\UserDefinedForm.SENDPLAIN',
255
                    'Send email as plain text? (HTML will be stripped)'
256
                )
257
            ),
258
            HTMLEditorField::create(
259
                'EmailBodyHtml',
260
                _t('SilverStripe\\UserForms\\Model\\UserDefinedForm.EMAILBODYHTML', 'Body')
261
            )
262
                ->addExtraClass('toggle-html-only'),
263
            TextareaField::create(
264
                'EmailBody',
265
                _t('SilverStripe\\UserForms\\Model\\UserDefinedForm.EMAILBODY', 'Body')
266
            )
267
                ->addExtraClass('toggle-plain-only'),
268
            LiteralField::create('EmailPreview', $preview)
269
        ]);
270
271
        $templates = $this->getEmailTemplateDropdownValues();
272
273
        if ($templates) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $templates of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
274
            $fields->insertBefore(
275
                DropdownField::create(
276
                    'EmailTemplate',
277
                    _t('SilverStripe\\UserForms\\Model\\UserDefinedForm.EMAILTEMPLATE', 'Email template'),
278
                    $templates
279
                )->addExtraClass('toggle-html-only'),
280
                'EmailBodyHtml'
281
            );
282
        }
283
284
        $fields->fieldByName('Root.EmailContent')->setTitle(_t(__CLASS__ . '.EMAILCONTENTTAB', 'Email Content'));
285
286
        // Custom rules for sending this field
287
        $grid = GridField::create(
288
            'CustomRules',
289
            _t('SilverStripe\\UserForms\\Model\\EditableFormField.CUSTOMRULES', 'Custom Rules'),
290
            $this->CustomRules(),
0 ignored issues
show
Bug introduced by
The method CustomRules() does not exist on SilverStripe\UserForms\M...ecipient\EmailRecipient. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

290
            $this->/** @scrutinizer ignore-call */ 
291
                   CustomRules(),
Loading history...
291
            $this->getRulesConfig()
0 ignored issues
show
Bug introduced by
$this->getRulesConfig() of type SilverStripe\Forms\GridField\GridFieldConfig is incompatible with the type array expected by parameter $args of SilverStripe\View\ViewableData::create(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

291
            /** @scrutinizer ignore-type */ $this->getRulesConfig()
Loading history...
292
        );
293
        $grid->setDescription(_t(
294
            'SilverStripe\\UserForms\\Model\\UserDefinedForm.RulesDescription',
295
            'Emails will only be sent to the recipient if the custom rules are met. If no rules are defined, '
296
            . 'this recipient will receive notifications for every submission.'
297
        ));
298
299
        $fields->addFieldsToTab('Root.CustomRules', [
300
            DropdownField::create(
301
                'CustomRulesCondition',
302
                _t('SilverStripe\\UserForms\\Model\\UserDefinedForm.SENDIF', 'Send condition'),
303
                [
304
                    'Or' => _t(
305
                        'SilverStripe\\UserForms\\Model\\UserDefinedForm.SENDIFOR',
306
                        'Any conditions are true'
307
                    ),
308
                    'And' => _t(
309
                        'SilverStripe\\UserForms\\Model\\UserDefinedForm.SENDIFAND',
310
                        'All conditions are true'
311
                    )
312
                ]
313
            ),
314
            $grid
315
        ]);
316
317
        $fields->fieldByName('Root.CustomRules')->setTitle(_t(__CLASS__ . '.CUSTOMRULESTAB', 'Custom Rules'));
318
319
        $this->extend('updateCMSFields', $fields);
320
        return $fields;
321
    }
322
323
    /**
324
     * Return whether a user can create an object of this type
325
     *
326
     * @param Member $member
327
     * @param array $context Virtual parameter to allow context to be passed in to check
328
     * @return bool
329
     */
330
    public function canCreate($member = null, $context = [])
331
    {
332
        // Check parent page
333
        $parent = $this->getCanCreateContext(func_get_args());
334
        if ($parent) {
0 ignored issues
show
introduced by
The condition $parent can never be true.
Loading history...
335
            return $parent->canEdit($member);
336
        }
337
338
        // Fall back to secure admin permissions
339
        return parent::canCreate($member);
340
    }
341
342
    /**
343
     * Helper method to check the parent for this object
344
     *
345
     * @param array $args List of arguments passed to canCreate
346
     * @return SiteTree Parent page instance
347
     */
348
    protected function getCanCreateContext($args)
349
    {
350
        // Inspect second parameter to canCreate for a 'Parent' context
351
        if (isset($args[1][Form::class])) {
352
            return $args[1][Form::class];
353
        }
354
        // Hack in currently edited page if context is missing
355
        if (Controller::has_curr() && Controller::curr() instanceof CMSMain) {
356
            return Controller::curr()->currentPage();
357
        }
358
359
        // No page being edited
360
        return null;
361
    }
362
363
    public function canView($member = null)
364
    {
365
        if ($form = $this->getFormParent()) {
366
            return $form->canView($member);
0 ignored issues
show
Bug introduced by
It seems like canView() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

366
            return $form->/** @scrutinizer ignore-call */ canView($member);
Loading history...
367
        }
368
        return parent::canView($member);
369
    }
370
371
    public function canEdit($member = null)
372
    {
373
        if ($form = $this->getFormParent()) {
374
            return $form->canEdit($member);
0 ignored issues
show
Bug introduced by
It seems like canEdit() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

374
            return $form->/** @scrutinizer ignore-call */ canEdit($member);
Loading history...
375
        }
376
377
        return parent::canEdit($member);
378
    }
379
380
    /**
381
     * @param Member
382
     *
383
     * @return boolean
384
     */
385
    public function canDelete($member = null)
386
    {
387
        return $this->canEdit($member);
388
    }
389
390
    /**
391
     * Determine if this recipient may receive notifications for this submission
392
     *
393
     * @param array $data
394
     * @param Form $form
395
     * @return bool
396
     */
397
    public function canSend($data, $form)
0 ignored issues
show
Unused Code introduced by
The parameter $form is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

397
    public function canSend($data, /** @scrutinizer ignore-unused */ $form)

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

Loading history...
398
    {
399
        // Skip if no rules configured
400
        $customRules = $this->CustomRules();
401
        if (!$customRules->count()) {
402
            return true;
403
        }
404
405
        // Check all rules
406
        $isAnd = $this->CustomRulesCondition === 'And';
0 ignored issues
show
Bug Best Practice introduced by
The property CustomRulesCondition does not exist on SilverStripe\UserForms\M...ecipient\EmailRecipient. Since you implemented __get, consider adding a @property annotation.
Loading history...
407
        foreach ($customRules as $customRule) {
408
            /** @var EmailRecipientCondition  $customRule */
409
            $matches = $customRule->matches($data);
410
            if ($isAnd && !$matches) {
411
                return false;
412
            }
413
            if (!$isAnd && $matches) {
414
                return true;
415
            }
416
        }
417
418
        // Once all rules are checked
419
        return $isAnd;
420
    }
421
422
    /**
423
     * Make sure the email template saved against the recipient exists on the file system.
424
     *
425
     * @param string
426
     *
427
     * @return boolean
428
     */
429
    public function emailTemplateExists($template = '')
430
    {
431
        $t = ($template ? $template : $this->EmailTemplate);
0 ignored issues
show
Bug Best Practice introduced by
The property EmailTemplate does not exist on SilverStripe\UserForms\M...ecipient\EmailRecipient. Since you implemented __get, consider adding a @property annotation.
Loading history...
432
433
        return array_key_exists($t, (array) $this->getEmailTemplateDropdownValues());
434
    }
435
436
    /**
437
     * Get the email body for the current email format
438
     *
439
     * @return string
440
     */
441
    public function getEmailBodyContent()
442
    {
443
        if ($this->SendPlain) {
0 ignored issues
show
Bug Best Practice introduced by
The property SendPlain does not exist on SilverStripe\UserForms\M...ecipient\EmailRecipient. Since you implemented __get, consider adding a @property annotation.
Loading history...
444
            return DBField::create_field('HTMLText', $this->EmailBody)->Plain();
0 ignored issues
show
Bug Best Practice introduced by
The property EmailBody does not exist on SilverStripe\UserForms\M...ecipient\EmailRecipient. Since you implemented __get, consider adding a @property annotation.
Loading history...
445
        }
446
        return DBField::create_field('HTMLText', $this->EmailBodyHtml);
0 ignored issues
show
Bug Best Practice introduced by
The property EmailBodyHtml does not exist on SilverStripe\UserForms\M...ecipient\EmailRecipient. Since you implemented __get, consider adding a @property annotation.
Loading history...
447
    }
448
449
    /**
450
     * Gets a list of email templates suitable for populating the email template dropdown.
451
     *
452
     * @return array
453
     */
454
    public function getEmailTemplateDropdownValues()
455
    {
456
        $templates = [];
457
458
        $finder = new FileFinder();
459
        $finder->setOption('name_regex', '/^.*\.ss$/');
460
461
        $parent = $this->getFormParent();
462
463
        if (!$parent) {
0 ignored issues
show
introduced by
The condition ! $parent can never be false.
Loading history...
464
            return [];
465
        }
466
467
        $emailTemplateDirectory = $parent->config()->get('email_template_directory');
468
        $templateDirectory = ModuleResourceLoader::resourcePath($emailTemplateDirectory);
469
470
        if (!$templateDirectory) {
471
            return [];
472
        }
473
474
        $found = $finder->find(BASE_PATH . DIRECTORY_SEPARATOR . $templateDirectory);
475
476
        foreach ($found as $key => $value) {
477
            $template = pathinfo($value);
478
            $absoluteFilename = $template['dirname'] . DIRECTORY_SEPARATOR . $template['filename'];
479
480
            // Optionally remove vendor/ path prefixes
481
            $resource = ModuleResourceLoader::singleton()->resolveResource($emailTemplateDirectory);
482
            if ($resource instanceof ModuleResource && $resource->getModule()) {
483
                $prefixToStrip = $resource->getModule()->getPath();
484
            } else {
485
                $prefixToStrip = BASE_PATH;
486
            }
487
            $templatePath = substr($absoluteFilename, strlen($prefixToStrip) + 1);
488
489
            // Optionally remove "templates/" prefixes
490
            if (substr($templatePath, 0, 10)) {
491
                $templatePath = substr($templatePath, 10);
492
            }
493
494
            $templates[$templatePath] = $template['filename'];
495
        }
496
497
        return $templates;
498
    }
499
500
    /**
501
     * Validate that valid email addresses are being used
502
     *
503
     * @return ValidationResult
504
     */
505
    public function validate()
506
    {
507
        $result = parent::validate();
508
        $checkEmail = [
509
            'EmailAddress' => 'EMAILADDRESSINVALID',
510
            'EmailFrom' => 'EMAILFROMINVALID',
511
            'EmailReplyTo' => 'EMAILREPLYTOINVALID',
512
        ];
513
        foreach ($checkEmail as $check => $translation) {
514
            if ($this->$check) {
515
                //may be a comma separated list of emails
516
                $addresses = explode(',', $this->$check);
517
                foreach ($addresses as $address) {
518
                    $trimAddress = trim($address);
519
                    if ($trimAddress && !Email::is_valid_address($trimAddress)) {
520
                        $error = _t(
521
                            __CLASS__.".$translation",
522
                            "Invalid email address $trimAddress"
523
                        );
524
                        $result->addError($error . " ($trimAddress)");
525
                    }
526
                }
527
            }
528
        }
529
530
        // if there is no from address and no fallback, you'll have errors if this isn't defined
531
        if (!$this->EmailFrom && empty(Email::getSendAllEmailsFrom()) && empty(Email::config()->get('admin_email'))) {
0 ignored issues
show
Bug Best Practice introduced by
The property EmailFrom does not exist on SilverStripe\UserForms\M...ecipient\EmailRecipient. Since you implemented __get, consider adding a @property annotation.
Loading history...
532
            $result->addError(_t(__CLASS__.".EMAILFROMREQUIRED", '"Email From" address is required'));
533
        }
534
        return $result;
535
    }
536
537
    /**
538
     * @return FieldGroup|TextField
539
     */
540
    protected function getSubjectCMSFields()
541
    {
542
        $subjectTextField = TextField::create(
543
            'EmailSubject',
0 ignored issues
show
Bug introduced by
'EmailSubject' of type string is incompatible with the type array expected by parameter $args of SilverStripe\View\ViewableData::create(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

543
            /** @scrutinizer ignore-type */ 'EmailSubject',
Loading history...
544
            _t('SilverStripe\\UserForms\\Model\\UserDefinedForm.TYPESUBJECT', 'Type subject')
545
        )
546
            ->setAttribute('style', 'min-width: 400px;');
547
548
        if ($this->getFormParent() && $this->getValidSubjectFields()) {
549
            return FieldGroup::create(
550
                $subjectTextField,
551
                DropdownField::create(
552
                    'SendEmailSubjectFieldID',
553
                    _t(
554
                        'SilverStripe\\UserForms\\Model\\UserDefinedForm.SELECTAFIELDTOSETSUBJECT',
555
                        '.. or select a field to use as the subject'
556
                    ),
557
                    $this->getValidSubjectFields()->map('ID', 'Title')
0 ignored issues
show
Bug introduced by
$this->getValidSubjectFi...s()->map('ID', 'Title') of type SilverStripe\ORM\Map is incompatible with the type array expected by parameter $args of SilverStripe\View\ViewableData::create(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

557
                    /** @scrutinizer ignore-type */ $this->getValidSubjectFields()->map('ID', 'Title')
Loading history...
558
                )->setEmptyString('')
559
            )
560
                ->setTitle(_t('SilverStripe\\UserForms\\Model\\UserDefinedForm.EMAILSUBJECT', 'Email subject'));
561
        } else {
562
            return $subjectTextField;
563
        }
564
    }
565
566
    /**
567
     * @return FieldGroup|TextField
568
     */
569
    protected function getEmailToCMSFields()
570
    {
571
        $emailToTextField = TextField::create(
572
            'EmailAddress',
0 ignored issues
show
Bug introduced by
'EmailAddress' of type string is incompatible with the type array expected by parameter $args of SilverStripe\View\ViewableData::create(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

572
            /** @scrutinizer ignore-type */ 'EmailAddress',
Loading history...
573
            _t('SilverStripe\\UserForms\\Model\\UserDefinedForm.TYPETO', 'Type to address')
574
        )
575
            ->setAttribute('style', 'min-width: 400px;');
576
577
        if ($this->getFormParent() && $this->getValidEmailToFields()) {
578
            return FieldGroup::create(
579
                $emailToTextField,
580
                DropdownField::create(
581
                    'SendEmailToFieldID',
582
                    _t(
583
                        'SilverStripe\\UserForms\\Model\\UserDefinedForm.ORSELECTAFIELDTOUSEASTO',
584
                        '.. or select a field to use as the to address'
585
                    ),
586
                    $this->getValidEmailToFields()->map('ID', 'Title')
0 ignored issues
show
Bug introduced by
$this->getValidEmailToFi...s()->map('ID', 'Title') of type SilverStripe\ORM\Map is incompatible with the type array expected by parameter $args of SilverStripe\View\ViewableData::create(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

586
                    /** @scrutinizer ignore-type */ $this->getValidEmailToFields()->map('ID', 'Title')
Loading history...
587
                )->setEmptyString(' ')
588
            )
589
                ->setTitle(_t('SilverStripe\\UserForms\\Model\\UserDefinedForm.SENDEMAILTO', 'Send email to'))
590
                ->setDescription(_t(
591
                    'SilverStripe\\UserForms\\Model\\UserDefinedForm.SENDEMAILTO_DESCRIPTION',
592
                    'You may enter multiple email addresses as a comma separated list.'
593
                ));
594
        } else {
595
            return $emailToTextField;
596
        }
597
    }
598
599
    /**
600
     * @return TextField
601
     */
602
    protected function getEmailFromCMSFields()
603
    {
604
        return TextField::create(
605
            'EmailFrom',
0 ignored issues
show
Bug introduced by
'EmailFrom' of type string is incompatible with the type array expected by parameter $args of SilverStripe\View\ViewableData::create(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

605
            /** @scrutinizer ignore-type */ 'EmailFrom',
Loading history...
606
            _t('SilverStripe\\UserForms\\Model\\UserDefinedForm.FROMADDRESS', 'Send email from')
607
        )
608
            ->setDescription(_t(
609
                'SilverStripe\\UserForms\\Model\\UserDefinedForm.EmailFromContent',
610
                "The from address allows you to set who the email comes from. On most servers this " .
611
                "will need to be set to an email address on the same domain name as your site. " .
612
                "For example on yoursite.com the from address may need to be [email protected]. " .
613
                "You can however, set any email address you wish as the reply to address."
614
            ));
615
    }
616
617
    /**
618
     * @return FieldGroup|TextField
619
     */
620
    protected function getEmailReplyToCMSFields()
621
    {
622
        $replyToTextField = TextField::create('EmailReplyTo', _t(
0 ignored issues
show
Bug introduced by
'EmailReplyTo' of type string is incompatible with the type array expected by parameter $args of SilverStripe\View\ViewableData::create(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

622
        $replyToTextField = TextField::create(/** @scrutinizer ignore-type */ 'EmailReplyTo', _t(
Loading history...
623
            'SilverStripe\\UserForms\\Model\\UserDefinedForm.TYPEREPLY',
624
            'Type reply address'
625
        ))
626
            ->setAttribute('style', 'min-width: 400px;');
627
        if ($this->getFormParent() && $this->getValidEmailFromFields()) {
628
            return FieldGroup::create(
629
                $replyToTextField,
630
                DropdownField::create(
631
                    'SendEmailFromFieldID',
632
                    _t(
633
                        'SilverStripe\\UserForms\\Model\\UserDefinedForm.ORSELECTAFIELDTOUSEASFROM',
634
                        '.. or select a field to use as reply to address'
635
                    ),
636
                    $this->getValidEmailFromFields()->map('ID', 'Title')
0 ignored issues
show
Bug introduced by
$this->getValidEmailFrom...s()->map('ID', 'Title') of type SilverStripe\ORM\Map is incompatible with the type array expected by parameter $args of SilverStripe\View\ViewableData::create(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

636
                    /** @scrutinizer ignore-type */ $this->getValidEmailFromFields()->map('ID', 'Title')
Loading history...
637
                )->setEmptyString(' ')
638
            )
639
                ->setTitle(_t(
640
                    'SilverStripe\\UserForms\\Model\\UserDefinedForm.REPLYADDRESS',
641
                    'Email for reply to'
642
                ))
643
                ->setDescription(_t(
644
                    'SilverStripe\\UserForms\\Model\\UserDefinedForm.REPLYADDRESS_DESCRIPTION',
645
                    'The email address which the recipient is able to \'reply\' to.'
646
                ));
647
        } else {
648
            return $replyToTextField;
649
        }
650
    }
651
652
    /**
653
     * @return DataList|null
654
     */
655
    protected function getMultiOptionFields()
656
    {
657
        if (!$form = $this->getFormParent()) {
658
            return null;
659
        }
660
        return EditableMultipleOptionField::get()->filter('ParentID', $form->ID);
0 ignored issues
show
Bug introduced by
The property ID does not seem to exist on SilverStripe\UserForms\UserForm.
Loading history...
661
    }
662
663
    /**
664
     * @return ArrayList|null
665
     */
666
    protected function getValidSubjectFields()
667
    {
668
        if (!$form = $this->getFormParent()) {
669
            return null;
670
        }
671
        // For the subject, only one-line entry boxes make sense
672
        $validSubjectFields = ArrayList::create(
673
            EditableTextField::get()
674
                ->filter('ParentID', $form->ID)
0 ignored issues
show
Bug introduced by
The property ID does not seem to exist on SilverStripe\UserForms\UserForm.
Loading history...
675
                ->exclude('Rows:GreaterThan', 1)
676
                ->toArray()
677
        );
678
        $validSubjectFields->merge($this->getMultiOptionFields());
679
        return $validSubjectFields;
680
    }
681
682
    /**
683
     * @return DataList|null
684
     */
685
    protected function getValidEmailFromFields()
686
    {
687
        if (!$form = $this->getFormParent()) {
688
            return null;
689
        }
690
691
        // if they have email fields then we could send from it
692
        return EditableEmailField::get()->filter('ParentID', $form->ID);
0 ignored issues
show
Bug introduced by
The property ID does not seem to exist on SilverStripe\UserForms\UserForm.
Loading history...
693
    }
694
695
    /**
696
     * @return ArrayList|DataList|null
697
     */
698
    protected function getValidEmailToFields()
699
    {
700
        if (!$this->getFormParent()) {
701
            return null;
702
        }
703
704
        // Check valid email-recipient fields
705
        if ($this->config()->get('allow_unbound_recipient_fields')) {
706
            // To address can only be email fields or multi option fields
707
            $validEmailToFields = ArrayList::create($this->getValidEmailFromFields()->toArray());
708
            $validEmailToFields->merge($this->getMultiOptionFields());
709
            return $validEmailToFields;
710
        } else {
711
            // To address cannot be unbound, so restrict to pre-defined lists
712
            return $this->getMultiOptionFields();
713
        }
714
    }
715
716
    protected function getUnsavedFormLiteralField()
717
    {
718
        return LiteralField::create(
719
            'UnsavedFormMessage',
0 ignored issues
show
Bug introduced by
'UnsavedFormMessage' of type string is incompatible with the type array expected by parameter $args of SilverStripe\View\ViewableData::create(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

719
            /** @scrutinizer ignore-type */ 'UnsavedFormMessage',
Loading history...
720
            sprintf('<p class="alert alert-warning">%s</p>',
721
                _t(
722
                    'SilverStripe\\UserForms\\Model\\UserDefinedForm.EMAIL_RECIPIENT_UNSAVED_FORM',
723
                    'You will be able to select from valid form fields after saving this record.'
724
                ))
725
        );
726
    }
727
}
728