Completed
Branch BUG/reg-status-change-recursio... (66d1a3)
by
unknown
16:58 queued 08:55
created
core/libraries/rest_api/ModelDataTranslator.php 3 patches
Unused Use Statements   -2 removed lines patch added patch discarded remove patch
@@ -3,11 +3,9 @@
 block discarded – undo
3 3
 namespace EventEspresso\core\libraries\rest_api;
4 4
 
5 5
 use DomainException;
6
-use EE_Capabilities;
7 6
 use EE_Datetime_Field;
8 7
 use EE_Error;
9 8
 use EE_Infinite_Integer_Field;
10
-use EE_Maybe_Serialized_Simple_HTML_Field;
11 9
 use EE_Model_Field_Base;
12 10
 use EE_Serialized_Text_Field;
13 11
 use EED_Core_Rest_Api;
Please login to merge, or discard this patch.
Indentation   +639 added lines, -639 removed lines patch added patch discarded remove patch
@@ -36,643 +36,643 @@
 block discarded – undo
36 36
 class ModelDataTranslator
37 37
 {
38 38
 
39
-    /**
40
-     * We used to use -1 for infinity in the rest api, but that's ambiguous for
41
-     * fields that COULD contain -1; so we use null
42
-     */
43
-    const EE_INF_IN_REST = null;
44
-
45
-
46
-    /**
47
-     * Prepares a possible array of input values from JSON for use by the models
48
-     *
49
-     * @param EE_Model_Field_Base $field_obj
50
-     * @param mixed               $original_value_maybe_array
51
-     * @param string              $requested_version
52
-     * @param string              $timezone_string treat values as being in this timezone
53
-     * @return mixed
54
-     * @throws RestException
55
-     */
56
-    public static function prepareFieldValuesFromJson(
57
-        $field_obj,
58
-        $original_value_maybe_array,
59
-        $requested_version,
60
-        $timezone_string = 'UTC'
61
-    ) {
62
-        if (is_array($original_value_maybe_array)
63
-            && ! $field_obj instanceof EE_Serialized_Text_Field
64
-        ) {
65
-            $new_value_maybe_array = array();
66
-            foreach ($original_value_maybe_array as $array_key => $array_item) {
67
-                $new_value_maybe_array[ $array_key ] = ModelDataTranslator::prepareFieldValueFromJson(
68
-                    $field_obj,
69
-                    $array_item,
70
-                    $requested_version,
71
-                    $timezone_string
72
-                );
73
-            }
74
-        } else {
75
-            $new_value_maybe_array = ModelDataTranslator::prepareFieldValueFromJson(
76
-                $field_obj,
77
-                $original_value_maybe_array,
78
-                $requested_version,
79
-                $timezone_string
80
-            );
81
-        }
82
-        return $new_value_maybe_array;
83
-    }
84
-
85
-
86
-    /**
87
-     * Prepares an array of field values FOR use in JSON/REST API
88
-     *
89
-     * @param EE_Model_Field_Base $field_obj
90
-     * @param mixed               $original_value_maybe_array
91
-     * @param string              $request_version (eg 4.8.36)
92
-     * @return array
93
-     */
94
-    public static function prepareFieldValuesForJson($field_obj, $original_value_maybe_array, $request_version)
95
-    {
96
-        if (is_array($original_value_maybe_array)) {
97
-            $new_value = array();
98
-            foreach ($original_value_maybe_array as $key => $value) {
99
-                $new_value[ $key ] = ModelDataTranslator::prepareFieldValuesForJson(
100
-                    $field_obj,
101
-                    $value,
102
-                    $request_version
103
-                );
104
-            }
105
-        } else {
106
-            $new_value = ModelDataTranslator::prepareFieldValueForJson(
107
-                $field_obj,
108
-                $original_value_maybe_array,
109
-                $request_version
110
-            );
111
-        }
112
-        return $new_value;
113
-    }
114
-
115
-
116
-    /**
117
-     * Prepares incoming data from the json or $_REQUEST parameters for the models'
118
-     * "$query_params".
119
-     *
120
-     * @param EE_Model_Field_Base $field_obj
121
-     * @param mixed               $original_value
122
-     * @param string              $requested_version
123
-     * @param string              $timezone_string treat values as being in this timezone
124
-     * @return mixed
125
-     * @throws RestException
126
-     * @throws DomainException
127
-     * @throws EE_Error
128
-     */
129
-    public static function prepareFieldValueFromJson(
130
-        $field_obj,
131
-        $original_value,
132
-        $requested_version,
133
-        $timezone_string = 'UTC' // UTC
134
-    ) {
135
-        // check if they accidentally submitted an error value. If so throw an exception
136
-        if (is_array($original_value)
137
-            && isset($original_value['error_code'], $original_value['error_message'])) {
138
-            throw new RestException(
139
-                'rest_submitted_error_value',
140
-                sprintf(
141
-                    esc_html__(
142
-                        'You tried to submit a JSON error object as a value for %1$s. That\'s not allowed.',
143
-                        'event_espresso'
144
-                    ),
145
-                    $field_obj->get_name()
146
-                ),
147
-                array(
148
-                    'status' => 400,
149
-                )
150
-            );
151
-        }
152
-        // double-check for serialized PHP. We never accept serialized PHP. No way Jose.
153
-        ModelDataTranslator::throwExceptionIfContainsSerializedData($original_value);
154
-        $timezone_string = $timezone_string !== '' ? $timezone_string : get_option('timezone_string', '');
155
-        $new_value = null;
156
-        // walk through the submitted data and double-check for serialized PHP. We never accept serialized PHP. No
157
-        // way Jose.
158
-        ModelDataTranslator::throwExceptionIfContainsSerializedData($original_value);
159
-        if ($field_obj instanceof EE_Infinite_Integer_Field
160
-            && in_array($original_value, array(null, ''), true)
161
-        ) {
162
-            $new_value = EE_INF;
163
-        } elseif ($field_obj instanceof EE_Datetime_Field) {
164
-            $new_value = rest_parse_date(
165
-                self::getTimestampWithTimezoneOffset($original_value, $field_obj, $timezone_string)
166
-            );
167
-            if ($new_value === false) {
168
-                throw new RestException(
169
-                    'invalid_format_for_timestamp',
170
-                    sprintf(
171
-                        esc_html__(
172
-                            'Timestamps received on a request as the value for Date and Time fields must be in %1$s/%2$s format.  The timestamp provided (%3$s) is not that format.',
173
-                            'event_espresso'
174
-                        ),
175
-                        'RFC3339',
176
-                        'ISO8601',
177
-                        $original_value
178
-                    ),
179
-                    array(
180
-                        'status' => 400,
181
-                    )
182
-                );
183
-            }
184
-        } else {
185
-            $new_value = $original_value;
186
-        }
187
-        return $new_value;
188
-    }
189
-
190
-
191
-    /**
192
-     * This checks if the incoming timestamp has timezone information already on it and if it doesn't then adds timezone
193
-     * information via details obtained from the host site.
194
-     *
195
-     * @param string            $original_timestamp
196
-     * @param EE_Datetime_Field $datetime_field
197
-     * @param                   $timezone_string
198
-     * @return string
199
-     * @throws DomainException
200
-     */
201
-    private static function getTimestampWithTimezoneOffset(
202
-        $original_timestamp,
203
-        EE_Datetime_Field $datetime_field,
204
-        $timezone_string
205
-    ) {
206
-        // already have timezone information?
207
-        if (preg_match('/Z|(\+|\-)(\d{2}:\d{2})/', $original_timestamp)) {
208
-            // yes, we're ignoring the timezone.
209
-            return $original_timestamp;
210
-        }
211
-        // need to append timezone
212
-        list($offset_sign, $offset_secs) = self::parseTimezoneOffset(
213
-            $datetime_field->get_timezone_offset(
214
-                new \DateTimeZone($timezone_string),
215
-                $original_timestamp
216
-            )
217
-        );
218
-        $offset_string =
219
-            str_pad(
220
-                floor($offset_secs / HOUR_IN_SECONDS),
221
-                2,
222
-                '0',
223
-                STR_PAD_LEFT
224
-            )
225
-            . ':'
226
-            . str_pad(
227
-                ($offset_secs % HOUR_IN_SECONDS) / MINUTE_IN_SECONDS,
228
-                2,
229
-                '0',
230
-                STR_PAD_LEFT
231
-            );
232
-        return $original_timestamp . $offset_sign . $offset_string;
233
-    }
234
-
235
-
236
-    /**
237
-     * Throws an exception if $data is a serialized PHP string (or somehow an actually PHP object, although I don't
238
-     * think that can happen). If $data is an array, recurses into its keys and values
239
-     *
240
-     * @param mixed $data
241
-     * @throws RestException
242
-     * @return void
243
-     */
244
-    public static function throwExceptionIfContainsSerializedData($data)
245
-    {
246
-        if (is_array($data)) {
247
-            foreach ($data as $key => $value) {
248
-                ModelDataTranslator::throwExceptionIfContainsSerializedData($key);
249
-                ModelDataTranslator::throwExceptionIfContainsSerializedData($value);
250
-            }
251
-        } else {
252
-            if (is_serialized($data) || is_object($data)) {
253
-                throw new RestException(
254
-                    'serialized_data_submission_prohibited',
255
-                    esc_html__(
256
-                    // @codingStandardsIgnoreStart
257
-                        'You tried to submit a string of serialized text. Serialized PHP is prohibited over the EE4 REST API.',
258
-                        // @codingStandardsIgnoreEnd
259
-                        'event_espresso'
260
-                    )
261
-                );
262
-            }
263
-        }
264
-    }
265
-
266
-
267
-    /**
268
-     * determines what's going on with them timezone strings
269
-     *
270
-     * @param int $timezone_offset
271
-     * @return array
272
-     */
273
-    private static function parseTimezoneOffset($timezone_offset)
274
-    {
275
-        $first_char = substr((string) $timezone_offset, 0, 1);
276
-        if ($first_char === '+' || $first_char === '-') {
277
-            $offset_sign = $first_char;
278
-            $offset_secs = substr((string) $timezone_offset, 1);
279
-        } else {
280
-            $offset_sign = '+';
281
-            $offset_secs = $timezone_offset;
282
-        }
283
-        return array($offset_sign, $offset_secs);
284
-    }
285
-
286
-
287
-    /**
288
-     * Prepares a field's value for display in the API
289
-     *
290
-     * @param EE_Model_Field_Base $field_obj
291
-     * @param mixed               $original_value
292
-     * @param string              $requested_version
293
-     * @return mixed
294
-     */
295
-    public static function prepareFieldValueForJson($field_obj, $original_value, $requested_version)
296
-    {
297
-        if ($original_value === EE_INF) {
298
-            $new_value = ModelDataTranslator::EE_INF_IN_REST;
299
-        } elseif ($field_obj instanceof EE_Datetime_Field) {
300
-            if (is_string($original_value)) {
301
-                // did they submit a string of a unix timestamp?
302
-                if (is_numeric($original_value)) {
303
-                    $datetime_obj = new \DateTime();
304
-                    $datetime_obj->setTimestamp((int) $original_value);
305
-                } else {
306
-                    // first, check if its a MySQL timestamp in GMT
307
-                    $datetime_obj = \DateTime::createFromFormat('Y-m-d H:i:s', $original_value);
308
-                }
309
-                if (! $datetime_obj instanceof \DateTime) {
310
-                    // so it's not a unix timestamp or a MySQL timestamp. Maybe its in the field's date/time format?
311
-                    $datetime_obj = $field_obj->prepare_for_set($original_value);
312
-                }
313
-                $original_value = $datetime_obj;
314
-            }
315
-            if ($original_value instanceof \DateTime) {
316
-                $new_value = $original_value->format('Y-m-d H:i:s');
317
-            } elseif (is_int($original_value) || is_float($original_value)) {
318
-                $new_value = date('Y-m-d H:i:s', $original_value);
319
-            } elseif ($original_value === null || $original_value === '') {
320
-                $new_value = null;
321
-            } else {
322
-                // so it's not a datetime object, unix timestamp (as string or int),
323
-                // MySQL timestamp, or even a string in the field object's format. So no idea what it is
324
-                throw new \EE_Error(
325
-                    sprintf(
326
-                        esc_html__(
327
-                        // @codingStandardsIgnoreStart
328
-                            'The value "%1$s" for the field "%2$s" on model "%3$s" could not be understood. It should be a PHP DateTime, unix timestamp, MySQL date, or string in the format "%4$s".',
329
-                            // @codingStandardsIgnoreEnd
330
-                            'event_espresso'
331
-                        ),
332
-                        $original_value,
333
-                        $field_obj->get_name(),
334
-                        $field_obj->get_model_name(),
335
-                        $field_obj->get_time_format() . ' ' . $field_obj->get_time_format()
336
-                    )
337
-                );
338
-            }
339
-            if ($new_value !== null) {
340
-                $new_value = mysql_to_rfc3339($new_value);
341
-            }
342
-        } else {
343
-            $new_value = $original_value;
344
-        }
345
-        // are we about to send an object? just don't. We have no good way to represent it in JSON.
346
-        // can't just check using is_object() because that missed PHP incomplete objects
347
-        if (! ModelDataTranslator::isRepresentableInJson($new_value)) {
348
-            $new_value = array(
349
-                'error_code'    => 'php_object_not_return',
350
-                'error_message' => esc_html__(
351
-                    'The value of this field in the database is a PHP object, which can\'t be represented in JSON.',
352
-                    'event_espresso'
353
-                ),
354
-            );
355
-        }
356
-        return apply_filters(
357
-            'FHEE__EventEspresso\core\libraries\rest_api\Model_Data_Translator__prepare_field_for_rest_api',
358
-            $new_value,
359
-            $field_obj,
360
-            $original_value,
361
-            $requested_version
362
-        );
363
-    }
364
-
365
-
366
-    /**
367
-     * Prepares condition-query-parameters (like what's in where and having) from
368
-     * the format expected in the API to use in the models
369
-     *
370
-     * @param array $inputted_query_params_of_this_type
371
-     * @param EEM_Base $model
372
-     * @param string $requested_version
373
-     * @param boolean $writing whether this data will be written to the DB, or if we're just building a query.
374
-     *                          If we're writing to the DB, we don't expect any operators, or any logic query
375
-     *                          parameters, and we also won't accept serialized data unless the current user has
376
-     *                          unfiltered_html.
377
-     * @return array
378
-     * @throws DomainException
379
-     * @throws EE_Error
380
-     * @throws RestException
381
-     * @throws InvalidDataTypeException
382
-     * @throws InvalidInterfaceException
383
-     * @throws InvalidArgumentException
384
-     */
385
-    public static function prepareConditionsQueryParamsForModels(
386
-        $inputted_query_params_of_this_type,
387
-        EEM_Base $model,
388
-        $requested_version,
389
-        $writing = false
390
-    ) {
391
-        $query_param_for_models = array();
392
-        $context = new RestIncomingQueryParamContext($model, $requested_version, $writing);
393
-        foreach ($inputted_query_params_of_this_type as $query_param_key => $query_param_value) {
394
-            $query_param_meta = new RestIncomingQueryParamMetadata($query_param_key, $query_param_value, $context);
395
-            if ($query_param_meta->getField() instanceof EE_Model_Field_Base) {
396
-                $translated_value = $query_param_meta->determineConditionsQueryParameterValue();
397
-                if ((isset($query_param_for_models[ $query_param_meta->getQueryParamKey() ]) && $query_param_meta->isGmtField())
398
-                    || $translated_value === null
399
-                ) {
400
-                    // they have already provided a non-gmt field, ignore the gmt one. That's what WP core
401
-                    // currently does (they might change it though). See https://core.trac.wordpress.org/ticket/39954
402
-                    // OR we couldn't create a translated value from their input
403
-                    continue;
404
-                }
405
-                $query_param_for_models[ $query_param_meta->getQueryParamKey() ] = $translated_value;
406
-            } else {
407
-                $nested_query_params = $query_param_meta->determineNestedConditionQueryParameters();
408
-                if ($nested_query_params) {
409
-                    $query_param_for_models[ $query_param_meta->getQueryParamKey() ] = $nested_query_params;
410
-                }
411
-            }
412
-        }
413
-        return $query_param_for_models;
414
-    }
415
-
416
-    /**
417
-     * Mostly checks if the last 4 characters are "_gmt", indicating its a
418
-     * gmt date field name
419
-     *
420
-     * @param string $field_name
421
-     * @return boolean
422
-     */
423
-    public static function isGmtDateFieldName($field_name)
424
-    {
425
-        return substr(
426
-            ModelDataTranslator::removeStarsAndAnythingAfterFromConditionQueryParamKey($field_name),
427
-            -4,
428
-            4
429
-        ) === '_gmt';
430
-    }
431
-
432
-
433
-    /**
434
-     * Removes the last "_gmt" part of a field name (and if there is no "_gmt" at the end, leave it alone)
435
-     *
436
-     * @param string $field_name
437
-     * @return string
438
-     */
439
-    public static function removeGmtFromFieldName($field_name)
440
-    {
441
-        if (! ModelDataTranslator::isGmtDateFieldName($field_name)) {
442
-            return $field_name;
443
-        }
444
-        $query_param_sans_stars = ModelDataTranslator::removeStarsAndAnythingAfterFromConditionQueryParamKey(
445
-            $field_name
446
-        );
447
-        $query_param_sans_gmt_and_sans_stars = substr(
448
-            $query_param_sans_stars,
449
-            0,
450
-            strrpos(
451
-                $field_name,
452
-                '_gmt'
453
-            )
454
-        );
455
-        return str_replace($query_param_sans_stars, $query_param_sans_gmt_and_sans_stars, $field_name);
456
-    }
457
-
458
-
459
-    /**
460
-     * Takes a field name from the REST API and prepares it for the model querying
461
-     *
462
-     * @param string $field_name
463
-     * @return string
464
-     */
465
-    public static function prepareFieldNameFromJson($field_name)
466
-    {
467
-        if (ModelDataTranslator::isGmtDateFieldName($field_name)) {
468
-            return ModelDataTranslator::removeGmtFromFieldName($field_name);
469
-        }
470
-        return $field_name;
471
-    }
472
-
473
-
474
-    /**
475
-     * Takes array of field names from REST API and prepares for models
476
-     *
477
-     * @param array $field_names
478
-     * @return array of field names (possibly include model prefixes)
479
-     */
480
-    public static function prepareFieldNamesFromJson(array $field_names)
481
-    {
482
-        $new_array = array();
483
-        foreach ($field_names as $key => $field_name) {
484
-            $new_array[ $key ] = ModelDataTranslator::prepareFieldNameFromJson($field_name);
485
-        }
486
-        return $new_array;
487
-    }
488
-
489
-
490
-    /**
491
-     * Takes array where array keys are field names (possibly with model path prefixes)
492
-     * from the REST API and prepares them for model querying
493
-     *
494
-     * @param array $field_names_as_keys
495
-     * @return array
496
-     */
497
-    public static function prepareFieldNamesInArrayKeysFromJson(array $field_names_as_keys)
498
-    {
499
-        $new_array = array();
500
-        foreach ($field_names_as_keys as $field_name => $value) {
501
-            $new_array[ ModelDataTranslator::prepareFieldNameFromJson($field_name) ] = $value;
502
-        }
503
-        return $new_array;
504
-    }
505
-
506
-
507
-    /**
508
-     * Prepares an array of model query params for use in the REST API
509
-     *
510
-     * @param array    $model_query_params
511
-     * @param EEM_Base $model
512
-     * @param string   $requested_version  eg "4.8.36". If null is provided, defaults to the latest release of the EE4
513
-     *                                     REST API
514
-     * @return array which can be passed into the EE4 REST API when querying a model resource
515
-     * @throws EE_Error
516
-     */
517
-    public static function prepareQueryParamsForRestApi(
518
-        array $model_query_params,
519
-        EEM_Base $model,
520
-        $requested_version = null
521
-    ) {
522
-        if ($requested_version === null) {
523
-            $requested_version = EED_Core_Rest_Api::latest_rest_api_version();
524
-        }
525
-        $rest_query_params = $model_query_params;
526
-        if (isset($model_query_params[0])) {
527
-            $rest_query_params['where'] = ModelDataTranslator::prepareConditionsQueryParamsForRestApi(
528
-                $model_query_params[0],
529
-                $model,
530
-                $requested_version
531
-            );
532
-            unset($rest_query_params[0]);
533
-        }
534
-        if (isset($model_query_params['having'])) {
535
-            $rest_query_params['having'] = ModelDataTranslator::prepareConditionsQueryParamsForRestApi(
536
-                $model_query_params['having'],
537
-                $model,
538
-                $requested_version
539
-            );
540
-        }
541
-        return apply_filters(
542
-            'FHEE__EventEspresso\core\libraries\rest_api\Model_Data_Translator__prepare_query_params_for_rest_api',
543
-            $rest_query_params,
544
-            $model_query_params,
545
-            $model,
546
-            $requested_version
547
-        );
548
-    }
549
-
550
-
551
-    /**
552
-     * Prepares all the sub-conditions query parameters (eg having or where conditions) for use in the rest api
553
-     *
554
-     * @param array    $inputted_query_params_of_this_type  eg like the "where" or "having" conditions query params
555
-     *                                                      passed into EEM_Base::get_all()
556
-     * @param EEM_Base $model
557
-     * @param string   $requested_version                   eg "4.8.36"
558
-     * @return array ready for use in the rest api query params
559
-     * @throws EE_Error
560
-     * @throws ObjectDetectedException if somehow a PHP object were in the query params' values,
561
-     *                                                      (which would be really unusual)
562
-     */
563
-    public static function prepareConditionsQueryParamsForRestApi(
564
-        $inputted_query_params_of_this_type,
565
-        EEM_Base $model,
566
-        $requested_version
567
-    ) {
568
-        $query_param_for_models = array();
569
-        foreach ($inputted_query_params_of_this_type as $query_param_key => $query_param_value) {
570
-            $field = ModelDataTranslator::deduceFieldFromQueryParam(
571
-                ModelDataTranslator::removeStarsAndAnythingAfterFromConditionQueryParamKey($query_param_key),
572
-                $model
573
-            );
574
-            if ($field instanceof EE_Model_Field_Base) {
575
-                // did they specify an operator?
576
-                if (is_array($query_param_value)) {
577
-                    $op = $query_param_value[0];
578
-                    $translated_value = array($op);
579
-                    if (isset($query_param_value[1])) {
580
-                        $value = $query_param_value[1];
581
-                        $translated_value[1] = ModelDataTranslator::prepareFieldValuesForJson(
582
-                            $field,
583
-                            $value,
584
-                            $requested_version
585
-                        );
586
-                    }
587
-                } else {
588
-                    $translated_value = ModelDataTranslator::prepareFieldValueForJson(
589
-                        $field,
590
-                        $query_param_value,
591
-                        $requested_version
592
-                    );
593
-                }
594
-                $query_param_for_models[ $query_param_key ] = $translated_value;
595
-            } else {
596
-                // so it's not for a field, assume it's a logic query param key
597
-                $query_param_for_models[ $query_param_key ] = ModelDataTranslator::prepareConditionsQueryParamsForRestApi(
598
-                    $query_param_value,
599
-                    $model,
600
-                    $requested_version
601
-                );
602
-            }
603
-        }
604
-        return $query_param_for_models;
605
-    }
606
-
607
-
608
-    /**
609
-     * @param $condition_query_param_key
610
-     * @return string
611
-     */
612
-    public static function removeStarsAndAnythingAfterFromConditionQueryParamKey($condition_query_param_key)
613
-    {
614
-        $pos_of_star = strpos($condition_query_param_key, '*');
615
-        if ($pos_of_star === false) {
616
-            return $condition_query_param_key;
617
-        } else {
618
-            $condition_query_param_sans_star = substr($condition_query_param_key, 0, $pos_of_star);
619
-            return $condition_query_param_sans_star;
620
-        }
621
-    }
622
-
623
-
624
-    /**
625
-     * Takes the input parameter and finds the model field that it indicates.
626
-     *
627
-     * @param string   $query_param_name like Registration.Transaction.TXN_ID, Event.Datetime.start_time, or REG_ID
628
-     * @param EEM_Base $model
629
-     * @return EE_Model_Field_Base
630
-     * @throws EE_Error
631
-     */
632
-    public static function deduceFieldFromQueryParam($query_param_name, EEM_Base $model)
633
-    {
634
-        // ok, now proceed with deducing which part is the model's name, and which is the field's name
635
-        // which will help us find the database table and column
636
-        $query_param_parts = explode('.', $query_param_name);
637
-        if (empty($query_param_parts)) {
638
-            throw new EE_Error(
639
-                sprintf(
640
-                    __(
641
-                        '_extract_column_name is empty when trying to extract column and table name from %s',
642
-                        'event_espresso'
643
-                    ),
644
-                    $query_param_name
645
-                )
646
-            );
647
-        }
648
-        $number_of_parts = count($query_param_parts);
649
-        $last_query_param_part = $query_param_parts[ count($query_param_parts) - 1 ];
650
-        if ($number_of_parts === 1) {
651
-            $field_name = $last_query_param_part;
652
-        } else {// $number_of_parts >= 2
653
-            // the last part is the column name, and there are only 2parts. therefore...
654
-            $field_name = $last_query_param_part;
655
-            $model = \EE_Registry::instance()->load_model($query_param_parts[ $number_of_parts - 2 ]);
656
-        }
657
-        try {
658
-            return $model->field_settings_for($field_name, false);
659
-        } catch (EE_Error $e) {
660
-            return null;
661
-        }
662
-    }
663
-
664
-
665
-    /**
666
-     * Returns true if $data can be easily represented in JSON.
667
-     * Basically, objects and resources can't be represented in JSON easily.
668
-     *
669
-     * @param mixed $data
670
-     * @return bool
671
-     */
672
-    protected static function isRepresentableInJson($data)
673
-    {
674
-        return is_scalar($data)
675
-               || is_array($data)
676
-               || is_null($data);
677
-    }
39
+	/**
40
+	 * We used to use -1 for infinity in the rest api, but that's ambiguous for
41
+	 * fields that COULD contain -1; so we use null
42
+	 */
43
+	const EE_INF_IN_REST = null;
44
+
45
+
46
+	/**
47
+	 * Prepares a possible array of input values from JSON for use by the models
48
+	 *
49
+	 * @param EE_Model_Field_Base $field_obj
50
+	 * @param mixed               $original_value_maybe_array
51
+	 * @param string              $requested_version
52
+	 * @param string              $timezone_string treat values as being in this timezone
53
+	 * @return mixed
54
+	 * @throws RestException
55
+	 */
56
+	public static function prepareFieldValuesFromJson(
57
+		$field_obj,
58
+		$original_value_maybe_array,
59
+		$requested_version,
60
+		$timezone_string = 'UTC'
61
+	) {
62
+		if (is_array($original_value_maybe_array)
63
+			&& ! $field_obj instanceof EE_Serialized_Text_Field
64
+		) {
65
+			$new_value_maybe_array = array();
66
+			foreach ($original_value_maybe_array as $array_key => $array_item) {
67
+				$new_value_maybe_array[ $array_key ] = ModelDataTranslator::prepareFieldValueFromJson(
68
+					$field_obj,
69
+					$array_item,
70
+					$requested_version,
71
+					$timezone_string
72
+				);
73
+			}
74
+		} else {
75
+			$new_value_maybe_array = ModelDataTranslator::prepareFieldValueFromJson(
76
+				$field_obj,
77
+				$original_value_maybe_array,
78
+				$requested_version,
79
+				$timezone_string
80
+			);
81
+		}
82
+		return $new_value_maybe_array;
83
+	}
84
+
85
+
86
+	/**
87
+	 * Prepares an array of field values FOR use in JSON/REST API
88
+	 *
89
+	 * @param EE_Model_Field_Base $field_obj
90
+	 * @param mixed               $original_value_maybe_array
91
+	 * @param string              $request_version (eg 4.8.36)
92
+	 * @return array
93
+	 */
94
+	public static function prepareFieldValuesForJson($field_obj, $original_value_maybe_array, $request_version)
95
+	{
96
+		if (is_array($original_value_maybe_array)) {
97
+			$new_value = array();
98
+			foreach ($original_value_maybe_array as $key => $value) {
99
+				$new_value[ $key ] = ModelDataTranslator::prepareFieldValuesForJson(
100
+					$field_obj,
101
+					$value,
102
+					$request_version
103
+				);
104
+			}
105
+		} else {
106
+			$new_value = ModelDataTranslator::prepareFieldValueForJson(
107
+				$field_obj,
108
+				$original_value_maybe_array,
109
+				$request_version
110
+			);
111
+		}
112
+		return $new_value;
113
+	}
114
+
115
+
116
+	/**
117
+	 * Prepares incoming data from the json or $_REQUEST parameters for the models'
118
+	 * "$query_params".
119
+	 *
120
+	 * @param EE_Model_Field_Base $field_obj
121
+	 * @param mixed               $original_value
122
+	 * @param string              $requested_version
123
+	 * @param string              $timezone_string treat values as being in this timezone
124
+	 * @return mixed
125
+	 * @throws RestException
126
+	 * @throws DomainException
127
+	 * @throws EE_Error
128
+	 */
129
+	public static function prepareFieldValueFromJson(
130
+		$field_obj,
131
+		$original_value,
132
+		$requested_version,
133
+		$timezone_string = 'UTC' // UTC
134
+	) {
135
+		// check if they accidentally submitted an error value. If so throw an exception
136
+		if (is_array($original_value)
137
+			&& isset($original_value['error_code'], $original_value['error_message'])) {
138
+			throw new RestException(
139
+				'rest_submitted_error_value',
140
+				sprintf(
141
+					esc_html__(
142
+						'You tried to submit a JSON error object as a value for %1$s. That\'s not allowed.',
143
+						'event_espresso'
144
+					),
145
+					$field_obj->get_name()
146
+				),
147
+				array(
148
+					'status' => 400,
149
+				)
150
+			);
151
+		}
152
+		// double-check for serialized PHP. We never accept serialized PHP. No way Jose.
153
+		ModelDataTranslator::throwExceptionIfContainsSerializedData($original_value);
154
+		$timezone_string = $timezone_string !== '' ? $timezone_string : get_option('timezone_string', '');
155
+		$new_value = null;
156
+		// walk through the submitted data and double-check for serialized PHP. We never accept serialized PHP. No
157
+		// way Jose.
158
+		ModelDataTranslator::throwExceptionIfContainsSerializedData($original_value);
159
+		if ($field_obj instanceof EE_Infinite_Integer_Field
160
+			&& in_array($original_value, array(null, ''), true)
161
+		) {
162
+			$new_value = EE_INF;
163
+		} elseif ($field_obj instanceof EE_Datetime_Field) {
164
+			$new_value = rest_parse_date(
165
+				self::getTimestampWithTimezoneOffset($original_value, $field_obj, $timezone_string)
166
+			);
167
+			if ($new_value === false) {
168
+				throw new RestException(
169
+					'invalid_format_for_timestamp',
170
+					sprintf(
171
+						esc_html__(
172
+							'Timestamps received on a request as the value for Date and Time fields must be in %1$s/%2$s format.  The timestamp provided (%3$s) is not that format.',
173
+							'event_espresso'
174
+						),
175
+						'RFC3339',
176
+						'ISO8601',
177
+						$original_value
178
+					),
179
+					array(
180
+						'status' => 400,
181
+					)
182
+				);
183
+			}
184
+		} else {
185
+			$new_value = $original_value;
186
+		}
187
+		return $new_value;
188
+	}
189
+
190
+
191
+	/**
192
+	 * This checks if the incoming timestamp has timezone information already on it and if it doesn't then adds timezone
193
+	 * information via details obtained from the host site.
194
+	 *
195
+	 * @param string            $original_timestamp
196
+	 * @param EE_Datetime_Field $datetime_field
197
+	 * @param                   $timezone_string
198
+	 * @return string
199
+	 * @throws DomainException
200
+	 */
201
+	private static function getTimestampWithTimezoneOffset(
202
+		$original_timestamp,
203
+		EE_Datetime_Field $datetime_field,
204
+		$timezone_string
205
+	) {
206
+		// already have timezone information?
207
+		if (preg_match('/Z|(\+|\-)(\d{2}:\d{2})/', $original_timestamp)) {
208
+			// yes, we're ignoring the timezone.
209
+			return $original_timestamp;
210
+		}
211
+		// need to append timezone
212
+		list($offset_sign, $offset_secs) = self::parseTimezoneOffset(
213
+			$datetime_field->get_timezone_offset(
214
+				new \DateTimeZone($timezone_string),
215
+				$original_timestamp
216
+			)
217
+		);
218
+		$offset_string =
219
+			str_pad(
220
+				floor($offset_secs / HOUR_IN_SECONDS),
221
+				2,
222
+				'0',
223
+				STR_PAD_LEFT
224
+			)
225
+			. ':'
226
+			. str_pad(
227
+				($offset_secs % HOUR_IN_SECONDS) / MINUTE_IN_SECONDS,
228
+				2,
229
+				'0',
230
+				STR_PAD_LEFT
231
+			);
232
+		return $original_timestamp . $offset_sign . $offset_string;
233
+	}
234
+
235
+
236
+	/**
237
+	 * Throws an exception if $data is a serialized PHP string (or somehow an actually PHP object, although I don't
238
+	 * think that can happen). If $data is an array, recurses into its keys and values
239
+	 *
240
+	 * @param mixed $data
241
+	 * @throws RestException
242
+	 * @return void
243
+	 */
244
+	public static function throwExceptionIfContainsSerializedData($data)
245
+	{
246
+		if (is_array($data)) {
247
+			foreach ($data as $key => $value) {
248
+				ModelDataTranslator::throwExceptionIfContainsSerializedData($key);
249
+				ModelDataTranslator::throwExceptionIfContainsSerializedData($value);
250
+			}
251
+		} else {
252
+			if (is_serialized($data) || is_object($data)) {
253
+				throw new RestException(
254
+					'serialized_data_submission_prohibited',
255
+					esc_html__(
256
+					// @codingStandardsIgnoreStart
257
+						'You tried to submit a string of serialized text. Serialized PHP is prohibited over the EE4 REST API.',
258
+						// @codingStandardsIgnoreEnd
259
+						'event_espresso'
260
+					)
261
+				);
262
+			}
263
+		}
264
+	}
265
+
266
+
267
+	/**
268
+	 * determines what's going on with them timezone strings
269
+	 *
270
+	 * @param int $timezone_offset
271
+	 * @return array
272
+	 */
273
+	private static function parseTimezoneOffset($timezone_offset)
274
+	{
275
+		$first_char = substr((string) $timezone_offset, 0, 1);
276
+		if ($first_char === '+' || $first_char === '-') {
277
+			$offset_sign = $first_char;
278
+			$offset_secs = substr((string) $timezone_offset, 1);
279
+		} else {
280
+			$offset_sign = '+';
281
+			$offset_secs = $timezone_offset;
282
+		}
283
+		return array($offset_sign, $offset_secs);
284
+	}
285
+
286
+
287
+	/**
288
+	 * Prepares a field's value for display in the API
289
+	 *
290
+	 * @param EE_Model_Field_Base $field_obj
291
+	 * @param mixed               $original_value
292
+	 * @param string              $requested_version
293
+	 * @return mixed
294
+	 */
295
+	public static function prepareFieldValueForJson($field_obj, $original_value, $requested_version)
296
+	{
297
+		if ($original_value === EE_INF) {
298
+			$new_value = ModelDataTranslator::EE_INF_IN_REST;
299
+		} elseif ($field_obj instanceof EE_Datetime_Field) {
300
+			if (is_string($original_value)) {
301
+				// did they submit a string of a unix timestamp?
302
+				if (is_numeric($original_value)) {
303
+					$datetime_obj = new \DateTime();
304
+					$datetime_obj->setTimestamp((int) $original_value);
305
+				} else {
306
+					// first, check if its a MySQL timestamp in GMT
307
+					$datetime_obj = \DateTime::createFromFormat('Y-m-d H:i:s', $original_value);
308
+				}
309
+				if (! $datetime_obj instanceof \DateTime) {
310
+					// so it's not a unix timestamp or a MySQL timestamp. Maybe its in the field's date/time format?
311
+					$datetime_obj = $field_obj->prepare_for_set($original_value);
312
+				}
313
+				$original_value = $datetime_obj;
314
+			}
315
+			if ($original_value instanceof \DateTime) {
316
+				$new_value = $original_value->format('Y-m-d H:i:s');
317
+			} elseif (is_int($original_value) || is_float($original_value)) {
318
+				$new_value = date('Y-m-d H:i:s', $original_value);
319
+			} elseif ($original_value === null || $original_value === '') {
320
+				$new_value = null;
321
+			} else {
322
+				// so it's not a datetime object, unix timestamp (as string or int),
323
+				// MySQL timestamp, or even a string in the field object's format. So no idea what it is
324
+				throw new \EE_Error(
325
+					sprintf(
326
+						esc_html__(
327
+						// @codingStandardsIgnoreStart
328
+							'The value "%1$s" for the field "%2$s" on model "%3$s" could not be understood. It should be a PHP DateTime, unix timestamp, MySQL date, or string in the format "%4$s".',
329
+							// @codingStandardsIgnoreEnd
330
+							'event_espresso'
331
+						),
332
+						$original_value,
333
+						$field_obj->get_name(),
334
+						$field_obj->get_model_name(),
335
+						$field_obj->get_time_format() . ' ' . $field_obj->get_time_format()
336
+					)
337
+				);
338
+			}
339
+			if ($new_value !== null) {
340
+				$new_value = mysql_to_rfc3339($new_value);
341
+			}
342
+		} else {
343
+			$new_value = $original_value;
344
+		}
345
+		// are we about to send an object? just don't. We have no good way to represent it in JSON.
346
+		// can't just check using is_object() because that missed PHP incomplete objects
347
+		if (! ModelDataTranslator::isRepresentableInJson($new_value)) {
348
+			$new_value = array(
349
+				'error_code'    => 'php_object_not_return',
350
+				'error_message' => esc_html__(
351
+					'The value of this field in the database is a PHP object, which can\'t be represented in JSON.',
352
+					'event_espresso'
353
+				),
354
+			);
355
+		}
356
+		return apply_filters(
357
+			'FHEE__EventEspresso\core\libraries\rest_api\Model_Data_Translator__prepare_field_for_rest_api',
358
+			$new_value,
359
+			$field_obj,
360
+			$original_value,
361
+			$requested_version
362
+		);
363
+	}
364
+
365
+
366
+	/**
367
+	 * Prepares condition-query-parameters (like what's in where and having) from
368
+	 * the format expected in the API to use in the models
369
+	 *
370
+	 * @param array $inputted_query_params_of_this_type
371
+	 * @param EEM_Base $model
372
+	 * @param string $requested_version
373
+	 * @param boolean $writing whether this data will be written to the DB, or if we're just building a query.
374
+	 *                          If we're writing to the DB, we don't expect any operators, or any logic query
375
+	 *                          parameters, and we also won't accept serialized data unless the current user has
376
+	 *                          unfiltered_html.
377
+	 * @return array
378
+	 * @throws DomainException
379
+	 * @throws EE_Error
380
+	 * @throws RestException
381
+	 * @throws InvalidDataTypeException
382
+	 * @throws InvalidInterfaceException
383
+	 * @throws InvalidArgumentException
384
+	 */
385
+	public static function prepareConditionsQueryParamsForModels(
386
+		$inputted_query_params_of_this_type,
387
+		EEM_Base $model,
388
+		$requested_version,
389
+		$writing = false
390
+	) {
391
+		$query_param_for_models = array();
392
+		$context = new RestIncomingQueryParamContext($model, $requested_version, $writing);
393
+		foreach ($inputted_query_params_of_this_type as $query_param_key => $query_param_value) {
394
+			$query_param_meta = new RestIncomingQueryParamMetadata($query_param_key, $query_param_value, $context);
395
+			if ($query_param_meta->getField() instanceof EE_Model_Field_Base) {
396
+				$translated_value = $query_param_meta->determineConditionsQueryParameterValue();
397
+				if ((isset($query_param_for_models[ $query_param_meta->getQueryParamKey() ]) && $query_param_meta->isGmtField())
398
+					|| $translated_value === null
399
+				) {
400
+					// they have already provided a non-gmt field, ignore the gmt one. That's what WP core
401
+					// currently does (they might change it though). See https://core.trac.wordpress.org/ticket/39954
402
+					// OR we couldn't create a translated value from their input
403
+					continue;
404
+				}
405
+				$query_param_for_models[ $query_param_meta->getQueryParamKey() ] = $translated_value;
406
+			} else {
407
+				$nested_query_params = $query_param_meta->determineNestedConditionQueryParameters();
408
+				if ($nested_query_params) {
409
+					$query_param_for_models[ $query_param_meta->getQueryParamKey() ] = $nested_query_params;
410
+				}
411
+			}
412
+		}
413
+		return $query_param_for_models;
414
+	}
415
+
416
+	/**
417
+	 * Mostly checks if the last 4 characters are "_gmt", indicating its a
418
+	 * gmt date field name
419
+	 *
420
+	 * @param string $field_name
421
+	 * @return boolean
422
+	 */
423
+	public static function isGmtDateFieldName($field_name)
424
+	{
425
+		return substr(
426
+			ModelDataTranslator::removeStarsAndAnythingAfterFromConditionQueryParamKey($field_name),
427
+			-4,
428
+			4
429
+		) === '_gmt';
430
+	}
431
+
432
+
433
+	/**
434
+	 * Removes the last "_gmt" part of a field name (and if there is no "_gmt" at the end, leave it alone)
435
+	 *
436
+	 * @param string $field_name
437
+	 * @return string
438
+	 */
439
+	public static function removeGmtFromFieldName($field_name)
440
+	{
441
+		if (! ModelDataTranslator::isGmtDateFieldName($field_name)) {
442
+			return $field_name;
443
+		}
444
+		$query_param_sans_stars = ModelDataTranslator::removeStarsAndAnythingAfterFromConditionQueryParamKey(
445
+			$field_name
446
+		);
447
+		$query_param_sans_gmt_and_sans_stars = substr(
448
+			$query_param_sans_stars,
449
+			0,
450
+			strrpos(
451
+				$field_name,
452
+				'_gmt'
453
+			)
454
+		);
455
+		return str_replace($query_param_sans_stars, $query_param_sans_gmt_and_sans_stars, $field_name);
456
+	}
457
+
458
+
459
+	/**
460
+	 * Takes a field name from the REST API and prepares it for the model querying
461
+	 *
462
+	 * @param string $field_name
463
+	 * @return string
464
+	 */
465
+	public static function prepareFieldNameFromJson($field_name)
466
+	{
467
+		if (ModelDataTranslator::isGmtDateFieldName($field_name)) {
468
+			return ModelDataTranslator::removeGmtFromFieldName($field_name);
469
+		}
470
+		return $field_name;
471
+	}
472
+
473
+
474
+	/**
475
+	 * Takes array of field names from REST API and prepares for models
476
+	 *
477
+	 * @param array $field_names
478
+	 * @return array of field names (possibly include model prefixes)
479
+	 */
480
+	public static function prepareFieldNamesFromJson(array $field_names)
481
+	{
482
+		$new_array = array();
483
+		foreach ($field_names as $key => $field_name) {
484
+			$new_array[ $key ] = ModelDataTranslator::prepareFieldNameFromJson($field_name);
485
+		}
486
+		return $new_array;
487
+	}
488
+
489
+
490
+	/**
491
+	 * Takes array where array keys are field names (possibly with model path prefixes)
492
+	 * from the REST API and prepares them for model querying
493
+	 *
494
+	 * @param array $field_names_as_keys
495
+	 * @return array
496
+	 */
497
+	public static function prepareFieldNamesInArrayKeysFromJson(array $field_names_as_keys)
498
+	{
499
+		$new_array = array();
500
+		foreach ($field_names_as_keys as $field_name => $value) {
501
+			$new_array[ ModelDataTranslator::prepareFieldNameFromJson($field_name) ] = $value;
502
+		}
503
+		return $new_array;
504
+	}
505
+
506
+
507
+	/**
508
+	 * Prepares an array of model query params for use in the REST API
509
+	 *
510
+	 * @param array    $model_query_params
511
+	 * @param EEM_Base $model
512
+	 * @param string   $requested_version  eg "4.8.36". If null is provided, defaults to the latest release of the EE4
513
+	 *                                     REST API
514
+	 * @return array which can be passed into the EE4 REST API when querying a model resource
515
+	 * @throws EE_Error
516
+	 */
517
+	public static function prepareQueryParamsForRestApi(
518
+		array $model_query_params,
519
+		EEM_Base $model,
520
+		$requested_version = null
521
+	) {
522
+		if ($requested_version === null) {
523
+			$requested_version = EED_Core_Rest_Api::latest_rest_api_version();
524
+		}
525
+		$rest_query_params = $model_query_params;
526
+		if (isset($model_query_params[0])) {
527
+			$rest_query_params['where'] = ModelDataTranslator::prepareConditionsQueryParamsForRestApi(
528
+				$model_query_params[0],
529
+				$model,
530
+				$requested_version
531
+			);
532
+			unset($rest_query_params[0]);
533
+		}
534
+		if (isset($model_query_params['having'])) {
535
+			$rest_query_params['having'] = ModelDataTranslator::prepareConditionsQueryParamsForRestApi(
536
+				$model_query_params['having'],
537
+				$model,
538
+				$requested_version
539
+			);
540
+		}
541
+		return apply_filters(
542
+			'FHEE__EventEspresso\core\libraries\rest_api\Model_Data_Translator__prepare_query_params_for_rest_api',
543
+			$rest_query_params,
544
+			$model_query_params,
545
+			$model,
546
+			$requested_version
547
+		);
548
+	}
549
+
550
+
551
+	/**
552
+	 * Prepares all the sub-conditions query parameters (eg having or where conditions) for use in the rest api
553
+	 *
554
+	 * @param array    $inputted_query_params_of_this_type  eg like the "where" or "having" conditions query params
555
+	 *                                                      passed into EEM_Base::get_all()
556
+	 * @param EEM_Base $model
557
+	 * @param string   $requested_version                   eg "4.8.36"
558
+	 * @return array ready for use in the rest api query params
559
+	 * @throws EE_Error
560
+	 * @throws ObjectDetectedException if somehow a PHP object were in the query params' values,
561
+	 *                                                      (which would be really unusual)
562
+	 */
563
+	public static function prepareConditionsQueryParamsForRestApi(
564
+		$inputted_query_params_of_this_type,
565
+		EEM_Base $model,
566
+		$requested_version
567
+	) {
568
+		$query_param_for_models = array();
569
+		foreach ($inputted_query_params_of_this_type as $query_param_key => $query_param_value) {
570
+			$field = ModelDataTranslator::deduceFieldFromQueryParam(
571
+				ModelDataTranslator::removeStarsAndAnythingAfterFromConditionQueryParamKey($query_param_key),
572
+				$model
573
+			);
574
+			if ($field instanceof EE_Model_Field_Base) {
575
+				// did they specify an operator?
576
+				if (is_array($query_param_value)) {
577
+					$op = $query_param_value[0];
578
+					$translated_value = array($op);
579
+					if (isset($query_param_value[1])) {
580
+						$value = $query_param_value[1];
581
+						$translated_value[1] = ModelDataTranslator::prepareFieldValuesForJson(
582
+							$field,
583
+							$value,
584
+							$requested_version
585
+						);
586
+					}
587
+				} else {
588
+					$translated_value = ModelDataTranslator::prepareFieldValueForJson(
589
+						$field,
590
+						$query_param_value,
591
+						$requested_version
592
+					);
593
+				}
594
+				$query_param_for_models[ $query_param_key ] = $translated_value;
595
+			} else {
596
+				// so it's not for a field, assume it's a logic query param key
597
+				$query_param_for_models[ $query_param_key ] = ModelDataTranslator::prepareConditionsQueryParamsForRestApi(
598
+					$query_param_value,
599
+					$model,
600
+					$requested_version
601
+				);
602
+			}
603
+		}
604
+		return $query_param_for_models;
605
+	}
606
+
607
+
608
+	/**
609
+	 * @param $condition_query_param_key
610
+	 * @return string
611
+	 */
612
+	public static function removeStarsAndAnythingAfterFromConditionQueryParamKey($condition_query_param_key)
613
+	{
614
+		$pos_of_star = strpos($condition_query_param_key, '*');
615
+		if ($pos_of_star === false) {
616
+			return $condition_query_param_key;
617
+		} else {
618
+			$condition_query_param_sans_star = substr($condition_query_param_key, 0, $pos_of_star);
619
+			return $condition_query_param_sans_star;
620
+		}
621
+	}
622
+
623
+
624
+	/**
625
+	 * Takes the input parameter and finds the model field that it indicates.
626
+	 *
627
+	 * @param string   $query_param_name like Registration.Transaction.TXN_ID, Event.Datetime.start_time, or REG_ID
628
+	 * @param EEM_Base $model
629
+	 * @return EE_Model_Field_Base
630
+	 * @throws EE_Error
631
+	 */
632
+	public static function deduceFieldFromQueryParam($query_param_name, EEM_Base $model)
633
+	{
634
+		// ok, now proceed with deducing which part is the model's name, and which is the field's name
635
+		// which will help us find the database table and column
636
+		$query_param_parts = explode('.', $query_param_name);
637
+		if (empty($query_param_parts)) {
638
+			throw new EE_Error(
639
+				sprintf(
640
+					__(
641
+						'_extract_column_name is empty when trying to extract column and table name from %s',
642
+						'event_espresso'
643
+					),
644
+					$query_param_name
645
+				)
646
+			);
647
+		}
648
+		$number_of_parts = count($query_param_parts);
649
+		$last_query_param_part = $query_param_parts[ count($query_param_parts) - 1 ];
650
+		if ($number_of_parts === 1) {
651
+			$field_name = $last_query_param_part;
652
+		} else {// $number_of_parts >= 2
653
+			// the last part is the column name, and there are only 2parts. therefore...
654
+			$field_name = $last_query_param_part;
655
+			$model = \EE_Registry::instance()->load_model($query_param_parts[ $number_of_parts - 2 ]);
656
+		}
657
+		try {
658
+			return $model->field_settings_for($field_name, false);
659
+		} catch (EE_Error $e) {
660
+			return null;
661
+		}
662
+	}
663
+
664
+
665
+	/**
666
+	 * Returns true if $data can be easily represented in JSON.
667
+	 * Basically, objects and resources can't be represented in JSON easily.
668
+	 *
669
+	 * @param mixed $data
670
+	 * @return bool
671
+	 */
672
+	protected static function isRepresentableInJson($data)
673
+	{
674
+		return is_scalar($data)
675
+			   || is_array($data)
676
+			   || is_null($data);
677
+	}
678 678
 }
