Completed
Pull Request — master (#581)
by
unknown
23:37
created

EditableFormField::getInlineClassnameField()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 0
cts 2
cp 0
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 2
crap 2
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
     * Set this to true to enable placeholder field for any given class
64
     * @config
65
     * @var bool
66
     */
67
    private static $has_placeholder = false;
0 ignored issues
show
Unused Code introduced by
The property $has_placeholder 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...
68
69
    /**
70
     * @config
71
     * @var array
72
     */
73
    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...
74
        'Title'
75
    );
76
77
    /**
78
     * @config
79
     * @var array
80
     */
81
    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...
82
        "Name" => "Varchar",
83
        "Title" => "Varchar(255)",
84
        "Default" => "Varchar(255)",
85
        "Sort" => "Int",
86
        "Required" => "Boolean",
87
        "CustomErrorMessage" => "Varchar(255)",
88
89
        "CustomRules" => "Text", // @deprecated from 2.0
90
        "CustomSettings" => "Text", // @deprecated from 2.0
91
        "Migrated" => "Boolean", // set to true when migrated
92
93
        "ExtraClass" => "Text", // from CustomSettings
94
        "RightTitle" => "Varchar(255)", // from CustomSettings
95
        "ShowOnLoad" => "Boolean(1)", // from CustomSettings
96
        "ShowInSummary" => "Boolean",
97
        "Placeholder" => "Varchar(255)"
98
    );
99
100
    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...
101
        'ShowOnLoad' => true,
102
    );
103
104
105
    /**
106
     * @config
107
     * @var array
108
     */
109
    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...
110
        "Parent" => "UserDefinedForm",
111
    );
112
113
    /**
114
     * Built in extensions required
115
     *
116
     * @config
117
     * @var array
118
     */
119
    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...
120
        "Versioned('Stage', 'Live')"
121
    );
122
123
    /**
124
     * @config
125
     * @var array
126
     */
127
    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...
128
        "DisplayRules" => "EditableCustomRule.Parent" // from CustomRules
129
    );
130
131
    /**
132
     * @var bool
133
     */
134
    protected $readonly;
135
136
    /**
137
     * Set the visibility of an individual form field
138
     *
139
     * @param bool
140
     */
141 10
    public function setReadonly($readonly = true)
142 10
    {
143 2
        $this->readonly = $readonly;
144 1
    }
145
146
    /**
147
     * Returns whether this field is readonly
148
     *
149
     * @return bool
150
     */
151 11
    private function isReadonly()
152
    {
153 11
        return $this->readonly;
154
    }
155
156
    /**
157
     * @return FieldList
158
     */
159
    public function getCMSFields()
