Moo_EditableField::getSettingName()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 1
crap 1
1
<?php
2
3
/**
4
 * Moo_EditableField is a base class for editable fields to extend.
5
 *
6
 * @author  Mohamed Alsharaf <[email protected]>
7
 *
8
 * @property string $Title
9
 * @property string $Name
10
 * @property int    $Required
11
 * @property string $CustomErrorMessage
12
 * @property string $CustomSettings
13
 */
14
class Moo_EditableField extends DataObject
15
{
16
    private static $db = [
17
        'Name'               => 'Varchar',
18
        'Title'              => 'Varchar(255)',
19
        'Required'           => 'Boolean',
20
        'CustomErrorMessage' => 'Varchar(255)',
21
        'CustomSettings'     => 'Text',
22
    ];
23
    private static $singular_name = 'Editable Field';
24
    private static $plural_name   = 'Editable Fields';
25
26
    /**
27
     * List of fields names part of the custom settings.
28
     *
29
     * @var array
30
     */
31
    protected $customSettingsFields = [];
32
33
    /**
34
     * Instance of FormField.
35
     *
36
     * @var FormField
37
     */
38
    protected $field;
39
40
    /**
41
     * To prevent having tables for each fields minor settings we store it as
42
     * a serialized array in the database.
43
     *
44
     * @return array Return all the Settings
45
     */
46 8
    public function getSettings()
47 1
    {
48 8
        return (!empty($this->CustomSettings)) ? unserialize($this->CustomSettings) : [];
49
    }
50
51
    /**
52
     * Set the custom settings for this field as we store the minor details in
53
     * a serialized array in the database.
54
     *
55
     * @param array $settings the custom settings
56
     */
57 8
    public function setSettings($settings = [])
58 1
    {
59 8
        $this->CustomSettings = serialize($settings);
60 8
    }
61
62
    /**
63
     * Set a given field setting. Appends the option to the settings or overrides
64
     * the existing value.
65
     *
66
     * @param string $key
67
     * @param string $value
68
     */
69 1
    public function setSetting($key, $value)
70
    {
71 1
        $settings       = $this->getSettings();
72 1
        $settings[$key] = $value;
73
74 1
        $this->setSettings($settings);
75 1
    }
76
77
    /**
78
     * Return just one custom setting or empty string if it does
79
     * not exist.
80
     *
81
     * @param string $setting
82
     *
83
     * @return string
84
     */
85 3
    public function getSetting($setting)
86
    {
87 3
        $settings = $this->getSettings();
88 3
        if (isset($settings) && count($settings) > 0) {
89 2
            if (isset($settings[$setting])) {
90 2
                return $settings[$setting];
91
            }
92
        }
93
94 3
        return '';
95
    }
96
97
    /**
98
     * Get the path to the icon for this field type, relative to the site root.
99
     *
100
     * @return string
101
     */
102 1
    public function getIcon()
103
    {
104 1
        return 'editablefield/images/formfields/' . strtolower(substr($this->class, 4)) . '.png';
105
    }
106
107
    /**
108
     * Get the icon HTML tag.
109
     *
110
     * @return string
111
     */
112 1
    public function getIconTag()
113
    {
114 1
        return '<img src="' . $this->getIcon() . '"/>';
115
    }
116
117 1
    public function getCMSFields()
118
    {
119 1
        $fields = parent::getCMSFields();
120
121
        // Remove field to be recreated in separate tabs
122 1
        $fields->removeByName([
123 1
            'Required', 'CustomErrorMessage', 'CustomSettings', 'Options',
124 1
        ]);
125
126
        // Add default field configurations "custom settings"
127 1
        $fields->addFieldsToTab('Root.FieldConfiguration', [
128 1
            new TextField(
129 1
                $this->getSettingName('RightTitle'), _t('Moo_EditableField.RIGHTTITLE', 'Right Title'),
130 1
                $this->getSetting('RightTitle')
131 1
            ),
132 1
        ]);
133
134
        // Add default validation fields
135
        $validateFields = [
136 1
            new CheckboxField('Required', _t('Moo_EditableField.REQUIRED', 'Is this field Required?'), $this->Required),
137 1
            new TextField('CustomErrorMessage', _t('Moo_EditableField.CUSTOMERROR', 'Custom Error Message'), $this->CustomErrorMessage),
138 1
        ];
139 1
        $fields->addFieldsToTab('Root.Validation', $validateFields);
140
141
        // Add extra field configurations "custom settings" from the subclass
142 1
        if (method_exists($this, 'getFieldConfiguration')) {
143 1
            $fields->addFieldsToTab('Root.FieldConfiguration', $this->getFieldConfiguration());
0 ignored issues
show
Bug introduced by
The method getFieldConfiguration() does not exist on Moo_EditableField. Did you maybe mean config()?

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...
144 1
        }
145
146
        // Add extra validation fields from the subclass
147 1
        if (method_exists($this, 'getFieldValidationOptions')) {
148
            $fields->addFieldsToTab('Root.Validation', $this->getFieldValidationOptions());
0 ignored issues
show
Documentation Bug introduced by
The method getFieldValidationOptions does not exist on object<Moo_EditableField>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
149
        }
150
151 1
        return $fields;
152
    }
153
154 1
    public function getCMSValidator()
155
    {
156 1
        return new RequiredFields(
157 1
            'Title', 'Name'
158 1
        );
159
    }
160
161
    /**
162
     * Returns the Title for rendering in the front-end (with XML values escaped).
163
     *
164
     * @return string
165
     */
166 8
    public function getTitle()
167
    {
168 8
        return Convert::raw2att($this->getField('Title'));
0 ignored issues
show
Bug Compatibility introduced by
The expression \Convert::raw2att($this->getField('Title')); of type array|string adds the type array to the return on line 168 which is incompatible with the return type of the parent method DataObject::getTitle of type string.
Loading history...
169
    }
170
171
    /**
172
     * Generate a name for the Setting field.
173
     *
174
     * @param string $field
175
     *
176
     * @return string
177
     */
178 1
    public function getSettingName($field)
179
    {
180 1
        return 'CustomSettings[' . $field . ']';
181
    }
182
183
    /**
184
     * How to save the data submitted in this field into the database object
185
     * which this field represents.
186
     *
187
     * Any class's which call this should also call
188
     * {@link parent::populateFromPostData()} to ensure that this method is
189
     * called
190
     *
191
     * @throws ValidationException
192
     */
193 8
    public function onBeforeWrite()
194
    {
195 8
        $return = parent::onBeforeWrite();
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $return is correct as parent::onBeforeWrite() (which targets DataObject::onBeforeWrite()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
196
197
        // Field name must be unique
198 8
        $exists = self::get()->filter('Name', $this->Name)->exclude('ID', $this->ID);
199 8
        if ($exists->count()) {
200
            throw new ValidationException(
201
                _t('Moo_EditableField.UNIQUENAME', 'Field name "{name}" must be unique', '',
202
                    ['name' => $this->Name])
0 ignored issues
show
Documentation introduced by
array('name' => $this->Name) is of type array<string,string,{"name":"string"}>, but the function expects a string.

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...
203
            );
204
        }
205
206
        // Filter field name from un-supported HTML attribute value
207 8
        $this->Name = preg_replace('/[^a-zA-Z0-9_]+/', '', $this->Name);
208
209
        // Get custom settings from the current object of request POST
210 8
        $customSettings = $this->getSettings();
211 8
        if (empty($customSettings)) {
212 8
            $customSettings = (array) Controller::curr()->getRequest()->postVar('CustomSettings');
213 8
        }
214
215
        // Filter out any un-supported settings by the subclasss
216 8
        if (!empty($this->customSettingsFields)) {
217 5
            $customSettings = array_intersect_key($customSettings, array_flip((array) $this->customSettingsFields));
218 5
        }
219
220
        // Set the filtered settings
221 8
        $this->setSettings($customSettings);
222
223 8
        return $return;
224
    }
225
226
    /**
227
     * Return a FormField.
228
     *
229
     * @return FormField
230
     */
231 1
    public function getFormField()
232
    {
233 1
        if (null === $this->field) {
234 1
            $this->field = $this->initFormField();
235 1
        }
236
237 1
        return $this->field;
238
    }
239
240
    /**
241
     * Initiate a form field.
242
     *
243
     * @return FormField
244
     */
245
    protected function initFormField()
246
    {
247
        throw new DomainException(sprintf('%s must be implemented by the class %s', __METHOD__, $this->class));
248
    }
249
250
    /**
251
     * Return the error message for this field. Either uses the custom
252
     * one (if provided) or the default SilverStripe message.
253
     *
254
     * @return Varchar
255
     */
256
    public function getErrorMessage()
257
    {
258
        $title    = strip_tags("'" . ($this->Title ? $this->Title : $this->Name) . "'");
259
        $standard = _t('Form.FIELDISREQUIRED', '{name} is required', ['name' => $title]);
0 ignored issues
show
Documentation introduced by
array('name' => $title) is of type array<string,string,{"name":"string"}>, but the function expects a string.

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...
260
261
        // only use CustomErrorMessage if it has a non empty value
262
        $errorMessage = (!empty($this->CustomErrorMessage)) ? $this->CustomErrorMessage : $standard;
263
264
        return DBField::create_field('Varchar', $errorMessage);
265
    }
266
267 1
    public function onBeforeDuplicate(Moo_EditableField $field)
268
    {
269
        // The name field must be unique, this is temporary workaround
270 1
        $this->setField('Name', $field->Name . uniqid());
271 1
    }
272
}
273