Completed
Pull Request — master (#441)
by Damian
33:28
created

EditableFormField::canUnpublish()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 3
rs 10
cc 1
eloc 2
nc 1
nop 1
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
 * @method UserDefinedForm Parent() Parent page
13
 * @method DataList DisplayRules() List of EditableCustomRule objects
14
 */
15
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...
16
17
	/**
18
	 * Set to true to hide from class selector
19
	 *
20
	 * @config
21
	 * @var bool
22
	 */
23
	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...
24
25
	/**
26
	 * Define this field as abstract (not inherited)
27
	 *
28
	 * @config
29
	 * @var bool
30
	 */
31
	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...
32
33
	/**
34
	 * Flag this field type as non-data (e.g. literal, header, html)
35
	 *
36
	 * @config
37
	 * @var bool
38
	 */
39
	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...
40
41
	/**
42
	 * Default sort order
43
	 *
44
	 * @config
45
	 * @var string
46
	 */
47
	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...
48
49
	/**
50
	 * A list of CSS classes that can be added
51
	 *
52
	 * @var array
53
	 */
54
	public static $allowed_css = array();
55
56
	/**
57
	 * @config
58
	 * @var array
59
	 */
60
	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...
61
		'Title'
62
	);
63
64
	/**
65
	 * @config
66
	 * @var array
67
	 */
68
	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...
69
		"Name" => "Varchar",
70
		"Title" => "Varchar(255)",
71
		"Default" => "Varchar(255)",
72
		"Sort" => "Int",
73
		"Required" => "Boolean",
74
		"CustomErrorMessage" => "Varchar(255)",
75
76
		"CustomRules" => "Text", // @deprecated from 2.0
77
		"CustomSettings" => "Text", // @deprecated from 2.0
78
		"Migrated" => "Boolean", // set to true when migrated
79
80
		"ExtraClass" => "Text", // from CustomSettings
81
		"RightTitle" => "Varchar(255)", // from CustomSettings
82
		"ShowOnLoad" => "Boolean(1)", // from CustomSettings
83
	);
84
85
	/**
86
	 * @config
87
	 * @var array
88
	 */
89
	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...
90
		"Parent" => "UserDefinedForm",
91
	);
92
93
	/**
94
	 * Built in extensions required
95
	 *
96
	 * @config
97
	 * @var array
98
	 */
99
	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...
100
		"Versioned('Stage', 'Live')"
101
	);
102
103
	/**
104
	 * @config
105
	 * @var array
106
	 */
107
	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...
108
		"DisplayRules" => "EditableCustomRule.Parent" // from CustomRules
109
	);
110
111
	/**
112
	 * @var bool
113
	 */
114
	protected $readonly;
115
116
	/**
117
	 * Set the visibility of an individual form field
118
	 *
119
	 * @param bool
120
	 */
121
	public function setReadonly($readonly = true) {
122
		$this->readonly = $readonly;
123
	}
124
125
	/**
126
	 * Returns whether this field is readonly
127
	 *
128
	 * @return bool
129
	 */
130
	private function isReadonly() {
131
		return $this->readonly;
132
	}
133
134
	/**
135
	 * @return FieldList
136
	 */