160
    {
161
        $fields = new FieldList(new TabSet('Root'));
162
163
        // Main tab
164
        $fields->addFieldsToTab(
165
            'Root.Main',
166
            array(
167
                ReadonlyField::create(
168
                    'Type',
169
                    _t('EditableFormField.TYPE', 'Type'),
170
                    $this->i18n_singular_name()
171
                ),
172
                CheckboxField::create('ShowInSummary', _t('EditableFormField.SHOWINSUMMARY', 'Show in summary gridfield')),
173
                LiteralField::create(
174
                    'MergeField',
175
                    _t(
176
                        'EditableFormField.MERGEFIELDNAME',
177
                        '<div class="field readonly">' .
178
                            '<label class="left">' . _t('EditableFormField.MERGEFIELDNAME', 'Merge field') . '</label>' .
179
                            '<div class="middleColumn">' .
180
                                '<span class="readonly">$' . $this->Name . '</span>' .
181
                            '</div>' .
182
                        '</div>'
183
                    )
184
                ),
185
                TextField::create('Title', _t('EditableFormField.TITLE', 'Title')),
186
                TextField::create('Default', _t('EditableFormField.DEFAULT', 'Default value')),
187
                TextField::create('RightTitle', _t('EditableFormField.RIGHTTITLE', 'Right title')),
188
                SegmentField::create('Name', _t('EditableFormField.NAME', 'Name'))->setModifiers(array(
189
                    UnderscoreSegmentFieldModifier::create()->setDefault('FieldName'),
190
                    DisambiguationSegmentFieldModifier::create(),
191
                ))->setPreview($this->Name)
192
            )
193
        );
194
        $fields->fieldByName('Root.Main')->setTitle(_t('SiteTree.TABMAIN', 'Main'));
195
196
        // Custom settings
197
        if (!empty(self::$allowed_css)) {
198
            $cssList = array();
199
            foreach (self::$allowed_css as $k => $v) {
200
                if (!is_array($v)) {
201
                    $cssList[$k]=$v;
202
                } elseif ($k === $this->ClassName) {
203
                    $cssList = array_merge($cssList, $v);
204
                }
205
            }
206
207
            $fields->addFieldToTab('Root.Main',
208
                DropdownField::create(
209
                    'ExtraClass',
210
                    _t('EditableFormField.EXTRACLASS_TITLE', 'Extra Styling/Layout'),
211
                    $cssList
212
                )->setDescription(_t(
213
                    'EditableFormField.EXTRACLASS_SELECT',
214
                    'Select from the list of allowed styles'
215
                ))
216
            );
217
        } else {
218
            $fields->addFieldToTab('Root.Main',
219
                TextField::create(
220
                    'ExtraClass',
221
                    _t('EditableFormField.EXTRACLASS_Title', 'Extra CSS classes')
222
                )->setDescription(_t(
223
                    'EditableFormField.EXTRACLASS_MULTIPLE',
224
                    'Separate each CSS class with a single space'
225
                ))
226
            );
227
        }
228
229
        // Validation
230
        $validationFields = $this->getFieldValidationOptions();
231
        if ($validationFields && $validationFields->count()) {
232
            $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...
233
            $fields->fieldByName('Root.Validation')->setTitle(_t('EditableFormField.VALIDATION', 'Validation'));
234
        }
235
236
        // Add display rule fields
237
        $displayFields = $this->getDisplayRuleFields();
238
        if ($displayFields && $displayFields->count()) {
239
            $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...
240
        }
241
242
        // Placeholder
243
        if ($this->config()->has_placeholder) {
244
            $fields->addFieldToTab(
245
                'Root.Main',
246
                TextField::create(
247
                    'Placeholder',
248
                    _t('EditableFormField.PLACEHOLDER', 'Placeholder')
249
                )
250
            );
251
        }
252
253
        $this->extend('updateCMSFields', $fields);
254
255
        return $fields;
256
    }
257
258
    /**
259
     * Return fields to display on the 'Display Rules' tab
260
     *
261
     * @return FieldList
262
     */
263
    protected function getDisplayRuleFields()
264
    {
265
        // Check display rules
266
        if ($this->Required) {
267
            return new FieldList(
268
                LabelField::create(_t(
269
                    'EditableFormField.DISPLAY_RULES_DISABLED',
270
                    'Display rules are not enabled for required fields. ' .
271
                    'Please uncheck "Is this field Required?" under "Validation" to re-enable.'
272
                ))->addExtraClass('message warning')
273
            );
274
        }
275
        $self = $this;
276
        $allowedClasses = array_keys($this->getEditableFieldClasses(false));
277
        $editableColumns = new GridFieldEditableColumns();
278
        $editableColumns->setDisplayFields(array(
279
            'Display' => '',
280
            '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...
281
                return DropdownField::create(
282
                    $column,
283
                    '',
284
                    EditableFormField::get()
285
                        ->filter(array(
286
                            '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...
287
                            'ClassName' => $allowedClasses
288
                        ))
289
                        ->exclude(array(
290
                            'ID' => $self->ID
291
                        ))
292
                        ->map('ID', 'Title')
293
                    );
294
            },
295
            '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...
296
                $options = Config::inst()->get('EditableCustomRule', 'condition_options');
297
                return DropdownField::create($column, '', $options);
298
            },
299
            '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...
300
                return TextField::create($column);
301
            },
302
            '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...
303
                return HiddenField::create($column, '', $self->ID);
304
            }
305
        ));
306
307
        // Custom rules
308
        $customRulesConfig = GridFieldConfig::create()
309
            ->addComponents(
310
                $editableColumns,
311
                new GridFieldButtonRow(),
312
                new GridFieldToolbarHeader(),
313
                new GridFieldAddNewInlineButton(),
314
                new GridFieldDeleteAction()
315
            );
