Completed
Push — master ( d16692...2e7378 )
by Damian
19:06
created

EditableFormField   F

Complexity

Total Complexity 119

Size/Duplication

Total Lines 960
Duplicated Lines 2.4 %

Coupling/Cohesion

Components 3
Dependencies 36

Test Coverage

Coverage 35.62%

Importance

Changes 0
Metric Value
wmc 119
lcom 3
cbo 36
dl 23
loc 960
ccs 135
cts 379
cp 0.3562
rs 1.0434
c 0
b 0
f 0

46 Methods

Rating   Name   Duplication   Size   Complexity  
A setReadonly() 0 4 1
A isReadonly() 0 4 1
C getCMSFields() 0 87 9
A getDisplayRuleFields() 0 72 2
B onBeforeWrite() 0 19 5
A generateName() 0 13 2
A getSetsOwnError() 0 4 1
A canDelete() 0 4 1
C canEdit() 0 24 10
A canView() 0 9 3
A canCreate() 10 11 2
A getCanCreateContext() 13 14 4
A canPublish() 0 4 1
A canUnpublish() 0 4 1
B doPublish() 0 25 4
A doDeleteFromStage() 0 12 2
A isNew() 0 12 3
A getIsModifiedOnStage() 0 12 3
A getSettings() 0 5 2
A setSettings() 0 5 1
A setSetting() 0 8 1
A setAllowedCss() 0 8 4
A getSetting() 0 12 4
A getIcon() 0 4 1
A getHasAddableOptions() 0 4 1
A showExtraOptions() 0 4 1
A getEscapedTitle() 0 4 1
C getFieldNumber() 0 31 11
A getCMSTitle() 0 4 1
A getFieldName() 0 5 2
A getSettingName() 0 7 1
A getFieldValidationOptions() 0 12 1
A getFormField() 0 4 1
A doUpdateFormField() 0 6 1
B updateFormField() 0 30 5
A getSubmittedFormField() 0 4 1
A showInReports() 0 4 1
A getErrorMessage() 0 10 3
B migrateSettings() 0 15 6
A getInlineClassnameField() 0 4 1
A getInlineTitleField() 0 6 1
A getSelectorHolder() 0 4 1
A getSelectorField() 0 4 1
C getEditableFieldClasses() 0 28 7
A getCMSValidator() 0 5 1
A EffectiveDisplayRules() 0 7 2

How to fix   Duplicated Code    Complexity   

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:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like EditableFormField often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use EditableFormField, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
use SilverStripe\Forms\SegmentField;
4
5
/**
6
 * Represents the base class of a editable form field
7
 * object like {@link EditableTextField}.
8
 *
9
 * @package userforms
10
 *
11
 * @property string $Name
12
 * @property string $Title
13
 * @property string $Default
14
 * @property int $Sort
15
 * @property bool $Required
16
 * @property string $CustomErrorMessage
17
 * @method UserDefinedForm Parent() Parent page
18
 * @method DataList DisplayRules() List of EditableCustomRule objects
19
 */
20
class EditableFormField extends DataObject
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
21
{
22
23
    /**
24
     * Set to true to hide from class selector
25
     *
26
     * @config
27
     * @var bool
28
     */
29
    private static $hidden = false;
0 ignored issues
show
Unused Code introduced by
The property $hidden 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...
30
31
    /**
32
     * Define this field as abstract (not inherited)
33
     *
34
     * @config
35
     * @var bool
36
     */
37
    private static $abstract = true;
0 ignored issues
show
Unused Code introduced by
The property $abstract 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...
38
39
    /**
40
     * Flag this field type as non-data (e.g. literal, header, html)
41
     *
42
     * @config
43
     * @var bool
44
     */
45
    private static $literal = false;
0 ignored issues
show
Unused Code introduced by
The property $literal 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...
46
47
    /**
48
     * Default sort order
49
     *
50
     * @config
51
     * @var string
52
     */
53
    private static $default_sort = '"Sort"';
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $default_sort 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...
54
55
    /**
56
     * A list of CSS classes that can be added
57
     *
58
     * @var array
59
     */
60
    public static $allowed_css = array();
61
62
    /**
63
     * @config
64
     * @var array
65
     */
66
    private static $summary_fields = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $summary_fields is not used and could be removed.

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

Loading history...
67
        'Title'
68
    );
69
70
    /**
71
     * @config
72
     * @var array
73
     */
74
    private static $db = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $db is not used and could be removed.

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

Loading history...
75
        "Name" => "Varchar",
76
        "Title" => "Varchar(255)",
77
        "Default" => "Varchar(255)",
78
        "Sort" => "Int",
79
        "Required" => "Boolean",