137
	public function getCMSFields() {
138
		$fields = new FieldList(new TabSet('Root'));
139
140
		// Main tab
141
		$fields->addFieldsToTab(
142
			'Root.Main',
143
			array(
144
				ReadonlyField::create(
145
					'Type',
146
					_t('EditableFormField.TYPE', 'Type'),
147
					$this->i18n_singular_name()
148
				),
149
				LiteralField::create(
150
					'MergeField',
151
					_t(
152
						'EditableFormField.MERGEFIELDNAME',
153
						'<div class="field readonly">' .
154
							'<label class="left">Merge field</label>' .
155
							'<div class="middleColumn">' .
156
								'<span class="readonly">$' . $this->Name . '</span>' .
157
							'</div>' .
158
						'</div>'
159
					)
160
				),
161
				TextField::create('Title'),
162
				TextField::create('Default', _t('EditableFormField.DEFAULT', 'Default value')),
163
				TextField::create('RightTitle', _t('EditableFormField.RIGHTTITLE', 'Right title')),
164
				SegmentField::create('Name')->setModifiers(array(
165
					UnderscoreSegmentFieldModifier::create()->setDefault('FieldName'),
166
					DisambiguationSegmentFieldModifier::create(),
167
				))->setPreview($this->Name)
168
			)
169
		);
170
171
		// Custom settings
172
		if (!empty(self::$allowed_css)) {
173
			$cssList = array();
174
			foreach(self::$allowed_css as $k => $v) {
175
				if (!is_array($v)) {
176
					$cssList[$k]=$v;
177
				} elseif ($k === $this->ClassName) {
178
					$cssList = array_merge($cssList, $v);
179
				}
180
			}
181
182
			$fields->addFieldToTab('Root.Main',
183
				DropdownField::create(
184
					'ExtraClass',
185
					_t('EditableFormField.EXTRACLASS_TITLE', 'Extra Styling/Layout'),
186
					$cssList
187
				)->setDescription(_t(
188
					'EditableFormField.EXTRACLASS_SELECT',
189
					'Select from the list of allowed styles'
190
				))
191
			);
192
		} else {
193
			$fields->addFieldToTab('Root.Main',
194
				TextField::create(
195
					'ExtraClass',
196
					_t('EditableFormField.EXTRACLASS_Title', 'Extra CSS classes')
197
				)->setDescription(_t(
198
					'EditableFormField.EXTRACLASS_MULTIPLE',
199
					'Separate each CSS class with a single space'
200
				))
201
			);
202
		}
203
204
		// Validation
205
		$validationFields = $this->getFieldValidationOptions();
206
		if($validationFields) {
207
			$fields->addFieldsToTab(
208
				'Root.Validation',
209
				$this->getFieldValidationOptions()
0 ignored issues
show
Documentation introduced by
$this->getFieldValidationOptions() 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...
210
			);
211
		}
212
		$allowedClasses = array_keys($this->getEditableFieldClasses(false));
213
		$self = $this;
214
		$editableColumns = new GridFieldEditableColumns();
215
		$editableColumns->setDisplayFields(array(
216
			'Display' => '',
217
			'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...
218
				return DropdownField::create(
219
					$column,
220
					'',
221
					EditableFormField::get()
222
						->filter(array(
223
							'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...
224
							'ClassName' => $allowedClasses
225
						))
226
						->exclude(array(
227
							'ID' => $self->ID
228
						))
229
						->map('ID', 'Title')
230
					);
231
			},
232
			'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...
233
				$options = Config::inst()->get('EditableCustomRule', 'condition_options');
234
				return DropdownField::create($column, '', $options);
235
			},
236
			'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...
237
				return TextField::create($column);
238
			},
239
			'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...
240
				return HiddenField::create($column, '', $self->ID);
241
			}
242
		));
243
244
		// Custom rules
245
		$customRulesConfig = GridFieldConfig::create()
246
			->addComponents(
247
				$editableColumns,
248
				new GridFieldButtonRow(),
249
				new GridFieldToolbarHeader(),
250
				new GridFieldAddNewInlineButton(),
251
				new GridFieldDeleteAction()
252
			);
253
254
		$fields->addFieldsToTab('Root.DisplayRules', array(
255
			CheckboxField::create('ShowOnLoad')
256
				->setDescription(_t(
257
					'EditableFormField.SHOWONLOAD',
258
					'Initial visibility before processing these rules'
259
				)),
260
			GridField::create(
261
				'DisplayRules',
262
				_t('EditableFormField.CUSTOMRULES', 'Custom Rules'),
263
				$this->DisplayRules(),
264
				$customRulesConfig
265
			)
266
		));
267
268
		$this->extend('updateCMSFields', $fields);
