Completed
Push — master ( 67ead9...61a703 )
by Daniel
12s
created

EditableCustomRule   A

Complexity

Total Complexity 31

Size/Duplication

Total Lines 238
Duplicated Lines 9.66 %

Coupling/Cohesion

Components 2
Dependencies 4

Test Coverage

Coverage 45.45%

Importance

Changes 0
Metric Value
wmc 31
lcom 2
cbo 4
dl 23
loc 238
ccs 35
cts 77
cp 0.4545
rs 9.8
c 0
b 0
f 0

11 Methods

Rating   Name   Duplication   Size   Complexity  
A doPublish() 0 4 1
A doDeleteFromStage() 0 4 1
A canDelete() 0 4 1
A canEdit() 0 4 1
A canView() 0 4 1
A canCreate() 10 11 2
A getCanCreateContext() 13 14 4
A canPublish() 0 4 1
A canUnpublish() 0 4 1
C buildExpression() 0 80 16
A toggleDisplayText() 0 4 2

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php
2
3
/**
4
 * A custom rule for showing / hiding an EditableFormField
5
 * based the value of another EditableFormField.
6
 *
7
 * @method EditableFormField Parent()
8
 * @package userforms
9
 *
10
 * @property string Display
11
 * @property string ConditionOption
12
 * @property string FieldValue
13
 */
14
class EditableCustomRule 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...
15
{
16
17
    private static $condition_options = array(
0 ignored issues
show
Unused Code introduced by
The property $condition_options 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...
18
        "IsBlank" => "Is blank",
19
        "IsNotBlank" => "Is not blank",
20
        "HasValue" => "Equals",
21
        "ValueNot" => "Doesn't equal",
22
        "ValueLessThan" => "Less than",
23
        "ValueLessThanEqual" => "Less than or equal",
24
        "ValueGreaterThan" => "Greater than",
25
        "ValueGreaterThanEqual" => "Greater than or equal"
26
    );
27
28
    private static $db = array(
0 ignored issues
show
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...
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
29
        'Display' => 'Enum("Show,Hide")',
30
        'ConditionOption' => 'Enum("IsBlank,IsNotBlank,HasValue,ValueNot,ValueLessThan,ValueLessThanEqual,ValueGreaterThan,ValueGreaterThanEqual")',
31
        'FieldValue' => 'Varchar(255)'
32
    );
33
34
    private static $has_one = array(
0 ignored issues
show
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...
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
35
        'Parent' => 'EditableFormField',
36
        'ConditionField' => 'EditableFormField'
37
    );
38
39
    /**
40
     * Built in extensions required
41
     *
42
     * @config
43
     * @var array
44
     */
45
    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...
46
        "Versioned('Stage', 'Live')"
47
    );
48
49
    /**
50
     * Publish this custom rule to the live site
51
     *
52
     * Wrapper for the {@link Versioned} publish function
53
     */
54 1
    public function doPublish($fromStage, $toStage, $createNewVersion = false)
55
    {
56 1
        $this->publish($fromStage, $toStage, $createNewVersion);
0 ignored issues
show
Bug introduced by
The method publish() does not exist on EditableCustomRule. Did you maybe mean doPublish()?

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...
57 1
    }
58
59
    /**
60
     * Delete this custom rule from a given stage
61
     *
62
     * Wrapper for the {@link Versioned} deleteFromStage function
63
     */
64
    public function doDeleteFromStage($stage)
65
    {
66
        $this->deleteFromStage($stage);
0 ignored issues
show
Bug introduced by
The method deleteFromStage() does not exist on EditableCustomRule. 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...
67
    }
68
69
70
    /**
71
     * @param Member $member
72
     * @return bool
73
     */
74
    public function canDelete($member = null)
75
    {
76
        return $this->canEdit($member);
77
    }
78
79
    /**
80
     * @param Member $member
81
     * @return bool
82
     */
83
    public function canEdit($member = null)
84
    {
85
        return $this->Parent()->canEdit($member);
86
    }
87
88
    /**
89
     * @param Member $member
90
     * @return bool
91
     */
92
    public function canView($member = null)
93
    {
94
        return $this->Parent()->canView($member);
95
    }
96
97
    /**
98
     * Return whether a user can create an object of this type
99
     *
100
     * @param Member $member
101
     * @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...
102
     * @return bool
103
     */
104 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...
105
    {
106
        // Check parent page
107
        $parent = $this->getCanCreateContext(func_get_args());
108
        if ($parent) {
109
            return $parent->canEdit($member);
110
        }
111
112
        // Fall back to secure admin permissions
113
        return parent::canCreate($member);
114
    }
115
116
    /**
117
     * Helper method to check the parent for this object
118
     *
119
     * @param array $args List of arguments passed to canCreate
120
     * @return DataObject Some parent dataobject to inherit permissions from
121
     */
