Completed
Branch master (89fa75)
by
unknown
05:04
created
core/libraries/rest_api/ModelVersionInfo.php 2 patches
Indentation   +420 added lines, -420 removed lines patch added patch discarded remove patch
@@ -25,424 +25,424 @@
 block discarded – undo
25 25
  */
26 26
 class ModelVersionInfo
27 27
 {
28
-    /**
29
-     * Constant used in the $_model_changes array to indicate that a model
30
-     * was completely new in this version
31
-     */
32
-    const MODEL_ADDED = 'model_added_in_this_version';
33
-
34
-    /**
35
-     * @var string indicating what version of the API was requested
36
-     * (eg although core might be at version 4.8.11, they may have sent a request
37
-     * for 4.6)
38
-     */
39
-    protected string $requested_version;
40
-
41
-    /**
42
-     * Top-level keys are versions (major and minor version numbers, eg "4.6")
43
-     * next-level keys are model names (eg "Event") that underwent some change in that version
44
-     * and the value is either Model_Version_Info::model_added (indicating the model is completely NEW in this version),
45
-     * or it's an array where the values are model field names,
46
-     * or API resource properties (ie, non-model fields that appear in REST API results)
47
-     * If a version is missing then we don't know anything about what changes it introduced from the previous version
48
-     */
49
-    protected array $model_changes = [];
50
-
51
-    /**
52
-     * top-level keys are version numbers,
53
-     * next-level keys are model CLASSNAMES (even parent classnames),
54
-     * and next-level keys are extra resource properties to attach to those models' resources,
55
-     * and next-level key-value pairs, where the keys are:
56
-     * 'raw', 'type', 'nullable', 'table_alias', 'table_column',  'always_available'
57
-     */
58
-    protected array $resource_changes = [];
59
-
60
-    /**
61
-     * 2d array where top-level keys are model names, 2nd-level keys are field names
62
-     * and values are the actual field objects
63
-     */
64
-    protected array $cached_fields_on_models = [];
65
-
66
-    /**
67
-     * Keys are model names, values are their classnames.
68
-     * We cache this so we only need to calculate this once per request
69
-     */
70
-    protected ?array $cached_models_for_requested_version = null;
71
-
72
-    protected ?array $cached_model_changes_between_requested_version_and_current = null;
73
-
74
-    protected ?array $cached_resource_changes_between_requested_version_and_current = null;
75
-
76
-
77
-    /**
78
-     * Model_Version_Info constructor.
79
-     *
80
-     * @param string $requested_version
81
-     */
82
-    public function __construct(string $requested_version)
83
-    {
84
-        $this->requested_version = $requested_version;
85
-        $this->model_changes     = [
86
-            '4.8.29' => [
87
-                // first version where the REST API is in EE core, so no need
88
-                // to specify how it's different from the previous
89
-            ],
90
-        ];
91
-        // setup data for "extra" fields added onto resources which don't actually exist on models
92
-        $this->resource_changes = (array) apply_filters(
93
-            'FHEE__Model_Version_Info___construct__extra_resource_properties_for_models',
94
-            []
95
-        );
96
-        $defaults               = [
97
-            'raw'              => false,
98
-            'type'             => 'N/A',
99
-            'nullable'         => true,
100
-            'table_alias'      => 'N/A',
101
-            'table_column'     => 'N/A',
102
-            'always_available' => true,
103
-        ];
104
-        foreach ($this->resource_changes as $model_classnames) {
105
-            foreach ($model_classnames as $model_classname => $extra_fields) {
106
-                foreach ($extra_fields as $fieldname => $field_data) {
107
-                    $this->resource_changes[ $model_classname ][ $fieldname ]['name'] = $fieldname;
108
-                    foreach ($defaults as $attribute => $default_value) {
109
-                        if (! isset($this->resource_changes[ $model_classname ][ $fieldname ][ $attribute ])) {
110
-                            $this->resource_changes[ $model_classname ][ $fieldname ][ $attribute ] = $default_value;
111
-                        }
112
-                    }
113
-                }
114
-            }
115
-        }
116
-    }
117
-
118
-
119
-    /**
120
-     * Returns a slice of Model_Version_Info::model_changes()'s array
121
-     * indicating exactly what changes happened between the current core version,
122
-     * and the version requested
123
-     *
124
-     * @return array
125
-     */
126
-    public function modelChangesBetweenRequestedVersionAndCurrent(): array
127
-    {
128
-        if ($this->cached_model_changes_between_requested_version_and_current === null) {
129
-            $model_changes = [];
130
-            foreach ($this->modelChanges() as $version => $models_changed_in_version) {
131
-                if ($version <= EED_Core_Rest_Api::core_version() && $version > $this->requestedVersion()) {
132
-                    $model_changes[ $version ] = $models_changed_in_version;
133
-                }
134
-            }
135
-            $this->cached_model_changes_between_requested_version_and_current = $model_changes;
136
-        }
137
-        return $this->cached_model_changes_between_requested_version_and_current;
138
-    }
139
-
140
-
141
-    /**
142
-     * Returns a slice of Model_Version_Info::model_changes()'s array
143
-     * indicating exactly what changes happened between the current core version,
144
-     * and the version requested
145
-     *
146
-     * @return array
147
-     */
148
-    public function resourceChangesBetweenRequestedVersionAndCurrent(): array
149
-    {
150
-        if ($this->cached_resource_changes_between_requested_version_and_current === null) {
151
-            $resource_changes = [];
152
-            foreach ($this->resourceChanges() as $version => $model_classnames) {
153
-                if ($version <= EED_Core_Rest_Api::core_version() && $version > $this->requestedVersion()) {
154
-                    $resource_changes[ $version ] = $model_classnames;
155
-                }
156
-            }
157
-            $this->cached_resource_changes_between_requested_version_and_current = $resource_changes;
158
-        }
159
-        return $this->cached_resource_changes_between_requested_version_and_current;
160
-    }
161
-
162
-
163
-    /**
164
-     * If a request was sent to 'wp-json/ee/v4.7/events' this would be '4.7'
165
-     *
166
-     * @return string like '4.6'
167
-     */
168
-    public function requestedVersion(): string
169
-    {
170
-        return $this->requested_version;
171
-    }
172
-
173
-
174
-    /**
175
-     * Returns an array describing how the models have changed in each version of core
176
-     * that supports the API (starting at 4.6)
177
-     * Top-level keys are versions (major and minor version numbers, eg "4.6")
178
-     * next-level keys are model names (eg "Event") that underwent some change in that version
179
-     * and the value is either NULL (indicating the model is completely NEW in this version),
180
-     * or it's an array where fields are value names.
181
-     * If a version is missing then we don't know anything about what changes it introduced from the previous version
182
-     *
183
-     * @return array
184
-     */
185
-    public function modelChanges(): array
186
-    {
187
-        return $this->model_changes;
188
-    }
189
-
190
-
191
-    /**
192
-     * Takes into account the requested version, and the current version, and
193
-     * what changed between the two, and tries to return.
194
-     * Analogous to EE_Registry::instance()->non_abstract_db_models
195
-     *
196
-     * @return array keys are model names, values are their classname
197
-     */
198
-    public function modelsForRequestedVersion(): array
199
-    {
200
-        if ($this->cached_models_for_requested_version === null) {
201
-            $all_models_in_current_version = EE_Registry::instance()->non_abstract_db_models;
202
-            foreach ($this->modelChangesBetweenRequestedVersionAndCurrent() as $models_changed) {
203
-                foreach ($models_changed as $model_name => $new_indicator_or_fields_added) {
204
-                    if ($new_indicator_or_fields_added === ModelVersionInfo::MODEL_ADDED) {
205
-                        unset($all_models_in_current_version[ $model_name ]);
206
-                    }
207
-                }
208
-            }
209
-            $this->cached_models_for_requested_version = (array) apply_filters(
210
-                'FHEE__EventEspresso_core_libraries_rest_api__models_for_requested_version',
211
-                $all_models_in_current_version,
212
-                $this
213
-            );
214
-        }
215
-        return $this->cached_models_for_requested_version;
216
-    }
217
-
218
-
219
-    /**
220
-     * Determines if this is a valid model name in the requested version.
221
-     * Similar to EE_Registry::instance()->is_model_name(), but takes the requested
222
-     * version's models into account
223
-     *
224
-     * @param string $model_name eg 'Event'
225
-     * @return boolean
226
-     */
227
-    public function isModelNameInThisVersion(string $model_name): bool
228
-    {
229
-        $model_names = $this->modelsForRequestedVersion();
230
-        return isset($model_names[ $model_name ]);
231
-    }
232
-
233
-
234
-    /**
235
-     * Wrapper for EE_Registry::instance()->load_model(), but takes the requested
236
-     * version's models into account
237
-     *
238
-     * @param string $model_name
239
-     * @return EEM_Base|null
240
-     * @throws EE_Error
241
-     * @throws ReflectionException
242
-     */
243
-    public function loadModel(string $model_name): ?EEM_Base
244
-    {
245
-        if ($this->isModelNameInThisVersion($model_name)) {
246
-            return EE_Registry::instance()->load_model($model_name);
247
-        }
248
-        throw new EE_Error(
249
-            sprintf(
250
-                esc_html__(
251
-                    'Cannot load model "%1$s" because it does not exist in version %2$s of Event Espresso',
252
-                    'event_espresso'
253
-                ),
254
-                $model_name,
255
-                $this->requestedVersion()
256
-            )
257
-        );
258
-    }
259
-
260
-
261
-    /**
262
-     * Gets all the fields that should exist on this model right now
263
-     *
264
-     * @param EEM_Base $model
265
-     * @return array|EE_Model_Field_Base[]
266
-     */
267
-    public function fieldsOnModelInThisVersion(EEM_Base $model): array
268
-    {
269
-        if (! isset($this->cached_fields_on_models[ $model->get_this_model_name() ])) {
270
-            // get all model changes between the requested version and current core version
271
-            $changes = $this->modelChangesBetweenRequestedVersionAndCurrent();
272
-            // fetch all fields currently on this model
273
-            $current_fields = $model->field_settings();
274
-            // remove all fields that have been added since
275
-            foreach ($changes as $changes_in_version) {
276
-                if (
277
-                    isset($changes_in_version[ $model->get_this_model_name() ])
278
-                    && $changes_in_version[ $model->get_this_model_name() ] !== ModelVersionInfo::MODEL_ADDED
279
-                ) {
280
-                    $current_fields = array_diff_key(
281
-                        $current_fields,
282
-                        array_flip($changes_in_version[ $model->get_this_model_name() ])
283
-                    );
284
-                }
285
-            }
286
-            $this->cached_fields_on_models = $current_fields;
287
-        }
288
-        return $this->cached_fields_on_models;
289
-    }
290
-
291
-
292
-    /**
293
-     * Determines if $object is of one of the classes of $classes. Similar to
294
-     * in_array(), except this checks if $object is a subclass of the classnames provided
295
-     * in $classnames
296
-     *
297
-     * @param object $object
298
-     * @param array  $classnames
299
-     * @return boolean
300
-     */
301
-    public function isSubclassOfOne(object $object, array $classnames): bool
302
-    {
303
-        foreach ($classnames as $classname) {
304
-            if (is_a($object, $classname)) {
305
-                return true;
306
-            }
307
-        }
308
-        return false;
309
-    }
310
-
311
-
312
-    /**
313
-     * Returns the list of model field classes that the API basically ignores
314
-     *
315
-     * @return array
316
-     */
317
-    public function fieldsIgnored(): array
318
-    {
319
-        return (array) apply_filters(
320
-            'FHEE__Controller_Model_Read_fields_ignored',
321
-            []
322
-        );
323
-    }
324
-
325
-
326
-    /**
327
-     * If this field one that should be ignored by the API?
328
-     *
329
-     * @param EE_Model_Field_Base $field_obj
330
-     * @return boolean
331
-     */
332
-    public function fieldIsIgnored(EE_Model_Field_Base $field_obj): bool
333
-    {
334
-        return $this->isSubclassOfOne($field_obj, $this->fieldsIgnored());
335
-    }
336
-
337
-
338
-    /**
339
-     * Returns the list of model field classes that have a "raw" and non-raw formats.
340
-     * Normally the "raw" versions are only accessible to those who can edit them.
341
-     *
342
-     * @return array an array of EE_Model_Field_Base child classnames
343
-     */
344
-    public function fieldsThatHaveRenderedFormat(): array
345
-    {
346
-        return (array) apply_filters(
347
-            'FHEE__Controller_Model_Read__fields_raw',
348
-            ['EE_Post_Content_Field', 'EE_Full_HTML_Field']
349
-        );
350
-    }
351
-
352
-
353
-    /**
354
-     * If this field one that has a raw format
355
-     *
356
-     * @param EE_Model_Field_Base $field_obj
357
-     * @return bool
358
-     */
359
-    public function fieldHasRenderedFormat(EE_Model_Field_Base $field_obj): bool
360
-    {
361
-        return $this->isSubclassOfOne($field_obj, $this->fieldsThatHaveRenderedFormat());
362
-    }
363
-
364
-
365
-    /**
366
-     * Returns the list of model field classes that have a "_pretty" and non-pretty versions.
367
-     * The pretty version of the field is NOT query-able or editable, but requires no extra permissions
368
-     * to view
369
-     *
370
-     * @return array an array of EE_Model_Field_Base child classnames
371
-     */
372
-    public function fieldsThatHavePrettyFormat(): array
373
-    {
374
-        return (array) apply_filters(
375
-            'FHEE__Controller_Model_Read__fields_pretty',
376
-            ['EE_Enum_Integer_Field', 'EE_Enum_Text_Field', 'EE_Money_Field']
377
-        );
378
-    }
379
-
380
-
381
-    /**
382
-     * If this field one that has a pretty equivalent
383
-     *
384
-     * @param EE_Model_Field_Base $field_obj
385
-     * @return bool
386
-     */
387
-    public function fieldHasPrettyFormat(EE_Model_Field_Base $field_obj): bool
388
-    {
389
-        return $this->isSubclassOfOne($field_obj, $this->fieldsThatHavePrettyFormat());
390
-    }
391
-
392
-
393
-    /**
394
-     * Returns an array describing what extra API resource properties have been added through the versions
395
-     *
396
-     * @return array @see $this->_extra_resource_properties_for_models
397
-     */
398
-    public function resourceChanges(): array
399
-    {
400
-        return $this->resource_changes;
401
-    }
402
-
403
-
404
-    /**
405
-     * Returns an array where keys are extra resource properties in this version of the API,
406
-     * and values are key-value pairs describing the new properties.
407
-     *
408
-     * @param EEM_Base $model
409
-     * @return array
410
-     * @see Model_Version::_resource_changes
411
-     */
412
-    public function extraResourcePropertiesForModel(EEM_Base $model): array
413
-    {
414
-        $extra_properties = [];
415
-        foreach ($this->resourceChangesBetweenRequestedVersionAndCurrent() as $model_classnames) {
416
-            foreach ($model_classnames as $model_classname => $properties_added_in_this_version) {
417
-                if (is_subclass_of($model, $model_classname)) {
418
-                    $extra_properties = array_merge($extra_properties, $properties_added_in_this_version);
419
-                }
420
-            }
421
-        }
422
-        return $extra_properties;
423
-    }
424
-
425
-
426
-    /**
427
-     * Gets all the related models for the specified model. It's good to use this
428
-     * in case this model didn't exist for this version or something
429
-     *
430
-     * @param EEM_Base $model
431
-     * @return EE_Model_Relation_Base[]
432
-     */
433
-    public function relationSettings(EEM_Base $model): array
434
-    {
435
-        $relations = [];
436
-        foreach ($model->relation_settings() as $relation_name => $relation_obj) {
437
-            if ($this->isModelNameInThisVersion($relation_name)) {
438
-                $relations[ $relation_name ] = $relation_obj;
439
-            }
440
-        }
441
-        // filter the results, but use the old filter name
442
-        return (array) apply_filters(
443
-            'FHEE__Read__create_entity_from_wpdb_result__related_models_to_include',
444
-            $relations,
445
-            $model
446
-        );
447
-    }
28
+	/**
29
+	 * Constant used in the $_model_changes array to indicate that a model
30
+	 * was completely new in this version
31
+	 */
32
+	const MODEL_ADDED = 'model_added_in_this_version';
33
+
34
+	/**
35
+	 * @var string indicating what version of the API was requested
36
+	 * (eg although core might be at version 4.8.11, they may have sent a request
37
+	 * for 4.6)
38
+	 */
39
+	protected string $requested_version;
40
+
41
+	/**
42
+	 * Top-level keys are versions (major and minor version numbers, eg "4.6")
43
+	 * next-level keys are model names (eg "Event") that underwent some change in that version
44
+	 * and the value is either Model_Version_Info::model_added (indicating the model is completely NEW in this version),
45
+	 * or it's an array where the values are model field names,
46
+	 * or API resource properties (ie, non-model fields that appear in REST API results)
47
+	 * If a version is missing then we don't know anything about what changes it introduced from the previous version
48
+	 */
49
+	protected array $model_changes = [];
50
+
51
+	/**
52
+	 * top-level keys are version numbers,
53
+	 * next-level keys are model CLASSNAMES (even parent classnames),
54
+	 * and next-level keys are extra resource properties to attach to those models' resources,
55
+	 * and next-level key-value pairs, where the keys are:
56
+	 * 'raw', 'type', 'nullable', 'table_alias', 'table_column',  'always_available'
57
+	 */
58
+	protected array $resource_changes = [];
59
+
60
+	/**
61
+	 * 2d array where top-level keys are model names, 2nd-level keys are field names
62
+	 * and values are the actual field objects
63
+	 */
64
+	protected array $cached_fields_on_models = [];
65
+
66
+	/**
67
+	 * Keys are model names, values are their classnames.
68
+	 * We cache this so we only need to calculate this once per request
69
+	 */
70
+	protected ?array $cached_models_for_requested_version = null;
71
+
72
+	protected ?array $cached_model_changes_between_requested_version_and_current = null;
73
+
74
+	protected ?array $cached_resource_changes_between_requested_version_and_current = null;
75
+
76
+
77
+	/**
78
+	 * Model_Version_Info constructor.
79
+	 *
80
+	 * @param string $requested_version
81
+	 */
82
+	public function __construct(string $requested_version)
83
+	{
84
+		$this->requested_version = $requested_version;
85
+		$this->model_changes     = [
86
+			'4.8.29' => [
87
+				// first version where the REST API is in EE core, so no need
88
+				// to specify how it's different from the previous
89
+			],
90
+		];
91
+		// setup data for "extra" fields added onto resources which don't actually exist on models
92
+		$this->resource_changes = (array) apply_filters(
93
+			'FHEE__Model_Version_Info___construct__extra_resource_properties_for_models',
94
+			[]
95
+		);
96
+		$defaults               = [
97
+			'raw'              => false,
98
+			'type'             => 'N/A',
99
+			'nullable'         => true,
100
+			'table_alias'      => 'N/A',
101
+			'table_column'     => 'N/A',
102
+			'always_available' => true,
103
+		];
104
+		foreach ($this->resource_changes as $model_classnames) {
105
+			foreach ($model_classnames as $model_classname => $extra_fields) {
106
+				foreach ($extra_fields as $fieldname => $field_data) {
107
+					$this->resource_changes[ $model_classname ][ $fieldname ]['name'] = $fieldname;
108
+					foreach ($defaults as $attribute => $default_value) {
109
+						if (! isset($this->resource_changes[ $model_classname ][ $fieldname ][ $attribute ])) {
110
+							$this->resource_changes[ $model_classname ][ $fieldname ][ $attribute ] = $default_value;
111
+						}
112
+					}
113
+				}
114
+			}
115
+		}
116
+	}
117
+
118
+
119
+	/**
120
+	 * Returns a slice of Model_Version_Info::model_changes()'s array
121
+	 * indicating exactly what changes happened between the current core version,
122
+	 * and the version requested
123
+	 *
124
+	 * @return array
125
+	 */
126
+	public function modelChangesBetweenRequestedVersionAndCurrent(): array
127
+	{
128
+		if ($this->cached_model_changes_between_requested_version_and_current === null) {
129
+			$model_changes = [];
130
+			foreach ($this->modelChanges() as $version => $models_changed_in_version) {
131
+				if ($version <= EED_Core_Rest_Api::core_version() && $version > $this->requestedVersion()) {
132
+					$model_changes[ $version ] = $models_changed_in_version;
133
+				}
134
+			}
135
+			$this->cached_model_changes_between_requested_version_and_current = $model_changes;
136
+		}
137
+		return $this->cached_model_changes_between_requested_version_and_current;
138
+	}
139
+
140
+
141
+	/**
142
+	 * Returns a slice of Model_Version_Info::model_changes()'s array
143
+	 * indicating exactly what changes happened between the current core version,
144
+	 * and the version requested
145
+	 *
146
+	 * @return array
147
+	 */
148
+	public function resourceChangesBetweenRequestedVersionAndCurrent(): array
149
+	{
150
+		if ($this->cached_resource_changes_between_requested_version_and_current === null) {
151
+			$resource_changes = [];
152
+			foreach ($this->resourceChanges() as $version => $model_classnames) {
153
+				if ($version <= EED_Core_Rest_Api::core_version() && $version > $this->requestedVersion()) {
154
+					$resource_changes[ $version ] = $model_classnames;
155
+				}
156
+			}
157
+			$this->cached_resource_changes_between_requested_version_and_current = $resource_changes;
158
+		}
159
+		return $this->cached_resource_changes_between_requested_version_and_current;
160
+	}
161
+
162
+
163
+	/**
164
+	 * If a request was sent to 'wp-json/ee/v4.7/events' this would be '4.7'
165
+	 *
166
+	 * @return string like '4.6'
167
+	 */
168
+	public function requestedVersion(): string
169
+	{
170
+		return $this->requested_version;
171
+	}
172
+
173
+
174
+	/**
175
+	 * Returns an array describing how the models have changed in each version of core
176
+	 * that supports the API (starting at 4.6)
177
+	 * Top-level keys are versions (major and minor version numbers, eg "4.6")
178
+	 * next-level keys are model names (eg "Event") that underwent some change in that version
179
+	 * and the value is either NULL (indicating the model is completely NEW in this version),
180
+	 * or it's an array where fields are value names.
181
+	 * If a version is missing then we don't know anything about what changes it introduced from the previous version
182
+	 *
183
+	 * @return array
184
+	 */
185
+	public function modelChanges(): array
186
+	{
187
+		return $this->model_changes;
188
+	}
189
+
190
+
191
+	/**
192
+	 * Takes into account the requested version, and the current version, and
193
+	 * what changed between the two, and tries to return.
194
+	 * Analogous to EE_Registry::instance()->non_abstract_db_models
195
+	 *
196
+	 * @return array keys are model names, values are their classname
197
+	 */
198
+	public function modelsForRequestedVersion(): array
199
+	{
200
+		if ($this->cached_models_for_requested_version === null) {
201
+			$all_models_in_current_version = EE_Registry::instance()->non_abstract_db_models;
202
+			foreach ($this->modelChangesBetweenRequestedVersionAndCurrent() as $models_changed) {
203
+				foreach ($models_changed as $model_name => $new_indicator_or_fields_added) {
204
+					if ($new_indicator_or_fields_added === ModelVersionInfo::MODEL_ADDED) {
205
+						unset($all_models_in_current_version[ $model_name ]);
206
+					}
207
+				}
208
+			}
209
+			$this->cached_models_for_requested_version = (array) apply_filters(
210
+				'FHEE__EventEspresso_core_libraries_rest_api__models_for_requested_version',
211
+				$all_models_in_current_version,
212
+				$this
213
+			);
214
+		}
215
+		return $this->cached_models_for_requested_version;
216
+	}
217
+
218
+
219
+	/**
220
+	 * Determines if this is a valid model name in the requested version.
221
+	 * Similar to EE_Registry::instance()->is_model_name(), but takes the requested
222
+	 * version's models into account
223
+	 *
224
+	 * @param string $model_name eg 'Event'
225
+	 * @return boolean
226
+	 */
227
+	public function isModelNameInThisVersion(string $model_name): bool
228
+	{
229
+		$model_names = $this->modelsForRequestedVersion();
230
+		return isset($model_names[ $model_name ]);
231
+	}
232
+
233
+
234
+	/**
235
+	 * Wrapper for EE_Registry::instance()->load_model(), but takes the requested
236
+	 * version's models into account
237
+	 *
238
+	 * @param string $model_name
239
+	 * @return EEM_Base|null
240
+	 * @throws EE_Error
241
+	 * @throws ReflectionException
242
+	 */
243
+	public function loadModel(string $model_name): ?EEM_Base
244
+	{
245
+		if ($this->isModelNameInThisVersion($model_name)) {
246
+			return EE_Registry::instance()->load_model($model_name);
247
+		}
248
+		throw new EE_Error(
249
+			sprintf(
250
+				esc_html__(
251
+					'Cannot load model "%1$s" because it does not exist in version %2$s of Event Espresso',
252
+					'event_espresso'
253
+				),
254
+				$model_name,
255
+				$this->requestedVersion()
256
+			)
257
+		);
258
+	}
259
+
260
+
261
+	/**
262
+	 * Gets all the fields that should exist on this model right now
263
+	 *
264
+	 * @param EEM_Base $model
265
+	 * @return array|EE_Model_Field_Base[]
266
+	 */
267
+	public function fieldsOnModelInThisVersion(EEM_Base $model): array
268
+	{
269
+		if (! isset($this->cached_fields_on_models[ $model->get_this_model_name() ])) {
270
+			// get all model changes between the requested version and current core version
271
+			$changes = $this->modelChangesBetweenRequestedVersionAndCurrent();
272
+			// fetch all fields currently on this model
273
+			$current_fields = $model->field_settings();
274
+			// remove all fields that have been added since
275
+			foreach ($changes as $changes_in_version) {
276
+				if (
277
+					isset($changes_in_version[ $model->get_this_model_name() ])
278
+					&& $changes_in_version[ $model->get_this_model_name() ] !== ModelVersionInfo::MODEL_ADDED
279
+				) {
280
+					$current_fields = array_diff_key(
281
+						$current_fields,
282
+						array_flip($changes_in_version[ $model->get_this_model_name() ])
283
+					);
284
+				}
285
+			}
286
+			$this->cached_fields_on_models = $current_fields;
287
+		}
288
+		return $this->cached_fields_on_models;
289
+	}
290
+
291
+
292
+	/**
293
+	 * Determines if $object is of one of the classes of $classes. Similar to
294
+	 * in_array(), except this checks if $object is a subclass of the classnames provided
295
+	 * in $classnames
296
+	 *
297
+	 * @param object $object
298
+	 * @param array  $classnames
299
+	 * @return boolean
300
+	 */
301
+	public function isSubclassOfOne(object $object, array $classnames): bool
302
+	{
303
+		foreach ($classnames as $classname) {
304
+			if (is_a($object, $classname)) {
305
+				return true;
306
+			}
307
+		}
308
+		return false;
309
+	}
310
+
311
+
312
+	/**
313
+	 * Returns the list of model field classes that the API basically ignores
314
+	 *
315
+	 * @return array
316
+	 */
317
+	public function fieldsIgnored(): array
318
+	{
319
+		return (array) apply_filters(
320
+			'FHEE__Controller_Model_Read_fields_ignored',
321
+			[]
322
+		);
323
+	}
324
+
325
+
326
+	/**
327
+	 * If this field one that should be ignored by the API?
328
+	 *
329
+	 * @param EE_Model_Field_Base $field_obj
330
+	 * @return boolean
331
+	 */
332
+	public function fieldIsIgnored(EE_Model_Field_Base $field_obj): bool
333
+	{
334
+		return $this->isSubclassOfOne($field_obj, $this->fieldsIgnored());
335
+	}
336
+
337
+
338
+	/**
339
+	 * Returns the list of model field classes that have a "raw" and non-raw formats.
340
+	 * Normally the "raw" versions are only accessible to those who can edit them.
341
+	 *
342
+	 * @return array an array of EE_Model_Field_Base child classnames
343
+	 */
344
+	public function fieldsThatHaveRenderedFormat(): array
345
+	{
346
+		return (array) apply_filters(
347
+			'FHEE__Controller_Model_Read__fields_raw',
348
+			['EE_Post_Content_Field', 'EE_Full_HTML_Field']
349
+		);
350
+	}
351
+
352
+
353
+	/**
354
+	 * If this field one that has a raw format
355
+	 *
356
+	 * @param EE_Model_Field_Base $field_obj
357
+	 * @return bool
358
+	 */
359
+	public function fieldHasRenderedFormat(EE_Model_Field_Base $field_obj): bool
360
+	{
361
+		return $this->isSubclassOfOne($field_obj, $this->fieldsThatHaveRenderedFormat());
362
+	}
363
+
364
+
365
+	/**
366
+	 * Returns the list of model field classes that have a "_pretty" and non-pretty versions.
367
+	 * The pretty version of the field is NOT query-able or editable, but requires no extra permissions
368
+	 * to view
369
+	 *
370
+	 * @return array an array of EE_Model_Field_Base child classnames
371
+	 */
372
+	public function fieldsThatHavePrettyFormat(): array
373
+	{
374
+		return (array) apply_filters(
375
+			'FHEE__Controller_Model_Read__fields_pretty',
376
+			['EE_Enum_Integer_Field', 'EE_Enum_Text_Field', 'EE_Money_Field']
377
+		);
378
+	}
379
+
380
+
381
+	/**
382
+	 * If this field one that has a pretty equivalent
383
+	 *
384
+	 * @param EE_Model_Field_Base $field_obj
385
+	 * @return bool
386
+	 */
387
+	public function fieldHasPrettyFormat(EE_Model_Field_Base $field_obj): bool
388
+	{
389
+		return $this->isSubclassOfOne($field_obj, $this->fieldsThatHavePrettyFormat());
390
+	}
391
+
392
+
393
+	/**
394
+	 * Returns an array describing what extra API resource properties have been added through the versions
395
+	 *
396
+	 * @return array @see $this->_extra_resource_properties_for_models
397
+	 */
398
+	public function resourceChanges(): array
399
+	{
400
+		return $this->resource_changes;
401
+	}
402
+
403
+
404
+	/**
405
+	 * Returns an array where keys are extra resource properties in this version of the API,
406
+	 * and values are key-value pairs describing the new properties.
407
+	 *
408
+	 * @param EEM_Base $model
409
+	 * @return array
410
+	 * @see Model_Version::_resource_changes
411
+	 */
412
+	public function extraResourcePropertiesForModel(EEM_Base $model): array
413
+	{
414
+		$extra_properties = [];
415
+		foreach ($this->resourceChangesBetweenRequestedVersionAndCurrent() as $model_classnames) {
416
+			foreach ($model_classnames as $model_classname => $properties_added_in_this_version) {
417
+				if (is_subclass_of($model, $model_classname)) {
418
+					$extra_properties = array_merge($extra_properties, $properties_added_in_this_version);
419
+				}
420
+			}
421
+		}
422
+		return $extra_properties;
423
+	}
424
+
425
+
426
+	/**
427
+	 * Gets all the related models for the specified model. It's good to use this
428
+	 * in case this model didn't exist for this version or something
429
+	 *
430
+	 * @param EEM_Base $model
431
+	 * @return EE_Model_Relation_Base[]
432
+	 */
433
+	public function relationSettings(EEM_Base $model): array
434
+	{
435
+		$relations = [];
436
+		foreach ($model->relation_settings() as $relation_name => $relation_obj) {
437
+			if ($this->isModelNameInThisVersion($relation_name)) {
438
+				$relations[ $relation_name ] = $relation_obj;
439
+			}
440
+		}
441
+		// filter the results, but use the old filter name
442
+		return (array) apply_filters(
443
+			'FHEE__Read__create_entity_from_wpdb_result__related_models_to_include',
444
+			$relations,
445
+			$model
446
+		);
447
+	}
448 448
 }