316
317
        return new FieldList(
318
            CheckboxField::create(
319
                'ShowOnLoad',
320
                _t('EditableFormField.SHOWONLOAD_TITLE', 'Show on load')
321
            )->setDescription(
322
                _t(
323
                    'EditableFormField.SHOWONLOAD',
324
                    'Initial visibility before processing these rules'
325
                )
326
            ),
327
            GridField::create(
328
                'DisplayRules',
329
                _t('EditableFormField.CUSTOMRULES', 'Custom Rules'),
330
                $this->DisplayRules(),
331
                $customRulesConfig
332
            )
333
        );
334
    }
335
336 39
    public function onBeforeWrite()
337
    {
338 39
        parent::onBeforeWrite();
339
340
        // Set a field name.
341 39
        if (!$this->Name) {
342
            // New random name
343 25
            $this->Name = $this->generateName();
344 39
        } elseif ($this->Name === 'Field') {
345
            throw new ValidationException('Field name cannot be "Field"');
346
        }
347
348 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...
349 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...
350 39
            $this->Sort = EditableFormField::get()
351 39
                ->filter('ParentID', $parentID)
352 39
                ->max('Sort') + 1;
353 39
        }
354 39
    }
355
356
    /**
357
     * Generate a new non-conflicting Name value
358
     *
359
     * @return string
360
     */
361 25
    protected function generateName()
362
    {
363
        do {
364
            // Generate a new random name after this class
365 25
            $class = get_class($this);
366 25
            $entropy = substr(sha1(uniqid()), 0, 5);
367 25
            $name = "{$class}_{$entropy}";
368
369
            // Check if it conflicts
370 25
            $exists = EditableFormField::get()->filter('Name', $name)->count() > 0;
371 25
        } while ($exists);
372 25
        return $name;
373
    }
374
375
    /**
376
     * Flag indicating that this field will set its own error message via data-msg='' attributes
377
     *
378
     * @return bool
379
     */
380
    public function getSetsOwnError()
381
    {
382
        return false;
383
    }
384
385
    /**
386
     * Return whether a user can delete this form field
387
     * based on whether they can edit the page
388
     *
389
     * @param Member $member
390
     * @return bool
391
     */
392 1
    public function canDelete($member = null)
393
    {
394 1
        return $this->canEdit($member);
395
    }
396
397
    /**
398
     * Return whether a user can edit this form field
399
     * based on whether they can edit the page
400
     *
401
     * @param Member $member
402
     * @return bool
403
     */
404 1
    public function canEdit($member = null)
405
    {
406 1
        $parent = $this->Parent();
407 1
        if ($parent && $parent->exists()) {
408 1
            return $parent->canEdit($member) && !$this->isReadonly();
409
        } elseif (!$this->exists() && Controller::has_curr()) {
410
            // This is for GridFieldOrderableRows support as it checks edit permissions on
411
            // singleton of the class. Allows editing of User Defined Form pages by
412
            // 'Content Authors' and those with permission to edit the UDF page. (ie. CanEditType/EditorGroups)
413
            // This is to restore User Forms 2.x backwards compatibility.
414
            $controller = Controller::curr();
415
            if ($controller && $controller instanceof CMSPageEditController) {
416
                $parent = $controller->getRecord($controller->currentPageID());
417
                // Only allow this behaviour on pages using UserFormFieldEditorExtension, such
418
                // as UserDefinedForm page type.
419
                if ($parent && $parent->hasExtension('UserFormFieldEditorExtension')) {
420
                    return $parent->canEdit($member);
421
                }
422
            }
423
        }
424
425
        // Fallback to secure admin permissions
426
        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 426 which is incompatible with the return type documented by EditableFormField::canEdit of type boolean.
Loading history...
427
    }
428
429
    /**
430
     * Return whether a user can view this form field
431
     * based on whether they can view the page, regardless of the ReadOnly status of the field
432
     *
433
     * @param Member $member
434
     * @return bool
435
     */
436 1
    public function canView($member = null)
437
    {
438 1
        $parent = $this->Parent();
439 1
        if ($parent && $parent->exists()) {
440 1
            return $parent->canView($member);
441
        }
442
443
        return true;
444
    }
445
446
    /**
447
     * Return whether a user can create an object of this type
448
     *
449
     * @param Member $member
450
     * @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...
451
     * @return bool
452
     */