80
        "CustomErrorMessage" => "Varchar(255)",
81
82
        "CustomRules" => "Text", // @deprecated from 2.0
83
        "CustomSettings" => "Text", // @deprecated from 2.0
84
        "Migrated" => "Boolean", // set to true when migrated
85
86
        "ExtraClass" => "Text", // from CustomSettings
87
        "RightTitle" => "Varchar(255)", // from CustomSettings
88
        "ShowOnLoad" => "Boolean(1)", // from CustomSettings
89
        "ShowInSummary" => "Boolean",
90
    );
91
92
    private static $defaults = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $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...
93
        'ShowOnLoad' => true,
94
    );
95
96
97
    /**
98
     * @config
99
     * @var array
100
     */
101
    private static $has_one = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $has_one is not used and could be removed.

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

Loading history...
102
        "Parent" => "UserDefinedForm",
103
    );
104
105
    /**
106
     * Built in extensions required
107
     *
108
     * @config
109
     * @var array
110
     */
111
    private static $extensions = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $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...
112
        "Versioned('Stage', 'Live')"
113
    );
114
115
    /**
116
     * @config
117
     * @var array
118
     */
119
    private static $has_many = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $has_many is not used and could be removed.

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

Loading history...
120
        "DisplayRules" => "EditableCustomRule.Parent" // from CustomRules
121
    );
122
123
    /**
124
     * @var bool
125
     */
126
    protected $readonly;
127
128
    /**
129
     * Set the visibility of an individual form field
130
     *
131
     * @param bool
132
     */
133 1
    public function setReadonly($readonly = true)
134
    {
135 1
        $this->readonly = $readonly;
136 1
    }
137
138
    /**
139
     * Returns whether this field is readonly
140
     *
141
     * @return bool
142
     */
143 1
    private function isReadonly()
144
    {
145 1
        return $this->readonly;
146
    }
147
148
    /**
149
     * @return FieldList
150
     */
151 10
    public function getCMSFields()
152
    {
153 10
        $fields = new FieldList(new TabSet('Root'));
154
155
        // Main tab
156
        $fields->addFieldsToTab(
157 1
            'Root.Main',
158
            array(
159
                ReadonlyField::create(
160 10
                    'Type',
161
                    _t('EditableFormField.TYPE', 'Type'),
162
                    $this->i18n_singular_name()
163
                ),
164 10
                CheckboxField::create('ShowInSummary', _t('EditableFormField.SHOWINSUMMARY', 'Show in summary gridfield')),
165
                LiteralField::create(
166
                    'MergeField',
167
                    _t(
168
                        'EditableFormField.MERGEFIELDNAME',
169
                        '<div class="field readonly">' .
170
                            '<label class="left">' . _t('EditableFormField.MERGEFIELDNAME', 'Merge field') . '</label>' .
171
                            '<div class="middleColumn">' .
172
                                '<span class="readonly">$' . $this->Name . '</span>' .
173
                            '</div>' .
174
                        '</div>'
175
                    )
176
                ),
177
                TextField::create('Title', _t('EditableFormField.TITLE', 'Title')),
178
                TextField::create('Default', _t('EditableFormField.DEFAULT', 'Default value')),
179
                TextField::create('RightTitle', _t('EditableFormField.RIGHTTITLE', 'Right title')),
180
                SegmentField::create('Name', _t('EditableFormField.NAME', 'Name'))->setModifiers(array(
181
                    UnderscoreSegmentFieldModifier::create()->setDefault('FieldName'),
182
                    DisambiguationSegmentFieldModifier::create(),
183
                ))->setPreview($this->Name)
184
            )
185
        );
186
        $fields->fieldByName('Root.Main')->setTitle(_t('SiteTree.TABMAIN', 'Main'));
187
188
        // Custom settings
189
        if (!empty(self::$allowed_css)) {
190
            $cssList = array();
191
            foreach (self::$allowed_css as $k => $v) {
192
                if (!is_array($v)) {
193
                    $cssList[$k]=$v;
194
                } elseif ($k === $this->ClassName) {
195
                    $cssList = array_merge($cssList, $v);
196
                }
197
            }
198
199
            $fields->addFieldToTab('Root.Main',
200
                DropdownField::create(
201
                    'ExtraClass',
202
                    _t('EditableFormField.EXTRACLASS_TITLE', 'Extra Styling/Layout'),
203
                    $cssList
204
                )->setDescription(_t(
205
                    'EditableFormField.EXTRACLASS_SELECT',
206
                    'Select from the list of allowed styles'
207
                ))
208
            );
209
        } else {
210
            $fields->addFieldToTab('Root.Main',
211
                TextField::create(
212
                    'ExtraClass',
213
                    _t('EditableFormField.EXTRACLASS_Title', 'Extra CSS classes')
214
                )->setDescription(_t(
215
                    'EditableFormField.EXTRACLASS_MULTIPLE',
216
                    'Separate each CSS class with a single space'
217
                ))
218
            );
219
        }
220
221
        // Validation
222
        $validationFields = $this->getFieldValidationOptions();
223
        if ($validationFields && $validationFields->count()) {
224
            $fields->addFieldsToTab('Root.Validation', $validationFields);
0 ignored issues
show
Documentation introduced by
$validationFields is of type object<FieldList>, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
225
            $fields->fieldByName('Root.Validation')->setTitle(_t('EditableFormField.VALIDATION', 'Validation'));
226
        }
227
228
        // Add display rule fields
229
        $displayFields = $this->getDisplayRuleFields();
230
        if ($displayFields && $displayFields->count()) {
231
            $fields->addFieldsToTab('Root.DisplayRules', $displayFields);
0 ignored issues
show
Documentation introduced by
$displayFields is of type object<FieldList>, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
232
        }
233
234
        $this->extend('updateCMSFields', $fields);
235
236
        return $fields;
237
    }