269
270
		return $fields;
271
	}
272
273
	/**
274
	 * @return void
275
	 */
276
	public function onBeforeWrite() {
277
		parent::onBeforeWrite();
278
279
		// Set a field name.
280
		if(!$this->Name) {
281
			// New random name
282
			$this->Name = $this->generateName();
283
284
		} elseif($this->Name === 'Field') {
285
			throw new ValidationException('Field name cannot be "Field"');
286
		}
287
288
		if(!$this->Sort && $this->ParentID) {
0 ignored issues
show
Documentation introduced by
The property Sort 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...
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...
289
			$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...
290
			$this->Sort = EditableFormField::get()
0 ignored issues
show
Documentation introduced by
The property Sort 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...
291
				->filter('ParentID', $parentID)
292
				->max('Sort') + 1;
293
		}
294
	}
295
296
	/**
297
	 * Generate a new non-conflicting Name value
298
	 *
299
	 * @return string
300
	 */
301
	protected function generateName() {
302
		do {
303
			// Generate a new random name after this class
304
			$class = get_class($this);
305
			$entropy = substr(sha1(uniqid()), 0, 5);
306
			$name = "{$class}_{$entropy}";
307
308
			// Check if it conflicts
309
			$exists = EditableFormField::get()->filter('Name', $name)->count() > 0;
310
		} while($exists);
311
		return $name;
312
	}
313
314
	/**
315
	 * Flag indicating that this field will set its own error message via data-msg='' attributes
316
	 *
317
	 * @return bool
318
	 */
319
	public function getSetsOwnError() {
320
		return false;
321
	}
322
323
    /**
324
     * Return whether a user can delete this form field
325
     * based on whether they can edit the page
326
     *
327
     * @param Member $member
328
     * @return bool
329
     */
330
	public function canDelete($member = null) {
331
		return $this->canEdit($member);
332
	}
333
334
    /**
335
     * Return whether a user can edit this form field
336
     * based on whether they can edit the page
337
     *
338
     * @param Member $member
339
     * @return bool
340
     */
341
	public function canEdit($member = null) {
342
        $parent = $this->Parent();
343
		if($parent && $parent->exists()) {
344
			return $parent->canEdit($member) && !$this->isReadonly();
345
		}
346
347
        // Fallback to secure admin permissions
348
		return parent::canEdit($member);
349
	}
350
351
    /**
352
     * Return whether a user can view this form field
353
     * based on whether they can view the page, regardless of the ReadOnly status of the field
354
     *
355
     * @param Member $member
356
     * @return bool
357
     */
358
	public function canView($member = null) {
359
		$parent = $this->Parent();
360
		if($parent && $parent->exists()) {
361
			return $parent->canView($member);
362
		}
363
364
		return true;
365
	}
366
367
	/**
368
	 * Return whether a user can create an object of this type
369
	 *
370
     * @param Member $member
371
     * @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...
372
	 * @return bool
373
	 */
374 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...
375
		// Check parent page
376
        $parent = $this->getCanCreateContext(func_get_args());
377
        if($parent) {
378
            return $parent->canEdit($member);
379
        }
380
381
        // Fall back to secure admin permissions
382
        return parent::canCreate($member);
383
	}
384
385
    /**
386
     * Helper method to check the parent for this object
387
     *
388
     * @param array $args List of arguments passed to canCreate
389
     * @return SiteTree Parent page instance
390
     */
391 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...
392
        // Inspect second parameter to canCreate for a 'Parent' context
393
        if(isset($args[1]['Parent'])) {
394
            return $args[1]['Parent'];
395
        }
396
        // Hack in currently edited page if context is missing
397
        if(Controller::has_curr() && Controller::curr() instanceof CMSMain) {
398
            return Controller::curr()->currentPage();
399
        }
400
401
        // No page being edited
402
        return null;
403
    }
404
405
    /**
406
     * Check if can publish
407
     *
408
     * @param Member $member
409
     * @return bool
410
     */
