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

UserDefinedForm   C

Complexity

Total Complexity 15

Size/Duplication

Total Lines 361
Duplicated Lines 0 %

Coupling/Cohesion

Components 0
Dependencies 31

Importance

Changes 0
Metric Value
wmc 15
lcom 0
cbo 31
dl 0
loc 361
rs 5
c 0
b 0
f 0

5 Methods

Rating   Name   Duplication   Size   Complexity  
A FilteredEmailRecipients() 0 14 1
A getFormOptions() 0 19 3
A getErrorContainerID() 0 4 1
A getCMSValidator() 0 4 1
D getCMSFields() 0 158 9
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\View\Requirements;
40
41
/**
42
 * @package userforms
43
 */
44
class UserDefinedForm extends Page
45
{
46
    /**
47
     * @var string
48
     */
49
    private static $icon = 'userforms/client/dist/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...
50
51
    /**
52
     * @var string
53
     */
54
    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...
55
56
    /**
57
     * @var string Required Identifier
58
     */
59
    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...
60
61
    /**
62
     * @var string
63
     */
64
    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...
65
66
    /**
67
     * Should this module automatically upgrade on dev/build?
68
     *
69
     * @config
70
     * @var bool
71
     */
72
    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...
73
74
    /**
75
     * Set this to true to disable automatic inclusion of CSS files
76
     * @config
77
     * @var bool
78
     */
79
    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...
80
81
    /**
82
     * Set this to true to disable automatic inclusion of JavaScript files
83
     * @config
84
     * @var bool
85
     */
86
    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...
87
88
    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...
89
90
    /**
91
     * Built in extensions required by this page
92
     * @config
93
     * @var array
94
     */
95
    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...
96
        UserFormFieldEditorExtension::class
97
    ];
98
99
    /**
100
     * @var array Fields on the user defined form page.
101
     */
102
    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...
103
        'SubmitButtonText' => 'Varchar',
104
        'ClearButtonText' => 'Varchar',
105
        'OnCompleteMessage' => 'HTMLText',
106
        'ShowClearButton' => 'Boolean',
107
        'DisableSaveSubmissions' => 'Boolean',
108
        'EnableLiveValidation' => 'Boolean',
109
        'DisplayErrorMessagesAtTop' => 'Boolean',
110
        'DisableAuthenicatedFinishAction' => 'Boolean',
111
        'DisableCsrfSecurityToken' => 'Boolean'
112
    ];
113
114
    /**
115
     * @var array Default values of variables when this page is created
116
     */
117
    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...
118
        'Content' => '$UserDefinedForm',
119
        'DisableSaveSubmissions' => 0,
120
        'OnCompleteMessage' => '<p>Thanks, we\'ve received your submission.</p>'
121
    ];
122
123
    /**
124
     * @var array
125
     */
126
    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...
127
        'Submissions' => SubmittedForm::class,
128
        'EmailRecipients' => EmailRecipient::class
129
    ];
130
131
    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...
132
        'EmailRecipients',
133
    ];
134 2
135
    /**
136 2
     * @var array
137
     * @config
138 2
     */
139
    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...
140
        'ErrorContainerID' => 'Text'
141
    ];
142 2
143 2
    /**
144 2
     * Error container selector which matches the element for grouped messages
145
     *
146
     * @var string
147 2
     * @config
148 2
     */
149 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...
150 2
151
    /**
152 2
     * The configuration used to determine whether a confirmation message is to
153
     * appear when navigating away from a partially completed form.
154 2
     *
155 2
     * @var boolean
156
     * @config
157
     */
158 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...
159 2
160 2
    /**
161 2
     * @var bool
162 2
     * @config
163
     */
164
    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...
165 2
166 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...
167 2
168 2
    /**
169
     * Temporary storage of field ids when the form is duplicated.
170 2
     * Example layout: array('EditableCheckbox3' => 'EditableCheckbox14')
171
     * @var array
172 2
     */
173 2
    protected $fieldsFromTo = [];
174 2
175
    /**
176 2
     * @return FieldList
177 2
     */
178 2
    public function getCMSFields()
179
    {
180
        Requirements::css(
181
            ModuleLoader::getModule('silverstripe/userforms')
182
                ->getRelativeResourcePath('client/dist/styles/userforms-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