453 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...
454
    {
455
        // Check parent page
456 3
        $parent = $this->getCanCreateContext(func_get_args());
457 3
        if ($parent) {
458
            return $parent->canEdit($member);
459
        }
460
461
        // Fall back to secure admin permissions
462 3
        return parent::canCreate($member);
463
    }
464
465
    /**
466
     * Helper method to check the parent for this object
467
     *
468
     * @param array $args List of arguments passed to canCreate
469
     * @return SiteTree Parent page instance
470
     */
471 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...
472
    {
473
        // Inspect second parameter to canCreate for a 'Parent' context
474 3
        if (isset($args[1]['Parent'])) {
475
            return $args[1]['Parent'];
476
        }
477
        // Hack in currently edited page if context is missing
478 3
        if (Controller::has_curr() && Controller::curr() instanceof CMSMain) {
479
            return Controller::curr()->currentPage();
480
        }
481
482
        // No page being edited
483 3
        return null;
484
    }
485
486
    /**
487
     * Check if can publish
488
     *
489
     * @param Member $member
490
     * @return bool
491
     */
492
    public function canPublish($member = null)
493
    {
494
        return $this->canEdit($member);
495
    }
496
497
    /**
498
     * Check if can unpublish
499
     *
500
     * @param Member $member
501
     * @return bool
502
     */
503
    public function canUnpublish($member = null)
504
    {
505
        return $this->canDelete($member);
506
    }
507
508
    /**
509
     * Publish this Form Field to the live site
510
     *
511
     * Wrapper for the {@link Versioned} publish function
512
     */
513 9
    public function doPublish($fromStage, $toStage, $createNewVersion = false)
514
    {
515 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...
516
517 9
		$seenIDs = array();
518
519
		// Don't forget to publish the related custom rules...
520 9
		foreach ($this->DisplayRules() as $rule) {
521 1
			$seenIDs[] = $rule->ID;
522 1
			$rule->doPublish($fromStage, $toStage, $createNewVersion);
523 1
			$rule->destroy();
524 9
		}
525
526
		// remove any orphans from the "fromStage"
527 9
        $rules = Versioned::get_by_stage('EditableCustomRule', $toStage)
528 9
            ->filter('ParentID', $this->ID);
529
530 9
		if (!empty($seenIDs)) {
531 1
            $rules = $rules->exclude('ID', $seenIDs);
532 1
        }
533
534 9
		foreach ($rules as $rule) {
535 1
		    $rule->deleteFromStage($toStage);
536 9
		}
537 9
	}
538
539
    /**
540
     * Delete this field from a given stage
541
     *
542
     * Wrapper for the {@link Versioned} deleteFromStage function
543
     */
544 1
    public function doDeleteFromStage($stage)
545
    {
546
        // Remove custom rules in this stage
547 1
        $rules = Versioned::get_by_stage('EditableCustomRule', $stage)
548 1
            ->filter('ParentID', $this->ID);
549 1
        foreach ($rules as $rule) {
550
            $rule->deleteFromStage($stage);
551 1
        }
552
553
        // Remove record
554 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...
555 1
    }
556
557
    /**
558
     * checks wether record is new, copied from Sitetree
559
     */
560
    public function isNew()
561
    {
562
        if (empty($this->ID)) {
563
            return true;
564
        }
565
566
        if (is_numeric($this->ID)) {
567
            return false;
568
        }
569
570
        return stripos($this->ID, 'new') === 0;
571
    }
572
573
    /**
574
     * checks if records is changed on stage
575
     * @return boolean
576
     */
577
    public function getIsModifiedOnStage()
578
    {
579
        // new unsaved fields could be never be published
580
        if ($this->isNew()) {
581
            return false;
582
        }
583
584
        $stageVersion = Versioned::get_versionnumber_by_stage('EditableFormField', 'Stage', $this->ID);
585
        $liveVersion = Versioned::get_versionnumber_by_stage('EditableFormField', 'Live', $this->ID);
586
587
        return ($stageVersion && $stageVersion != $liveVersion);
588
    }
589
590
    /**
591
     * @deprecated since version 4.0
592
     */
593
    public function getSettings()