411
    public function canPublish($member = null) {
412
        return $this->canEdit($member);
413
    }
414
415
    /**
416
     * Check if can unpublish
417
     *
418
     * @param Member $member
419
     * @return bool
420
     */
421
    public function canUnpublish($member = null) {
422
        return $this->canDelete($member);
423
    }
424
425
	/**
426
	 * Publish this Form Field to the live site
427
	 *
428
	 * Wrapper for the {@link Versioned} publish function
429
	 */
430
	public function doPublish($fromStage, $toStage, $createNewVersion = false) {
431
		$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...
432
433
		// Don't forget to publish the related custom rules...
434
		foreach ($this->DisplayRules() as $rule) {
435
			$rule->doPublish($fromStage, $toStage, $createNewVersion);
436
		}
437
	}
438
439
	/**
440
	 * Delete this field from a given stage
441
	 *
442
	 * Wrapper for the {@link Versioned} deleteFromStage function
443
	 */
444
	public function doDeleteFromStage($stage) {
445
		// Remove custom rules in this stage
446
		$rules = Versioned::get_by_stage('EditableCustomRule', $stage)
447
			->filter('ParentID', $this->ID);
448
		foreach ($rules as $rule) {
449
			$rule->deleteFromStage($stage);
450
		}
451
452
		// Remove record
453
		$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...
454
	}
455
456
	/**
457
	 * checks wether record is new, copied from Sitetree
458
	 */
459
	function isNew() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
460
		if(empty($this->ID)) return true;
461
462
		if(is_numeric($this->ID)) return false;
463
464
		return stripos($this->ID, 'new') === 0;
465
	}
466
467
	/**
468
	 * checks if records is changed on stage
469
	 * @return boolean
470
	 */
471
	public function getIsModifiedOnStage() {
472
		// new unsaved fields could be never be published
473
		if($this->isNew()) return false;
474
475
		$stageVersion = Versioned::get_versionnumber_by_stage('EditableFormField', 'Stage', $this->ID);
476
		$liveVersion = Versioned::get_versionnumber_by_stage('EditableFormField', 'Live', $this->ID);
477
478
		return ($stageVersion && $stageVersion != $liveVersion);
479
	}
480
481
	/**
482
	 * @deprecated since version 4.0
483
	 */
484
	public function getSettings() {
485
		Deprecation::notice('4.0', 'getSettings is deprecated');
486
		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...
487
	}
488
489
	/**
490
	 * @deprecated since version 4.0
491
	 */
492
	public function setSettings($settings = array()) {
493
		Deprecation::notice('4.0', 'setSettings is deprecated');
494
		$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...
495
	}
496
497
	/**
498
	 * @deprecated since version 4.0
499
	 */
500
	public function setSetting($key, $value) {
501
		Deprecation::notice('4.0', "setSetting({$key}) is deprecated");
502
		$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...
503
		$settings[$key] = $value;
504
505
		$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...
506
	}
507
508
	/**
509
	 * Set the allowed css classes for the extraClass custom setting
510
	 *
511
	 * @param array The permissible CSS classes to add
512
	 */
513
	public function setAllowedCss(array $allowed) {
514
		if (is_array($allowed)) {
515
			foreach ($allowed as $k => $v) {
516
				self::$allowed_css[$k] = (!is_null($v)) ? $v : $k;
517
			}
518
		}
519
	}
520
521
	/**
522
	 * @deprecated since version 4.0
523
	 */
524
	public function getSetting($setting) {
525
		Deprecation::notice("4.0", "getSetting({$setting}) is deprecated");
526
527
		$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...
528
		if(isset($settings) && count($settings) > 0) {
529
			if(isset($settings[$setting])) {
530
				return $settings[$setting];
531
			}
532
		}
533
		return '';
534
	}
535
536
	/**
537
	 * Get the path to the icon for this field type, relative to the site root.
538
	 *
539
	 * @return string
540
	 */
