Completed
Pull Request — master (#51)
by Robbie
04:56
created

EditableSpamProtectionField::getFormField()   B

Complexity

Conditions 4
Paths 4

Size

Total Lines 24
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 24
rs 8.6845
c 0
b 0
f 0
cc 4
eloc 13
nc 4
nop 0
1
<?php
2
3
namespace SilverStripe\SpamProtection;
4
5
use SilverStripe\Forms\DropdownField;
6
use SilverStripe\Forms\FieldGroup;
7
use SilverStripe\Forms\FieldList;
8
use SilverStripe\SpamProtection\Extension\FormSpamProtectionExtension;
9
use SilverStripe\ORM\UnsavedRelationList;
10
11
// @todo
12
use EditableEmailField;
13
use EditableFormField;
14
use EditableNumericField;
15
use EditableTextField;
16
17
/**
18
 * Editable Spam Protecter Field. Used with the User Defined Forms module (if
19
 * installed) to allow the user to have captcha fields with their custom forms
20
 *
21
 * @package spamprotection
22
 */
23
// @todo update namespaced for userforms when it is 4.0 compatible
24
if (class_exists('EditableFormField')) {
25
    class EditableSpamProtectionField extends EditableFormField
26
    {
27
        private static $singular_name = 'Spam Protection Field';
0 ignored issues
show
Unused Code introduced by
The property $singular_name 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...
28
29
        private static $plural_name = 'Spam Protection Fields';
0 ignored issues
show
Unused Code introduced by
The property $plural_name 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...
30
31
        private static $table_name = 'EditableSpamProtectionField';
0 ignored issues
show
Unused Code introduced by
The property $table_name 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
         * Fields to include spam detection for
35
         *
36
         * @var array
37
         * @config
38
         */
39
        private static $check_fields = array(
0 ignored issues
show
Unused Code introduced by
The property $check_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...
40
            EditableEmailField::class,
41
            EditableTextField::class,
42
            EditableNumericField::class
43
        );
44
45
        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...
46
            'SpamFieldSettings' => 'Text'
47
        );
48
49
        /**
50
         * @var FormField
51
         */
52
        protected $formField = null;
53
54
        public function getFormField()
55
        {
56
            if ($this->formField) {
57
                return $this->formField;
58
            }
59
60
            // Get protector
61
            $protector = FormSpamProtectionExtension::get_protector();
62
            if (!$protector) {
63
                return false;
64
            }
65
66
            // Extract saved field mappings and update this field.
67
            $fieldMapping = array();
68
            foreach ($this->getCandidateFields() as $otherField) {
69
                $mapSetting = "Map-{$otherField->Name}";
70
                $spamField = $this->spamMapValue($mapSetting);
71
                $fieldMapping[$otherField->Name] = $spamField;
72
            }
73
            $protector->setFieldMapping($fieldMapping);
74
75
            // Generate field
76
            return $protector->getFormField($this->Name, $this->Title, null);
77
        }
78
79
        /**
80
         * @param FormField $field
81
         * @return self
82
         */
83
        public function setFormField(FormField $field)
84
        {
85
            $this->formField = $field;
86
87
            return $this;
88
        }
89
90
        /**
91
         * Gets the list of all candidate spam detectable fields on this field's form
92
         *
93
         * @return DataList
94
         */
95
        protected function getCandidateFields()
96
        {
97
98
            // Get list of all configured classes available for spam detection
99
            $types = self::config()->check_fields;
100
            $typesInherit = array();
101
            foreach ($types as $type) {
102
                $subTypes = ClassInfo::subclassesFor($type);
103
                $typesInherit = array_merge($typesInherit, $subTypes);
104
            }
105
106
            // Get all candidates of the above types
107
            return $this
108
                ->Parent()
109
                ->Fields()
110
                ->filter('ClassName', $typesInherit)
111
                ->exclude('Title', ''); // Ignore this field and those without titles
112
        }
113
114
        /**
115
         * This method is in place for userforms 2.x
116
         *
117
         * @deprecated 3.0 Please use {@link getCMSFields()} instead
118
         */
119
        public function getFieldConfiguration()
120
        {
121
            return $this->getCMSFields();
122
        }
123
124
        /**
125
         * Write the spam field mapping values to a serialised DB field
126
         *
127
         * {@inheritDoc}
128
         */
129
        public function onBeforeWrite()
130
        {
131
            $fieldMap = Convert::json2array($this->SpamFieldSettings);
132
            if (empty($fieldMap)) {
133
                $fieldMap = array();
134
            }
135
136
            foreach ($this->record as $key => $value) {
137
                if (substr($key, 0, 8) === 'spammap-') {
138
                    $fieldMap[substr($key, 8)] = $value;
139
                }
140
            }
141
            $this->setField('SpamFieldSettings', Convert::raw2json($fieldMap));
142
143
            return parent::onBeforeWrite();
144
        }
145
146
        /**
147
         * Used in userforms 3.x and above
148
         *
149
         * {@inheritDoc}
150
         */
151
        public function getCMSFields()
152
        {
153
            /** @var FieldList $fields */
154
            $fields = parent::getCMSFields();
155
156
            // Get protector
157
            $protector = FormSpamProtectionExtension::get_protector();
158
            if (!$protector) {
159
                return $fields;
160
            }
161
162
            if ($this->Parent()->Fields() instanceof UnsavedRelationList) {
163
                return $fields;
164
            }
165
166
            // Each other text field in this group can be assigned a field mapping
167
            $mapGroup = FieldGroup::create()
168
                ->setTitle(_t(__CLASS__.'.SPAMFIELDMAPPING', 'Spam Field Mapping'))
169
                ->setName('SpamFieldMapping')
170
                ->setDescription(_t(
171
                    __CLASS__.'.SPAMFIELDMAPPINGDESCRIPTION',
172
                    'Select the form fields that correspond to any relevant spam protection identifiers'
173
                ));
174
175
            // Generate field specific settings
176
            $mappableFields = Config::inst()->get(FormSpamProtectionExtension::class, 'mappable_fields');
177
            $mappableFieldsMerged = array_combine($mappableFields, $mappableFields);
178
            foreach ($this->getCandidateFields() as $otherField) {
179
                $mapSetting = "Map-{$otherField->Name}";
180
                $fieldOption = DropdownField::create(
181
                    'spammap-' . $mapSetting,
182
                    $otherField->Title,
183
                    $mappableFieldsMerged,
184
                    $this->spamMapValue($mapSetting)
185
                )->setEmptyString('');
186
                $mapGroup->push($fieldOption);
187
            }
188
            $fields->addFieldToTab('Root.Main', $mapGroup);
189
190
            return $fields;
191
        }
192
193
        /**
194
         * Try to retrieve a value for the given spam field map name from the serialised data
195
         *
196
         * @param string $mapSetting
197
         * @return string
198
         */
199
        public function spamMapValue($mapSetting)
200
        {
201
            $map = Convert::json2array($this->SpamFieldSettings);
202
            if (empty($map)) {
203
                $map = array();
204
            }
205
206
            if (array_key_exists($mapSetting, $map)) {
207
                return $map[$mapSetting];
208
            }
209
            return '';
210
        }
211
212
        /**
213
         * Using custom validateField method
214
         * as Spam Protection Field implementations may have their own error messages
215
         * and may not be based on the field being required, e.g. Honeypot Field
216
         *
217
         * @param array $data
218
         * @param Form $form
219
         * @return void
220
         */
221
        public function validateField($data, $form)
222
        {
223
            $formField = $this->getFormField();
224
            $formField->setForm($form);
225
226
            if (isset($data[$this->Name])) {
227
                $formField->setValue($data[$this->Name]);
228
            }
229
230
            $validator = $form->getValidator();
231
            if (!$formField->validate($validator)) {
232
                $errors = $validator->getErrors();
233
                $foundError = false;
234
235
                // field validate implementation may not add error to validator
236
                if (count($errors) > 0) {
237
                    // check if error already added from fields' validate method
238
                    foreach ($errors as $error) {
239
                        if ($error['fieldName'] == $this->Name) {
240
                            $foundError = $error;
241
                            break;
242
                        }
243
                    }
244
                }
245
246
                if ($foundError !== false) {
247
                    // use error messaging already set from validate method
248
                    $form->sessionMessage($foundError['message'], $foundError['messageType']);
249
                } else {
250
                    // fallback to custom message set in CMS or default message if none set
251
                    $form->sessionError($this->getErrorMessage()->HTML());
252
                }
253
            }
254
        }
255
256
        public function getFieldValidationOptions()
257
        {
258
            return new FieldList();
259
        }
260
261
        public function getRequired()
262
        {
263
            return false;
264
        }
265
266
        public function getIcon()
267
        {
268
            return 'spamprotection/images/' . strtolower($this->class) . '.png';
269
        }
270
271
        public function showInReports()
272
        {
273
            return false;
274
        }
275
    }
276
}
277