594
    {
595
        Deprecation::notice('4.0', 'getSettings is deprecated');
596
        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...
597
    }
598
599
    /**
600
     * @deprecated since version 4.0
601
     */
602
    public function setSettings($settings = array())
603
    {
604
        Deprecation::notice('4.0', 'setSettings is deprecated');
605
        $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...
606
    }
607
608
    /**
609
     * @deprecated since version 4.0
610
     */
611
    public function setSetting($key, $value)
612
    {
613
        Deprecation::notice('4.0', "setSetting({$key}) is deprecated");
614
        $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...
615
        $settings[$key] = $value;
616
617
        $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...
618
    }
619
620
    /**
621
     * Set the allowed css classes for the extraClass custom setting
622
     *
623
     * @param array The permissible CSS classes to add
624
     */
625
    public function setAllowedCss(array $allowed)
626
    {
627
        if (is_array($allowed)) {
628
            foreach ($allowed as $k => $v) {
629
                self::$allowed_css[$k] = (!is_null($v)) ? $v : $k;
630
            }
631
        }
632
    }
633
634
    /**
635
     * @deprecated since version 4.0
636
     */
637
    public function getSetting($setting)
638
    {
639
        Deprecation::notice("4.0", "getSetting({$setting}) is deprecated");
640
641
        $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...
642
        if (isset($settings) && count($settings) > 0) {
643
            if (isset($settings[$setting])) {
644
                return $settings[$setting];
645
            }
646
        }
647
        return '';
648
    }
649
650
    /**
651
     * Get the path to the icon for this field type, relative to the site root.
652
     *
653
     * @return string
654
     */
655
    public function getIcon()
656
    {
657
        return USERFORMS_DIR . '/images/' . strtolower($this->class) . '.png';
658
    }
659
660
    /**
661
     * Return whether or not this field has addable options
662
     * such as a dropdown field or radio set
663
     *
664
     * @return bool
665
     */
666
    public function getHasAddableOptions()
667
    {
668
        return false;
669
    }
670
671
    /**
672
     * Return whether or not this field needs to show the extra
673
     * options dropdown list
674
     *
675
     * @return bool
676
     */
677
    public function showExtraOptions()
678
    {
679
        return true;
680
    }
681
682
    /**
683
     * Returns the Title for rendering in the front-end (with XML values escaped)
684
     *
685
     * @return string
686
     */
687 18
    public function getEscapedTitle()
688
    {
689 18
        return Convert::raw2xml($this->Title);
690
    }
691
692
    /**
693
     * Find the numeric indicator (1.1.2) that represents it's nesting value
694
     *
695
     * Only useful for fields attached to a current page, and that contain other fields such as pages
696
     * or groups
697
     *
698
     * @return string
699
     */
700
    public function getFieldNumber()
701
    {
702
        // Check if exists
703
        if (!$this->exists()) {
704
            return null;
705
        }
706
        // Check parent
707
        $form = $this->Parent();
708
        if (!$form || !$form->exists() || !($fields = $form->Fields())) {
709
            return null;
710
        }
711
712
        $prior = 0; // Number of prior group at this level
713
        $stack = array(); // Current stack of nested groups, where the top level = the page
714
        foreach ($fields->map('ID', 'ClassName') as $id => $className) {
715
            if ($className === 'EditableFormStep') {
716
                $priorPage = empty($stack) ? $prior : $stack[0];
717
                $stack = array($priorPage + 1);
718
                $prior = 0;
719
            } elseif ($className === 'EditableFieldGroup') {
720
                $stack[] = $prior + 1;
721
                $prior = 0;
722
            } elseif ($className === 'EditableFieldGroupEnd') {
723
                $prior = array_pop($stack);
724
            }
725
            if ($id == $this->ID) {
726
                return implode('.', $stack);
727
            }
728
        }
729
        return null;
730
    }
731
732
    public function getCMSTitle()
733
    {
734
        return $this->i18n_singular_name() . ' (' . $this->Title . ')';
735
    }
736
737
    /**
738
     * @deprecated since version 4.0
739
     */
740
    public function getFieldName($field = false)
741
    {
742
        Deprecation::notice('4.0', "getFieldName({$field}) is deprecated");
743
        return ($field) ? "Fields[".$this->ID."][".$field."]" : "Fields[".$this->ID."]";
744
    }