122 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...
123
    {
124
        // Inspect second parameter to canCreate for a 'Parent' context
125
        if (isset($args[1]['Parent'])) {
126
            return $args[1]['Parent'];
127
        }
128
        // Hack in currently edited page if context is missing
129
        if (Controller::has_curr() && Controller::curr() instanceof CMSMain) {
130
            return Controller::curr()->currentPage();
131
        }
132
133
        // No page being edited
134
        return null;
135
    }
136
137
    /**
138
     * @param Member $member
139
     * @return bool
140
     */
141
    public function canPublish($member = null)
142
    {
143
        return $this->canEdit($member);
144
    }
145
146
    /**
147
     * @param Member $member
148
     * @return bool
149
     */
150
    public function canUnpublish($member = null)
151
    {
152
        return $this->canDelete($member);
153
    }
154
155
    /**
156
     * Substitutes configured rule logic with it's JS equivalents and returns them as array elements
157
     * @return array
158
     */
159 2
    public function buildExpression()
160
    {
161
        /** @var EditableFormField $formFieldWatch */
162 2
        $formFieldWatch = $this->ConditionField();
0 ignored issues
show
Documentation Bug introduced by
The method ConditionField does not exist on object<EditableCustomRule>? 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...
163
        //Encapsulated the action to the object
164 2
        $action = $formFieldWatch->getJsEventHandler();
165
166
        // is this field a special option field
167 2
        $checkboxField = $formFieldWatch->isCheckBoxField();
168 2
        $radioField = $formFieldWatch->isRadioField();
169 2
        $target = sprintf('$("%s")', $formFieldWatch->getSelectorFieldOnly());
170 2
        $fieldValue = Convert::raw2js($this->FieldValue);
171
172
        $conditionOptions = array(
173 2
            'ValueLessThan'         => '<',
174
            'ValueLessThanEqual'    => '<=',
175
            'ValueGreaterThan'      => '>',
176
            'ValueGreaterThanEqual' => '>='
177
        );
178
        // and what should we evaluate
179 2
        switch ($this->ConditionOption) {
180 2
            case 'IsNotBlank':
181 2
            case 'IsBlank':
182
                $expression = ($checkboxField || $radioField) ? "!{$target}.is(\":checked\")" : "{$target}.val() == ''";
183
                if ($this->ConditionOption == 'IsNotBlank') {
184
                    //Negate
185
                    $expression = "!({$expression})";
186
                }
187
                break;
188 2
            case 'HasValue':
189 1
            case 'ValueNot':
190 2
                if ($checkboxField) {
191
                    if ($formFieldWatch->isCheckBoxGroupField()) {
192
                        $expression = sprintf(
193
                            "$.inArray('%s', %s.filter(':checked').map(function(){ return $(this).val();}).get()) > -1",
194
                            $fieldValue,
195
                            $target
196
                        );
197
                    } else {
198
                        $expression = "{$target}.prop('checked')";
199
                    }
200 2
                } elseif ($radioField) {
201
                    // We cannot simply get the value of the radio group, we need to find the checked option first.
202
                    $expression = sprintf(
203
                        '%s.closest(".field, .control-group").find("input:checked").val() == "%s"',
204
                        $target,
205
                        $fieldValue
206
                    );
207
                } else {
208 2
                    $expression = sprintf('%s.val() == "%s"', $target, $fieldValue);
209
                }
210
211 2
                if ($this->ConditionOption == 'ValueNot') {
212
                    //Negate
213
                    $expression = "!({$expression})";
214
                }
215 2
                break;
216 1
            case 'ValueLessThan':
217 1
            case 'ValueLessThanEqual':
218 1
            case 'ValueGreaterThan':
219
            case 'ValueGreaterThanEqual':
220 1
                $expression = sprintf(
221 1
                    '%s.val() %s parseFloat("%s")',
222 1
                    $target,
223 1
                    $conditionOptions[$this->ConditionOption],
224 1
                    $fieldValue
225
                );
226 1
                break;
227
            default:
228
                throw new LogicException("Unhandled rule {$this->ConditionOption}");
229
                break;
0 ignored issues
show
Unused Code introduced by
break; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
230
        }
231
232
        $result = array(
233 2
            'operation' => $expression,
234 2
            'event'     => $action,
235
        );
236
237 2
        return $result;
238
    }
239
240
    /**
241
     * Returns the opposite visibility function for the value of the initial visibility field, e.g. show/hide. This
242
     * will toggle the "hide" class either way, which is handled by CSS.
243
     *
244
     * @param string $text
245
     * @return string
246
     */
247 2
    public function toggleDisplayText($text)
248
    {
249 2
        return (strtolower($text) === 'hide') ? 'removeClass("hide")' : 'addClass("hide")';
250
    }
251
}
252