Please login to merge, or discard this patch.
Spacing   +16 added lines, -16 removed lines patch added patch discarded remove patch
@@ -64,7 +64,7 @@  discard block
 block discarded – undo
64 64
         ) {
65 65
             $new_value_maybe_array = array();
66 66
             foreach ($original_value_maybe_array as $array_key => $array_item) {
67
-                $new_value_maybe_array[ $array_key ] = ModelDataTranslator::prepareFieldValueFromJson(
67
+                $new_value_maybe_array[$array_key] = ModelDataTranslator::prepareFieldValueFromJson(
68 68
                     $field_obj,
69 69
                     $array_item,
70 70
                     $requested_version,
@@ -96,7 +96,7 @@  discard block
 block discarded – undo
96 96
         if (is_array($original_value_maybe_array)) {
97 97
             $new_value = array();
98 98
             foreach ($original_value_maybe_array as $key => $value) {
99
-                $new_value[ $key ] = ModelDataTranslator::prepareFieldValuesForJson(
99
+                $new_value[$key] = ModelDataTranslator::prepareFieldValuesForJson(
100 100
                     $field_obj,
101 101
                     $value,
102 102
                     $request_version
@@ -229,7 +229,7 @@  discard block
 block discarded – undo
229 229
                 '0',
230 230
                 STR_PAD_LEFT
231 231
             );
232
-        return $original_timestamp . $offset_sign . $offset_string;
232
+        return $original_timestamp.$offset_sign.$offset_string;
233 233
     }
234 234
 
235 235
 
@@ -306,7 +306,7 @@  discard block
 block discarded – undo
306 306
                     // first, check if its a MySQL timestamp in GMT
307 307
                     $datetime_obj = \DateTime::createFromFormat('Y-m-d H:i:s', $original_value);
308 308
                 }
309
-                if (! $datetime_obj instanceof \DateTime) {
309
+                if ( ! $datetime_obj instanceof \DateTime) {
310 310
                     // so it's not a unix timestamp or a MySQL timestamp. Maybe its in the field's date/time format?
311 311
                     $datetime_obj = $field_obj->prepare_for_set($original_value);
312 312
                 }
@@ -332,7 +332,7 @@  discard block
 block discarded – undo
332 332
                         $original_value,
333 333
                         $field_obj->get_name(),
334 334
                         $field_obj->get_model_name(),
335
-                        $field_obj->get_time_format() . ' ' . $field_obj->get_time_format()
335
+                        $field_obj->get_time_format().' '.$field_obj->get_time_format()
336 336
                     )
337 337
                 );
338 338
             }
@@ -344,7 +344,7 @@  discard block
 block discarded – undo
344 344
         }
345 345
         // are we about to send an object? just don't. We have no good way to represent it in JSON.
346 346
         // can't just check using is_object() because that missed PHP incomplete objects
347
-        if (! ModelDataTranslator::isRepresentableInJson($new_value)) {
347
+        if ( ! ModelDataTranslator::isRepresentableInJson($new_value)) {
348 348
             $new_value = array(
349 349
                 'error_code'    => 'php_object_not_return',
350 350
                 'error_message' => esc_html__(
@@ -394,7 +394,7 @@  discard block
 block discarded – undo
394 394
             $query_param_meta = new RestIncomingQueryParamMetadata($query_param_key, $query_param_value, $context);
395 395
             if ($query_param_meta->getField() instanceof EE_Model_Field_Base) {
396 396
                 $translated_value = $query_param_meta->determineConditionsQueryParameterValue();
397
-                if ((isset($query_param_for_models[ $query_param_meta->getQueryParamKey() ]) && $query_param_meta->isGmtField())
397
+                if ((isset($query_param_for_models[$query_param_meta->getQueryParamKey()]) && $query_param_meta->isGmtField())
398 398
                     || $translated_value === null
399 399
                 ) {
400 400
                     // they have already provided a non-gmt field, ignore the gmt one. That's what WP core
@@ -402,11 +402,11 @@  discard block
 block discarded – undo
402 402
                     // OR we couldn't create a translated value from their input
403 403
                     continue;
404 404
                 }
405
-                $query_param_for_models[ $query_param_meta->getQueryParamKey() ] = $translated_value;
405
+                $query_param_for_models[$query_param_meta->getQueryParamKey()] = $translated_value;
406 406
             } else {
407 407
                 $nested_query_params = $query_param_meta->determineNestedConditionQueryParameters();
408 408
                 if ($nested_query_params) {
409
-                    $query_param_for_models[ $query_param_meta->getQueryParamKey() ] = $nested_query_params;
409
+                    $query_param_for_models[$query_param_meta->getQueryParamKey()] = $nested_query_params;
410 410
                 }
411 411
             }
412 412
         }
@@ -438,7 +438,7 @@  discard block
 block discarded – undo
438 438
      */
439 439
     public static function removeGmtFromFieldName($field_name)
440 440
     {
441
-        if (! ModelDataTranslator::isGmtDateFieldName($field_name)) {
441
+        if ( ! ModelDataTranslator::isGmtDateFieldName($field_name)) {
442 442
             return $field_name;
443 443
         }
444 444
         $query_param_sans_stars = ModelDataTranslator::removeStarsAndAnythingAfterFromConditionQueryParamKey(
@@ -481,7 +481,7 @@  discard block
 block discarded – undo
481 481
     {
482 482
         $new_array = array();
483 483
         foreach ($field_names as $key => $field_name) {
484
-            $new_array[ $key ] = ModelDataTranslator::prepareFieldNameFromJson($field_name);
484
+            $new_array[$key] = ModelDataTranslator::prepareFieldNameFromJson($field_name);
485 485
         }
486 486
         return $new_array;
487 487
     }
@@ -498,7 +498,7 @@  discard block
 block discarded – undo
498 498
     {
499 499
         $new_array = array();
500 500
         foreach ($field_names_as_keys as $field_name => $value) {
501
-            $new_array[ ModelDataTranslator::prepareFieldNameFromJson($field_name) ] = $value;
501
+            $new_array[ModelDataTranslator::prepareFieldNameFromJson($field_name)] = $value;
502 502
         }
503 503
         return $new_array;
504 504
     }
@@ -591,10 +591,10 @@  discard block
 block discarded – undo
591 591
                         $requested_version
592 592
                     );
593 593
                 }
594
-                $query_param_for_models[ $query_param_key ] = $translated_value;
594
+                $query_param_for_models[$query_param_key] = $translated_value;
595 595
             } else {
596 596
                 // so it's not for a field, assume it's a logic query param key
597
-                $query_param_for_models[ $query_param_key ] = ModelDataTranslator::prepareConditionsQueryParamsForRestApi(
597
+                $query_param_for_models[$query_param_key] = ModelDataTranslator::prepareConditionsQueryParamsForRestApi(
598 598
                     $query_param_value,
599 599
                     $model,
600 600
                     $requested_version
@@ -646,13 +646,13 @@  discard block
 block discarded – undo
646 646
             );
647 647
         }
648 648
         $number_of_parts = count($query_param_parts);
649
-        $last_query_param_part = $query_param_parts[ count($query_param_parts) - 1 ];
649
+        $last_query_param_part = $query_param_parts[count($query_param_parts) - 1];
650 650
         if ($number_of_parts === 1) {
651 651
             $field_name = $last_query_param_part;
652 652
         } else {// $number_of_parts >= 2
653 653
             // the last part is the column name, and there are only 2parts. therefore...
654 654
             $field_name = $last_query_param_part;
655
-            $model = \EE_Registry::instance()->load_model($query_param_parts[ $number_of_parts - 2 ]);
655
+            $model = \EE_Registry::instance()->load_model($query_param_parts[$number_of_parts - 2]);
656 656
         }
657 657
         try {
658 658
             return $model->field_settings_for($field_name, false);
Please login to merge, or discard this patch.
modules/core_rest_api/EED_Core_Rest_Api.module.php 2 patches
Spacing   +46 added lines, -46 removed lines patch added patch discarded remove patch
@@ -123,7 +123,7 @@  discard block
 block discarded – undo
123 123
      */
124 124
     protected static function _set_hooks_for_changes()
125 125
     {
126
-        $folder_contents = EEH_File::get_contents_of_folders(array(EE_LIBRARIES . 'rest_api' . DS . 'changes'), false);
126
+        $folder_contents = EEH_File::get_contents_of_folders(array(EE_LIBRARIES.'rest_api'.DS.'changes'), false);
127 127
         foreach ($folder_contents as $classname_in_namespace => $filepath) {
128 128
             // ignore the base parent class
129 129
             // and legacy named classes
@@ -132,7 +132,7 @@  discard block
 block discarded – undo
132 132
             ) {
133 133
                 continue;
134 134
             }
135
-            $full_classname = 'EventEspresso\core\libraries\rest_api\changes\\' . $classname_in_namespace;
135
+            $full_classname = 'EventEspresso\core\libraries\rest_api\changes\\'.$classname_in_namespace;
136 136
             if (class_exists($full_classname)) {
137 137
                 $instance_of_class = new $full_classname;
138 138
                 if ($instance_of_class instanceof ChangesInBase) {
@@ -177,10 +177,10 @@  discard block
 block discarded – undo
177 177
                      * }
178 178
                      */
179 179
                     // skip route options
180
-                    if (! is_numeric($endpoint_key)) {
180
+                    if ( ! is_numeric($endpoint_key)) {
181 181
                         continue;
182 182
                     }
183
-                    if (! isset($data_for_single_endpoint['callback'], $data_for_single_endpoint['methods'])) {
183
+                    if ( ! isset($data_for_single_endpoint['callback'], $data_for_single_endpoint['methods'])) {
184 184
                         throw new EE_Error(
185 185
                             esc_html__(
186 186
                             // @codingStandardsIgnoreStart
@@ -201,7 +201,7 @@  discard block
 block discarded – undo
201 201
                     }
202 202
                     if (isset($data_for_single_endpoint['callback_args'])) {
203 203
                         $callback_args = $data_for_single_endpoint['callback_args'];
204
-                        $single_endpoint_args['callback'] = function (\WP_REST_Request $request) use (
204
+                        $single_endpoint_args['callback'] = function(\WP_REST_Request $request) use (
205 205
                             $callback,
206 206
                             $callback_args
207 207
                         ) {
@@ -220,7 +220,7 @@  discard block
 block discarded – undo
220 220
                     $schema_route_data = $data_for_multiple_endpoints['schema'];
221 221
                     $schema_callback = $schema_route_data['schema_callback'];
222 222
                     $callback_args = $schema_route_data['callback_args'];
223
-                    $multiple_endpoint_args['schema'] = function () use ($schema_callback, $callback_args) {
223
+                    $multiple_endpoint_args['schema'] = function() use ($schema_callback, $callback_args) {
224 224
                         return call_user_func_array(
225 225
                             $schema_callback,
226 226
                             $callback_args
@@ -262,7 +262,7 @@  discard block
 block discarded – undo
262 262
     {
263 263
         // delete the saved EE REST API routes
264 264
         foreach (EED_Core_Rest_Api::versions_served() as $version => $hidden) {
265
-            delete_option(EED_Core_Rest_Api::saved_routes_option_names . $version);
265
+            delete_option(EED_Core_Rest_Api::saved_routes_option_names.$version);
266 266
         }
267 267
     }
268 268
 
@@ -281,7 +281,7 @@  discard block
 block discarded – undo
281 281
     {
282 282
         $ee_routes = array();
283 283
         foreach (self::versions_served() as $version => $hidden_endpoints) {
284
-            $ee_routes[ self::ee_api_namespace . $version ] = self::_get_ee_route_data_for_version(
284
+            $ee_routes[self::ee_api_namespace.$version] = self::_get_ee_route_data_for_version(
285 285
                 $version,
286 286
                 $hidden_endpoints
287 287
             );
@@ -301,8 +301,8 @@  discard block
 block discarded – undo
301 301
      */
302 302
     protected static function _get_ee_route_data_for_version($version, $hidden_endpoints = false)
303 303
     {
304
-        $ee_routes = get_option(self::saved_routes_option_names . $version, null);
305
-        if (! $ee_routes || EED_Core_Rest_Api::debugMode()) {
304
+        $ee_routes = get_option(self::saved_routes_option_names.$version, null);
305
+        if ( ! $ee_routes || EED_Core_Rest_Api::debugMode()) {
306 306
             $ee_routes = self::_save_ee_route_data_for_version($version, $hidden_endpoints);
307 307
         }
308 308
         return $ee_routes;
@@ -329,7 +329,7 @@  discard block
 block discarded – undo
329 329
                 $instance->_get_rpc_route_data_for_version($version, $hidden_endpoints)
330 330
             )
331 331
         );
332
-        $option_name = self::saved_routes_option_names . $version;
332
+        $option_name = self::saved_routes_option_names.$version;
333 333
         if (get_option($option_name)) {
334 334
             update_option($option_name, $routes, true);
335 335
         } else {
@@ -374,8 +374,8 @@  discard block
 block discarded – undo
374 374
     {
375 375
         $model_routes = array();
376 376
         foreach (self::versions_served() as $version => $hidden_endpoint) {
377
-            $model_routes[ EED_Core_Rest_Api::ee_api_namespace
378
-                           . $version ] = $this->_get_config_route_data_for_version($version, $hidden_endpoint);
377
+            $model_routes[EED_Core_Rest_Api::ee_api_namespace
378
+                           . $version] = $this->_get_config_route_data_for_version($version, $hidden_endpoint);
379 379
         }
380 380
         return $model_routes;
381 381
     }
@@ -444,13 +444,13 @@  discard block
 block discarded – undo
444 444
         foreach (EED_Core_Rest_Api::model_names_with_plural_routes($version) as $model_name => $model_classname) {
445 445
             $model = \EE_Registry::instance()->load_model($model_name);
446 446
             // if this isn't a valid model then let's skip iterate to the next item in the loop.
447
-            if (! $model instanceof EEM_Base) {
447
+            if ( ! $model instanceof EEM_Base) {
448 448
                 continue;
449 449
             }
450 450
             // yes we could just register one route for ALL models, but then they wouldn't show up in the index
451 451
             $plural_model_route = EED_Core_Rest_Api::get_collection_route($model);
452 452
             $singular_model_route = EED_Core_Rest_Api::get_entity_route($model, '(?P<id>[^\/]+)');
453
-            $model_routes[ $plural_model_route ] = array(
453
+            $model_routes[$plural_model_route] = array(
454 454
                 array(
455 455
                     'callback'        => array(
456 456
                         'EventEspresso\core\libraries\rest_api\controllers\model\Read',
@@ -461,7 +461,7 @@  discard block
 block discarded – undo
461 461
                     'hidden_endpoint' => $hidden_endpoint,
462 462
                     'args'            => $this->_get_read_query_params($model, $version),
463 463
                     '_links'          => array(
464
-                        'self' => rest_url(EED_Core_Rest_Api::ee_api_namespace . $version . $singular_model_route),
464
+                        'self' => rest_url(EED_Core_Rest_Api::ee_api_namespace.$version.$singular_model_route),
465 465
                     ),
466 466
                 ),
467 467
                 'schema' => array(
@@ -472,7 +472,7 @@  discard block
 block discarded – undo
472 472
                     'callback_args'   => array($version, $model_name),
473 473
                 ),
474 474
             );
475
-            $model_routes[ $singular_model_route ] = array(
475
+            $model_routes[$singular_model_route] = array(
476 476
                 array(
477 477
                     'callback'        => array(
478 478
                         'EventEspresso\core\libraries\rest_api\controllers\model\Read',
@@ -489,7 +489,7 @@  discard block
 block discarded – undo
489 489
                 EED_Core_Rest_Api::should_have_write_endpoints($model),
490 490
                 $model
491 491
             )) {
492
-                $model_routes[ $plural_model_route ][] = array(
492
+                $model_routes[$plural_model_route][] = array(
493 493
                     'callback'        => array(
494 494
                         'EventEspresso\core\libraries\rest_api\controllers\model\Write',
495 495
                         'handleRequestInsert',
@@ -499,8 +499,8 @@  discard block
 block discarded – undo
499 499
                     'hidden_endpoint' => $hidden_endpoint,
500 500
                     'args'            => $this->_get_write_params($model_name, $model_version_info, true),
501 501
                 );
502
-                $model_routes[ $singular_model_route ] = array_merge(
503
-                    $model_routes[ $singular_model_route ],
502
+                $model_routes[$singular_model_route] = array_merge(
503
+                    $model_routes[$singular_model_route],
504 504
                     array(
505 505
                         array(
506 506
                             'callback'        => array(
@@ -543,7 +543,7 @@  discard block
 block discarded – undo
543 543
                         'args'            => $this->_get_read_query_params($relation_obj->get_other_model(), $version),
544 544
                     ),
545 545
                 );
546
-                $model_routes[ $related_route ] = $endpoints;
546
+                $model_routes[$related_route] = $endpoints;
547 547
             }
548 548
         }
549 549
         return $model_routes;
@@ -575,7 +575,7 @@  discard block
 block discarded – undo
575 575
      */
576 576
     public static function get_entity_route($model, $id)
577 577
     {
578
-        return EED_Core_Rest_Api::get_collection_route($model) . '/' . $id;
578
+        return EED_Core_Rest_Api::get_collection_route($model).'/'.$id;
579 579
     }
580 580
 
581 581
 
@@ -595,7 +595,7 @@  discard block
 block discarded – undo
595 595
             $relation_obj->get_other_model()->get_this_model_name(),
596 596
             $relation_obj
597 597
         );
598
-        return EED_Core_Rest_Api::get_entity_route($model, $id) . '/' . $related_model_name_endpoint_part;
598
+        return EED_Core_Rest_Api::get_entity_route($model, $id).'/'.$related_model_name_endpoint_part;
599 599
     }
600 600
 
601 601
 
@@ -609,7 +609,7 @@  discard block
 block discarded – undo
609 609
      */
610 610
     public static function get_versioned_route_to($relative_route, $version = '4.8.36')
611 611
     {
612
-        return '/' . EED_Core_Rest_Api::ee_api_namespace . $version . '/' . $relative_route;
612
+        return '/'.EED_Core_Rest_Api::ee_api_namespace.$version.'/'.$relative_route;
613 613
     }
614 614
 
615 615
 
@@ -623,7 +623,7 @@  discard block
 block discarded – undo
623 623
     {
624 624
         $routes = array();
625 625
         foreach (self::versions_served() as $version => $hidden_endpoint) {
626
-            $routes[ self::ee_api_namespace . $version ] = $this->_get_rpc_route_data_for_version(
626
+            $routes[self::ee_api_namespace.$version] = $this->_get_rpc_route_data_for_version(
627 627
                 $version,
628 628
                 $hidden_endpoint
629 629
             );
@@ -749,7 +749,7 @@  discard block
 block discarded – undo
749 749
     {
750 750
         $default_orderby = array();
751 751
         foreach ($model->get_combined_primary_key_fields() as $key_field) {
752
-            $default_orderby[ $key_field->get_name() ] = 'ASC';
752
+            $default_orderby[$key_field->get_name()] = 'ASC';
753 753
         }
754 754
         return array_merge(
755 755
             $this->_get_response_selection_query_params($model, $version),
@@ -783,7 +783,7 @@  discard block
 block discarded – undo
783 783
                     'type'              => array(
784 784
                         'object',
785 785
                         'string',
786
-                    ),// because we accept a variety of types, WP core validation and sanitization
786
+                    ), // because we accept a variety of types, WP core validation and sanitization
787 787
                     // freaks out. We'll just validate this argument while handling the request
788 788
                     'validate_callback' => null,
789 789
                     'sanitize_callback' => null,
@@ -881,7 +881,7 @@  discard block
 block discarded – undo
881 881
                 $sanitize_callback = null;
882 882
             }
883 883
             $arg_info['sanitize_callback'] = $sanitize_callback;
884
-            $args_info[ $field_name ] = $arg_info;
884
+            $args_info[$field_name] = $arg_info;
885 885
             if ($field_obj instanceof EE_Datetime_Field) {
886 886
                 $gmt_arg_info = $arg_info;
887 887
                 $gmt_arg_info['description'] = sprintf(
@@ -892,7 +892,7 @@  discard block
 block discarded – undo
892 892
                     $field_obj->get_nicename(),
893 893
                     $field_name
894 894
                 );
895
-                $args_info[ $field_name . '_gmt' ] = $gmt_arg_info;
895
+                $args_info[$field_name.'_gmt'] = $gmt_arg_info;
896 896
             }
897 897
         }
898 898
         return $args_info;
@@ -915,16 +915,16 @@  discard block
 block discarded – undo
915 915
     public static function default_sanitize_callback($value, WP_REST_Request $request, $param)
916 916
     {
917 917
         $attributes = $request->get_attributes();
918
-        if (! isset($attributes['args'][ $param ])
919
-            || ! is_array($attributes['args'][ $param ])) {
918
+        if ( ! isset($attributes['args'][$param])
919
+            || ! is_array($attributes['args'][$param])) {
920 920
             $validation_result = true;
921 921
         } else {
922
-            $args = $attributes['args'][ $param ];
922
+            $args = $attributes['args'][$param];
923 923
             if ((
924 924
                     $value === ''
925 925
                     || $value === null
926 926
                 )
927
-                && (! isset($args['required'])
927
+                && ( ! isset($args['required'])
928 928
                     || $args['required'] === false
929 929
                 )
930 930
             ) {
@@ -934,7 +934,7 @@  discard block
 block discarded – undo
934 934
                       && $args['format'] === 'email'
935 935
             ) {
936 936
                 $validation_result = true;
937
-                if (! self::_validate_email($value)) {
937
+                if ( ! self::_validate_email($value)) {
938 938
                     $validation_result = new WP_Error(
939 939
                         'rest_invalid_param',
940 940
                         esc_html__(
@@ -984,7 +984,7 @@  discard block
 block discarded – undo
984 984
     {
985 985
         $config_routes = array();
986 986
         foreach (self::versions_served() as $version => $hidden_endpoint) {
987
-            $config_routes[ self::ee_api_namespace . $version ] = $this->_get_config_route_data_for_version(
987
+            $config_routes[self::ee_api_namespace.$version] = $this->_get_config_route_data_for_version(
988 988
                 $version,
989 989
                 $hidden_endpoint
990 990
             );
@@ -1039,7 +1039,7 @@  discard block
 block discarded – undo
1039 1039
     {
1040 1040
         $meta_routes = array();
1041 1041
         foreach (self::versions_served() as $version => $hidden_endpoint) {
1042
-            $meta_routes[ self::ee_api_namespace . $version ] = $this->_get_meta_route_data_for_version(
1042
+            $meta_routes[self::ee_api_namespace.$version] = $this->_get_meta_route_data_for_version(
1043 1043
                 $version,
1044 1044
                 $hidden_endpoint
1045 1045
             );
@@ -1091,7 +1091,7 @@  discard block
 block discarded – undo
1091 1091
             foreach ($relative_urls as $resource_name => $endpoints) {
1092 1092
                 foreach ($endpoints as $key => $endpoint) {
1093 1093
                     // skip schema and other route options
1094
-                    if (! is_numeric($key)) {
1094
+                    if ( ! is_numeric($key)) {
1095 1095
                         continue;
1096 1096
                     }
1097 1097
                     // by default, hide "hidden_endpoint"s, unless the request indicates
@@ -1100,9 +1100,9 @@  discard block
 block discarded – undo
1100 1100
                     if (($force_show_ee_namespace !== '' && $force_show_ee_namespace !== $namespace)
1101 1101
                         || ($endpoint['hidden_endpoint'] && $force_show_ee_namespace === '')
1102 1102
                     ) {
1103
-                        $full_route = '/' . ltrim($namespace, '/');
1104
-                        $full_route .= '/' . ltrim($resource_name, '/');
1105
-                        unset($route_data[ $full_route ]);
1103
+                        $full_route = '/'.ltrim($namespace, '/');
1104
+                        $full_route .= '/'.ltrim($resource_name, '/');
1105
+                        unset($route_data[$full_route]);
1106 1106
                     }
1107 1107
                 }
1108 1108
             }
@@ -1175,19 +1175,19 @@  discard block
 block discarded – undo
1175 1175
             // if it's not above the current core version, and it's compatible with the current version of core
1176 1176
             if ($key_versioned_endpoint === $latest_version) {
1177 1177
                 // don't hide the latest version in the index
1178
-                $versions_served[ $key_versioned_endpoint ] = false;
1178
+                $versions_served[$key_versioned_endpoint] = false;
1179 1179
             } elseif ($key_versioned_endpoint >= $lowest_compatible_version
1180 1180
                 && $key_versioned_endpoint < EED_Core_Rest_Api::core_version()
1181 1181
             ) {
1182 1182
                 // include, but hide, previous versions which are still supported
1183
-                $versions_served[ $key_versioned_endpoint ] = true;
1183
+                $versions_served[$key_versioned_endpoint] = true;
1184 1184
             } elseif (apply_filters(
1185 1185
                 'FHEE__EED_Core_Rest_Api__versions_served__include_incompatible_versions',
1186 1186
                 false,
1187 1187
                 $possibly_served_versions
1188 1188
             )) {
1189 1189
                 // if a version is no longer supported, don't include it in index or list of versions served
1190
-                $versions_served[ $key_versioned_endpoint ] = true;
1190
+                $versions_served[$key_versioned_endpoint] = true;
1191 1191
             }
1192 1192
         }
1193 1193
         return $versions_served;
@@ -1245,7 +1245,7 @@  discard block
 block discarded – undo
1245 1245
         $model_names = self::model_names_with_plural_routes($version);
1246 1246
         $collection_routes = array();
1247 1247
         foreach ($model_names as $model_name => $model_class_name) {
1248
-            $collection_routes[ strtolower($model_name) ] = '/' . self::ee_api_namespace . $version . '/'
1248
+            $collection_routes[strtolower($model_name)] = '/'.self::ee_api_namespace.$version.'/'
1249 1249
                                                             . EEH_Inflector::pluralize_and_lower($model_name);
1250 1250
         }
1251 1251
         return $collection_routes;
@@ -1266,9 +1266,9 @@  discard block
 block discarded – undo
1266 1266
             $primary_keys = $model_class_name::instance()->get_combined_primary_key_fields();
1267 1267
             foreach ($primary_keys as $primary_key_name => $primary_key_field) {
1268 1268
                 if (count($primary_keys) > 1) {
1269
-                    $primary_key_items[ strtolower($model_name) ][] = $primary_key_name;
1269
+                    $primary_key_items[strtolower($model_name)][] = $primary_key_name;
1270 1270
                 } else {
1271
-                    $primary_key_items[ strtolower($model_name) ] = $primary_key_name;
1271
+                    $primary_key_items[strtolower($model_name)] = $primary_key_name;
1272 1272
                 }
1273 1273
             }
1274 1274
         }
Please login to merge, or discard this patch.
Indentation   +1278 added lines, -1278 removed lines patch added patch discarded remove patch
@@ -23,1282 +23,1282 @@
 block discarded – undo
23 23
 class EED_Core_Rest_Api extends \EED_Module
24 24
 {
25 25
 
26
-    const ee_api_namespace = Domain::API_NAMESPACE;
27
-
28
-    const ee_api_namespace_for_regex = 'ee\/v([^/]*)\/';
29
-
30
-    const saved_routes_option_names = 'ee_core_routes';
31
-
32
-    /**
33
-     * string used in _links response bodies to make them globally unique.
34
-     *
35
-     * @see http://v2.wp-api.org/extending/linking/
36
-     */
37
-    const ee_api_link_namespace = 'https://api.eventespresso.com/';
38
-
39
-    /**
40
-     * @var CalculatedModelFields
41
-     */
42
-    protected static $_field_calculator;
43
-
44
-
45
-    /**
46
-     * @return EED_Core_Rest_Api|EED_Module
47
-     */
48
-    public static function instance()
49
-    {
50
-        self::$_field_calculator = LoaderFactory::getLoader()->load('EventEspresso\core\libraries\rest_api\CalculatedModelFields');
51
-        return parent::get_instance(__CLASS__);
52
-    }
53
-
54
-
55
-    /**
56
-     *    set_hooks - for hooking into EE Core, other modules, etc
57
-     *
58
-     * @access    public
59
-     * @return    void
60
-     */
61
-    public static function set_hooks()
62
-    {
63
-        self::set_hooks_both();
64
-    }
65
-
66
-
67
-    /**
68
-     *    set_hooks_admin - for hooking into EE Admin Core, other modules, etc
69
-     *
70
-     * @access    public
71
-     * @return    void
72
-     */
73
-    public static function set_hooks_admin()
74
-    {
75
-        self::set_hooks_both();
76
-    }
77
-
78
-
79
-    public static function set_hooks_both()
80
-    {
81
-        add_action('rest_api_init', array('EED_Core_Rest_Api', 'register_routes'), 10);
82
-        add_action('rest_api_init', array('EED_Core_Rest_Api', 'set_hooks_rest_api'), 5);
83
-        add_filter('rest_route_data', array('EED_Core_Rest_Api', 'hide_old_endpoints'), 10, 2);
84
-        add_filter(
85
-            'rest_index',
86
-            array('EventEspresso\core\libraries\rest_api\controllers\model\Meta', 'filterEeMetadataIntoIndex')
87
-        );
88
-        EED_Core_Rest_Api::invalidate_cached_route_data_on_version_change();
89
-    }
90
-
91
-
92
-    /**
93
-     * sets up hooks which only need to be included as part of REST API requests;
94
-     * other requests like to the frontend or admin etc don't need them
95
-     *
96
-     * @throws \EE_Error
97
-     */
98
-    public static function set_hooks_rest_api()
99
-    {
100
-        // set hooks which account for changes made to the API
101
-        EED_Core_Rest_Api::_set_hooks_for_changes();
102
-    }
103
-
104
-
105
-    /**
106
-     * public wrapper of _set_hooks_for_changes.
107
-     * Loads all the hooks which make requests to old versions of the API
108
-     * appear the same as they always did
109
-     *
110
-     * @throws EE_Error
111
-     */
112
-    public static function set_hooks_for_changes()
113
-    {
114
-        self::_set_hooks_for_changes();
115
-    }
116
-
117
-
118
-    /**
119
-     * Loads all the hooks which make requests to old versions of the API
120
-     * appear the same as they always did
121
-     *
122
-     * @throws EE_Error
123
-     */
124
-    protected static function _set_hooks_for_changes()
125
-    {
126
-        $folder_contents = EEH_File::get_contents_of_folders(array(EE_LIBRARIES . 'rest_api' . DS . 'changes'), false);
127
-        foreach ($folder_contents as $classname_in_namespace => $filepath) {
128
-            // ignore the base parent class
129
-            // and legacy named classes
130
-            if ($classname_in_namespace === 'ChangesInBase'
131
-                || strpos($classname_in_namespace, 'Changes_In_') === 0
132
-            ) {
133
-                continue;
134
-            }
135
-            $full_classname = 'EventEspresso\core\libraries\rest_api\changes\\' . $classname_in_namespace;
136
-            if (class_exists($full_classname)) {
137
-                $instance_of_class = new $full_classname;
138
-                if ($instance_of_class instanceof ChangesInBase) {
139
-                    $instance_of_class->setHooks();
140
-                }
141
-            }
142
-        }
143
-    }
144
-
145
-
146
-    /**
147
-     * Filters the WP routes to add our EE-related ones. This takes a bit of time
148
-     * so we actually prefer to only do it when an EE plugin is activated or upgraded
149
-     *
150
-     * @throws \EE_Error
151
-     */
152
-    public static function register_routes()
153
-    {
154
-        foreach (EED_Core_Rest_Api::get_ee_route_data() as $namespace => $relative_routes) {
155
-            foreach ($relative_routes as $relative_route => $data_for_multiple_endpoints) {
156
-                /**
157
-                 * @var array     $data_for_multiple_endpoints numerically indexed array
158
-                 *                                         but can also contain route options like {
159
-                 * @type array    $schema                      {
160
-                 * @type callable $schema_callback
161
-                 * @type array    $callback_args               arguments that will be passed to the callback, after the
162
-                 * WP_REST_Request of course
163
-                 * }
164
-                 * }
165
-                 */
166
-                // when registering routes, register all the endpoints' data at the same time
167
-                $multiple_endpoint_args = array();
168
-                foreach ($data_for_multiple_endpoints as $endpoint_key => $data_for_single_endpoint) {
169
-                    /**
170
-                     * @var array     $data_for_single_endpoint {
171
-                     * @type callable $callback
172
-                     * @type string methods
173
-                     * @type array args
174
-                     * @type array _links
175
-                     * @type array    $callback_args            arguments that will be passed to the callback, after the
176
-                     * WP_REST_Request of course
177
-                     * }
178
-                     */
179
-                    // skip route options
180
-                    if (! is_numeric($endpoint_key)) {
181
-                        continue;
182
-                    }
183
-                    if (! isset($data_for_single_endpoint['callback'], $data_for_single_endpoint['methods'])) {
184
-                        throw new EE_Error(
185
-                            esc_html__(
186
-                            // @codingStandardsIgnoreStart
187
-                                'Endpoint configuration data needs to have entries "callback" (callable) and "methods" (comma-separated list of accepts HTTP methods).',
188
-                                // @codingStandardsIgnoreEnd
189
-                                'event_espresso'
190
-                            )
191
-                        );
192
-                    }
193
-                    $callback = $data_for_single_endpoint['callback'];
194
-                    $single_endpoint_args = array(
195
-                        'methods' => $data_for_single_endpoint['methods'],
196
-                        'args'    => isset($data_for_single_endpoint['args']) ? $data_for_single_endpoint['args']
197
-                            : array(),
198
-                    );
199
-                    if (isset($data_for_single_endpoint['_links'])) {
200
-                        $single_endpoint_args['_links'] = $data_for_single_endpoint['_links'];
201
-                    }
202
-                    if (isset($data_for_single_endpoint['callback_args'])) {
203
-                        $callback_args = $data_for_single_endpoint['callback_args'];
204
-                        $single_endpoint_args['callback'] = function (\WP_REST_Request $request) use (
205
-                            $callback,
206
-                            $callback_args
207
-                        ) {
208
-                            array_unshift($callback_args, $request);
209
-                            return call_user_func_array(
210
-                                $callback,
211
-                                $callback_args
212
-                            );
213
-                        };
214
-                    } else {
215
-                        $single_endpoint_args['callback'] = $data_for_single_endpoint['callback'];
216
-                    }
217
-                    $multiple_endpoint_args[] = $single_endpoint_args;
218
-                }
219
-                if (isset($data_for_multiple_endpoints['schema'])) {
220
-                    $schema_route_data = $data_for_multiple_endpoints['schema'];
221
-                    $schema_callback = $schema_route_data['schema_callback'];
222
-                    $callback_args = $schema_route_data['callback_args'];
223
-                    $multiple_endpoint_args['schema'] = function () use ($schema_callback, $callback_args) {
224
-                        return call_user_func_array(
225
-                            $schema_callback,
226
-                            $callback_args
227
-                        );
228
-                    };
229
-                }
230
-                register_rest_route(
231
-                    $namespace,
232
-                    $relative_route,
233
-                    $multiple_endpoint_args
234
-                );
235
-            }
236
-        }
237
-    }
238
-
239
-
240
-    /**
241
-     * Checks if there was a version change or something that merits invalidating the cached
242
-     * route data. If so, invalidates the cached route data so that it gets refreshed
243
-     * next time the WP API is used
244
-     */
245
-    public static function invalidate_cached_route_data_on_version_change()
246
-    {
247
-        if (EE_System::instance()->detect_req_type() !== EE_System::req_type_normal) {
248
-            EED_Core_Rest_Api::invalidate_cached_route_data();
249
-        }
250
-        foreach (EE_Registry::instance()->addons as $addon) {
251
-            if ($addon instanceof EE_Addon && $addon->detect_req_type() !== EE_System::req_type_normal) {
252
-                EED_Core_Rest_Api::invalidate_cached_route_data();
253
-            }
254
-        }
255
-    }
256
-
257
-
258
-    /**
259
-     * Removes the cached route data so it will get refreshed next time the WP API is used
260
-     */
261
-    public static function invalidate_cached_route_data()
262
-    {
263
-        // delete the saved EE REST API routes
264
-        foreach (EED_Core_Rest_Api::versions_served() as $version => $hidden) {
265
-            delete_option(EED_Core_Rest_Api::saved_routes_option_names . $version);
266
-        }
267
-    }
268
-
269
-
270
-    /**
271
-     * Gets the EE route data
272
-     *
273
-     * @return array top-level key is the namespace, next-level key is the route and its value is array{
274
-     * @throws \EE_Error
275
-     * @type string|array $callback
276
-     * @type string       $methods
277
-     * @type boolean      $hidden_endpoint
278
-     * }
279
-     */
280
-    public static function get_ee_route_data()
281
-    {
282
-        $ee_routes = array();
283
-        foreach (self::versions_served() as $version => $hidden_endpoints) {
284
-            $ee_routes[ self::ee_api_namespace . $version ] = self::_get_ee_route_data_for_version(
285
-                $version,
286
-                $hidden_endpoints
287
-            );
288
-        }
289
-        return $ee_routes;
290
-    }
291
-
292
-
293
-    /**
294
-     * Gets the EE route data from the wp options if it exists already,
295
-     * otherwise re-generates it and saves it to the option
296
-     *
297
-     * @param string  $version
298
-     * @param boolean $hidden_endpoints
299
-     * @return array
300
-     * @throws \EE_Error
301
-     */
302
-    protected static function _get_ee_route_data_for_version($version, $hidden_endpoints = false)
303
-    {
304
-        $ee_routes = get_option(self::saved_routes_option_names . $version, null);
305
-        if (! $ee_routes || EED_Core_Rest_Api::debugMode()) {
306
-            $ee_routes = self::_save_ee_route_data_for_version($version, $hidden_endpoints);
307
-        }
308
-        return $ee_routes;
309
-    }
310
-
311
-
312
-    /**
313
-     * Saves the EE REST API route data to a wp option and returns it
314
-     *
315
-     * @param string  $version
316
-     * @param boolean $hidden_endpoints
317
-     * @return mixed|null
318
-     * @throws \EE_Error
319
-     */
320
-    protected static function _save_ee_route_data_for_version($version, $hidden_endpoints = false)
321
-    {
322
-        $instance = self::instance();
323
-        $routes = apply_filters(
324
-            'EED_Core_Rest_Api__save_ee_route_data_for_version__routes',
325
-            array_replace_recursive(
326
-                $instance->_get_config_route_data_for_version($version, $hidden_endpoints),
327
-                $instance->_get_meta_route_data_for_version($version, $hidden_endpoints),
328
-                $instance->_get_model_route_data_for_version($version, $hidden_endpoints),
329
-                $instance->_get_rpc_route_data_for_version($version, $hidden_endpoints)
330
-            )
331
-        );
332
-        $option_name = self::saved_routes_option_names . $version;
333
-        if (get_option($option_name)) {
334
-            update_option($option_name, $routes, true);
335
-        } else {
336
-            add_option($option_name, $routes, null, 'no');
337
-        }
338
-        return $routes;
339
-    }
340
-
341
-
342
-    /**
343
-     * Calculates all the EE routes and saves it to a WordPress option so we don't
344
-     * need to calculate it on every request
345
-     *
346
-     * @deprecated since version 4.9.1
347
-     * @return void
348
-     */
349
-    public static function save_ee_routes()
350
-    {
351
-        if (EE_Maintenance_Mode::instance()->models_can_query()) {
352
-            $instance = self::instance();
353
-            $routes = apply_filters(
354
-                'EED_Core_Rest_Api__save_ee_routes__routes',
355
-                array_replace_recursive(
356
-                    $instance->_register_config_routes(),
357
-                    $instance->_register_meta_routes(),
358
-                    $instance->_register_model_routes(),
359
-                    $instance->_register_rpc_routes()
360
-                )
361
-            );
362
-            update_option(self::saved_routes_option_names, $routes, true);
363
-        }
364
-    }
365
-
366
-
367
-    /**
368
-     * Gets all the route information relating to EE models
369
-     *
370
-     * @return array @see get_ee_route_data
371
-     * @deprecated since version 4.9.1
372
-     */
373
-    protected function _register_model_routes()
374
-    {
375
-        $model_routes = array();
376
-        foreach (self::versions_served() as $version => $hidden_endpoint) {
377
-            $model_routes[ EED_Core_Rest_Api::ee_api_namespace
378
-                           . $version ] = $this->_get_config_route_data_for_version($version, $hidden_endpoint);
379
-        }
380
-        return $model_routes;
381
-    }
382
-
383
-
384
-    /**
385
-     * Decides whether or not to add write endpoints for this model.
386
-     *
387
-     * Currently, this defaults to exclude all global tables and models
388
-     * which would allow inserting WP core data (we don't want to duplicate
389
-     * what WP API does, as it's unnecessary, extra work, and potentially extra bugs)
390
-     *
391
-     * @param EEM_Base $model
392
-     * @return bool
393
-     */
394
-    public static function should_have_write_endpoints(EEM_Base $model)
395
-    {
396
-        if ($model->is_wp_core_model()) {
397
-            return false;
398
-        }
399
-        foreach ($model->get_tables() as $table) {
400
-            if ($table->is_global()) {
401
-                return false;
402
-            }
403
-        }
404
-        return true;
405
-    }
406
-
407
-
408
-    /**
409
-     * Gets the names of all models which should have plural routes (eg `ee/v4.8.36/events`)
410
-     * in this versioned namespace of EE4
411
-     *
412
-     * @param $version
413
-     * @return array keys are model names (eg 'Event') and values ar either classnames (eg 'EEM_Event')
414
-     */
415
-    public static function model_names_with_plural_routes($version)
416
-    {
417
-        $model_version_info = new ModelVersionInfo($version);
418
-        $models_to_register = $model_version_info->modelsForRequestedVersion();
419
-        // let's not bother having endpoints for extra metas
420
-        unset(
421
-            $models_to_register['Extra_Meta'],
422
-            $models_to_register['Extra_Join'],
423
-            $models_to_register['Post_Meta']
424
-        );
425
-        return apply_filters(
426
-            'FHEE__EED_Core_REST_API___register_model_routes',
427
-            $models_to_register
428
-        );
429
-    }
430
-
431
-
432
-    /**
433
-     * Gets the route data for EE models in the specified version
434
-     *
435
-     * @param string  $version
436
-     * @param boolean $hidden_endpoint
437
-     * @return array
438
-     * @throws EE_Error
439
-     */
440
-    protected function _get_model_route_data_for_version($version, $hidden_endpoint = false)
441
-    {
442
-        $model_routes = array();
443
-        $model_version_info = new ModelVersionInfo($version);
444
-        foreach (EED_Core_Rest_Api::model_names_with_plural_routes($version) as $model_name => $model_classname) {
445
-            $model = \EE_Registry::instance()->load_model($model_name);
446
-            // if this isn't a valid model then let's skip iterate to the next item in the loop.
447
-            if (! $model instanceof EEM_Base) {
448
-                continue;
449
-            }
450
-            // yes we could just register one route for ALL models, but then they wouldn't show up in the index
451
-            $plural_model_route = EED_Core_Rest_Api::get_collection_route($model);
452
-            $singular_model_route = EED_Core_Rest_Api::get_entity_route($model, '(?P<id>[^\/]+)');
453
-            $model_routes[ $plural_model_route ] = array(
454
-                array(
455
-                    'callback'        => array(
456
-                        'EventEspresso\core\libraries\rest_api\controllers\model\Read',
457
-                        'handleRequestGetAll',
458
-                    ),
459
-                    'callback_args'   => array($version, $model_name),
460
-                    'methods'         => WP_REST_Server::READABLE,
461
-                    'hidden_endpoint' => $hidden_endpoint,
462
-                    'args'            => $this->_get_read_query_params($model, $version),
463
-                    '_links'          => array(
464
-                        'self' => rest_url(EED_Core_Rest_Api::ee_api_namespace . $version . $singular_model_route),
465
-                    ),
466
-                ),
467
-                'schema' => array(
468
-                    'schema_callback' => array(
469
-                        'EventEspresso\core\libraries\rest_api\controllers\model\Read',
470
-                        'handleSchemaRequest',
471
-                    ),
472
-                    'callback_args'   => array($version, $model_name),
473
-                ),
474
-            );
475
-            $model_routes[ $singular_model_route ] = array(
476
-                array(
477
-                    'callback'        => array(
478
-                        'EventEspresso\core\libraries\rest_api\controllers\model\Read',
479
-                        'handleRequestGetOne',
480
-                    ),
481
-                    'callback_args'   => array($version, $model_name),
482
-                    'methods'         => WP_REST_Server::READABLE,
483
-                    'hidden_endpoint' => $hidden_endpoint,
484
-                    'args'            => $this->_get_response_selection_query_params($model, $version),
485
-                ),
486
-            );
487
-            if (apply_filters(
488
-                'FHEE__EED_Core_Rest_Api___get_model_route_data_for_version__add_write_endpoints',
489
-                EED_Core_Rest_Api::should_have_write_endpoints($model),
490
-                $model
491
-            )) {
492
-                $model_routes[ $plural_model_route ][] = array(
493
-                    'callback'        => array(
494
-                        'EventEspresso\core\libraries\rest_api\controllers\model\Write',
495
-                        'handleRequestInsert',
496
-                    ),
497
-                    'callback_args'   => array($version, $model_name),
498
-                    'methods'         => WP_REST_Server::CREATABLE,
499
-                    'hidden_endpoint' => $hidden_endpoint,
500
-                    'args'            => $this->_get_write_params($model_name, $model_version_info, true),
501
-                );
502
-                $model_routes[ $singular_model_route ] = array_merge(
503
-                    $model_routes[ $singular_model_route ],
504
-                    array(
505
-                        array(
506
-                            'callback'        => array(
507
-                                'EventEspresso\core\libraries\rest_api\controllers\model\Write',
508
-                                'handleRequestUpdate',
509
-                            ),
510
-                            'callback_args'   => array($version, $model_name),
511
-                            'methods'         => WP_REST_Server::EDITABLE,
512
-                            'hidden_endpoint' => $hidden_endpoint,
513
-                            'args'            => $this->_get_write_params($model_name, $model_version_info),
514
-                        ),
515
-                        array(
516
-                            'callback'        => array(
517
-                                'EventEspresso\core\libraries\rest_api\controllers\model\Write',
518
-                                'handleRequestDelete',
519
-                            ),
520
-                            'callback_args'   => array($version, $model_name),
521
-                            'methods'         => WP_REST_Server::DELETABLE,
522
-                            'hidden_endpoint' => $hidden_endpoint,
523
-                            'args'            => $this->_get_delete_query_params($model, $version),
524
-                        ),
525
-                    )
526
-                );
527
-            }
528
-            foreach ($model->relation_settings() as $relation_name => $relation_obj) {
529
-                $related_route = EED_Core_Rest_Api::get_relation_route_via(
530
-                    $model,
531
-                    '(?P<id>[^\/]+)',
532
-                    $relation_obj
533
-                );
534
-                $endpoints = array(
535
-                    array(
536
-                        'callback'        => array(
537
-                            'EventEspresso\core\libraries\rest_api\controllers\model\Read',
538
-                            'handleRequestGetRelated',
539
-                        ),
540
-                        'callback_args'   => array($version, $model_name, $relation_name),
541
-                        'methods'         => WP_REST_Server::READABLE,
542
-                        'hidden_endpoint' => $hidden_endpoint,
543
-                        'args'            => $this->_get_read_query_params($relation_obj->get_other_model(), $version),
544
-                    ),
545
-                );
546
-                $model_routes[ $related_route ] = $endpoints;
547
-            }
548
-        }
549
-        return $model_routes;
550
-    }
551
-
552
-
553
-    /**
554
-     * Gets the relative URI to a model's REST API plural route, after the EE4 versioned namespace,
555
-     * excluding the preceding slash.
556
-     * Eg you pass get_plural_route_to('Event') = 'events'
557
-     *
558
-     * @param EEM_Base $model
559
-     * @return string
560
-     */
561
-    public static function get_collection_route(EEM_Base $model)
562
-    {
563
-        return EEH_Inflector::pluralize_and_lower($model->get_this_model_name());
564
-    }
565
-
566
-
567
-    /**
568
-     * Gets the relative URI to a model's REST API singular route, after the EE4 versioned namespace,
569
-     * excluding the preceding slash.
570
-     * Eg you pass get_plural_route_to('Event', 12) = 'events/12'
571
-     *
572
-     * @param EEM_Base $model eg Event or Venue
573
-     * @param string   $id
574
-     * @return string
575
-     */
576
-    public static function get_entity_route($model, $id)
577
-    {
578
-        return EED_Core_Rest_Api::get_collection_route($model) . '/' . $id;
579
-    }
580
-
581
-
582
-    /**
583
-     * Gets the relative URI to a model's REST API singular route, after the EE4 versioned namespace,
584
-     * excluding the preceding slash.
585
-     * Eg you pass get_plural_route_to('Event', 12) = 'events/12'
586
-     *
587
-     * @param EEM_Base               $model eg Event or Venue
588
-     * @param string                 $id
589
-     * @param EE_Model_Relation_Base $relation_obj
590
-     * @return string
591
-     */
592
-    public static function get_relation_route_via(EEM_Base $model, $id, EE_Model_Relation_Base $relation_obj)
593
-    {
594
-        $related_model_name_endpoint_part = ModelRead::getRelatedEntityName(
595
-            $relation_obj->get_other_model()->get_this_model_name(),
596
-            $relation_obj
597
-        );
598
-        return EED_Core_Rest_Api::get_entity_route($model, $id) . '/' . $related_model_name_endpoint_part;
599
-    }
600
-
601
-
602
-    /**
603
-     * Adds onto the $relative_route the EE4 REST API versioned namespace.
604
-     * Eg if given '4.8.36' and 'events', will return 'ee/v4.8.36/events'
605
-     *
606
-     * @param string $relative_route
607
-     * @param string $version
608
-     * @return string
609
-     */
610
-    public static function get_versioned_route_to($relative_route, $version = '4.8.36')
611
-    {
612
-        return '/' . EED_Core_Rest_Api::ee_api_namespace . $version . '/' . $relative_route;
613
-    }
614
-
615
-
616
-    /**
617
-     * Adds all the RPC-style routes (remote procedure call-like routes, ie
618
-     * routes that don't conform to the traditional REST CRUD-style).
619
-     *
620
-     * @deprecated since 4.9.1
621
-     */
622
-    protected function _register_rpc_routes()
623
-    {
624
-        $routes = array();
625
-        foreach (self::versions_served() as $version => $hidden_endpoint) {
626
-            $routes[ self::ee_api_namespace . $version ] = $this->_get_rpc_route_data_for_version(
627
-                $version,
628
-                $hidden_endpoint
629
-            );
630
-        }
631
-        return $routes;
632
-    }
633
-
634
-
635
-    /**
636
-     * @param string  $version
637
-     * @param boolean $hidden_endpoint
638
-     * @return array
639
-     */
640
-    protected function _get_rpc_route_data_for_version($version, $hidden_endpoint = false)
641
-    {
642
-        $this_versions_routes = array();
643
-        // checkin endpoint
644
-        $this_versions_routes['registrations/(?P<REG_ID>\d+)/toggle_checkin_for_datetime/(?P<DTT_ID>\d+)'] = array(
645
-            array(
646
-                'callback'        => array(
647
-                    'EventEspresso\core\libraries\rest_api\controllers\rpc\Checkin',
648
-                    'handleRequestToggleCheckin',
649
-                ),
650
-                'methods'         => WP_REST_Server::CREATABLE,
651
-                'hidden_endpoint' => $hidden_endpoint,
652
-                'args'            => array(
653
-                    'force' => array(
654
-                        'required'    => false,
655
-                        'default'     => false,
656
-                        'description' => __(
657
-                        // @codingStandardsIgnoreStart
658
-                            'Whether to force toggle checkin, or to verify the registration status and allowed ticket uses',
659
-                            // @codingStandardsIgnoreEnd
660
-                            'event_espresso'
661
-                        ),
662
-                    ),
663
-                ),
664
-                'callback_args'   => array($version),
665
-            ),
666
-        );
667
-        return apply_filters(
668
-            'FHEE__EED_Core_Rest_Api___register_rpc_routes__this_versions_routes',
669
-            $this_versions_routes,
670
-            $version,
671
-            $hidden_endpoint
672
-        );
673
-    }
674
-
675
-
676
-    /**
677
-     * Gets the query params that can be used when request one or many
678
-     *
679
-     * @param EEM_Base $model
680
-     * @param string   $version
681
-     * @return array
682
-     */
683
-    protected function _get_response_selection_query_params(\EEM_Base $model, $version)
684
-    {
685
-        return apply_filters(
686
-            'FHEE__EED_Core_Rest_Api___get_response_selection_query_params',
687
-            array(
688
-                'include'   => array(
689
-                    'required' => false,
690
-                    'default'  => '*',
691
-                    'type'     => 'string',
692
-                ),
693
-                'calculate' => array(
694
-                    'required'          => false,
695
-                    'default'           => '',
696
-                    'enum'              => self::$_field_calculator->retrieveCalculatedFieldsForModel($model),
697
-                    'type'              => 'string',
698
-                    // because we accept a CSV'd list of the enumerated strings, WP core validation and sanitization
699
-                    // freaks out. We'll just validate this argument while handling the request
700
-                    'validate_callback' => null,
701
-                    'sanitize_callback' => null,
702
-                ),
703
-            ),
704
-            $model,
705
-            $version
706
-        );
707
-    }
708
-
709
-
710
-    /**
711
-     * Gets the parameters acceptable for delete requests
712
-     *
713
-     * @param \EEM_Base $model
714
-     * @param string    $version
715
-     * @return array
716
-     */
717
-    protected function _get_delete_query_params(\EEM_Base $model, $version)
718
-    {
719
-        $params_for_delete = array(
720
-            'allow_blocking' => array(
721
-                'required' => false,
722
-                'default'  => true,
723
-                'type'     => 'boolean',
724
-            ),
725
-        );
726
-        $params_for_delete['force'] = array(
727
-            'required' => false,
728
-            'default'  => false,
729
-            'type'     => 'boolean',
730
-        );
731
-        return apply_filters(
732
-            'FHEE__EED_Core_Rest_Api___get_delete_query_params',
733
-            $params_for_delete,
734
-            $model,
735
-            $version
736
-        );
737
-    }
738
-
739
-
740
-    /**
741
-     * Gets info about reading query params that are acceptable
742
-     *
743
-     * @param \EEM_Base $model eg 'Event' or 'Venue'
744
-     * @param  string   $version
745
-     * @return array    describing the args acceptable when querying this model
746
-     * @throws EE_Error
747
-     */
748
-    protected function _get_read_query_params(\EEM_Base $model, $version)
749
-    {
750
-        $default_orderby = array();
751
-        foreach ($model->get_combined_primary_key_fields() as $key_field) {
752
-            $default_orderby[ $key_field->get_name() ] = 'ASC';
753
-        }
754
-        return array_merge(
755
-            $this->_get_response_selection_query_params($model, $version),
756
-            array(
757
-                'where'    => array(
758
-                    'required'          => false,
759
-                    'default'           => array(),
760
-                    'type'              => 'object',
761
-                    // because we accept an almost infinite list of possible where conditions, WP
762
-                    // core validation and sanitization freaks out. We'll just validate this argument
763
-                    // while handling the request
764
-                    'validate_callback' => null,
765
-                    'sanitize_callback' => null,
766
-                ),
767
-                'limit'    => array(
768
-                    'required'          => false,
769
-                    'default'           => EED_Core_Rest_Api::get_default_query_limit(),
770
-                    'type'              => array(
771
-                        'array',
772
-                        'string',
773
-                        'integer',
774
-                    ),
775
-                    // because we accept a variety of types, WP core validation and sanitization
776
-                    // freaks out. We'll just validate this argument while handling the request
777
-                    'validate_callback' => null,
778
-                    'sanitize_callback' => null,
779
-                ),
780
-                'order_by' => array(
781
-                    'required'          => false,
782
-                    'default'           => $default_orderby,
783
-                    'type'              => array(
784
-                        'object',
785
-                        'string',
786
-                    ),// because we accept a variety of types, WP core validation and sanitization
787
-                    // freaks out. We'll just validate this argument while handling the request
788
-                    'validate_callback' => null,
789
-                    'sanitize_callback' => null,
790
-                ),
791
-                'group_by' => array(
792
-                    'required'          => false,
793
-                    'default'           => null,
794
-                    'type'              => array(
795
-                        'object',
796
-                        'string',
797
-                    ),
798
-                    // because we accept  an almost infinite list of possible groupings,
799
-                    // WP core validation and sanitization
800
-                    // freaks out. We'll just validate this argument while handling the request
801
-                    'validate_callback' => null,
802
-                    'sanitize_callback' => null,
803
-                ),
804
-                'having'   => array(
805
-                    'required'          => false,
806
-                    'default'           => null,
807
-                    'type'              => 'object',
808
-                    // because we accept an almost infinite list of possible where conditions, WP
809
-                    // core validation and sanitization freaks out. We'll just validate this argument
810
-                    // while handling the request
811
-                    'validate_callback' => null,
812
-                    'sanitize_callback' => null,
813
-                ),
814
-                'caps'     => array(
815
-                    'required' => false,
816
-                    'default'  => EEM_Base::caps_read,
817
-                    'type'     => 'string',
818
-                    'enum'     => array(
819
-                        EEM_Base::caps_read,
820
-                        EEM_Base::caps_read_admin,
821
-                        EEM_Base::caps_edit,
822
-                        EEM_Base::caps_delete,
823
-                    ),
824
-                ),
825
-            )
826
-        );
827
-    }
828
-
829
-
830
-    /**
831
-     * Gets parameter information for a model regarding writing data
832
-     *
833
-     * @param string           $model_name
834
-     * @param ModelVersionInfo $model_version_info
835
-     * @param boolean          $create                                       whether this is for request to create (in
836
-     *                                                                       which case we need all required params) or
837
-     *                                                                       just to update (in which case we don't
838
-     *                                                                       need those on every request)
839
-     * @return array
840
-     */
841
-    protected function _get_write_params(
842
-        $model_name,
843
-        ModelVersionInfo $model_version_info,
844
-        $create = false
845
-    ) {
846
-        $model = EE_Registry::instance()->load_model($model_name);
847
-        $fields = $model_version_info->fieldsOnModelInThisVersion($model);
848
-        $args_info = array();
849
-        foreach ($fields as $field_name => $field_obj) {
850
-            if ($field_obj->is_auto_increment()) {
851
-                // totally ignore auto increment IDs
852
-                continue;
853
-            }
854
-            $arg_info = $field_obj->getSchema();
855
-            $required = $create && ! $field_obj->is_nullable() && $field_obj->get_default_value() === null;
856
-            $arg_info['required'] = $required;
857
-            // remove the read-only flag. If it were read-only we wouldn't list it as an argument while writing, right?
858
-            unset($arg_info['readonly']);
859
-            $schema_properties = $field_obj->getSchemaProperties();
860
-            if (isset($schema_properties['raw'])
861
-                && $field_obj->getSchemaType() === 'object'
862
-            ) {
863
-                // if there's a "raw" form of this argument, use those properties instead
864
-                $arg_info = array_replace(
865
-                    $arg_info,
866
-                    $schema_properties['raw']
867
-                );
868
-            }
869
-            $arg_info['default'] = ModelDataTranslator::prepareFieldValueForJson(
870
-                $field_obj,
871
-                $field_obj->get_default_value(),
872
-                $model_version_info->requestedVersion()
873
-            );
874
-            // we do our own validation and sanitization within the controller
875
-            if (function_exists('rest_validate_value_from_schema')) {
876
-                $sanitize_callback = array(
877
-                    'EED_Core_Rest_Api',
878
-                    'default_sanitize_callback',
879
-                );
880
-            } else {
881
-                $sanitize_callback = null;
882
-            }
883
-            $arg_info['sanitize_callback'] = $sanitize_callback;
884
-            $args_info[ $field_name ] = $arg_info;
885
-            if ($field_obj instanceof EE_Datetime_Field) {
886
-                $gmt_arg_info = $arg_info;
887
-                $gmt_arg_info['description'] = sprintf(
888
-                    esc_html__(
889
-                        '%1$s - the value for this field in UTC. Ignored if %2$s is provided.',
890
-                        'event_espresso'
891
-                    ),
892
-                    $field_obj->get_nicename(),
893
-                    $field_name
894
-                );
895
-                $args_info[ $field_name . '_gmt' ] = $gmt_arg_info;
896
-            }
897
-        }
898
-        return $args_info;
899
-    }
900
-
901
-
902
-    /**
903
-     * Replacement for WP API's 'rest_parse_request_arg'.
904
-     * If the value is blank but not required, don't bother validating it.
905
-     * Also, it uses our email validation instead of WP API's default.
906
-     *
907
-     * @param                 $value
908
-     * @param WP_REST_Request $request
909
-     * @param                 $param
910
-     * @return bool|true|WP_Error
911
-     * @throws InvalidArgumentException
912
-     * @throws InvalidInterfaceException
913
-     * @throws InvalidDataTypeException
914
-     */
915
-    public static function default_sanitize_callback($value, WP_REST_Request $request, $param)
916
-    {
917
-        $attributes = $request->get_attributes();
918
-        if (! isset($attributes['args'][ $param ])
919
-            || ! is_array($attributes['args'][ $param ])) {
920
-            $validation_result = true;
921
-        } else {
922
-            $args = $attributes['args'][ $param ];
923
-            if ((
924
-                    $value === ''
925
-                    || $value === null
926
-                )
927
-                && (! isset($args['required'])
928
-                    || $args['required'] === false
929
-                )
930
-            ) {
931
-                // not required and not provided? that's cool
932
-                $validation_result = true;
933
-            } elseif (isset($args['format'])
934
-                      && $args['format'] === 'email'
935
-            ) {
936
-                $validation_result = true;
937
-                if (! self::_validate_email($value)) {
938
-                    $validation_result = new WP_Error(
939
-                        'rest_invalid_param',
940
-                        esc_html__(
941
-                            'The email address is not valid or does not exist.',
942
-                            'event_espresso'
943
-                        )
944
-                    );
945
-                }
946
-            } else {
947
-                $validation_result = rest_validate_value_from_schema($value, $args, $param);
948
-            }
949
-        }
950
-        if (is_wp_error($validation_result)) {
951
-            return $validation_result;
952
-        }
953
-        return rest_sanitize_request_arg($value, $request, $param);
954
-    }
955
-
956
-
957
-    /**
958
-     * Returns whether or not this email address is valid. Copied from EE_Email_Validation_Strategy::_validate_email()
959
-     *
960
-     * @param $email
961
-     * @return bool
962
-     * @throws InvalidArgumentException
963
-     * @throws InvalidInterfaceException
964
-     * @throws InvalidDataTypeException
965
-     */
966
-    protected static function _validate_email($email)
967
-    {
968
-        try {
969
-            EmailAddressFactory::create($email);
970
-            return true;
971
-        } catch (EmailValidationException $e) {
972
-            return false;
973
-        }
974
-    }
975
-
976
-
977
-    /**
978
-     * Gets routes for the config
979
-     *
980
-     * @return array @see _register_model_routes
981
-     * @deprecated since version 4.9.1
982
-     */
983
-    protected function _register_config_routes()
984
-    {
985
-        $config_routes = array();
986
-        foreach (self::versions_served() as $version => $hidden_endpoint) {
987
-            $config_routes[ self::ee_api_namespace . $version ] = $this->_get_config_route_data_for_version(
988
-                $version,
989
-                $hidden_endpoint
990
-            );
991
-        }
992
-        return $config_routes;
993
-    }
994
-
995
-
996
-    /**
997
-     * Gets routes for the config for the specified version
998
-     *
999
-     * @param string  $version
1000
-     * @param boolean $hidden_endpoint
1001
-     * @return array
1002
-     */
1003
-    protected function _get_config_route_data_for_version($version, $hidden_endpoint)
1004
-    {
1005
-        return array(
1006
-            'config'    => array(
1007
-                array(
1008
-                    'callback'        => array(
1009
-                        'EventEspresso\core\libraries\rest_api\controllers\config\Read',
1010
-                        'handleRequest',
1011
-                    ),
1012
-                    'methods'         => WP_REST_Server::READABLE,
1013
-                    'hidden_endpoint' => $hidden_endpoint,
1014
-                    'callback_args'   => array($version),
1015
-                ),
1016
-            ),
1017
-            'site_info' => array(
1018
-                array(
1019
-                    'callback'        => array(
1020
-                        'EventEspresso\core\libraries\rest_api\controllers\config\Read',
1021
-                        'handleRequestSiteInfo',
1022
-                    ),
1023
-                    'methods'         => WP_REST_Server::READABLE,
1024
-                    'hidden_endpoint' => $hidden_endpoint,
1025
-                    'callback_args'   => array($version),
1026
-                ),
1027
-            ),
1028
-        );
1029
-    }
1030
-
1031
-
1032
-    /**
1033
-     * Gets the meta info routes
1034
-     *
1035
-     * @return array @see _register_model_routes
1036
-     * @deprecated since version 4.9.1
1037
-     */
1038
-    protected function _register_meta_routes()
1039
-    {
1040
-        $meta_routes = array();
1041
-        foreach (self::versions_served() as $version => $hidden_endpoint) {
1042
-            $meta_routes[ self::ee_api_namespace . $version ] = $this->_get_meta_route_data_for_version(
1043
-                $version,
1044
-                $hidden_endpoint
1045
-            );
1046
-        }
1047
-        return $meta_routes;
1048
-    }
1049
-
1050
-
1051
-    /**
1052
-     * @param string  $version
1053
-     * @param boolean $hidden_endpoint
1054
-     * @return array
1055
-     */
1056
-    protected function _get_meta_route_data_for_version($version, $hidden_endpoint = false)
1057
-    {
1058
-        return array(
1059
-            'resources' => array(
1060
-                array(
1061
-                    'callback'        => array(
1062
-                        'EventEspresso\core\libraries\rest_api\controllers\model\Meta',
1063
-                        'handleRequestModelsMeta',
1064
-                    ),
1065
-                    'methods'         => WP_REST_Server::READABLE,
1066
-                    'hidden_endpoint' => $hidden_endpoint,
1067
-                    'callback_args'   => array($version),
1068
-                ),
1069
-            ),
1070
-        );
1071
-    }
1072
-
1073
-
1074
-    /**
1075
-     * Tries to hide old 4.6 endpoints from the
1076
-     *
1077
-     * @param array $route_data
1078
-     * @return array
1079
-     * @throws \EE_Error
1080
-     */
1081
-    public static function hide_old_endpoints($route_data)
1082
-    {
1083
-        // allow API clients to override which endpoints get hidden, in case
1084
-        // they want to discover particular endpoints
1085
-        // also, we don't have access to the request so we have to just grab it from the superglobal
1086
-        $force_show_ee_namespace = ltrim(
1087
-            EEH_Array::is_set($_REQUEST, 'force_show_ee_namespace', ''),
1088
-            '/'
1089
-        );
1090
-        foreach (EED_Core_Rest_Api::get_ee_route_data() as $namespace => $relative_urls) {
1091
-            foreach ($relative_urls as $resource_name => $endpoints) {
1092
-                foreach ($endpoints as $key => $endpoint) {
1093
-                    // skip schema and other route options
1094
-                    if (! is_numeric($key)) {
1095
-                        continue;
1096
-                    }
1097
-                    // by default, hide "hidden_endpoint"s, unless the request indicates
1098
-                    // to $force_show_ee_namespace, in which case only show that one
1099
-                    // namespace's endpoints (and hide all others)
1100
-                    if (($force_show_ee_namespace !== '' && $force_show_ee_namespace !== $namespace)
1101
-                        || ($endpoint['hidden_endpoint'] && $force_show_ee_namespace === '')
1102
-                    ) {
1103
-                        $full_route = '/' . ltrim($namespace, '/');
1104
-                        $full_route .= '/' . ltrim($resource_name, '/');
1105
-                        unset($route_data[ $full_route ]);
1106
-                    }
1107
-                }
1108
-            }
1109
-        }
1110
-        return $route_data;
1111
-    }
1112
-
1113
-
1114
-    /**
1115
-     * Returns an array describing which versions of core support serving requests for.
1116
-     * Keys are core versions' major and minor version, and values are the
1117
-     * LOWEST requested version they can serve. Eg, 4.7 can serve requests for 4.6-like
1118
-     * data by just removing a few models and fields from the responses. However, 4.15 might remove
1119
-     * the answers table entirely, in which case it would be very difficult for
1120
-     * it to serve 4.6-style responses.
1121
-     * Versions of core that are missing from this array are unknowns.
1122
-     * previous ver
1123
-     *
1124
-     * @return array
1125
-     */
1126
-    public static function version_compatibilities()
1127
-    {
1128
-        return apply_filters(
1129
-            'FHEE__EED_Core_REST_API__version_compatibilities',
1130
-            array(
1131
-                '4.8.29' => '4.8.29',
1132
-                '4.8.33' => '4.8.29',
1133
-                '4.8.34' => '4.8.29',
1134
-                '4.8.36' => '4.8.29',
1135
-            )
1136
-        );
1137
-    }
1138
-
1139
-
1140
-    /**
1141
-     * Gets the latest API version served. Eg if there
1142
-     * are two versions served of the API, 4.8.29 and 4.8.32, and
1143
-     * we are on core version 4.8.34, it will return the string "4.8.32"
1144
-     *
1145
-     * @return string
1146
-     */
1147
-    public static function latest_rest_api_version()
1148
-    {
1149
-        $versions_served = \EED_Core_Rest_Api::versions_served();
1150
-        $versions_served_keys = array_keys($versions_served);
1151
-        return end($versions_served_keys);
1152
-    }
1153
-
1154
-
1155
-    /**
1156
-     * Using EED_Core_Rest_Api::version_compatibilities(), determines what version of
1157
-     * EE the API can serve requests for. Eg, if we are on 4.15 of core, and
1158
-     * we can serve requests from 4.12 or later, this will return array( '4.12', '4.13', '4.14', '4.15' ).
1159
-     * We also indicate whether or not this version should be put in the index or not
1160
-     *
1161
-     * @return array keys are API version numbers (just major and minor numbers), and values
1162
-     * are whether or not they should be hidden
1163
-     */
1164
-    public static function versions_served()
1165
-    {
1166
-        $versions_served = array();
1167
-        $possibly_served_versions = EED_Core_Rest_Api::version_compatibilities();
1168
-        $lowest_compatible_version = end($possibly_served_versions);
1169
-        reset($possibly_served_versions);
1170
-        $versions_served_historically = array_keys($possibly_served_versions);
1171
-        $latest_version = end($versions_served_historically);
1172
-        reset($versions_served_historically);
1173
-        // for each version of core we have ever served:
1174
-        foreach ($versions_served_historically as $key_versioned_endpoint) {
1175
-            // if it's not above the current core version, and it's compatible with the current version of core
1176
-            if ($key_versioned_endpoint === $latest_version) {
1177
-                // don't hide the latest version in the index
1178
-                $versions_served[ $key_versioned_endpoint ] = false;
1179
-            } elseif ($key_versioned_endpoint >= $lowest_compatible_version
1180
-                && $key_versioned_endpoint < EED_Core_Rest_Api::core_version()
1181
-            ) {
1182
-                // include, but hide, previous versions which are still supported
1183
-                $versions_served[ $key_versioned_endpoint ] = true;
1184
-            } elseif (apply_filters(
1185
-                'FHEE__EED_Core_Rest_Api__versions_served__include_incompatible_versions',
1186
-                false,
1187
-                $possibly_served_versions
1188
-            )) {
1189
-                // if a version is no longer supported, don't include it in index or list of versions served
1190
-                $versions_served[ $key_versioned_endpoint ] = true;
1191
-            }
1192
-        }
1193
-        return $versions_served;
1194
-    }
1195
-
1196
-
1197
-    /**
1198
-     * Gets the major and minor version of EE core's version string
1199
-     *
1200
-     * @return string
1201
-     */
1202
-    public static function core_version()
1203
-    {
1204
-        return apply_filters(
1205
-            'FHEE__EED_Core_REST_API__core_version',
1206
-            implode(
1207
-                '.',
1208
-                array_slice(
1209
-                    explode(
1210
-                        '.',
1211
-                        espresso_version()
1212
-                    ),
1213
-                    0,
1214
-                    3
1215
-                )
1216
-            )
1217
-        );
1218
-    }
1219
-
1220
-
1221
-    /**
1222
-     * Gets the default limit that should be used when querying for resources
1223
-     *
1224
-     * @return int
1225
-     */
1226
-    public static function get_default_query_limit()
1227
-    {
1228
-        // we actually don't use a const because we want folks to always use
1229
-        // this method, not the const directly
1230
-        return apply_filters(
1231
-            'FHEE__EED_Core_Rest_Api__get_default_query_limit',
1232
-            50
1233
-        );
1234
-    }
1235
-
1236
-
1237
-    /**
1238
-     *
1239
-     * @param string $version api version string (i.e. '4.8.36')
1240
-     * @return array
1241
-     */
1242
-    public static function getCollectionRoutesIndexedByModelName($version = '')
1243
-    {
1244
-        $version = empty($version) ? self::latest_rest_api_version() : $version;
1245
-        $model_names = self::model_names_with_plural_routes($version);
1246
-        $collection_routes = array();
1247
-        foreach ($model_names as $model_name => $model_class_name) {
1248
-            $collection_routes[ strtolower($model_name) ] = '/' . self::ee_api_namespace . $version . '/'
1249
-                                                            . EEH_Inflector::pluralize_and_lower($model_name);
1250
-        }
1251
-        return $collection_routes;
1252
-    }
1253
-
1254
-
1255
-    /**
1256
-     * Returns an array of primary key names indexed by model names.
1257
-     * @param string $version
1258
-     * @return array
1259
-     */
1260
-    public static function getPrimaryKeyNamesIndexedByModelName($version = '')
1261
-    {
1262
-        $version = empty($version) ? self::latest_rest_api_version() : $version;
1263
-        $model_names = self::model_names_with_plural_routes($version);
1264
-        $primary_key_items = array();
1265
-        foreach ($model_names as $model_name => $model_class_name) {
1266
-            $primary_keys = $model_class_name::instance()->get_combined_primary_key_fields();
1267
-            foreach ($primary_keys as $primary_key_name => $primary_key_field) {
1268
-                if (count($primary_keys) > 1) {
1269
-                    $primary_key_items[ strtolower($model_name) ][] = $primary_key_name;
1270
-                } else {
1271
-                    $primary_key_items[ strtolower($model_name) ] = $primary_key_name;
1272
-                }
1273
-            }
1274
-        }
1275
-        return $primary_key_items;
1276
-    }
1277
-
1278
-    /**
1279
-     * Determines the EE REST API debug mode is activated, or not.
1280
-     * @since 4.9.72.p
1281
-     * @return bool
1282
-     */
1283
-    public static function debugMode()
1284
-    {
1285
-        static $debug_mode = null; // could be class prop
1286
-        if ($debug_mode === null) {
1287
-            $debug_mode = defined('EE_REST_API_DEBUG_MODE') && EE_REST_API_DEBUG_MODE;
1288
-        }
1289
-        return $debug_mode;
1290
-    }
1291
-
1292
-
1293
-
1294
-    /**
1295
-     *    run - initial module setup
1296
-     *
1297
-     * @access    public
1298
-     * @param  WP $WP
1299
-     * @return    void
1300
-     */
1301
-    public function run($WP)
1302
-    {
1303
-    }
26
+	const ee_api_namespace = Domain::API_NAMESPACE;
27
+
28
+	const ee_api_namespace_for_regex = 'ee\/v([^/]*)\/';
29
+
30
+	const saved_routes_option_names = 'ee_core_routes';
31
+
32
+	/**
33
+	 * string used in _links response bodies to make them globally unique.
34
+	 *
35
+	 * @see http://v2.wp-api.org/extending/linking/
36
+	 */
37
+	const ee_api_link_namespace = 'https://api.eventespresso.com/';
38
+
39
+	/**
40
+	 * @var CalculatedModelFields
41
+	 */
42
+	protected static $_field_calculator;
43
+
44
+
45
+	/**
46
+	 * @return EED_Core_Rest_Api|EED_Module
47
+	 */
48
+	public static function instance()
49
+	{
50
+		self::$_field_calculator = LoaderFactory::getLoader()->load('EventEspresso\core\libraries\rest_api\CalculatedModelFields');
51
+		return parent::get_instance(__CLASS__);
52
+	}
53
+
54
+
55
+	/**
56
+	 *    set_hooks - for hooking into EE Core, other modules, etc
57
+	 *
58
+	 * @access    public
59
+	 * @return    void
60
+	 */
61
+	public static function set_hooks()
62
+	{
63
+		self::set_hooks_both();
64
+	}
65
+
66
+
67
+	/**
68
+	 *    set_hooks_admin - for hooking into EE Admin Core, other modules, etc
69
+	 *
70
+	 * @access    public
71
+	 * @return    void
72
+	 */
73
+	public static function set_hooks_admin()
74
+	{
75
+		self::set_hooks_both();
76
+	}
77
+
78
+
79
+	public static function set_hooks_both()
80
+	{
81
+		add_action('rest_api_init', array('EED_Core_Rest_Api', 'register_routes'), 10);
82
+		add_action('rest_api_init', array('EED_Core_Rest_Api', 'set_hooks_rest_api'), 5);
83
+		add_filter('rest_route_data', array('EED_Core_Rest_Api', 'hide_old_endpoints'), 10, 2);
84
+		add_filter(
85
+			'rest_index',
86
+			array('EventEspresso\core\libraries\rest_api\controllers\model\Meta', 'filterEeMetadataIntoIndex')
87
+		);
88
+		EED_Core_Rest_Api::invalidate_cached_route_data_on_version_change();
89
+	}
90
+
91
+
92
+	/**
93
+	 * sets up hooks which only need to be included as part of REST API requests;
94
+	 * other requests like to the frontend or admin etc don't need them
95
+	 *
96
+	 * @throws \EE_Error
97
+	 */
98
+	public static function set_hooks_rest_api()
99
+	{
100
+		// set hooks which account for changes made to the API
101
+		EED_Core_Rest_Api::_set_hooks_for_changes();
102
+	}
103
+
104
+
105
+	/**
106
+	 * public wrapper of _set_hooks_for_changes.
107
+	 * Loads all the hooks which make requests to old versions of the API
108
+	 * appear the same as they always did
109
+	 *
110
+	 * @throws EE_Error
111
+	 */
112
+	public static function set_hooks_for_changes()
113
+	{
114
+		self::_set_hooks_for_changes();
115
+	}
116
+
117
+
118
+	/**
119
+	 * Loads all the hooks which make requests to old versions of the API
120
+	 * appear the same as they always did
121
+	 *
122
+	 * @throws EE_Error
123
+	 */
124
+	protected static function _set_hooks_for_changes()
125
+	{
126
+		$folder_contents = EEH_File::get_contents_of_folders(array(EE_LIBRARIES . 'rest_api' . DS . 'changes'), false);
127
+		foreach ($folder_contents as $classname_in_namespace => $filepath) {
128
+			// ignore the base parent class
129
+			// and legacy named classes
130
+			if ($classname_in_namespace === 'ChangesInBase'
131
+				|| strpos($classname_in_namespace, 'Changes_In_') === 0
132
+			) {
133
+				continue;
134
+			}
135
+			$full_classname = 'EventEspresso\core\libraries\rest_api\changes\\' . $classname_in_namespace;
136
+			if (class_exists($full_classname)) {
137
+				$instance_of_class = new $full_classname;
138
+				if ($instance_of_class instanceof ChangesInBase) {
139
+					$instance_of_class->setHooks();
140
+				}
141
+			}
142
+		}
143
+	}
144
+
145
+
146
+	/**
147
+	 * Filters the WP routes to add our EE-related ones. This takes a bit of time
148
+	 * so we actually prefer to only do it when an EE plugin is activated or upgraded
149
+	 *
150
+	 * @throws \EE_Error
151
+	 */
152
+	public static function register_routes()
153
+	{
154
+		foreach (EED_Core_Rest_Api::get_ee_route_data() as $namespace => $relative_routes) {
155
+			foreach ($relative_routes as $relative_route => $data_for_multiple_endpoints) {
156
+				/**
157
+				 * @var array     $data_for_multiple_endpoints numerically indexed array
158
+				 *                                         but can also contain route options like {
159
+				 * @type array    $schema                      {
160
+				 * @type callable $schema_callback
161
+				 * @type array    $callback_args               arguments that will be passed to the callback, after the
162
+				 * WP_REST_Request of course
163
+				 * }
164
+				 * }
165
+				 */
166
+				// when registering routes, register all the endpoints' data at the same time
167
+				$multiple_endpoint_args = array();
168
+				foreach ($data_for_multiple_endpoints as $endpoint_key => $data_for_single_endpoint) {
169
+					/**
170
+					 * @var array     $data_for_single_endpoint {
171
+					 * @type callable $callback
172
+					 * @type string methods
173
+					 * @type array args
174
+					 * @type array _links
175
+					 * @type array    $callback_args            arguments that will be passed to the callback, after the
176
+					 * WP_REST_Request of course
177
+					 * }
178
+					 */
179
+					// skip route options
180
+					if (! is_numeric($endpoint_key)) {
181
+						continue;
182
+					}
183
+					if (! isset($data_for_single_endpoint['callback'], $data_for_single_endpoint['methods'])) {
184
+						throw new EE_Error(
185
+							esc_html__(
186
+							// @codingStandardsIgnoreStart
187
+								'Endpoint configuration data needs to have entries "callback" (callable) and "methods" (comma-separated list of accepts HTTP methods).',
188
+								// @codingStandardsIgnoreEnd
189
+								'event_espresso'
190
+							)
191
+						);
192
+					}
193
+					$callback = $data_for_single_endpoint['callback'];
194
+					$single_endpoint_args = array(
195
+						'methods' => $data_for_single_endpoint['methods'],
196
+						'args'    => isset($data_for_single_endpoint['args']) ? $data_for_single_endpoint['args']
197
+							: array(),
198
+					);
199
+					if (isset($data_for_single_endpoint['_links'])) {
200
+						$single_endpoint_args['_links'] = $data_for_single_endpoint['_links'];
201
+					}
202
+					if (isset($data_for_single_endpoint['callback_args'])) {
203
+						$callback_args = $data_for_single_endpoint['callback_args'];
204
+						$single_endpoint_args['callback'] = function (\WP_REST_Request $request) use (
205
+							$callback,
206
+							$callback_args
207
+						) {
208
+							array_unshift($callback_args, $request);
209
+							return call_user_func_array(
210
+								$callback,
211
+								$callback_args
212
+							);
213
+						};
214
+					} else {
215
+						$single_endpoint_args['callback'] = $data_for_single_endpoint['callback'];
216
+					}
217
+					$multiple_endpoint_args[] = $single_endpoint_args;
218
+				}
219
+				if (isset($data_for_multiple_endpoints['schema'])) {
220
+					$schema_route_data = $data_for_multiple_endpoints['schema'];
221
+					$schema_callback = $schema_route_data['schema_callback'];
222
+					$callback_args = $schema_route_data['callback_args'];
223
+					$multiple_endpoint_args['schema'] = function () use ($schema_callback, $callback_args) {
224
+						return call_user_func_array(
225
+							$schema_callback,
226
+							$callback_args
227
+						);
228
+					};
229
+				}
230
+				register_rest_route(
231
+					$namespace,
232
+					$relative_route,
233
+					$multiple_endpoint_args
234
+				);
235
+			}
236
+		}
237
+	}
238
+
239
+
240
+	/**
241
+	 * Checks if there was a version change or something that merits invalidating the cached
242
+	 * route data. If so, invalidates the cached route data so that it gets refreshed
243
+	 * next time the WP API is used
244
+	 */
245
+	public static function invalidate_cached_route_data_on_version_change()
246
+	{
247
+		if (EE_System::instance()->detect_req_type() !== EE_System::req_type_normal) {
248
+			EED_Core_Rest_Api::invalidate_cached_route_data();
249
+		}
250
+		foreach (EE_Registry::instance()->addons as $addon) {
251
+			if ($addon instanceof EE_Addon && $addon->detect_req_type() !== EE_System::req_type_normal) {
252
+				EED_Core_Rest_Api::invalidate_cached_route_data();
253
+			}
254
+		}
255
+	}
256
+
257
+
258
+	/**
259
+	 * Removes the cached route data so it will get refreshed next time the WP API is used
260
+	 */
261
+	public static function invalidate_cached_route_data()
262
+	{
263
+		// delete the saved EE REST API routes
264
+		foreach (EED_Core_Rest_Api::versions_served() as $version => $hidden) {
265
+			delete_option(EED_Core_Rest_Api::saved_routes_option_names . $version);
266
+		}
267
+	}
268
+
269
+
270
+	/**
271
+	 * Gets the EE route data
272
+	 *
273
+	 * @return array top-level key is the namespace, next-level key is the route and its value is array{
274
+	 * @throws \EE_Error
275
+	 * @type string|array $callback
276
+	 * @type string       $methods
277
+	 * @type boolean      $hidden_endpoint
278
+	 * }
279
+	 */
280
+	public static function get_ee_route_data()
281
+	{
282
+		$ee_routes = array();
283
+		foreach (self::versions_served() as $version => $hidden_endpoints) {
284
+			$ee_routes[ self::ee_api_namespace . $version ] = self::_get_ee_route_data_for_version(
285
+				$version,
286
+				$hidden_endpoints
287
+			);
288
+		}
289
+		return $ee_routes;
290
+	}
291
+
292
+
293
+	/**
294
+	 * Gets the EE route data from the wp options if it exists already,
295
+	 * otherwise re-generates it and saves it to the option
296
+	 *
297
+	 * @param string  $version
298
+	 * @param boolean $hidden_endpoints
299
+	 * @return array
300
+	 * @throws \EE_Error
301
+	 */
302
+	protected static function _get_ee_route_data_for_version($version, $hidden_endpoints = false)
303
+	{
304
+		$ee_routes = get_option(self::saved_routes_option_names . $version, null);
305
+		if (! $ee_routes || EED_Core_Rest_Api::debugMode()) {
306
+			$ee_routes = self::_save_ee_route_data_for_version($version, $hidden_endpoints);
307
+		}
308
+		return $ee_routes;
309
+	}
310
+
311
+
312
+	/**
313
+	 * Saves the EE REST API route data to a wp option and returns it
314
+	 *
315
+	 * @param string  $version
316
+	 * @param boolean $hidden_endpoints
317
+	 * @return mixed|null
318
+	 * @throws \EE_Error
319
+	 */
320
+	protected static function _save_ee_route_data_for_version($version, $hidden_endpoints = false)
321
+	{
322
+		$instance = self::instance();
323
+		$routes = apply_filters(
324
+			'EED_Core_Rest_Api__save_ee_route_data_for_version__routes',
325
+			array_replace_recursive(
326
+				$instance->_get_config_route_data_for_version($version, $hidden_endpoints),
327
+				$instance->_get_meta_route_data_for_version($version, $hidden_endpoints),
328
+				$instance->_get_model_route_data_for_version($version, $hidden_endpoints),
329
+				$instance->_get_rpc_route_data_for_version($version, $hidden_endpoints)
330
+			)
331
+		);
332
+		$option_name = self::saved_routes_option_names . $version;
333
+		if (get_option($option_name)) {
334
+			update_option($option_name, $routes, true);
335
+		} else {
336
+			add_option($option_name, $routes, null, 'no');
337
+		}
338
+		return $routes;
339
+	}
340
+
341
+
342
+	/**
343
+	 * Calculates all the EE routes and saves it to a WordPress option so we don't
344
+	 * need to calculate it on every request
345
+	 *
346
+	 * @deprecated since version 4.9.1
347
+	 * @return void
348
+	 */
349
+	public static function save_ee_routes()
350
+	{
351
+		if (EE_Maintenance_Mode::instance()->models_can_query()) {
352
+			$instance = self::instance();
353
+			$routes = apply_filters(
354
+				'EED_Core_Rest_Api__save_ee_routes__routes',
355
+				array_replace_recursive(
356
+					$instance->_register_config_routes(),
357
+					$instance->_register_meta_routes(),
358
+					$instance->_register_model_routes(),
359
+					$instance->_register_rpc_routes()
360
+				)
361
+			);
362
+			update_option(self::saved_routes_option_names, $routes, true);
363
+		}
364
+	}
365
+
366
+
367
+	/**
368
+	 * Gets all the route information relating to EE models
369
+	 *
370
+	 * @return array @see get_ee_route_data
371
+	 * @deprecated since version 4.9.1
372
+	 */
373
+	protected function _register_model_routes()
374
+	{
375
+		$model_routes = array();
376
+		foreach (self::versions_served() as $version => $hidden_endpoint) {
377
+			$model_routes[ EED_Core_Rest_Api::ee_api_namespace
378
+						   . $version ] = $this->_get_config_route_data_for_version($version, $hidden_endpoint);
379
+		}
380
+		return $model_routes;
381
+	}
382
+
383
+
384
+	/**
385
+	 * Decides whether or not to add write endpoints for this model.
386
+	 *
387
+	 * Currently, this defaults to exclude all global tables and models
388
+	 * which would allow inserting WP core data (we don't want to duplicate
389
+	 * what WP API does, as it's unnecessary, extra work, and potentially extra bugs)
390
+	 *
391
+	 * @param EEM_Base $model
392
+	 * @return bool
393
+	 */
394
+	public static function should_have_write_endpoints(EEM_Base $model)
395
+	{
396
+		if ($model->is_wp_core_model()) {
397
+			return false;
398
+		}
399
+		foreach ($model->get_tables() as $table) {
400
+			if ($table->is_global()) {
401
+				return false;
402
+			}
403
+		}
404
+		return true;
405
+	}
406
+
407
+
408
+	/**
409
+	 * Gets the names of all models which should have plural routes (eg `ee/v4.8.36/events`)
410
+	 * in this versioned namespace of EE4
411
+	 *
412
+	 * @param $version
413
+	 * @return array keys are model names (eg 'Event') and values ar either classnames (eg 'EEM_Event')
414
+	 */
415
+	public static function model_names_with_plural_routes($version)
416
+	{
417
+		$model_version_info = new ModelVersionInfo($version);
418
+		$models_to_register = $model_version_info->modelsForRequestedVersion();
419
+		// let's not bother having endpoints for extra metas
420
+		unset(
421
+			$models_to_register['Extra_Meta'],
422
+			$models_to_register['Extra_Join'],
423
+			$models_to_register['Post_Meta']
424
+		);
425
+		return apply_filters(
426
+			'FHEE__EED_Core_REST_API___register_model_routes',
427
+			$models_to_register
428
+		);
429
+	}
430
+
431
+
432
+	/**
433
+	 * Gets the route data for EE models in the specified version
434
+	 *
435
+	 * @param string  $version
436
+	 * @param boolean $hidden_endpoint
437
+	 * @return array
438
+	 * @throws EE_Error
439
+	 */
440
+	protected function _get_model_route_data_for_version($version, $hidden_endpoint = false)
441
+	{
442
+		$model_routes = array();
443
+		$model_version_info = new ModelVersionInfo($version);
444
+		foreach (EED_Core_Rest_Api::model_names_with_plural_routes($version) as $model_name => $model_classname) {
445
+			$model = \EE_Registry::instance()->load_model($model_name);
446
+			// if this isn't a valid model then let's skip iterate to the next item in the loop.
447
+			if (! $model instanceof EEM_Base) {
448
+				continue;
449
+			}
450
+			// yes we could just register one route for ALL models, but then they wouldn't show up in the index
451
+			$plural_model_route = EED_Core_Rest_Api::get_collection_route($model);
452
+			$singular_model_route = EED_Core_Rest_Api::get_entity_route($model, '(?P<id>[^\/]+)');
453
+			$model_routes[ $plural_model_route ] = array(
454
+				array(
455
+					'callback'        => array(
456
+						'EventEspresso\core\libraries\rest_api\controllers\model\Read',
457
+						'handleRequestGetAll',
458
+					),
459
+					'callback_args'   => array($version, $model_name),
460
+					'methods'         => WP_REST_Server::READABLE,
461
+					'hidden_endpoint' => $hidden_endpoint,
462
+					'args'            => $this->_get_read_query_params($model, $version),
463
+					'_links'          => array(
464
+						'self' => rest_url(EED_Core_Rest_Api::ee_api_namespace . $version . $singular_model_route),
465
+					),
466
+				),
467
+				'schema' => array(
468
+					'schema_callback' => array(
469
+						'EventEspresso\core\libraries\rest_api\controllers\model\Read',
470
+						'handleSchemaRequest',
471
+					),
472
+					'callback_args'   => array($version, $model_name),
473
+				),
474
+			);
475
+			$model_routes[ $singular_model_route ] = array(
476
+				array(
477
+					'callback'        => array(
478
+						'EventEspresso\core\libraries\rest_api\controllers\model\Read',
479
+						'handleRequestGetOne',
480
+					),
481
+					'callback_args'   => array($version, $model_name),
482
+					'methods'         => WP_REST_Server::READABLE,
483
+					'hidden_endpoint' => $hidden_endpoint,
484
+					'args'            => $this->_get_response_selection_query_params($model, $version),
485
+				),
486
+			);
487
+			if (apply_filters(
488
+				'FHEE__EED_Core_Rest_Api___get_model_route_data_for_version__add_write_endpoints',
489
+				EED_Core_Rest_Api::should_have_write_endpoints($model),
490
+				$model
491
+			)) {
492
+				$model_routes[ $plural_model_route ][] = array(
493
+					'callback'        => array(
494
+						'EventEspresso\core\libraries\rest_api\controllers\model\Write',
495
+						'handleRequestInsert',
496
+					),
497
+					'callback_args'   => array($version, $model_name),
498
+					'methods'         => WP_REST_Server::CREATABLE,
499
+					'hidden_endpoint' => $hidden_endpoint,
500
+					'args'            => $this->_get_write_params($model_name, $model_version_info, true),
501
+				);
502
+				$model_routes[ $singular_model_route ] = array_merge(
503
+					$model_routes[ $singular_model_route ],
504
+					array(
505
+						array(
506
+							'callback'        => array(
507
+								'EventEspresso\core\libraries\rest_api\controllers\model\Write',
508
+								'handleRequestUpdate',
509
+							),
510
+							'callback_args'   => array($version, $model_name),
511
+							'methods'         => WP_REST_Server::EDITABLE,
512
+							'hidden_endpoint' => $hidden_endpoint,
513
+							'args'            => $this->_get_write_params($model_name, $model_version_info),
514
+						),
515
+						array(
516
+							'callback'        => array(
517
+								'EventEspresso\core\libraries\rest_api\controllers\model\Write',
518
+								'handleRequestDelete',
519
+							),
520
+							'callback_args'   => array($version, $model_name),
521
+							'methods'         => WP_REST_Server::DELETABLE,
522
+							'hidden_endpoint' => $hidden_endpoint,
523
+							'args'            => $this->_get_delete_query_params($model, $version),
524
+						),
525
+					)
526
+				);
527
+			}
528
+			foreach ($model->relation_settings() as $relation_name => $relation_obj) {
529
+				$related_route = EED_Core_Rest_Api::get_relation_route_via(
530
+					$model,
531
+					'(?P<id>[^\/]+)',
532
+					$relation_obj
533
+				);
534
+				$endpoints = array(
535
+					array(
536
+						'callback'        => array(
537
+							'EventEspresso\core\libraries\rest_api\controllers\model\Read',
538
+							'handleRequestGetRelated',
539
+						),
540
+						'callback_args'   => array($version, $model_name, $relation_name),
541
+						'methods'         => WP_REST_Server::READABLE,
542
+						'hidden_endpoint' => $hidden_endpoint,
543
+						'args'            => $this->_get_read_query_params($relation_obj->get_other_model(), $version),
544
+					),
545
+				);
546
+				$model_routes[ $related_route ] = $endpoints;
547
+			}
548
+		}
549
+		return $model_routes;
550
+	}
551
+
552
+
553
+	/**
554
+	 * Gets the relative URI to a model's REST API plural route, after the EE4 versioned namespace,
555
+	 * excluding the preceding slash.
556
+	 * Eg you pass get_plural_route_to('Event') = 'events'
557
+	 *
558
+	 * @param EEM_Base $model
559
+	 * @return string
560
+	 */
561
+	public static function get_collection_route(EEM_Base $model)
562
+	{
563
+		return EEH_Inflector::pluralize_and_lower($model->get_this_model_name());
564
+	}
565
+
566
+
567
+	/**
568
+	 * Gets the relative URI to a model's REST API singular route, after the EE4 versioned namespace,
569
+	 * excluding the preceding slash.
570
+	 * Eg you pass get_plural_route_to('Event', 12) = 'events/12'
571
+	 *
572
+	 * @param EEM_Base $model eg Event or Venue
573
+	 * @param string   $id
574
+	 * @return string
575
+	 */
576
+	public static function get_entity_route($model, $id)
577
+	{
578
+		return EED_Core_Rest_Api::get_collection_route($model) . '/' . $id;
579
+	}
580
+
581
+
582
+	/**
583
+	 * Gets the relative URI to a model's REST API singular route, after the EE4 versioned namespace,
584
+	 * excluding the preceding slash.
585
+	 * Eg you pass get_plural_route_to('Event', 12) = 'events/12'
586
+	 *
587
+	 * @param EEM_Base               $model eg Event or Venue
588
+	 * @param string                 $id
589
+	 * @param EE_Model_Relation_Base $relation_obj
590
+	 * @return string
591
+	 */
592
+	public static function get_relation_route_via(EEM_Base $model, $id, EE_Model_Relation_Base $relation_obj)
593
+	{
594
+		$related_model_name_endpoint_part = ModelRead::getRelatedEntityName(
595
+			$relation_obj->get_other_model()->get_this_model_name(),
596
+			$relation_obj
597
+		);
598
+		return EED_Core_Rest_Api::get_entity_route($model, $id) . '/' . $related_model_name_endpoint_part;
599
+	}
600
+
601
+
602
+	/**
603
+	 * Adds onto the $relative_route the EE4 REST API versioned namespace.
604
+	 * Eg if given '4.8.36' and 'events', will return 'ee/v4.8.36/events'
605
+	 *
606
+	 * @param string $relative_route
607
+	 * @param string $version
608
+	 * @return string
609
+	 */
610
+	public static function get_versioned_route_to($relative_route, $version = '4.8.36')
611
+	{
612
+		return '/' . EED_Core_Rest_Api::ee_api_namespace . $version . '/' . $relative_route;
613
+	}
614
+
615
+
616
+	/**
617
+	 * Adds all the RPC-style routes (remote procedure call-like routes, ie
618
+	 * routes that don't conform to the traditional REST CRUD-style).
619
+	 *
620
+	 * @deprecated since 4.9.1
621
+	 */
622
+	protected function _register_rpc_routes()
623
+	{
624
+		$routes = array();
625
+		foreach (self::versions_served() as $version => $hidden_endpoint) {
626
+			$routes[ self::ee_api_namespace . $version ] = $this->_get_rpc_route_data_for_version(
627
+				$version,
628
+				$hidden_endpoint
629
+			);
630
+		}
631
+		return $routes;
632
+	}
633
+
634
+
635
+	/**
636
+	 * @param string  $version
637
+	 * @param boolean $hidden_endpoint
638
+	 * @return array
639
+	 */
640
+	protected function _get_rpc_route_data_for_version($version, $hidden_endpoint = false)
641
+	{
642
+		$this_versions_routes = array();
643
+		// checkin endpoint
644
+		$this_versions_routes['registrations/(?P<REG_ID>\d+)/toggle_checkin_for_datetime/(?P<DTT_ID>\d+)'] = array(
645
+			array(
646
+				'callback'        => array(
647
+					'EventEspresso\core\libraries\rest_api\controllers\rpc\Checkin',
648
+					'handleRequestToggleCheckin',
649
+				),
650
+				'methods'         => WP_REST_Server::CREATABLE,
651
+				'hidden_endpoint' => $hidden_endpoint,
652
+				'args'            => array(
653
+					'force' => array(
654
+						'required'    => false,
655
+						'default'     => false,
656
+						'description' => __(
657
+						// @codingStandardsIgnoreStart
658
+							'Whether to force toggle checkin, or to verify the registration status and allowed ticket uses',
659
+							// @codingStandardsIgnoreEnd
660
+							'event_espresso'
661
+						),
662
+					),
663
+				),
664
+				'callback_args'   => array($version),
665
+			),
666
+		);
667
+		return apply_filters(
668
+			'FHEE__EED_Core_Rest_Api___register_rpc_routes__this_versions_routes',
669
+			$this_versions_routes,
670
+			$version,
671
+			$hidden_endpoint
672
+		);
673
+	}
674
+
675
+
676
+	/**
677
+	 * Gets the query params that can be used when request one or many
678
+	 *
679
+	 * @param EEM_Base $model
680
+	 * @param string   $version
681
+	 * @return array
682
+	 */
683
+	protected function _get_response_selection_query_params(\EEM_Base $model, $version)
684
+	{
685
+		return apply_filters(
686
+			'FHEE__EED_Core_Rest_Api___get_response_selection_query_params',
687
+			array(
688
+				'include'   => array(
689
+					'required' => false,
690
+					'default'  => '*',
691
+					'type'     => 'string',
692
+				),
693
+				'calculate' => array(
694
+					'required'          => false,
695
+					'default'           => '',
696
+					'enum'              => self::$_field_calculator->retrieveCalculatedFieldsForModel($model),
697
+					'type'              => 'string',
698
+					// because we accept a CSV'd list of the enumerated strings, WP core validation and sanitization
699
+					// freaks out. We'll just validate this argument while handling the request
700
+					'validate_callback' => null,
701
+					'sanitize_callback' => null,
702
+				),
703
+			),
704
+			$model,
705
+			$version
706
+		);
707
+	}
708
+
709
+
710
+	/**
711
+	 * Gets the parameters acceptable for delete requests
712
+	 *
713
+	 * @param \EEM_Base $model
714
+	 * @param string    $version
715
+	 * @return array
716
+	 */
717
+	protected function _get_delete_query_params(\EEM_Base $model, $version)
718
+	{
719
+		$params_for_delete = array(
720
+			'allow_blocking' => array(
721
+				'required' => false,
722
+				'default'  => true,
723
+				'type'     => 'boolean',
724
+			),
725
+		);
726
+		$params_for_delete['force'] = array(
727
+			'required' => false,
728
+			'default'  => false,
729
+			'type'     => 'boolean',
730
+		);
731
+		return apply_filters(
732
+			'FHEE__EED_Core_Rest_Api___get_delete_query_params',
733
+			$params_for_delete,
734
+			$model,
735
+			$version
736
+		);
737
+	}
738
+
739
+
740
+	/**
741
+	 * Gets info about reading query params that are acceptable
742
+	 *
743
+	 * @param \EEM_Base $model eg 'Event' or 'Venue'
744
+	 * @param  string   $version
745
+	 * @return array    describing the args acceptable when querying this model
746
+	 * @throws EE_Error
747
+	 */
748
+	protected function _get_read_query_params(\EEM_Base $model, $version)
749
+	{
750
+		$default_orderby = array();
751
+		foreach ($model->get_combined_primary_key_fields() as $key_field) {
752
+			$default_orderby[ $key_field->get_name() ] = 'ASC';
753
+		}
754
+		return array_merge(
755
+			$this->_get_response_selection_query_params($model, $version),
756
+			array(
757
+				'where'    => array(
758
+					'required'          => false,
759
+					'default'           => array(),
760
+					'type'              => 'object',
761
+					// because we accept an almost infinite list of possible where conditions, WP
762
+					// core validation and sanitization freaks out. We'll just validate this argument
763
+					// while handling the request
764
+					'validate_callback' => null,
765
+					'sanitize_callback' => null,
766
+				),
767
+				'limit'    => array(
768
+					'required'          => false,
769
+					'default'           => EED_Core_Rest_Api::get_default_query_limit(),
770
+					'type'              => array(
771
+						'array',
772
+						'string',
773
+						'integer',
774
+					),
775
+					// because we accept a variety of types, WP core validation and sanitization
776
+					// freaks out. We'll just validate this argument while handling the request
777
+					'validate_callback' => null,
778
+					'sanitize_callback' => null,
779
+				),
780
+				'order_by' => array(
781
+					'required'          => false,
782
+					'default'           => $default_orderby,
783
+					'type'              => array(
784
+						'object',
785
+						'string',
786
+					),// because we accept a variety of types, WP core validation and sanitization
787
+					// freaks out. We'll just validate this argument while handling the request
788
+					'validate_callback' => null,
789
+					'sanitize_callback' => null,
790
+				),
791
+				'group_by' => array(
792
+					'required'          => false,
793
+					'default'           => null,
794
+					'type'              => array(
795
+						'object',
796
+						'string',
797
+					),
798
+					// because we accept  an almost infinite list of possible groupings,
799
+					// WP core validation and sanitization
800
+					// freaks out. We'll just validate this argument while handling the request
801
+					'validate_callback' => null,
802
+					'sanitize_callback' => null,
803
+				),
804
+				'having'   => array(
805
+					'required'          => false,
806
+					'default'           => null,
807
+					'type'              => 'object',
808
+					// because we accept an almost infinite list of possible where conditions, WP
809
+					// core validation and sanitization freaks out. We'll just validate this argument
810
+					// while handling the request
811
+					'validate_callback' => null,
812
+					'sanitize_callback' => null,
813
+				),
814
+				'caps'     => array(
815
+					'required' => false,
816
+					'default'  => EEM_Base::caps_read,
817
+					'type'     => 'string',
818
+					'enum'     => array(
819
+						EEM_Base::caps_read,
820
+						EEM_Base::caps_read_admin,
821
+						EEM_Base::caps_edit,
822
+						EEM_Base::caps_delete,
823
+					),
824
+				),
825
+			)
826
+		);
827
+	}
828
+
829
+
830
+	/**
831
+	 * Gets parameter information for a model regarding writing data
832
+	 *
833
+	 * @param string           $model_name
834
+	 * @param ModelVersionInfo $model_version_info
835
+	 * @param boolean          $create                                       whether this is for request to create (in
836
+	 *                                                                       which case we need all required params) or
837
+	 *                                                                       just to update (in which case we don't
838
+	 *                                                                       need those on every request)
839
+	 * @return array
840
+	 */
841
+	protected function _get_write_params(
842
+		$model_name,
843
+		ModelVersionInfo $model_version_info,
844
+		$create = false
845
+	) {
846
+		$model = EE_Registry::instance()->load_model($model_name);
847
+		$fields = $model_version_info->fieldsOnModelInThisVersion($model);
848
+		$args_info = array();
849
+		foreach ($fields as $field_name => $field_obj) {
850
+			if ($field_obj->is_auto_increment()) {
851
+				// totally ignore auto increment IDs
852
+				continue;
853
+			}
854
+			$arg_info = $field_obj->getSchema();
855
+			$required = $create && ! $field_obj->is_nullable() && $field_obj->get_default_value() === null;
856
+			$arg_info['required'] = $required;
857
+			// remove the read-only flag. If it were read-only we wouldn't list it as an argument while writing, right?
858
+			unset($arg_info['readonly']);
859
+			$schema_properties = $field_obj->getSchemaProperties();
860
+			if (isset($schema_properties['raw'])
861
+				&& $field_obj->getSchemaType() === 'object'
862
+			) {
863
+				// if there's a "raw" form of this argument, use those properties instead
864
+				$arg_info = array_replace(
865
+					$arg_info,
866
+					$schema_properties['raw']
867
+				);
868
+			}
869
+			$arg_info['default'] = ModelDataTranslator::prepareFieldValueForJson(
870
+				$field_obj,
871
+				$field_obj->get_default_value(),
872
+				$model_version_info->requestedVersion()
873
+			);
874
+			// we do our own validation and sanitization within the controller
875
+			if (function_exists('rest_validate_value_from_schema')) {
876
+				$sanitize_callback = array(
877
+					'EED_Core_Rest_Api',
878
+					'default_sanitize_callback',
879
+				);
880
+			} else {
881
+				$sanitize_callback = null;
882
+			}
883
+			$arg_info['sanitize_callback'] = $sanitize_callback;
884
+			$args_info[ $field_name ] = $arg_info;
885
+			if ($field_obj instanceof EE_Datetime_Field) {
886
+				$gmt_arg_info = $arg_info;
887
+				$gmt_arg_info['description'] = sprintf(
888
+					esc_html__(
889
+						'%1$s - the value for this field in UTC. Ignored if %2$s is provided.',
890
+						'event_espresso'
891
+					),
892
+					$field_obj->get_nicename(),
893
+					$field_name
894
+				);
895
+				$args_info[ $field_name . '_gmt' ] = $gmt_arg_info;
896
+			}
897
+		}
898
+		return $args_info;
899
+	}
900
+
901
+
902
+	/**
903
+	 * Replacement for WP API's 'rest_parse_request_arg'.
904
+	 * If the value is blank but not required, don't bother validating it.
905
+	 * Also, it uses our email validation instead of WP API's default.
906
+	 *
907
+	 * @param                 $value
908
+	 * @param WP_REST_Request $request
909
+	 * @param                 $param
910
+	 * @return bool|true|WP_Error
911
+	 * @throws InvalidArgumentException
912
+	 * @throws InvalidInterfaceException
913
+	 * @throws InvalidDataTypeException
914
+	 */
915
+	public static function default_sanitize_callback($value, WP_REST_Request $request, $param)
916
+	{
917
+		$attributes = $request->get_attributes();
918
+		if (! isset($attributes['args'][ $param ])
919
+			|| ! is_array($attributes['args'][ $param ])) {
920
+			$validation_result = true;
921
+		} else {
922
+			$args = $attributes['args'][ $param ];
923
+			if ((
924
+					$value === ''
925
+					|| $value === null
926
+				)
927
+				&& (! isset($args['required'])
928
+					|| $args['required'] === false
929
+				)
930
+			) {
931
+				// not required and not provided? that's cool
932
+				$validation_result = true;
933
+			} elseif (isset($args['format'])
934
+					  && $args['format'] === 'email'
935
+			) {
936
+				$validation_result = true;
937
+				if (! self::_validate_email($value)) {
938
+					$validation_result = new WP_Error(
939
+						'rest_invalid_param',
940
+						esc_html__(
941
+							'The email address is not valid or does not exist.',
942
+							'event_espresso'
943
+						)
944
+					);
945
+				}
946
+			} else {
947
+				$validation_result = rest_validate_value_from_schema($value, $args, $param);
948
+			}
949
+		}
950
+		if (is_wp_error($validation_result)) {
951
+			return $validation_result;
952
+		}
953
+		return rest_sanitize_request_arg($value, $request, $param);
954
+	}
955
+
956
+
957
+	/**
958
+	 * Returns whether or not this email address is valid. Copied from EE_Email_Validation_Strategy::_validate_email()
959
+	 *
960
+	 * @param $email
961
+	 * @return bool
962
+	 * @throws InvalidArgumentException
963
+	 * @throws InvalidInterfaceException
964
+	 * @throws InvalidDataTypeException
965
+	 */
966
+	protected static function _validate_email($email)
967
+	{
968
+		try {
969
+			EmailAddressFactory::create($email);
970
+			return true;
971
+		} catch (EmailValidationException $e) {
972
+			return false;
973
+		}
974
+	}
975
+
976
+
977
+	/**
978
+	 * Gets routes for the config
979
+	 *
980
+	 * @return array @see _register_model_routes
981
+	 * @deprecated since version 4.9.1
982
+	 */
983
+	protected function _register_config_routes()
984
+	{
985
+		$config_routes = array();
986
+		foreach (self::versions_served() as $version => $hidden_endpoint) {
987
+			$config_routes[ self::ee_api_namespace . $version ] = $this->_get_config_route_data_for_version(
988
+				$version,
989
+				$hidden_endpoint
990
+			);
991
+		}
992
+		return $config_routes;
993
+	}
994
+
995
+
996
+	/**
997
+	 * Gets routes for the config for the specified version
998
+	 *
999
+	 * @param string  $version
1000
+	 * @param boolean $hidden_endpoint
1001
+	 * @return array
1002
+	 */
1003
+	protected function _get_config_route_data_for_version($version, $hidden_endpoint)
1004
+	{
1005
+		return array(
1006
+			'config'    => array(
1007
+				array(
1008
+					'callback'        => array(
1009
+						'EventEspresso\core\libraries\rest_api\controllers\config\Read',
1010
+						'handleRequest',
1011
+					),
1012
+					'methods'         => WP_REST_Server::READABLE,
1013
+					'hidden_endpoint' => $hidden_endpoint,
1014
+					'callback_args'   => array($version),
1015
+				),
1016
+			),
1017
+			'site_info' => array(
1018
+				array(
1019
+					'callback'        => array(
1020
+						'EventEspresso\core\libraries\rest_api\controllers\config\Read',
1021
+						'handleRequestSiteInfo',
1022
+					),
1023
+					'methods'         => WP_REST_Server::READABLE,
1024
+					'hidden_endpoint' => $hidden_endpoint,
1025
+					'callback_args'   => array($version),
1026
+				),
1027
+			),
1028
+		);
1029
+	}
1030
+
1031
+
1032
+	/**
1033
+	 * Gets the meta info routes
1034
+	 *
1035
+	 * @return array @see _register_model_routes
1036
+	 * @deprecated since version 4.9.1
1037
+	 */
1038
+	protected function _register_meta_routes()
1039
+	{
1040
+		$meta_routes = array();
1041
+		foreach (self::versions_served() as $version => $hidden_endpoint) {
1042
+			$meta_routes[ self::ee_api_namespace . $version ] = $this->_get_meta_route_data_for_version(
1043
+				$version,
1044
+				$hidden_endpoint
1045
+			);
1046
+		}
1047
+		return $meta_routes;
1048
+	}
1049
+
1050
+
1051
+	/**
1052
+	 * @param string  $version
1053
+	 * @param boolean $hidden_endpoint
1054
+	 * @return array
1055
+	 */
1056
+	protected function _get_meta_route_data_for_version($version, $hidden_endpoint = false)
1057
+	{
1058
+		return array(
1059
+			'resources' => array(
1060
+				array(
1061
+					'callback'        => array(
1062
+						'EventEspresso\core\libraries\rest_api\controllers\model\Meta',
1063
+						'handleRequestModelsMeta',
1064
+					),
1065
+					'methods'         => WP_REST_Server::READABLE,
1066
+					'hidden_endpoint' => $hidden_endpoint,
1067
+					'callback_args'   => array($version),
1068
+				),
1069
+			),
1070
+		);
1071
+	}
1072
+
1073
+
1074
+	/**
1075
+	 * Tries to hide old 4.6 endpoints from the
1076
+	 *
1077
+	 * @param array $route_data
1078
+	 * @return array
1079
+	 * @throws \EE_Error
1080
+	 */
1081
+	public static function hide_old_endpoints($route_data)
1082
+	{
1083
+		// allow API clients to override which endpoints get hidden, in case
1084
+		// they want to discover particular endpoints
1085
+		// also, we don't have access to the request so we have to just grab it from the superglobal
1086
+		$force_show_ee_namespace = ltrim(
1087
+			EEH_Array::is_set($_REQUEST, 'force_show_ee_namespace', ''),
1088
+			'/'
1089
+		);
1090
+		foreach (EED_Core_Rest_Api::get_ee_route_data() as $namespace => $relative_urls) {
1091
+			foreach ($relative_urls as $resource_name => $endpoints) {
1092
+				foreach ($endpoints as $key => $endpoint) {
1093
+					// skip schema and other route options
1094
+					if (! is_numeric($key)) {
1095
+						continue;
1096
+					}
1097
+					// by default, hide "hidden_endpoint"s, unless the request indicates
1098
+					// to $force_show_ee_namespace, in which case only show that one
1099
+					// namespace's endpoints (and hide all others)
1100
+					if (($force_show_ee_namespace !== '' && $force_show_ee_namespace !== $namespace)
1101
+						|| ($endpoint['hidden_endpoint'] && $force_show_ee_namespace === '')
1102
+					) {
1103
+						$full_route = '/' . ltrim($namespace, '/');
1104
+						$full_route .= '/' . ltrim($resource_name, '/');
1105
+						unset($route_data[ $full_route ]);
1106
+					}
1107
+				}
1108
+			}
1109
+		}
1110
+		return $route_data;
1111
+	}
1112
+
1113
+
1114
+	/**
1115
+	 * Returns an array describing which versions of core support serving requests for.
1116
+	 * Keys are core versions' major and minor version, and values are the
1117
+	 * LOWEST requested version they can serve. Eg, 4.7 can serve requests for 4.6-like
1118
+	 * data by just removing a few models and fields from the responses. However, 4.15 might remove
1119
+	 * the answers table entirely, in which case it would be very difficult for
1120
+	 * it to serve 4.6-style responses.
1121
+	 * Versions of core that are missing from this array are unknowns.
1122
+	 * previous ver
1123
+	 *
1124
+	 * @return array
1125
+	 */
1126
+	public static function version_compatibilities()
1127
+	{
1128
+		return apply_filters(
1129
+			'FHEE__EED_Core_REST_API__version_compatibilities',
1130
+			array(
1131
+				'4.8.29' => '4.8.29',
1132
+				'4.8.33' => '4.8.29',
1133
+				'4.8.34' => '4.8.29',
1134
+				'4.8.36' => '4.8.29',
1135
+			)
1136
+		);
1137
+	}
1138
+
1139
+
1140
+	/**
1141
+	 * Gets the latest API version served. Eg if there
1142
+	 * are two versions served of the API, 4.8.29 and 4.8.32, and
1143
+	 * we are on core version 4.8.34, it will return the string "4.8.32"
1144
+	 *
1145
+	 * @return string
1146
+	 */
1147
+	public static function latest_rest_api_version()
1148
+	{
1149
+		$versions_served = \EED_Core_Rest_Api::versions_served();
1150
+		$versions_served_keys = array_keys($versions_served);
1151
+		return end($versions_served_keys);
1152
+	}
1153
+
1154
+
1155
+	/**
1156
+	 * Using EED_Core_Rest_Api::version_compatibilities(), determines what version of
1157
+	 * EE the API can serve requests for. Eg, if we are on 4.15 of core, and
1158
+	 * we can serve requests from 4.12 or later, this will return array( '4.12', '4.13', '4.14', '4.15' ).
1159
+	 * We also indicate whether or not this version should be put in the index or not
1160
+	 *
1161
+	 * @return array keys are API version numbers (just major and minor numbers), and values
1162
+	 * are whether or not they should be hidden
1163
+	 */
1164
+	public static function versions_served()
1165
+	{
1166
+		$versions_served = array();
1167
+		$possibly_served_versions = EED_Core_Rest_Api::version_compatibilities();
1168
+		$lowest_compatible_version = end($possibly_served_versions);
1169
+		reset($possibly_served_versions);
1170
+		$versions_served_historically = array_keys($possibly_served_versions);
1171
+		$latest_version = end($versions_served_historically);
1172
+		reset($versions_served_historically);
1173
+		// for each version of core we have ever served:
1174
+		foreach ($versions_served_historically as $key_versioned_endpoint) {
1175
+			// if it's not above the current core version, and it's compatible with the current version of core
1176
+			if ($key_versioned_endpoint === $latest_version) {
1177
+				// don't hide the latest version in the index
1178
+				$versions_served[ $key_versioned_endpoint ] = false;
1179
+			} elseif ($key_versioned_endpoint >= $lowest_compatible_version
1180
+				&& $key_versioned_endpoint < EED_Core_Rest_Api::core_version()
1181
+			) {
1182
+				// include, but hide, previous versions which are still supported
1183
+				$versions_served[ $key_versioned_endpoint ] = true;
1184
+			} elseif (apply_filters(
1185
+				'FHEE__EED_Core_Rest_Api__versions_served__include_incompatible_versions',
1186
+				false,
1187
+				$possibly_served_versions
1188
+			)) {
1189
+				// if a version is no longer supported, don't include it in index or list of versions served
1190
+				$versions_served[ $key_versioned_endpoint ] = true;
1191
+			}
1192
+		}
1193
+		return $versions_served;
1194
+	}
1195
+
1196
+
1197
+	/**
1198
+	 * Gets the major and minor version of EE core's version string
1199
+	 *
1200
+	 * @return string
1201
+	 */
1202
+	public static function core_version()
1203
+	{
1204
+		return apply_filters(
1205
+			'FHEE__EED_Core_REST_API__core_version',
1206
+			implode(
1207
+				'.',
1208
+				array_slice(
1209
+					explode(
1210
+						'.',
1211
+						espresso_version()
1212
+					),
1213
+					0,
1214
+					3
1215
+				)
1216
+			)
1217
+		);
1218
+	}
1219
+
1220
+
1221
+	/**
1222
+	 * Gets the default limit that should be used when querying for resources
1223
+	 *
1224
+	 * @return int
1225
+	 */
1226
+	public static function get_default_query_limit()
1227
+	{
1228
+		// we actually don't use a const because we want folks to always use
1229
+		// this method, not the const directly
1230
+		return apply_filters(
1231
+			'FHEE__EED_Core_Rest_Api__get_default_query_limit',
1232
+			50
1233
+		);
1234
+	}
1235
+
1236
+
1237
+	/**
1238
+	 *
1239
+	 * @param string $version api version string (i.e. '4.8.36')
1240
+	 * @return array
1241
+	 */
1242
+	public static function getCollectionRoutesIndexedByModelName($version = '')
1243
+	{
1244
+		$version = empty($version) ? self::latest_rest_api_version() : $version;
1245
+		$model_names = self::model_names_with_plural_routes($version);
1246
+		$collection_routes = array();
1247
+		foreach ($model_names as $model_name => $model_class_name) {
1248
+			$collection_routes[ strtolower($model_name) ] = '/' . self::ee_api_namespace . $version . '/'
1249
+															. EEH_Inflector::pluralize_and_lower($model_name);
1250
+		}
1251
+		return $collection_routes;
1252
+	}
1253
+
1254
+
1255
+	/**
1256
+	 * Returns an array of primary key names indexed by model names.
1257
+	 * @param string $version
1258
+	 * @return array
1259
+	 */
1260
+	public static function getPrimaryKeyNamesIndexedByModelName($version = '')
1261
+	{
1262
+		$version = empty($version) ? self::latest_rest_api_version() : $version;
1263
+		$model_names = self::model_names_with_plural_routes($version);
1264
+		$primary_key_items = array();
1265
+		foreach ($model_names as $model_name => $model_class_name) {
1266
+			$primary_keys = $model_class_name::instance()->get_combined_primary_key_fields();
1267
+			foreach ($primary_keys as $primary_key_name => $primary_key_field) {
1268
+				if (count($primary_keys) > 1) {
1269
+					$primary_key_items[ strtolower($model_name) ][] = $primary_key_name;
1270
+				} else {
1271
+					$primary_key_items[ strtolower($model_name) ] = $primary_key_name;
1272
+				}
1273
+			}
1274
+		}
1275
+		return $primary_key_items;
1276
+	}
1277
+
1278
+	/**
1279
+	 * Determines the EE REST API debug mode is activated, or not.
1280
+	 * @since 4.9.72.p
1281
+	 * @return bool
1282
+	 */
1283
+	public static function debugMode()
1284
+	{
1285
+		static $debug_mode = null; // could be class prop
1286
+		if ($debug_mode === null) {
1287
+			$debug_mode = defined('EE_REST_API_DEBUG_MODE') && EE_REST_API_DEBUG_MODE;
1288
+		}
1289
+		return $debug_mode;
1290
+	}
1291
+
1292
+
1293
+
1294
+	/**
1295
+	 *    run - initial module setup
1296
+	 *
1297
+	 * @access    public
1298
+	 * @param  WP $WP
1299
+	 * @return    void
1300
+	 */
1301
+	public function run($WP)
1302
+	{
1303
+	}
1304 1304
 }
Please login to merge, or discard this patch.
core/libraries/rest_api/controllers/Base.php 1 patch
Indentation   +334 added lines, -334 removed lines patch added patch discarded remove patch
@@ -22,338 +22,338 @@
 block discarded – undo
22 22
 class Base
23 23
 {
24 24
 
25
-    /**
26
-     * @deprecated use all-caps version
27
-     */
28
-    // @codingStandardsIgnoreStart
29
-    const header_prefix_for_ee = 'X-EE-';
30
-    // @codingStandardsIgnoreEnd
31
-
32
-    const HEADER_PREFIX_FOR_EE = 'X-EE-';
33
-
34
-    /**
35
-     * @deprecated use all-caps version instead
36
-     */
37
-    // @codingStandardsIgnoreStart
38
-    const header_prefix_for_wp = 'X-WP-';
39
-    // @codingStandardsIgnoreEnd
40
-
41
-    const HEADER_PREFIX_FOR_WP = 'X-WP-';
42
-
43
-    /**
44
-     * Contains debug info we'll send back in the response headers
45
-     *
46
-     * @var array
47
-     */
48
-    protected $debug_info = array();
49
-
50
-    /**
51
-     * Indicates whether or not the API is in debug mode
52
-     *
53
-     * @var boolean
54
-     */
55
-    protected $debug_mode = false;
56
-
57
-    /**
58
-     * Indicates the version that was requested
59
-     *
60
-     * @var string
61
-     */
62
-    protected $requested_version;
63
-
64
-    /**
65
-     * flat array of headers to send in the response
66
-     *
67
-     * @var array
68
-     */
69
-    protected $response_headers = array();
70
-
71
-
72
-    public function __construct()
73
-    {
74
-        $this->debug_mode = EED_Core_Rest_Api::debugMode();
75
-        // we are handling a REST request. Don't show a fancy HTML error message is any error comes up
76
-        add_filter('FHEE__EE_Error__get_error__show_normal_exceptions', '__return_true');
77
-    }
78
-
79
-
80
-    /**
81
-     * Sets the version the user requested
82
-     *
83
-     * @param string $version eg '4.8'
84
-     */
85
-    public function setRequestedVersion($version)
86
-    {
87
-        $this->requested_version = $version;
88
-    }
89
-
90
-
91
-    /**
92
-     * Sets some debug info that we'll send back in headers
93
-     *
94
-     * @param string       $key
95
-     * @param string|array $info
96
-     */
97
-    protected function setDebugInfo($key, $info)
98
-    {
99
-        $this->debug_info[ $key ] = $info;
100
-    }
101
-
102
-
103
-    /**
104
-     * Sets headers for the response
105
-     *
106
-     * @param string       $header_key    , excluding the "X-EE-" part
107
-     * @param array|string $value         if an array, multiple headers will be added, one
108
-     *                                    for each key in the array
109
-     * @param boolean      $use_ee_prefix whether to use the EE prefix on the header, or fallback to
110
-     *                                    the standard WP one
111
-     */
112
-    protected function setResponseHeader($header_key, $value, $use_ee_prefix = true)
113
-    {
114
-        if (is_array($value)) {
115
-            foreach ($value as $value_key => $value_value) {
116
-                $this->setResponseHeader($header_key . '[' . $value_key . ']', $value_value);
117
-            }
118
-        } else {
119
-            $prefix = $use_ee_prefix ? Base::HEADER_PREFIX_FOR_EE : Base::HEADER_PREFIX_FOR_WP;
120
-            $this->response_headers[ $prefix . $header_key ] = $value;
121
-        }
122
-    }
123
-
124
-
125
-    /**
126
-     * Returns a flat array of headers to be added to the response
127
-     *
128
-     * @return array
129
-     */
130
-    protected function getResponseHeaders()
131
-    {
132
-        return apply_filters(
133
-            'FHEE__EventEspresso\core\libraries\rest_api\controllers\Base___get_response_headers',
134
-            $this->response_headers,
135
-            $this,
136
-            $this->requested_version
137
-        );
138
-    }
139
-
140
-
141
-    /**
142
-     * Adds error notices from EE_Error onto the provided \WP_Error
143
-     *
144
-     * @param WP_Error $wp_error_response
145
-     * @return WP_Error
146
-     */
147
-    protected function addEeErrorsToResponse(WP_Error $wp_error_response)
148
-    {
149
-        $notices_during_checkin = EE_Error::get_raw_notices();
150
-        if (! empty($notices_during_checkin['errors'])) {
151
-            foreach ($notices_during_checkin['errors'] as $error_code => $error_message) {
152
-                $wp_error_response->add(
153
-                    sanitize_key($error_code),
154
-                    strip_tags($error_message)
155
-                );
156
-            }
157
-        }
158
-        return $wp_error_response;
159
-    }
160
-
161
-
162
-    /**
163
-     * Sends a response, but also makes sure to attach headers that
164
-     * are handy for debugging.
165
-     * Specifically, we assume folks will want to know what exactly was the DB query that got run,
166
-     * what exactly was the Models query that got run, what capabilities came into play, what fields were omitted from
167
-     * the response, others?
168
-     *
169
-     * @param array|WP_Error|Exception|RestException $response
170
-     * @return WP_REST_Response
171
-     */
172
-    public function sendResponse($response)
173
-    {
174
-        if ($response instanceof RestException) {
175
-            $response = new WP_Error($response->getStringCode(), $response->getMessage(), $response->getData());
176
-        }
177
-        if ($response instanceof Exception) {
178
-            $code = $response->getCode() ? $response->getCode() : 'error_occurred';
179
-            $response = new WP_Error($code, $response->getMessage());
180
-        }
181
-        if ($response instanceof WP_Error) {
182
-            $response = $this->addEeErrorsToResponse($response);
183
-            $rest_response = $this->createRestResponseFromWpError($response);
184
-        } else {
185
-            $rest_response = new WP_REST_Response($response, 200);
186
-        }
187
-        $headers = array();
188
-        if ($this->debug_mode && is_array($this->debug_info)) {
189
-            foreach ($this->debug_info as $debug_key => $debug_info) {
190
-                if (is_array($debug_info)) {
191
-                    $debug_info = wp_json_encode($debug_info);
192
-                }
193
-                $headers[ 'X-EE4-Debug-' . ucwords($debug_key) ] = $debug_info;
194
-            }
195
-        }
196
-        $headers = array_merge(
197
-            $headers,
198
-            $this->getResponseHeaders(),
199
-            $this->getHeadersFromEeNotices()
200
-        );
201
-        $rest_response->set_headers($headers);
202
-        return $rest_response;
203
-    }
204
-
205
-
206
-    /**
207
-     * Converts the \WP_Error into `WP_REST_Response.
208
-     * Mostly this is just a copy-and-paste from \WP_REST_Server::error_to_response
209
-     * (which is protected)
210
-     *
211
-     * @param WP_Error $wp_error
212
-     * @return WP_REST_Response
213
-     */
214
-    protected function createRestResponseFromWpError(WP_Error $wp_error)
215
-    {
216
-        $error_data = $wp_error->get_error_data();
217
-        if (is_array($error_data) && isset($error_data['status'])) {
218
-            $status = $error_data['status'];
219
-        } else {
220
-            $status = 500;
221
-        }
222
-        $errors = array();
223
-        foreach ((array) $wp_error->errors as $code => $messages) {
224
-            foreach ((array) $messages as $message) {
225
-                $errors[] = array(
226
-                    'code'    => $code,
227
-                    'message' => $message,
228
-                    'data'    => $wp_error->get_error_data($code),
229
-                );
230
-            }
231
-        }
232
-        $data = isset($errors[0]) ? $errors[0] : array();
233
-        if (count($errors) > 1) {
234
-            // Remove the primary error.
235
-            array_shift($errors);
236
-            $data['additional_errors'] = $errors;
237
-        }
238
-        return new WP_REST_Response($data, $status);
239
-    }
240
-
241
-
242
-    /**
243
-     * Array of headers derived from EE success, attention, and error messages
244
-     *
245
-     * @return array
246
-     */
247
-    protected function getHeadersFromEeNotices()
248
-    {
249
-        $headers = array();
250
-        $notices = EE_Error::get_raw_notices();
251
-        foreach ($notices as $notice_type => $sub_notices) {
252
-            if (! is_array($sub_notices)) {
253
-                continue;
254
-            }
255
-            foreach ($sub_notices as $notice_code => $sub_notice) {
256
-                $headers[ 'X-EE4-Notices-'
257
-                          . EEH_Inflector::humanize($notice_type)
258
-                          . '['
259
-                          . $notice_code
260
-                          . ']' ] = strip_tags($sub_notice);
261
-            }
262
-        }
263
-        return apply_filters(
264
-            'FHEE__EventEspresso\core\libraries\rest_api\controllers\Base___get_headers_from_ee_notices__return',
265
-            $headers,
266
-            $this->requested_version,
267
-            $notices
268
-        );
269
-    }
270
-
271
-
272
-    /**
273
-     * Finds which version of the API was requested given the route, and returns it.
274
-     * eg in a request to "mysite.com/wp-json/ee/v4.8.29/events/123" this would return
275
-     * "4.8.29".
276
-     * We should know hte requested version in this model though, so if no route is
277
-     * provided just use what we set earlier
278
-     *
279
-     * @param string $route
280
-     * @return string
281
-     */
282
-    public function getRequestedVersion($route = null)
283
-    {
284
-        if ($route === null) {
285
-            return $this->requested_version;
286
-        }
287
-        $matches = $this->parseRoute(
288
-            $route,
289
-            '~' . EED_Core_Rest_Api::ee_api_namespace_for_regex . '~',
290
-            array('version')
291
-        );
292
-        if (isset($matches['version'])) {
293
-            return $matches['version'];
294
-        } else {
295
-            return EED_Core_Rest_Api::latest_rest_api_version();
296
-        }
297
-    }
298
-
299
-
300
-    /**
301
-     * Applies the regex to the route, then creates an array using the values of
302
-     * $match_keys as keys (but ignores the full pattern match). Returns the array of matches.
303
-     * For example, if you call
304
-     * parse_route( '/ee/v4.8/events', '~\/ee\/v([^/]*)\/(.*)~', array( 'version', 'model' ) )
305
-     * it will return array( 'version' => '4.8', 'model' => 'events' )
306
-     *
307
-     * @param string $route
308
-     * @param string $regex
309
-     * @param array  $match_keys EXCLUDING matching the entire regex
310
-     * @return array where  $match_keys are the keys (the first value of $match_keys
311
-     *                           becomes the first key of the return value, etc. Eg passing in $match_keys of
312
-     *                           array( 'model', 'id' ), will, if the regex is successful, will return
313
-     *                           array( 'model' => 'foo', 'id' => 'bar' )
314
-     * @throws EE_Error if it couldn't be parsed
315
-     */
316
-    public function parseRoute($route, $regex, $match_keys)
317
-    {
318
-        $indexed_matches = array();
319
-        $success = preg_match($regex, $route, $matches);
320
-        if (is_array($matches)) {
321
-            // skip the overall regex match. Who cares
322
-            for ($i = 1; $i <= count($match_keys); $i++) {
323
-                if (! isset($matches[ $i ])) {
324
-                    $success = false;
325
-                } else {
326
-                    $indexed_matches[ $match_keys[ $i - 1 ] ] = $matches[ $i ];
327
-                }
328
-            }
329
-        }
330
-        if (! $success) {
331
-            throw new EE_Error(
332
-                __('We could not parse the URL. Please contact Event Espresso Support', 'event_espresso'),
333
-                'endpoint_parsing_error'
334
-            );
335
-        }
336
-        return $indexed_matches;
337
-    }
338
-
339
-
340
-    /**
341
-     * Gets the body's params (either from JSON or parsed body), which EXCLUDES the GET params and URL params
342
-     *
343
-     * @param \WP_REST_Request $request
344
-     * @return array
345
-     */
346
-    protected function getBodyParams(\WP_REST_Request $request)
347
-    {
348
-        // $request->get_params();
349
-        return array_merge(
350
-            (array) $request->get_body_params(),
351
-            (array) $request->get_json_params()
352
-        );
353
-        // return array_diff_key(
354
-        //    $request->get_params(),
355
-        //     $request->get_url_params(),
356
-        //     $request->get_query_params()
357
-        // );
358
-    }
25
+	/**
26
+	 * @deprecated use all-caps version
27
+	 */
28
+	// @codingStandardsIgnoreStart
29
+	const header_prefix_for_ee = 'X-EE-';
30
+	// @codingStandardsIgnoreEnd
31
+
32
+	const HEADER_PREFIX_FOR_EE = 'X-EE-';
33
+
34
+	/**
35
+	 * @deprecated use all-caps version instead
36
+	 */
37
+	// @codingStandardsIgnoreStart
38
+	const header_prefix_for_wp = 'X-WP-';
39
+	// @codingStandardsIgnoreEnd
40
+
41
+	const HEADER_PREFIX_FOR_WP = 'X-WP-';
42
+
43
+	/**
44
+	 * Contains debug info we'll send back in the response headers
45
+	 *
46
+	 * @var array
47
+	 */
48
+	protected $debug_info = array();
49
+
50
+	/**
51
+	 * Indicates whether or not the API is in debug mode
52
+	 *
53
+	 * @var boolean
54
+	 */
55
+	protected $debug_mode = false;
56
+
57
+	/**
58
+	 * Indicates the version that was requested
59
+	 *
60
+	 * @var string
61
+	 */
62
+	protected $requested_version;
63
+
64
+	/**
65
+	 * flat array of headers to send in the response
66
+	 *
67
+	 * @var array
68
+	 */
69
+	protected $response_headers = array();
70
+
71
+
72
+	public function __construct()
73
+	{
74
+		$this->debug_mode = EED_Core_Rest_Api::debugMode();
75
+		// we are handling a REST request. Don't show a fancy HTML error message is any error comes up
76
+		add_filter('FHEE__EE_Error__get_error__show_normal_exceptions', '__return_true');
77
+	}
78
+
79
+
80
+	/**
81
+	 * Sets the version the user requested
82
+	 *
83
+	 * @param string $version eg '4.8'
84
+	 */
85
+	public function setRequestedVersion($version)
86
+	{
87
+		$this->requested_version = $version;
88
+	}
89
+
90
+
91
+	/**
92
+	 * Sets some debug info that we'll send back in headers
93
+	 *
94
+	 * @param string       $key
95
+	 * @param string|array $info
96
+	 */
97
+	protected function setDebugInfo($key, $info)
98
+	{
99
+		$this->debug_info[ $key ] = $info;
100
+	}
101
+
102
+
103
+	/**
104
+	 * Sets headers for the response
105
+	 *
106
+	 * @param string       $header_key    , excluding the "X-EE-" part
107
+	 * @param array|string $value         if an array, multiple headers will be added, one
108
+	 *                                    for each key in the array
109
+	 * @param boolean      $use_ee_prefix whether to use the EE prefix on the header, or fallback to
110
+	 *                                    the standard WP one
111
+	 */
112
+	protected function setResponseHeader($header_key, $value, $use_ee_prefix = true)
113
+	{
114
+		if (is_array($value)) {
115
+			foreach ($value as $value_key => $value_value) {
116
+				$this->setResponseHeader($header_key . '[' . $value_key . ']', $value_value);
117
+			}
118
+		} else {
119
+			$prefix = $use_ee_prefix ? Base::HEADER_PREFIX_FOR_EE : Base::HEADER_PREFIX_FOR_WP;
120
+			$this->response_headers[ $prefix . $header_key ] = $value;
121
+		}
122
+	}
123
+
124
+
125
+	/**
126
+	 * Returns a flat array of headers to be added to the response
127
+	 *
128
+	 * @return array
129
+	 */
130
+	protected function getResponseHeaders()
131
+	{
132
+		return apply_filters(
133
+			'FHEE__EventEspresso\core\libraries\rest_api\controllers\Base___get_response_headers',
134
+			$this->response_headers,
135
+			$this,
136
+			$this->requested_version
137
+		);
138
+	}
139
+
140
+
141
+	/**
142
+	 * Adds error notices from EE_Error onto the provided \WP_Error
143
+	 *
144
+	 * @param WP_Error $wp_error_response
145
+	 * @return WP_Error
146
+	 */
147
+	protected function addEeErrorsToResponse(WP_Error $wp_error_response)
148
+	{
149
+		$notices_during_checkin = EE_Error::get_raw_notices();
150
+		if (! empty($notices_during_checkin['errors'])) {
151
+			foreach ($notices_during_checkin['errors'] as $error_code => $error_message) {
152
+				$wp_error_response->add(
153
+					sanitize_key($error_code),
154
+					strip_tags($error_message)
155
+				);
156
+			}
157
+		}
158
+		return $wp_error_response;
159
+	}
160
+
161
+
162
+	/**
163
+	 * Sends a response, but also makes sure to attach headers that
164
+	 * are handy for debugging.
165
+	 * Specifically, we assume folks will want to know what exactly was the DB query that got run,
166
+	 * what exactly was the Models query that got run, what capabilities came into play, what fields were omitted from
167
+	 * the response, others?
168
+	 *
169
+	 * @param array|WP_Error|Exception|RestException $response
170
+	 * @return WP_REST_Response
171
+	 */
172
+	public function sendResponse($response)
173
+	{
174
+		if ($response instanceof RestException) {
175
+			$response = new WP_Error($response->getStringCode(), $response->getMessage(), $response->getData());
176
+		}
177
+		if ($response instanceof Exception) {
178
+			$code = $response->getCode() ? $response->getCode() : 'error_occurred';
179
+			$response = new WP_Error($code, $response->getMessage());
180
+		}
181
+		if ($response instanceof WP_Error) {
182
+			$response = $this->addEeErrorsToResponse($response);
183
+			$rest_response = $this->createRestResponseFromWpError($response);
184
+		} else {
185
+			$rest_response = new WP_REST_Response($response, 200);
186
+		}
187
+		$headers = array();
188
+		if ($this->debug_mode && is_array($this->debug_info)) {
189
+			foreach ($this->debug_info as $debug_key => $debug_info) {
190
+				if (is_array($debug_info)) {
191
+					$debug_info = wp_json_encode($debug_info);
192
+				}
193
+				$headers[ 'X-EE4-Debug-' . ucwords($debug_key) ] = $debug_info;
194
+			}
195
+		}
196
+		$headers = array_merge(
197
+			$headers,
198
+			$this->getResponseHeaders(),
199
+			$this->getHeadersFromEeNotices()
200
+		);
201
+		$rest_response->set_headers($headers);
202
+		return $rest_response;
203
+	}
204
+
205
+
206
+	/**
207
+	 * Converts the \WP_Error into `WP_REST_Response.
208
+	 * Mostly this is just a copy-and-paste from \WP_REST_Server::error_to_response
209
+	 * (which is protected)
210
+	 *
211
+	 * @param WP_Error $wp_error
212
+	 * @return WP_REST_Response
213
+	 */
214
+	protected function createRestResponseFromWpError(WP_Error $wp_error)
215
+	{
216
+		$error_data = $wp_error->get_error_data();
217
+		if (is_array($error_data) && isset($error_data['status'])) {
218
+			$status = $error_data['status'];
219
+		} else {
220
+			$status = 500;
221
+		}
222
+		$errors = array();
223
+		foreach ((array) $wp_error->errors as $code => $messages) {
224
+			foreach ((array) $messages as $message) {
225
+				$errors[] = array(
226
+					'code'    => $code,
227
+					'message' => $message,
228
+					'data'    => $wp_error->get_error_data($code),
229
+				);
230
+			}
231
+		}
232
+		$data = isset($errors[0]) ? $errors[0] : array();
233
+		if (count($errors) > 1) {
234
+			// Remove the primary error.
235
+			array_shift($errors);
236
+			$data['additional_errors'] = $errors;
237
+		}
238
+		return new WP_REST_Response($data, $status);
239
+	}
240
+
241
+
242
+	/**
243
+	 * Array of headers derived from EE success, attention, and error messages
244
+	 *
245
+	 * @return array
246
+	 */
247
+	protected function getHeadersFromEeNotices()
248
+	{
249
+		$headers = array();
250
+		$notices = EE_Error::get_raw_notices();
251
+		foreach ($notices as $notice_type => $sub_notices) {
252
+			if (! is_array($sub_notices)) {
253
+				continue;
254
+			}
255
+			foreach ($sub_notices as $notice_code => $sub_notice) {
256
+				$headers[ 'X-EE4-Notices-'
257
+						  . EEH_Inflector::humanize($notice_type)
258
+						  . '['
259
+						  . $notice_code
260
+						  . ']' ] = strip_tags($sub_notice);
261
+			}
262
+		}
263
+		return apply_filters(
264
+			'FHEE__EventEspresso\core\libraries\rest_api\controllers\Base___get_headers_from_ee_notices__return',
265
+			$headers,
266
+			$this->requested_version,
267
+			$notices
268
+		);
269
+	}
270
+
271
+
272
+	/**
273
+	 * Finds which version of the API was requested given the route, and returns it.
274
+	 * eg in a request to "mysite.com/wp-json/ee/v4.8.29/events/123" this would return
275
+	 * "4.8.29".
276
+	 * We should know hte requested version in this model though, so if no route is
277
+	 * provided just use what we set earlier
278
+	 *
279
+	 * @param string $route
280
+	 * @return string
281
+	 */
282
+	public function getRequestedVersion($route = null)
283
+	{
284
+		if ($route === null) {
285
+			return $this->requested_version;
286
+		}
287
+		$matches = $this->parseRoute(
288
+			$route,
289
+			'~' . EED_Core_Rest_Api::ee_api_namespace_for_regex . '~',
290
+			array('version')
291
+		);
292
+		if (isset($matches['version'])) {
293
+			return $matches['version'];
294
+		} else {
295
+			return EED_Core_Rest_Api::latest_rest_api_version();
296
+		}
297
+	}
298
+
299
+
300
+	/**
301
+	 * Applies the regex to the route, then creates an array using the values of
302
+	 * $match_keys as keys (but ignores the full pattern match). Returns the array of matches.
303
+	 * For example, if you call
304
+	 * parse_route( '/ee/v4.8/events', '~\/ee\/v([^/]*)\/(.*)~', array( 'version', 'model' ) )
305
+	 * it will return array( 'version' => '4.8', 'model' => 'events' )
306
+	 *
307
+	 * @param string $route
308
+	 * @param string $regex
309
+	 * @param array  $match_keys EXCLUDING matching the entire regex
310
+	 * @return array where  $match_keys are the keys (the first value of $match_keys
311
+	 *                           becomes the first key of the return value, etc. Eg passing in $match_keys of
312
+	 *                           array( 'model', 'id' ), will, if the regex is successful, will return
313
+	 *                           array( 'model' => 'foo', 'id' => 'bar' )
314
+	 * @throws EE_Error if it couldn't be parsed
315
+	 */
316
+	public function parseRoute($route, $regex, $match_keys)
317
+	{
318
+		$indexed_matches = array();
319
+		$success = preg_match($regex, $route, $matches);
320
+		if (is_array($matches)) {
321
+			// skip the overall regex match. Who cares
322
+			for ($i = 1; $i <= count($match_keys); $i++) {
323
+				if (! isset($matches[ $i ])) {
324
+					$success = false;
325
+				} else {
326
+					$indexed_matches[ $match_keys[ $i - 1 ] ] = $matches[ $i ];
327
+				}
328
+			}
329
+		}
330
+		if (! $success) {
331
+			throw new EE_Error(
332
+				__('We could not parse the URL. Please contact Event Espresso Support', 'event_espresso'),
333
+				'endpoint_parsing_error'
334
+			);
335
+		}
336
+		return $indexed_matches;
337
+	}
338
+
339
+
340
+	/**
341
+	 * Gets the body's params (either from JSON or parsed body), which EXCLUDES the GET params and URL params
342
+	 *
343
+	 * @param \WP_REST_Request $request
344
+	 * @return array
345
+	 */
346
+	protected function getBodyParams(\WP_REST_Request $request)
347
+	{
348
+		// $request->get_params();
349
+		return array_merge(
350
+			(array) $request->get_body_params(),
351
+			(array) $request->get_json_params()
352
+		);
353
+		// return array_diff_key(
354
+		//    $request->get_params(),
355
+		//     $request->get_url_params(),
356
+		//     $request->get_query_params()
357
+		// );
358
+	}
359 359
 }
Please login to merge, or discard this patch.
core/libraries/rest_api/RestIncomingQueryParamMetadata.php 3 patches
Doc Comments   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -179,7 +179,7 @@
 block discarded – undo
179 179
     }
180 180
 
181 181
     /**
182
-     * @param mixed $is_gmt_field
182
+     * @param boolean $is_gmt_field
183 183
      */
184 184
     private function setIsGmtField($is_gmt_field)
185 185
     {
Please login to merge, or discard this patch.
Spacing   +13 added lines, -13 removed lines patch added patch discarded remove patch
@@ -206,7 +206,7 @@  discard block
 block discarded – undo
206 206
             $this->getContext()->getModel()
207 207
         ));
208 208
         // double-check is it a *_gmt field?
209
-        if (!$this->getField() instanceof EE_Model_Field_Base
209
+        if ( ! $this->getField() instanceof EE_Model_Field_Base
210 210
             && ModelDataTranslator::isGmtDateFieldName($this->getQueryParamKeySansStars())
211 211
         ) {
212 212
             // yep, take off '_gmt', and find the field
@@ -304,7 +304,7 @@  discard block
 block discarded – undo
304 304
      */
305 305
     private function valueIsArrayDuringRead()
306 306
     {
307
-        return !$this->getContext()->isWriting() && is_array($this->getQueryParamValue());
307
+        return ! $this->getContext()->isWriting() && is_array($this->getQueryParamValue());
308 308
     }
309 309
 
310 310
     /**
@@ -315,7 +315,7 @@  discard block
 block discarded – undo
315 315
      */
316 316
     private function valueIsAssociativeArray()
317 317
     {
318
-        return !EEH_Array::is_array_numerically_and_sequentially_indexed($this->getQueryParamValue());
318
+        return ! EEH_Array::is_array_numerically_and_sequentially_indexed($this->getQueryParamValue());
319 319
     }
320 320
 
321 321
     /**
@@ -403,7 +403,7 @@  discard block
 block discarded – undo
403 403
     {
404 404
         // the value should be JSON or CSV
405 405
         $values = json_decode($sub_array_value);
406
-        if (!is_array($values)) {
406
+        if ( ! is_array($values)) {
407 407
             $values = array_filter(
408 408
                 array_map(
409 409
                     'trim',
@@ -424,7 +424,7 @@  discard block
 block discarded – undo
424 424
      */
425 425
     private function assertSimplifiedSpecifiedOperator()
426 426
     {
427
-        if (!$this->valueIsSimplifiedSpecifiedOperator() && EED_Core_Rest_Api::debugMode()) {
427
+        if ( ! $this->valueIsSimplifiedSpecifiedOperator() && EED_Core_Rest_Api::debugMode()) {
428 428
             throw new RestException(
429 429
                 'numerically_indexed_array_of_values_only',
430 430
                 sprintf(
@@ -479,7 +479,7 @@  discard block
 block discarded – undo
479 479
         $valid_operators = $this->getContext()->getModel()->valid_operators();
480 480
         $query_param_value = $this->getQueryParamValue();
481 481
         return isset($query_param_value[0])
482
-            && isset($valid_operators[ $query_param_value[0] ]);
482
+            && isset($valid_operators[$query_param_value[0]]);
483 483
     }
484 484
 
485 485
     /**
@@ -497,7 +497,7 @@  discard block
 block discarded – undo
497 497
         )
498 498
             && isset($valueArray[1])
499 499
             && is_array($valueArray[1])
500
-            && !isset($valueArray[2]);
500
+            && ! isset($valueArray[2]);
501 501
     }
502 502
 
503 503
     /**
@@ -512,8 +512,8 @@  discard block
 block discarded – undo
512 512
             && isset($query_param_value[1])
513 513
             && is_array($query_param_value[1])
514 514
             && isset($query_param_value[1][0], $query_param_value[1][1])
515
-            && !isset($query_param_value[1][2])
516
-            && !isset($query_param_value[2]);
515
+            && ! isset($query_param_value[1][2])
516
+            && ! isset($query_param_value[2]);
517 517
     }
518 518
 
519 519
     /**
@@ -527,7 +527,7 @@  discard block
 block discarded – undo
527 527
         $query_param_value = $this->getQueryParamValue();
528 528
         return array_key_exists($operator, $this->getContext()->getModel()->valid_like_style_operators())
529 529
             && isset($query_param_value[1])
530
-            && !isset($query_param_value[2]);
530
+            && ! isset($query_param_value[2]);
531 531
     }
532 532
 
533 533
     /**
@@ -540,7 +540,7 @@  discard block
 block discarded – undo
540 540
     {
541 541
         $query_param_value = $this->getQueryParamValue();
542 542
         return array_key_exists($operator, $this->getContext()->getModel()->valid_null_style_operators())
543
-            && !isset($query_param_value[1]);
543
+            && ! isset($query_param_value[1]);
544 544
     }
545 545
 
546 546
     /**
@@ -554,8 +554,8 @@  discard block
 block discarded – undo
554 554
         $query_param_value = $this->getQueryParamValue();
555 555
         $model = $this->getContext()->getModel();
556 556
         return isset($query_param_value[1])
557
-            && !isset($query_param_value[2])
558
-            && !array_key_exists(
557
+            && ! isset($query_param_value[2])
558
+            && ! array_key_exists(
559 559
                 $operator,
560 560
                 array_merge(
561 561
                     $model->valid_in_style_operators(),
Please login to merge, or discard this patch.
Indentation   +675 added lines, -675 removed lines patch added patch discarded remove patch
@@ -26,681 +26,681 @@
 block discarded – undo
26 26
  */
27 27
 class RestIncomingQueryParamMetadata
28 28
 {
29
-    private $query_param_key;
30
-    private $query_param_value;
31
-    /**
32
-     * @var RestIncomingQueryParamContext
33
-     */
34
-    private $context;
35
-
36
-    /**
37
-     * @var EE_Model_Field_Base|null
38
-     */
39
-    private $field;
40
-
41
-    /**
42
-     * @var string same as $query_param_key but has the * and anything after it removed
43
-     */
44
-    private $query_param_key_sans_stars;
45
-
46
-    /**
47
-     * @var string for timezone or timezone offset
48
-     */
49
-    private $timezone;
50
-
51
-    /**
52
-     * @var boolean if the field in $query_param_key is for a GMT field (eg `EVT_modified_gmt`)
53
-     */
54
-    private $is_gmt_field = false;
55
-
56
-    /**
57
-     * RestIncomingQueryParamMetadata constructor.
58
-     * You probably want to call
59
-     * @param string $query_param_key
60
-     * @param string $query_param_value
61
-     * @param RestIncomingQueryParamContext $context
62
-     */
63
-    public function __construct($query_param_key, $query_param_value, RestIncomingQueryParamContext $context)
64
-    {
65
-        $this->query_param_key = $query_param_key;
66
-        $this->query_param_value = $query_param_value;
67
-        $this->context = $context;
68
-        $this->determineFieldAndTimezone();
69
-    }
70
-
71
-    /**
72
-     * Gets the query parameter key. This may have been modified (see setQueryParamValue())
73
-     * @return string
74
-     */
75
-    public function getQueryParamKey()
76
-    {
77
-        return $this->query_param_key;
78
-    }
79
-
80
-    /**
81
-     * Modifies the query parameter key passed in (Eg this is done when rewriting the simplified specified operator REST
82
-     * query parameters into the legacy structure)
83
-     * @param string|array|int|float $query_param_value
84
-     */
85
-    private function setQueryParamValue($query_param_value)
86
-    {
87
-        $this->query_param_value = $query_param_value;
88
-    }
89
-
90
-    /**
91
-     * Gets the original query parameter value passed in.
92
-     * @return string
93
-     */
94
-    public function getQueryParamValue()
95
-    {
96
-        return $this->query_param_value;
97
-    }
98
-
99
-    /**
100
-     * Gets the context object.
101
-     * @return RestIncomingQueryParamContext
102
-     */
103
-    public function getContext()
104
-    {
105
-        return $this->context;
106
-    }
107
-
108
-    /**
109
-     * Sets the query parameter key. This may be used to rewrite a key into its non-GMT alternative.
110
-     * @param string $query_param_key
111
-     */
112
-    private function setQueryParamKey($query_param_key)
113
-    {
114
-        $this->query_param_key = $query_param_key;
115
-    }
116
-
117
-    /**
118
-     * Gets the field the query parameter key indicated. This may be null (in cases where the query parameter key
119
-     * did not indicate a field, eg if it were `OR`).
120
-     * @return EE_Model_Field_Base|null
121
-     */
122
-    public function getField()
123
-    {
124
-        return $this->field;
125
-    }
126
-
127
-    /**
128
-     * Gets the query parameter key (with the star and everything afterwards removed).
129
-     * @return string
130
-     */
131
-    public function getQueryParamKeySansStars()
132
-    {
133
-        return $this->query_param_key_sans_stars;
134
-    }
135
-
136
-    /**
137
-     * Gets the timezone associated with this model (the site timezone, except for GMT datetime fields).
138
-     * @return string
139
-     */
140
-    public function getTimezone()
141
-    {
142
-        return $this->timezone;
143
-    }
144
-
145
-    /**
146
-     * Returns whether or not this is a GMT field
147
-     * @return boolean
148
-     */
149
-    public function isGmtField()
150
-    {
151
-        return $this->is_gmt_field;
152
-    }
153
-
154
-    /**
155
-     * Sets the field indicated by the query parameter key (might be null).
156
-     * @param EE_Model_Field_Base|null $field
157
-     */
158
-    private function setField(EE_Model_Field_Base $field = null)
159
-    {
160
-        $this->field = $field;
161
-    }
162
-
163
-    /**
164
-     * Sets the query parameter key-with-stars-removed.
165
-     * @param string $query_param_key_sans_stars
166
-     */
167
-    private function setQueryParamKeySansStars($query_param_key_sans_stars)
168
-    {
169
-        $this->query_param_key_sans_stars = $query_param_key_sans_stars;
170
-    }
171
-
172
-    /**
173
-     * Sets the timezone (this could be a timezeon offset string).
174
-     * @param string $timezone
175
-     */
176
-    private function setTimezone($timezone)
177
-    {
178
-        $this->timezone = $timezone;
179
-    }
180
-
181
-    /**
182
-     * @param mixed $is_gmt_field
183
-     */
184
-    private function setIsGmtField($is_gmt_field)
185
-    {
186
-        $this->is_gmt_field = $is_gmt_field;
187
-    }
188
-
189
-    /**
190
-     * Determines what field, query param name, and query param name without stars, and timezone to use.
191
-     * @since 4.9.72.p
192
-     * @type EE_Model_Field_Base $field
193
-     * @return void {
194
-     * @throws EE_Error
195
-     * @throws InvalidDataTypeException
196
-     * @throws InvalidInterfaceException
197
-     * @throws InvalidArgumentException
198
-     */
199
-    private function determineFieldAndTimezone()
200
-    {
201
-        $this->setQueryParamKeySansStars(ModelDataTranslator::removeStarsAndAnythingAfterFromConditionQueryParamKey(
202
-            $this->getQueryParamKey()
203
-        ));
204
-        $this->setField(ModelDataTranslator::deduceFieldFromQueryParam(
205
-            $this->getQueryParamKeySansStars(),
206
-            $this->getContext()->getModel()
207
-        ));
208
-        // double-check is it a *_gmt field?
209
-        if (!$this->getField() instanceof EE_Model_Field_Base
210
-            && ModelDataTranslator::isGmtDateFieldName($this->getQueryParamKeySansStars())
211
-        ) {
212
-            // yep, take off '_gmt', and find the field
213
-            $this->setQueryParamKey(ModelDataTranslator::removeGmtFromFieldName($this->getQueryParamKeySansStars()));
214
-            $this->setField(ModelDataTranslator::deduceFieldFromQueryParam(
215
-                $this->getQueryParamKey(),
216
-                $this->context->getModel()
217
-            ));
218
-            $this->setTimezone('UTC');
219
-            $this->setIsGmtField(true);
220
-        } elseif ($this->getField() instanceof EE_Datetime_Field) {
221
-            // so it's not a GMT field. Set the timezone on the model to the default
222
-            $this->setTimezone(EEH_DTT_Helper::get_valid_timezone_string());
223
-        } else {
224
-            // just keep using what's already set for the timezone
225
-            $this->setTimezone($this->context->getModel()->get_timezone());
226
-        }
227
-    }
228
-
229
-    /**
230
-     * Given a ton of input, determines the value to use for the models.
231
-     * @since 4.9.72.p
232
-     * @return array|null
233
-     * @throws DomainException
234
-     * @throws EE_Error
235
-     * @throws RestException
236
-     * @throws DomainException
237
-     */
238
-    public function determineConditionsQueryParameterValue()
239
-    {
240
-        if ($this->valueIsArrayDuringRead()) {
241
-            return $this->determineModelValueGivenRestInputArray();
242
-        }
243
-        return ModelDataTranslator::prepareFieldValueFromJson(
244
-            $this->getField(),
245
-            $this->getQueryParamValue(),
246
-            $this->getContext()->getRequestedVersion(),
247
-            $this->getTimezone()
248
-        );
249
-    }
250
-
251
-    /**
252
-     * Given that the array value provided was itself an array, handles finding the correct value to pass to the model.
253
-     * @since 4.9.72.p
254
-     * @return array|null
255
-     * @throws RestException
256
-     */
257
-    private function determineModelValueGivenRestInputArray()
258
-    {
259
-        $this->transformSimplifiedSpecifiedOperatorSyntaxIntoStandardSyntax();
260
-        // did they specify an operator?
261
-        if ($this->valueIsLegacySpecifiedOperator()) {
262
-            $query_param_value = $this->getQueryParamValue();
263
-            $sub_array_key = $query_param_value[0];
264
-            $translated_value = array($sub_array_key);
265
-            if ($this->operatorIsNAry($sub_array_key)) {
266
-                $translated_value[] = $this->prepareValuesFromJson($query_param_value[1]);
267
-            } elseif ($this->operatorIsTernary($sub_array_key)) {
268
-                $translated_value[] = array(
269
-                    $this->prepareValuesFromJson($query_param_value[1][0]),
270
-                    $this->prepareValuesFromJson($query_param_value[1][1])
271
-                );
272
-            } elseif ($this->operatorIsLike($sub_array_key)) {
273
-                // we want to leave this value mostly-as-is (eg don't force it to be a float
274
-                // or a boolean or an enum value. Leave it as-is with wildcards etc)
275
-                // but do verify it at least doesn't have any serialized data
276
-                ModelDataTranslator::throwExceptionIfContainsSerializedData($query_param_value[1]);
277
-                $translated_value[] = $query_param_value[1];
278
-            } elseif ($this->operatorIsUnary($sub_array_key)) {
279
-                // no arguments should have been provided, so don't look for any
280
-            } elseif ($this->operatorisBinary($sub_array_key)) {
281
-                // it's a valid operator, but none of the exceptions. Treat it normally.
282
-                $translated_value[] = $this->prepareValuesFromJson($query_param_value[1]);
283
-            } else {
284
-                // so they provided a valid operator, but wrong number of arguments
285
-                $this->throwWrongNumberOfArgsExceptionIfDebugging($sub_array_key);
286
-                $translated_value = null;
287
-            }
288
-        } else {
289
-            // so they didn't provide a valid operator
290
-            // if we aren't in debug mode, then just try our best to fulfill the user's request
291
-            $this->throwInvalidOperatorExceptionIfDebugging();
292
-            $translated_value = null;
293
-        }
294
-        return $translated_value;
295
-    }
296
-
297
-    /**
298
-     * Returns if this request is a "read" request and the value provided was an array.
299
-     * This will indicate is such things as `array('<', 123)` and `array('IN', array(1,2,3))` are acceptable or not.
300
-     * @since 4.9.72.p
301
-     * @return boolean
302
-     */
303
-    private function valueIsArrayDuringRead()
304
-    {
305
-        return !$this->getContext()->isWriting() && is_array($this->getQueryParamValue());
306
-    }
307
-
308
-    /**
309
-     * Returns if the value provided was an associative array (we should have already verified it's an array of some
310
-     * sort). If the value is an associative array, it had better be in the simplified specified operator structure.
311
-     * @since 4.9.72.p
312
-     * @return boolean
313
-     */
314
-    private function valueIsAssociativeArray()
315
-    {
316
-        return !EEH_Array::is_array_numerically_and_sequentially_indexed($this->getQueryParamValue());
317
-    }
318
-
319
-    /**
320
-     * Checks if the array value is itself an array that fits into the simplified specified operator structure
321
-     * (eg `array('!=' => 123)`).
322
-     * @since 4.9.72.p
323
-     * @return boolean
324
-     */
325
-    private function valueIsSimplifiedSpecifiedOperator()
326
-    {
327
-        return count($this->getQueryParamValue()) === 1
328
-            && array_key_exists(
329
-                key($this->getQueryParamValue()),
330
-                $this->getContext()->getModel()->valid_operators()
331
-            );
332
-    }
333
-
334
-    /**
335
-     * Throws an exception if the sub-value is an array (eg `array('!=' => array())`). It needs to just be a string,
336
-     * of either comma-separated-values, or a JSON array.
337
-     * @since 4.9.72.p
338
-     * @param $sub_array_key
339
-     * @param $sub_array_value
340
-     * @throws RestException
341
-     */
342
-    private function assertSubValueIsntArray($sub_array_key, $sub_array_value)
343
-    {
344
-        if (is_array($sub_array_value) && EED_Core_Rest_Api::debugMode()) {
345
-            throw new RestException(
346
-                'csv_or_json_string_only',
347
-                sprintf(
348
-                    /* translators: 1: variable name*/
349
-                    esc_html__(
350
-                        'The value provided for the operator "%1$s" should be comma-separated value string or a JSON array.',
351
-                        'event_espresso'
352
-                    ),
353
-                    $sub_array_key
354
-                ),
355
-                array(
356
-                    'status' => 400,
357
-                )
358
-            );
359
-        }
360
-    }
361
-
362
-    /**
363
-     * Determines if the sub-array key is an operator taking 3 or more operators.
364
-     * @since 4.9.72.p
365
-     * @param $sub_array_key
366
-     * @return boolean
367
-     */
368
-    private function subArrayKeyIsNonBinaryOperator($sub_array_key)
369
-    {
370
-        return array_key_exists(
371
-            $sub_array_key,
372
-            array_merge(
373
-                $this->getContext()->getModel()->valid_in_style_operators(),
374
-                $this->getContext()->getModel()->valid_between_style_operators()
375
-            )
376
-        );
377
-    }
378
-
379
-    /**
380
-     * Given that the $sub_array_key is a string, checks if it's an operator taking only 1 argument.
381
-     * @since 4.9.72.p
382
-     * @param string $sub_array_key
383
-     * @return boolean
384
-     */
385
-    private function subArrayKeyIsUnaryOperator($sub_array_key)
386
-    {
387
-        return array_key_exists(
388
-            $sub_array_key,
389
-            $this->getContext()->getModel()->valid_null_style_operators()
390
-        );
391
-    }
392
-
393
-    /**
394
-     * Parses the $sub_array_value string into an array (given it could either be a comma-separated-list or a JSON
395
-     * array). eg `"1,2,3"` or `"[1,2,3]"` into `array(1,2,3)`.
396
-     * @since 4.9.72.p
397
-     * @param $sub_array_value
398
-     * @return array|mixed|object
399
-     */
400
-    private function extractQuickStyleSpecifiedOperatorValue($sub_array_value)
401
-    {
402
-        // the value should be JSON or CSV
403
-        $values = json_decode($sub_array_value);
404
-        if (!is_array($values)) {
405
-            $values = array_filter(
406
-                array_map(
407
-                    'trim',
408
-                    explode(
409
-                        ',',
410
-                        $sub_array_value
411
-                    )
412
-                )
413
-            );
414
-        }
415
-        return $values;
416
-    }
417
-
418
-    /**
419
-     * Throws an exception if the value isn't a simplified specified operator (only called when we expect that).
420
-     * @since 4.9.72.p
421
-     * @throws RestException
422
-     */
423
-    private function assertSimplifiedSpecifiedOperator()
424
-    {
425
-        if (!$this->valueIsSimplifiedSpecifiedOperator() && EED_Core_Rest_Api::debugMode()) {
426
-            throw new RestException(
427
-                'numerically_indexed_array_of_values_only',
428
-                sprintf(
429
-                    /* translators: 1: variable name*/
430
-                    esc_html__(
431
-                        'The array provided for the parameter "%1$s" should be numerically indexed.',
432
-                        'event_espresso'
433
-                    ),
434
-                    $this->getQueryParamKey()
435
-                ),
436
-                array(
437
-                    'status' => 400,
438
-                )
439
-            );
440
-        }
441
-    }
442
-
443
-    /**
444
-     * If query_param_value were in the simplified specific operator structure, change it into the legacy structure.
445
-     * @since 4.9.72.p
446
-     * @throws RestException
447
-     */
448
-    private function transformSimplifiedSpecifiedOperatorSyntaxIntoStandardSyntax()
449
-    {
450
-        if ($this->valueIsAssociativeArray()) {
451
-            $this->assertSimplifiedSpecifiedOperator();
452
-            $query_param_value = $this->getQueryParamValue();
453
-            $sub_array_value = reset($query_param_value);
454
-            $sub_array_key = key($query_param_value);
455
-            $this->assertSubValueIsntArray($sub_array_key, $sub_array_value);
456
-            // they're doing something like "&where[EVT_ID][IN]=1,2,3" or "&where[EVT_ID][>]=5"
457
-            if ($this->subArrayKeyIsNonBinaryOperator($sub_array_key)) {
458
-                $this->setQueryParamValue(array(
459
-                    $sub_array_key,
460
-                    $this->extractQuickStyleSpecifiedOperatorValue($sub_array_value)
461
-                ));
462
-            } elseif ($this->subArrayKeyIsUnaryOperator($sub_array_key)) {
463
-                $this->setQueryParamValue(array($sub_array_key));
464
-            } else {
465
-                $this->setQueryParamValue(array($sub_array_key, $sub_array_value));
466
-            }
467
-        }
468
-    }
469
-
470
-    /**
471
-     * Returns true is the value is an array using the legacy structure to specify the operator. Eg `array('!=',123)`.
472
-     * @since 4.9.72.p
473
-     * @return boolean
474
-     */
475
-    private function valueIsLegacySpecifiedOperator()
476
-    {
477
-        $valid_operators = $this->getContext()->getModel()->valid_operators();
478
-        $query_param_value = $this->getQueryParamValue();
479
-        return isset($query_param_value[0])
480
-            && isset($valid_operators[ $query_param_value[0] ]);
481
-    }
482
-
483
-    /**
484
-     * Returns true if the value specified operator accepts arbitrary number of arguments, like "IN".
485
-     * @since 4.9.72.p
486
-     * @param $operator
487
-     * @return boolean
488
-     */
489
-    private function operatorIsNAry($operator)
490
-    {
491
-        $valueArray = $this->getQueryParamValue();
492
-        return array_key_exists(
493
-            $operator,
494
-            $this->getContext()->getModel()->valid_in_style_operators()
495
-        )
496
-            && isset($valueArray[1])
497
-            && is_array($valueArray[1])
498
-            && !isset($valueArray[2]);
499
-    }
500
-
501
-    /**
502
-     * Returns true if the operator accepts 3 arguments (eg "BETWEEN").
503
-     * So we're looking for a value that looks like
504
-     * `array('BETWEEN', array('2015-01-01T00:00:00', '2016-01-01T00:00:00'))`.
505
-     * @since 4.9.72.p
506
-     * @param $operator
507
-     * @return boolean
508
-     */
509
-    private function operatorIsTernary($operator)
510
-    {
511
-        $query_param_value = $this->getQueryParamValue();
512
-        return array_key_exists($operator, $this->getContext()->getModel()->valid_between_style_operators())
513
-            && isset($query_param_value[1])
514
-            && is_array($query_param_value[1])
515
-            && isset($query_param_value[1][0], $query_param_value[1][1])
516
-            && !isset($query_param_value[1][2])
517
-            && !isset($query_param_value[2]);
518
-    }
519
-
520
-    /**
521
-     * Returns true if the operator is a similar to LIKE, indicating the value may have wildcards we should leave alone.
522
-     * @since 4.9.72.p
523
-     * @param $operator
524
-     * @return boolean
525
-     */
526
-    private function operatorIsLike($operator)
527
-    {
528
-        $query_param_value = $this->getQueryParamValue();
529
-        return array_key_exists($operator, $this->getContext()->getModel()->valid_like_style_operators())
530
-            && isset($query_param_value[1])
531
-            && !isset($query_param_value[2]);
532
-    }
533
-
534
-    /**
535
-     * Returns true if the operator only takes one argument (eg it's like `IS NULL`).
536
-     * @since 4.9.72.p
537
-     * @param $operator
538
-     * @return boolean
539
-     */
540
-    private function operatorIsUnary($operator)
541
-    {
542
-        $query_param_value = $this->getQueryParamValue();
543
-        return array_key_exists($operator, $this->getContext()->getModel()->valid_null_style_operators())
544
-            && !isset($query_param_value[1]);
545
-    }
546
-
547
-    /**
548
-     * Returns true if the operator specified is a binary opeator (eg `=`, `!=`)
549
-     * @since 4.9.72.p
550
-     * @param $operator
551
-     * @return boolean
552
-     */
553
-    private function operatorisBinary($operator)
554
-    {
555
-        $query_param_value = $this->getQueryParamValue();
556
-        $model = $this->getContext()->getModel();
557
-        return isset($query_param_value[1])
558
-            && !isset($query_param_value[2])
559
-            && !array_key_exists(
560
-                $operator,
561
-                array_merge(
562
-                    $model->valid_in_style_operators(),
563
-                    $model->valid_null_style_operators(),
564
-                    $model->valid_like_style_operators(),
565
-                    $model->valid_between_style_operators()
566
-                )
567
-            );
568
-    }
569
-
570
-    /**
571
-     * If we're debugging, throws an exception saying that the wrong number of arguments was provided.
572
-     * @since 4.9.72.p
573
-     * @param $operator
574
-     * @throws RestException
575
-     */
576
-    private function throwWrongNumberOfArgsExceptionIfDebugging($operator)
577
-    {
578
-        if (EED_Core_Rest_Api::debugMode()) {
579
-            throw new RestException(
580
-                'wrong_number_of_arguments',
581
-                sprintf(
582
-                    esc_html__(
583
-                        'The operator you provided, "%1$s" had the wrong number of arguments',
584
-                        'event_espresso'
585
-                    ),
586
-                    $operator
587
-                ),
588
-                array(
589
-                    'status' => 400,
590
-                )
591
-            );
592
-        }
593
-    }
594
-
595
-    /**
596
-     * Wrapper for ModelDataTranslator::prepareFieldValuesFromJson(), just a tad more DRY.
597
-     * @since 4.9.72.p
598
-     * @param $value
599
-     * @return mixed
600
-     * @throws RestException
601
-     */
602
-    private function prepareValuesFromJson($value)
603
-    {
604
-        return ModelDataTranslator::prepareFieldValuesFromJson(
605
-            $this->getField(),
606
-            $value,
607
-            $this->getContext()->getRequestedVersion(),
608
-            $this->getTimezone()
609
-        );
610
-    }
611
-
612
-    /**
613
-     * Throws an exception if an invalid operator was specified and we're debugging.
614
-     * @since 4.9.72.p
615
-     * @throws RestException
616
-     */
617
-    private function throwInvalidOperatorExceptionIfDebugging()
618
-    {
619
-        // so they didn't provide a valid operator
620
-        if (EED_Core_Rest_Api::debugMode()) {
621
-            throw new RestException(
622
-                'invalid_operator',
623
-                sprintf(
624
-                    esc_html__(
625
-                        'You provided an invalid parameter, with key "%1$s" and value "%2$s"',
626
-                        'event_espresso'
627
-                    ),
628
-                    $this->getQueryParamKey(),
629
-                    $this->getQueryParamValue()
630
-                ),
631
-                array(
632
-                    'status' => 400,
633
-                )
634
-            );
635
-        }
636
-    }
637
-
638
-    /**
639
-     * Returns true if the query_param_key was a logic query parameter, eg `OR`, `AND`, `NOT`, `OR*`, etc.
640
-     * @since 4.9.72.p
641
-     * @return boolean
642
-     */
643
-    private function isLogicQueryParam()
644
-    {
645
-        return in_array($this->getQueryParamKeySansStars(), $this->getContext()->getModel()->logic_query_param_keys());
646
-    }
647
-
648
-
649
-    /**
650
-     * If the query param isn't for a field, it must be a nested query parameter which requires different logic.
651
-     * @since 4.9.72.p
652
-     * @return array
653
-     * @throws DomainException
654
-     * @throws EE_Error
655
-     * @throws RestException
656
-     * @throws InvalidDataTypeException
657
-     * @throws InvalidInterfaceException
658
-     * @throws InvalidArgumentException
659
-     */
660
-    public function determineNestedConditionQueryParameters()
661
-    {
662
-
663
-        // so this param doesn't correspond to a field eh?
664
-        if ($this->getContext()->isWriting()) {
665
-            // always tell API clients about invalid parameters when they're creating data. Otherwise,
666
-            // they are probably going to create invalid data
667
-            throw new RestException(
668
-                'invalid_field',
669
-                sprintf(
670
-                    /* translators: 1: variable name */
671
-                    esc_html__('You have provided an invalid parameter: "%1$s"', 'event_espresso'),
672
-                    $this->getQueryParamKey()
673
-                )
674
-            );
675
-        }
676
-        // so it's not for a field, is it a logic query param key?
677
-        if ($this->isLogicQueryParam()) {
678
-            return ModelDataTranslator::prepareConditionsQueryParamsForModels(
679
-                $this->getQueryParamValue(),
680
-                $this->getContext()->getModel(),
681
-                $this->getContext()->getRequestedVersion()
682
-            );
683
-        }
684
-        if (EED_Core_Rest_Api::debugMode()) {
685
-            // only tell API clients they got it wrong if we're in debug mode
686
-            // otherwise try our best ot fulfill their request by ignoring this invalid data
687
-            throw new RestException(
688
-                'invalid_parameter',
689
-                sprintf(
690
-                    /* translators: 1: variable name */
691
-                    esc_html__(
692
-                        'You provided an invalid parameter, with key "%1$s"',
693
-                        'event_espresso'
694
-                    ),
695
-                    $this->getQueryParamKey()
696
-                ),
697
-                array(
698
-                    'status' => 400,
699
-                )
700
-            );
701
-        }
702
-        return null;
703
-    }
29
+	private $query_param_key;
30
+	private $query_param_value;
31
+	/**
32
+	 * @var RestIncomingQueryParamContext
33
+	 */
34
+	private $context;
35
+
36
+	/**
37
+	 * @var EE_Model_Field_Base|null
38
+	 */
39
+	private $field;
40
+
41
+	/**
42
+	 * @var string same as $query_param_key but has the * and anything after it removed
43
+	 */
44
+	private $query_param_key_sans_stars;
45
+
46
+	/**
47
+	 * @var string for timezone or timezone offset
48
+	 */
49
+	private $timezone;
50
+
51
+	/**
52
+	 * @var boolean if the field in $query_param_key is for a GMT field (eg `EVT_modified_gmt`)
53
+	 */
54
+	private $is_gmt_field = false;
55
+
56
+	/**
57
+	 * RestIncomingQueryParamMetadata constructor.
58
+	 * You probably want to call
59
+	 * @param string $query_param_key
60
+	 * @param string $query_param_value
61
+	 * @param RestIncomingQueryParamContext $context
62
+	 */
63
+	public function __construct($query_param_key, $query_param_value, RestIncomingQueryParamContext $context)
64
+	{
65
+		$this->query_param_key = $query_param_key;
66
+		$this->query_param_value = $query_param_value;
67
+		$this->context = $context;
68
+		$this->determineFieldAndTimezone();
69
+	}
70
+
71
+	/**
72
+	 * Gets the query parameter key. This may have been modified (see setQueryParamValue())
73
+	 * @return string
74
+	 */
75
+	public function getQueryParamKey()
76
+	{
77
+		return $this->query_param_key;
78
+	}
79
+
80
+	/**
81
+	 * Modifies the query parameter key passed in (Eg this is done when rewriting the simplified specified operator REST
82
+	 * query parameters into the legacy structure)
83
+	 * @param string|array|int|float $query_param_value
84
+	 */
85
+	private function setQueryParamValue($query_param_value)
86
+	{
87
+		$this->query_param_value = $query_param_value;
88
+	}
89
+
90
+	/**
91
+	 * Gets the original query parameter value passed in.
92
+	 * @return string
93
+	 */
94
+	public function getQueryParamValue()
95
+	{
96
+		return $this->query_param_value;
97
+	}
98
+
99
+	/**
100
+	 * Gets the context object.
101
+	 * @return RestIncomingQueryParamContext
102
+	 */
103
+	public function getContext()
104
+	{
105
+		return $this->context;
106
+	}
107
+
108
+	/**
109
+	 * Sets the query parameter key. This may be used to rewrite a key into its non-GMT alternative.
110
+	 * @param string $query_param_key
111
+	 */
112
+	private function setQueryParamKey($query_param_key)
113
+	{
114
+		$this->query_param_key = $query_param_key;
115
+	}
116
+
117
+	/**
118
+	 * Gets the field the query parameter key indicated. This may be null (in cases where the query parameter key
119
+	 * did not indicate a field, eg if it were `OR`).
120
+	 * @return EE_Model_Field_Base|null
121
+	 */
122
+	public function getField()
123
+	{
124
+		return $this->field;
125
+	}
126
+
127
+	/**
128
+	 * Gets the query parameter key (with the star and everything afterwards removed).
129
+	 * @return string
130
+	 */
131
+	public function getQueryParamKeySansStars()
132
+	{
133
+		return $this->query_param_key_sans_stars;
134
+	}
135
+
136
+	/**
137
+	 * Gets the timezone associated with this model (the site timezone, except for GMT datetime fields).
138
+	 * @return string
139
+	 */
140
+	public function getTimezone()
141
+	{
142
+		return $this->timezone;
143
+	}
144
+
145
+	/**
146
+	 * Returns whether or not this is a GMT field
147
+	 * @return boolean
148
+	 */
149
+	public function isGmtField()
150
+	{
151
+		return $this->is_gmt_field;
152
+	}
153
+
154
+	/**
155
+	 * Sets the field indicated by the query parameter key (might be null).
156
+	 * @param EE_Model_Field_Base|null $field
157
+	 */
158
+	private function setField(EE_Model_Field_Base $field = null)
159
+	{
160
+		$this->field = $field;
161
+	}
162
+
163
+	/**
164
+	 * Sets the query parameter key-with-stars-removed.
165
+	 * @param string $query_param_key_sans_stars
166
+	 */
167
+	private function setQueryParamKeySansStars($query_param_key_sans_stars)
168
+	{
169
+		$this->query_param_key_sans_stars = $query_param_key_sans_stars;
170
+	}
171
+
172
+	/**
173
+	 * Sets the timezone (this could be a timezeon offset string).
174
+	 * @param string $timezone
175
+	 */
176
+	private function setTimezone($timezone)
177
+	{
178
+		$this->timezone = $timezone;
179
+	}
180
+
181
+	/**
182
+	 * @param mixed $is_gmt_field
183
+	 */
184
+	private function setIsGmtField($is_gmt_field)
185
+	{
186
+		$this->is_gmt_field = $is_gmt_field;
187
+	}
188
+
189
+	/**
190
+	 * Determines what field, query param name, and query param name without stars, and timezone to use.
191
+	 * @since 4.9.72.p
192
+	 * @type EE_Model_Field_Base $field
193
+	 * @return void {
194
+	 * @throws EE_Error
195
+	 * @throws InvalidDataTypeException
196
+	 * @throws InvalidInterfaceException
197
+	 * @throws InvalidArgumentException
198
+	 */
199
+	private function determineFieldAndTimezone()
200
+	{
201
+		$this->setQueryParamKeySansStars(ModelDataTranslator::removeStarsAndAnythingAfterFromConditionQueryParamKey(
202
+			$this->getQueryParamKey()
203
+		));
204
+		$this->setField(ModelDataTranslator::deduceFieldFromQueryParam(
205
+			$this->getQueryParamKeySansStars(),
206
+			$this->getContext()->getModel()
207
+		));
208
+		// double-check is it a *_gmt field?
209
+		if (!$this->getField() instanceof EE_Model_Field_Base
210
+			&& ModelDataTranslator::isGmtDateFieldName($this->getQueryParamKeySansStars())
211
+		) {
212
+			// yep, take off '_gmt', and find the field
213
+			$this->setQueryParamKey(ModelDataTranslator::removeGmtFromFieldName($this->getQueryParamKeySansStars()));
214
+			$this->setField(ModelDataTranslator::deduceFieldFromQueryParam(
215
+				$this->getQueryParamKey(),
216
+				$this->context->getModel()
217
+			));
218
+			$this->setTimezone('UTC');
219
+			$this->setIsGmtField(true);
220
+		} elseif ($this->getField() instanceof EE_Datetime_Field) {
221
+			// so it's not a GMT field. Set the timezone on the model to the default
222
+			$this->setTimezone(EEH_DTT_Helper::get_valid_timezone_string());
223
+		} else {
224
+			// just keep using what's already set for the timezone
225
+			$this->setTimezone($this->context->getModel()->get_timezone());
226
+		}
227
+	}
228
+
229
+	/**
230
+	 * Given a ton of input, determines the value to use for the models.
231
+	 * @since 4.9.72.p
232
+	 * @return array|null
233
+	 * @throws DomainException
234
+	 * @throws EE_Error
235
+	 * @throws RestException
236
+	 * @throws DomainException
237
+	 */
238
+	public function determineConditionsQueryParameterValue()
239
+	{
240
+		if ($this->valueIsArrayDuringRead()) {
241
+			return $this->determineModelValueGivenRestInputArray();
242
+		}
243
+		return ModelDataTranslator::prepareFieldValueFromJson(
244
+			$this->getField(),
245
+			$this->getQueryParamValue(),
246
+			$this->getContext()->getRequestedVersion(),
247
+			$this->getTimezone()
248
+		);
249
+	}
250
+
251
+	/**
252
+	 * Given that the array value provided was itself an array, handles finding the correct value to pass to the model.
253
+	 * @since 4.9.72.p
254
+	 * @return array|null
255
+	 * @throws RestException
256
+	 */
257
+	private function determineModelValueGivenRestInputArray()
258
+	{
259
+		$this->transformSimplifiedSpecifiedOperatorSyntaxIntoStandardSyntax();
260
+		// did they specify an operator?
261
+		if ($this->valueIsLegacySpecifiedOperator()) {
262
+			$query_param_value = $this->getQueryParamValue();
263
+			$sub_array_key = $query_param_value[0];
264
+			$translated_value = array($sub_array_key);
265
+			if ($this->operatorIsNAry($sub_array_key)) {
266
+				$translated_value[] = $this->prepareValuesFromJson($query_param_value[1]);
267
+			} elseif ($this->operatorIsTernary($sub_array_key)) {
268
+				$translated_value[] = array(
269
+					$this->prepareValuesFromJson($query_param_value[1][0]),
270
+					$this->prepareValuesFromJson($query_param_value[1][1])
271
+				);
272
+			} elseif ($this->operatorIsLike($sub_array_key)) {
273
+				// we want to leave this value mostly-as-is (eg don't force it to be a float
274
+				// or a boolean or an enum value. Leave it as-is with wildcards etc)
275
+				// but do verify it at least doesn't have any serialized data
276
+				ModelDataTranslator::throwExceptionIfContainsSerializedData($query_param_value[1]);
277
+				$translated_value[] = $query_param_value[1];
278
+			} elseif ($this->operatorIsUnary($sub_array_key)) {
279
+				// no arguments should have been provided, so don't look for any
280
+			} elseif ($this->operatorisBinary($sub_array_key)) {
281
+				// it's a valid operator, but none of the exceptions. Treat it normally.
282
+				$translated_value[] = $this->prepareValuesFromJson($query_param_value[1]);
283
+			} else {
284
+				// so they provided a valid operator, but wrong number of arguments
285
+				$this->throwWrongNumberOfArgsExceptionIfDebugging($sub_array_key);
286
+				$translated_value = null;
287
+			}
288
+		} else {
289
+			// so they didn't provide a valid operator
290
+			// if we aren't in debug mode, then just try our best to fulfill the user's request
291
+			$this->throwInvalidOperatorExceptionIfDebugging();
292
+			$translated_value = null;
293
+		}
294
+		return $translated_value;
295
+	}
296
+
297
+	/**
298
+	 * Returns if this request is a "read" request and the value provided was an array.
299
+	 * This will indicate is such things as `array('<', 123)` and `array('IN', array(1,2,3))` are acceptable or not.
300
+	 * @since 4.9.72.p
301
+	 * @return boolean
302
+	 */
303
+	private function valueIsArrayDuringRead()
304
+	{
305
+		return !$this->getContext()->isWriting() && is_array($this->getQueryParamValue());
306
+	}
307
+
308
+	/**
309
+	 * Returns if the value provided was an associative array (we should have already verified it's an array of some
310
+	 * sort). If the value is an associative array, it had better be in the simplified specified operator structure.
311
+	 * @since 4.9.72.p
312
+	 * @return boolean
313
+	 */
314
+	private function valueIsAssociativeArray()
315
+	{
316
+		return !EEH_Array::is_array_numerically_and_sequentially_indexed($this->getQueryParamValue());
317
+	}
318
+
319
+	/**
320
+	 * Checks if the array value is itself an array that fits into the simplified specified operator structure
321
+	 * (eg `array('!=' => 123)`).
322
+	 * @since 4.9.72.p
323
+	 * @return boolean
324
+	 */
325
+	private function valueIsSimplifiedSpecifiedOperator()
326
+	{
327
+		return count($this->getQueryParamValue()) === 1
328
+			&& array_key_exists(
329
+				key($this->getQueryParamValue()),
330
+				$this->getContext()->getModel()->valid_operators()
331
+			);
332
+	}
333
+
334
+	/**
335
+	 * Throws an exception if the sub-value is an array (eg `array('!=' => array())`). It needs to just be a string,
336
+	 * of either comma-separated-values, or a JSON array.
337
+	 * @since 4.9.72.p
338
+	 * @param $sub_array_key
339
+	 * @param $sub_array_value
340
+	 * @throws RestException
341
+	 */
342
+	private function assertSubValueIsntArray($sub_array_key, $sub_array_value)
343
+	{
344
+		if (is_array($sub_array_value) && EED_Core_Rest_Api::debugMode()) {
345
+			throw new RestException(
346
+				'csv_or_json_string_only',
347
+				sprintf(
348
+					/* translators: 1: variable name*/
349
+					esc_html__(
350
+						'The value provided for the operator "%1$s" should be comma-separated value string or a JSON array.',
351
+						'event_espresso'
352
+					),
353
+					$sub_array_key
354
+				),
355
+				array(
356
+					'status' => 400,
357
+				)
358
+			);
359
+		}
360
+	}
361
+
362
+	/**
363
+	 * Determines if the sub-array key is an operator taking 3 or more operators.
364
+	 * @since 4.9.72.p
365
+	 * @param $sub_array_key
366
+	 * @return boolean
367
+	 */
368
+	private function subArrayKeyIsNonBinaryOperator($sub_array_key)
369
+	{
370
+		return array_key_exists(
371
+			$sub_array_key,
372
+			array_merge(
373
+				$this->getContext()->getModel()->valid_in_style_operators(),
374
+				$this->getContext()->getModel()->valid_between_style_operators()
375
+			)
376
+		);
377
+	}
378
+
379
+	/**
380
+	 * Given that the $sub_array_key is a string, checks if it's an operator taking only 1 argument.
381
+	 * @since 4.9.72.p
382
+	 * @param string $sub_array_key
383
+	 * @return boolean
384
+	 */
385
+	private function subArrayKeyIsUnaryOperator($sub_array_key)
386
+	{
387
+		return array_key_exists(
388
+			$sub_array_key,
389
+			$this->getContext()->getModel()->valid_null_style_operators()
390
+		);
391
+	}
392
+
393
+	/**
394
+	 * Parses the $sub_array_value string into an array (given it could either be a comma-separated-list or a JSON
395
+	 * array). eg `"1,2,3"` or `"[1,2,3]"` into `array(1,2,3)`.
396
+	 * @since 4.9.72.p
397
+	 * @param $sub_array_value
398
+	 * @return array|mixed|object
399
+	 */
400
+	private function extractQuickStyleSpecifiedOperatorValue($sub_array_value)
401
+	{
402
+		// the value should be JSON or CSV
403
+		$values = json_decode($sub_array_value);
404
+		if (!is_array($values)) {
405
+			$values = array_filter(
406
+				array_map(
407
+					'trim',
408
+					explode(
409
+						',',
410
+						$sub_array_value
411
+					)
412
+				)
413
+			);
414
+		}
415
+		return $values;
416
+	}
417
+
418
+	/**
419
+	 * Throws an exception if the value isn't a simplified specified operator (only called when we expect that).
420
+	 * @since 4.9.72.p
421
+	 * @throws RestException
422
+	 */
423
+	private function assertSimplifiedSpecifiedOperator()
424
+	{
425
+		if (!$this->valueIsSimplifiedSpecifiedOperator() && EED_Core_Rest_Api::debugMode()) {
426
+			throw new RestException(
427
+				'numerically_indexed_array_of_values_only',
428
+				sprintf(
429
+					/* translators: 1: variable name*/
430
+					esc_html__(
431
+						'The array provided for the parameter "%1$s" should be numerically indexed.',
432
+						'event_espresso'
433
+					),
434
+					$this->getQueryParamKey()
435
+				),
436
+				array(
437
+					'status' => 400,
438
+				)
439
+			);
440
+		}
441
+	}
442
+
443
+	/**
444
+	 * If query_param_value were in the simplified specific operator structure, change it into the legacy structure.
445
+	 * @since 4.9.72.p
446
+	 * @throws RestException
447
+	 */
448
+	private function transformSimplifiedSpecifiedOperatorSyntaxIntoStandardSyntax()
449
+	{
450
+		if ($this->valueIsAssociativeArray()) {
451
+			$this->assertSimplifiedSpecifiedOperator();
452
+			$query_param_value = $this->getQueryParamValue();
453
+			$sub_array_value = reset($query_param_value);
454
+			$sub_array_key = key($query_param_value);
455
+			$this->assertSubValueIsntArray($sub_array_key, $sub_array_value);
456
+			// they're doing something like "&where[EVT_ID][IN]=1,2,3" or "&where[EVT_ID][>]=5"
457
+			if ($this->subArrayKeyIsNonBinaryOperator($sub_array_key)) {
458
+				$this->setQueryParamValue(array(
459
+					$sub_array_key,
460
+					$this->extractQuickStyleSpecifiedOperatorValue($sub_array_value)
461
+				));
462
+			} elseif ($this->subArrayKeyIsUnaryOperator($sub_array_key)) {
463
+				$this->setQueryParamValue(array($sub_array_key));
464
+			} else {
465
+				$this->setQueryParamValue(array($sub_array_key, $sub_array_value));
466
+			}
467
+		}
468
+	}
469
+
470
+	/**
471
+	 * Returns true is the value is an array using the legacy structure to specify the operator. Eg `array('!=',123)`.
472
+	 * @since 4.9.72.p
473
+	 * @return boolean
474
+	 */
475
+	private function valueIsLegacySpecifiedOperator()
476
+	{
477
+		$valid_operators = $this->getContext()->getModel()->valid_operators();
478
+		$query_param_value = $this->getQueryParamValue();
479
+		return isset($query_param_value[0])
480
+			&& isset($valid_operators[ $query_param_value[0] ]);
481
+	}
482
+
483
+	/**
484
+	 * Returns true if the value specified operator accepts arbitrary number of arguments, like "IN".
485
+	 * @since 4.9.72.p
486
+	 * @param $operator
487
+	 * @return boolean
488
+	 */
489
+	private function operatorIsNAry($operator)
490
+	{
491
+		$valueArray = $this->getQueryParamValue();
492
+		return array_key_exists(
493
+			$operator,
494
+			$this->getContext()->getModel()->valid_in_style_operators()
495
+		)
496
+			&& isset($valueArray[1])
497
+			&& is_array($valueArray[1])
498
+			&& !isset($valueArray[2]);
499
+	}
500
+
501
+	/**
502
+	 * Returns true if the operator accepts 3 arguments (eg "BETWEEN").
503
+	 * So we're looking for a value that looks like
504
+	 * `array('BETWEEN', array('2015-01-01T00:00:00', '2016-01-01T00:00:00'))`.
505
+	 * @since 4.9.72.p
506
+	 * @param $operator
507
+	 * @return boolean
508
+	 */
509
+	private function operatorIsTernary($operator)
510
+	{
511
+		$query_param_value = $this->getQueryParamValue();
512
+		return array_key_exists($operator, $this->getContext()->getModel()->valid_between_style_operators())
513
+			&& isset($query_param_value[1])
514
+			&& is_array($query_param_value[1])
515
+			&& isset($query_param_value[1][0], $query_param_value[1][1])
516
+			&& !isset($query_param_value[1][2])
517
+			&& !isset($query_param_value[2]);
518
+	}
519
+
520
+	/**
521
+	 * Returns true if the operator is a similar to LIKE, indicating the value may have wildcards we should leave alone.
522
+	 * @since 4.9.72.p
523
+	 * @param $operator
524
+	 * @return boolean
525
+	 */
526
+	private function operatorIsLike($operator)
527
+	{
528
+		$query_param_value = $this->getQueryParamValue();
529
+		return array_key_exists($operator, $this->getContext()->getModel()->valid_like_style_operators())
530
+			&& isset($query_param_value[1])
531
+			&& !isset($query_param_value[2]);
532
+	}
533
+
534
+	/**
535
+	 * Returns true if the operator only takes one argument (eg it's like `IS NULL`).
536
+	 * @since 4.9.72.p
537
+	 * @param $operator
538
+	 * @return boolean
539
+	 */
540
+	private function operatorIsUnary($operator)
541
+	{
542
+		$query_param_value = $this->getQueryParamValue();
543
+		return array_key_exists($operator, $this->getContext()->getModel()->valid_null_style_operators())
544
+			&& !isset($query_param_value[1]);
545
+	}
546
+
547
+	/**
548
+	 * Returns true if the operator specified is a binary opeator (eg `=`, `!=`)
549
+	 * @since 4.9.72.p
550
+	 * @param $operator
551
+	 * @return boolean
552
+	 */
553
+	private function operatorisBinary($operator)
554
+	{
555
+		$query_param_value = $this->getQueryParamValue();
556
+		$model = $this->getContext()->getModel();
557
+		return isset($query_param_value[1])
558
+			&& !isset($query_param_value[2])
559
+			&& !array_key_exists(
560
+				$operator,
561
+				array_merge(
562
+					$model->valid_in_style_operators(),
563
+					$model->valid_null_style_operators(),
564
+					$model->valid_like_style_operators(),
565
+					$model->valid_between_style_operators()
566
+				)
567
+			);
568
+	}
569
+
570
+	/**
571
+	 * If we're debugging, throws an exception saying that the wrong number of arguments was provided.
572
+	 * @since 4.9.72.p
573
+	 * @param $operator
574
+	 * @throws RestException
575
+	 */
576
+	private function throwWrongNumberOfArgsExceptionIfDebugging($operator)
577
+	{
578
+		if (EED_Core_Rest_Api::debugMode()) {
579
+			throw new RestException(
580
+				'wrong_number_of_arguments',
581
+				sprintf(
582
+					esc_html__(
583
+						'The operator you provided, "%1$s" had the wrong number of arguments',
584
+						'event_espresso'
585
+					),
586
+					$operator
587
+				),
588
+				array(
589
+					'status' => 400,
590
+				)
591
+			);
592
+		}
593
+	}
594
+
595
+	/**
596
+	 * Wrapper for ModelDataTranslator::prepareFieldValuesFromJson(), just a tad more DRY.
597
+	 * @since 4.9.72.p
598
+	 * @param $value
599
+	 * @return mixed
600
+	 * @throws RestException
601
+	 */
602
+	private function prepareValuesFromJson($value)
603
+	{
604
+		return ModelDataTranslator::prepareFieldValuesFromJson(
605
+			$this->getField(),
606
+			$value,
607
+			$this->getContext()->getRequestedVersion(),
608
+			$this->getTimezone()
609
+		);
610
+	}
611
+
612
+	/**
613
+	 * Throws an exception if an invalid operator was specified and we're debugging.
614
+	 * @since 4.9.72.p
615
+	 * @throws RestException
616
+	 */
617
+	private function throwInvalidOperatorExceptionIfDebugging()
618
+	{
619
+		// so they didn't provide a valid operator
620
+		if (EED_Core_Rest_Api::debugMode()) {
621
+			throw new RestException(
622
+				'invalid_operator',
623
+				sprintf(
624
+					esc_html__(
625
+						'You provided an invalid parameter, with key "%1$s" and value "%2$s"',
626
+						'event_espresso'
627
+					),
628
+					$this->getQueryParamKey(),
629
+					$this->getQueryParamValue()
630
+				),
631
+				array(
632
+					'status' => 400,
633
+				)
634
+			);
635
+		}
636
+	}
637
+
638
+	/**
639
+	 * Returns true if the query_param_key was a logic query parameter, eg `OR`, `AND`, `NOT`, `OR*`, etc.
640
+	 * @since 4.9.72.p
641
+	 * @return boolean
642
+	 */
643
+	private function isLogicQueryParam()
644
+	{
645
+		return in_array($this->getQueryParamKeySansStars(), $this->getContext()->getModel()->logic_query_param_keys());
646
+	}
647
+
648
+
649
+	/**
650
+	 * If the query param isn't for a field, it must be a nested query parameter which requires different logic.
651
+	 * @since 4.9.72.p
652
+	 * @return array
653
+	 * @throws DomainException
654
+	 * @throws EE_Error
655
+	 * @throws RestException
656
+	 * @throws InvalidDataTypeException
657
+	 * @throws InvalidInterfaceException
658
+	 * @throws InvalidArgumentException
659
+	 */
660
+	public function determineNestedConditionQueryParameters()
661
+	{
662
+
663
+		// so this param doesn't correspond to a field eh?
664
+		if ($this->getContext()->isWriting()) {
665
+			// always tell API clients about invalid parameters when they're creating data. Otherwise,
666
+			// they are probably going to create invalid data
667
+			throw new RestException(
668
+				'invalid_field',
669
+				sprintf(
670
+					/* translators: 1: variable name */
671
+					esc_html__('You have provided an invalid parameter: "%1$s"', 'event_espresso'),
672
+					$this->getQueryParamKey()
673
+				)
674
+			);
675
+		}
676
+		// so it's not for a field, is it a logic query param key?
677
+		if ($this->isLogicQueryParam()) {
678
+			return ModelDataTranslator::prepareConditionsQueryParamsForModels(
679
+				$this->getQueryParamValue(),
680
+				$this->getContext()->getModel(),
681
+				$this->getContext()->getRequestedVersion()
682
+			);
683
+		}
684
+		if (EED_Core_Rest_Api::debugMode()) {
685
+			// only tell API clients they got it wrong if we're in debug mode
686
+			// otherwise try our best ot fulfill their request by ignoring this invalid data
687
+			throw new RestException(
688
+				'invalid_parameter',
689
+				sprintf(
690
+					/* translators: 1: variable name */
691
+					esc_html__(
692
+						'You provided an invalid parameter, with key "%1$s"',
693
+						'event_espresso'
694
+					),
695
+					$this->getQueryParamKey()
696
+				),
697
+				array(
698
+					'status' => 400,
699
+				)
700
+			);
701
+		}
702
+		return null;
703
+	}
704 704
 }
705 705
 // End of file RestQueryParamMetadata.php
706 706
 // Location: EventEspresso\core\libraries\rest_api/RestQueryParamMetadata.php
Please login to merge, or discard this patch.
core/services/assets/BlockAssetManager.php 2 patches
Doc Comments   +4 added lines, -4 removed lines patch added patch discarded remove patch
@@ -142,7 +142,7 @@  discard block
 block discarded – undo
142 142
 
143 143
 
144 144
     /**
145
-     * @param       $handle
145
+     * @param       string $handle
146 146
      * @param array $dependencies
147 147
      * @since 4.9.71.p
148 148
      * @return JavascriptAsset
@@ -168,7 +168,7 @@  discard block
 block discarded – undo
168 168
 
169 169
 
170 170
     /**
171
-     * @param        $handle
171
+     * @param        string $handle
172 172
      * @param array  $dependencies
173 173
      * @since 4.9.71.p
174 174
      * @return StylesheetAsset
@@ -193,7 +193,7 @@  discard block
 block discarded – undo
193 193
 
194 194
 
195 195
     /**
196
-     * @param       $handle
196
+     * @param       string $handle
197 197
      * @param array $dependencies
198 198
      * @since 4.9.71.p
199 199
      * @return JavascriptAsset
@@ -219,7 +219,7 @@  discard block
 block discarded – undo
219 219
 
220 220
 
221 221
     /**
222
-     * @param        $handle
222
+     * @param        string $handle
223 223
      * @param array  $dependencies
224 224
      * @since 4.9.71.p
225 225
      * @return StylesheetAsset
Please login to merge, or discard this patch.
Indentation   +305 added lines, -305 removed lines patch added patch discarded remove patch
@@ -22,310 +22,310 @@
 block discarded – undo
22 22
 abstract class BlockAssetManager extends AssetManager implements BlockAssetManagerInterface
23 23
 {
24 24
 
25
-    /**
26
-     * @var string $editor_script_handle
27
-     */
28
-    private $editor_script_handle;
29
-
30
-    /**
31
-     * @var string $editor_style_handle
32
-     */
33
-    private $editor_style_handle;
34
-
35
-    /**
36
-     * @var string $script_handle
37
-     */
38
-    private $script_handle;
39
-
40
-    /**
41
-     * @var string $style_handle
42
-     */
43
-    private $style_handle;
44
-
45
-
46
-    /**
47
-     * @return string
48
-     */
49
-    public function getEditorScriptHandle()
50
-    {
51
-        return $this->editor_script_handle;
52
-    }
53
-
54
-
55
-    /**
56
-     * @param string $editor_script_handle
57
-     */
58
-    public function setEditorScriptHandle($editor_script_handle)
59
-    {
60
-        if(strpos($editor_script_handle, BlockInterface::NAME_SPACE . '-') !== 0) {
61
-            $editor_script_handle = BlockInterface::NAME_SPACE . '-' . $editor_script_handle;
62
-        }
63
-        $this->editor_script_handle = $editor_script_handle;
64
-    }
65
-
66
-
67
-    /**
68
-     * @return string
69
-     */
70
-    public function getEditorStyleHandle()
71
-    {
72
-        return $this->editor_style_handle;
73
-    }
74
-
75
-
76
-    /**
77
-     * @param string $editor_style_handle
78
-     */
79
-    public function setEditorStyleHandle($editor_style_handle)
80
-    {
81
-        if (strpos($editor_style_handle, BlockInterface::NAME_SPACE . '-') !== 0) {
82
-            $editor_style_handle = BlockInterface::NAME_SPACE . '-' . $editor_style_handle;
83
-        }
84
-        $this->editor_style_handle = $editor_style_handle;
85
-    }
86
-
87
-
88
-    /**
89
-     * @return string
90
-     */
91
-    public function getScriptHandle()
92
-    {
93
-        return $this->script_handle;
94
-    }
95
-
96
-
97
-    /**
98
-     * @param string $script_handle
99
-     */
100
-    public function setScriptHandle($script_handle)
101
-    {
102
-        if (strpos($script_handle, BlockInterface::NAME_SPACE . '-') !== 0) {
103
-            $script_handle = BlockInterface::NAME_SPACE . '-' . $script_handle;
104
-        }
105
-        $this->script_handle = $script_handle;
106
-    }
107
-
108
-
109
-    /**
110
-     * @return string
111
-     */
112
-    public function getStyleHandle()
113
-    {
114
-        return $this->style_handle;
115
-    }
116
-
117
-
118
-    /**
119
-     * @param string $style_handle
120
-     */
121
-    public function setStyleHandle($style_handle)
122
-    {
123
-        if (strpos($style_handle, BlockInterface::NAME_SPACE . '-') !== 0) {
124
-            $style_handle = BlockInterface::NAME_SPACE . '-' . $style_handle;
125
-        }
126
-        $this->style_handle = $style_handle;
127
-    }
128
-
129
-    /**
130
-     * @since 4.9.71.p
131
-     * @throws InvalidDataTypeException
132
-     * @throws InvalidEntityException
133
-     * @throws DuplicateCollectionIdentifierException
134
-     */
135
-    public function addAssets()
136
-    {
137
-        $this->addEditorScript($this->getEditorScriptHandle());
138
-        $this->addEditorStyle($this->getEditorStyleHandle());
139
-        $this->addScript($this->getScriptHandle());
140
-        $this->addStyle($this->getStyleHandle());
141
-    }
142
-
143
-
144
-    /**
145
-     * @param       $handle
146
-     * @param array $dependencies
147
-     * @since 4.9.71.p
148
-     * @return JavascriptAsset
149
-     * @throws InvalidDataTypeException
150
-     * @throws InvalidEntityException
151
-     * @throws DuplicateCollectionIdentifierException
152
-     */
153
-    public function addEditorScript($handle, array $dependencies = array())
154
-    {
155
-        if($this->assets->hasJavascriptAsset($handle)){
156
-            return $this->assets->getJavascriptAsset($handle);
157
-        }
158
-        return parent::addJavascript(
159
-            $handle,
160
-            $this->registry->getJsUrl(
161
-                $this->domain->assetNamespace(),
162
-                $handle
163
-            ),
164
-            $this->addDefaultBlockScriptDependencies($dependencies)
165
-        )
166
-        ->setRequiresTranslation();
167
-    }
168
-
169
-
170
-    /**
171
-     * @param        $handle
172
-     * @param array  $dependencies
173
-     * @since 4.9.71.p
174
-     * @return StylesheetAsset
175
-     * @throws InvalidDataTypeException
176
-     * @throws InvalidEntityException
177
-     * @throws DuplicateCollectionIdentifierException
178
-     */
179
-    public function addEditorStyle($handle, array $dependencies = array())
180
-    {
181
-        if ($this->assets->hasStylesheetAsset($handle)) {
182
-            return $this->assets->getStylesheetAsset($handle);
183
-        }
184
-        return parent::addStylesheet(
185
-            $handle,
186
-            $this->registry->getCssUrl(
187
-                $this->domain->assetNamespace(),
188
-                $handle
189
-            ),
190
-            $this->addDefaultBlockStyleDependencies($dependencies)
191
-        );
192
-    }
193
-
194
-
195
-    /**
196
-     * @param       $handle
197
-     * @param array $dependencies
198
-     * @since 4.9.71.p
199
-     * @return JavascriptAsset
200
-     * @throws InvalidDataTypeException
201
-     * @throws InvalidEntityException
202
-     * @throws DuplicateCollectionIdentifierException
203
-     */
204
-    public function addScript($handle, array $dependencies = array())
205
-    {
206
-        if ($this->assets->hasJavascriptAsset($handle)) {
207
-            return $this->assets->getJavascriptAsset($handle);
208
-        }
209
-        return parent::addJavascript(
210
-            $handle,
211
-            $this->registry->getJsUrl(
212
-                $this->domain->assetNamespace(),
213
-                $handle
214
-            ),
215
-            $dependencies + array( CoreAssetManager::JS_HANDLE_COMPONENTS )
216
-        )
217
-        ->setRequiresTranslation();
218
-    }
219
-
220
-
221
-    /**
222
-     * @param        $handle
223
-     * @param array  $dependencies
224
-     * @since 4.9.71.p
225
-     * @return StylesheetAsset
226
-     * @throws InvalidDataTypeException
227
-     * @throws InvalidEntityException
228
-     * @throws DuplicateCollectionIdentifierException
229
-     */
230
-    public function addStyle($handle, array $dependencies = array())
231
-    {
232
-        if ($this->assets->hasStylesheetAsset($handle)) {
233
-            return $this->assets->getStylesheetAsset($handle);
234
-        }
235
-        return parent::addStylesheet(
236
-            $handle,
237
-            $this->registry->getCssUrl(
238
-                $this->domain->assetNamespace(),
239
-                $handle
240
-            ),
241
-            $dependencies + array( CoreAssetManager::CSS_HANDLE_COMPONENTS )
242
-        );
243
-    }
244
-
245
-
246
-    /**
247
-     * @param array $dependencies
248
-     * @return array
249
-     */
250
-    protected function addDefaultBlockScriptDependencies(array $dependencies)
251
-    {
252
-        $dependencies += array(
253
-                'wp-blocks',    // Provides useful functions and components for extending the editor
254
-                'wp-i18n',      // Provides localization functions
255
-                'wp-element',   // Provides React.Component
256
-                'wp-components', // Provides many prebuilt components and controls
257
-                CoreAssetManager::JS_HANDLE_EDITOR_HOCS,
258
-                $this->getScriptHandle(),
259
-            );
260
-        return $dependencies;
261
-    }
262
-
263
-
264
-    /**
265
-     * @param array $dependencies
266
-     * @return array
267
-     */
268
-    protected function addDefaultBlockStyleDependencies(array $dependencies)
269
-    {
270
-        $dependencies += array(
271
-            $this->getStyleHandle()
272
-        );
273
-        return $dependencies;
274
-    }
275
-
276
-
277
-    /**
278
-     * @return JavascriptAsset|null
279
-     */
280
-    public function getEditorScript()
281
-    {
282
-        return $this->assets->getJavascriptAsset($this->editor_script_handle);
283
-    }
284
-
285
-
286
-    /**
287
-     * @return StylesheetAsset|null
288
-     */
289
-    public function getEditorStyle()
290
-    {
291
-        return $this->assets->getStylesheetAsset($this->editor_style_handle);
292
-    }
293
-
294
-
295
-    /**
296
-     * @return JavascriptAsset|null
297
-     */
298
-    public function getScript()
299
-    {
300
-        return $this->assets->getJavascriptAsset($this->script_handle);
301
-    }
302
-
303
-
304
-    /**
305
-     * @return StylesheetAsset|null
306
-     */
307
-    public function getStyle()
308
-    {
309
-        return $this->assets->getStylesheetAsset($this->style_handle);
310
-    }
311
-
312
-
313
-    /**
314
-     * @return  void
315
-     */
316
-    public function enqueueAssets()
317
-    {
318
-        $assets = array(
319
-            $this->getEditorScript(),
320
-            $this->getEditorStyle(),
321
-            $this->getScript(),
322
-            $this->getStyle(),
323
-        );
324
-        foreach ($assets as $asset) {
325
-            if ($asset instanceof BrowserAsset && $asset->isRegistered()) {
326
-                $asset->enqueueAsset();
327
-            }
328
-        }
329
-    }
25
+	/**
26
+	 * @var string $editor_script_handle
27
+	 */
28
+	private $editor_script_handle;
29
+
30
+	/**
31
+	 * @var string $editor_style_handle
32
+	 */
33
+	private $editor_style_handle;
34
+
35
+	/**
36
+	 * @var string $script_handle
37
+	 */
38
+	private $script_handle;
39
+
40
+	/**
41
+	 * @var string $style_handle
42
+	 */
43
+	private $style_handle;
44
+
45
+
46
+	/**
47
+	 * @return string
48
+	 */
49
+	public function getEditorScriptHandle()
50
+	{
51
+		return $this->editor_script_handle;
52
+	}
53
+
54
+
55
+	/**
56
+	 * @param string $editor_script_handle
57
+	 */
58
+	public function setEditorScriptHandle($editor_script_handle)
59
+	{
60
+		if(strpos($editor_script_handle, BlockInterface::NAME_SPACE . '-') !== 0) {
61
+			$editor_script_handle = BlockInterface::NAME_SPACE . '-' . $editor_script_handle;
62
+		}
63
+		$this->editor_script_handle = $editor_script_handle;
64
+	}
65
+
66
+
67
+	/**
68
+	 * @return string
69
+	 */
70
+	public function getEditorStyleHandle()
71
+	{
72
+		return $this->editor_style_handle;
73
+	}
74
+
75
+
76
+	/**
77
+	 * @param string $editor_style_handle
78
+	 */
79
+	public function setEditorStyleHandle($editor_style_handle)
80
+	{
81
+		if (strpos($editor_style_handle, BlockInterface::NAME_SPACE . '-') !== 0) {
82
+			$editor_style_handle = BlockInterface::NAME_SPACE . '-' . $editor_style_handle;
83
+		}
84
+		$this->editor_style_handle = $editor_style_handle;
85
+	}
86
+
87
+
88
+	/**
89
+	 * @return string
90
+	 */
91
+	public function getScriptHandle()
92
+	{
93
+		return $this->script_handle;
94
+	}
95
+
96
+
97
+	/**
98
+	 * @param string $script_handle
99
+	 */
100
+	public function setScriptHandle($script_handle)
101
+	{
102
+		if (strpos($script_handle, BlockInterface::NAME_SPACE . '-') !== 0) {
103
+			$script_handle = BlockInterface::NAME_SPACE . '-' . $script_handle;
104
+		}
105
+		$this->script_handle = $script_handle;
106
+	}
107
+
108
+
109
+	/**
110
+	 * @return string
111
+	 */
112
+	public function getStyleHandle()
113
+	{
114
+		return $this->style_handle;
115
+	}
116
+
117
+
118
+	/**
119
+	 * @param string $style_handle
120
+	 */
121
+	public function setStyleHandle($style_handle)
122
+	{
123
+		if (strpos($style_handle, BlockInterface::NAME_SPACE . '-') !== 0) {
124
+			$style_handle = BlockInterface::NAME_SPACE . '-' . $style_handle;
125
+		}
126
+		$this->style_handle = $style_handle;
127
+	}
128
+
129
+	/**
130
+	 * @since 4.9.71.p
131
+	 * @throws InvalidDataTypeException
132
+	 * @throws InvalidEntityException
133
+	 * @throws DuplicateCollectionIdentifierException
134
+	 */
135
+	public function addAssets()
136
+	{
137
+		$this->addEditorScript($this->getEditorScriptHandle());
138
+		$this->addEditorStyle($this->getEditorStyleHandle());
139
+		$this->addScript($this->getScriptHandle());
140
+		$this->addStyle($this->getStyleHandle());
141
+	}
142
+
143
+
144
+	/**
145
+	 * @param       $handle
146
+	 * @param array $dependencies
147
+	 * @since 4.9.71.p
148
+	 * @return JavascriptAsset
149
+	 * @throws InvalidDataTypeException
150
+	 * @throws InvalidEntityException
151
+	 * @throws DuplicateCollectionIdentifierException
152
+	 */
153
+	public function addEditorScript($handle, array $dependencies = array())
154
+	{
155
+		if($this->assets->hasJavascriptAsset($handle)){
156
+			return $this->assets->getJavascriptAsset($handle);
157
+		}
158
+		return parent::addJavascript(
159
+			$handle,
160
+			$this->registry->getJsUrl(
161
+				$this->domain->assetNamespace(),
162
+				$handle
163
+			),
164
+			$this->addDefaultBlockScriptDependencies($dependencies)
165
+		)
166
+		->setRequiresTranslation();
167
+	}
168
+
169
+
170
+	/**
171
+	 * @param        $handle
172
+	 * @param array  $dependencies
173
+	 * @since 4.9.71.p
174
+	 * @return StylesheetAsset
175
+	 * @throws InvalidDataTypeException
176
+	 * @throws InvalidEntityException
177
+	 * @throws DuplicateCollectionIdentifierException
178
+	 */
179
+	public function addEditorStyle($handle, array $dependencies = array())
180
+	{
181
+		if ($this->assets->hasStylesheetAsset($handle)) {
182
+			return $this->assets->getStylesheetAsset($handle);
183
+		}
184
+		return parent::addStylesheet(
185
+			$handle,
186
+			$this->registry->getCssUrl(
187
+				$this->domain->assetNamespace(),
188
+				$handle
189
+			),
190
+			$this->addDefaultBlockStyleDependencies($dependencies)
191
+		);
192
+	}
193
+
194
+
195
+	/**
196
+	 * @param       $handle
197
+	 * @param array $dependencies
198
+	 * @since 4.9.71.p
199
+	 * @return JavascriptAsset
200
+	 * @throws InvalidDataTypeException
201
+	 * @throws InvalidEntityException
202
+	 * @throws DuplicateCollectionIdentifierException
203
+	 */
204
+	public function addScript($handle, array $dependencies = array())
205
+	{
206
+		if ($this->assets->hasJavascriptAsset($handle)) {
207
+			return $this->assets->getJavascriptAsset($handle);
208
+		}
209
+		return parent::addJavascript(
210
+			$handle,
211
+			$this->registry->getJsUrl(
212
+				$this->domain->assetNamespace(),
213
+				$handle
214
+			),
215
+			$dependencies + array( CoreAssetManager::JS_HANDLE_COMPONENTS )
216
+		)
217
+		->setRequiresTranslation();
218
+	}
219
+
220
+
221
+	/**
222
+	 * @param        $handle
223
+	 * @param array  $dependencies
224
+	 * @since 4.9.71.p
225
+	 * @return StylesheetAsset
226
+	 * @throws InvalidDataTypeException
227
+	 * @throws InvalidEntityException
228
+	 * @throws DuplicateCollectionIdentifierException
229
+	 */
230
+	public function addStyle($handle, array $dependencies = array())
231
+	{
232
+		if ($this->assets->hasStylesheetAsset($handle)) {
233
+			return $this->assets->getStylesheetAsset($handle);
234
+		}
235
+		return parent::addStylesheet(
236
+			$handle,
237
+			$this->registry->getCssUrl(
238
+				$this->domain->assetNamespace(),
239
+				$handle
240
+			),
241
+			$dependencies + array( CoreAssetManager::CSS_HANDLE_COMPONENTS )
242
+		);
243
+	}
244
+
245
+
246
+	/**
247
+	 * @param array $dependencies
248
+	 * @return array
249
+	 */
250
+	protected function addDefaultBlockScriptDependencies(array $dependencies)
251
+	{
252
+		$dependencies += array(
253
+				'wp-blocks',    // Provides useful functions and components for extending the editor
254
+				'wp-i18n',      // Provides localization functions
255
+				'wp-element',   // Provides React.Component
256
+				'wp-components', // Provides many prebuilt components and controls
257
+				CoreAssetManager::JS_HANDLE_EDITOR_HOCS,
258
+				$this->getScriptHandle(),
259
+			);
260
+		return $dependencies;
261
+	}
262
+
263
+
264
+	/**
265
+	 * @param array $dependencies
266
+	 * @return array
267
+	 */
268
+	protected function addDefaultBlockStyleDependencies(array $dependencies)
269
+	{
270
+		$dependencies += array(
271
+			$this->getStyleHandle()
272
+		);
273
+		return $dependencies;
274
+	}
275
+
276
+
277
+	/**
278
+	 * @return JavascriptAsset|null
279
+	 */
280
+	public function getEditorScript()
281
+	{
282
+		return $this->assets->getJavascriptAsset($this->editor_script_handle);
283
+	}
284
+
285
+
286
+	/**
287
+	 * @return StylesheetAsset|null
288
+	 */
289
+	public function getEditorStyle()
290
+	{
291
+		return $this->assets->getStylesheetAsset($this->editor_style_handle);
292
+	}
293
+
294
+
295
+	/**
296
+	 * @return JavascriptAsset|null
297
+	 */
298
+	public function getScript()
299
+	{
300
+		return $this->assets->getJavascriptAsset($this->script_handle);
301
+	}
302
+
303
+
304
+	/**
305
+	 * @return StylesheetAsset|null
306
+	 */
307
+	public function getStyle()
308
+	{
309
+		return $this->assets->getStylesheetAsset($this->style_handle);
310
+	}
311
+
312
+
313
+	/**
314
+	 * @return  void
315
+	 */
316
+	public function enqueueAssets()
317
+	{
318
+		$assets = array(
319
+			$this->getEditorScript(),
320
+			$this->getEditorStyle(),
321
+			$this->getScript(),
322
+			$this->getStyle(),
323
+		);
324
+		foreach ($assets as $asset) {
325
+			if ($asset instanceof BrowserAsset && $asset->isRegistered()) {
326
+				$asset->enqueueAsset();
327
+			}
328
+		}
329
+	}
330 330
 
331 331
 }
Please login to merge, or discard this patch.
core/domain/entities/editor/blocks/EventAttendees.php 1 patch
Indentation   +157 added lines, -157 removed lines patch added patch discarded remove patch
@@ -21,172 +21,172 @@
 block discarded – undo
21 21
 class EventAttendees extends Block
22 22
 {
23 23
 
24
-    const BLOCK_TYPE = 'event-attendees';
24
+	const BLOCK_TYPE = 'event-attendees';
25 25
 
26
-    /**
27
-     * @var EventAttendeesBlockRenderer $renderer
28
-     */
29
-    protected $renderer;
26
+	/**
27
+	 * @var EventAttendeesBlockRenderer $renderer
28
+	 */
29
+	protected $renderer;
30 30
 
31 31
 
32
-    /**
33
-     * EventAttendees constructor.
34
-     *
35
-     * @param CoreBlocksAssetManager      $block_asset_manager
36
-     * @param RequestInterface            $request
37
-     * @param EventAttendeesBlockRenderer $renderer
38
-     */
39
-    public function __construct(
40
-        CoreBlocksAssetManager $block_asset_manager,
41
-        RequestInterface $request,
42
-        EventAttendeesBlockRenderer $renderer
43
-    ) {
44
-        parent::__construct($block_asset_manager, $request);
45
-        $this->renderer= $renderer;
46
-    }
32
+	/**
33
+	 * EventAttendees constructor.
34
+	 *
35
+	 * @param CoreBlocksAssetManager      $block_asset_manager
36
+	 * @param RequestInterface            $request
37
+	 * @param EventAttendeesBlockRenderer $renderer
38
+	 */
39
+	public function __construct(
40
+		CoreBlocksAssetManager $block_asset_manager,
41
+		RequestInterface $request,
42
+		EventAttendeesBlockRenderer $renderer
43
+	) {
44
+		parent::__construct($block_asset_manager, $request);
45
+		$this->renderer= $renderer;
46
+	}
47 47
 
48 48
 
49
-    /**
50
-     * Perform any early setup required by the block
51
-     * including setting the block type and supported post types
52
-     *
53
-     * @return void
54
-     */
55
-    public function initialize()
56
-    {
57
-        $this->setBlockType(self::BLOCK_TYPE);
58
-        $this->setSupportedRoutes(
59
-            array(
60
-                'EventEspresso\core\domain\entities\route_match\specifications\admin\EspressoStandardPostTypeEditor',
61
-                'EventEspresso\core\domain\entities\route_match\specifications\admin\WordPressPostTypeEditor',
62
-                'EventEspresso\core\domain\entities\route_match\specifications\frontend\EspressoBlockRenderer',
63
-                'EventEspresso\core\domain\entities\route_match\specifications\frontend\AnyFrontendRequest'
64
-            )
65
-        );
66
-        $EVT_ID = $this->request->getRequestParam('page') === 'espresso_events'
67
-            ? $this->request->getRequestParam('post', 0)
68
-            : 0;
69
-        $this->setAttributes(
70
-            array(
71
-                'eventId'           => array(
72
-                    'type'    => 'number',
73
-                    'default' => $EVT_ID,
74
-                ),
75
-                'datetimeId'        => array(
76
-                    'type'    => 'number',
77
-                    'default' => 0,
78
-                ),
79
-                'ticketId'          => array(
80
-                    'type'    => 'number',
81
-                    'default' => 0,
82
-                ),
83
-                'status'            => array(
84
-                    'type'    => 'string',
85
-                    'default' => EEM_Registration::status_id_approved,
86
-                ),
87
-                'limit'             => array(
88
-                    'type'    => 'number',
89
-                    'default' => 10,
90
-                ),
91
-                'order' => array(
92
-                    'type' => 'string',
93
-                    'default' => 'ASC'
94
-                ),
95
-                'orderBy' => array(
96
-                    'type' => 'string',
97
-                    'default' => 'lastThenFirstName',
98
-                ),
99
-                'showGravatar'      => array(
100
-                    'type'    => 'boolean',
101
-                    'default' => false,
102
-                ),
103
-                'avatarClass' => array(
104
-                    'type' => 'string',
105
-                    'default' => 'contact',
106
-                ),
107
-                'avatarSize' => array(
108
-                    'type' => 'number',
109
-                    'default' => 24,
110
-                ),
111
-                'displayOnArchives' => array(
112
-                    'type'    => 'boolean',
113
-                    'default' => false,
114
-                ),
115
-            )
116
-        );
117
-        $this->setDynamic();
118
-    }
49
+	/**
50
+	 * Perform any early setup required by the block
51
+	 * including setting the block type and supported post types
52
+	 *
53
+	 * @return void
54
+	 */
55
+	public function initialize()
56
+	{
57
+		$this->setBlockType(self::BLOCK_TYPE);
58
+		$this->setSupportedRoutes(
59
+			array(
60
+				'EventEspresso\core\domain\entities\route_match\specifications\admin\EspressoStandardPostTypeEditor',
61
+				'EventEspresso\core\domain\entities\route_match\specifications\admin\WordPressPostTypeEditor',
62
+				'EventEspresso\core\domain\entities\route_match\specifications\frontend\EspressoBlockRenderer',
63
+				'EventEspresso\core\domain\entities\route_match\specifications\frontend\AnyFrontendRequest'
64
+			)
65
+		);
66
+		$EVT_ID = $this->request->getRequestParam('page') === 'espresso_events'
67
+			? $this->request->getRequestParam('post', 0)
68
+			: 0;
69
+		$this->setAttributes(
70
+			array(
71
+				'eventId'           => array(
72
+					'type'    => 'number',
73
+					'default' => $EVT_ID,
74
+				),
75
+				'datetimeId'        => array(
76
+					'type'    => 'number',
77
+					'default' => 0,
78
+				),
79
+				'ticketId'          => array(
80
+					'type'    => 'number',
81
+					'default' => 0,
82
+				),
83
+				'status'            => array(
84
+					'type'    => 'string',
85
+					'default' => EEM_Registration::status_id_approved,
86
+				),
87
+				'limit'             => array(
88
+					'type'    => 'number',
89
+					'default' => 10,
90
+				),
91
+				'order' => array(
92
+					'type' => 'string',
93
+					'default' => 'ASC'
94
+				),
95
+				'orderBy' => array(
96
+					'type' => 'string',
97
+					'default' => 'lastThenFirstName',
98
+				),
99
+				'showGravatar'      => array(
100
+					'type'    => 'boolean',
101
+					'default' => false,
102
+				),
103
+				'avatarClass' => array(
104
+					'type' => 'string',
105
+					'default' => 'contact',
106
+				),
107
+				'avatarSize' => array(
108
+					'type' => 'number',
109
+					'default' => 24,
110
+				),
111
+				'displayOnArchives' => array(
112
+					'type'    => 'boolean',
113
+					'default' => false,
114
+				),
115
+			)
116
+		);
117
+		$this->setDynamic();
118
+	}
119 119
 
120 120
 
121
-    /**
122
-     * Returns an array where the key corresponds to the incoming attribute name from the WP block
123
-     * and the value corresponds to the attribute name for the existing EspressoEventAttendees shortcode
124
-     *
125
-     * @since 4.9.71.p
126
-     * @return array
127
-     */
128
-    private function getAttributesMap()
129
-    {
130
-        return array(
131
-            'eventId'           => 'absint',
132
-            'datetimeId'        => 'absint',
133
-            'ticketId'          => 'absint',
134
-            'status'            => 'sanitize_text_field',
135
-            'limit'             => 'intval',
136
-            'showGravatar'      => 'bool',
137
-            'avatarClass'       => 'sanitize_text_field',
138
-            'avatarSize'        => 'absint',
139
-            'displayOnArchives' => 'bool',
140
-            'order' => 'sanitize_text_field',
141
-            'orderBy' => 'sanitize_text_field',
142
-        );
143
-    }
121
+	/**
122
+	 * Returns an array where the key corresponds to the incoming attribute name from the WP block
123
+	 * and the value corresponds to the attribute name for the existing EspressoEventAttendees shortcode
124
+	 *
125
+	 * @since 4.9.71.p
126
+	 * @return array
127
+	 */
128
+	private function getAttributesMap()
129
+	{
130
+		return array(
131
+			'eventId'           => 'absint',
132
+			'datetimeId'        => 'absint',
133
+			'ticketId'          => 'absint',
134
+			'status'            => 'sanitize_text_field',
135
+			'limit'             => 'intval',
136
+			'showGravatar'      => 'bool',
137
+			'avatarClass'       => 'sanitize_text_field',
138
+			'avatarSize'        => 'absint',
139
+			'displayOnArchives' => 'bool',
140
+			'order' => 'sanitize_text_field',
141
+			'orderBy' => 'sanitize_text_field',
142
+		);
143
+	}
144 144
 
145 145
 
146
-    /**
147
-     * Sanitizes attributes.
148
-     *
149
-     * @param array $attributes
150
-     * @return array
151
-     */
152
-    private function sanitizeAttributes(array $attributes)
153
-    {
154
-        $sanitized_attributes = array();
155
-        foreach ($attributes as $attribute => $value) {
156
-            $convert = $this->getAttributesMap();
157
-            if (isset($convert[ $attribute ])) {
158
-                $sanitize = $convert[ $attribute ];
159
-                if ($sanitize === 'bool') {
160
-                    $sanitized_attributes[ $attribute ] = filter_var(
161
-                        $value,
162
-                        FILTER_VALIDATE_BOOLEAN
163
-                    );
164
-                } else {
165
-                    $sanitized_attributes[ $attribute ] = $sanitize($value);
166
-                }
167
-                // don't pass along attributes with a 0 value
168
-                if ($sanitized_attributes[ $attribute ] === 0) {
169
-                    unset($sanitized_attributes[ $attribute ]);
170
-                }
171
-            }
172
-        }
173
-        return $attributes;
174
-    }
146
+	/**
147
+	 * Sanitizes attributes.
148
+	 *
149
+	 * @param array $attributes
150
+	 * @return array
151
+	 */
152
+	private function sanitizeAttributes(array $attributes)
153
+	{
154
+		$sanitized_attributes = array();
155
+		foreach ($attributes as $attribute => $value) {
156
+			$convert = $this->getAttributesMap();
157
+			if (isset($convert[ $attribute ])) {
158
+				$sanitize = $convert[ $attribute ];
159
+				if ($sanitize === 'bool') {
160
+					$sanitized_attributes[ $attribute ] = filter_var(
161
+						$value,
162
+						FILTER_VALIDATE_BOOLEAN
163
+					);
164
+				} else {
165
+					$sanitized_attributes[ $attribute ] = $sanitize($value);
166
+				}
167
+				// don't pass along attributes with a 0 value
168
+				if ($sanitized_attributes[ $attribute ] === 0) {
169
+					unset($sanitized_attributes[ $attribute ]);
170
+				}
171
+			}
172
+		}
173
+		return $attributes;
174
+	}
175 175
 
176 176
 
177
-    /**
178
-     * Returns the rendered HTML for the block
179
-     *
180
-     * @param array $attributes
181
-     * @return string
182
-     * @throws DomainException
183
-     * @throws EE_Error
184
-     */
185
-    public function renderBlock(array $attributes = array())
186
-    {
187
-        $attributes = $this->sanitizeAttributes($attributes);
188
-        return (is_archive() || is_front_page() || is_home()) && ! $attributes['displayOnArchives']
189
-            ? ''
190
-            : $this->renderer->render($attributes);
191
-    }
177
+	/**
178
+	 * Returns the rendered HTML for the block
179
+	 *
180
+	 * @param array $attributes
181
+	 * @return string
182
+	 * @throws DomainException
183
+	 * @throws EE_Error
184
+	 */
185
+	public function renderBlock(array $attributes = array())
186
+	{
187
+		$attributes = $this->sanitizeAttributes($attributes);
188
+		return (is_archive() || is_front_page() || is_home()) && ! $attributes['displayOnArchives']
189
+			? ''
190
+			: $this->renderer->render($attributes);
191
+	}
192 192
 }
Please login to merge, or discard this patch.
core/domain/entities/editor/CoreBlocksAssetManager.php 1 patch
Indentation   +14 added lines, -14 removed lines patch added patch discarded remove patch
@@ -14,20 +14,20 @@
 block discarded – undo
14 14
  */
15 15
 class CoreBlocksAssetManager extends BlockAssetManager
16 16
 {
17
-    const JS_HANDLE_CORE_BLOCKS_EDITOR = 'core-blocks';
18
-    const CSS_HANDLE_CORE_BLOCKS_EDITOR = 'core-blocks';
19
-    const JS_HANDLE_CORE_BLOCKS = 'core-blocks-frontend';
20
-    const CSS_HANDLE_CORE_BLOCKS = 'core-blocks-frontend';
17
+	const JS_HANDLE_CORE_BLOCKS_EDITOR = 'core-blocks';
18
+	const CSS_HANDLE_CORE_BLOCKS_EDITOR = 'core-blocks';
19
+	const JS_HANDLE_CORE_BLOCKS = 'core-blocks-frontend';
20
+	const CSS_HANDLE_CORE_BLOCKS = 'core-blocks-frontend';
21 21
 
22 22
 
23
-    /**
24
-     * @since 4.9.71.p
25
-     */
26
-    public function setAssetHandles()
27
-    {
28
-        $this->setEditorScriptHandle(self::JS_HANDLE_CORE_BLOCKS_EDITOR);
29
-        $this->setEditorStyleHandle(self::CSS_HANDLE_CORE_BLOCKS_EDITOR);
30
-        $this->setScriptHandle(self::JS_HANDLE_CORE_BLOCKS);
31
-        $this->setStyleHandle(self::CSS_HANDLE_CORE_BLOCKS);
32
-    }
23
+	/**
24
+	 * @since 4.9.71.p
25
+	 */
26
+	public function setAssetHandles()
27
+	{
28
+		$this->setEditorScriptHandle(self::JS_HANDLE_CORE_BLOCKS_EDITOR);
29
+		$this->setEditorStyleHandle(self::CSS_HANDLE_CORE_BLOCKS_EDITOR);
30
+		$this->setScriptHandle(self::JS_HANDLE_CORE_BLOCKS);
31
+		$this->setStyleHandle(self::CSS_HANDLE_CORE_BLOCKS);
32
+	}
33 33
 }
Please login to merge, or discard this patch.
core/domain/entities/route_match/MatchAnyRouteSpecification.php 1 patch
Indentation   +15 added lines, -15 removed lines patch added patch discarded remove patch
@@ -14,19 +14,19 @@
 block discarded – undo
14 14
 class MatchAnyRouteSpecification extends MultiRouteSpecification
15 15
 {
16 16
 
17
-    /**
18
-     * returns true if current request matches specification
19
-     *
20
-     * @since 4.9.71.p
21
-     * @return boolean
22
-     */
23
-    public function isMatchingRoute()
24
-    {
25
-        foreach ($this->specifications as $specification) {
26
-            if ($specification->isMatchingRoute()) {
27
-                return true;
28
-            }
29
-        }
30
-        return false;
31
-    }
17
+	/**
18
+	 * returns true if current request matches specification
19
+	 *
20
+	 * @since 4.9.71.p
21
+	 * @return boolean
22
+	 */
23
+	public function isMatchingRoute()
24
+	{
25
+		foreach ($this->specifications as $specification) {
26
+			if ($specification->isMatchingRoute()) {
27
+				return true;
28
+			}
29
+		}
30
+		return false;
31
+	}
32 32
 }
Please login to merge, or discard this patch.
core/domain/entities/route_match/MatchAllRouteSpecifications.php 1 patch
Indentation   +15 added lines, -15 removed lines patch added patch discarded remove patch
@@ -14,19 +14,19 @@
 block discarded – undo
14 14
 class MatchAllRouteSpecifications extends MultiRouteSpecification
15 15
 {
16 16
 
17
-    /**
18
-     * returns true if current request matches specification
19
-     *
20
-     * @since 4.9.71.p
21
-     * @return boolean
22
-     */
23
-    public function isMatchingRoute()
24
-    {
25
-        foreach ($this->specifications as $specification) {
26
-            if (! $specification->isMatchingRoute()) {
27
-                return false;
28
-            }
29
-        }
30
-        return true;
31
-    }
17
+	/**
18
+	 * returns true if current request matches specification
19
+	 *
20
+	 * @since 4.9.71.p
21
+	 * @return boolean
22
+	 */
23
+	public function isMatchingRoute()
24
+	{
25
+		foreach ($this->specifications as $specification) {
26
+			if (! $specification->isMatchingRoute()) {
27
+				return false;
28
+			}
29
+		}
30
+		return true;
31
+	}
32 32
 }
Please login to merge, or discard this patch.