745
746
    /**
747
     * @deprecated since version 4.0
748
     */
749
    public function getSettingName($field)
750
    {
751
        Deprecation::notice('4.0', "getSettingName({$field}) is deprecated");
752
        $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...
753
754
        return $name . '[' . $field .']';
755
    }
756
757
    /**
758
     * Append custom validation fields to the default 'Validation'
759
     * section in the editable options view
760
     *
761
     * @return FieldList
762
     */
763
    public function getFieldValidationOptions()
764
    {
765
        $fields = new FieldList(
766
            CheckboxField::create('Required', _t('EditableFormField.REQUIRED', 'Is this field Required?'))
767
                ->setDescription(_t('EditableFormField.REQUIRED_DESCRIPTION', 'Please note that conditional fields can\'t be required')),
768
            TextField::create('CustomErrorMessage', _t('EditableFormField.CUSTOMERROR', 'Custom Error Message'))
769
        );
770
771
        $this->extend('updateFieldValidationOptions', $fields);
772
773
        return $fields;
774
    }
775
776
    /**
777
     * Return a FormField to appear on the front end. Implement on
778
     * your subclass.
779
     *
780
     * @return FormField
781
     */
782
    public function getFormField()
783
    {
784
        user_error("Please implement a getFormField() on your EditableFormClass ". $this->ClassName, E_USER_ERROR);
785
    }
786
787
    /**
788
     * Updates a formfield with extensions
789
     *
790
     * @param FormField $field
791
     */
792 17
    public function doUpdateFormField($field)
793
    {
794 17
        $this->extend('beforeUpdateFormField', $field);
795 17
        $this->updateFormField($field);
796 17
        $this->extend('afterUpdateFormField', $field);
797 17
    }
798
799
    /**
800
     * Updates a formfield with the additional metadata specified by this field
801
     *
802
     * @param FormField $field
803
     */
804 17
    protected function updateFormField($field)
805
    {
806
        // set the error / formatting messages
807 17
        $field->setCustomValidationMessage($this->getErrorMessage()->RAW());
808
809
        // set the right title on this field
810 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...
811
            // Since this field expects raw html, safely escape the user data prior
812 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...
813 1
        }
814
815
        // if this field is required add some
816 17
        if ($this->Required) {
817
            // Required validation can conflict so add the Required validation messages as input attributes
818 3
            $errorMessage = $this->getErrorMessage()->HTML();
819 3
            $field->addExtraClass('requiredField');
820 3
            $field->setAttribute('data-rule-required', 'true');
821 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 818 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...
822
823 3
            if ($identifier = UserDefinedForm::config()->required_identifier) {
824 1
                $title = $field->Title() . " <span class='required-identifier'>". $identifier . "</span>";
825 1
                $field->setTitle($title);
826 1
            }
827 3
        }
828
829
        // if this field has an extra class
830 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...
831
            $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...
832
        }
833
834
        // if this field has a placeholder
835 17
        if ($this->Placeholder) {
0 ignored issues
show
Bug introduced by
The property Placeholder does not seem to exist. Did you mean has_placeholder?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
836
            $field->setAttribute('placeholder', $this->Placeholder);
0 ignored issues
show
Bug introduced by
The property Placeholder does not seem to exist. Did you mean has_placeholder?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
837
        }
838 17
    }
839
840
    /**
841
     * Return the instance of the submission field class
842
     *
843
     * @return SubmittedFormField
844
     */
845 2
    public function getSubmittedFormField()
846
    {
847 2
        return new SubmittedFormField();
848
    }
849
850
851
    /**
852
     * Show this form field (and its related value) in the reports and in emails.
853
     *
854
     * @return bool
855
     */
856 2
    public function showInReports()
857
    {
858 2
        return true;
859
    }
860
861
    /**
862
     * Return the error message for this field. Either uses the custom
863
     * one (if provided) or the default SilverStripe message
864
     *
865
     * @return Varchar
866
     */
867 17
    public function getErrorMessage()
868
    {
869 17
        $title = strip_tags("'". ($this->Title ? $this->Title : $this->Name) . "'");
870 17
        $standard = sprintf(_t('Form.FIELDISREQUIRED', '%s is required').'.', $title);
871
872
        // only use CustomErrorMessage if it has a non empty value
873 17
        $errorMessage = (!empty($this->CustomErrorMessage)) ? $this->CustomErrorMessage : $standard;
874
875 17
        return DBField::create_field('Varchar', $errorMessage);
876
    }
