Passed
Pull Request — master (#770)
by Jess
03:11
created

EditableCustomRule::validateAgainstFormData()   D

Complexity

Conditions 10
Paths 10

Size

Total Lines 45
Code Lines 35

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 10
eloc 35
nc 10
nop 1
dl 0
loc 45
rs 4.8196
c 0
b 0
f 0

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace SilverStripe\UserForms\Model;
4
5
use LogicException;
6
use SilverStripe\CMS\Controllers\CMSMain;
7
use SilverStripe\Control\Controller;
8
use SilverStripe\Core\Convert;
9
use SilverStripe\ORM\DataObject;
10
use Silverstripe\Versioned\Versioned;
11
12
/**
13
 * A custom rule for showing / hiding an EditableFormField
14
 * based the value of another EditableFormField.
15
 *
16
 * @method EditableFormField Parent()
17
 * @package userforms
18
 *
19
 * @property string Display
20
 * @property string ConditionOption
21
 * @property string FieldValue
22
 */
23
class EditableCustomRule extends DataObject
24
{
25
    private static $condition_options = [
0 ignored issues
show
introduced by
The private property $condition_options is not used, and could be removed.
Loading history...
26
        'IsBlank'               => 'Is blank',
27
        'IsNotBlank'            => 'Is not blank',
28
        'HasValue'              => 'Equals',
29
        'ValueNot'              => 'Doesn\'t equal',
30
        'ValueLessThan'         => 'Less than',
31
        'ValueLessThanEqual'    => 'Less than or equal',
32
        'ValueGreaterThan'      => 'Greater than',
33
        'ValueGreaterThanEqual' => 'Greater than or equal'
34
    ];
35
36
    private static $db = [
0 ignored issues
show
introduced by
The private property $db is not used, and could be removed.
Loading history...
37
        'Display'         => 'Enum("Show,Hide")',
38
        'ConditionOption' => 'Enum("IsBlank,IsNotBlank,HasValue,ValueNot,ValueLessThan,ValueLessThanEqual,ValueGreaterThan,ValueGreaterThanEqual")',
39
        'FieldValue'      => 'Varchar(255)'
40
    ];
41
42
    private static $has_one = [
0 ignored issues
show
introduced by
The private property $has_one is not used, and could be removed.
Loading history...
43
        'Parent'         => EditableFormField::class,
44
        'ConditionField' => EditableFormField::class
45
    ];
46
47
    /**
48
     * Built in extensions required
49
     *
50
     * @config
51
     * @var array
52
     */
53
    private static $extensions = [
0 ignored issues
show
introduced by
The private property $extensions is not used, and could be removed.
Loading history...
54 1
        Versioned::class . "('Stage', 'Live')"
55
    ];
56 1
57 1
    private static $table_name = 'EditableCustomRule';
0 ignored issues
show
introduced by
The private property $table_name is not used, and could be removed.
Loading history...
58
59
    /**
60
     * @param Member $member
0 ignored issues
show
Bug introduced by
The type SilverStripe\UserForms\Model\Member was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
61
     * @return bool
62
     */
63
    public function canDelete($member = null)
64
    {
65
        return $this->canEdit($member);
66
    }
67
68
    /**
69
     * @param Member $member
70
     * @return bool
71
     */
72
    public function canEdit($member = null)
73
    {
74
        return $this->Parent()->canEdit($member);
75
    }
76
77
    /**
78
     * @param Member $member
79
     * @return bool
80
     */
81
    public function canView($member = null)
82
    {
83
        return $this->Parent()->canView($member);
84
    }
85
86
    /**
87
     * Return whether a user can create an object of this type
88
     *
89
     * @param Member $member
90
     * @param array  $context Virtual parameter to allow context to be passed in to check
91
     * @return bool
92
     */
93
    public function canCreate($member = null, $context = [])
94
    {
95
        // Check parent page
96
        $parent = $this->getCanCreateContext(func_get_args());
97
        if ($parent) {
0 ignored issues
show
introduced by
$parent is of type SilverStripe\ORM\DataObject, thus it always evaluated to true.
Loading history...
98
            return $parent->canEdit($member);
99
        }
100
101
        // Fall back to secure admin permissions
102
        return parent::canCreate($member);
103
    }
104
105
    /**
106
     * Helper method to check the parent for this object
107
     *
108
     * @param array $args List of arguments passed to canCreate
109
     * @return DataObject Some parent dataobject to inherit permissions from
110
     */
111
    protected function getCanCreateContext($args)
112
    {
113
        // Inspect second parameter to canCreate for a 'Parent' context
114
        if (isset($args[1]['Parent'])) {
115
            return $args[1]['Parent'];
116
        }
117
        // Hack in currently edited page if context is missing
118
        if (Controller::has_curr() && Controller::curr() instanceof CMSMain) {
119
            return Controller::curr()->currentPage();
120
        }
121
122
        // No page being edited
123
        return null;
124
    }
125
126
    /**
127
     * @param Member $member
128
     * @return bool
129
     */
130
    public function canPublish($member = null)
131
    {
132
        return $this->canEdit($member);
133
    }
134
135
    /**
136
     * @param Member $member
137
     * @return bool
138
     */
139
    public function canUnpublish($member = null)
140
    {
141
        return $this->canDelete($member);
142
    }
143
144
    /**
145
     * Substitutes configured rule logic with it's JS equivalents and returns them as array elements
146
     *
147
     * @return array
148
     * @throws LogicException If the provided condition option was not able to be handled
149
     */
150
    public function buildExpression()
151
    {
152
        /** @var EditableFormField $formFieldWatch */
153
        $formFieldWatch = $this->ConditionField();
0 ignored issues
show
Bug introduced by
The method ConditionField() does not exist on SilverStripe\UserForms\Model\EditableCustomRule. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

153
        /** @scrutinizer ignore-call */ 
154
        $formFieldWatch = $this->ConditionField();
Loading history...
154
        //Encapsulated the action to the object
155
        $action = $formFieldWatch->getJsEventHandler();
156
157
        // is this field a special option field
158
        $checkboxField = $formFieldWatch->isCheckBoxField();
159 2
        $radioField = $formFieldWatch->isRadioField();
160
        $target = sprintf('$("%s")', $formFieldWatch->getSelectorFieldOnly());
161
        $fieldValue = Convert::raw2js($this->FieldValue);
162 2
163
        $conditionOptions = [
164 2
            'ValueLessThan'         => '<',
165
            'ValueLessThanEqual'    => '<=',
166
            'ValueGreaterThan'      => '>',
167 2
            'ValueGreaterThanEqual' => '>='
168 2
        ];
169 2
170 2
        // and what should we evaluate
171
        switch ($this->ConditionOption) {
172
            case 'IsNotBlank':
173 2
            case 'IsBlank':
174 2
                $expression = ($checkboxField || $radioField) ? "!{$target}.is(\":checked\")" : "{$target}.val() == ''";
0 ignored issues
show
introduced by
The condition $radioField is always false.
Loading history...
175 2
                if ($this->ConditionOption == 'IsNotBlank') {
176
                    //Negate
177 2
                    $expression = "!({$expression})";
178
                }
179 2
                break;
180 2
            case 'HasValue':
181 2
            case 'ValueNot':
182
                if ($checkboxField) {
0 ignored issues
show
introduced by
The condition $checkboxField is always false.
Loading history...
183
                    if ($formFieldWatch->isCheckBoxGroupField()) {
184
                        $expression = sprintf(
185
                            "$.inArray('%s', %s.filter(':checked').map(function(){ return $(this).val();}).get()) > -1",
186
                            $fieldValue,
187
                            $target
188 2
                        );
189 2
                    } else {
190 2
                        $expression = "{$target}.prop('checked')";
191
                    }
192
                } elseif ($radioField) {
0 ignored issues
show
introduced by
The condition $radioField is always false.
Loading history...
193
                    // We cannot simply get the value of the radio group, we need to find the checked option first.
194
                    $expression = sprintf(
195
                        '%s.closest(".field, .control-group").find("input:checked").val() == "%s"',
196
                        $target,
197
                        $fieldValue
198
                    );
199
                } else {
200 2
                    $expression = sprintf('%s.val() == "%s"', $target, $fieldValue);
201
                }
202
203
                if ($this->ConditionOption == 'ValueNot') {
204
                    //Negate
205
                    $expression = "!({$expression})";
206
                }
207
                break;
208 2
            case 'ValueLessThan':
209
            case 'ValueLessThanEqual':
210
            case 'ValueGreaterThan':
211 2
            case 'ValueGreaterThanEqual':
212
                $expression = sprintf(
213
                    '%s.val() %s parseFloat("%s")',
214
                    $target,
215 2
                    $conditionOptions[$this->ConditionOption],
216 1
                    $fieldValue
217 1
                );
218 1
                break;
219 1
            default:
220 1
                throw new LogicException("Unhandled rule {$this->ConditionOption}");
221 1
                break;
222 1
        }
223 1
224
        $result = [
225 1
            'operation' => $expression,
226 1
            'event'     => $action,
227
        ];
228
229
        return $result;
230 2
    }
231
232
233 2
    /**
234 2
     * Determines whether the rule is satisfied, based on provided form data.
235 2
     * Used for php validation of required conditional fields
236
     *
237 2
     * @param array $data Submitted form data
238
     * @return boolean
239
     */
240
    public function validateAgainstFormData($data)
241
    {
242
243
        $controllingField = EditableFormField::get()->byID($this->ConditionFieldID);
0 ignored issues
show
Bug Best Practice introduced by
The property ConditionFieldID does not exist on SilverStripe\UserForms\Model\EditableCustomRule. Since you implemented __get, consider adding a @property annotation.
Loading history...
244
245
        if (!isset($data[$controllingField->Name])) {
246
            return false;
247 2
        }
248
249 2
        $valid = false;
250
251
        $targetFieldValue = $this->FieldValue;
252 1
        $actualFieldValue = $data[$controllingField->Name];
253
254
        switch ($this->ConditionOption) {
255
            case 'IsNotBlank':
256
                $valid = ($actualFieldValue !== '');
257
                break;
258
            case 'IsBlank':
259
                $valid = ($actualFieldValue === '');
260
                break;
261
            case 'HasValue':
262
                $valid = ($actualFieldValue === $targetFieldValue);
263
                break;
264
            case 'ValueNot':
265
                $valid = ($actualFieldValue !== $targetFieldValue);
266
                break;
267
            case 'ValueLessThan':
268
                $valid = ($actualFieldValue < $targetFieldValue);
269
                break;
270
            case 'ValueLessThanEqual':
271
                $valid = ($actualFieldValue <= $targetFieldValue);
272
                break;
273
            case 'ValueGreaterThan':
274
                $valid = ($actualFieldValue > $targetFieldValue);
275
                break;
276
            case 'ValueGreaterThanEqual':
277
                $valid = ($actualFieldValue >= $targetFieldValue);
278
                break;
279
            default:
280
                throw new LogicException("Unhandled rule {$this->ConditionOption}");
281
                break;
282
        }
283
284
        return $valid;
285
    }
286
287
288
    /**
289
     * Returns the opposite visibility function for the value of the initial visibility field, e.g. show/hide. This
290
     * will toggle the "hide" class either way, which is handled by CSS.
291
     *
292
     * @param string $text
293
     * @return string
294
     */
295
    public function toggleDisplayText($text)
296
    {
297
        return (strtolower($text) === 'hide') ? 'removeClass("hide")' : 'addClass("hide")';
298
    }
299
}
300