238
239
    /**
240
     * Return fields to display on the 'Display Rules' tab
241
     *
242
     * @return FieldList
243
     */
244
    protected function getDisplayRuleFields()
245
    {
246
        // Check display rules
247
        if ($this->Required) {
248
            return new FieldList(
249
                LabelField::create(_t(
250
                    'EditableFormField.DISPLAY_RULES_DISABLED',
251
                    'Display rules are not enabled for required fields. ' .
252
                    'Please uncheck "Is this field Required?" under "Validation" to re-enable.'
253
                ))->addExtraClass('message warning')
254
            );
255
        }
256
        $self = $this;
257
        $allowedClasses = array_keys($this->getEditableFieldClasses(false));
258
        $editableColumns = new GridFieldEditableColumns();
259
        $editableColumns->setDisplayFields(array(
260
            'Display' => '',
261
            'ConditionFieldID' => function ($record, $column, $grid) use ($allowedClasses, $self) {
0 ignored issues
show
Unused Code introduced by
The parameter $grid is not used and could be removed.

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

Loading history...
262
                return DropdownField::create(
263
                    $column,
264
                    '',
265
                    EditableFormField::get()
266
                        ->filter(array(
267
                            'ParentID' => $self->ParentID,
0 ignored issues
show
Documentation introduced by
The property ParentID does not exist on object<EditableFormField>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
268
                            'ClassName' => $allowedClasses
269
                        ))
270
                        ->exclude(array(
271
                            'ID' => $self->ID
272
                        ))
273
                        ->map('ID', 'Title')
274
                    );
275
            },
276
            'ConditionOption' => function ($record, $column, $grid) {
0 ignored issues
show
Unused Code introduced by
The parameter $grid is not used and could be removed.

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

Loading history...
277
                $options = Config::inst()->get('EditableCustomRule', 'condition_options');
278
                return DropdownField::create($column, '', $options);
279
            },
280
            'FieldValue' => function ($record, $column, $grid) {
0 ignored issues
show
Unused Code introduced by
The parameter $grid is not used and could be removed.

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

Loading history...
281
                return TextField::create($column);
282
            },
283
            'ParentID' => function ($record, $column, $grid) use ($self) {
0 ignored issues
show
Unused Code introduced by
The parameter $grid is not used and could be removed.

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

Loading history...
284
                return HiddenField::create($column, '', $self->ID);
285
            }
286
        ));
287
288
        // Custom rules
289
        $customRulesConfig = GridFieldConfig::create()
290
            ->addComponents(
291
                $editableColumns,
292
                new GridFieldButtonRow(),
293
                new GridFieldToolbarHeader(),
294
                new GridFieldAddNewInlineButton(),
295
                new GridFieldDeleteAction()
296
            );
297
298
        return new FieldList(
299
            CheckboxField::create(
300
                'ShowOnLoad',
301
                _t('EditableFormField.SHOWONLOAD_TITLE', 'Show on load')
302
            )->setDescription(
303
                _t(
304
                    'EditableFormField.SHOWONLOAD',
305
                    'Initial visibility before processing these rules'
306
                )
307
            ),
308
            GridField::create(
309
                'DisplayRules',
310
                _t('EditableFormField.CUSTOMRULES', 'Custom Rules'),
311
                $this->DisplayRules(),
312
                $customRulesConfig
313
            )
314
        );
315
    }
316
317 39
    public function onBeforeWrite()
