Passed
Pull Request — master (#688)
by Robbie
03:46
created

UserFormFieldEditorExtension   A

Complexity

Total Complexity 28

Size/Duplication

Total Lines 251
Duplicated Lines 2.39 %

Importance

Changes 0
Metric Value
dl 6
loc 251
rs 10
c 0
b 0
f 0
wmc 28

9 Methods

Rating   Name   Duplication   Size   Complexity  
B createInitialFormStep() 0 33 6
A getFieldEditorGrid() 0 53 3
B onAfterDuplicate() 5 28 6
A onAfterWrite() 0 3 1
A onAfterUnpublish() 0 4 2
A onAfterRevertToLive() 0 5 2
B onAfterPublish() 0 27 4
A updateCMSFields() 0 8 1
A isModifiedOnDraft() 0 5 3

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php
2
3
namespace SilverStripe\UserForms\Extension;
4
5
use SilverStripe\Forms\FieldList;
6
use SilverStripe\Forms\Tab;
7
use SilverStripe\Forms\GridField\GridField;
8
use SilverStripe\Forms\GridField\GridFieldButtonRow;
9
use SilverStripe\Forms\GridField\GridFieldConfig;
10
use SilverStripe\Forms\GridField\GridFieldEditButton;
11
use SilverStripe\Forms\GridField\GridFieldDeleteAction;
12
use SilverStripe\Forms\GridField\GridFieldDetailForm;
13
use SilverStripe\Forms\GridField\GridFieldToolbarHeader;
14
use SilverStripe\ORM\DataExtension;
15
use SilverStripe\UserForms\Form\GridFieldAddClassesButton;
16
use SilverStripe\UserForms\Model\EditableFormField;
17
use SilverStripe\UserForms\Model\EditableFormField\EditableFieldGroup;
18
use SilverStripe\UserForms\Model\EditableFormField\EditableFieldGroupEnd;
19
use SilverStripe\UserForms\Model\EditableFormField\EditableFormStep;
20
use SilverStripe\UserForms\Model\EditableFormField\EditableTextField;
21
use SilverStripe\Versioned\Versioned;
22
use SilverStripe\View\Requirements;
23
use Symbiote\GridFieldExtensions\GridFieldEditableColumns;
24
use Symbiote\GridFieldExtensions\GridFieldOrderableRows;
25
26
/**
27
 * @package userforms
28
 */
29
class UserFormFieldEditorExtension extends DataExtension
30
{
31
    /**
32
     * @var array
33
     */
34
    private static $has_many = array(
0 ignored issues
show
introduced by
The private property $has_many is not used, and could be removed.
Loading history...
35
        'Fields' => EditableFormField::class
36
    );
37
38
    private static $owns = [
0 ignored issues
show
introduced by
The private property $owns is not used, and could be removed.
Loading history...
39
        'Fields'
40
    ];
41
42
    private static $cascade_deletes = [
0 ignored issues
show
introduced by
The private property $cascade_deletes is not used, and could be removed.
Loading history...
43
        'Fields'
44
    ];
45
46
    /**
47
     * Adds the field editor to the page.
48
     *
49
     * @return FieldList
50
     */
51
    public function updateCMSFields(FieldList $fields)
52
    {
53
        $fieldEditor = $this->getFieldEditorGrid();
54
55
        $fields->insertAfter(new Tab('FormFields', _t(__CLASS__.'.FORMFIELDS', 'Form Fields')), 'Main');
0 ignored issues
show
Bug introduced by
'Main' of type string is incompatible with the type SilverStripe\Forms\FormField expected by parameter $item of SilverStripe\Forms\FieldList::insertAfter(). ( Ignorable by Annotation )

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

55
        $fields->insertAfter(new Tab('FormFields', _t(__CLASS__.'.FORMFIELDS', 'Form Fields')), /** @scrutinizer ignore-type */ 'Main');
Loading history...
56
        $fields->addFieldToTab('Root.FormFields', $fieldEditor);
57
58
        return $fields;
59
    }
60
61
    /**
62
     * Gets the field editor, for adding and removing EditableFormFields.
63
     *
64
     * @return GridField
65
     */
66
    public function getFieldEditorGrid()
67
    {
68
        Requirements::javascript('silverstripe/userforms:client/dist/js/userforms-cms.js');
69
70
        $fields = $this->owner->Fields();
71
72
        $this->createInitialFormStep(true);
73
74
        $editableColumns = new GridFieldEditableColumns();
75
        $fieldClasses = singleton(EditableFormField::class)->getEditableFieldClasses();
76
        $editableColumns->setDisplayFields([
77
            'ClassName' => function ($record, $column, $grid) use ($fieldClasses) {
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

77
            'ClassName' => function ($record, $column, /** @scrutinizer ignore-unused */ $grid) use ($fieldClasses) {

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...
78
                if ($record instanceof EditableFormField) {
79
                    return $record->getInlineClassnameField($column, $fieldClasses);
80
                }
81
            },
82
            'Title' => 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

82
            'Title' => 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...
83
                if ($record instanceof EditableFormField) {
84
                    return $record->getInlineTitleField($column);
85
                }
86
            }
87
        ]);
88
89
        $config = GridFieldConfig::create()
90
            ->addComponents(
91
                $editableColumns,
92
                new GridFieldButtonRow(),
93
                (new GridFieldAddClassesButton(EditableTextField::class))
0 ignored issues
show
Bug introduced by
SilverStripe\UserForms\M...ditableTextField::class of type string is incompatible with the type array expected by parameter $classes of SilverStripe\UserForms\F...esButton::__construct(). ( Ignorable by Annotation )

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

93
                (new GridFieldAddClassesButton(/** @scrutinizer ignore-type */ EditableTextField::class))
Loading history...
94
                    ->setButtonName(_t(__CLASS__.'.ADD_FIELD', 'Add Field'))
95
                    ->setButtonClass('btn-primary'),
96
                (new GridFieldAddClassesButton(EditableFormStep::class))
97
                    ->setButtonName(_t(__CLASS__.'.ADD_PAGE_BREAK', 'Add Page Break'))
98
                    ->setButtonClass('btn-secondary'),
99
                (new GridFieldAddClassesButton([EditableFieldGroup::class, EditableFieldGroupEnd::class]))
100
                    ->setButtonName(_t(__CLASS__.'.ADD_FIELD_GROUP', 'Add Field Group'))
101
                    ->setButtonClass('btn-secondary'),
102
                $editButton = new GridFieldEditButton(),
103
                new GridFieldDeleteAction(),
104
                new GridFieldToolbarHeader(),
105
                new GridFieldOrderableRows('Sort'),
106
                new GridFieldDetailForm()
107
            );
108
109
        $editButton->removeExtraClass('grid-field__icon-action--hidden-on-hover');
110
111
        $fieldEditor = GridField::create(
112
            'Fields',
0 ignored issues
show
Bug introduced by
'Fields' 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

112
            /** @scrutinizer ignore-type */ 'Fields',
Loading history...
113
            '',
114
            $fields,
115
            $config
116
        )->addExtraClass('uf-field-editor');
117
118
        return $fieldEditor;
119
    }
120
121
    /**
122
     * A UserForm must have at least one step.
123
     * If no steps exist, create an initial step, and put all fields inside it.
124
     *
125
     * @param bool $force
126
     * @return void
127
     */
128
    public function createInitialFormStep($force = false)
129
    {
130
        // Only invoke once saved
131
        if (!$this->owner->exists()) {
132
            return;
133
        }
134
135
        // Check if first field is a step
136
        $fields = $this->owner->Fields();
137
        $firstField = $fields->first();
138
        if ($firstField instanceof EditableFormStep) {
139
            return;
140
        }
141
142
        // Don't create steps on write if there are no formfields, as this
143
        // can create duplicate first steps during publish of new records
144
        if (!$force && !$firstField) {
145
            return;
146
        }
147
148
        // Re-apply sort to each field starting at 2
149
        $next = 2;
150
        foreach ($fields as $field) {
151
            $field->Sort = $next++;
152
            $field->write();
153
        }
154
155
        // Add step
156
        $step = EditableFormStep::create();
157
        $step->Title = _t('SilverStripe\\UserForms\\Model\\EditableFormField\\EditableFormStep.TITLE_FIRST', 'First Page');
158
        $step->Sort = 1;
159
        $step->write();
160
        $fields->add($step);
161
    }
162
163
    /**
164
     * Ensure that at least one page exists at the start
165
     */
166
    public function onAfterWrite()
167
    {
168
        $this->createInitialFormStep();
169
    }
170
171
    /**
172
     * Remove any orphaned child records on publish
173
     */
174
    public function onAfterPublish()
175
    {
176
        // store IDs of fields we've published
177
        $seenIDs = [];
178
        foreach ($this->owner->Fields() as $field) {
179
            // store any IDs of fields we publish so we don't unpublish them
180
            $seenIDs[] = $field->ID;
181
            $field->doPublish(Versioned::DRAFT, Versioned::LIVE);
182
            $field->destroy();
183
        }
184
185
        // fetch any orphaned live records
186
        $live = Versioned::get_by_stage(EditableFormField::class, Versioned::LIVE)
187
            ->filter([
188
                'ParentID' => $this->owner->ID,
189
            ]);
190
191
        if (!empty($seenIDs)) {
192
            $live = $live->exclude([
193
                'ID' => $seenIDs,
194
            ]);
195
        }
196
197
        // delete orphaned records
198
        foreach ($live as $field) {
199
            $field->deleteFromStage(Versioned::LIVE);
200
            $field->destroy();
201
        }
202
    }
203
204
    /**
205
     * Remove all fields from the live stage when unpublishing the page
206
     */
207
    public function onAfterUnpublish()
208
    {
209
        foreach ($this->owner->Fields() as $field) {
210
            $field->deleteFromStage(Versioned::LIVE);
211
        }
212
    }
213
214
    /**
215
     * When duplicating a UserDefinedForm, duplicate all of its fields and display rules
216
     *
217
     * @see \SilverStripe\ORM\DataObject::duplicate
218
     * @param \SilverStripe\ORM\DataObject $oldPage
219
     * @param bool $doWrite
220
     * @param string $manyMany
221
     * @return \SilverStripe\ORM\DataObject
222
     */
223
    public function onAfterDuplicate($oldPage, $doWrite, $manyMany)
0 ignored issues
show
Unused Code introduced by
The parameter $doWrite 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

223
    public function onAfterDuplicate($oldPage, /** @scrutinizer ignore-unused */ $doWrite, $manyMany)

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...
Unused Code introduced by
The parameter $manyMany 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

223
    public function onAfterDuplicate($oldPage, $doWrite, /** @scrutinizer ignore-unused */ $manyMany)

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...
224
    {
225
        // List of EditableFieldGroups, where the key of the array is the ID of the old end group
226
        $fieldGroups = [];
227
        foreach ($oldPage->Fields() as $field) {
228
            $newField = $field->duplicate(false);
229
            $newField->ParentID = $this->owner->ID;
230
            $newField->ParentClass = $this->owner->ClassName;
231
            $newField->Version = 0;
232
            $newField->write();
233
234
            // If we encounter a group start, record it for later use
235
            if ($field instanceof EditableFieldGroup) {
236
                $fieldGroups[$field->EndID] = $newField;
0 ignored issues
show
Bug Best Practice introduced by
The property EndID does not exist on SilverStripe\UserForms\M...ield\EditableFieldGroup. Since you implemented __get, consider adding a @property annotation.
Loading history...
237
            }
238
239
            // If we encounter an end group, link it back to the group start
240
            if ($field instanceof EditableFieldGroupEnd && isset($fieldGroups[$field->ID])) {
241
                $groupStart = $fieldGroups[$field->ID];
242
                $groupStart->EndID = $newField->ID;
243
                $groupStart->write();
244
            }
245
246 View Code Duplication
            foreach ($field->DisplayRules() as $customRule) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
247
                $newRule = $customRule->duplicate(false);
248
                $newRule->ParentID = $newField->ID;
249
                $newRule->Version = 0;
250
                $newRule->write();
251
            }
252
        }
253
    }
254
255
    /**
256
     * Checks child fields to see if any are modified in draft as well. The owner of this extension will still
257
     * use the Versioned method to determine its own status.
258
     *
259
     * @see Versioned::isModifiedOnDraft
260
     *
261
     * @return boolean|null
262
     */
263
    public function isModifiedOnDraft()
264
    {
265
        foreach ($this->owner->Fields() as $field) {
266
            if ($field->isModifiedOnDraft()) {
267
                return true;
268
            }
269
        }
270
    }
271
272
    /**
273
     * @see Versioned::doRevertToLive
274
     */
275
    public function onAfterRevertToLive()
276
    {
277
        foreach ($this->owner->Fields() as $field) {
278
            $field->copyVersionToStage(Versioned::LIVE, Versioned::DRAFT, false);
279
            $field->writeWithoutVersion();
280
        }
281
    }
282
}
283