Please login to merge, or discard this patch.
Spacing   +13 added lines, -13 removed lines patch added patch discarded remove patch
@@ -93,7 +93,7 @@  discard block
 block discarded – undo
93 93
             'FHEE__Model_Version_Info___construct__extra_resource_properties_for_models',
94 94
             []
95 95
         );
96
-        $defaults               = [
96
+        $defaults = [
97 97
             'raw'              => false,
98 98
             'type'             => 'N/A',
99 99
             'nullable'         => true,
@@ -104,10 +104,10 @@  discard block
 block discarded – undo
104 104
         foreach ($this->resource_changes as $model_classnames) {
105 105
             foreach ($model_classnames as $model_classname => $extra_fields) {
106 106
                 foreach ($extra_fields as $fieldname => $field_data) {
107
-                    $this->resource_changes[ $model_classname ][ $fieldname ]['name'] = $fieldname;
107
+                    $this->resource_changes[$model_classname][$fieldname]['name'] = $fieldname;
108 108
                     foreach ($defaults as $attribute => $default_value) {
109
-                        if (! isset($this->resource_changes[ $model_classname ][ $fieldname ][ $attribute ])) {
110
-                            $this->resource_changes[ $model_classname ][ $fieldname ][ $attribute ] = $default_value;
109
+                        if ( ! isset($this->resource_changes[$model_classname][$fieldname][$attribute])) {
110
+                            $this->resource_changes[$model_classname][$fieldname][$attribute] = $default_value;
111 111
                         }
112 112
                     }
113 113
                 }
@@ -129,7 +129,7 @@  discard block
 block discarded – undo
129 129
             $model_changes = [];
130 130
             foreach ($this->modelChanges() as $version => $models_changed_in_version) {
131 131
                 if ($version <= EED_Core_Rest_Api::core_version() && $version > $this->requestedVersion()) {
132
-                    $model_changes[ $version ] = $models_changed_in_version;
132
+                    $model_changes[$version] = $models_changed_in_version;
133 133
                 }
134 134
             }
135 135
             $this->cached_model_changes_between_requested_version_and_current = $model_changes;
@@ -151,7 +151,7 @@  discard block
 block discarded – undo
151 151
             $resource_changes = [];
152 152
             foreach ($this->resourceChanges() as $version => $model_classnames) {
153 153
                 if ($version <= EED_Core_Rest_Api::core_version() && $version > $this->requestedVersion()) {
154
-                    $resource_changes[ $version ] = $model_classnames;
154
+                    $resource_changes[$version] = $model_classnames;
155 155
                 }
156 156
             }
157 157
             $this->cached_resource_changes_between_requested_version_and_current = $resource_changes;
@@ -202,7 +202,7 @@  discard block
 block discarded – undo
202 202
             foreach ($this->modelChangesBetweenRequestedVersionAndCurrent() as $models_changed) {
203 203
                 foreach ($models_changed as $model_name => $new_indicator_or_fields_added) {
204 204
                     if ($new_indicator_or_fields_added === ModelVersionInfo::MODEL_ADDED) {
205
-                        unset($all_models_in_current_version[ $model_name ]);
205
+                        unset($all_models_in_current_version[$model_name]);
206 206
                     }
207 207
                 }
208 208
             }
@@ -227,7 +227,7 @@  discard block
 block discarded – undo
227 227
     public function isModelNameInThisVersion(string $model_name): bool
228 228
     {
229 229
         $model_names = $this->modelsForRequestedVersion();
230
-        return isset($model_names[ $model_name ]);
230
+        return isset($model_names[$model_name]);
231 231
     }
232 232
 
233 233
 
@@ -266,7 +266,7 @@  discard block
 block discarded – undo
266 266
      */
267 267
     public function fieldsOnModelInThisVersion(EEM_Base $model): array
268 268
     {
269
-        if (! isset($this->cached_fields_on_models[ $model->get_this_model_name() ])) {
269
+        if ( ! isset($this->cached_fields_on_models[$model->get_this_model_name()])) {
270 270
             // get all model changes between the requested version and current core version
271 271
             $changes = $this->modelChangesBetweenRequestedVersionAndCurrent();
272 272
             // fetch all fields currently on this model
@@ -274,12 +274,12 @@  discard block
 block discarded – undo
274 274
             // remove all fields that have been added since
275 275
             foreach ($changes as $changes_in_version) {
276 276
                 if (
277
-                    isset($changes_in_version[ $model->get_this_model_name() ])
278
-                    && $changes_in_version[ $model->get_this_model_name() ] !== ModelVersionInfo::MODEL_ADDED
277
+                    isset($changes_in_version[$model->get_this_model_name()])
278
+                    && $changes_in_version[$model->get_this_model_name()] !== ModelVersionInfo::MODEL_ADDED
279 279
                 ) {
280 280
                     $current_fields = array_diff_key(
281 281
                         $current_fields,
282
-                        array_flip($changes_in_version[ $model->get_this_model_name() ])
282
+                        array_flip($changes_in_version[$model->get_this_model_name()])
283 283
                     );
284 284
                 }
285 285
             }
@@ -435,7 +435,7 @@  discard block
 block discarded – undo
435 435
         $relations = [];
436 436
         foreach ($model->relation_settings() as $relation_name => $relation_obj) {
437 437
             if ($this->isModelNameInThisVersion($relation_name)) {
438
-                $relations[ $relation_name ] = $relation_obj;
438
+                $relations[$relation_name] = $relation_obj;
439 439
             }
440 440
         }
441 441
         // filter the results, but use the old filter name
Please login to merge, or discard this patch.
core/libraries/rest_api/RestException.php 1 patch
Indentation   +42 added lines, -42 removed lines patch added patch discarded remove patch
@@ -17,46 +17,46 @@
 block discarded – undo
17 17
  */
18 18
 class RestException extends EE_Error
19 19
 {
20
-    protected array $wp_error_data = [];
21
-
22
-    protected string $wp_error_code = '';
23
-
24
-
25
-    public function __construct(
26
-        string $string_code,
27
-        string $message,
28
-        array $wp_error_data = [],
29
-        ?Exception $previous = null
30
-    ) {
31
-        $this->wp_error_code = $string_code;
32
-        $this->wp_error_data = $wp_error_data;
33
-
34
-        parent::__construct(
35
-            $message,
36
-            ! empty($wp_error_data['status']) ? $wp_error_data['status'] : 500,
37
-            $previous
38
-        );
39
-    }
40
-
41
-
42
-    /**
43
-     * Array of data that may have been set during the constructor, intended for WP_Error's data
44
-     *
45
-     * @return array
46
-     */
47
-    public function getData(): array
48
-    {
49
-        return $this->wp_error_data;
50
-    }
51
-
52
-
53
-    /**
54
-     * Gets the error string
55
-     *
56
-     * @return string
57
-     */
58
-    public function getStringCode(): string
59
-    {
60
-        return $this->wp_error_code;
61
-    }
20
+	protected array $wp_error_data = [];
21
+
22
+	protected string $wp_error_code = '';
23
+
24
+
25
+	public function __construct(
26
+		string $string_code,
27
+		string $message,
28
+		array $wp_error_data = [],
29
+		?Exception $previous = null
30
+	) {
31
+		$this->wp_error_code = $string_code;
32
+		$this->wp_error_data = $wp_error_data;
33
+
34
+		parent::__construct(
35
+			$message,
36
+			! empty($wp_error_data['status']) ? $wp_error_data['status'] : 500,
37
+			$previous
38
+		);
39
+	}
40
+
41
+
42
+	/**
43
+	 * Array of data that may have been set during the constructor, intended for WP_Error's data
44
+	 *
45
+	 * @return array
46
+	 */
47
+	public function getData(): array
48
+	{
49
+		return $this->wp_error_data;
50
+	}
51
+
52
+
53
+	/**
54
+	 * Gets the error string
55
+	 *
56
+	 * @return string
57
+	 */
58
+	public function getStringCode(): string
59
+	{
60
+		return $this->wp_error_code;
61
+	}
62 62
 }
Please login to merge, or discard this patch.
core/libraries/rest_api/RestIncomingQueryParamMetadata.php 2 patches
Indentation   +788 added lines, -788 removed lines patch added patch discarded remove patch
@@ -30,794 +30,794 @@
 block discarded – undo
30 30
  */
31 31
 class RestIncomingQueryParamMetadata
32 32
 {
33
-    private string $query_param_key;
34
-
35
-    private $query_param_value;
36
-
37
-    private RestIncomingQueryParamContext $context;
38
-
39
-    private ?EE_Model_Field_Base $field;
40
-
41
-    /**
42
-     * @var string same as $query_param_key but has the * and anything after it removed
43
-     */
44
-    private string $query_param_key_sans_stars;
45
-
46
-    /**
47
-     * @var string for timezone or timezone offset
48
-     */
49
-    private string $timezone;
50
-
51
-    /**
52
-     * @var bool if the field in $query_param_key is for a GMT field (eg `EVT_modified_gmt`)
53
-     */
54
-    private bool $is_gmt_field = false;
55
-
56
-
57
-    /**
58
-     * RestIncomingQueryParamMetadata constructor.
59
-     * You probably want to call
60
-     *
61
-     * @param string                        $query_param_key
62
-     * @param mixed                         $query_param_value
63
-     * @param RestIncomingQueryParamContext $context
64
-     * @throws EE_Error
65
-     * @throws ReflectionException
66
-     */
67
-    public function __construct(string $query_param_key, $query_param_value, RestIncomingQueryParamContext $context)
68
-    {
69
-        $this->query_param_key   = $query_param_key;
70
-        $this->query_param_value = $query_param_value;
71
-        $this->context           = $context;
72
-        $this->determineFieldAndTimezone();
73
-    }
74
-
75
-
76
-    /**
77
-     * Gets the query parameter key. This may have been modified (see setQueryParamValue())
78
-     *
79
-     * @return string
80
-     */
81
-    public function getQueryParamKey(): string
82
-    {
83
-        return $this->query_param_key;
84
-    }
85
-
86
-
87
-    /**
88
-     * Modifies the query parameter key passed in (Eg this is done when rewriting the simplified specified operator REST
89
-     * query parameters into the legacy structure)
90
-     *
91
-     * @param string|array|int|float $query_param_value
92
-     */
93
-    private function setQueryParamValue($query_param_value)
94
-    {
95
-        $this->query_param_value = $query_param_value;
96
-    }
97
-
98
-
99
-    /**
100
-     * Gets the original query parameter value passed in.
101
-     *
102
-     * @return mixed
103
-     */
104
-    public function getQueryParamValue()
105
-    {
106
-        return $this->query_param_value;
107
-    }
108
-
109
-
110
-    /**
111
-     * Gets the context object.
112
-     *
113
-     * @return RestIncomingQueryParamContext
114
-     */
115
-    public function getContext(): RestIncomingQueryParamContext
116
-    {
117
-        return $this->context;
118
-    }
119
-
120
-
121
-    /**
122
-     * Sets the query parameter key. This may be used to rewrite a key into its non-GMT alternative.
123
-     *
124
-     * @param string $query_param_key
125
-     */
126
-    private function setQueryParamKey(string $query_param_key)
127
-    {
128
-        $this->query_param_key = $query_param_key;
129
-    }
130
-
131
-
132
-    /**
133
-     * Gets the field the query parameter key indicated. This may be null (in cases where the query parameter key
134
-     * did not indicate a field, eg if it were `OR`).
135
-     *
136
-     * @return EE_Model_Field_Base|null
137
-     */
138
-    public function getField(): ?EE_Model_Field_Base
139
-    {
140
-        return $this->field;
141
-    }
142
-
143
-
144
-    /**
145
-     * Gets the query parameter key (with the star and everything afterwards removed).
146
-     *
147
-     * @return string
148
-     */
149
-    public function getQueryParamKeySansStars(): string
150
-    {
151
-        return $this->query_param_key_sans_stars;
152
-    }
153
-
154
-
155
-    /**
156
-     * Gets the timezone associated with this model (the site timezone, except for GMT datetime fields).
157
-     *
158
-     * @return string
159
-     */
160
-    public function getTimezone(): string
161
-    {
162
-        return $this->timezone;
163
-    }
164
-
165
-
166
-    /**
167
-     * Returns whether this is a GMT field
168
-     *
169
-     * @return bool
170
-     */
171
-    public function isGmtField(): bool
172
-    {
173
-        return $this->is_gmt_field;
174
-    }
175
-
176
-
177
-    /**
178
-     * Sets the field indicated by the query parameter key (might be null).
179
-     *
180
-     * @param EE_Model_Field_Base|null $field
181
-     */
182
-    private function setField(EE_Model_Field_Base $field = null)
183
-    {
184
-        $this->field = $field;
185
-    }
186
-
187
-
188
-    /**
189
-     * Sets the query parameter key-with-stars-removed.
190
-     *
191
-     * @param string $query_param_key_sans_stars
192
-     */
193
-    private function setQueryParamKeySansStars(string $query_param_key_sans_stars)
194
-    {
195
-        $this->query_param_key_sans_stars = $query_param_key_sans_stars;
196
-    }
197
-
198
-
199
-    /**
200
-     * Sets the timezone (this could be a timezone offset string).
201
-     *
202
-     * @param string $timezone
203
-     */
204
-    private function setTimezone(string $timezone)
205
-    {
206
-        $this->timezone = $timezone;
207
-    }
208
-
209
-
210
-    /**
211
-     * @param bool|int|string|null $is_gmt_field
212
-     */
213
-    private function setIsGmtField($is_gmt_field)
214
-    {
215
-        $this->is_gmt_field = filter_var($is_gmt_field, FILTER_VALIDATE_BOOLEAN);
216
-    }
217
-
218
-
219
-    /**
220
-     * Determines what field, query param name, and query param name without stars, and timezone to use.
221
-     *
222
-     * @return void {
223
-     * @throws EE_Error
224
-     * @throws InvalidDataTypeException
225
-     * @throws InvalidInterfaceException
226
-     * @throws InvalidArgumentException
227
-     * @throws ReflectionException
228
-     * @throws Exception
229
-     * @since 4.9.72.p
230
-     * @type EE_Model_Field_Base $field
231
-     */
232
-    private function determineFieldAndTimezone()
233
-    {
234
-        $this->setQueryParamKeySansStars(
235
-            ModelDataTranslator::removeStarsAndAnythingAfterFromConditionQueryParamKey(
236
-                $this->getQueryParamKey()
237
-            )
238
-        );
239
-        $this->setField(
240
-            ModelDataTranslator::deduceFieldFromQueryParam(
241
-                $this->getQueryParamKeySansStars(),
242
-                $this->getContext()->getModel()
243
-            )
244
-        );
245
-        // double-check is it a *_gmt field?
246
-        if (
247
-            ! $this->getField() instanceof EE_Model_Field_Base
248
-            && ModelDataTranslator::isGmtDateFieldName($this->getQueryParamKeySansStars())
249
-        ) {
250
-            // yep, take off '_gmt', and find the field
251
-            $this->setQueryParamKey(ModelDataTranslator::removeGmtFromFieldName($this->getQueryParamKeySansStars()));
252
-            $this->setField(
253
-                ModelDataTranslator::deduceFieldFromQueryParam(
254
-                    $this->getQueryParamKey(),
255
-                    $this->context->getModel()
256
-                )
257
-            );
258
-            $this->setTimezone('UTC');
259
-            $this->setIsGmtField(true);
260
-        } elseif ($this->getField() instanceof EE_Datetime_Field) {
261
-            // so it's not a GMT field. Set the timezone on the model to the default
262
-            $this->setTimezone(EEH_DTT_Helper::get_valid_timezone_string());
263
-        } else {
264
-            // just keep using what's already set for the timezone
265
-            $this->setTimezone($this->context->getModel()->get_timezone());
266
-        }
267
-        $this->assertOnlyAdminCanReadPasswordFields();
268
-    }
269
-
270
-
271
-    /**
272
-     * Throws an exception if a non-admin is trying to query by password.
273
-     *
274
-     * @throws RestException
275
-     * @since 4.9.74.p
276
-     */
277
-    private function assertOnlyAdminCanReadPasswordFields()
278
-    {
279
-        if (
280
-            $this->getField() instanceof EE_Password_Field
281
-            && ! current_user_can(EE_Restriction_Generator_Base::get_default_restrictions_cap())
282
-        ) {
283
-            // only full admins can query by password. sorry bub!
284
-            throw new RestException(
285
-                'only_admins_can_query_by_password',
286
-                // @codingStandardsIgnoreStart
287
-                esc_html__(
288
-                    'You attempted to filter by a password field without the needed privileges. Only a full admin is allowed to do that.',
289
-                    'event_espresso'
290
-                ),
291
-                // @codingStandardsIgnoreEnd
292
-                [
293
-                    'status' => 403,
294
-                ]
295
-            );
296
-        }
297
-    }
298
-
299
-
300
-    /**
301
-     * Given a ton of input, determines the value to use for the models.
302
-     *
303
-     * @return mixed
304
-     * @throws DomainException
305
-     * @throws EE_Error
306
-     * @throws RestException
307
-     * @throws DomainException
308
-     * @since 4.9.72.p
309
-     */
310
-    public function determineConditionsQueryParameterValue()
311
-    {
312
-        if ($this->valueIsArrayDuringRead()) {
313
-            return $this->determineModelValueGivenRestInputArray();
314
-        }
315
-        return ModelDataTranslator::prepareFieldValueFromJson(
316
-            $this->getField(),
317
-            $this->getQueryParamValue(),
318
-            $this->getContext()->getRequestedVersion(),
319
-            $this->getTimezone()
320
-        );
321
-    }
322
-
323
-
324
-    /**
325
-     * Given that the array value provided was itself an array, handles finding the correct value to pass to the model.
326
-     *
327
-     * @return array|null
328
-     * @throws RestException
329
-     * @throws EE_Error
330
-     * @since 4.9.72.p
331
-     * @noinspection PhpStatementHasEmptyBodyInspection
332
-     */
333
-    private function determineModelValueGivenRestInputArray(): ?array
334
-    {
335
-        $this->transformSimplifiedSpecifiedOperatorSyntaxIntoStandardSyntax();
336
-        // did they specify an operator?
337
-        if ($this->valueIsLegacySpecifiedOperator()) {
338
-            $query_param_value = $this->getQueryParamValue();
339
-            $sub_array_key     = $query_param_value[0];
340
-            $translated_value  = [$sub_array_key];
341
-            if ($this->operatorIsNAry($sub_array_key)) {
342
-                $translated_value[] = $this->prepareValuesFromJson($query_param_value[1]);
343
-            } elseif ($this->operatorIsTernary($sub_array_key)) {
344
-                $translated_value[] = [
345
-                    $this->prepareValuesFromJson($query_param_value[1][0]),
346
-                    $this->prepareValuesFromJson($query_param_value[1][1]),
347
-                ];
348
-            } elseif ($this->operatorIsLike($sub_array_key)) {
349
-                // we want to leave this value mostly-as-is (eg don't force it to be a float
350
-                // or a boolean or an enum value. Leave it as-is with wildcards etc)
351
-                // but do verify it at least doesn't have any serialized data
352
-                ModelDataTranslator::throwExceptionIfContainsSerializedData($query_param_value[1]);
353
-                $translated_value[] = $query_param_value[1];
354
-            } elseif ($this->operatorIsUnary($sub_array_key)) {
355
-                // no arguments should have been provided, so don't look for any
356
-            } elseif ($this->operatorIsBinary($sub_array_key)) {
357
-                // it's a valid operator, but none of the exceptions. Treat it normally.
358
-                $translated_value[] = $this->prepareValuesFromJson($query_param_value[1]);
359
-            } else {
360
-                // so they provided a valid operator, but wrong number of arguments
361
-                $this->throwWrongNumberOfArgsExceptionIfDebugging($sub_array_key);
362
-                $translated_value = null;
363
-            }
364
-        } else {
365
-            // so they didn't provide a valid operator
366
-            // if we aren't in debug mode, then just try our best to fulfill the user's request
367
-            $this->throwInvalidOperatorExceptionIfDebugging();
368
-            $translated_value = null;
369
-        }
370
-        return $translated_value;
371
-    }
372
-
373
-
374
-    /**
375
-     * Returns if this request is a "read" request and the value provided was an array.
376
-     * This will indicate is such things as `array('<', 123)` and `array('IN', array(1,2,3))` are acceptable or not.
377
-     *
378
-     * @return bool
379
-     * @since 4.9.72.p
380
-     */
381
-    private function valueIsArrayDuringRead(): bool
382
-    {
383
-        return ! $this->getContext()->isWriting() && is_array($this->getQueryParamValue());
384
-    }
385
-
386
-
387
-    /**
388
-     * Returns if the value provided was an associative array (we should have already verified it's an array of some
389
-     * sort). If the value is an associative array, it had better be in the simplified specified operator structure.
390
-     *
391
-     * @return bool
392
-     * @since 4.9.72.p
393
-     */
394
-    private function valueIsAssociativeArray(): bool
395
-    {
396
-        return ! EEH_Array::is_array_numerically_and_sequentially_indexed((array) $this->getQueryParamValue());
397
-    }
398
-
399
-
400
-    /**
401
-     * Checks if the array value is itself an array that fits into the simplified specified operator structure
402
-     * (eg ["!=" => 123]).
403
-     *
404
-     * @return bool
405
-     * @since 4.9.72.p
406
-     */
407
-    private function valueIsSimplifiedSpecifiedOperator(): bool
408
-    {
409
-        return count($this->getQueryParamValue()) === 1
410
-            && array_key_exists(
411
-                key($this->getQueryParamValue()),
412
-                $this->getContext()->getModel()->valid_operators()
413
-            );
414
-    }
415
-
416
-
417
-    /**
418
-     * Throws an exception if the sub-value is an array (eg ["!=" => []). It needs to just be a string,
419
-     * of either comma-separated-values, or a JSON array.
420
-     *
421
-     * @param $sub_array_key
422
-     * @param $sub_array_value
423
-     * @throws RestException
424
-     * @since 4.9.72.p
425
-     */
426
-    private function assertSubValueIsntArray($sub_array_key, $sub_array_value)
427
-    {
428
-        if (is_array($sub_array_value) && EED_Core_Rest_Api::debugMode()) {
429
-            throw new RestException(
430
-                'csv_or_json_string_only',
431
-                sprintf(
432
-                /* translators: 1: variable name*/
433
-                    esc_html__(
434
-                        'The value provided for the operator "%1$s" should be comma-separated value string or a JSON array.',
435
-                        'event_espresso'
436
-                    ),
437
-                    $sub_array_key
438
-                ),
439
-                [
440
-                    'status' => 400,
441
-                ]
442
-            );
443
-        }
444
-    }
445
-
446
-
447
-    /**
448
-     * Determines if the sub-array key is an operator taking 3 or more operators.
449
-     *
450
-     * @param $sub_array_key
451
-     * @return bool
452
-     * @since 4.9.72.p
453
-     */
454
-    private function subArrayKeyIsNonBinaryOperator($sub_array_key): bool
455
-    {
456
-        return array_key_exists(
457
-            $sub_array_key,
458
-            array_merge(
459
-                $this->getContext()->getModel()->valid_in_style_operators(),
460
-                $this->getContext()->getModel()->valid_between_style_operators()
461
-            )
462
-        );
463
-    }
464
-
465
-
466
-    /**
467
-     * Given that the $sub_array_key is a string, checks if it's an operator taking only 1 argument.
468
-     *
469
-     * @param string $sub_array_key
470
-     * @return bool
471
-     * @since 4.9.72.p
472
-     */
473
-    private function subArrayKeyIsUnaryOperator(string $sub_array_key): bool
474
-    {
475
-        return array_key_exists(
476
-            $sub_array_key,
477
-            $this->getContext()->getModel()->valid_null_style_operators()
478
-        );
479
-    }
480
-
481
-
482
-    /**
483
-     * Parses the $sub_array_value string into an array (given it could either be a comma-separated-list or a JSON
484
-     * array). eg `"1,2,3"` or `"[1,2,3]"` into `array(1,2,3)`.
485
-     *
486
-     * @param $sub_array_value
487
-     * @return array|mixed|object
488
-     * @since 4.9.72.p
489
-     */
490
-    private function extractQuickStyleSpecifiedOperatorValue($sub_array_value)
491
-    {
492
-        // the value should be JSON or CSV
493
-        $values = json_decode($sub_array_value);
494
-        if (! is_array($values)) {
495
-            $values = array_filter(
496
-                array_map(
497
-                    'trim',
498
-                    explode(
499
-                        ',',
500
-                        $sub_array_value
501
-                    )
502
-                )
503
-            );
504
-        }
505
-        return $values;
506
-    }
507
-
508
-
509
-    /**
510
-     * Throws an exception if the value isn't a simplified specified operator (only called when we expect that).
511
-     *
512
-     * @throws RestException
513
-     * @since 4.9.72.p
514
-     */
515
-    private function assertSimplifiedSpecifiedOperator()
516
-    {
517
-        if (! $this->valueIsSimplifiedSpecifiedOperator() && EED_Core_Rest_Api::debugMode()) {
518
-            throw new RestException(
519
-                'numerically_indexed_array_of_values_only',
520
-                sprintf(
521
-                /* translators: 1: variable name*/
522
-                    esc_html__(
523
-                        'The array provided for the parameter "%1$s" should be numerically indexed.',
524
-                        'event_espresso'
525
-                    ),
526
-                    $this->getQueryParamKey()
527
-                ),
528
-                [
529
-                    'status' => 400,
530
-                ]
531
-            );
532
-        }
533
-    }
534
-
535
-
536
-    /**
537
-     * If query_param_value were in the simplified specific operator structure, change it into the legacy structure.
538
-     *
539
-     * @throws RestException
540
-     * @since 4.9.72.p
541
-     */
542
-    private function transformSimplifiedSpecifiedOperatorSyntaxIntoStandardSyntax()
543
-    {
544
-        if ($this->valueIsAssociativeArray()) {
545
-            $this->assertSimplifiedSpecifiedOperator();
546
-            $query_param_value = $this->getQueryParamValue();
547
-            $sub_array_value   = reset($query_param_value);
548
-            $sub_array_key     = key($query_param_value);
549
-            $this->assertSubValueIsntArray($sub_array_key, $sub_array_value);
550
-            // they're doing something like "&where[EVT_ID][IN]=1,2,3" or "&where[EVT_ID][>]=5"
551
-            if ($this->subArrayKeyIsNonBinaryOperator($sub_array_key)) {
552
-                $this->setQueryParamValue(
553
-                    [
554
-                        $sub_array_key,
555
-                        $this->extractQuickStyleSpecifiedOperatorValue($sub_array_value),
556
-                    ]
557
-                );
558
-            } elseif ($this->subArrayKeyIsUnaryOperator($sub_array_key)) {
559
-                $this->setQueryParamValue([$sub_array_key]);
560
-            } else {
561
-                $this->setQueryParamValue([$sub_array_key, $sub_array_value]);
562
-            }
563
-        }
564
-    }
565
-
566
-
567
-    /**
568
-     * Returns true is the value is an array using the legacy structure to specify the operator. Eg `array('!=',123)`.
569
-     *
570
-     * @return bool
571
-     * @since 4.9.72.p
572
-     */
573
-    private function valueIsLegacySpecifiedOperator(): bool
574
-    {
575
-        $valid_operators   = $this->getContext()->getModel()->valid_operators();
576
-        $query_param_value = $this->getQueryParamValue();
577
-        return isset($query_param_value[0])
578
-            && isset($valid_operators[ $query_param_value[0] ]);
579
-    }
580
-
581
-
582
-    /**
583
-     * Returns true if the value specified operator accepts arbitrary number of arguments, like "IN".
584
-     *
585
-     * @param $operator
586
-     * @return bool
587
-     * @since 4.9.72.p
588
-     */
589
-    private function operatorIsNAry($operator): bool
590
-    {
591
-        $valueArray = $this->getQueryParamValue();
592
-        return array_key_exists(
593
-                $operator,
594
-                $this->getContext()->getModel()->valid_in_style_operators()
595
-            )
596
-            && isset($valueArray[1])
597
-            && is_array($valueArray[1])
598
-            && ! isset($valueArray[2]);
599
-    }
600
-
601
-
602
-    /**
603
-     * Returns true if the operator accepts 3 arguments (eg "BETWEEN").
604
-     * So we're looking for a value that looks like
605
-     * `array('BETWEEN', array('2015-01-01T00:00:00', '2016-01-01T00:00:00'))`.
606
-     *
607
-     * @param $operator
608
-     * @return bool
609
-     * @since 4.9.72.p
610
-     */
611
-    private function operatorIsTernary($operator): bool
612
-    {
613
-        $query_param_value = $this->getQueryParamValue();
614
-        return array_key_exists($operator, $this->getContext()->getModel()->valid_between_style_operators())
615
-            && isset($query_param_value[1])
616
-            && is_array($query_param_value[1])
617
-            && isset($query_param_value[1][0], $query_param_value[1][1])
618
-            && ! isset($query_param_value[1][2])
619
-            && ! isset($query_param_value[2]);
620
-    }
621
-
622
-
623
-    /**
624
-     * Returns true if the operator is a similar to LIKE, indicating the value may have wildcards we should leave alone.
625
-     *
626
-     * @param $operator
627
-     * @return bool
628
-     * @since 4.9.72.p
629
-     */
630
-    private function operatorIsLike($operator): bool
631
-    {
632
-        $query_param_value = $this->getQueryParamValue();
633
-        return array_key_exists($operator, $this->getContext()->getModel()->valid_like_style_operators())
634
-            && isset($query_param_value[1])
635
-            && ! isset($query_param_value[2]);
636
-    }
637
-
638
-
639
-    /**
640
-     * Returns true if the operator only takes one argument (eg it's like `IS NULL`).
641
-     *
642
-     * @param $operator
643
-     * @return bool
644
-     * @since 4.9.72.p
645
-     */
646
-    private function operatorIsUnary($operator): bool
647
-    {
648
-        $query_param_value = $this->getQueryParamValue();
649
-        return array_key_exists($operator, $this->getContext()->getModel()->valid_null_style_operators())
650
-            && ! isset($query_param_value[1]);
651
-    }
652
-
653
-
654
-    /**
655
-     * Returns true if the operator specified is a binary operator (eg `=`, `!=`)
656
-     *
657
-     * @param $operator
658
-     * @return bool
659
-     * @since 4.9.72.p
660
-     */
661
-    private function operatorIsBinary($operator): bool
662
-    {
663
-        $query_param_value = $this->getQueryParamValue();
664
-        $model             = $this->getContext()->getModel();
665
-        return isset($query_param_value[1])
666
-            && ! isset($query_param_value[2])
667
-            && ! array_key_exists(
668
-                $operator,
669
-                array_merge(
670
-                    $model->valid_in_style_operators(),
671
-                    $model->valid_null_style_operators(),
672
-                    $model->valid_like_style_operators(),
673
-                    $model->valid_between_style_operators()
674
-                )
675
-            );
676
-    }
677
-
678
-
679
-    /**
680
-     * If we're debugging, throws an exception saying that the wrong number of arguments was provided.
681
-     *
682
-     * @param $operator
683
-     * @throws RestException
684
-     * @since 4.9.72.p
685
-     */
686
-    private function throwWrongNumberOfArgsExceptionIfDebugging($operator)
687
-    {
688
-        if (EED_Core_Rest_Api::debugMode()) {
689
-            throw new RestException(
690
-                'wrong_number_of_arguments',
691
-                sprintf(
692
-                    esc_html__(
693
-                        'The operator you provided, "%1$s" had the wrong number of arguments',
694
-                        'event_espresso'
695
-                    ),
696
-                    $operator
697
-                ),
698
-                [
699
-                    'status' => 400,
700
-                ]
701
-            );
702
-        }
703
-    }
704
-
705
-
706
-    /**
707
-     * Wrapper for ModelDataTranslator::prepareFieldValuesFromJson(), just a tad more DRY.
708
-     *
709
-     * @param $value
710
-     * @return mixed
711
-     * @throws RestException
712
-     * @throws EE_Error
713
-     * @since 4.9.72.p
714
-     */
715
-    private function prepareValuesFromJson($value)
716
-    {
717
-        return ModelDataTranslator::prepareFieldValuesFromJson(
718
-            $this->getField(),
719
-            $value,
720
-            $this->getContext()->getRequestedVersion(),
721
-            $this->getTimezone()
722
-        );
723
-    }
724
-
725
-
726
-    /**
727
-     * Throws an exception if an invalid operator was specified and we're debugging.
728
-     *
729
-     * @throws RestException
730
-     * @since 4.9.72.p
731
-     */
732
-    private function throwInvalidOperatorExceptionIfDebugging()
733
-    {
734
-        // so they didn't provide a valid operator
735
-        if (EED_Core_Rest_Api::debugMode()) {
736
-            throw new RestException(
737
-                'invalid_operator',
738
-                sprintf(
739
-                    esc_html__(
740
-                        'You provided an invalid parameter, with key "%1$s" and value "%2$s"',
741
-                        'event_espresso'
742
-                    ),
743
-                    $this->getQueryParamKey(),
744
-                    $this->getQueryParamValue()
745
-                ),
746
-                [
747
-                    'status' => 400,
748
-                ]
749
-            );
750
-        }
751
-    }
752
-
753
-
754
-    /**
755
-     * Returns true if the query_param_key was a logic query parameter, eg `OR`, `AND`, `NOT`, `OR*`, etc.
756
-     *
757
-     * @return bool
758
-     * @since 4.9.72.p
759
-     */
760
-    private function isLogicQueryParam(): bool
761
-    {
762
-        return in_array($this->getQueryParamKeySansStars(), $this->getContext()->getModel()->logic_query_param_keys());
763
-    }
764
-
765
-
766
-    /**
767
-     * If the query param isn't for a field, it must be a nested query parameter which requires different logic.
768
-     *
769
-     * @return array
770
-     * @throws DomainException
771
-     * @throws EE_Error
772
-     * @throws RestException
773
-     * @throws InvalidDataTypeException
774
-     * @throws InvalidInterfaceException
775
-     * @throws InvalidArgumentException
776
-     * @since 4.9.72.p
777
-     */
778
-    public function determineNestedConditionQueryParameters(): array
779
-    {
780
-        // so this param doesn't correspond to a field eh?
781
-        if ($this->getContext()->isWriting()) {
782
-            // always tell API clients about invalid parameters when they're creating data. Otherwise,
783
-            // they are probably going to create invalid data
784
-            throw new RestException(
785
-                'invalid_field',
786
-                sprintf(
787
-                /* translators: 1: variable name */
788
-                    esc_html__('You have provided an invalid parameter: "%1$s"', 'event_espresso'),
789
-                    $this->getQueryParamKey()
790
-                )
791
-            );
792
-        }
793
-        // so it's not for a field, is it a logic query param key?
794
-        if ($this->isLogicQueryParam()) {
795
-            return ModelDataTranslator::prepareConditionsQueryParamsForModels(
796
-                $this->getQueryParamValue(),
797
-                $this->getContext()->getModel(),
798
-                $this->getContext()->getRequestedVersion()
799
-            );
800
-        }
801
-        if (EED_Core_Rest_Api::debugMode()) {
802
-            // only tell API clients they got it wrong if we're in debug mode
803
-            // otherwise try our best ot fulfill their request by ignoring this invalid data
804
-            throw new RestException(
805
-                'invalid_parameter',
806
-                sprintf(
807
-                /* translators: 1: variable name */
808
-                    esc_html__(
809
-                        'You provided an invalid parameter, with key "%1$s"',
810
-                        'event_espresso'
811
-                    ),
812
-                    $this->getQueryParamKey()
813
-                ),
814
-                array(
815
-                    'status' => 400,
816
-                )
817
-            );
818
-        }
819
-        return [];
820
-    }
33
+	private string $query_param_key;
34
+
35
+	private $query_param_value;
36
+
37
+	private RestIncomingQueryParamContext $context;
38
+
39
+	private ?EE_Model_Field_Base $field;
40
+
41
+	/**
42
+	 * @var string same as $query_param_key but has the * and anything after it removed
43
+	 */
44
+	private string $query_param_key_sans_stars;
45
+
46
+	/**
47
+	 * @var string for timezone or timezone offset
48
+	 */
49
+	private string $timezone;
50
+
51
+	/**
52
+	 * @var bool if the field in $query_param_key is for a GMT field (eg `EVT_modified_gmt`)
53
+	 */
54
+	private bool $is_gmt_field = false;
55
+
56
+
57
+	/**
58
+	 * RestIncomingQueryParamMetadata constructor.
59
+	 * You probably want to call
60
+	 *
61
+	 * @param string                        $query_param_key
62
+	 * @param mixed                         $query_param_value
63
+	 * @param RestIncomingQueryParamContext $context
64
+	 * @throws EE_Error
65
+	 * @throws ReflectionException
66
+	 */
67
+	public function __construct(string $query_param_key, $query_param_value, RestIncomingQueryParamContext $context)
68
+	{
69
+		$this->query_param_key   = $query_param_key;
70
+		$this->query_param_value = $query_param_value;
71
+		$this->context           = $context;
72
+		$this->determineFieldAndTimezone();
73
+	}
74
+
75
+
76
+	/**
77
+	 * Gets the query parameter key. This may have been modified (see setQueryParamValue())
78
+	 *
79
+	 * @return string
80
+	 */
81
+	public function getQueryParamKey(): string
82
+	{
83
+		return $this->query_param_key;
84
+	}
85
+
86
+
87
+	/**
88
+	 * Modifies the query parameter key passed in (Eg this is done when rewriting the simplified specified operator REST
89
+	 * query parameters into the legacy structure)
90
+	 *
91
+	 * @param string|array|int|float $query_param_value
92
+	 */
93
+	private function setQueryParamValue($query_param_value)
94
+	{
95
+		$this->query_param_value = $query_param_value;
96
+	}
97
+
98
+
99
+	/**
100
+	 * Gets the original query parameter value passed in.
101
+	 *
102
+	 * @return mixed
103
+	 */
104
+	public function getQueryParamValue()
105
+	{
106
+		return $this->query_param_value;
107
+	}
108
+
109
+
110
+	/**
111
+	 * Gets the context object.
112
+	 *
113
+	 * @return RestIncomingQueryParamContext
114
+	 */
115
+	public function getContext(): RestIncomingQueryParamContext
116
+	{
117
+		return $this->context;
118
+	}
119
+
120
+
121
+	/**
122
+	 * Sets the query parameter key. This may be used to rewrite a key into its non-GMT alternative.
123
+	 *
124
+	 * @param string $query_param_key
125
+	 */
126
+	private function setQueryParamKey(string $query_param_key)
127
+	{
128
+		$this->query_param_key = $query_param_key;
129
+	}
130
+
131
+
132
+	/**
133
+	 * Gets the field the query parameter key indicated. This may be null (in cases where the query parameter key
134
+	 * did not indicate a field, eg if it were `OR`).
135
+	 *
136
+	 * @return EE_Model_Field_Base|null
137
+	 */
138
+	public function getField(): ?EE_Model_Field_Base
139
+	{
140
+		return $this->field;
141
+	}
142
+
143
+
144
+	/**
145
+	 * Gets the query parameter key (with the star and everything afterwards removed).
146
+	 *
147
+	 * @return string
148
+	 */
149
+	public function getQueryParamKeySansStars(): string
150
+	{
151
+		return $this->query_param_key_sans_stars;
152
+	}
153
+
154
+
155
+	/**
156
+	 * Gets the timezone associated with this model (the site timezone, except for GMT datetime fields).
157
+	 *
158
+	 * @return string
159
+	 */
160
+	public function getTimezone(): string
161
+	{
162
+		return $this->timezone;
163
+	}
164
+
165
+
166
+	/**
167
+	 * Returns whether this is a GMT field
168
+	 *
169
+	 * @return bool
170
+	 */
171
+	public function isGmtField(): bool
172
+	{
173
+		return $this->is_gmt_field;
174
+	}
175
+
176
+
177
+	/**
178
+	 * Sets the field indicated by the query parameter key (might be null).
179
+	 *
180
+	 * @param EE_Model_Field_Base|null $field
181
+	 */
182
+	private function setField(EE_Model_Field_Base $field = null)
183
+	{
184
+		$this->field = $field;
185
+	}
186
+
187
+
188
+	/**
189
+	 * Sets the query parameter key-with-stars-removed.
190
+	 *
191
+	 * @param string $query_param_key_sans_stars
192
+	 */
193
+	private function setQueryParamKeySansStars(string $query_param_key_sans_stars)
194
+	{
195
+		$this->query_param_key_sans_stars = $query_param_key_sans_stars;
196
+	}
197
+
198
+
199
+	/**
200
+	 * Sets the timezone (this could be a timezone offset string).
201
+	 *
202
+	 * @param string $timezone
203
+	 */
204
+	private function setTimezone(string $timezone)
205
+	{
206
+		$this->timezone = $timezone;
207
+	}
208
+
209
+
210
+	/**
211
+	 * @param bool|int|string|null $is_gmt_field
212
+	 */
213
+	private function setIsGmtField($is_gmt_field)
214
+	{
215
+		$this->is_gmt_field = filter_var($is_gmt_field, FILTER_VALIDATE_BOOLEAN);
216
+	}
217
+
218
+
219
+	/**
220
+	 * Determines what field, query param name, and query param name without stars, and timezone to use.
221
+	 *
222
+	 * @return void {
223
+	 * @throws EE_Error
224
+	 * @throws InvalidDataTypeException
225
+	 * @throws InvalidInterfaceException
226
+	 * @throws InvalidArgumentException
227
+	 * @throws ReflectionException
228
+	 * @throws Exception
229
+	 * @since 4.9.72.p
230
+	 * @type EE_Model_Field_Base $field
231
+	 */
232
+	private function determineFieldAndTimezone()
233
+	{
234
+		$this->setQueryParamKeySansStars(
235
+			ModelDataTranslator::removeStarsAndAnythingAfterFromConditionQueryParamKey(
236
+				$this->getQueryParamKey()
237
+			)
238
+		);
239
+		$this->setField(
240
+			ModelDataTranslator::deduceFieldFromQueryParam(
241
+				$this->getQueryParamKeySansStars(),
242
+				$this->getContext()->getModel()
243
+			)
244
+		);
245
+		// double-check is it a *_gmt field?
246
+		if (
247
+			! $this->getField() instanceof EE_Model_Field_Base
248
+			&& ModelDataTranslator::isGmtDateFieldName($this->getQueryParamKeySansStars())
249
+		) {
250
+			// yep, take off '_gmt', and find the field
251
+			$this->setQueryParamKey(ModelDataTranslator::removeGmtFromFieldName($this->getQueryParamKeySansStars()));
252
+			$this->setField(
253
+				ModelDataTranslator::deduceFieldFromQueryParam(
254
+					$this->getQueryParamKey(),
255
+					$this->context->getModel()
256
+				)
257
+			);
258
+			$this->setTimezone('UTC');
259
+			$this->setIsGmtField(true);
260
+		} elseif ($this->getField() instanceof EE_Datetime_Field) {
261
+			// so it's not a GMT field. Set the timezone on the model to the default
262
+			$this->setTimezone(EEH_DTT_Helper::get_valid_timezone_string());
263
+		} else {
264
+			// just keep using what's already set for the timezone
265
+			$this->setTimezone($this->context->getModel()->get_timezone());
266
+		}
267
+		$this->assertOnlyAdminCanReadPasswordFields();
268
+	}
269
+
270
+
271
+	/**
272
+	 * Throws an exception if a non-admin is trying to query by password.
273
+	 *
274
+	 * @throws RestException
275
+	 * @since 4.9.74.p
276
+	 */
277
+	private function assertOnlyAdminCanReadPasswordFields()
278
+	{
279
+		if (
280
+			$this->getField() instanceof EE_Password_Field
281
+			&& ! current_user_can(EE_Restriction_Generator_Base::get_default_restrictions_cap())
282
+		) {
283
+			// only full admins can query by password. sorry bub!
284
+			throw new RestException(
285
+				'only_admins_can_query_by_password',
286
+				// @codingStandardsIgnoreStart
287
+				esc_html__(
288
+					'You attempted to filter by a password field without the needed privileges. Only a full admin is allowed to do that.',
289
+					'event_espresso'
290
+				),
291
+				// @codingStandardsIgnoreEnd
292
+				[
293
+					'status' => 403,
294
+				]
295
+			);
296
+		}
297
+	}
298
+
299
+
300
+	/**
301
+	 * Given a ton of input, determines the value to use for the models.
302
+	 *
303
+	 * @return mixed
304
+	 * @throws DomainException
305
+	 * @throws EE_Error
306
+	 * @throws RestException
307
+	 * @throws DomainException
308
+	 * @since 4.9.72.p
309
+	 */
310
+	public function determineConditionsQueryParameterValue()
311
+	{
312
+		if ($this->valueIsArrayDuringRead()) {
313
+			return $this->determineModelValueGivenRestInputArray();
314
+		}
315
+		return ModelDataTranslator::prepareFieldValueFromJson(
316
+			$this->getField(),
317
+			$this->getQueryParamValue(),
318
+			$this->getContext()->getRequestedVersion(),
319
+			$this->getTimezone()
320
+		);
321
+	}
322
+
323
+
324
+	/**
325
+	 * Given that the array value provided was itself an array, handles finding the correct value to pass to the model.
326
+	 *
327
+	 * @return array|null
328
+	 * @throws RestException
329
+	 * @throws EE_Error
330
+	 * @since 4.9.72.p
331
+	 * @noinspection PhpStatementHasEmptyBodyInspection
332
+	 */
333
+	private function determineModelValueGivenRestInputArray(): ?array
334
+	{
335
+		$this->transformSimplifiedSpecifiedOperatorSyntaxIntoStandardSyntax();
336
+		// did they specify an operator?
337
+		if ($this->valueIsLegacySpecifiedOperator()) {
338
+			$query_param_value = $this->getQueryParamValue();
339
+			$sub_array_key     = $query_param_value[0];
340
+			$translated_value  = [$sub_array_key];
341
+			if ($this->operatorIsNAry($sub_array_key)) {
342
+				$translated_value[] = $this->prepareValuesFromJson($query_param_value[1]);
343
+			} elseif ($this->operatorIsTernary($sub_array_key)) {
344
+				$translated_value[] = [
345
+					$this->prepareValuesFromJson($query_param_value[1][0]),
346
+					$this->prepareValuesFromJson($query_param_value[1][1]),
347
+				];
348
+			} elseif ($this->operatorIsLike($sub_array_key)) {
349
+				// we want to leave this value mostly-as-is (eg don't force it to be a float
350
+				// or a boolean or an enum value. Leave it as-is with wildcards etc)
351
+				// but do verify it at least doesn't have any serialized data
352
+				ModelDataTranslator::throwExceptionIfContainsSerializedData($query_param_value[1]);
353
+				$translated_value[] = $query_param_value[1];
354
+			} elseif ($this->operatorIsUnary($sub_array_key)) {
355
+				// no arguments should have been provided, so don't look for any
356
+			} elseif ($this->operatorIsBinary($sub_array_key)) {
357
+				// it's a valid operator, but none of the exceptions. Treat it normally.
358
+				$translated_value[] = $this->prepareValuesFromJson($query_param_value[1]);
359
+			} else {
360
+				// so they provided a valid operator, but wrong number of arguments
361
+				$this->throwWrongNumberOfArgsExceptionIfDebugging($sub_array_key);
362
+				$translated_value = null;
363
+			}
364
+		} else {
365
+			// so they didn't provide a valid operator
366
+			// if we aren't in debug mode, then just try our best to fulfill the user's request
367
+			$this->throwInvalidOperatorExceptionIfDebugging();
368
+			$translated_value = null;
369
+		}
370
+		return $translated_value;
371
+	}
372
+
373
+
374
+	/**
375
+	 * Returns if this request is a "read" request and the value provided was an array.
376
+	 * This will indicate is such things as `array('<', 123)` and `array('IN', array(1,2,3))` are acceptable or not.
377
+	 *
378
+	 * @return bool
379
+	 * @since 4.9.72.p
380
+	 */
381
+	private function valueIsArrayDuringRead(): bool
382
+	{
383
+		return ! $this->getContext()->isWriting() && is_array($this->getQueryParamValue());
384
+	}
385
+
386
+
387
+	/**
388
+	 * Returns if the value provided was an associative array (we should have already verified it's an array of some
389
+	 * sort). If the value is an associative array, it had better be in the simplified specified operator structure.
390
+	 *
391
+	 * @return bool
392
+	 * @since 4.9.72.p
393
+	 */
394
+	private function valueIsAssociativeArray(): bool
395
+	{
396
+		return ! EEH_Array::is_array_numerically_and_sequentially_indexed((array) $this->getQueryParamValue());
397
+	}
398
+
399
+
400
+	/**
401
+	 * Checks if the array value is itself an array that fits into the simplified specified operator structure
402
+	 * (eg ["!=" => 123]).
403
+	 *
404
+	 * @return bool
405
+	 * @since 4.9.72.p
406
+	 */
407
+	private function valueIsSimplifiedSpecifiedOperator(): bool
408
+	{
409
+		return count($this->getQueryParamValue()) === 1
410
+			&& array_key_exists(
411
+				key($this->getQueryParamValue()),
412
+				$this->getContext()->getModel()->valid_operators()
413
+			);
414
+	}
415
+
416
+
417
+	/**
418
+	 * Throws an exception if the sub-value is an array (eg ["!=" => []). It needs to just be a string,
419
+	 * of either comma-separated-values, or a JSON array.
420
+	 *
421
+	 * @param $sub_array_key
422
+	 * @param $sub_array_value
423
+	 * @throws RestException
424
+	 * @since 4.9.72.p
425
+	 */
426
+	private function assertSubValueIsntArray($sub_array_key, $sub_array_value)
427
+	{
428
+		if (is_array($sub_array_value) && EED_Core_Rest_Api::debugMode()) {
429
+			throw new RestException(
430
+				'csv_or_json_string_only',
431
+				sprintf(
432
+				/* translators: 1: variable name*/
433
+					esc_html__(
434
+						'The value provided for the operator "%1$s" should be comma-separated value string or a JSON array.',
435
+						'event_espresso'
436
+					),
437
+					$sub_array_key
438
+				),
439
+				[
440
+					'status' => 400,
441
+				]
442
+			);
443
+		}
444
+	}
445
+
446
+
447
+	/**
448
+	 * Determines if the sub-array key is an operator taking 3 or more operators.
449
+	 *
450
+	 * @param $sub_array_key
451
+	 * @return bool
452
+	 * @since 4.9.72.p
453
+	 */
454
+	private function subArrayKeyIsNonBinaryOperator($sub_array_key): bool
455
+	{
456
+		return array_key_exists(
457
+			$sub_array_key,
458
+			array_merge(
459
+				$this->getContext()->getModel()->valid_in_style_operators(),
460
+				$this->getContext()->getModel()->valid_between_style_operators()
461
+			)
462
+		);
463
+	}
464
+
465
+
466
+	/**
467
+	 * Given that the $sub_array_key is a string, checks if it's an operator taking only 1 argument.
468
+	 *
469
+	 * @param string $sub_array_key
470
+	 * @return bool
471
+	 * @since 4.9.72.p
472
+	 */
473
+	private function subArrayKeyIsUnaryOperator(string $sub_array_key): bool
474
+	{
475
+		return array_key_exists(
476
+			$sub_array_key,
477
+			$this->getContext()->getModel()->valid_null_style_operators()
478
+		);
479
+	}
480
+
481
+
482
+	/**
483
+	 * Parses the $sub_array_value string into an array (given it could either be a comma-separated-list or a JSON
484
+	 * array). eg `"1,2,3"` or `"[1,2,3]"` into `array(1,2,3)`.
485
+	 *
486
+	 * @param $sub_array_value
487
+	 * @return array|mixed|object
488
+	 * @since 4.9.72.p
489
+	 */
490
+	private function extractQuickStyleSpecifiedOperatorValue($sub_array_value)
491
+	{
492
+		// the value should be JSON or CSV
493
+		$values = json_decode($sub_array_value);
494
+		if (! is_array($values)) {
495
+			$values = array_filter(
496
+				array_map(
497
+					'trim',
498
+					explode(
499
+						',',
500
+						$sub_array_value
501
+					)
502
+				)
503
+			);
504
+		}
505
+		return $values;
506
+	}
507
+
508
+
509
+	/**
510
+	 * Throws an exception if the value isn't a simplified specified operator (only called when we expect that).
511
+	 *
512
+	 * @throws RestException
513
+	 * @since 4.9.72.p
514
+	 */
515
+	private function assertSimplifiedSpecifiedOperator()
516
+	{
517
+		if (! $this->valueIsSimplifiedSpecifiedOperator() && EED_Core_Rest_Api::debugMode()) {
518
+			throw new RestException(
519
+				'numerically_indexed_array_of_values_only',
520
+				sprintf(
521
+				/* translators: 1: variable name*/
522
+					esc_html__(
523
+						'The array provided for the parameter "%1$s" should be numerically indexed.',
524
+						'event_espresso'
525
+					),
526
+					$this->getQueryParamKey()
527
+				),
528
+				[
529
+					'status' => 400,
530
+				]
531
+			);
532
+		}
533
+	}
534
+
535
+
536
+	/**
537
+	 * If query_param_value were in the simplified specific operator structure, change it into the legacy structure.
538
+	 *
539
+	 * @throws RestException
540
+	 * @since 4.9.72.p
541
+	 */
542
+	private function transformSimplifiedSpecifiedOperatorSyntaxIntoStandardSyntax()
543
+	{
544
+		if ($this->valueIsAssociativeArray()) {
545
+			$this->assertSimplifiedSpecifiedOperator();
546
+			$query_param_value = $this->getQueryParamValue();
547
+			$sub_array_value   = reset($query_param_value);
548
+			$sub_array_key     = key($query_param_value);
549
+			$this->assertSubValueIsntArray($sub_array_key, $sub_array_value);
550
+			// they're doing something like "&where[EVT_ID][IN]=1,2,3" or "&where[EVT_ID][>]=5"
551
+			if ($this->subArrayKeyIsNonBinaryOperator($sub_array_key)) {
552
+				$this->setQueryParamValue(
553
+					[
554
+						$sub_array_key,
555
+						$this->extractQuickStyleSpecifiedOperatorValue($sub_array_value),
556
+					]
557
+				);
558
+			} elseif ($this->subArrayKeyIsUnaryOperator($sub_array_key)) {
559
+				$this->setQueryParamValue([$sub_array_key]);
560
+			} else {
561
+				$this->setQueryParamValue([$sub_array_key, $sub_array_value]);
562
+			}
563
+		}
564
+	}
565
+
566
+
567
+	/**
568
+	 * Returns true is the value is an array using the legacy structure to specify the operator. Eg `array('!=',123)`.
569
+	 *
570
+	 * @return bool
571
+	 * @since 4.9.72.p
572
+	 */
573
+	private function valueIsLegacySpecifiedOperator(): bool
574
+	{
575
+		$valid_operators   = $this->getContext()->getModel()->valid_operators();
576
+		$query_param_value = $this->getQueryParamValue();
577
+		return isset($query_param_value[0])
578
+			&& isset($valid_operators[ $query_param_value[0] ]);
579
+	}
580
+
581
+
582
+	/**
583
+	 * Returns true if the value specified operator accepts arbitrary number of arguments, like "IN".
584
+	 *
585
+	 * @param $operator
586
+	 * @return bool
587
+	 * @since 4.9.72.p
588
+	 */
589
+	private function operatorIsNAry($operator): bool
590
+	{
591
+		$valueArray = $this->getQueryParamValue();
592
+		return array_key_exists(
593
+				$operator,
594
+				$this->getContext()->getModel()->valid_in_style_operators()
595
+			)
596
+			&& isset($valueArray[1])
597
+			&& is_array($valueArray[1])
598
+			&& ! isset($valueArray[2]);
599
+	}
600
+
601
+
602
+	/**
603
+	 * Returns true if the operator accepts 3 arguments (eg "BETWEEN").
604
+	 * So we're looking for a value that looks like
605
+	 * `array('BETWEEN', array('2015-01-01T00:00:00', '2016-01-01T00:00:00'))`.
606
+	 *
607
+	 * @param $operator
608
+	 * @return bool
609
+	 * @since 4.9.72.p
610
+	 */
611
+	private function operatorIsTernary($operator): bool
612
+	{
613
+		$query_param_value = $this->getQueryParamValue();
614
+		return array_key_exists($operator, $this->getContext()->getModel()->valid_between_style_operators())
615
+			&& isset($query_param_value[1])
616
+			&& is_array($query_param_value[1])
617
+			&& isset($query_param_value[1][0], $query_param_value[1][1])
618
+			&& ! isset($query_param_value[1][2])
619
+			&& ! isset($query_param_value[2]);
620
+	}
621
+
622
+
623
+	/**
624
+	 * Returns true if the operator is a similar to LIKE, indicating the value may have wildcards we should leave alone.
625
+	 *
626
+	 * @param $operator
627
+	 * @return bool
628
+	 * @since 4.9.72.p
629
+	 */
630
+	private function operatorIsLike($operator): bool
631
+	{
632
+		$query_param_value = $this->getQueryParamValue();
633
+		return array_key_exists($operator, $this->getContext()->getModel()->valid_like_style_operators())
634
+			&& isset($query_param_value[1])
635
+			&& ! isset($query_param_value[2]);
636
+	}
637
+
638
+
639
+	/**
640
+	 * Returns true if the operator only takes one argument (eg it's like `IS NULL`).
641
+	 *
642
+	 * @param $operator
643
+	 * @return bool
644
+	 * @since 4.9.72.p
645
+	 */
646
+	private function operatorIsUnary($operator): bool
647
+	{
648
+		$query_param_value = $this->getQueryParamValue();
649
+		return array_key_exists($operator, $this->getContext()->getModel()->valid_null_style_operators())
650
+			&& ! isset($query_param_value[1]);
651
+	}
652
+
653
+
654
+	/**
655
+	 * Returns true if the operator specified is a binary operator (eg `=`, `!=`)
656
+	 *
657
+	 * @param $operator
658
+	 * @return bool
659
+	 * @since 4.9.72.p
660
+	 */
661
+	private function operatorIsBinary($operator): bool
662
+	{
663
+		$query_param_value = $this->getQueryParamValue();
664
+		$model             = $this->getContext()->getModel();
665
+		return isset($query_param_value[1])
666
+			&& ! isset($query_param_value[2])
667
+			&& ! array_key_exists(
668
+				$operator,
669
+				array_merge(
670
+					$model->valid_in_style_operators(),
671
+					$model->valid_null_style_operators(),
672
+					$model->valid_like_style_operators(),
673
+					$model->valid_between_style_operators()
674
+				)
675
+			);
676
+	}
677
+
678
+
679
+	/**
680
+	 * If we're debugging, throws an exception saying that the wrong number of arguments was provided.
681
+	 *
682
+	 * @param $operator
683
+	 * @throws RestException
684
+	 * @since 4.9.72.p
685
+	 */
686
+	private function throwWrongNumberOfArgsExceptionIfDebugging($operator)
687
+	{
688
+		if (EED_Core_Rest_Api::debugMode()) {
689
+			throw new RestException(
690
+				'wrong_number_of_arguments',
691
+				sprintf(
692
+					esc_html__(
693
+						'The operator you provided, "%1$s" had the wrong number of arguments',
694
+						'event_espresso'
695
+					),
696
+					$operator
697
+				),
698
+				[
699
+					'status' => 400,
700
+				]
701
+			);
702
+		}
703
+	}
704
+
705
+
706
+	/**
707
+	 * Wrapper for ModelDataTranslator::prepareFieldValuesFromJson(), just a tad more DRY.
708
+	 *
709
+	 * @param $value
710
+	 * @return mixed
711
+	 * @throws RestException
712
+	 * @throws EE_Error
713
+	 * @since 4.9.72.p
714
+	 */
715
+	private function prepareValuesFromJson($value)
716
+	{
717
+		return ModelDataTranslator::prepareFieldValuesFromJson(
718
+			$this->getField(),
719
+			$value,
720
+			$this->getContext()->getRequestedVersion(),
721
+			$this->getTimezone()
722
+		);
723
+	}
724
+
725
+
726
+	/**
727
+	 * Throws an exception if an invalid operator was specified and we're debugging.
728
+	 *
729
+	 * @throws RestException
730
+	 * @since 4.9.72.p
731
+	 */
732
+	private function throwInvalidOperatorExceptionIfDebugging()
733
+	{
734
+		// so they didn't provide a valid operator
735
+		if (EED_Core_Rest_Api::debugMode()) {
736
+			throw new RestException(
737
+				'invalid_operator',
738
+				sprintf(
739
+					esc_html__(
740
+						'You provided an invalid parameter, with key "%1$s" and value "%2$s"',
741
+						'event_espresso'
742
+					),
743
+					$this->getQueryParamKey(),
744
+					$this->getQueryParamValue()
745
+				),
746
+				[
747
+					'status' => 400,
748
+				]
749
+			);
750
+		}
751
+	}
752
+
753
+
754
+	/**
755
+	 * Returns true if the query_param_key was a logic query parameter, eg `OR`, `AND`, `NOT`, `OR*`, etc.
756
+	 *
757
+	 * @return bool
758
+	 * @since 4.9.72.p
759
+	 */
760
+	private function isLogicQueryParam(): bool
761
+	{
762
+		return in_array($this->getQueryParamKeySansStars(), $this->getContext()->getModel()->logic_query_param_keys());
763
+	}
764
+
765
+
766
+	/**
767
+	 * If the query param isn't for a field, it must be a nested query parameter which requires different logic.
768
+	 *
769
+	 * @return array
770
+	 * @throws DomainException
771
+	 * @throws EE_Error
772
+	 * @throws RestException
773
+	 * @throws InvalidDataTypeException
774
+	 * @throws InvalidInterfaceException
775
+	 * @throws InvalidArgumentException
776
+	 * @since 4.9.72.p
777
+	 */
778
+	public function determineNestedConditionQueryParameters(): array
779
+	{
780
+		// so this param doesn't correspond to a field eh?
781
+		if ($this->getContext()->isWriting()) {
782
+			// always tell API clients about invalid parameters when they're creating data. Otherwise,
783
+			// they are probably going to create invalid data
784
+			throw new RestException(
785
+				'invalid_field',
786
+				sprintf(
787
+				/* translators: 1: variable name */
788
+					esc_html__('You have provided an invalid parameter: "%1$s"', 'event_espresso'),
789
+					$this->getQueryParamKey()
790
+				)
791
+			);
792
+		}
793
+		// so it's not for a field, is it a logic query param key?
794
+		if ($this->isLogicQueryParam()) {
795
+			return ModelDataTranslator::prepareConditionsQueryParamsForModels(
796
+				$this->getQueryParamValue(),
797
+				$this->getContext()->getModel(),
798
+				$this->getContext()->getRequestedVersion()
799
+			);
800
+		}
801
+		if (EED_Core_Rest_Api::debugMode()) {
802
+			// only tell API clients they got it wrong if we're in debug mode
803
+			// otherwise try our best ot fulfill their request by ignoring this invalid data
804
+			throw new RestException(
805
+				'invalid_parameter',
806
+				sprintf(
807
+				/* translators: 1: variable name */
808
+					esc_html__(
809
+						'You provided an invalid parameter, with key "%1$s"',
810
+						'event_espresso'
811
+					),
812
+					$this->getQueryParamKey()
813
+				),
814
+				array(
815
+					'status' => 400,
816
+				)
817
+			);
818
+		}
819
+		return [];
820
+	}
821 821
 }
822 822
 // End of file RestQueryParamMetadata.php
823 823
 // Location: EventEspresso\core\libraries\rest_api/RestQueryParamMetadata.php
Please login to merge, or discard this patch.
Spacing   +3 added lines, -3 removed lines patch added patch discarded remove patch
@@ -491,7 +491,7 @@  discard block
 block discarded – undo
491 491
     {
492 492
         // the value should be JSON or CSV
493 493
         $values = json_decode($sub_array_value);
494
-        if (! is_array($values)) {
494
+        if ( ! is_array($values)) {
495 495
             $values = array_filter(
496 496
                 array_map(
497 497
                     'trim',
@@ -514,7 +514,7 @@  discard block
 block discarded – undo
514 514
      */
515 515
     private function assertSimplifiedSpecifiedOperator()
516 516
     {
517
-        if (! $this->valueIsSimplifiedSpecifiedOperator() && EED_Core_Rest_Api::debugMode()) {
517
+        if ( ! $this->valueIsSimplifiedSpecifiedOperator() && EED_Core_Rest_Api::debugMode()) {
518 518
             throw new RestException(
519 519
                 'numerically_indexed_array_of_values_only',
520 520
                 sprintf(
@@ -575,7 +575,7 @@  discard block
 block discarded – undo
575 575
         $valid_operators   = $this->getContext()->getModel()->valid_operators();
576 576
         $query_param_value = $this->getQueryParamValue();
577 577
         return isset($query_param_value[0])
578
-            && isset($valid_operators[ $query_param_value[0] ]);
578
+            && isset($valid_operators[$query_param_value[0]]);
579 579
     }
580 580
 
581 581
 
Please login to merge, or discard this patch.
core/libraries/rest_api/Capabilities.php 2 patches
Indentation   +189 added lines, -189 removed lines patch added patch discarded remove patch
@@ -16,203 +16,203 @@
 block discarded – undo
16 16
  */
17 17
 class Capabilities
18 18
 {
19
-    /**
20
-     * The current user can see at least SOME of these entities.
21
-     *
22
-     * @param EEM_Base $model
23
-     * @param string   $model_context one of the return values from EEM_Base::valid_cap_contexts()
24
-     * @return boolean
25
-     * @throws EE_Error
26
-     */
27
-    public static function currentUserHasPartialAccessTo(
28
-        EEM_Base $model,
29
-        string $model_context = EEM_Base::caps_read
30
-    ): bool {
31
-        if (
32
-            apply_filters(
33
-                'FHEE__Capabilities__current_user_has_partial_access_to__override_begin',
34
-                false,
35
-                $model,
36
-                $model
37
-            )
38
-        ) {
39
-            return true;
40
-        }
41
-        foreach ($model->caps_missing($model_context) as $restriction_obj) {
42
-            if ($restriction_obj instanceof EE_Return_None_Where_Conditions) {
43
-                return false;
44
-            }
45
-        }
46
-        if (
47
-            apply_filters(
48
-                'FHEE__Capabilities__current_user_has_partial_access_to__override_end',
49
-                false,
50
-                $model,
51
-                $model
52
-            )
53
-        ) {
54
-            return false;
55
-        }
56
-        return true;
57
-    }
19
+	/**
20
+	 * The current user can see at least SOME of these entities.
21
+	 *
22
+	 * @param EEM_Base $model
23
+	 * @param string   $model_context one of the return values from EEM_Base::valid_cap_contexts()
24
+	 * @return boolean
25
+	 * @throws EE_Error
26
+	 */
27
+	public static function currentUserHasPartialAccessTo(
28
+		EEM_Base $model,
29
+		string $model_context = EEM_Base::caps_read
30
+	): bool {
31
+		if (
32
+			apply_filters(
33
+				'FHEE__Capabilities__current_user_has_partial_access_to__override_begin',
34
+				false,
35
+				$model,
36
+				$model
37
+			)
38
+		) {
39
+			return true;
40
+		}
41
+		foreach ($model->caps_missing($model_context) as $restriction_obj) {
42
+			if ($restriction_obj instanceof EE_Return_None_Where_Conditions) {
43
+				return false;
44
+			}
45
+		}
46
+		if (
47
+			apply_filters(
48
+				'FHEE__Capabilities__current_user_has_partial_access_to__override_end',
49
+				false,
50
+				$model,
51
+				$model
52
+			)
53
+		) {
54
+			return false;
55
+		}
56
+		return true;
57
+	}
58 58
 
59 59
 
60
-    /**
61
-     * Gets an array of all the capabilities the current user is missing that affected
62
-     * the query
63
-     *
64
-     * @param EEM_Base $model
65
-     * @param string   $request_type one of the constants on WP_JSON_Server
66
-     * @return array
67
-     * @throws EE_Error
68
-     */
69
-    public static function getMissingPermissions(EEM_Base $model, string $request_type = EEM_Base::caps_read): array
70
-    {
71
-        return $model->caps_missing($request_type);
72
-    }
60
+	/**
61
+	 * Gets an array of all the capabilities the current user is missing that affected
62
+	 * the query
63
+	 *
64
+	 * @param EEM_Base $model
65
+	 * @param string   $request_type one of the constants on WP_JSON_Server
66
+	 * @return array
67
+	 * @throws EE_Error
68
+	 */
69
+	public static function getMissingPermissions(EEM_Base $model, string $request_type = EEM_Base::caps_read): array
70
+	{
71
+		return $model->caps_missing($request_type);
72
+	}
73 73
 
74 74
 
75
-    /**
76
-     * Gets a string of all the capabilities the current user is missing that affected
77
-     * the query
78
-     *
79
-     * @param EEM_Base $model
80
-     * @param string   $model_context one of the return values from EEM_Base::valid_cap_contexts()
81
-     * @return string
82
-     * @throws EE_Error
83
-     */
84
-    public static function getMissingPermissionsString(
85
-        EEM_Base $model,
86
-        string $model_context = EEM_Base::caps_read
87
-    ): string {
88
-        return implode(',', array_keys(self::getMissingPermissions($model, $model_context)));
89
-    }
75
+	/**
76
+	 * Gets a string of all the capabilities the current user is missing that affected
77
+	 * the query
78
+	 *
79
+	 * @param EEM_Base $model
80
+	 * @param string   $model_context one of the return values from EEM_Base::valid_cap_contexts()
81
+	 * @return string
82
+	 * @throws EE_Error
83
+	 */
84
+	public static function getMissingPermissionsString(
85
+		EEM_Base $model,
86
+		string $model_context = EEM_Base::caps_read
87
+	): string {
88
+		return implode(',', array_keys(self::getMissingPermissions($model, $model_context)));
89
+	}
90 90
 
91 91
 
92
-    /**
93
-     * "Removes" password-protected fields. Currently that means setting their values to their default.
94
-     *
95
-     * @param array            $entity
96
-     * @param EEM_Base         $model
97
-     * @param ModelVersionInfo $model_version_info
98
-     * @return array
99
-     * @throws EE_Error
100
-     * @since 4.9.74.p
101
-     */
102
-    public static function filterOutPasswordProtectedFields(
103
-        array $entity,
104
-        EEM_Base $model,
105
-        ModelVersionInfo $model_version_info
106
-    ): array {
107
-        $has_password = $model->hasPassword();
108
-        if ($has_password) {
109
-            $entity[ $model->getPasswordField()->get_name() ] = ModelDataTranslator::prepareFieldValueForJson(
110
-                $model->getPasswordField(),
111
-                $model->getPasswordField()->get_default_value(),
112
-                $model_version_info->requestedVersion()
113
-            );
114
-        }
115
-        foreach ($model->field_settings() as $field_name => $field_obj) {
116
-            if (
117
-                $has_password
118
-                && $model->getPasswordField()->fieldIsProtected($field_name)
119
-                && $entity[ $field_name ]
120
-            ) {
121
-                $replacement_value = ModelDataTranslator::prepareFieldValueForJson(
122
-                    $field_obj,
123
-                    $field_obj->get_default_value(),
124
-                    $model_version_info->requestedVersion()
125
-                );
126
-                if ($model_version_info->fieldHasRenderedFormat($field_obj)) {
127
-                    $entity[ $field_name ]['rendered'] = $replacement_value;
128
-                } elseif ($model_version_info->fieldHasPrettyFormat($field_obj)) {
129
-                    $entity[ $field_name ]['raw']    = $replacement_value;
130
-                    $entity[ $field_name ]['pretty'] = ModelDataTranslator::prepareFieldValueForJson(
131
-                        $field_obj,
132
-                        $field_obj->prepare_for_pretty_echoing($field_obj->get_default_value()),
133
-                        $model_version_info->requestedVersion()
134
-                    );
135
-                } else {
136
-                    // this is most likely an excerpt field. (These should have also had "rendered" and "raw"
137
-                    // versions, but we missed that, and can't change it without breaking backward compatibility)
138
-                    // so just remove it (or rather, set its default)
139
-                    // API clients will just need to look to fields with rendered formats to know if these have
140
-                    // been redacted. Sorry.
141
-                    $entity[ $field_name ] = $replacement_value;
142
-                }
143
-            }
144
-        }
145
-        return $entity;
146
-    }
92
+	/**
93
+	 * "Removes" password-protected fields. Currently that means setting their values to their default.
94
+	 *
95
+	 * @param array            $entity
96
+	 * @param EEM_Base         $model
97
+	 * @param ModelVersionInfo $model_version_info
98
+	 * @return array
99
+	 * @throws EE_Error
100
+	 * @since 4.9.74.p
101
+	 */
102
+	public static function filterOutPasswordProtectedFields(
103
+		array $entity,
104
+		EEM_Base $model,
105
+		ModelVersionInfo $model_version_info
106
+	): array {
107
+		$has_password = $model->hasPassword();
108
+		if ($has_password) {
109
+			$entity[ $model->getPasswordField()->get_name() ] = ModelDataTranslator::prepareFieldValueForJson(
110
+				$model->getPasswordField(),
111
+				$model->getPasswordField()->get_default_value(),
112
+				$model_version_info->requestedVersion()
113
+			);
114
+		}
115
+		foreach ($model->field_settings() as $field_name => $field_obj) {
116
+			if (
117
+				$has_password
118
+				&& $model->getPasswordField()->fieldIsProtected($field_name)
119
+				&& $entity[ $field_name ]
120
+			) {
121
+				$replacement_value = ModelDataTranslator::prepareFieldValueForJson(
122
+					$field_obj,
123
+					$field_obj->get_default_value(),
124
+					$model_version_info->requestedVersion()
125
+				);
126
+				if ($model_version_info->fieldHasRenderedFormat($field_obj)) {
127
+					$entity[ $field_name ]['rendered'] = $replacement_value;
128
+				} elseif ($model_version_info->fieldHasPrettyFormat($field_obj)) {
129
+					$entity[ $field_name ]['raw']    = $replacement_value;
130
+					$entity[ $field_name ]['pretty'] = ModelDataTranslator::prepareFieldValueForJson(
131
+						$field_obj,
132
+						$field_obj->prepare_for_pretty_echoing($field_obj->get_default_value()),
133
+						$model_version_info->requestedVersion()
134
+					);
135
+				} else {
136
+					// this is most likely an excerpt field. (These should have also had "rendered" and "raw"
137
+					// versions, but we missed that, and can't change it without breaking backward compatibility)
138
+					// so just remove it (or rather, set its default)
139
+					// API clients will just need to look to fields with rendered formats to know if these have
140
+					// been redacted. Sorry.
141
+					$entity[ $field_name ] = $replacement_value;
142
+				}
143
+			}
144
+		}
145
+		return $entity;
146
+	}
147 147
 
148 148
 
149
-    /**
150
-     * Takes a entity that's ready to be returned and removes fields which the user shouldn't be able to access.
151
-     *
152
-     * @param array            $entity
153
-     * @param EEM_Base         $model
154
-     * @param string           $request_type         one of the return values from EEM_Base::valid_cap_contexts()
155
-     * @param ModelVersionInfo $model_version_info
156
-     * @param string|null      $primary_key_string   result of EEM_Base::get_index_primary_key_string(), so that we can
157
-     *                                               use this with models that have no primary key
158
-     * @return array ready for converting into json
159
-     */
160
-    public static function filterOutInaccessibleEntityFields(
161
-        array $entity,
162
-        EEM_Base $model,
163
-        string $request_type,
164
-        ModelVersionInfo $model_version_info,
165
-        ?string $primary_key_string = null
166
-    ): array {
167
-        foreach ($model->field_settings() as $field_name => $field_obj) {
168
-            if (
169
-                $model_version_info->fieldHasRenderedFormat($field_obj)
170
-                && isset($entity[ $field_name ])
171
-                && is_array($entity[ $field_name ])
172
-                && isset($entity[ $field_name ]['raw'])
173
-            ) {
174
-                unset($entity[ $field_name ]['raw']);
175
-            }
176
-        }
177
-        // theoretically we may want to filter out specific fields for specific models
178
-        return apply_filters(
179
-            'FHEE__Capabilities__filter_out_inaccessible_entity_fields',
180
-            $entity,
181
-            $model,
182
-            $request_type
183
-        );
184
-    }
149
+	/**
150
+	 * Takes a entity that's ready to be returned and removes fields which the user shouldn't be able to access.
151
+	 *
152
+	 * @param array            $entity
153
+	 * @param EEM_Base         $model
154
+	 * @param string           $request_type         one of the return values from EEM_Base::valid_cap_contexts()
155
+	 * @param ModelVersionInfo $model_version_info
156
+	 * @param string|null      $primary_key_string   result of EEM_Base::get_index_primary_key_string(), so that we can
157
+	 *                                               use this with models that have no primary key
158
+	 * @return array ready for converting into json
159
+	 */
160
+	public static function filterOutInaccessibleEntityFields(
161
+		array $entity,
162
+		EEM_Base $model,
163
+		string $request_type,
164
+		ModelVersionInfo $model_version_info,
165
+		?string $primary_key_string = null
166
+	): array {
167
+		foreach ($model->field_settings() as $field_name => $field_obj) {
168
+			if (
169
+				$model_version_info->fieldHasRenderedFormat($field_obj)
170
+				&& isset($entity[ $field_name ])
171
+				&& is_array($entity[ $field_name ])
172
+				&& isset($entity[ $field_name ]['raw'])
173
+			) {
174
+				unset($entity[ $field_name ]['raw']);
175
+			}
176
+		}
177
+		// theoretically we may want to filter out specific fields for specific models
178
+		return apply_filters(
179
+			'FHEE__Capabilities__filter_out_inaccessible_entity_fields',
180
+			$entity,
181
+			$model,
182
+			$request_type
183
+		);
184
+	}
185 185
 
186 186
 
187
-    /**
188
-     * Verifies the current user has at least partial access to do this action on this model.
189
-     * If not, throws an exception (so we can define the code that sets up this error object
190
-     * once)
191
-     *
192
-     * @param EEM_Base $model
193
-     * @param string   $model_action_context
194
-     * @param string   $action_name
195
-     * @return void
196
-     * @throws RestException
197
-     * @throws EE_Error
198
-     */
199
-    public static function verifyAtLeastPartialAccessTo(
200
-        EEM_Base $model,
201
-        string $model_action_context,
202
-        string $action_name = 'list'
203
-    ): void {
204
-        if (! Capabilities::currentUserHasPartialAccessTo($model, $model_action_context)) {
205
-            $model_name_plural = EEH_Inflector::pluralize_and_lower($model->get_this_model_name());
206
-            throw new RestException(
207
-                sprintf('rest_cannot_%s_%s', strtolower($action_name), $model_name_plural),
208
-                sprintf(
209
-                    esc_html__('Sorry, you are not allowed to %1$s %2$s. Missing permissions: %3$s', 'event_espresso'),
210
-                    $action_name,
211
-                    $model_name_plural,
212
-                    Capabilities::getMissingPermissionsString($model, $model_action_context)
213
-                ),
214
-                ['status' => 403]
215
-            );
216
-        }
217
-    }
187
+	/**
188
+	 * Verifies the current user has at least partial access to do this action on this model.
189
+	 * If not, throws an exception (so we can define the code that sets up this error object
190
+	 * once)
191
+	 *
192
+	 * @param EEM_Base $model
193
+	 * @param string   $model_action_context
194
+	 * @param string   $action_name
195
+	 * @return void
196
+	 * @throws RestException
197
+	 * @throws EE_Error
198
+	 */
199
+	public static function verifyAtLeastPartialAccessTo(
200
+		EEM_Base $model,
201
+		string $model_action_context,
202
+		string $action_name = 'list'
203
+	): void {
204
+		if (! Capabilities::currentUserHasPartialAccessTo($model, $model_action_context)) {
205
+			$model_name_plural = EEH_Inflector::pluralize_and_lower($model->get_this_model_name());
206
+			throw new RestException(
207
+				sprintf('rest_cannot_%s_%s', strtolower($action_name), $model_name_plural),
208
+				sprintf(
209
+					esc_html__('Sorry, you are not allowed to %1$s %2$s. Missing permissions: %3$s', 'event_espresso'),
210
+					$action_name,
211
+					$model_name_plural,
212
+					Capabilities::getMissingPermissionsString($model, $model_action_context)
213
+				),
214
+				['status' => 403]
215
+			);
216
+		}
217
+	}
218 218
 }
Please login to merge, or discard this patch.
Spacing   +11 added lines, -11 removed lines patch added patch discarded remove patch
@@ -106,7 +106,7 @@  discard block
 block discarded – undo
106 106
     ): array {
107 107
         $has_password = $model->hasPassword();
108 108
         if ($has_password) {
109
-            $entity[ $model->getPasswordField()->get_name() ] = ModelDataTranslator::prepareFieldValueForJson(
109
+            $entity[$model->getPasswordField()->get_name()] = ModelDataTranslator::prepareFieldValueForJson(
110 110
                 $model->getPasswordField(),
111 111
                 $model->getPasswordField()->get_default_value(),
112 112
                 $model_version_info->requestedVersion()
@@ -116,7 +116,7 @@  discard block
 block discarded – undo
116 116
             if (
117 117
                 $has_password
118 118
                 && $model->getPasswordField()->fieldIsProtected($field_name)
119
-                && $entity[ $field_name ]
119
+                && $entity[$field_name]
120 120
             ) {
121 121
                 $replacement_value = ModelDataTranslator::prepareFieldValueForJson(
122 122
                     $field_obj,
@@ -124,10 +124,10 @@  discard block
 block discarded – undo
124 124
                     $model_version_info->requestedVersion()
125 125
                 );
126 126
                 if ($model_version_info->fieldHasRenderedFormat($field_obj)) {
127
-                    $entity[ $field_name ]['rendered'] = $replacement_value;
127
+                    $entity[$field_name]['rendered'] = $replacement_value;
128 128
                 } elseif ($model_version_info->fieldHasPrettyFormat($field_obj)) {
129
-                    $entity[ $field_name ]['raw']    = $replacement_value;
130
-                    $entity[ $field_name ]['pretty'] = ModelDataTranslator::prepareFieldValueForJson(
129
+                    $entity[$field_name]['raw']    = $replacement_value;
130
+                    $entity[$field_name]['pretty'] = ModelDataTranslator::prepareFieldValueForJson(
131 131
                         $field_obj,
132 132
                         $field_obj->prepare_for_pretty_echoing($field_obj->get_default_value()),
133 133
                         $model_version_info->requestedVersion()
@@ -138,7 +138,7 @@  discard block
 block discarded – undo
138 138
                     // so just remove it (or rather, set its default)
139 139
                     // API clients will just need to look to fields with rendered formats to know if these have
140 140
                     // been redacted. Sorry.
141
-                    $entity[ $field_name ] = $replacement_value;
141
+                    $entity[$field_name] = $replacement_value;
142 142
                 }
143 143
             }
144 144
         }
@@ -167,11 +167,11 @@  discard block
 block discarded – undo
167 167
         foreach ($model->field_settings() as $field_name => $field_obj) {
168 168
             if (
169 169
                 $model_version_info->fieldHasRenderedFormat($field_obj)
170
-                && isset($entity[ $field_name ])
171
-                && is_array($entity[ $field_name ])
172
-                && isset($entity[ $field_name ]['raw'])
170
+                && isset($entity[$field_name])
171
+                && is_array($entity[$field_name])
172
+                && isset($entity[$field_name]['raw'])
173 173
             ) {
174
-                unset($entity[ $field_name ]['raw']);
174
+                unset($entity[$field_name]['raw']);
175 175
             }
176 176
         }
177 177
         // theoretically we may want to filter out specific fields for specific models
@@ -201,7 +201,7 @@  discard block
 block discarded – undo
201 201
         string $model_action_context,
202 202
         string $action_name = 'list'
203 203
     ): void {
204
-        if (! Capabilities::currentUserHasPartialAccessTo($model, $model_action_context)) {
204
+        if ( ! Capabilities::currentUserHasPartialAccessTo($model, $model_action_context)) {
205 205
             $model_name_plural = EEH_Inflector::pluralize_and_lower($model->get_this_model_name());
206 206
             throw new RestException(
207 207
                 sprintf('rest_cannot_%s_%s', strtolower($action_name), $model_name_plural),
Please login to merge, or discard this patch.
core/libraries/rest_api/RestIncomingQueryParamContext.php 1 patch
Indentation   +46 added lines, -46 removed lines patch added patch discarded remove patch
@@ -18,62 +18,62 @@
 block discarded – undo
18 18
  */
19 19
 class RestIncomingQueryParamContext
20 20
 {
21
-    private EEM_Base $model;
21
+	private EEM_Base $model;
22 22
 
23
-    private string $requested_version;
23
+	private string $requested_version;
24 24
 
25
-    private bool $writing;
25
+	private bool $writing;
26 26
 
27 27
 
28
-    /**
29
-     * RestIncomingQueryParamContext constructor.
30
-     *
31
-     * @param EEM_Base        $model
32
-     * @param string          $requested_version
33
-     * @param bool|int|string $writing
34
-     */
35
-    public function __construct(EEM_Base $model, string $requested_version, $writing)
36
-    {
37
-        $this->model             = $model;
38
-        $this->requested_version = $requested_version;
39
-        $this->writing           = filter_var($writing, FILTER_VALIDATE_BOOLEAN);
40
-    }
28
+	/**
29
+	 * RestIncomingQueryParamContext constructor.
30
+	 *
31
+	 * @param EEM_Base        $model
32
+	 * @param string          $requested_version
33
+	 * @param bool|int|string $writing
34
+	 */
35
+	public function __construct(EEM_Base $model, string $requested_version, $writing)
36
+	{
37
+		$this->model             = $model;
38
+		$this->requested_version = $requested_version;
39
+		$this->writing           = filter_var($writing, FILTER_VALIDATE_BOOLEAN);
40
+	}
41 41
 
42 42
 
43
-    /**
44
-     * Gets the model currently being requested, eg class EEM_Event
45
-     *
46
-     * @return EEM_Base
47
-     * @since 4.9.72.p
48
-     */
49
-    public function getModel(): EEM_Base
50
-    {
51
-        return $this->model;
52
-    }
43
+	/**
44
+	 * Gets the model currently being requested, eg class EEM_Event
45
+	 *
46
+	 * @return EEM_Base
47
+	 * @since 4.9.72.p
48
+	 */
49
+	public function getModel(): EEM_Base
50
+	{
51
+		return $this->model;
52
+	}
53 53
 
54 54
 
55
-    /**
56
-     * Gets the version being requested, eg 4.8.36
57
-     *
58
-     * @return string
59
-     * @since 4.9.72.p
60
-     */
61
-    public function getRequestedVersion(): string
62
-    {
63
-        return $this->requested_version;
64
-    }
55
+	/**
56
+	 * Gets the version being requested, eg 4.8.36
57
+	 *
58
+	 * @return string
59
+	 * @since 4.9.72.p
60
+	 */
61
+	public function getRequestedVersion(): string
62
+	{
63
+		return $this->requested_version;
64
+	}
65 65
 
66 66
 
67
-    /**
68
-     * Gets if the current request is for a writing request or just simple read
69
-     *
70
-     * @return bool
71
-     * @since 4.9.72.p
72
-     */
73
-    public function isWriting(): bool
74
-    {
75
-        return $this->writing;
76
-    }
67
+	/**
68
+	 * Gets if the current request is for a writing request or just simple read
69
+	 *
70
+	 * @return bool
71
+	 * @since 4.9.72.p
72
+	 */
73
+	public function isWriting(): bool
74
+	{
75
+		return $this->writing;
76
+	}
77 77
 }
78 78
 // End of file RestIncomingQueryParamContext.php
79 79
 // Location: EventEspresso\core\libraries\rest_api/RestIncomingQueryParamContext.php
Please login to merge, or discard this patch.
core/libraries/rest_api/CalculatedModelFields.php 2 patches
Indentation   +170 added lines, -170 removed lines patch added patch discarded remove patch
@@ -21,174 +21,174 @@
 block discarded – undo
21 21
  */
22 22
 class CalculatedModelFields
23 23
 {
24
-    private CalculatedModelFieldsFactory $factory;
25
-
26
-    protected array $mapping = [];
27
-
28
-    protected array $mapping_schema = [];
29
-
30
-
31
-    /**
32
-     * CalculatedModelFields constructor.
33
-     *
34
-     * @param CalculatedModelFieldsFactory $factory
35
-     */
36
-    public function __construct(CalculatedModelFieldsFactory $factory)
37
-    {
38
-        $this->factory = $factory;
39
-    }
40
-
41
-
42
-    /**
43
-     * @param bool $refresh
44
-     * @return array top-level-keys are model names (eg "Event")
45
-     * next-level are the calculated field names AND method names on classes
46
-     * which perform calculations, values are the fully qualified classnames which do the calculations
47
-     * These callbacks should accept as arguments:
48
-     * the wpdb row results,
49
-     * the WP_Request object,
50
-     * the controller object
51
-     */
52
-    public function mapping(bool $refresh = false): array
53
-    {
54
-        if (! $this->mapping || $refresh) {
55
-            $this->mapping = $this->generateNewMapping();
56
-        }
57
-        return $this->mapping;
58
-    }
59
-
60
-
61
-    /**
62
-     * Generates a new mapping between model calculated fields and their callbacks
63
-     *
64
-     * @return array
65
-     */
66
-    protected function generateNewMapping(): array
67
-    {
68
-        $mapping                       = [];
69
-        $models_with_calculated_fields = [
70
-            'Attendee',
71
-            'Datetime',
72
-            'Event',
73
-            'Registration',
74
-        ];
75
-        foreach ($models_with_calculated_fields as $model_name) {
76
-            $calculator = $this->factory->createFromModel($model_name);
77
-            foreach (array_keys(call_user_func([$calculator, 'schemaForCalculations'])) as $field_name) {
78
-                $mapping[ $model_name ][ $field_name ] = get_class($calculator);
79
-            }
80
-        }
81
-        return apply_filters(
82
-            'FHEE__EventEspresso\core\libraries\rest_api\Calculated_Model_Fields__mapping',
83
-            $mapping
84
-        );
85
-    }
86
-
87
-
88
-    /**
89
-     * Generates the schema for each calculation index in the calculation map.
90
-     *
91
-     * @return array
92
-     * @throws UnexpectedEntityException
93
-     */
94
-    protected function generateNewMappingSchema(): array
95
-    {
96
-        $schema_map = [];
97
-        foreach ($this->mapping() as $map_model => $map_for_model) {
98
-            /**
99
-             * @var string $calculation_index
100
-             * @var string $calculations_class
101
-             */
102
-            foreach ($map_for_model as $calculation_index => $calculations_class) {
103
-                $calculator = $this->factory->createFromClassname($calculations_class);
104
-                $schema     = call_user_func([$calculator, 'schemaForCalculation'], $calculation_index);
105
-                if (! empty($schema)) {
106
-                    $schema_map[ $map_model ][ $calculation_index ] = $schema;
107
-                }
108
-            }
109
-        }
110
-        return $schema_map;
111
-    }
112
-
113
-
114
-    /**
115
-     * Gets the known calculated fields for model
116
-     *
117
-     * @param EEM_Base $model
118
-     * @return array allowable values for this field
119
-     */
120
-    public function retrieveCalculatedFieldsForModel(EEM_Base $model): array
121
-    {
122
-        $mapping = $this->mapping();
123
-        if (isset($mapping[ $model->get_this_model_name() ])) {
124
-            return array_keys($mapping[ $model->get_this_model_name() ]);
125
-        }
126
-        return [];
127
-    }
128
-
129
-
130
-    /**
131
-     * Returns the JsonSchema for the calculated fields on the given model.
132
-     *
133
-     * @param EEM_Base $model
134
-     * @return array
135
-     */
136
-    public function getJsonSchemaForModel(EEM_Base $model): array
137
-    {
138
-        if (! $this->mapping_schema) {
139
-            $this->mapping_schema = $this->generateNewMappingSchema();
140
-        }
141
-        return [
142
-            'description'          => esc_html__(
143
-                'Available calculated fields for this model.  Fields are only present in the response if explicitly requested',
144
-                'event_espresso'
145
-            ),
146
-            'type'                 => 'object',
147
-            'properties'           => isset($this->mapping_schema[ $model->get_this_model_name() ])
148
-                ? $this->mapping_schema[ $model->get_this_model_name() ]
149
-                : [],
150
-            'additionalProperties' => false,
151
-            'readonly'             => true,
152
-        ];
153
-    }
154
-
155
-
156
-    /**
157
-     * Retrieves the value for this calculation
158
-     *
159
-     * @param EEM_Base       $model
160
-     * @param string         $field_name
161
-     * @param array          $wpdb_row
162
-     * @param                $rest_request
163
-     * @param BaseController $controller
164
-     * @return mixed|null
165
-     * @throws RestException
166
-     * @throws UnexpectedEntityException
167
-     */
168
-    public function retrieveCalculatedFieldValue(
169
-        EEM_Base $model,
170
-        string $field_name,
171
-        array $wpdb_row,
172
-        $rest_request,
173
-        Base $controller
174
-    ) {
175
-        $mapping = $this->mapping();
176
-        if (
177
-            isset($mapping[ $model->get_this_model_name() ])
178
-            && isset($mapping[ $model->get_this_model_name() ][ $field_name ])
179
-        ) {
180
-            $classname         = $mapping[ $model->get_this_model_name() ][ $field_name ];
181
-            $calculator        = $this->factory->createFromClassname($classname);
182
-            $class_method_name = EEH_Inflector::camelize_all_but_first($field_name);
183
-            return call_user_func([$calculator, $class_method_name], $wpdb_row, $rest_request, $controller);
184
-        }
185
-        throw new RestException(
186
-            'calculated_field_does_not_exist',
187
-            sprintf(
188
-                esc_html__('There is no calculated field %1$s on resource %2$s', 'event_espresso'),
189
-                $field_name,
190
-                $model->get_this_model_name()
191
-            )
192
-        );
193
-    }
24
+	private CalculatedModelFieldsFactory $factory;
25
+
26
+	protected array $mapping = [];
27
+
28
+	protected array $mapping_schema = [];
29
+
30
+
31
+	/**
32
+	 * CalculatedModelFields constructor.
33
+	 *
34
+	 * @param CalculatedModelFieldsFactory $factory
35
+	 */
36
+	public function __construct(CalculatedModelFieldsFactory $factory)
37
+	{
38
+		$this->factory = $factory;
39
+	}
40
+
41
+
42
+	/**
43
+	 * @param bool $refresh
44
+	 * @return array top-level-keys are model names (eg "Event")
45
+	 * next-level are the calculated field names AND method names on classes
46
+	 * which perform calculations, values are the fully qualified classnames which do the calculations
47
+	 * These callbacks should accept as arguments:
48
+	 * the wpdb row results,
49
+	 * the WP_Request object,
50
+	 * the controller object
51
+	 */
52
+	public function mapping(bool $refresh = false): array
53
+	{
54
+		if (! $this->mapping || $refresh) {
55
+			$this->mapping = $this->generateNewMapping();
56
+		}
57
+		return $this->mapping;
58
+	}
59
+
60
+
61
+	/**
62
+	 * Generates a new mapping between model calculated fields and their callbacks
63
+	 *
64
+	 * @return array
65
+	 */
66
+	protected function generateNewMapping(): array
67
+	{
68
+		$mapping                       = [];
69
+		$models_with_calculated_fields = [
70
+			'Attendee',
71
+			'Datetime',
72
+			'Event',
73
+			'Registration',
74
+		];
75
+		foreach ($models_with_calculated_fields as $model_name) {
76
+			$calculator = $this->factory->createFromModel($model_name);
77
+			foreach (array_keys(call_user_func([$calculator, 'schemaForCalculations'])) as $field_name) {
78
+				$mapping[ $model_name ][ $field_name ] = get_class($calculator);
79
+			}
80
+		}
81
+		return apply_filters(
82
+			'FHEE__EventEspresso\core\libraries\rest_api\Calculated_Model_Fields__mapping',
83
+			$mapping
84
+		);
85
+	}
86
+
87
+
88
+	/**
89
+	 * Generates the schema for each calculation index in the calculation map.
90
+	 *
91
+	 * @return array
92
+	 * @throws UnexpectedEntityException
93
+	 */
94
+	protected function generateNewMappingSchema(): array
95
+	{
96
+		$schema_map = [];
97
+		foreach ($this->mapping() as $map_model => $map_for_model) {
98
+			/**
99
+			 * @var string $calculation_index
100
+			 * @var string $calculations_class
101
+			 */
102
+			foreach ($map_for_model as $calculation_index => $calculations_class) {
103
+				$calculator = $this->factory->createFromClassname($calculations_class);
104
+				$schema     = call_user_func([$calculator, 'schemaForCalculation'], $calculation_index);
105
+				if (! empty($schema)) {
106
+					$schema_map[ $map_model ][ $calculation_index ] = $schema;
107
+				}
108
+			}
109
+		}
110
+		return $schema_map;
111
+	}
112
+
113
+
114
+	/**
115
+	 * Gets the known calculated fields for model
116
+	 *
117
+	 * @param EEM_Base $model
118
+	 * @return array allowable values for this field
119
+	 */
120
+	public function retrieveCalculatedFieldsForModel(EEM_Base $model): array
121
+	{
122
+		$mapping = $this->mapping();
123
+		if (isset($mapping[ $model->get_this_model_name() ])) {
124
+			return array_keys($mapping[ $model->get_this_model_name() ]);
125
+		}
126
+		return [];
127
+	}
128
+
129
+
130
+	/**
131
+	 * Returns the JsonSchema for the calculated fields on the given model.
132
+	 *
133
+	 * @param EEM_Base $model
134
+	 * @return array
135
+	 */
136
+	public function getJsonSchemaForModel(EEM_Base $model): array
137
+	{
138
+		if (! $this->mapping_schema) {
139
+			$this->mapping_schema = $this->generateNewMappingSchema();
140
+		}
141
+		return [
142
+			'description'          => esc_html__(
143
+				'Available calculated fields for this model.  Fields are only present in the response if explicitly requested',
144
+				'event_espresso'
145
+			),
146
+			'type'                 => 'object',
147
+			'properties'           => isset($this->mapping_schema[ $model->get_this_model_name() ])
148
+				? $this->mapping_schema[ $model->get_this_model_name() ]
149
+				: [],
150
+			'additionalProperties' => false,
151
+			'readonly'             => true,
152
+		];
153
+	}
154
+
155
+
156
+	/**
157
+	 * Retrieves the value for this calculation
158
+	 *
159
+	 * @param EEM_Base       $model
160
+	 * @param string         $field_name
161
+	 * @param array          $wpdb_row
162
+	 * @param                $rest_request
163
+	 * @param BaseController $controller
164
+	 * @return mixed|null
165
+	 * @throws RestException
166
+	 * @throws UnexpectedEntityException
167
+	 */
168
+	public function retrieveCalculatedFieldValue(
169
+		EEM_Base $model,
170
+		string $field_name,
171
+		array $wpdb_row,
172
+		$rest_request,
173
+		Base $controller
174
+	) {
175
+		$mapping = $this->mapping();
176
+		if (
177
+			isset($mapping[ $model->get_this_model_name() ])
178
+			&& isset($mapping[ $model->get_this_model_name() ][ $field_name ])
179
+		) {
180
+			$classname         = $mapping[ $model->get_this_model_name() ][ $field_name ];
181
+			$calculator        = $this->factory->createFromClassname($classname);
182
+			$class_method_name = EEH_Inflector::camelize_all_but_first($field_name);
183
+			return call_user_func([$calculator, $class_method_name], $wpdb_row, $rest_request, $controller);
184
+		}
185
+		throw new RestException(
186
+			'calculated_field_does_not_exist',
187
+			sprintf(
188
+				esc_html__('There is no calculated field %1$s on resource %2$s', 'event_espresso'),
189
+				$field_name,
190
+				$model->get_this_model_name()
191
+			)
192
+		);
193
+	}
194 194
 }
Please login to merge, or discard this patch.
Spacing   +12 added lines, -12 removed lines patch added patch discarded remove patch
@@ -51,7 +51,7 @@  discard block
 block discarded – undo
51 51
      */
52 52
     public function mapping(bool $refresh = false): array
53 53
     {
54
-        if (! $this->mapping || $refresh) {
54
+        if ( ! $this->mapping || $refresh) {
55 55
             $this->mapping = $this->generateNewMapping();
56 56
         }
57 57
         return $this->mapping;
@@ -75,7 +75,7 @@  discard block
 block discarded – undo
75 75
         foreach ($models_with_calculated_fields as $model_name) {
76 76
             $calculator = $this->factory->createFromModel($model_name);
77 77
             foreach (array_keys(call_user_func([$calculator, 'schemaForCalculations'])) as $field_name) {
78
-                $mapping[ $model_name ][ $field_name ] = get_class($calculator);
78
+                $mapping[$model_name][$field_name] = get_class($calculator);
79 79
             }
80 80
         }
81 81
         return apply_filters(
@@ -102,8 +102,8 @@  discard block
 block discarded – undo
102 102
             foreach ($map_for_model as $calculation_index => $calculations_class) {
103 103
                 $calculator = $this->factory->createFromClassname($calculations_class);
104 104
                 $schema     = call_user_func([$calculator, 'schemaForCalculation'], $calculation_index);
105
-                if (! empty($schema)) {
106
-                    $schema_map[ $map_model ][ $calculation_index ] = $schema;
105
+                if ( ! empty($schema)) {
106
+                    $schema_map[$map_model][$calculation_index] = $schema;
107 107
                 }
108 108
             }
109 109
         }
@@ -120,8 +120,8 @@  discard block
 block discarded – undo
120 120
     public function retrieveCalculatedFieldsForModel(EEM_Base $model): array
121 121
     {
122 122
         $mapping = $this->mapping();
123
-        if (isset($mapping[ $model->get_this_model_name() ])) {
124
-            return array_keys($mapping[ $model->get_this_model_name() ]);
123
+        if (isset($mapping[$model->get_this_model_name()])) {
124
+            return array_keys($mapping[$model->get_this_model_name()]);
125 125
         }
126 126
         return [];
127 127
     }
@@ -135,7 +135,7 @@  discard block
 block discarded – undo
135 135
      */
136 136
     public function getJsonSchemaForModel(EEM_Base $model): array
137 137
     {
138
-        if (! $this->mapping_schema) {
138
+        if ( ! $this->mapping_schema) {
139 139
             $this->mapping_schema = $this->generateNewMappingSchema();
140 140
         }
141 141
         return [
@@ -144,8 +144,8 @@  discard block
 block discarded – undo
144 144
                 'event_espresso'
145 145
             ),
146 146
             'type'                 => 'object',
147
-            'properties'           => isset($this->mapping_schema[ $model->get_this_model_name() ])
148
-                ? $this->mapping_schema[ $model->get_this_model_name() ]
147
+            'properties'           => isset($this->mapping_schema[$model->get_this_model_name()])
148
+                ? $this->mapping_schema[$model->get_this_model_name()]
149 149
                 : [],
150 150
             'additionalProperties' => false,
151 151
             'readonly'             => true,
@@ -174,10 +174,10 @@  discard block
 block discarded – undo
174 174
     ) {
175 175
         $mapping = $this->mapping();
176 176
         if (
177
-            isset($mapping[ $model->get_this_model_name() ])
178
-            && isset($mapping[ $model->get_this_model_name() ][ $field_name ])
177
+            isset($mapping[$model->get_this_model_name()])
178
+            && isset($mapping[$model->get_this_model_name()][$field_name])
179 179
         ) {
180
-            $classname         = $mapping[ $model->get_this_model_name() ][ $field_name ];
180
+            $classname         = $mapping[$model->get_this_model_name()][$field_name];
181 181
             $calculator        = $this->factory->createFromClassname($classname);
182 182
             $class_method_name = EEH_Inflector::camelize_all_but_first($field_name);
183 183
             return call_user_func([$calculator, $class_method_name], $wpdb_row, $rest_request, $controller);
Please login to merge, or discard this patch.
strategies/validation/EE_Required_Validation_Strategy.strategy.php 1 patch
Indentation   +41 added lines, -41 removed lines patch added patch discarded remove patch
@@ -11,51 +11,51 @@
 block discarded – undo
11 11
  */
12 12
 class EE_Required_Validation_Strategy extends EE_Validation_Strategy_Base
13 13
 {
14
-    /**
15
-     * @param string $validation_error_message
16
-     */
17
-    public function __construct($validation_error_message = null)
18
-    {
19
-        if (! $validation_error_message) {
20
-            $validation_error_message = esc_html__("This field is required.", "event_espresso");
21
-        }
22
-        parent::__construct($validation_error_message);
23
-    }
14
+	/**
15
+	 * @param string $validation_error_message
16
+	 */
17
+	public function __construct($validation_error_message = null)
18
+	{
19
+		if (! $validation_error_message) {
20
+			$validation_error_message = esc_html__("This field is required.", "event_espresso");
21
+		}
22
+		parent::__construct($validation_error_message);
23
+	}
24 24
 
25 25
 
26 26
 
27
-    /**
28
-     * just checks the field isn't blank, provided the requirement conditions
29
-     * indicate this input is still required
30
-     *
31
-     * @param $normalized_value
32
-     * @return bool
33
-     * @throws EE_Validation_Error
34
-     */
35
-    public function validate($normalized_value)
36
-    {
37
-        if (
38
-            $normalized_value === ''
39
-            || $normalized_value === null
40
-            || $normalized_value === array()
41
-        ) {
42
-            throw new EE_Validation_Error($this->get_validation_error_message(), 'required');
43
-        }
44
-        return true;
45
-    }
27
+	/**
28
+	 * just checks the field isn't blank, provided the requirement conditions
29
+	 * indicate this input is still required
30
+	 *
31
+	 * @param $normalized_value
32
+	 * @return bool
33
+	 * @throws EE_Validation_Error
34
+	 */
35
+	public function validate($normalized_value)
36
+	{
37
+		if (
38
+			$normalized_value === ''
39
+			|| $normalized_value === null
40
+			|| $normalized_value === array()
41
+		) {
42
+			throw new EE_Validation_Error($this->get_validation_error_message(), 'required');
43
+		}
44
+		return true;
45
+	}
46 46
 
47 47
 
48 48
 
49
-    /**
50
-     * @return array
51
-     */
52
-    public function get_jquery_validation_rule_array()
53
-    {
54
-        return array(
55
-            'required' => true,
56
-            'messages' => array(
57
-                'required' => $this->get_validation_error_message()
58
-            )
59
-        );
60
-    }
49
+	/**
50
+	 * @return array
51
+	 */
52
+	public function get_jquery_validation_rule_array()
53
+	{
54
+		return array(
55
+			'required' => true,
56
+			'messages' => array(
57
+				'required' => $this->get_validation_error_message()
58
+			)
59
+		);
60
+	}
61 61
 }
Please login to merge, or discard this patch.
strategies/validation/EE_Equal_To_Validation_Strategy.strategy.php 2 patches
Indentation   +40 added lines, -40 removed lines patch added patch discarded remove patch
@@ -15,44 +15,44 @@
 block discarded – undo
15 15
  */
16 16
 class EE_Equal_To_Validation_Strategy extends EE_Text_Validation_Strategy
17 17
 {
18
-    protected string $_compare_to = '';
19
-
20
-
21
-    /**
22
-     * @param string $validation_error_message
23
-     * @param string $compare_to
24
-     */
25
-    public function __construct($validation_error_message = '', $compare_to = '')
26
-    {
27
-        if (! $validation_error_message) {
28
-            $validation_error_message = apply_filters(
29
-                'FHEE__EE_Equal_To_Validation_Strategy____construct__validation_error_message',
30
-                esc_html__('Fields do not match.', 'event_espresso')
31
-            );
32
-        }
33
-        parent::__construct($validation_error_message);
34
-        $this->_compare_to = $compare_to;
35
-    }
36
-
37
-
38
-    /**
39
-     * just checks the field isn't blank
40
-     *
41
-     * @param $normalized_value
42
-     * @return bool
43
-     */
44
-    public function validate($normalized_value)
45
-    {
46
-        // No need to be validated
47
-        return true;
48
-    }
49
-
50
-
51
-    /**
52
-     * @return array
53
-     */
54
-    public function get_jquery_validation_rule_array()
55
-    {
56
-        return ['equalTo' => $this->_compare_to, 'messages' => ['equalTo' => $this->get_validation_error_message()]];
57
-    }
18
+	protected string $_compare_to = '';
19
+
20
+
21
+	/**
22
+	 * @param string $validation_error_message
23
+	 * @param string $compare_to
24
+	 */
25
+	public function __construct($validation_error_message = '', $compare_to = '')
26
+	{
27
+		if (! $validation_error_message) {
28
+			$validation_error_message = apply_filters(
29
+				'FHEE__EE_Equal_To_Validation_Strategy____construct__validation_error_message',
30
+				esc_html__('Fields do not match.', 'event_espresso')
31
+			);
32
+		}
33
+		parent::__construct($validation_error_message);
34
+		$this->_compare_to = $compare_to;
35
+	}
36
+
37
+
38
+	/**
39
+	 * just checks the field isn't blank
40
+	 *
41
+	 * @param $normalized_value
42
+	 * @return bool
43
+	 */
44
+	public function validate($normalized_value)
45
+	{
46
+		// No need to be validated
47
+		return true;
48
+	}
49
+
50
+
51
+	/**
52
+	 * @return array
53
+	 */
54
+	public function get_jquery_validation_rule_array()
55
+	{
56
+		return ['equalTo' => $this->_compare_to, 'messages' => ['equalTo' => $this->get_validation_error_message()]];
57
+	}
58 58
 }
Please login to merge, or discard this patch.
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -24,7 +24,7 @@
 block discarded – undo
24 24
      */
25 25
     public function __construct($validation_error_message = '', $compare_to = '')
26 26
     {
27
-        if (! $validation_error_message) {
27
+        if ( ! $validation_error_message) {
28 28
             $validation_error_message = apply_filters(
29 29
                 'FHEE__EE_Equal_To_Validation_Strategy____construct__validation_error_message',
30 30
                 esc_html__('Fields do not match.', 'event_espresso')
Please login to merge, or discard this patch.
core/libraries/form_sections/base/EE_Form_Section_Validatable.form.php 1 patch
Indentation   +153 added lines, -153 removed lines patch added patch discarded remove patch
@@ -19,157 +19,157 @@
 block discarded – undo
19 19
  */
20 20
 abstract class EE_Form_Section_Validatable extends EE_Form_Section_Base
21 21
 {
22
-    /**
23
-     * Array of validation errors in this section. Does not contain validation errors in subsections, however.
24
-     * Those are stored individually on each subsection.
25
-     *
26
-     * @var EE_Validation_Error[]
27
-     */
28
-    protected $_validation_errors = array();
29
-
30
-
31
-
32
-    /**
33
-     * Errors on this form section. Note: EE_Form_Section_Proper
34
-     * has another function for getting all errors in this form section and subsections
35
-     * called get_validation_errors_accumulated
36
-     *
37
-     * @return EE_Validation_Error[]
38
-     */
39
-    public function get_validation_errors()
40
-    {
41
-        return $this->_validation_errors;
42
-    }
43
-
44
-
45
-
46
-    /**
47
-     * returns a comma-separated list of all the validation errors in it.
48
-     * If we want this to be customizable, we may decide to create a strategy for displaying it
49
-     *
50
-     * @return string
51
-     */
52
-    public function get_validation_error_string()
53
-    {
54
-        $validation_error_messages = array();
55
-        if ($this->get_validation_errors()) {
56
-            foreach ($this->get_validation_errors() as $validation_error) {
57
-                if ($validation_error instanceof EE_Validation_Error) {
58
-                    $validation_error_messages[] = $validation_error->getMessage();
59
-                }
60
-            }
61
-        }
62
-        return implode(", ", $validation_error_messages);
63
-    }
64
-
65
-
66
-
67
-    /**
68
-     * Performs validation on this form section (and subsections). Should be called after _normalize()
69
-     *
70
-     * @return boolean of whether or not the form section is valid
71
-     */
72
-    abstract protected function _validate();
73
-
74
-
75
-
76
-    /**
77
-     * Checks if this field has any validation errors
78
-     *
79
-     * @return boolean
80
-     */
81
-    public function is_valid()
82
-    {
83
-        if (count($this->_validation_errors)) {
84
-            return false;
85
-        } else {
86
-            return true;
87
-        }
88
-    }
89
-
90
-
91
-
92
-    /**
93
-     * Sanitizes input for this form section
94
-     *
95
-     * @param array $req_data is the full request data
96
-     * @return boolean of whether a normalization error occurred
97
-     * @throws EE_Validation_Error
98
-     */
99
-    abstract protected function _normalize($req_data);
100
-
101
-
102
-
103
-    /**
104
-     * Creates a validation error from the arguments provided, and adds it to the form section's list.
105
-     * If such an EE_Validation_Error object is passed in as the first arg, simply sets this as its form section, and
106
-     * adds it to the list of validation errors of errors
107
-     *
108
-     * @param mixed     $message_or_object  internationalized string describing the validation error; or it could be a
109
-     *                                      proper EE_Validation_Error object
110
-     * @param string    $error_code         a short key which can be used to uniquely identify the error
111
-     * @param Exception $previous_exception if there was an exception that caused the error, that exception
112
-     * @return void
113
-     */
114
-    public function add_validation_error($message_or_object, $error_code = null, $previous_exception = null)
115
-    {
116
-        if ($message_or_object instanceof EE_Validation_Error) {
117
-            $validation_error = $message_or_object;
118
-            $validation_error->set_form_section($this);
119
-        } else {
120
-            $validation_error = new EE_Validation_Error($message_or_object, $error_code, $this, $previous_exception);
121
-        }
122
-        // check if this error is already in the list
123
-        foreach ($this->_validation_errors as $existing_error) {
124
-            if ($existing_error->getMessage() === $validation_error->getMessage()) {
125
-                return;
126
-            }
127
-        }
128
-        $this->_validation_errors[] = $validation_error;
129
-    }
130
-
131
-
132
-
133
-    /**
134
-     * When generating the JS for the jquery validation rules like<br>
135
-     * <code>$( "#myform" ).validate({
136
-     * rules: {
137
-     * password: "required",
138
-     * password_again: {
139
-     * equalTo: "#password"
140
-     * }
141
-     * }
142
-     * });</code>
143
-     * gets the sections like
144
-     * <br><code>password: "required",
145
-     * password_again: {
146
-     * equalTo: "#password"
147
-     * }</code>
148
-     * except we leave it as a PHP object, and leave wp_localize_script to
149
-     * turn it into a JSON object which can be used by the js
150
-     *
151
-     * @return array
152
-     */
153
-    abstract public function get_jquery_validation_rules();
154
-
155
-
156
-
157
-    /**
158
-     * Checks if this form section's data is present in the req data specified
159
-     *
160
-     * @param array $req_data usually post data, if null that's what's used
161
-     * @return boolean
162
-     */
163
-    abstract public function form_data_present_in($req_data = null);
164
-
165
-
166
-
167
-    /**
168
-     * Removes teh sensitive data from this form section (usually done after
169
-     * utilizing the data business function, but before saving it somewhere. Eg,
170
-     * may remove a password from the form after verifying it was correct)
171
-     *
172
-     * @return void
173
-     */
174
-    abstract public function clean_sensitive_data();
22
+	/**
23
+	 * Array of validation errors in this section. Does not contain validation errors in subsections, however.
24
+	 * Those are stored individually on each subsection.
25
+	 *
26
+	 * @var EE_Validation_Error[]
27
+	 */
28
+	protected $_validation_errors = array();
29
+
30
+
31
+
32
+	/**
33
+	 * Errors on this form section. Note: EE_Form_Section_Proper
34
+	 * has another function for getting all errors in this form section and subsections
35
+	 * called get_validation_errors_accumulated
36
+	 *
37
+	 * @return EE_Validation_Error[]
38
+	 */
39
+	public function get_validation_errors()
40
+	{
41
+		return $this->_validation_errors;
42
+	}
43
+
44
+
45
+
46
+	/**
47
+	 * returns a comma-separated list of all the validation errors in it.
48
+	 * If we want this to be customizable, we may decide to create a strategy for displaying it
49
+	 *
50
+	 * @return string
51
+	 */
52
+	public function get_validation_error_string()
53
+	{
54
+		$validation_error_messages = array();
55
+		if ($this->get_validation_errors()) {
56
+			foreach ($this->get_validation_errors() as $validation_error) {
57
+				if ($validation_error instanceof EE_Validation_Error) {
58
+					$validation_error_messages[] = $validation_error->getMessage();
59
+				}
60
+			}
61
+		}
62
+		return implode(", ", $validation_error_messages);
63
+	}
64
+
65
+
66
+
67
+	/**
68
+	 * Performs validation on this form section (and subsections). Should be called after _normalize()
69
+	 *
70
+	 * @return boolean of whether or not the form section is valid
71
+	 */
72
+	abstract protected function _validate();
73
+
74
+
75
+
76
+	/**
77
+	 * Checks if this field has any validation errors
78
+	 *
79
+	 * @return boolean
80
+	 */
81
+	public function is_valid()
82
+	{
83
+		if (count($this->_validation_errors)) {
84
+			return false;
85
+		} else {
86
+			return true;
87
+		}
88
+	}
89
+
90
+
91
+
92
+	/**
93
+	 * Sanitizes input for this form section
94
+	 *
95
+	 * @param array $req_data is the full request data
96
+	 * @return boolean of whether a normalization error occurred
97
+	 * @throws EE_Validation_Error
98
+	 */
99
+	abstract protected function _normalize($req_data);
100
+
101
+
102
+
103
+	/**
104
+	 * Creates a validation error from the arguments provided, and adds it to the form section's list.
105
+	 * If such an EE_Validation_Error object is passed in as the first arg, simply sets this as its form section, and
106
+	 * adds it to the list of validation errors of errors
107
+	 *
108
+	 * @param mixed     $message_or_object  internationalized string describing the validation error; or it could be a
109
+	 *                                      proper EE_Validation_Error object
110
+	 * @param string    $error_code         a short key which can be used to uniquely identify the error
111
+	 * @param Exception $previous_exception if there was an exception that caused the error, that exception
112
+	 * @return void
113
+	 */
114
+	public function add_validation_error($message_or_object, $error_code = null, $previous_exception = null)
115
+	{
116
+		if ($message_or_object instanceof EE_Validation_Error) {
117
+			$validation_error = $message_or_object;
118
+			$validation_error->set_form_section($this);
119
+		} else {
120
+			$validation_error = new EE_Validation_Error($message_or_object, $error_code, $this, $previous_exception);
121
+		}
122
+		// check if this error is already in the list
123
+		foreach ($this->_validation_errors as $existing_error) {
124
+			if ($existing_error->getMessage() === $validation_error->getMessage()) {
125
+				return;
126
+			}
127
+		}
128
+		$this->_validation_errors[] = $validation_error;
129
+	}
130
+
131
+
132
+
133
+	/**
134
+	 * When generating the JS for the jquery validation rules like<br>
135
+	 * <code>$( "#myform" ).validate({
136
+	 * rules: {
137
+	 * password: "required",
138
+	 * password_again: {
139
+	 * equalTo: "#password"
140
+	 * }
141
+	 * }
142
+	 * });</code>
143
+	 * gets the sections like
144
+	 * <br><code>password: "required",
145
+	 * password_again: {
146
+	 * equalTo: "#password"
147
+	 * }</code>
148
+	 * except we leave it as a PHP object, and leave wp_localize_script to
149
+	 * turn it into a JSON object which can be used by the js
150
+	 *
151
+	 * @return array
152
+	 */
153
+	abstract public function get_jquery_validation_rules();
154
+
155
+
156
+
157
+	/**
158
+	 * Checks if this form section's data is present in the req data specified
159
+	 *
160
+	 * @param array $req_data usually post data, if null that's what's used
161
+	 * @return boolean
162
+	 */
163
+	abstract public function form_data_present_in($req_data = null);
164
+
165
+
166
+
167
+	/**
168
+	 * Removes teh sensitive data from this form section (usually done after
169
+	 * utilizing the data business function, but before saving it somewhere. Eg,
170
+	 * may remove a password from the form after verifying it was correct)
171
+	 *
172
+	 * @return void
173
+	 */
174
+	abstract public function clean_sensitive_data();
175 175
 }
Please login to merge, or discard this patch.