Completed
Pull Request — master (#647)
by Robbie
20:35 queued 18:30
created

UserDefinedForm   C

Complexity

Total Complexity 15

Size/Duplication

Total Lines 360
Duplicated Lines 0 %

Coupling/Cohesion

Components 0
Dependencies 30

Importance

Changes 0
Metric Value
wmc 15
lcom 0
cbo 30
dl 0
loc 360
rs 5
c 0
b 0
f 0

5 Methods

Rating   Name   Duplication   Size   Complexity  
D getCMSFields() 0 157 9
A FilteredEmailRecipients() 0 14 1
A getFormOptions() 0 19 3
A getErrorContainerID() 0 4 1
A getCMSValidator() 0 4 1
1
<?php
2
3
namespace SilverStripe\UserForms\Model;
4
5
use Page;
6
use Colymba\BulkManager\BulkManager;
7
use SilverStripe\Core\Injector\Injector;
8
use SilverStripe\Core\Manifest\ModuleLoader;
9
use SilverStripe\Forms\CheckboxField;
10
use SilverStripe\Forms\CompositeField;
11
use SilverStripe\Forms\FieldList;
12
use SilverStripe\Forms\GridField\GridField;
13
use SilverStripe\Forms\GridField\GridFieldAddNewButton;
14
use SilverStripe\Forms\GridField\GridFieldButtonRow;
15
use SilverStripe\Forms\GridField\GridFieldConfig;
16
use SilverStripe\Forms\GridField\GridFieldConfig_RecordEditor;
17
use SilverStripe\Forms\GridField\GridFieldDataColumns;
18
use SilverStripe\Forms\GridField\GridFieldDeleteAction;
19
use SilverStripe\Forms\GridField\GridFieldDetailForm;
20
use SilverStripe\Forms\GridField\GridFieldEditButton;
21
use SilverStripe\Forms\GridField\GridFieldExportButton;
22
use SilverStripe\Forms\GridField\GridFieldPageCount;
23
use SilverStripe\Forms\GridField\GridFieldPaginator;
24
use SilverStripe\Forms\GridField\GridFieldPrintButton;
25
use SilverStripe\Forms\GridField\GridFieldSortableHeader;
26
use SilverStripe\Forms\GridField\GridFieldToolbarHeader;
27
use SilverStripe\Forms\HTMLEditor\HTMLEditorField;
28
use SilverStripe\Forms\LabelField;
29
use SilverStripe\Forms\LiteralField;
30
use SilverStripe\Forms\TextField;
31
use SilverStripe\ORM\ArrayList;
32
use SilverStripe\ORM\DB;
33
use SilverStripe\UserForms\Extension\UserFormFieldEditorExtension;
34
use SilverStripe\UserForms\Extension\UserFormValidator;
35
use SilverStripe\UserForms\Form\UserFormsGridFieldFilterHeader;
36
use SilverStripe\UserForms\Model\Recipient\EmailRecipient;
37
use SilverStripe\UserForms\Model\Recipient\UserFormRecipientItemRequest;
38
use SilverStripe\UserForms\Model\Submission\SubmittedForm;
39
use SilverStripe\UserForms\Task\UserFormsUpgradeService;
40
use SilverStripe\View\Requirements;
41
42
/**
43
 * @package userforms
44
 */
45
class UserDefinedForm extends Page
46
{
47
    /**
48
     * @var string
49
     */
50
    private static $icon = 'userforms/images/sitetree_icon.png';
0 ignored issues
show
Unused Code introduced by
The property $icon 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...
51
52
    /**
53
     * @var string
54
     */
55
    private static $description = 'Adds a customizable form.';
0 ignored issues
show
Unused Code introduced by
The property $description 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...
56
57
    /**
58
     * @var string Required Identifier
59
     */
60
    private static $required_identifier = null;
0 ignored issues
show
Unused Code introduced by
The property $required_identifier is not used and could be removed.

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

Loading history...
61
62
    /**
63
     * @var string
64
     */
65
    private static $email_template_directory = 'userforms/templates/email/';
0 ignored issues
show
Unused Code introduced by
The property $email_template_directory 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...
66
67
    /**
68
     * Should this module automatically upgrade on dev/build?
69
     *
70
     * @config
71
     * @var bool
72
     */
73
    private static $upgrade_on_build = true;
0 ignored issues
show
Unused Code introduced by
The property $upgrade_on_build 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...
74
75
    /**
76
     * Set this to true to disable automatic inclusion of CSS files
77
     * @config
78
     * @var bool
79
     */
80
    private static $block_default_userforms_css = false;
0 ignored issues
show
Unused Code introduced by
The property $block_default_userforms_css 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
82
    /**
83
     * Set this to true to disable automatic inclusion of JavaScript files
84
     * @config
85
     * @var bool
86
     */
87
    private static $block_default_userforms_js = false;
0 ignored issues
show
Unused Code introduced by
The property $block_default_userforms_js 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...
88
89
    private static $table_name = 'UserDefinedForm';
0 ignored issues
show
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...
90
91
    /**
92
     * Built in extensions required by this page
93
     * @config
94
     * @var array
95
     */
96
    private static $extensions = [
0 ignored issues
show
Unused Code introduced by
The property $extensions is not used and could be removed.

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

Loading history...
97
        UserFormFieldEditorExtension::class
98
    ];
99
100
    /**
101
     * @var array Fields on the user defined form page.
102
     */
103
    private static $db = [
0 ignored issues
show
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...
104
        'SubmitButtonText' => 'Varchar',
105
        'ClearButtonText' => 'Varchar',
106
        'OnCompleteMessage' => 'HTMLText',
107
        'ShowClearButton' => 'Boolean',
108
        'DisableSaveSubmissions' => 'Boolean',
109
        'EnableLiveValidation' => 'Boolean',
110
        'DisplayErrorMessagesAtTop' => 'Boolean',
111
        'DisableAuthenicatedFinishAction' => 'Boolean',
112
        'DisableCsrfSecurityToken' => 'Boolean'
113
    ];
114
115
    /**
116
     * @var array Default values of variables when this page is created
117
     */
118
    private static $defaults = [
0 ignored issues
show
Unused Code introduced by
The property $defaults 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...
119
        'Content' => '$UserDefinedForm',
120
        'DisableSaveSubmissions' => 0,
121
        'OnCompleteMessage' => '<p>Thanks, we\'ve received your submission.</p>'
122
    ];
123
124
    /**
125
     * @var array
126
     */
127
    private static $has_many = [
0 ignored issues
show
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...
128
        'Submissions' => SubmittedForm::class,
129
        'EmailRecipients' => EmailRecipient::class
130
    ];
131
132
    private static $cascade_deletes = [
0 ignored issues
show
Unused Code introduced by
The property $cascade_deletes 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...
133
        'EmailRecipients',
134 2
    ];
135
136 2
    /**
137
     * @var array
138 2
     * @config
139
     */
140
    private static $casting = [
0 ignored issues
show
Unused Code introduced by
The property $casting 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...
141
        'ErrorContainerID' => 'Text'
142 2
    ];
143 2
144 2
    /**
145
     * Error container selector which matches the element for grouped messages
146
     *
147 2
     * @var string
148 2
     * @config
149 2
     */
150 2
    private static $error_container_id = 'error-container';
0 ignored issues
show
Unused Code introduced by
The property $error_container_id 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...
151
152 2
    /**
153
     * The configuration used to determine whether a confirmation message is to
154 2
     * appear when navigating away from a partially completed form.
155 2
     *
156
     * @var boolean
157
     * @config
158 2
     */
159 2
    private static $enable_are_you_sure = true;
0 ignored issues
show
Unused Code introduced by
The property $enable_are_you_sure 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...
160 2
161 2
    /**
162 2
     * @var bool
163
     * @config
164
     */
165 2
    private static $recipients_warning_enabled = false;
0 ignored issues
show
Unused Code introduced by
The property $recipients_warning_enabled 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...
166 2
167 2
    private static $non_live_permissions = ['SITETREE_VIEW_ALL'];
0 ignored issues
show
Unused Code introduced by
The property $non_live_permissions 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...
168 2
169
    /**
170 2
     * Temporary storage of field ids when the form is duplicated.
171
     * Example layout: array('EditableCheckbox3' => 'EditableCheckbox14')
172 2
     * @var array
173 2
     */
174 2
    protected $fieldsFromTo = [];
175
176 2
    /**
177 2
     * @return FieldList
178 2
     */
179
    public function getCMSFields()
180
    {
181
        Requirements::css(
182
            ModuleLoader::getModule('silverstripe/userforms')->getRelativeResourcePath('css/UserForm_cms.css')
183 2
        );
184
185
        $this->beforeUpdateCMSFields(function ($fields) {
186
            // define tabs
187
            $fields->findOrMakeTab('Root.FormOptions', _t(__CLASS__.'.CONFIGURATION', 'Configuration'));
188
            $fields->findOrMakeTab('Root.Recipients', _t(__CLASS__.'.RECIPIENTS', 'Recipients'));
189
            $fields->findOrMakeTab('Root.Submissions', _t(__CLASS__.'.SUBMISSIONS', 'Submissions'));
190
191 2
            // text to show on complete
192 2
            $onCompleteFieldSet = CompositeField::create(
193 2
                $label = LabelField::create(
194
                    'OnCompleteMessageLabel',
195 2
                    _t(__CLASS__.'.ONCOMPLETELABEL', 'Show on completion')
196 2
                ),
197
                $editor = HTMLEditorField::create(
198 2
                    'OnCompleteMessage',
199
                    '',
200 2
                    _t(__CLASS__.'.ONCOMPLETEMESSAGE', $this->OnCompleteMessage)
201 2
                )
202 2
            );
203 2
204 2
            $onCompleteFieldSet->addExtraClass('field');
205 2
206 2
            $editor->setRows(3);
207 2
            $label->addExtraClass('left');
208 2
209 2
            // Define config for email recipients
210 2
            $emailRecipientsConfig = GridFieldConfig_RecordEditor::create(10);
211 2
            $emailRecipientsConfig->getComponentByType(GridFieldAddNewButton::class)
212 2
                ->setButtonName(
213
                    _t(__CLASS__.'.ADDEMAILRECIPIENT', 'Add Email Recipient')
214
                );
215
216 2
            // who do we email on submission
217 2
            $emailRecipients = GridField::create(
218
                'EmailRecipients',
219 2
                _t(__CLASS__.'.EMAILRECIPIENTS', 'Email Recipients'),
220 2
                $this->EmailRecipients(),
221 2
                $emailRecipientsConfig
222 1
            );
223 1
            $emailRecipients
224 2
                ->getConfig()
225
                ->getComponentByType(GridFieldDetailForm::class)
226 2
                ->setItemRequestClass(UserFormRecipientItemRequest::class);
227
228
            $fields->addFieldsToTab('Root.FormOptions', $onCompleteFieldSet);
229
            $fields->addFieldToTab('Root.Recipients', $emailRecipients);
230
            $fields->addFieldsToTab('Root.FormOptions', $this->getFormOptions());
231 2
232
233
            // view the submissions
234
            // make sure a numeric not a empty string is checked against this int column for SQL server
235 2
            $parentID = (!empty($this->ID)) ? (int) $this->ID : 0;
236 2
237 2
            // get a list of all field names and values used for print and export CSV views of the GridField below.
238
            $columnSQL = <<<SQL
239
SELECT "SubmittedFormField"."Name" as "Name", COALESCE("EditableFormField"."Title", "SubmittedFormField"."Title") as "Title", COALESCE("EditableFormField"."Sort", 999) AS "Sort"
240 2
FROM "SubmittedFormField"
241 2
LEFT JOIN "SubmittedForm" ON "SubmittedForm"."ID" = "SubmittedFormField"."ParentID"
242 2
LEFT JOIN "EditableFormField" ON "EditableFormField"."Name" = "SubmittedFormField"."Name" AND "EditableFormField"."ParentID" = '$parentID'
243
WHERE "SubmittedForm"."ParentID" = '$parentID'
244
ORDER BY "Sort", "Title"
245
SQL;
246 2
            // Sanitise periods in title
247 2
            $columns = array();
248
            foreach (DB::query($columnSQL)->map() as $name => $title) {
249
                $columns[$name] = trim(strtr($title, '.', ' '));
250 2
            }
251 2
252
            $config = GridFieldConfig::create();
253 2
            $config->addComponent(new GridFieldToolbarHeader());
254 2
            $config->addComponent($sort = new GridFieldSortableHeader());
255 2
            $config->addComponent($filter = new UserFormsGridFieldFilterHeader());
256 2
            $config->addComponent(new GridFieldDataColumns());
257
            $config->addComponent(new GridFieldEditButton());
258 2
            $config->addComponent(new GridFieldDeleteAction());
259 2
            $config->addComponent(new GridFieldPageCount('toolbar-header-right'));
260 2
            $config->addComponent($pagination = new GridFieldPaginator(25));
261 2
            $config->addComponent(new GridFieldDetailForm());
262 2
            $config->addComponent(new GridFieldButtonRow('after'));
263 2
            $config->addComponent($export = new GridFieldExportButton('buttons-after-left'));
264 2
            $config->addComponent($print = new GridFieldPrintButton('buttons-after-left'));
265 2
266 2
            // show user form items in the summary tab
267 2
            $summaryarray = array(
268
                'ID' => 'ID',
269 2
                'Created' => 'Created',
270
                'LastEdited' => 'Last Edited'
271 2
            );
272
            foreach (EditableFormField::get()->filter(array('ParentID' => $parentID)) as $eff) {
273
                if ($eff->ShowInSummary) {
274
                    $summaryarray[$eff->Name] = $eff->Title ?: $eff->Name;
275
                }
276
            }
277
278 2
            $config->getComponentByType(GridFieldDataColumns::class)->setDisplayFields($summaryarray);
279
280
            /**
281
             * Support for {@link https://github.com/colymba/GridFieldBulkEditingTools}
282
             */
283
            if (class_exists(BulkManager::class)) {
284
                $config->addComponent(new BulkManager);
285
            }
286
287
            $sort->setThrowExceptionOnBadDataType(false);
288
            $filter->setThrowExceptionOnBadDataType(false);
289 3
            $pagination->setThrowExceptionOnBadDataType(false);
290
291 3
            // attach every column to the print view form
292
            $columns['Created'] = 'Created';
293
            $columns['SubmittedBy.Email'] = 'Submitter';
294
            $filter->setColumns($columns);
295
296 2
            // print configuration
297 3
298
            $print->setPrintHasHeader(true);
299 3
            $print->setPrintColumns($columns);
300
301 3
            // export configuration
302
            $export->setCsvHasHeader(true);
303
            $export->setExportColumns($columns);
304
305
            $submissions = GridField::create(
306
                'Submissions',
307
                _t(__CLASS__.'.SUBMISSIONS', 'Submissions'),
308
                $this->Submissions()->sort('Created', 'DESC'),
309
                $config
310 3
            );
311
            $fields->addFieldToTab('Root.Submissions', $submissions);
312 3
            $fields->addFieldToTab(
313 3
                'Root.FormOptions',
314
                CheckboxField::create(
315 3
                    'DisableSaveSubmissions',
316 3
                    _t(__CLASS__.'.SAVESUBMISSIONS', 'Disable Saving Submissions to Server')
317 3
                )
318 3
            );
319 3
        });
320 3
321 3
        $fields = parent::getCMSFields();
322 3
323 3
        if ($this->EmailRecipients()->Count() == 0 && static::config()->recipients_warning_enabled) {
324
            $fields->addFieldToTab('Root.Main', LiteralField::create(
325 3
                'EmailRecipientsWarning',
326
                '<p class="message warning">' . _t(
327 3
                    __CLASS__.'.NORECIPIENTS',
328
                    'Warning: You have not configured any recipients. Form submissions may be missed.'
329
                )
330
                . '</p>'
331
            ), 'Title');
332
        }
333
334
        return $fields;
335
    }
336
337
    /**
338
     * Allow overriding the EmailRecipients on a {@link DataExtension}
339
     * so you can customise who receives an email.
340
     * Converts the RelationList to an ArrayList so that manipulation
341
     * of the original source data isn't possible.
342
     *
343
     * @return ArrayList
344
     */
345
    public function FilteredEmailRecipients($data = null, $form = null)
346
    {
347
        $recipients = ArrayList::create($this->EmailRecipients()->toArray());
348
349
        // Filter by rules
350
        $recipients = $recipients->filterByCallback(function ($recipient) use ($data, $form) {
351
            /** @var EmailRecipient $recipient */
352
            return $recipient->canSend($data, $form);
353
        });
354
355
        $this->extend('updateFilteredEmailRecipients', $recipients, $data, $form);
356
357
        return $recipients;
358
    }
359
360
    /**
361
     * Custom options for the form. You can extend the built in options by
362
     * using {@link updateFormOptions()}
363
     *
364
     * @return FieldList
365
     */
366
    public function getFormOptions()
367
    {
368
        $submit = ($this->SubmitButtonText) ? $this->SubmitButtonText : _t(__CLASS__.'.SUBMITBUTTON', 'Submit');
369
        $clear = ($this->ClearButtonText) ? $this->ClearButtonText : _t(__CLASS__.'.CLEARBUTTON', 'Clear');
370
371
        $options = FieldList::create(
372
            TextField::create('SubmitButtonText', _t(__CLASS__.'.TEXTONSUBMIT', 'Text on submit button:'), $submit),
373
            TextField::create('ClearButtonText', _t(__CLASS__.'.TEXTONCLEAR', 'Text on clear button:'), $clear),
374
            CheckboxField::create('ShowClearButton', _t(__CLASS__.'.SHOWCLEARFORM', 'Show Clear Form Button'), $this->ShowClearButton),
375
            CheckboxField::create('EnableLiveValidation', _t(__CLASS__.'.ENABLELIVEVALIDATION', 'Enable live validation')),
376
            CheckboxField::create('DisplayErrorMessagesAtTop', _t(__CLASS__.'.DISPLAYERRORMESSAGESATTOP', 'Display error messages above the form?')),
377
            CheckboxField::create('DisableCsrfSecurityToken', _t(__CLASS__.'.DISABLECSRFSECURITYTOKEN', 'Disable CSRF Token')),
378
            CheckboxField::create('DisableAuthenicatedFinishAction', _t(__CLASS__.'.DISABLEAUTHENICATEDFINISHACTION', 'Disable Authentication on finish action'))
379
        );
380
381
        $this->extend('updateFormOptions', $options);
382
383
        return $options;
384
    }
385 6
386
    /**
387 6
     * Get the HTML id of the error container displayed above the form.
388
     *
389 5
     * @return string
390
     */
391
    public function getErrorContainerID()
392 5
    {
393 5
        return $this->config()->get('error_container_id');
394 5
    }
395
396
397 5
    /**
398 5
     * Validate formfields
399 5
     */
400 5
    public function getCMSValidator()
401 5
    {
402 5
        return UserFormValidator::create();
403
    }
404
}
405