541
	public function getIcon() {
542
		return USERFORMS_DIR . '/images/' . strtolower($this->class) . '.png';
543
	}
544
545
	/**
546
	 * Return whether or not this field has addable options
547
	 * such as a dropdown field or radio set
548
	 *
549
	 * @return bool
550
	 */
551
	public function getHasAddableOptions() {
552
		return false;
553
	}
554
555
	/**
556
	 * Return whether or not this field needs to show the extra
557
	 * options dropdown list
558
	 *
559
	 * @return bool
560
	 */
561
	public function showExtraOptions() {
562
		return true;
563
	}
564
565
	/**
566
	 * Returns the Title for rendering in the front-end (with XML values escaped)
567
	 *
568
	 * @return string
569
	 */
570
	public function getEscapedTitle() {
571
		return Convert::raw2xml($this->Title);
0 ignored issues
show
Documentation introduced by
The property Title 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...
572
	}
573
574
	/**
575
	 * Find the numeric indicator (1.1.2) that represents it's nesting value
576
	 *
577
	 * Only useful for fields attached to a current page, and that contain other fields such as pages
578
	 * or groups
579
	 *
580
	 * @return string
581
	 */
582
	public function getFieldNumber() {
583
		// Check if exists
584
		if(!$this->exists()) {
585
			return null;
586
		}
587
		// Check parent
588
		$form = $this->Parent();
589
		if(!$form || !$form->exists() || !($fields = $form->Fields())) {
590
			return null;
591
		}
592
593
		$prior = 0; // Number of prior group at this level
594
		$stack = array(); // Current stack of nested groups, where the top level = the page
595
		foreach($fields->map('ID', 'ClassName') as $id => $className) {
596
			if($className === 'EditableFormStep') {
597
				$priorPage = empty($stack) ? $prior : $stack[0];
598
				$stack = array($priorPage + 1);
599
				$prior = 0;
600
			} elseif($className === 'EditableFieldGroup') {
601
				$stack[] = $prior + 1;
602
				$prior = 0;
603
			} elseif($className === 'EditableFieldGroupEnd') {
604
				$prior = array_pop($stack);
605
			}
606
			if($id == $this->ID) {
607
				return implode('.', $stack);
608
			}
609
		}
610
		return null;
611
	}
612
613
	public function getCMSTitle() {
614
		return $this->i18n_singular_name() . ' (' . $this->Title . ')';
0 ignored issues
show
Documentation introduced by
The property Title 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...
615
	}
616
617
	/**
618
	 * @deprecated since version 4.0
619
	 */
620
	public function getFieldName($field = false) {
621
		Deprecation::notice('4.0', "getFieldName({$field}) is deprecated");
622
		return ($field) ? "Fields[".$this->ID."][".$field."]" : "Fields[".$this->ID."]";
623
	}
624
625
	/**
626
	 * @deprecated since version 4.0
627
	 */
628
	public function getSettingName($field) {
629
		Deprecation::notice('4.0', "getSettingName({$field}) is deprecated");
630
		$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...
631
632
		return $name . '[' . $field .']';
633
	}
634
635
	/**
636
	 * Append custom validation fields to the default 'Validation'
637
	 * section in the editable options view
638
	 *
639
	 * @return FieldList
640
	 */
641
	public function getFieldValidationOptions() {
642
		$fields = new FieldList(
643
			CheckboxField::create('Required', _t('EditableFormField.REQUIRED', 'Is this field Required?'))
644
				->setDescription(_t('EditableFormField.REQUIRED_DESCRIPTION', 'Please note that conditional fields can\'t be required')),
645
			TextField::create('CustomErrorMessage', _t('EditableFormField.CUSTOMERROR','Custom Error Message'))
646
		);
647
648
		$this->extend('updateFieldValidationOptions', $fields);
649
650
		return $fields;
651
	}
652
653
	/**
654
	 * Return a FormField to appear on the front end. Implement on
655
	 * your subclass.
656
	 *
657
	 * @return FormField
658
	 */