318
    {
319 39
        parent::onBeforeWrite();
320
321
        // Set a field name.
322 39
        if (!$this->Name) {
323
            // New random name
324 25
            $this->Name = $this->generateName();
325 39
        } elseif ($this->Name === 'Field') {
326
            throw new ValidationException('Field name cannot be "Field"');
327
        }
328
329 39
        if (!$this->Sort && $this->ParentID) {
0 ignored issues
show
Documentation introduced by
The property ParentID does not exist on object<EditableFormField>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
330 39
            $parentID = $this->ParentID;
0 ignored issues
show
Documentation introduced by
The property ParentID does not exist on object<EditableFormField>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
331 39
            $this->Sort = EditableFormField::get()
332 39
                ->filter('ParentID', $parentID)
333 39
                ->max('Sort') + 1;
334 39
        }
335 39
    }
336
337
    /**
338
     * Generate a new non-conflicting Name value
339
     *
340
     * @return string
341
     */
342 25
    protected function generateName()
343
    {
344
        do {
345
            // Generate a new random name after this class
346 25
            $class = get_class($this);
347 25
            $entropy = substr(sha1(uniqid()), 0, 5);
348 25
            $name = "{$class}_{$entropy}";
349
350
            // Check if it conflicts
351 25
            $exists = EditableFormField::get()->filter('Name', $name)->count() > 0;
352 25
        } while ($exists);
353 25
        return $name;
354
    }
355
356
    /**
357
     * Flag indicating that this field will set its own error message via data-msg='' attributes
358
     *
359
     * @return bool
360
     */
361
    public function getSetsOwnError()
362
    {
363
        return false;
364
    }
365
366
    /**
367
     * Return whether a user can delete this form field
368
     * based on whether they can edit the page
369
     *
370
     * @param Member $member
371
     * @return bool
372
     */
373 1
    public function canDelete($member = null)
374
    {
375 1
        return $this->canEdit($member);
376
    }
377
378
    /**
379
     * Return whether a user can edit this form field
380
     * based on whether they can edit the page
381
     *
382
     * @param Member $member
383
     * @return bool
384
     */
385 1
    public function canEdit($member = null)
386
    {
387 1
        $parent = $this->Parent();
388 1
        if ($parent && $parent->exists()) {
389 1
            return $parent->canEdit($member) && !$this->isReadonly();
390
        } elseif (!$this->exists() && Controller::has_curr()) {
391
            // This is for GridFieldOrderableRows support as it checks edit permissions on
392
            // singleton of the class. Allows editing of User Defined Form pages by
393
            // 'Content Authors' and those with permission to edit the UDF page. (ie. CanEditType/EditorGroups)
394
            // This is to restore User Forms 2.x backwards compatibility.
395
            $controller = Controller::curr();
396
            if ($controller && $controller instanceof CMSPageEditController) {
397
                $parent = $controller->getRecord($controller->currentPageID());
398
                // Only allow this behaviour on pages using UserFormFieldEditorExtension, such
399
                // as UserDefinedForm page type.
400
                if ($parent && $parent->hasExtension('UserFormFieldEditorExtension')) {
401
                    return $parent->canEdit($member);
402
                }
403
            }
404
        }
405
406
        // Fallback to secure admin permissions
407
        return parent::canEdit($member);
0 ignored issues
show
Bug Compatibility introduced by
The expression parent::canEdit($member); of type boolean|string|null adds the type string to the return on line 407 which is incompatible with the return type documented by EditableFormField::canEdit of type boolean.
Loading history...
408
    }
409
410
    /**
411
     * Return whether a user can view this form field
412
     * based on whether they can view the page, regardless of the ReadOnly status of the field
413
     *
414
     * @param Member $member
415
     * @return bool
416
     */
417 1
    public function canView($member = null)
418
    {
419 1
        $parent = $this->Parent();
420 1
        if ($parent && $parent->exists()) {
421 1
            return $parent->canView($member);
422
        }
423
424
        return true;
425
    }
426
427
    /**
428
     * Return whether a user can create an object of this type
429
     *
430
     * @param Member $member
431
     * @param array $context Virtual parameter to allow context to be passed in to check
0 ignored issues
show
Bug introduced by
There is no parameter named $context. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
432
     * @return bool
433
     */
