1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace SilverStripe\UserForms\Form; |
4
|
|
|
|
5
|
|
|
use SilverStripe\Dev\Debug; |
6
|
|
|
use SilverStripe\Forms\RequiredFields; |
7
|
|
|
use SilverStripe\ORM\ArrayLib; |
8
|
|
|
use SilverStripe\UserForms\Model\EditableFormField; |
9
|
|
|
|
10
|
|
|
/** |
11
|
|
|
* An extension of RequiredFields which handles conditionally required fields. |
12
|
|
|
* |
13
|
|
|
* A conditionally required is a field that is required, but can be hidden by display rules. |
14
|
|
|
* When it is visible, (according to the submitted form data) it will be validated as required. |
15
|
|
|
* When it is hidden, it will skip required validation. |
16
|
|
|
* |
17
|
|
|
* Required fields will be validated as usual. |
18
|
|
|
* Conditionally required fields will be validated IF the display rules are satisfied in the submitted dataset. |
19
|
|
|
*/ |
20
|
|
|
class UserFormsRequiredFields extends RequiredFields |
21
|
|
|
{ |
22
|
|
|
/** |
23
|
|
|
* Allows validation of fields via specification of a php function for |
24
|
|
|
* validation which is executed after the form is submitted. |
25
|
|
|
* |
26
|
|
|
* @param array $data |
27
|
|
|
* |
28
|
|
|
* @return boolean |
29
|
|
|
*/ |
30
|
|
|
public function php($data) |
31
|
|
|
{ |
32
|
|
|
$valid = true; |
33
|
|
|
$fields = $this->form->Fields(); |
34
|
|
|
|
35
|
|
|
foreach ($fields as $field) { |
36
|
|
|
$valid = ($field->validate($this) && $valid); |
37
|
|
|
} |
38
|
|
|
|
39
|
|
|
if (!$this->required) { |
|
|
|
|
40
|
|
|
return $valid; |
41
|
|
|
} |
42
|
|
|
|
43
|
|
|
foreach ($this->required as $fieldName) { |
44
|
|
|
if (!$fieldName) { |
45
|
|
|
continue; |
46
|
|
|
} |
47
|
|
|
|
48
|
|
|
// get form field |
49
|
|
|
if ($fieldName instanceof FormField) { |
|
|
|
|
50
|
|
|
$formField = $fieldName; |
51
|
|
|
$fieldName = $fieldName->getName(); |
52
|
|
|
} else { |
53
|
|
|
$formField = $fields->dataFieldByName($fieldName); |
54
|
|
|
} |
55
|
|
|
|
56
|
|
|
// get editable form field - owns display rules for field |
57
|
|
|
$editableFormField = $this->getEditableFormFieldByName($fieldName); |
58
|
|
|
|
59
|
|
|
$error = false; |
60
|
|
|
|
61
|
|
|
// validate if there are no display rules or the field is conditionally visible |
62
|
|
|
if (!$this->hasDisplayRules($editableFormField) || |
63
|
|
|
$this->conditionalFieldEnabled($editableFormField, $data)) { |
64
|
|
|
$error = $this->validateRequired($formField, $data); |
65
|
|
|
} |
66
|
|
|
|
67
|
|
|
// handle error case |
68
|
|
|
if ($formField && $error) { |
69
|
|
|
$this->handleError($formField, $fieldName); |
70
|
|
|
|
71
|
|
|
$valid = false; |
72
|
|
|
} |
73
|
|
|
} |
74
|
|
|
|
75
|
|
|
return $valid; |
76
|
|
|
} |
77
|
|
|
|
78
|
|
|
private function getEditableFormFieldByName($name) |
79
|
|
|
{ |
80
|
|
|
return EditableFormField::get()->filter(['name' => $name])->first(); |
81
|
|
|
} |
82
|
|
|
|
83
|
|
|
private function getEditableFormFieldByID($id) |
|
|
|
|
84
|
|
|
{ |
85
|
|
|
return EditableFormField::get_by_id($id); |
|
|
|
|
86
|
|
|
} |
87
|
|
|
|
88
|
|
|
private function hasDisplayRules($field) |
89
|
|
|
{ |
90
|
|
|
return ($field->DisplayRules()->count() > 0); |
91
|
|
|
} |
92
|
|
|
|
93
|
|
|
private function conditionalFieldEnabled($editableFormField, $data) |
94
|
|
|
{ |
95
|
|
|
$displayRules = $editableFormField->DisplayRules(); |
96
|
|
|
|
97
|
|
|
$conjunction = $editableFormField->DisplayRulesConjunctionNice(); |
98
|
|
|
|
99
|
|
|
$displayed = ($editableFormField->ShowOnLoadNice() === 'show'); |
100
|
|
|
|
101
|
|
|
// && start with true and find and condition that doesn't satisfy |
102
|
|
|
// || start with false and find and condition that satisfies |
103
|
|
|
$conditionsSatisfied = ($conjunction === '&&'); |
104
|
|
|
|
105
|
|
|
foreach ($displayRules as $rule) { |
106
|
|
|
$controllingField = EditableFormField::get()->byID($rule->ConditionFieldID); |
107
|
|
|
$valid = false; |
|
|
|
|
108
|
|
|
|
109
|
|
|
if ($controllingField->DisplayRules()->count() > 0) { // controllingField is also a conditional field |
110
|
|
|
// recursively check - if any of the dependant fields are hidden, then this field cannot be visible. |
111
|
|
|
if ($this->conditionalFieldEnabled($controllingField, $data)) { |
112
|
|
|
return false; |
113
|
|
|
}; |
114
|
|
|
} |
115
|
|
|
|
116
|
|
|
$ruleSatisfied = $rule->validateAgainstFormData($data); |
117
|
|
|
|
118
|
|
|
if ($conjunction === '||' && $ruleSatisfied) { |
119
|
|
|
$conditionsSatisfied = true; |
120
|
|
|
break; |
121
|
|
|
} |
122
|
|
|
if ($conjunction === '&&' && !$ruleSatisfied) { |
123
|
|
|
$conditionsSatisfied = false; |
124
|
|
|
break; |
125
|
|
|
} |
126
|
|
|
} |
127
|
|
|
|
128
|
|
|
// initially displayed - condition fails || initially hidden, condition passes |
129
|
|
|
return ($displayed xor $conditionsSatisfied); |
130
|
|
|
} |
131
|
|
|
|
132
|
|
|
private function validateRequired($field, $data) |
133
|
|
|
{ |
134
|
|
|
$error = false; |
135
|
|
|
$fieldName = $field->getName(); |
136
|
|
|
// submitted data for file upload fields come back as an array |
137
|
|
|
$value = isset($data[$fieldName]) ? $data[$fieldName] : null; |
138
|
|
|
|
139
|
|
|
if (is_array($value)) { |
140
|
|
|
if ($formField instanceof FileField && isset($value['error']) && $value['error']) { |
|
|
|
|
141
|
|
|
$error = true; |
142
|
|
|
} else { |
143
|
|
|
$error = (count($value)) ? false : true; |
144
|
|
|
} |
145
|
|
|
} else { |
146
|
|
|
// assume a string or integer |
147
|
|
|
$error = (strlen($value)) ? false : true; |
148
|
|
|
} |
149
|
|
|
|
150
|
|
|
return $error; |
151
|
|
|
} |
152
|
|
|
|
153
|
|
|
private function handleError($formField, $fieldName) |
154
|
|
|
{ |
155
|
|
|
$errorMessage = _t( |
156
|
|
|
'SilverStripe\\Forms\\Form.FIELDISREQUIRED', |
157
|
|
|
'{name} is required', |
158
|
|
|
array( |
159
|
|
|
'name' => strip_tags( |
160
|
|
|
'"' . ($formField->Title() ? $formField->Title() : $fieldName) . '"' |
161
|
|
|
) |
162
|
|
|
) |
163
|
|
|
); |
164
|
|
|
|
165
|
|
|
if ($msg = $formField->getCustomValidationMessage()) { |
166
|
|
|
$errorMessage = $msg; |
167
|
|
|
} |
168
|
|
|
|
169
|
|
|
$this->validationError( |
170
|
|
|
$fieldName, |
171
|
|
|
$errorMessage, |
172
|
|
|
"required" |
173
|
|
|
); |
174
|
|
|
} |
175
|
|
|
} |
176
|
|
|
|
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.