659
	public function getFormField() {
660
		user_error("Please implement a getFormField() on your EditableFormClass ". $this->ClassName, E_USER_ERROR);
661
	}
662
663
	/**
664
	 * Updates a formfield with extensions
665
	 *
666
	 * @param FormField $field
667
	 */
668
	public function doUpdateFormField($field) {
669
		$this->extend('beforeUpdateFormField', $field);
670
		$this->updateFormField($field);
671
		$this->extend('afterUpdateFormField', $field);
672
	}
673
674
	/**
675
	 * Updates a formfield with the additional metadata specified by this field
676
	 *
677
	 * @param FormField $field
678
	 */
679
	protected function updateFormField($field) {
680
		// set the error / formatting messages
681
		$field->setCustomValidationMessage($this->getErrorMessage());
682
683
		// set the right title on this field
684
		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...
685
			// Since this field expects raw html, safely escape the user data prior
686
			$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...
687
		}
688
689
		// if this field is required add some
690
		if($this->Required) {
0 ignored issues
show
Documentation introduced by
The property Required 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...
691
			// Required validation can conflict so add the Required validation messages as input attributes
692
			$errorMessage = $this->getErrorMessage()->HTML();
693
			$field->addExtraClass('requiredField');
694
			$field->setAttribute('data-rule-required', 'true');
695
			$field->setAttribute('data-msg-required', $errorMessage);
0 ignored issues
show
Bug introduced by
It seems like $errorMessage defined by $this->getErrorMessage()->HTML() on line 692 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...
696
697
			if($identifier = UserDefinedForm::config()->required_identifier) {
698
				$title = $field->Title() . " <span class='required-identifier'>". $identifier . "</span>";
699
				$field->setTitle($title);
700
			}
701
		}
702
703
		// if this field has an extra class
704
		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...
705
			$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...
706
		}
707
	}
708
709
	/**
710
	 * Return the instance of the submission field class
711
	 *
712
	 * @return SubmittedFormField
713
	 */
714
	public function getSubmittedFormField() {
715
		return new SubmittedFormField();
716
	}
717
718
719
	/**
720
	 * Show this form field (and its related value) in the reports and in emails.
721
	 *
722
	 * @return bool
723
	 */
724
	public function showInReports() {
725
		return true;
726
	}
727
728
	/**
729
	 * Return the error message for this field. Either uses the custom
730
	 * one (if provided) or the default SilverStripe message
731
	 *
732
	 * @return Varchar
733
	 */
734
	public function getErrorMessage() {
735
		$title = strip_tags("'". ($this->Title ? $this->Title : $this->Name) . "'");
0 ignored issues
show
Documentation introduced by
The property Title 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...
736
		$standard = sprintf(_t('Form.FIELDISREQUIRED', '%s is required').'.', $title);
737
738
		// only use CustomErrorMessage if it has a non empty value
739
		$errorMessage = (!empty($this->CustomErrorMessage)) ? $this->CustomErrorMessage : $standard;
0 ignored issues
show
Documentation introduced by
The property CustomErrorMessage 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...
740
741
		return DBField::create_field('Varchar', $errorMessage);
742
	}
743
744
	/**
745
	 * Validate the field taking into account its custom rules.
746
	 *
747
	 * @param Array $data
748
	 * @param UserForm $form
749
	 *
750
	 * @return boolean
751
	 */
752
	public function validateField($data, $form) {
753
		if($this->Required && $this->DisplayRules()->Count() == 0) {
0 ignored issues
show
Documentation introduced by
The property Required 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...
754
			$formField = $this->getFormField();
755
756
			if(isset($data[$this->Name])) {
757
				$formField->setValue($data[$this->Name]);
758
			}
759
760
			if(
761
				!isset($data[$this->Name]) ||
762
				!$data[$this->Name] ||
763
				!$formField->validate($form->getValidator())
0 ignored issues
show
Bug introduced by
It seems like $form->getValidator() can be null; however, validate() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
764
			) {
765
				$form->addErrorMessage($this->Name, $this->getErrorMessage()->HTML(), 'error', false);
0 ignored issues
show
Bug introduced by
It seems like $this->getErrorMessage()->HTML() targeting DBField::HTML() can also be of type array; however, Form::addErrorMessage() 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...
766
			}
767
		}
768
769
		return true;
770
	}