434 3 View Code Duplication
    public function canCreate($member = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
435
    {
436
        // Check parent page
437 3
        $parent = $this->getCanCreateContext(func_get_args());
438 3
        if ($parent) {
439
            return $parent->canEdit($member);
440
        }
441
442
        // Fall back to secure admin permissions
443 3
        return parent::canCreate($member);
444
    }
445
446
    /**
447
     * Helper method to check the parent for this object
448
     *
449
     * @param array $args List of arguments passed to canCreate
450
     * @return SiteTree Parent page instance
451
     */
452 3 View Code Duplication
    protected function getCanCreateContext($args)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
453
    {
454
        // Inspect second parameter to canCreate for a 'Parent' context
455 3
        if (isset($args[1]['Parent'])) {
456
            return $args[1]['Parent'];
457
        }
458
        // Hack in currently edited page if context is missing
459 3
        if (Controller::has_curr() && Controller::curr() instanceof CMSMain) {
460
            return Controller::curr()->currentPage();
461
        }
462
463
        // No page being edited
464 3
        return null;
465
    }
466
467
    /**
468
     * Check if can publish
469
     *
470
     * @param Member $member
471
     * @return bool
472
     */
473
    public function canPublish($member = null)
474
    {
475
        return $this->canEdit($member);
476
    }
477
478
    /**
479
     * Check if can unpublish
480
     *
481
     * @param Member $member
482
     * @return bool
483
     */
484
    public function canUnpublish($member = null)
485
    {
486
        return $this->canDelete($member);
487
    }
488
489
    /**
490
     * Publish this Form Field to the live site
491
     *
492
     * Wrapper for the {@link Versioned} publish function
493
     */
494 9
    public function doPublish($fromStage, $toStage, $createNewVersion = false)
495
    {
496 9
        $this->publish($fromStage, $toStage, $createNewVersion);
0 ignored issues
show
Bug introduced by
The method publish() does not exist on EditableFormField. Did you maybe mean canPublish()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
497
498 9
		$seenIDs = array();
499
500
		// Don't forget to publish the related custom rules...
501 9
		foreach ($this->DisplayRules() as $rule) {
502 1
			$seenIDs[] = $rule->ID;
503 1
			$rule->doPublish($fromStage, $toStage, $createNewVersion);
504 1
			$rule->destroy();
505 9
		}
506
507
		// remove any orphans from the "fromStage"
508 9
        $rules = Versioned::get_by_stage('EditableCustomRule', $toStage)
509 9
            ->filter('ParentID', $this->ID);
510
511 9
		if (!empty($seenIDs)) {
512 1
            $rules = $rules->exclude('ID', $seenIDs);
513 1
        }
514
515 9
		foreach ($rules as $rule) {
516 1
		    $rule->deleteFromStage($toStage);
517 9
		}
518 9
	}
519
520
    /**
521
     * Delete this field from a given stage
522
     *
523
     * Wrapper for the {@link Versioned} deleteFromStage function
524
     */
525 1
    public function doDeleteFromStage($stage)
526
    {
527
        // Remove custom rules in this stage
528 1
        $rules = Versioned::get_by_stage('EditableCustomRule', $stage)
529 1
            ->filter('ParentID', $this->ID);
530 1
        foreach ($rules as $rule) {
531
            $rule->deleteFromStage($stage);
532 1
        }
533
534
        // Remove record
535 1
        $this->deleteFromStage($stage);
0 ignored issues
show
Bug introduced by
The method deleteFromStage() does not exist on EditableFormField. Did you maybe mean doDeleteFromStage()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
536 1
    }
537
538
    /**
539
     * checks wether record is new, copied from Sitetree
540
     */
541
    public function isNew()
542
    {
543
        if (empty($this->ID)) {
544
            return true;
545
        }
546
547
        if (is_numeric($this->ID)) {
548
            return false;
549
        }
550
551
        return stripos($this->ID, 'new') === 0;
552
    }
553
554
    /**
555
     * checks if records is changed on stage
556
     * @return boolean
557
     */
558
    public function getIsModifiedOnStage()
559
    {
560
        // new unsaved fields could be never be published
561
        if ($this->isNew()) {
562
            return false;
563
        }
564
565
        $stageVersion = Versioned::get_versionnumber_by_stage('EditableFormField', 'Stage', $this->ID);
566
        $liveVersion = Versioned::get_versionnumber_by_stage('EditableFormField', 'Live', $this->ID);
567
568
        return ($stageVersion && $stageVersion != $liveVersion);
569
    }
570
571
    /**
572
     * @deprecated since version 4.0
573
     */
574
    public function getSettings()
575
    {
576
        Deprecation::notice('4.0', 'getSettings is deprecated');
577
        return (!empty($this->CustomSettings)) ? unserialize($this->CustomSettings) : array();
0 ignored issues
show
Documentation introduced by
The property CustomSettings does not exist on object<EditableFormField>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
578
    }
579
580
    /**
581
     * @deprecated since version 4.0
582
     */
583
    public function setSettings($settings = array())
584
    {
585
        Deprecation::notice('4.0', 'setSettings is deprecated');
586
        $this->CustomSettings = serialize($settings);
0 ignored issues
show
Documentation introduced by
The property CustomSettings does not exist on object<EditableFormField>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
587
    }
588
589
    /**
590
     * @deprecated since version 4.0
591
     */
592
    public function setSetting($key, $value)
593
    {
594
        Deprecation::notice('4.0', "setSetting({$key}) is deprecated");
595
        $settings = $this->getSettings();
0 ignored issues
show
Deprecated Code introduced by
The method EditableFormField::getSettings() has been deprecated with message: since version 4.0

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
596
        $settings[$key] = $value;
597
598
        $this->setSettings($settings);
0 ignored issues
show
Deprecated Code introduced by
The method EditableFormField::setSettings() has been deprecated with message: since version 4.0

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
599
    }
600
601
    /**
602
     * Set the allowed css classes for the extraClass custom setting
603
     *
604
     * @param array The permissible CSS classes to add
605
     */
606
    public function setAllowedCss(array $allowed)
607
    {
608
        if (is_array($allowed)) {
609
            foreach ($allowed as $k => $v) {
610
                self::$allowed_css[$k] = (!is_null($v)) ? $v : $k;
611
            }
612
        }
613
    }
614
615
    /**
616
     * @deprecated since version 4.0
617
     */
618
    public function getSetting($setting)
619
    {
620
        Deprecation::notice("4.0", "getSetting({$setting}) is deprecated");
621
622
        $settings = $this->getSettings();
0 ignored issues
show
Deprecated Code introduced by
The method EditableFormField::getSettings() has been deprecated with message: since version 4.0

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
623
        if (isset($settings) && count($settings) > 0) {
624
            if (isset($settings[$setting])) {
625
                return $settings[$setting];
626
            }
627
        }
628
        return '';
629
    }
630
631
    /**
632
     * Get the path to the icon for this field type, relative to the site root.
633
     *
634
     * @return string
635
     */
636
    public function getIcon()
637
    {
638
        return USERFORMS_DIR . '/images/' . strtolower($this->class) . '.png';
639
    }
640
641
    /**
642
     * Return whether or not this field has addable options
643
     * such as a dropdown field or radio set
644
     *
645
     * @return bool
646
     */
647
    public function getHasAddableOptions()
648
    {
649
        return false;
650
    }
651
652
    /**
653
     * Return whether or not this field needs to show the extra
654
     * options dropdown list
655
     *
656
     * @return bool
657
     */
658
    public function showExtraOptions()
659
    {
660
        return true;
661
    }
662
663
    /**
664
     * Returns the Title for rendering in the front-end (with XML values escaped)
665
     *
666
     * @return string
667
     */
668 18
    public function getEscapedTitle()
669
    {
670 18
        return Convert::raw2xml($this->Title);
671
    }
672
673
    /**
674
     * Find the numeric indicator (1.1.2) that represents it's nesting value
675
     *
676
     * Only useful for fields attached to a current page, and that contain other fields such as pages
677
     * or groups
678
     *
679
     * @return string
680
     */
681
    public function getFieldNumber()
682
    {
683
        // Check if exists
684
        if (!$this->exists()) {
685
            return null;
686
        }
687
        // Check parent
688
        $form = $this->Parent();
689
        if (!$form || !$form->exists() || !($fields = $form->Fields())) {
690
            return null;
691
        }
692
693
        $prior = 0; // Number of prior group at this level
694
        $stack = array(); // Current stack of nested groups, where the top level = the page
695
        foreach ($fields->map('ID', 'ClassName') as $id => $className) {
696
            if ($className === 'EditableFormStep') {
697
                $priorPage = empty($stack) ? $prior : $stack[0];
698
                $stack = array($priorPage + 1);
699
                $prior = 0;
700
            } elseif ($className === 'EditableFieldGroup') {
701
                $stack[] = $prior + 1;
702
                $prior = 0;
703
            } elseif ($className === 'EditableFieldGroupEnd') {
704
                $prior = array_pop($stack);
705
            }
706
            if ($id == $this->ID) {
707
                return implode('.', $stack);
708
            }
709
        }
710
        return null;
711
    }
712
713
    public function getCMSTitle()
714
    {
715
        return $this->i18n_singular_name() . ' (' . $this->Title . ')';
716
    }
717
718
    /**
719
     * @deprecated since version 4.0
720
     */
721
    public function getFieldName($field = false)
722
    {
723
        Deprecation::notice('4.0', "getFieldName({$field}) is deprecated");
724
        return ($field) ? "Fields[".$this->ID."][".$field."]" : "Fields[".$this->ID."]";
725
    }
726
727
    /**
728
     * @deprecated since version 4.0
729
     */
730
    public function getSettingName($field)
731
    {
732
        Deprecation::notice('4.0', "getSettingName({$field}) is deprecated");
733
        $name = $this->getFieldName('CustomSettings');
0 ignored issues
show
Documentation introduced by
'CustomSettings' is of type string, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Deprecated Code introduced by
The method EditableFormField::getFieldName() has been deprecated with message: since version 4.0

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
734
735
        return $name . '[' . $field .']';
736
    }
737
738
    /**
739
     * Append custom validation fields to the default 'Validation'
740
     * section in the editable options view
741
     *
742
     * @return FieldList
743
     */
744
    public function getFieldValidationOptions()
745
    {
746
        $fields = new FieldList(
747
            CheckboxField::create('Required', _t('EditableFormField.REQUIRED', 'Is this field Required?'))
748
                ->setDescription(_t('EditableFormField.REQUIRED_DESCRIPTION', 'Please note that conditional fields can\'t be required')),
749
            TextField::create('CustomErrorMessage', _t('EditableFormField.CUSTOMERROR', 'Custom Error Message'))
750
        );
751
752
        $this->extend('updateFieldValidationOptions', $fields);
753
754
        return $fields;
755
    }
756
757
    /**
758
     * Return a FormField to appear on the front end. Implement on
759
     * your subclass.
760
     *
761
     * @return FormField
762
     */
763
    public function getFormField()
764
    {
765
        user_error("Please implement a getFormField() on your EditableFormClass ". $this->ClassName, E_USER_ERROR);
766
    }
767
768
    /**
769
     * Updates a formfield with extensions
770
     *
771
     * @param FormField $field
772
     */
773 17
    public function doUpdateFormField($field)
774
    {
775 17
        $this->extend('beforeUpdateFormField', $field);
776 17
        $this->updateFormField($field);
777 17
        $this->extend('afterUpdateFormField', $field);
778 17
    }
779
780
    /**
781
     * Updates a formfield with the additional metadata specified by this field
782
     *
783
     * @param FormField $field
784
     */
785 17
    protected function updateFormField($field)
786
    {
787
        // set the error / formatting messages
788 17
        $field->setCustomValidationMessage($this->getErrorMessage()->RAW());
789
790
        // set the right title on this field
791 17
        if ($this->RightTitle) {
0 ignored issues
show
Documentation introduced by
The property RightTitle does not exist on object<EditableFormField>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
792
            // Since this field expects raw html, safely escape the user data prior
793 1
            $field->setRightTitle(Convert::raw2xml($this->RightTitle));
0 ignored issues
show
Documentation introduced by
The property RightTitle does not exist on object<EditableFormField>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
Bug introduced by
It seems like \Convert::raw2xml($this->RightTitle) targeting Convert::raw2xml() can also be of type array; however, FormField::setRightTitle() does only seem to accept string, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
794 1
        }
795
796
        // if this field is required add some
797 17
        if ($this->Required) {
798
            // Required validation can conflict so add the Required validation messages as input attributes
799 3
            $errorMessage = $this->getErrorMessage()->HTML();
800 3
            $field->addExtraClass('requiredField');
801 3
            $field->setAttribute('data-rule-required', 'true');
802 3
            $field->setAttribute('data-msg-required', $errorMessage);
0 ignored issues
show
Bug introduced by
It seems like $errorMessage defined by $this->getErrorMessage()->HTML() on line 799 can also be of type array; however, FormField::setAttribute() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
803
804 3
            if ($identifier = UserDefinedForm::config()->required_identifier) {
805 1
                $title = $field->Title() . " <span class='required-identifier'>". $identifier . "</span>";
806 1
                $field->setTitle($title);
807 1
            }
808 3
        }
809
810
        // if this field has an extra class
811 17
        if ($this->ExtraClass) {
0 ignored issues
show
Documentation introduced by
The property ExtraClass does not exist on object<EditableFormField>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
812
            $field->addExtraClass($this->ExtraClass);
0 ignored issues
show
Documentation introduced by
The property ExtraClass does not exist on object<EditableFormField>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
813
        }
814 17
    }
815
816
    /**
817
     * Return the instance of the submission field class
818
     *
819
     * @return SubmittedFormField
820
     */
821 2
    public function getSubmittedFormField()
822
    {
823 2
        return new SubmittedFormField();
824
    }
825
826
827
    /**
828
     * Show this form field (and its related value) in the reports and in emails.
829
     *
830
     * @return bool
831
     */
832 2
    public function showInReports()
833
    {
834 2
        return true;
835
    }
836
837
    /**
838
     * Return the error message for this field. Either uses the custom
839
     * one (if provided) or the default SilverStripe message
840
     *
841
     * @return Varchar
842
     */
843 17
    public function getErrorMessage()
844
    {
845 17
        $title = strip_tags("'". ($this->Title ? $this->Title : $this->Name) . "'");
846 17
        $standard = sprintf(_t('Form.FIELDISREQUIRED', '%s is required').'.', $title);
847
848
        // only use CustomErrorMessage if it has a non empty value
849 17
        $errorMessage = (!empty($this->CustomErrorMessage)) ? $this->CustomErrorMessage : $standard;
850
851 17
        return DBField::create_field('Varchar', $errorMessage);
852
    }
853
854
    /**
855
     * Invoked by UserFormUpgradeService to migrate settings specific to this field from CustomSettings
856
     * to the field proper
857
     *
858
     * @param array $data Unserialised data
859
     */
860 2
    public function migrateSettings($data)
861
    {
862
        // Map 'Show' / 'Hide' to boolean
863 2
        if (isset($data['ShowOnLoad'])) {
864 2
            $this->ShowOnLoad = $data['ShowOnLoad'] === '' || ($data['ShowOnLoad'] && $data['ShowOnLoad'] !== 'Hide');
0 ignored issues
show
Documentation introduced by
The property ShowOnLoad does not exist on object<EditableFormField>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
865 2
            unset($data['ShowOnLoad']);
866 2
        }
867
868
        // Migrate all other settings
869 2
        foreach ($data as $key => $value) {
870 2
            if ($this->hasField($key)) {
871 2
                $this->setField($key, $value);
872 2
            }
873 2
        }
874 2
    }
875
876
    /**
877
     * Get the formfield to use when editing this inline in gridfield
878
     *
879
     * @param string $column name of column
880
     * @param array $fieldClasses List of allowed classnames if this formfield has a selectable class
881
     * @return FormField
882
     */
883
    public function getInlineClassnameField($column, $fieldClasses)
884
    {
885
        return DropdownField::create($column, false, $fieldClasses);
886
    }
887
888
    /**
889
     * Get the formfield to use when editing the title inline
890
     *
891
     * @param string $column
892
     * @return FormField
893
     */
894
    public function getInlineTitleField($column)
895
    {
896
        return TextField::create($column, false)
897
            ->setAttribute('placeholder', _t('EditableFormField.TITLE', 'Title'))
898
            ->setAttribute('data-placeholder', _t('EditableFormField.TITLE', 'Title'));
899
    }
900
901
    /**
902
     * Get the JS expression for selecting the holder for this field
903
     *
904
     * @return string
905
     */
906 8
    public function getSelectorHolder()
907
    {
908 8
        return "$(\"#{$this->Name}\")";
909
    }
910
911
    /**
912
     * Gets the JS expression for selecting the value for this field
913
     *
914
     * @param EditableCustomRule $rule Custom rule this selector will be used with
915
     * @param bool $forOnLoad Set to true if this will be invoked on load
916
     */
917
    public function getSelectorField(EditableCustomRule $rule, $forOnLoad = false)
918
    {
919
        return "$(\"input[name='{$this->Name}']\")";
920
    }
921
922
923
    /**
924
     * Get the list of classes that can be selected and used as data-values
925
     *
926
     * @param $includeLiterals Set to false to exclude non-data fields
927
     * @return array
928
     */
929 2
    public function getEditableFieldClasses($includeLiterals = true)
930
    {
931 2
        $classes = ClassInfo::getValidSubClasses('EditableFormField');
932
933
        // Remove classes we don't want to display in the dropdown.
934 2
        $editableFieldClasses = array();
935 2
        foreach ($classes as $class) {
936
            // Skip abstract / hidden classes
937 2
            if (Config::inst()->get($class, 'abstract', Config::UNINHERITED) || Config::inst()->get($class, 'hidden')
938 2
            ) {
939 2
                continue;
940
            }
941
942 2
            if (!$includeLiterals && Config::inst()->get($class, 'literal')) {
943
                continue;
944
            }
945
946 2
            $singleton = singleton($class);
947 2
            if (!$singleton->canCreate()) {
948
                continue;
949
            }
950
951 2
            $editableFieldClasses[$class] = $singleton->i18n_singular_name();
952 2
        }
953
954 2
        asort($editableFieldClasses);
955 2
        return $editableFieldClasses;
956
    }
957
958
    /**
959
     * @return EditableFormFieldValidator
960
     */
961
    public function getCMSValidator()
962
    {
963
        return EditableFormFieldValidator::create()
964
            ->setRecord($this);
965
    }
966
967
    /**
968
     * Determine effective display rules for this field.
969
     *
970
     * @return SS_List
971
     */
972 10
    public function EffectiveDisplayRules()
973
    {
974 10
        if ($this->Required) {
975 4
            return new ArrayList();
976
        }
977 10
        return $this->DisplayRules();
978
    }
979
}
980