877
878
    /**
879
     * Invoked by UserFormUpgradeService to migrate settings specific to this field from CustomSettings
880
     * to the field proper
881
     *
882
     * @param array $data Unserialised data
883
     */
884 2
    public function migrateSettings($data)
885
    {
886
        // Map 'Show' / 'Hide' to boolean
887 2
        if (isset($data['ShowOnLoad'])) {
888 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...
889 2
            unset($data['ShowOnLoad']);
890 2
        }
891
892
        // Migrate all other settings
893 2
        foreach ($data as $key => $value) {
894 2
            if ($this->hasField($key)) {
895 2
                $this->setField($key, $value);
896 2
            }
897 2
        }
898 2
    }
899
900
    /**
901
     * Get the formfield to use when editing this inline in gridfield
902
     *
903
     * @param string $column name of column
904
     * @param array $fieldClasses List of allowed classnames if this formfield has a selectable class
905
     * @return FormField
906
     */
907
    public function getInlineClassnameField($column, $fieldClasses)
908
    {
909
        return DropdownField::create($column, false, $fieldClasses);
910
    }
911
912
    /**
913
     * Get the formfield to use when editing the title inline
914
     *
915
     * @param string $column
916
     * @return FormField
917
     */
918
    public function getInlineTitleField($column)
919
    {
920
        return TextField::create($column, false)
921
            ->setAttribute('placeholder', _t('EditableFormField.TITLE', 'Title'))
922
            ->setAttribute('data-placeholder', _t('EditableFormField.TITLE', 'Title'));
923
    }
924
925
    /**
926
     * Get the JS expression for selecting the holder for this field
927
     *
928
     * @return string
929
     */
930 8
    public function getSelectorHolder()
931
    {
932 8
        return "$(\"#{$this->Name}\")";
933
    }
934
935
    /**
936
     * Gets the JS expression for selecting the value for this field
937
     *
938
     * @param EditableCustomRule $rule Custom rule this selector will be used with
939
     * @param bool $forOnLoad Set to true if this will be invoked on load
940
     */
941
    public function getSelectorField(EditableCustomRule $rule, $forOnLoad = false)
942
    {
943
        return "$(\"input[name='{$this->Name}']\")";
944
    }
945
946
947
    /**
948
     * Get the list of classes that can be selected and used as data-values
949
     *
950
     * @param $includeLiterals Set to false to exclude non-data fields
951
     * @return array
952
     */
953 2
    public function getEditableFieldClasses($includeLiterals = true)
954
    {
955 2
        $classes = ClassInfo::getValidSubClasses('EditableFormField');
956
957
        // Remove classes we don't want to display in the dropdown.
958 2
        $editableFieldClasses = array();
959 2
        foreach ($classes as $class) {
960
            // Skip abstract / hidden classes
961 2
            if (Config::inst()->get($class, 'abstract', Config::UNINHERITED) || Config::inst()->get($class, 'hidden')
962 2
            ) {
963 2
                continue;
964
            }
965
966 2
            if (!$includeLiterals && Config::inst()->get($class, 'literal')) {
967
                continue;
968
            }
969
970 2
            $singleton = singleton($class);
971 2
            if (!$singleton->canCreate()) {
972
                continue;
973
            }
974
975 2
            $editableFieldClasses[$class] = $singleton->i18n_singular_name();
976 2
        }
977
978 2
        asort($editableFieldClasses);
979 2
        return $editableFieldClasses;
980
    }
981
982
    /**
983
     * @return EditableFormFieldValidator
984
     */
985
    public function getCMSValidator()
986
    {
987
        return EditableFormFieldValidator::create()
988
            ->setRecord($this);
989
    }
990
991
    /**
992
     * Determine effective display rules for this field.
993
     *
994
     * @return SS_List
995
     */
996 10
    public function EffectiveDisplayRules()
997
    {
998 10
        if ($this->Required) {
999 4
            return new ArrayList();
1000
        }
1001 10
        return $this->DisplayRules();
1002
    }
1003
}
1004