771
772
	/**
773
	 * Invoked by UserFormUpgradeService to migrate settings specific to this field from CustomSettings
774
	 * to the field proper
775
	 *
776
	 * @param array $data Unserialised data
777
	 */
778
	public function migrateSettings($data) {
779
		// Map 'Show' / 'Hide' to boolean
780
		if(isset($data['ShowOnLoad'])) {
781
			$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...
782
			unset($data['ShowOnLoad']);
783
		}
784
785
		// Migrate all other settings
786
		foreach($data as $key => $value) {
787
			if($this->hasField($key)) {
788
				$this->setField($key, $value);
789
			}
790
		}
791
	}
792
793
	/**
794
	 * Get the formfield to use when editing this inline in gridfield
795
	 *
796
	 * @param string $column name of column
797
	 * @param array $fieldClasses List of allowed classnames if this formfield has a selectable class
798
	 * @return FormField
799
	 */
800
	public function getInlineClassnameField($column, $fieldClasses) {
801
		return DropdownField::create($column, false, $fieldClasses);
802
	}
803
804
	/**
805
	 * Get the formfield to use when editing the title inline
806
	 *
807
	 * @param string $column
808
	 * @return FormField
809
	 */
810
	public function getInlineTitleField($column) {
811
		return TextField::create($column, false)
812
			->setAttribute('placeholder', _t('EditableFormField.TITLE', 'Title'))
813
			->setAttribute('data-placeholder', _t('EditableFormField.TITLE', 'Title'));
814
	}
815
816
	/**
817
	 * Get the JS expression for selecting the holder for this field
818
	 *
819
	 * @return string
820
	 */
821
	public function getSelectorHolder() {
822
		return "$(\"#{$this->Name}\")";
823
	}
824
825
	/**
826
	 * Gets the JS expression for selecting the value for this field
827
	 *
828
	 * @param EditableCustomRule $rule Custom rule this selector will be used with
829
	 * @param bool $forOnLoad Set to true if this will be invoked on load
830
	 */
831
	public function getSelectorField(EditableCustomRule $rule, $forOnLoad = false) {
832
		return "$(\"input[name='{$this->Name}']\")";
833
	}
834
835
836
	/**
837
	 * Get the list of classes that can be selected and used as data-values
838
	 *
839
	 * @param $includeLiterals Set to false to exclude non-data fields
840
	 * @return array
841
	 */
842
	public function getEditableFieldClasses($includeLiterals = true) {
843
		$classes = ClassInfo::getValidSubClasses('EditableFormField');
844
845
		// Remove classes we don't want to display in the dropdown.
846
		$editableFieldClasses = array();
847
		foreach ($classes as $class) {
848
			// Skip abstract / hidden classes
849
			if(Config::inst()->get($class, 'abstract', Config::UNINHERITED) || Config::inst()->get($class, 'hidden')
850
			) {
851
				continue;
852
			}
853
854
			if(!$includeLiterals && Config::inst()->get($class, 'literal')) {
855
				continue;
856
			}
857
858
			$singleton = singleton($class);
859
			if(!$singleton->canCreate()) {
860
				continue;
861
			}
862
863
			$editableFieldClasses[$class] = $singleton->i18n_singular_name();
864
		}
865
866
		asort($editableFieldClasses);
867
		return $editableFieldClasses;
868
	}
869
870
	/**
871
	 * @return EditableFormFieldValidator
872
	 */
873
	public function getCMSValidator() {
874
		return EditableFormFieldValidator::create()
875
			->setRecord($this);
876
	}
877
}
878