Completed
Branch BUG-10878-event-spaces-remaini... (def5f8)
by
unknown
65:16 queued 53:59
created
core/libraries/rest_api/ModelDataTranslator.php 3 patches
Unused Use Statements   -2 removed lines patch added patch discarded remove patch
@@ -1,11 +1,9 @@
 block discarded – undo
1 1
 <?php
2 2
 namespace EventEspresso\core\libraries\rest_api;
3 3
 
4
-use EE_Capabilities;
5 4
 use EE_Datetime_Field;
6 5
 use EE_Error;
7 6
 use EE_Infinite_Integer_Field;
8
-use EE_Maybe_Serialized_Simple_HTML_Field;
9 7
 use EE_Model_Field_Base;
10 8
 use EE_Serialized_Text_Field;
11 9
 use EEM_Base;
Please login to merge, or discard this patch.
Indentation   +789 added lines, -789 removed lines patch added patch discarded remove patch
@@ -11,7 +11,7 @@  discard block
 block discarded – undo
11 11
 use EEM_Base;
12 12
 
13 13
 if (! defined('EVENT_ESPRESSO_VERSION')) {
14
-    exit('No direct script access allowed');
14
+	exit('No direct script access allowed');
15 15
 }
16 16
 
17 17
 
@@ -36,792 +36,792 @@  discard block
 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
-    /**
48
-     * Prepares a possible array of input values from JSON for use by the models
49
-     *
50
-     * @param EE_Model_Field_Base $field_obj
51
-     * @param mixed                $original_value_maybe_array
52
-     * @param string               $requested_version
53
-     * @param string               $timezone_string treat values as being in this timezone
54
-     * @return mixed
55
-     * @throws RestException
56
-     */
57
-    public static function prepareFieldValuesFromJson(
58
-        $field_obj,
59
-        $original_value_maybe_array,
60
-        $requested_version,
61
-        $timezone_string = 'UTC'
62
-    ) {
63
-        if (is_array($original_value_maybe_array)
64
-            && ! $field_obj instanceof EE_Serialized_Text_Field
65
-        ) {
66
-            $new_value_maybe_array = array();
67
-            foreach ($original_value_maybe_array as $array_key => $array_item) {
68
-                $new_value_maybe_array[$array_key] = ModelDataTranslator::prepareFieldValueFromJson(
69
-                    $field_obj,
70
-                    $array_item,
71
-                    $requested_version,
72
-                    $timezone_string
73
-                );
74
-            }
75
-        } else {
76
-            $new_value_maybe_array = ModelDataTranslator::prepareFieldValueFromJson(
77
-                $field_obj,
78
-                $original_value_maybe_array,
79
-                $requested_version,
80
-                $timezone_string
81
-            );
82
-        }
83
-        return $new_value_maybe_array;
84
-    }
85
-
86
-
87
-
88
-    /**
89
-     * Prepares an array of field values FOR use in JSON/REST API
90
-     *
91
-     * @param EE_Model_Field_Base $field_obj
92
-     * @param mixed                $original_value_maybe_array
93
-     * @param string               $request_version (eg 4.8.36)
94
-     * @return array
95
-     */
96
-    public static function prepareFieldValuesForJson($field_obj, $original_value_maybe_array, $request_version)
97
-    {
98
-        if (is_array($original_value_maybe_array)) {
99
-            $new_value = array();
100
-            foreach ($original_value_maybe_array as $key => $value) {
101
-                $new_value[$key] = ModelDataTranslator::prepareFieldValuesForJson($field_obj, $value, $request_version);
102
-            }
103
-        } else {
104
-            $new_value = ModelDataTranslator::prepareFieldValueForJson(
105
-                $field_obj,
106
-                $original_value_maybe_array,
107
-                $request_version
108
-            );
109
-        }
110
-        return $new_value;
111
-    }
112
-
113
-
114
-
115
-    /**
116
-     * Prepares incoming data from the json or $_REQUEST parameters for the models'
117
-     * "$query_params".
118
-     *
119
-     * @param EE_Model_Field_Base $field_obj
120
-     * @param mixed                $original_value
121
-     * @param string               $requested_version
122
-     * @param string               $timezone_string treat values as being in this timezone
123
-     * @return mixed
124
-     * @throws RestException
125
-     */
126
-    public static function prepareFieldValueFromJson(
127
-        $field_obj,
128
-        $original_value,
129
-        $requested_version,
130
-        $timezone_string = 'UTC' // UTC
131
-    ) {
132
-        //check if they accidentally submitted an error value. If so throw an exception
133
-        if (is_array($original_value)
134
-            && isset($original_value['error_code'], $original_value['error_message'])) {
135
-            throw new RestException(
136
-                'rest_submitted_error_value',
137
-                sprintf(
138
-                    esc_html__(
139
-                        'You tried to submit a JSON error object as a value for %1$s. That\'s not allowed.',
140
-                        'event_espresso'
141
-                    ),
142
-                    $field_obj->get_name()
143
-                ),
144
-                array(
145
-                    'status' => 400,
146
-                )
147
-            );
148
-        }
149
-        //double-check for serialized PHP. We never accept serialized PHP. No way Jose.
150
-        ModelDataTranslator::throwExceptionIfContainsSerializedData($original_value);
151
-        $timezone_string = $timezone_string !== '' ? $timezone_string : get_option('timezone_string', '');
152
-        $new_value = null;
153
-        //walk through the submitted data and double-check for serialized PHP. We never accept serialized PHP. No
154
-        // way Jose.
155
-        ModelDataTranslator::throwExceptionIfContainsSerializedData($original_value);
156
-        if ($field_obj instanceof EE_Infinite_Integer_Field
157
-            && in_array($original_value, array(null, ''), true)
158
-        ) {
159
-            $new_value = EE_INF;
160
-        } elseif ($field_obj instanceof EE_Datetime_Field) {
161
-            list($offset_sign, $offset_secs) = ModelDataTranslator::parseTimezoneOffset(
162
-                $field_obj->get_timezone_offset(
163
-                    new \DateTimeZone($timezone_string),
164
-                    $original_value
165
-                )
166
-            );
167
-            $offset_string =
168
-                str_pad(
169
-                    floor($offset_secs / HOUR_IN_SECONDS),
170
-                    2,
171
-                    '0',
172
-                    STR_PAD_LEFT
173
-                )
174
-                . ':'
175
-                . str_pad(
176
-                    ($offset_secs % HOUR_IN_SECONDS) / MINUTE_IN_SECONDS,
177
-                    2,
178
-                    '0',
179
-                    STR_PAD_LEFT
180
-                );
181
-            $new_value = rest_parse_date($original_value . $offset_sign . $offset_string);
182
-        } else {
183
-            $new_value = $original_value;
184
-        }
185
-        return $new_value;
186
-    }
187
-
188
-
189
-
190
-    /**
191
-     * Throws an exception if $data is a serialized PHP string (or somehow an actually PHP object, although I don't
192
-     * think that can happen). If $data is an array, recurses into its keys and values
193
-     * @param mixed $data
194
-     * @throws RestException
195
-     * @return void
196
-     */
197
-    public static function throwExceptionIfContainsSerializedData($data)
198
-    {
199
-        if (is_array($data)) {
200
-            foreach ($data as $key => $value) {
201
-                ModelDataTranslator::throwExceptionIfContainsSerializedData($key);
202
-                ModelDataTranslator::throwExceptionIfContainsSerializedData($value);
203
-            }
204
-        } else {
205
-            if (is_serialized($data) || is_object($data)) {
206
-                throw new RestException(
207
-                    'serialized_data_submission_prohibited',
208
-                    esc_html__(
209
-                        // @codingStandardsIgnoreStart
210
-                        'You tried to submit a string of serialized text. Serialized PHP is prohibited over the EE4 REST API.',
211
-                        // @codingStandardsIgnoreEnd
212
-                        'event_espresso'
213
-                    )
214
-                );
215
-            }
216
-        }
217
-    }
218
-
219
-
220
-
221
-    /**
222
-     * determines what's going on with them timezone strings
223
-     *
224
-     * @param int $timezone_offset
225
-     * @return array
226
-     */
227
-    private static function parseTimezoneOffset($timezone_offset)
228
-    {
229
-        $first_char = substr((string)$timezone_offset, 0, 1);
230
-        if ($first_char === '+' || $first_char === '-') {
231
-            $offset_sign = $first_char;
232
-            $offset_secs = substr((string)$timezone_offset, 1);
233
-        } else {
234
-            $offset_sign = '+';
235
-            $offset_secs = $timezone_offset;
236
-        }
237
-        return array($offset_sign, $offset_secs);
238
-    }
239
-
240
-
241
-
242
-    /**
243
-     * Prepares a field's value for display in the API
244
-     *
245
-     * @param EE_Model_Field_Base $field_obj
246
-     * @param mixed                $original_value
247
-     * @param string               $requested_version
248
-     * @return mixed
249
-     */
250
-    public static function prepareFieldValueForJson($field_obj, $original_value, $requested_version)
251
-    {
252
-        if ($original_value === EE_INF) {
253
-            $new_value = ModelDataTranslator::EE_INF_IN_REST;
254
-        } elseif ($field_obj instanceof EE_Datetime_Field) {
255
-            if (is_string($original_value)) {
256
-                //did they submit a string of a unix timestamp?
257
-                if (is_numeric($original_value)) {
258
-                    $datetime_obj = new \DateTime();
259
-                    $datetime_obj->setTimestamp((int)$original_value);
260
-                } else {
261
-                    //first, check if its a MySQL timestamp in GMT
262
-                    $datetime_obj = \DateTime::createFromFormat('Y-m-d H:i:s', $original_value);
263
-                }
264
-                if (! $datetime_obj instanceof \DateTime) {
265
-                    //so it's not a unix timestamp or a MySQL timestamp. Maybe its in the field's date/time format?
266
-                    $datetime_obj = $field_obj->prepare_for_set($original_value);
267
-                }
268
-                $original_value = $datetime_obj;
269
-            }
270
-            if ($original_value instanceof \DateTime) {
271
-                $new_value = $original_value->format('Y-m-d H:i:s');
272
-            } elseif (is_int($original_value)) {
273
-                $new_value = date('Y-m-d H:i:s', $original_value);
274
-            } elseif($original_value === null || $original_value === '') {
275
-                $new_value = null;
276
-            } else {
277
-                //so it's not a datetime object, unix timestamp (as string or int),
278
-                //MySQL timestamp, or even a string in the field object's format. So no idea what it is
279
-                throw new \EE_Error(
280
-                    sprintf(
281
-                        esc_html__(
282
-                        // @codingStandardsIgnoreStart
283
-                            '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".',
284
-                            // @codingStandardsIgnoreEnd
285
-                            'event_espressso'
286
-                        ),
287
-                        $original_value,
288
-                        $field_obj->get_name(),
289
-                        $field_obj->get_model_name(),
290
-                        $field_obj->get_time_format() . ' ' . $field_obj->get_time_format()
291
-                    )
292
-                );
293
-            }
294
-            $new_value = mysql_to_rfc3339($new_value);
295
-        } else {
296
-            $new_value = $original_value;
297
-        }
298
-        //are we about to send an object? just don't. We have no good way to represent it in JSON.
299
-        // can't just check using is_object() because that missed PHP incomplete objects
300
-        if (! ModelDataTranslator::isRepresentableInJson($new_value)) {
301
-            $new_value = array(
302
-                'error_code' => 'php_object_not_return',
303
-                'error_message' => esc_html__('The value of this field in the database is a PHP object, which can\'t be represented in JSON.', 'event_espresso')
304
-            );
305
-        }
306
-        return apply_filters(
307
-            'FHEE__EventEspresso\core\libraries\rest_api\Model_Data_Translator__prepare_field_for_rest_api',
308
-            $new_value,
309
-            $field_obj,
310
-            $original_value,
311
-            $requested_version
312
-        );
313
-    }
314
-
315
-
316
-
317
-    /**
318
-     * Prepares condition-query-parameters (like what's in where and having) from
319
-     * the format expected in the API to use in the models
320
-     *
321
-     * @param array     $inputted_query_params_of_this_type
322
-     * @param EEM_Base $model
323
-     * @param string    $requested_version
324
-     * @param boolean $writing whether this data will be written to the DB, or if we're just building a query.
325
-     *                         If we're writing to the DB, we don't expect any operators, or any logic query parameters,
326
-     *                         and we also won't accept serialized data unless the current user has unfiltered_html.
327
-     * @return array
328
-     * @throws \DomainException
329
-     * @throws RestException
330
-     * @throws EE_Error
331
-     */
332
-    public static function prepareConditionsQueryParamsForModels(
333
-        $inputted_query_params_of_this_type,
334
-        EEM_Base $model,
335
-        $requested_version,
336
-        $writing = false
337
-    ) {
338
-        $query_param_for_models = array();
339
-        $valid_operators = $model->valid_operators();
340
-        foreach ($inputted_query_params_of_this_type as $query_param_key => $query_param_value) {
341
-            $is_gmt_datetime_field = false;
342
-            $query_param_sans_stars = ModelDataTranslator::removeStarsAndAnythingAfterFromConditionQueryParamKey(
343
-                $query_param_key
344
-            );
345
-            $field = ModelDataTranslator::deduceFieldFromQueryParam(
346
-                $query_param_sans_stars,
347
-                $model
348
-            );
349
-            //double-check is it a *_gmt field?
350
-            if (! $field instanceof EE_Model_Field_Base
351
-                && ModelDataTranslator::isGmtDateFieldName($query_param_sans_stars)
352
-            ) {
353
-                //yep, take off '_gmt', and find the field
354
-                $query_param_key = ModelDataTranslator::removeGmtFromFieldName($query_param_sans_stars);
355
-                $field = ModelDataTranslator::deduceFieldFromQueryParam(
356
-                    $query_param_key,
357
-                    $model
358
-                );
359
-                $timezone = 'UTC';
360
-                $is_gmt_datetime_field = true;
361
-            } elseif ($field instanceof EE_Datetime_Field) {
362
-                //so it's not a GMT field. Set the timezone on the model to the default
363
-                $timezone = \EEH_DTT_Helper::get_valid_timezone_string();
364
-            } else {
365
-                //just keep using what's already set for the timezone
366
-                $timezone = $model->get_timezone();
367
-            }
368
-            if ($field instanceof EE_Model_Field_Base) {
369
-                if (! $writing && is_array($query_param_value)) {
370
-                    if (! \EEH_Array::is_array_numerically_and_sequentially_indexed($query_param_value)) {
371
-                        if (defined('EE_REST_API_DEBUG_MODE') && EE_REST_API_DEBUG_MODE) {
372
-                            throw new RestException(
373
-                                'numerically_indexed_array_of_values_only',
374
-                                sprintf(
375
-                                    esc_html__(
376
-                                        'The array provided for the parameter "%1$s" should be numerically indexed.',
377
-                                        'event_espresso'
378
-                                    ),
379
-                                    $query_param_key
380
-                                ),
381
-                                array(
382
-                                    'status' => 400,
383
-                                )
384
-                            );
385
-                        }
386
-                    }
387
-                    //did they specify an operator?
388
-                    if (isset($query_param_value[0])
389
-                        && isset($valid_operators[$query_param_value[0]])
390
-                    ) {
391
-                        $op = $query_param_value[0];
392
-                        $translated_value = array($op);
393
-                        if (array_key_exists($op, $model->valid_in_style_operators())
394
-                            && isset($query_param_value[1])
395
-                            && ! isset($query_param_value[2])
396
-                        ) {
397
-                            $translated_value[] = ModelDataTranslator::prepareFieldValuesFromJson(
398
-                                $field,
399
-                                $query_param_value[1],
400
-                                $requested_version,
401
-                                $timezone
402
-                            );
403
-                        } elseif (array_key_exists($op, $model->valid_between_style_operators())
404
-                            && isset($query_param_value[1], $query_param_value[2])
405
-                            && !isset($query_param_value[3])
406
-                        ) {
407
-                            $translated_value[] = ModelDataTranslator::prepareFieldValuesFromJson(
408
-                                $field,
409
-                                $query_param_value[1],
410
-                                $requested_version,
411
-                                $timezone
412
-                            );
413
-                            $translated_value[] = ModelDataTranslator::prepareFieldValuesFromJson(
414
-                                $field,
415
-                                $query_param_value[2],
416
-                                $requested_version,
417
-                                $timezone
418
-                            );
419
-                        } elseif (array_key_exists($op, $model->valid_like_style_operators())
420
-                            && isset($query_param_value[1])
421
-                            && ! isset($query_param_value[2])
422
-                        ) {
423
-                            //we want to leave this value mostly-as-is (eg don't force it to be a float
424
-                            //or a boolean or an enum value. Leave it as-is with wildcards etc)
425
-                            //but do verify it at least doesn't have any serialized data
426
-                            ModelDataTranslator::throwExceptionIfContainsSerializedData($query_param_value[1]);
427
-                            $translated_value[] = $query_param_value[1];
428
-                        } elseif (array_key_exists($op, $model->valid_null_style_operators())
429
-                            && !isset($query_param_value[1])) {
430
-                            //no arguments should have been provided, so don't look for any
431
-                        } elseif (isset($query_param_value[1])
432
-                            && !isset($query_param_value[2])
433
-                            && ! array_key_exists(
434
-                                $op,
435
-                                array_merge(
436
-                                    $model->valid_in_style_operators(),
437
-                                    $model->valid_null_style_operators(),
438
-                                    $model->valid_like_style_operators(),
439
-                                    $model->valid_between_style_operators()
440
-                                )
441
-                            )
442
-                        ) {
443
-                            //it's a valid operator, but none of the exceptions. Treat it normally.
444
-                            $translated_value[] = ModelDataTranslator::prepareFieldValuesFromJson(
445
-                                $field,
446
-                                $query_param_value[1],
447
-                                $requested_version,
448
-                                $timezone
449
-                            );
450
-                        } else {
451
-                            //so they provided a valid operator, but wrong number of arguments
452
-                            if (defined('EE_REST_API_DEBUG_MODE') && EE_REST_API_DEBUG_MODE) {
453
-                                throw new RestException(
454
-                                    'wrong_number_of_arguments',
455
-                                    sprintf(
456
-                                        esc_html__(
457
-                                            'The operator you provided, "%1$s" had the wrong number of arguments',
458
-                                            'event_espresso'
459
-                                        ),
460
-                                        $op
461
-                                    ),
462
-                                    array(
463
-                                        'status' => 400,
464
-                                    )
465
-                                );
466
-                            }
467
-                            $translated_value = null;
468
-                        }
469
-                    } else {
470
-                        //so they didn't provide a valid operator
471
-                        if (defined('EE_REST_API_DEBUG_MODE') && EE_REST_API_DEBUG_MODE) {
472
-                            throw new RestException(
473
-                                'invalid_operator',
474
-                                sprintf(
475
-                                    esc_html__(
476
-                                        'You provided an invalid parameter, with key "%1$s" and value "%2$s"',
477
-                                        'event_espresso'
478
-                                    ),
479
-                                    $query_param_key,
480
-                                    $query_param_value
481
-                                ),
482
-                                array(
483
-                                    'status' => 400,
484
-                                )
485
-                            );
486
-                        }
487
-                        //if we aren't in debug mode, then just try our best to fulfill the user's request
488
-                        $translated_value = null;
489
-                    }
490
-                } else {
491
-                    $translated_value = ModelDataTranslator::prepareFieldValueFromJson(
492
-                        $field,
493
-                        $query_param_value,
494
-                        $requested_version,
495
-                        $timezone
496
-                    );
497
-                }
498
-                if (
499
-                    (isset($query_param_for_models[$query_param_key]) && $is_gmt_datetime_field)
500
-                    ||
501
-                    $translated_value === null
502
-                ) {
503
-                    //they have already provided a non-gmt field, ignore the gmt one. That's what WP core
504
-                    //currently does (they might change it though). See https://core.trac.wordpress.org/ticket/39954
505
-                    //OR we couldn't create a translated value from their input
506
-                    continue;
507
-                }
508
-                $query_param_for_models[$query_param_key] = $translated_value;
509
-            } else {
510
-                //so this param doesn't correspond to a field eh?
511
-                if ($writing) {
512
-                    //always tell API clients about invalid parameters when they're creating data. Otherwise,
513
-                    //they are probably going to create invalid data
514
-                    throw new RestException(
515
-                        'invalid_field',
516
-                        sprintf(
517
-                            esc_html__('You have provided an invalid parameter: "%1$s"', 'event_espresso'),
518
-                            $query_param_key
519
-                        )
520
-                    );
521
-                } else {
522
-                    //so it's not for a field, is it a logic query param key?
523
-                    if (in_array(
524
-                        $query_param_sans_stars,
525
-                        $model->logic_query_param_keys()
526
-                    )) {
527
-                        $query_param_for_models[$query_param_key] = ModelDataTranslator::prepareConditionsQueryParamsForModels(
528
-                            $query_param_value,
529
-                            $model,
530
-                            $requested_version
531
-                        );
532
-                    } elseif (defined('EE_REST_API_DEBUG_MODE') && EE_REST_API_DEBUG_MODE) {
533
-                        //only tell API clients they got it wrong if we're in debug mode
534
-                        //otherwise try our best ot fulfill their request by ignoring this invalid data
535
-                        throw new RestException(
536
-                            'invalid_parameter',
537
-                            sprintf(
538
-                                esc_html__(
539
-                                    'You provided an invalid parameter, with key "%1$s"',
540
-                                    'event_espresso'
541
-                                ),
542
-                                $query_param_sans_stars
543
-                            ),
544
-                            array(
545
-                                'status' => 400,
546
-                            )
547
-                        );
548
-                    }
549
-                }
550
-            }
551
-        }
552
-        return $query_param_for_models;
553
-    }
554
-
555
-
556
-
557
-    /**
558
-     * Mostly checks if the last 4 characters are "_gmt", indicating its a
559
-     * gmt date field name
560
-     *
561
-     * @param string $field_name
562
-     * @return boolean
563
-     */
564
-    public static function isGmtDateFieldName($field_name)
565
-    {
566
-        return substr(
567
-            ModelDataTranslator::removeStarsAndAnythingAfterFromConditionQueryParamKey($field_name),
568
-            -4,
569
-            4
570
-        ) === '_gmt';
571
-    }
572
-
573
-
574
-
575
-    /**
576
-     * Removes the last "_gmt" part of a field name (and if there is no "_gmt" at the end, leave it alone)
577
-     *
578
-     * @param string $field_name
579
-     * @return string
580
-     */
581
-    public static function removeGmtFromFieldName($field_name)
582
-    {
583
-        if (! ModelDataTranslator::isGmtDateFieldName($field_name)) {
584
-            return $field_name;
585
-        }
586
-        $query_param_sans_stars = ModelDataTranslator::removeStarsAndAnythingAfterFromConditionQueryParamKey(
587
-            $field_name
588
-        );
589
-        $query_param_sans_gmt_and_sans_stars = substr(
590
-            $query_param_sans_stars,
591
-            0,
592
-            strrpos(
593
-                $field_name,
594
-                '_gmt'
595
-            )
596
-        );
597
-        return str_replace($query_param_sans_stars, $query_param_sans_gmt_and_sans_stars, $field_name);
598
-    }
599
-
600
-
601
-
602
-    /**
603
-     * Takes a field name from the REST API and prepares it for the model querying
604
-     *
605
-     * @param string $field_name
606
-     * @return string
607
-     */
608
-    public static function prepareFieldNameFromJson($field_name)
609
-    {
610
-        if (ModelDataTranslator::isGmtDateFieldName($field_name)) {
611
-            return ModelDataTranslator::removeGmtFromFieldName($field_name);
612
-        }
613
-        return $field_name;
614
-    }
615
-
616
-
617
-
618
-    /**
619
-     * Takes array of field names from REST API and prepares for models
620
-     *
621
-     * @param array $field_names
622
-     * @return array of field names (possibly include model prefixes)
623
-     */
624
-    public static function prepareFieldNamesFromJson(array $field_names)
625
-    {
626
-        $new_array = array();
627
-        foreach ($field_names as $key => $field_name) {
628
-            $new_array[$key] = ModelDataTranslator::prepareFieldNameFromJson($field_name);
629
-        }
630
-        return $new_array;
631
-    }
632
-
633
-
634
-
635
-    /**
636
-     * Takes array where array keys are field names (possibly with model path prefixes)
637
-     * from the REST API and prepares them for model querying
638
-     *
639
-     * @param array $field_names_as_keys
640
-     * @return array
641
-     */
642
-    public static function prepareFieldNamesInArrayKeysFromJson(array $field_names_as_keys)
643
-    {
644
-        $new_array = array();
645
-        foreach ($field_names_as_keys as $field_name => $value) {
646
-            $new_array[ModelDataTranslator::prepareFieldNameFromJson($field_name)] = $value;
647
-        }
648
-        return $new_array;
649
-    }
650
-
651
-
652
-
653
-    /**
654
-     * Prepares an array of model query params for use in the REST API
655
-     *
656
-     * @param array     $model_query_params
657
-     * @param EEM_Base $model
658
-     * @param string    $requested_version eg "4.8.36". If null is provided, defaults to the latest release of the EE4
659
-     *                                     REST API
660
-     * @return array which can be passed into the EE4 REST API when querying a model resource
661
-     * @throws EE_Error
662
-     */
663
-    public static function prepareQueryParamsForRestApi(
664
-        array $model_query_params,
665
-        EEM_Base $model,
666
-        $requested_version = null
667
-    ) {
668
-        if ($requested_version === null) {
669
-            $requested_version = \EED_Core_Rest_Api::latest_rest_api_version();
670
-        }
671
-        $rest_query_params = $model_query_params;
672
-        if (isset($model_query_params[0])) {
673
-            $rest_query_params['where'] = ModelDataTranslator::prepareConditionsQueryParamsForRestApi(
674
-                $model_query_params[0],
675
-                $model,
676
-                $requested_version
677
-            );
678
-            unset($rest_query_params[0]);
679
-        }
680
-        if (isset($model_query_params['having'])) {
681
-            $rest_query_params['having'] = ModelDataTranslator::prepareConditionsQueryParamsForRestApi(
682
-                $model_query_params['having'],
683
-                $model,
684
-                $requested_version
685
-            );
686
-        }
687
-        return apply_filters(
688
-            'FHEE__EventEspresso\core\libraries\rest_api\Model_Data_Translator__prepare_query_params_for_rest_api',
689
-            $rest_query_params,
690
-            $model_query_params,
691
-            $model,
692
-            $requested_version
693
-        );
694
-    }
695
-
696
-
697
-
698
-    /**
699
-     * Prepares all the sub-conditions query parameters (eg having or where conditions) for use in the rest api
700
-     *
701
-     * @param array     $inputted_query_params_of_this_type eg like the "where" or "having" conditions query params
702
-     *                                                      passed into EEM_Base::get_all()
703
-     * @param EEM_Base $model
704
-     * @param string    $requested_version                  eg "4.8.36"
705
-     * @return array ready for use in the rest api query params
706
-     * @throws EE_Error
707
-     * @throws ObjectDetectedException if somehow a PHP object were in the query params' values,
708
-     *                                     (which would be really unusual)
709
-     */
710
-    public static function prepareConditionsQueryParamsForRestApi(
711
-        $inputted_query_params_of_this_type,
712
-        EEM_Base $model,
713
-        $requested_version
714
-    ) {
715
-        $query_param_for_models = array();
716
-        foreach ($inputted_query_params_of_this_type as $query_param_key => $query_param_value) {
717
-            $field = ModelDataTranslator::deduceFieldFromQueryParam(
718
-                ModelDataTranslator::removeStarsAndAnythingAfterFromConditionQueryParamKey($query_param_key),
719
-                $model
720
-            );
721
-            if ($field instanceof EE_Model_Field_Base) {
722
-                //did they specify an operator?
723
-                if (is_array($query_param_value)) {
724
-                    $op = $query_param_value[0];
725
-                    $translated_value = array($op);
726
-                    if (isset($query_param_value[1])) {
727
-                        $value = $query_param_value[1];
728
-                        $translated_value[1] = ModelDataTranslator::prepareFieldValuesForJson(
729
-                            $field,
730
-                            $value,
731
-                            $requested_version
732
-                        );
733
-                    }
734
-                } else {
735
-                    $translated_value = ModelDataTranslator::prepareFieldValueForJson(
736
-                        $field,
737
-                        $query_param_value,
738
-                        $requested_version
739
-                    );
740
-                }
741
-                $query_param_for_models[$query_param_key] = $translated_value;
742
-            } else {
743
-                //so it's not for a field, assume it's a logic query param key
744
-                $query_param_for_models[$query_param_key] = ModelDataTranslator::prepareConditionsQueryParamsForRestApi(
745
-                    $query_param_value,
746
-                    $model,
747
-                    $requested_version
748
-                );
749
-            }
750
-        }
751
-        return $query_param_for_models;
752
-    }
753
-
754
-
755
-
756
-    /**
757
-     * @param $condition_query_param_key
758
-     * @return string
759
-     */
760
-    public static function removeStarsAndAnythingAfterFromConditionQueryParamKey($condition_query_param_key)
761
-    {
762
-        $pos_of_star = strpos($condition_query_param_key, '*');
763
-        if ($pos_of_star === false) {
764
-            return $condition_query_param_key;
765
-        } else {
766
-            $condition_query_param_sans_star = substr($condition_query_param_key, 0, $pos_of_star);
767
-            return $condition_query_param_sans_star;
768
-        }
769
-    }
770
-
771
-
772
-
773
-    /**
774
-     * Takes the input parameter and finds the model field that it indicates.
775
-     *
776
-     * @param string    $query_param_name like Registration.Transaction.TXN_ID, Event.Datetime.start_time, or REG_ID
777
-     * @param EEM_Base $model
778
-     * @return EE_Model_Field_Base
779
-     * @throws EE_Error
780
-     */
781
-    public static function deduceFieldFromQueryParam($query_param_name, EEM_Base $model)
782
-    {
783
-        //ok, now proceed with deducing which part is the model's name, and which is the field's name
784
-        //which will help us find the database table and column
785
-        $query_param_parts = explode('.', $query_param_name);
786
-        if (empty($query_param_parts)) {
787
-            throw new EE_Error(
788
-                sprintf(
789
-                    __(
790
-                        '_extract_column_name is empty when trying to extract column and table name from %s',
791
-                        'event_espresso'
792
-                    ),
793
-                    $query_param_name
794
-                )
795
-            );
796
-        }
797
-        $number_of_parts = count($query_param_parts);
798
-        $last_query_param_part = $query_param_parts[count($query_param_parts) - 1];
799
-        if ($number_of_parts === 1) {
800
-            $field_name = $last_query_param_part;
801
-        } else {// $number_of_parts >= 2
802
-            //the last part is the column name, and there are only 2parts. therefore...
803
-            $field_name = $last_query_param_part;
804
-            $model = \EE_Registry::instance()->load_model($query_param_parts[$number_of_parts - 2]);
805
-        }
806
-        try {
807
-            return $model->field_settings_for($field_name, false);
808
-        } catch (EE_Error $e) {
809
-            return null;
810
-        }
811
-    }
812
-
813
-
814
-
815
-    /**
816
-     * Returns true if $data can be easily represented in JSON.
817
-     * Basically, objects and resources can't be represented in JSON easily.
818
-     * @param mixed $data
819
-     * @return bool
820
-     */
821
-    protected static function isRepresentableInJson($data)
822
-    {
823
-        return is_scalar($data)
824
-               || is_array($data)
825
-               || is_null($data);
826
-    }
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
+	/**
48
+	 * Prepares a possible array of input values from JSON for use by the models
49
+	 *
50
+	 * @param EE_Model_Field_Base $field_obj
51
+	 * @param mixed                $original_value_maybe_array
52
+	 * @param string               $requested_version
53
+	 * @param string               $timezone_string treat values as being in this timezone
54
+	 * @return mixed
55
+	 * @throws RestException
56
+	 */
57
+	public static function prepareFieldValuesFromJson(
58
+		$field_obj,
59
+		$original_value_maybe_array,
60
+		$requested_version,
61
+		$timezone_string = 'UTC'
62
+	) {
63
+		if (is_array($original_value_maybe_array)
64
+			&& ! $field_obj instanceof EE_Serialized_Text_Field
65
+		) {
66
+			$new_value_maybe_array = array();
67
+			foreach ($original_value_maybe_array as $array_key => $array_item) {
68
+				$new_value_maybe_array[$array_key] = ModelDataTranslator::prepareFieldValueFromJson(
69
+					$field_obj,
70
+					$array_item,
71
+					$requested_version,
72
+					$timezone_string
73
+				);
74
+			}
75
+		} else {
76
+			$new_value_maybe_array = ModelDataTranslator::prepareFieldValueFromJson(
77
+				$field_obj,
78
+				$original_value_maybe_array,
79
+				$requested_version,
80
+				$timezone_string
81
+			);
82
+		}
83
+		return $new_value_maybe_array;
84
+	}
85
+
86
+
87
+
88
+	/**
89
+	 * Prepares an array of field values FOR use in JSON/REST API
90
+	 *
91
+	 * @param EE_Model_Field_Base $field_obj
92
+	 * @param mixed                $original_value_maybe_array
93
+	 * @param string               $request_version (eg 4.8.36)
94
+	 * @return array
95
+	 */
96
+	public static function prepareFieldValuesForJson($field_obj, $original_value_maybe_array, $request_version)
97
+	{
98
+		if (is_array($original_value_maybe_array)) {
99
+			$new_value = array();
100
+			foreach ($original_value_maybe_array as $key => $value) {
101
+				$new_value[$key] = ModelDataTranslator::prepareFieldValuesForJson($field_obj, $value, $request_version);
102
+			}
103
+		} else {
104
+			$new_value = ModelDataTranslator::prepareFieldValueForJson(
105
+				$field_obj,
106
+				$original_value_maybe_array,
107
+				$request_version
108
+			);
109
+		}
110
+		return $new_value;
111
+	}
112
+
113
+
114
+
115
+	/**
116
+	 * Prepares incoming data from the json or $_REQUEST parameters for the models'
117
+	 * "$query_params".
118
+	 *
119
+	 * @param EE_Model_Field_Base $field_obj
120
+	 * @param mixed                $original_value
121
+	 * @param string               $requested_version
122
+	 * @param string               $timezone_string treat values as being in this timezone
123
+	 * @return mixed
124
+	 * @throws RestException
125
+	 */
126
+	public static function prepareFieldValueFromJson(
127
+		$field_obj,
128
+		$original_value,
129
+		$requested_version,
130
+		$timezone_string = 'UTC' // UTC
131
+	) {
132
+		//check if they accidentally submitted an error value. If so throw an exception
133
+		if (is_array($original_value)
134
+			&& isset($original_value['error_code'], $original_value['error_message'])) {
135
+			throw new RestException(
136
+				'rest_submitted_error_value',
137
+				sprintf(
138
+					esc_html__(
139
+						'You tried to submit a JSON error object as a value for %1$s. That\'s not allowed.',
140
+						'event_espresso'
141
+					),
142
+					$field_obj->get_name()
143
+				),
144
+				array(
145
+					'status' => 400,
146
+				)
147
+			);
148
+		}
149
+		//double-check for serialized PHP. We never accept serialized PHP. No way Jose.
150
+		ModelDataTranslator::throwExceptionIfContainsSerializedData($original_value);
151
+		$timezone_string = $timezone_string !== '' ? $timezone_string : get_option('timezone_string', '');
152
+		$new_value = null;
153
+		//walk through the submitted data and double-check for serialized PHP. We never accept serialized PHP. No
154
+		// way Jose.
155
+		ModelDataTranslator::throwExceptionIfContainsSerializedData($original_value);
156
+		if ($field_obj instanceof EE_Infinite_Integer_Field
157
+			&& in_array($original_value, array(null, ''), true)
158
+		) {
159
+			$new_value = EE_INF;
160
+		} elseif ($field_obj instanceof EE_Datetime_Field) {
161
+			list($offset_sign, $offset_secs) = ModelDataTranslator::parseTimezoneOffset(
162
+				$field_obj->get_timezone_offset(
163
+					new \DateTimeZone($timezone_string),
164
+					$original_value
165
+				)
166
+			);
167
+			$offset_string =
168
+				str_pad(
169
+					floor($offset_secs / HOUR_IN_SECONDS),
170
+					2,
171
+					'0',
172
+					STR_PAD_LEFT
173
+				)
174
+				. ':'
175
+				. str_pad(
176
+					($offset_secs % HOUR_IN_SECONDS) / MINUTE_IN_SECONDS,
177
+					2,
178
+					'0',
179
+					STR_PAD_LEFT
180
+				);
181
+			$new_value = rest_parse_date($original_value . $offset_sign . $offset_string);
182
+		} else {
183
+			$new_value = $original_value;
184
+		}
185
+		return $new_value;
186
+	}
187
+
188
+
189
+
190
+	/**
191
+	 * Throws an exception if $data is a serialized PHP string (or somehow an actually PHP object, although I don't
192
+	 * think that can happen). If $data is an array, recurses into its keys and values
193
+	 * @param mixed $data
194
+	 * @throws RestException
195
+	 * @return void
196
+	 */
197
+	public static function throwExceptionIfContainsSerializedData($data)
198
+	{
199
+		if (is_array($data)) {
200
+			foreach ($data as $key => $value) {
201
+				ModelDataTranslator::throwExceptionIfContainsSerializedData($key);
202
+				ModelDataTranslator::throwExceptionIfContainsSerializedData($value);
203
+			}
204
+		} else {
205
+			if (is_serialized($data) || is_object($data)) {
206
+				throw new RestException(
207
+					'serialized_data_submission_prohibited',
208
+					esc_html__(
209
+						// @codingStandardsIgnoreStart
210
+						'You tried to submit a string of serialized text. Serialized PHP is prohibited over the EE4 REST API.',
211
+						// @codingStandardsIgnoreEnd
212
+						'event_espresso'
213
+					)
214
+				);
215
+			}
216
+		}
217
+	}
218
+
219
+
220
+
221
+	/**
222
+	 * determines what's going on with them timezone strings
223
+	 *
224
+	 * @param int $timezone_offset
225
+	 * @return array
226
+	 */
227
+	private static function parseTimezoneOffset($timezone_offset)
228
+	{
229
+		$first_char = substr((string)$timezone_offset, 0, 1);
230
+		if ($first_char === '+' || $first_char === '-') {
231
+			$offset_sign = $first_char;
232
+			$offset_secs = substr((string)$timezone_offset, 1);
233
+		} else {
234
+			$offset_sign = '+';
235
+			$offset_secs = $timezone_offset;
236
+		}
237
+		return array($offset_sign, $offset_secs);
238
+	}
239
+
240
+
241
+
242
+	/**
243
+	 * Prepares a field's value for display in the API
244
+	 *
245
+	 * @param EE_Model_Field_Base $field_obj
246
+	 * @param mixed                $original_value
247
+	 * @param string               $requested_version
248
+	 * @return mixed
249
+	 */
250
+	public static function prepareFieldValueForJson($field_obj, $original_value, $requested_version)
251
+	{
252
+		if ($original_value === EE_INF) {
253
+			$new_value = ModelDataTranslator::EE_INF_IN_REST;
254
+		} elseif ($field_obj instanceof EE_Datetime_Field) {
255
+			if (is_string($original_value)) {
256
+				//did they submit a string of a unix timestamp?
257
+				if (is_numeric($original_value)) {
258
+					$datetime_obj = new \DateTime();
259
+					$datetime_obj->setTimestamp((int)$original_value);
260
+				} else {
261
+					//first, check if its a MySQL timestamp in GMT
262
+					$datetime_obj = \DateTime::createFromFormat('Y-m-d H:i:s', $original_value);
263
+				}
264
+				if (! $datetime_obj instanceof \DateTime) {
265
+					//so it's not a unix timestamp or a MySQL timestamp. Maybe its in the field's date/time format?
266
+					$datetime_obj = $field_obj->prepare_for_set($original_value);
267
+				}
268
+				$original_value = $datetime_obj;
269
+			}
270
+			if ($original_value instanceof \DateTime) {
271
+				$new_value = $original_value->format('Y-m-d H:i:s');
272
+			} elseif (is_int($original_value)) {
273
+				$new_value = date('Y-m-d H:i:s', $original_value);
274
+			} elseif($original_value === null || $original_value === '') {
275
+				$new_value = null;
276
+			} else {
277
+				//so it's not a datetime object, unix timestamp (as string or int),
278
+				//MySQL timestamp, or even a string in the field object's format. So no idea what it is
279
+				throw new \EE_Error(
280
+					sprintf(
281
+						esc_html__(
282
+						// @codingStandardsIgnoreStart
283
+							'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".',
284
+							// @codingStandardsIgnoreEnd
285
+							'event_espressso'
286
+						),
287
+						$original_value,
288
+						$field_obj->get_name(),
289
+						$field_obj->get_model_name(),
290
+						$field_obj->get_time_format() . ' ' . $field_obj->get_time_format()
291
+					)
292
+				);
293
+			}
294
+			$new_value = mysql_to_rfc3339($new_value);
295
+		} else {
296
+			$new_value = $original_value;
297
+		}
298
+		//are we about to send an object? just don't. We have no good way to represent it in JSON.
299
+		// can't just check using is_object() because that missed PHP incomplete objects
300
+		if (! ModelDataTranslator::isRepresentableInJson($new_value)) {
301
+			$new_value = array(
302
+				'error_code' => 'php_object_not_return',
303
+				'error_message' => esc_html__('The value of this field in the database is a PHP object, which can\'t be represented in JSON.', 'event_espresso')
304
+			);
305
+		}
306
+		return apply_filters(
307
+			'FHEE__EventEspresso\core\libraries\rest_api\Model_Data_Translator__prepare_field_for_rest_api',
308
+			$new_value,
309
+			$field_obj,
310
+			$original_value,
311
+			$requested_version
312
+		);
313
+	}
314
+
315
+
316
+
317
+	/**
318
+	 * Prepares condition-query-parameters (like what's in where and having) from
319
+	 * the format expected in the API to use in the models
320
+	 *
321
+	 * @param array     $inputted_query_params_of_this_type
322
+	 * @param EEM_Base $model
323
+	 * @param string    $requested_version
324
+	 * @param boolean $writing whether this data will be written to the DB, or if we're just building a query.
325
+	 *                         If we're writing to the DB, we don't expect any operators, or any logic query parameters,
326
+	 *                         and we also won't accept serialized data unless the current user has unfiltered_html.
327
+	 * @return array
328
+	 * @throws \DomainException
329
+	 * @throws RestException
330
+	 * @throws EE_Error
331
+	 */
332
+	public static function prepareConditionsQueryParamsForModels(
333
+		$inputted_query_params_of_this_type,
334
+		EEM_Base $model,
335
+		$requested_version,
336
+		$writing = false
337
+	) {
338
+		$query_param_for_models = array();
339
+		$valid_operators = $model->valid_operators();
340
+		foreach ($inputted_query_params_of_this_type as $query_param_key => $query_param_value) {
341
+			$is_gmt_datetime_field = false;
342
+			$query_param_sans_stars = ModelDataTranslator::removeStarsAndAnythingAfterFromConditionQueryParamKey(
343
+				$query_param_key
344
+			);
345
+			$field = ModelDataTranslator::deduceFieldFromQueryParam(
346
+				$query_param_sans_stars,
347
+				$model
348
+			);
349
+			//double-check is it a *_gmt field?
350
+			if (! $field instanceof EE_Model_Field_Base
351
+				&& ModelDataTranslator::isGmtDateFieldName($query_param_sans_stars)
352
+			) {
353
+				//yep, take off '_gmt', and find the field
354
+				$query_param_key = ModelDataTranslator::removeGmtFromFieldName($query_param_sans_stars);
355
+				$field = ModelDataTranslator::deduceFieldFromQueryParam(
356
+					$query_param_key,
357
+					$model
358
+				);
359
+				$timezone = 'UTC';
360
+				$is_gmt_datetime_field = true;
361
+			} elseif ($field instanceof EE_Datetime_Field) {
362
+				//so it's not a GMT field. Set the timezone on the model to the default
363
+				$timezone = \EEH_DTT_Helper::get_valid_timezone_string();
364
+			} else {
365
+				//just keep using what's already set for the timezone
366
+				$timezone = $model->get_timezone();
367
+			}
368
+			if ($field instanceof EE_Model_Field_Base) {
369
+				if (! $writing && is_array($query_param_value)) {
370
+					if (! \EEH_Array::is_array_numerically_and_sequentially_indexed($query_param_value)) {
371
+						if (defined('EE_REST_API_DEBUG_MODE') && EE_REST_API_DEBUG_MODE) {
372
+							throw new RestException(
373
+								'numerically_indexed_array_of_values_only',
374
+								sprintf(
375
+									esc_html__(
376
+										'The array provided for the parameter "%1$s" should be numerically indexed.',
377
+										'event_espresso'
378
+									),
379
+									$query_param_key
380
+								),
381
+								array(
382
+									'status' => 400,
383
+								)
384
+							);
385
+						}
386
+					}
387
+					//did they specify an operator?
388
+					if (isset($query_param_value[0])
389
+						&& isset($valid_operators[$query_param_value[0]])
390
+					) {
391
+						$op = $query_param_value[0];
392
+						$translated_value = array($op);
393
+						if (array_key_exists($op, $model->valid_in_style_operators())
394
+							&& isset($query_param_value[1])
395
+							&& ! isset($query_param_value[2])
396
+						) {
397
+							$translated_value[] = ModelDataTranslator::prepareFieldValuesFromJson(
398
+								$field,
399
+								$query_param_value[1],
400
+								$requested_version,
401
+								$timezone
402
+							);
403
+						} elseif (array_key_exists($op, $model->valid_between_style_operators())
404
+							&& isset($query_param_value[1], $query_param_value[2])
405
+							&& !isset($query_param_value[3])
406
+						) {
407
+							$translated_value[] = ModelDataTranslator::prepareFieldValuesFromJson(
408
+								$field,
409
+								$query_param_value[1],
410
+								$requested_version,
411
+								$timezone
412
+							);
413
+							$translated_value[] = ModelDataTranslator::prepareFieldValuesFromJson(
414
+								$field,
415
+								$query_param_value[2],
416
+								$requested_version,
417
+								$timezone
418
+							);
419
+						} elseif (array_key_exists($op, $model->valid_like_style_operators())
420
+							&& isset($query_param_value[1])
421
+							&& ! isset($query_param_value[2])
422
+						) {
423
+							//we want to leave this value mostly-as-is (eg don't force it to be a float
424
+							//or a boolean or an enum value. Leave it as-is with wildcards etc)
425
+							//but do verify it at least doesn't have any serialized data
426
+							ModelDataTranslator::throwExceptionIfContainsSerializedData($query_param_value[1]);
427
+							$translated_value[] = $query_param_value[1];
428
+						} elseif (array_key_exists($op, $model->valid_null_style_operators())
429
+							&& !isset($query_param_value[1])) {
430
+							//no arguments should have been provided, so don't look for any
431
+						} elseif (isset($query_param_value[1])
432
+							&& !isset($query_param_value[2])
433
+							&& ! array_key_exists(
434
+								$op,
435
+								array_merge(
436
+									$model->valid_in_style_operators(),
437
+									$model->valid_null_style_operators(),
438
+									$model->valid_like_style_operators(),
439
+									$model->valid_between_style_operators()
440
+								)
441
+							)
442
+						) {
443
+							//it's a valid operator, but none of the exceptions. Treat it normally.
444
+							$translated_value[] = ModelDataTranslator::prepareFieldValuesFromJson(
445
+								$field,
446
+								$query_param_value[1],
447
+								$requested_version,
448
+								$timezone
449
+							);
450
+						} else {
451
+							//so they provided a valid operator, but wrong number of arguments
452
+							if (defined('EE_REST_API_DEBUG_MODE') && EE_REST_API_DEBUG_MODE) {
453
+								throw new RestException(
454
+									'wrong_number_of_arguments',
455
+									sprintf(
456
+										esc_html__(
457
+											'The operator you provided, "%1$s" had the wrong number of arguments',
458
+											'event_espresso'
459
+										),
460
+										$op
461
+									),
462
+									array(
463
+										'status' => 400,
464
+									)
465
+								);
466
+							}
467
+							$translated_value = null;
468
+						}
469
+					} else {
470
+						//so they didn't provide a valid operator
471
+						if (defined('EE_REST_API_DEBUG_MODE') && EE_REST_API_DEBUG_MODE) {
472
+							throw new RestException(
473
+								'invalid_operator',
474
+								sprintf(
475
+									esc_html__(
476
+										'You provided an invalid parameter, with key "%1$s" and value "%2$s"',
477
+										'event_espresso'
478
+									),
479
+									$query_param_key,
480
+									$query_param_value
481
+								),
482
+								array(
483
+									'status' => 400,
484
+								)
485
+							);
486
+						}
487
+						//if we aren't in debug mode, then just try our best to fulfill the user's request
488
+						$translated_value = null;
489
+					}
490
+				} else {
491
+					$translated_value = ModelDataTranslator::prepareFieldValueFromJson(
492
+						$field,
493
+						$query_param_value,
494
+						$requested_version,
495
+						$timezone
496
+					);
497
+				}
498
+				if (
499
+					(isset($query_param_for_models[$query_param_key]) && $is_gmt_datetime_field)
500
+					||
501
+					$translated_value === null
502
+				) {
503
+					//they have already provided a non-gmt field, ignore the gmt one. That's what WP core
504
+					//currently does (they might change it though). See https://core.trac.wordpress.org/ticket/39954
505
+					//OR we couldn't create a translated value from their input
506
+					continue;
507
+				}
508
+				$query_param_for_models[$query_param_key] = $translated_value;
509
+			} else {
510
+				//so this param doesn't correspond to a field eh?
511
+				if ($writing) {
512
+					//always tell API clients about invalid parameters when they're creating data. Otherwise,
513
+					//they are probably going to create invalid data
514
+					throw new RestException(
515
+						'invalid_field',
516
+						sprintf(
517
+							esc_html__('You have provided an invalid parameter: "%1$s"', 'event_espresso'),
518
+							$query_param_key
519
+						)
520
+					);
521
+				} else {
522
+					//so it's not for a field, is it a logic query param key?
523
+					if (in_array(
524
+						$query_param_sans_stars,
525
+						$model->logic_query_param_keys()
526
+					)) {
527
+						$query_param_for_models[$query_param_key] = ModelDataTranslator::prepareConditionsQueryParamsForModels(
528
+							$query_param_value,
529
+							$model,
530
+							$requested_version
531
+						);
532
+					} elseif (defined('EE_REST_API_DEBUG_MODE') && EE_REST_API_DEBUG_MODE) {
533
+						//only tell API clients they got it wrong if we're in debug mode
534
+						//otherwise try our best ot fulfill their request by ignoring this invalid data
535
+						throw new RestException(
536
+							'invalid_parameter',
537
+							sprintf(
538
+								esc_html__(
539
+									'You provided an invalid parameter, with key "%1$s"',
540
+									'event_espresso'
541
+								),
542
+								$query_param_sans_stars
543
+							),
544
+							array(
545
+								'status' => 400,
546
+							)
547
+						);
548
+					}
549
+				}
550
+			}
551
+		}
552
+		return $query_param_for_models;
553
+	}
554
+
555
+
556
+
557
+	/**
558
+	 * Mostly checks if the last 4 characters are "_gmt", indicating its a
559
+	 * gmt date field name
560
+	 *
561
+	 * @param string $field_name
562
+	 * @return boolean
563
+	 */
564
+	public static function isGmtDateFieldName($field_name)
565
+	{
566
+		return substr(
567
+			ModelDataTranslator::removeStarsAndAnythingAfterFromConditionQueryParamKey($field_name),
568
+			-4,
569
+			4
570
+		) === '_gmt';
571
+	}
572
+
573
+
574
+
575
+	/**
576
+	 * Removes the last "_gmt" part of a field name (and if there is no "_gmt" at the end, leave it alone)
577
+	 *
578
+	 * @param string $field_name
579
+	 * @return string
580
+	 */
581
+	public static function removeGmtFromFieldName($field_name)
582
+	{
583
+		if (! ModelDataTranslator::isGmtDateFieldName($field_name)) {
584
+			return $field_name;
585
+		}
586
+		$query_param_sans_stars = ModelDataTranslator::removeStarsAndAnythingAfterFromConditionQueryParamKey(
587
+			$field_name
588
+		);
589
+		$query_param_sans_gmt_and_sans_stars = substr(
590
+			$query_param_sans_stars,
591
+			0,
592
+			strrpos(
593
+				$field_name,
594
+				'_gmt'
595
+			)
596
+		);
597
+		return str_replace($query_param_sans_stars, $query_param_sans_gmt_and_sans_stars, $field_name);
598
+	}
599
+
600
+
601
+
602
+	/**
603
+	 * Takes a field name from the REST API and prepares it for the model querying
604
+	 *
605
+	 * @param string $field_name
606
+	 * @return string
607
+	 */
608
+	public static function prepareFieldNameFromJson($field_name)
609
+	{
610
+		if (ModelDataTranslator::isGmtDateFieldName($field_name)) {
611
+			return ModelDataTranslator::removeGmtFromFieldName($field_name);
612
+		}
613
+		return $field_name;
614
+	}
615
+
616
+
617
+
618
+	/**
619
+	 * Takes array of field names from REST API and prepares for models
620
+	 *
621
+	 * @param array $field_names
622
+	 * @return array of field names (possibly include model prefixes)
623
+	 */
624
+	public static function prepareFieldNamesFromJson(array $field_names)
625
+	{
626
+		$new_array = array();
627
+		foreach ($field_names as $key => $field_name) {
628
+			$new_array[$key] = ModelDataTranslator::prepareFieldNameFromJson($field_name);
629
+		}
630
+		return $new_array;
631
+	}
632
+
633
+
634
+
635
+	/**
636
+	 * Takes array where array keys are field names (possibly with model path prefixes)
637
+	 * from the REST API and prepares them for model querying
638
+	 *
639
+	 * @param array $field_names_as_keys
640
+	 * @return array
641
+	 */
642
+	public static function prepareFieldNamesInArrayKeysFromJson(array $field_names_as_keys)
643
+	{
644
+		$new_array = array();
645
+		foreach ($field_names_as_keys as $field_name => $value) {
646
+			$new_array[ModelDataTranslator::prepareFieldNameFromJson($field_name)] = $value;
647
+		}
648
+		return $new_array;
649
+	}
650
+
651
+
652
+
653
+	/**
654
+	 * Prepares an array of model query params for use in the REST API
655
+	 *
656
+	 * @param array     $model_query_params
657
+	 * @param EEM_Base $model
658
+	 * @param string    $requested_version eg "4.8.36". If null is provided, defaults to the latest release of the EE4
659
+	 *                                     REST API
660
+	 * @return array which can be passed into the EE4 REST API when querying a model resource
661
+	 * @throws EE_Error
662
+	 */
663
+	public static function prepareQueryParamsForRestApi(
664
+		array $model_query_params,
665
+		EEM_Base $model,
666
+		$requested_version = null
667
+	) {
668
+		if ($requested_version === null) {
669
+			$requested_version = \EED_Core_Rest_Api::latest_rest_api_version();
670
+		}
671
+		$rest_query_params = $model_query_params;
672
+		if (isset($model_query_params[0])) {
673
+			$rest_query_params['where'] = ModelDataTranslator::prepareConditionsQueryParamsForRestApi(
674
+				$model_query_params[0],
675
+				$model,
676
+				$requested_version
677
+			);
678
+			unset($rest_query_params[0]);
679
+		}
680
+		if (isset($model_query_params['having'])) {
681
+			$rest_query_params['having'] = ModelDataTranslator::prepareConditionsQueryParamsForRestApi(
682
+				$model_query_params['having'],
683
+				$model,
684
+				$requested_version
685
+			);
686
+		}
687
+		return apply_filters(
688
+			'FHEE__EventEspresso\core\libraries\rest_api\Model_Data_Translator__prepare_query_params_for_rest_api',
689
+			$rest_query_params,
690
+			$model_query_params,
691
+			$model,
692
+			$requested_version
693
+		);
694
+	}
695
+
696
+
697
+
698
+	/**
699
+	 * Prepares all the sub-conditions query parameters (eg having or where conditions) for use in the rest api
700
+	 *
701
+	 * @param array     $inputted_query_params_of_this_type eg like the "where" or "having" conditions query params
702
+	 *                                                      passed into EEM_Base::get_all()
703
+	 * @param EEM_Base $model
704
+	 * @param string    $requested_version                  eg "4.8.36"
705
+	 * @return array ready for use in the rest api query params
706
+	 * @throws EE_Error
707
+	 * @throws ObjectDetectedException if somehow a PHP object were in the query params' values,
708
+	 *                                     (which would be really unusual)
709
+	 */
710
+	public static function prepareConditionsQueryParamsForRestApi(
711
+		$inputted_query_params_of_this_type,
712
+		EEM_Base $model,
713
+		$requested_version
714
+	) {
715
+		$query_param_for_models = array();
716
+		foreach ($inputted_query_params_of_this_type as $query_param_key => $query_param_value) {
717
+			$field = ModelDataTranslator::deduceFieldFromQueryParam(
718
+				ModelDataTranslator::removeStarsAndAnythingAfterFromConditionQueryParamKey($query_param_key),
719
+				$model
720
+			);
721
+			if ($field instanceof EE_Model_Field_Base) {
722
+				//did they specify an operator?
723
+				if (is_array($query_param_value)) {
724
+					$op = $query_param_value[0];
725
+					$translated_value = array($op);
726
+					if (isset($query_param_value[1])) {
727
+						$value = $query_param_value[1];
728
+						$translated_value[1] = ModelDataTranslator::prepareFieldValuesForJson(
729
+							$field,
730
+							$value,
731
+							$requested_version
732
+						);
733
+					}
734
+				} else {
735
+					$translated_value = ModelDataTranslator::prepareFieldValueForJson(
736
+						$field,
737
+						$query_param_value,
738
+						$requested_version
739
+					);
740
+				}
741
+				$query_param_for_models[$query_param_key] = $translated_value;
742
+			} else {
743
+				//so it's not for a field, assume it's a logic query param key
744
+				$query_param_for_models[$query_param_key] = ModelDataTranslator::prepareConditionsQueryParamsForRestApi(
745
+					$query_param_value,
746
+					$model,
747
+					$requested_version
748
+				);
749
+			}
750
+		}
751
+		return $query_param_for_models;
752
+	}
753
+
754
+
755
+
756
+	/**
757
+	 * @param $condition_query_param_key
758
+	 * @return string
759
+	 */
760
+	public static function removeStarsAndAnythingAfterFromConditionQueryParamKey($condition_query_param_key)
761
+	{
762
+		$pos_of_star = strpos($condition_query_param_key, '*');
763
+		if ($pos_of_star === false) {
764
+			return $condition_query_param_key;
765
+		} else {
766
+			$condition_query_param_sans_star = substr($condition_query_param_key, 0, $pos_of_star);
767
+			return $condition_query_param_sans_star;
768
+		}
769
+	}
770
+
771
+
772
+
773
+	/**
774
+	 * Takes the input parameter and finds the model field that it indicates.
775
+	 *
776
+	 * @param string    $query_param_name like Registration.Transaction.TXN_ID, Event.Datetime.start_time, or REG_ID
777
+	 * @param EEM_Base $model
778
+	 * @return EE_Model_Field_Base
779
+	 * @throws EE_Error
780
+	 */
781
+	public static function deduceFieldFromQueryParam($query_param_name, EEM_Base $model)
782
+	{
783
+		//ok, now proceed with deducing which part is the model's name, and which is the field's name
784
+		//which will help us find the database table and column
785
+		$query_param_parts = explode('.', $query_param_name);
786
+		if (empty($query_param_parts)) {
787
+			throw new EE_Error(
788
+				sprintf(
789
+					__(
790
+						'_extract_column_name is empty when trying to extract column and table name from %s',
791
+						'event_espresso'
792
+					),
793
+					$query_param_name
794
+				)
795
+			);
796
+		}
797
+		$number_of_parts = count($query_param_parts);
798
+		$last_query_param_part = $query_param_parts[count($query_param_parts) - 1];
799
+		if ($number_of_parts === 1) {
800
+			$field_name = $last_query_param_part;
801
+		} else {// $number_of_parts >= 2
802
+			//the last part is the column name, and there are only 2parts. therefore...
803
+			$field_name = $last_query_param_part;
804
+			$model = \EE_Registry::instance()->load_model($query_param_parts[$number_of_parts - 2]);
805
+		}
806
+		try {
807
+			return $model->field_settings_for($field_name, false);
808
+		} catch (EE_Error $e) {
809
+			return null;
810
+		}
811
+	}
812
+
813
+
814
+
815
+	/**
816
+	 * Returns true if $data can be easily represented in JSON.
817
+	 * Basically, objects and resources can't be represented in JSON easily.
818
+	 * @param mixed $data
819
+	 * @return bool
820
+	 */
821
+	protected static function isRepresentableInJson($data)
822
+	{
823
+		return is_scalar($data)
824
+			   || is_array($data)
825
+			   || is_null($data);
826
+	}
827 827
 }
Please login to merge, or discard this patch.
Spacing   +16 added lines, -16 removed lines patch added patch discarded remove patch
@@ -10,7 +10,7 @@  discard block
 block discarded – undo
10 10
 use EE_Serialized_Text_Field;
11 11
 use EEM_Base;
12 12
 
13
-if (! defined('EVENT_ESPRESSO_VERSION')) {
13
+if ( ! defined('EVENT_ESPRESSO_VERSION')) {
14 14
     exit('No direct script access allowed');
15 15
 }
16 16
 
@@ -178,7 +178,7 @@  discard block
 block discarded – undo
178 178
                     '0',
179 179
                     STR_PAD_LEFT
180 180
                 );
181
-            $new_value = rest_parse_date($original_value . $offset_sign . $offset_string);
181
+            $new_value = rest_parse_date($original_value.$offset_sign.$offset_string);
182 182
         } else {
183 183
             $new_value = $original_value;
184 184
         }
@@ -226,10 +226,10 @@  discard block
 block discarded – undo
226 226
      */
227 227
     private static function parseTimezoneOffset($timezone_offset)
228 228
     {
229
-        $first_char = substr((string)$timezone_offset, 0, 1);
229
+        $first_char = substr((string) $timezone_offset, 0, 1);
230 230
         if ($first_char === '+' || $first_char === '-') {
231 231
             $offset_sign = $first_char;
232
-            $offset_secs = substr((string)$timezone_offset, 1);
232
+            $offset_secs = substr((string) $timezone_offset, 1);
233 233
         } else {
234 234
             $offset_sign = '+';
235 235
             $offset_secs = $timezone_offset;
@@ -256,12 +256,12 @@  discard block
 block discarded – undo
256 256
                 //did they submit a string of a unix timestamp?
257 257
                 if (is_numeric($original_value)) {
258 258
                     $datetime_obj = new \DateTime();
259
-                    $datetime_obj->setTimestamp((int)$original_value);
259
+                    $datetime_obj->setTimestamp((int) $original_value);
260 260
                 } else {
261 261
                     //first, check if its a MySQL timestamp in GMT
262 262
                     $datetime_obj = \DateTime::createFromFormat('Y-m-d H:i:s', $original_value);
263 263
                 }
264
-                if (! $datetime_obj instanceof \DateTime) {
264
+                if ( ! $datetime_obj instanceof \DateTime) {
265 265
                     //so it's not a unix timestamp or a MySQL timestamp. Maybe its in the field's date/time format?
266 266
                     $datetime_obj = $field_obj->prepare_for_set($original_value);
267 267
                 }
@@ -271,7 +271,7 @@  discard block
 block discarded – undo
271 271
                 $new_value = $original_value->format('Y-m-d H:i:s');
272 272
             } elseif (is_int($original_value)) {
273 273
                 $new_value = date('Y-m-d H:i:s', $original_value);
274
-            } elseif($original_value === null || $original_value === '') {
274
+            } elseif ($original_value === null || $original_value === '') {
275 275
                 $new_value = null;
276 276
             } else {
277 277
                 //so it's not a datetime object, unix timestamp (as string or int),
@@ -287,7 +287,7 @@  discard block
 block discarded – undo
287 287
                         $original_value,
288 288
                         $field_obj->get_name(),
289 289
                         $field_obj->get_model_name(),
290
-                        $field_obj->get_time_format() . ' ' . $field_obj->get_time_format()
290
+                        $field_obj->get_time_format().' '.$field_obj->get_time_format()
291 291
                     )
292 292
                 );
293 293
             }
@@ -297,7 +297,7 @@  discard block
 block discarded – undo
297 297
         }
298 298
         //are we about to send an object? just don't. We have no good way to represent it in JSON.
299 299
         // can't just check using is_object() because that missed PHP incomplete objects
300
-        if (! ModelDataTranslator::isRepresentableInJson($new_value)) {
300
+        if ( ! ModelDataTranslator::isRepresentableInJson($new_value)) {
301 301
             $new_value = array(
302 302
                 'error_code' => 'php_object_not_return',
303 303
                 'error_message' => esc_html__('The value of this field in the database is a PHP object, which can\'t be represented in JSON.', 'event_espresso')
@@ -347,7 +347,7 @@  discard block
 block discarded – undo
347 347
                 $model
348 348
             );
349 349
             //double-check is it a *_gmt field?
350
-            if (! $field instanceof EE_Model_Field_Base
350
+            if ( ! $field instanceof EE_Model_Field_Base
351 351
                 && ModelDataTranslator::isGmtDateFieldName($query_param_sans_stars)
352 352
             ) {
353 353
                 //yep, take off '_gmt', and find the field
@@ -366,8 +366,8 @@  discard block
 block discarded – undo
366 366
                 $timezone = $model->get_timezone();
367 367
             }
368 368
             if ($field instanceof EE_Model_Field_Base) {
369
-                if (! $writing && is_array($query_param_value)) {
370
-                    if (! \EEH_Array::is_array_numerically_and_sequentially_indexed($query_param_value)) {
369
+                if ( ! $writing && is_array($query_param_value)) {
370
+                    if ( ! \EEH_Array::is_array_numerically_and_sequentially_indexed($query_param_value)) {
371 371
                         if (defined('EE_REST_API_DEBUG_MODE') && EE_REST_API_DEBUG_MODE) {
372 372
                             throw new RestException(
373 373
                                 'numerically_indexed_array_of_values_only',
@@ -402,7 +402,7 @@  discard block
 block discarded – undo
402 402
                             );
403 403
                         } elseif (array_key_exists($op, $model->valid_between_style_operators())
404 404
                             && isset($query_param_value[1], $query_param_value[2])
405
-                            && !isset($query_param_value[3])
405
+                            && ! isset($query_param_value[3])
406 406
                         ) {
407 407
                             $translated_value[] = ModelDataTranslator::prepareFieldValuesFromJson(
408 408
                                 $field,
@@ -426,10 +426,10 @@  discard block
 block discarded – undo
426 426
                             ModelDataTranslator::throwExceptionIfContainsSerializedData($query_param_value[1]);
427 427
                             $translated_value[] = $query_param_value[1];
428 428
                         } elseif (array_key_exists($op, $model->valid_null_style_operators())
429
-                            && !isset($query_param_value[1])) {
429
+                            && ! isset($query_param_value[1])) {
430 430
                             //no arguments should have been provided, so don't look for any
431 431
                         } elseif (isset($query_param_value[1])
432
-                            && !isset($query_param_value[2])
432
+                            && ! isset($query_param_value[2])
433 433
                             && ! array_key_exists(
434 434
                                 $op,
435 435
                                 array_merge(
@@ -580,7 +580,7 @@  discard block
 block discarded – undo
580 580
      */
581 581
     public static function removeGmtFromFieldName($field_name)
582 582
     {
583
-        if (! ModelDataTranslator::isGmtDateFieldName($field_name)) {
583
+        if ( ! ModelDataTranslator::isGmtDateFieldName($field_name)) {
584 584
             return $field_name;
585 585
         }
586 586
         $query_param_sans_stars = ModelDataTranslator::removeStarsAndAnythingAfterFromConditionQueryParamKey(
Please login to merge, or discard this patch.
core/libraries/form_sections/inputs/EE_Email_Input.input.php 1 patch
Spacing   +8 added lines, -8 removed lines patch added patch discarded remove patch
@@ -6,22 +6,22 @@
 block discarded – undo
6 6
  * @subpackage
7 7
  * @author				Mike Nelson
8 8
  */
9
-class EE_Email_Input extends EE_Form_Input_Base{
9
+class EE_Email_Input extends EE_Form_Input_Base {
10 10
 
11 11
 	/**
12 12
 	 * @param array $input_settings
13 13
 	 */
14
-	public function __construct( $input_settings = array() ){
15
-		$this->_set_display_strategy( new EE_Text_Input_Display_Strategy('email') );
16
-		$this->_set_normalization_strategy( new EE_Text_Normalization() );
14
+	public function __construct($input_settings = array()) {
15
+		$this->_set_display_strategy(new EE_Text_Input_Display_Strategy('email'));
16
+		$this->_set_normalization_strategy(new EE_Text_Normalization());
17 17
 		$this->_add_validation_strategy(
18 18
 			new EE_Email_Validation_Strategy(
19
-				isset( $input_settings[ 'validation_error_message' ] )
20
-					? $input_settings[ 'validation_error_message' ]
19
+				isset($input_settings['validation_error_message'])
20
+					? $input_settings['validation_error_message']
21 21
 					: NULL
22 22
 			)
23 23
 		);
24
-		parent::__construct( $input_settings );
25
-		$this->set_html_class( $this->html_class() . ' email' );
24
+		parent::__construct($input_settings);
25
+		$this->set_html_class($this->html_class().' email');
26 26
 	}
27 27
 }
Please login to merge, or discard this patch.
modules/core_rest_api/EED_Core_Rest_Api.module.php 3 patches
Doc Comments   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -421,7 +421,7 @@
 block discarded – undo
421 421
     /**
422 422
      * Gets the names of all models which should have plural routes (eg `ee/v4.8.36/events`)
423 423
      * in this versioned namespace of EE4
424
-     * @param $version
424
+     * @param string $version
425 425
      * @return array keys are model names (eg 'Event') and values ar either classnames (eg 'EEM_Event')
426 426
      */
427 427
     public static function model_names_with_plural_routes($version){
Please login to merge, or discard this patch.
Indentation   +1221 added lines, -1221 removed lines patch added patch discarded remove patch
@@ -23,1228 +23,1228 @@
 block discarded – undo
23 23
 class EED_Core_Rest_Api extends \EED_Module
24 24
 {
25 25
 
26
-    const ee_api_namespace           = 'ee/v';
26
+	const ee_api_namespace           = 'ee/v';
27 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
-    /**
47
-     * @return EED_Core_Rest_Api|EED_Module
48
-     */
49
-    public static function instance()
50
-    {
51
-        self::$_field_calculator = new CalculatedModelFields();
52
-        return parent::get_instance(__CLASS__);
53
-    }
54
-
55
-
56
-
57
-    /**
58
-     *    set_hooks - for hooking into EE Core, other modules, etc
59
-     *
60
-     * @access    public
61
-     * @return    void
62
-     */
63
-    public static function set_hooks()
64
-    {
65
-        self::set_hooks_both();
66
-    }
67
-
68
-
69
-
70
-    /**
71
-     *    set_hooks_admin - for hooking into EE Admin Core, other modules, etc
72
-     *
73
-     * @access    public
74
-     * @return    void
75
-     */
76
-    public static function set_hooks_admin()
77
-    {
78
-        self::set_hooks_both();
79
-    }
80
-
81
-
82
-
83
-    public static function set_hooks_both()
84
-    {
85
-        add_action('rest_api_init', array('EED_Core_Rest_Api', 'register_routes'), 10);
86
-        add_action('rest_api_init', array('EED_Core_Rest_Api', 'set_hooks_rest_api'), 5);
87
-        add_filter('rest_route_data', array('EED_Core_Rest_Api', 'hide_old_endpoints'), 10, 2);
88
-        add_filter('rest_index',
89
-            array('EventEspresso\core\libraries\rest_api\controllers\model\Meta', 'filterEeMetadataIntoIndex'));
90
-        EED_Core_Rest_Api::invalidate_cached_route_data_on_version_change();
91
-    }
92
-
93
-
94
-
95
-    /**
96
-     * sets up hooks which only need to be included as part of REST API requests;
97
-     * other requests like to the frontend or admin etc don't need them
98
-     *
99
-     * @throws \EE_Error
100
-     */
101
-    public static function set_hooks_rest_api()
102
-    {
103
-        //set hooks which account for changes made to the API
104
-        EED_Core_Rest_Api::_set_hooks_for_changes();
105
-    }
106
-
107
-
108
-
109
-    /**
110
-     * public wrapper of _set_hooks_for_changes.
111
-     * Loads all the hooks which make requests to old versions of the API
112
-     * appear the same as they always did
113
-     *
114
-     * @throws EE_Error
115
-     */
116
-    public static function set_hooks_for_changes()
117
-    {
118
-        self::_set_hooks_for_changes();
119
-    }
120
-
121
-
122
-
123
-    /**
124
-     * Loads all the hooks which make requests to old versions of the API
125
-     * appear the same as they always did
126
-     *
127
-     * @throws EE_Error
128
-     */
129
-    protected static function _set_hooks_for_changes()
130
-    {
131
-        $folder_contents = EEH_File::get_contents_of_folders(array(EE_LIBRARIES . 'rest_api' . DS . 'changes'), false);
132
-        foreach ($folder_contents as $classname_in_namespace => $filepath) {
133
-            //ignore the base parent class
134
-            //and legacy named classes
135
-            if ($classname_in_namespace === 'ChangesInBase'
136
-                || strpos($classname_in_namespace, 'Changes_In_') === 0
137
-            ) {
138
-                continue;
139
-            }
140
-            $full_classname = 'EventEspresso\core\libraries\rest_api\changes\\' . $classname_in_namespace;
141
-            if (class_exists($full_classname)) {
142
-                $instance_of_class = new $full_classname;
143
-                if ($instance_of_class instanceof ChangesInBase) {
144
-                    $instance_of_class->setHooks();
145
-                }
146
-            }
147
-        }
148
-    }
149
-
150
-
151
-
152
-    /**
153
-     * Filters the WP routes to add our EE-related ones. This takes a bit of time
154
-     * so we actually prefer to only do it when an EE plugin is activated or upgraded
155
-     *
156
-     * @throws \EE_Error
157
-     */
158
-    public static function register_routes()
159
-    {
160
-        foreach (EED_Core_Rest_Api::get_ee_route_data() as $namespace => $relative_routes) {
161
-            foreach ($relative_routes as $relative_route => $data_for_multiple_endpoints) {
162
-                /**
163
-                 * @var array $data_for_multiple_endpoints numerically indexed array
164
-                 *                                         but can also contain route options like {
165
-                 * @type array    $schema                      {
166
-                 * @type callable $schema_callback
167
-                 * @type array    $callback_args               arguments that will be passed to the callback, after the
168
-                 * WP_REST_Request of course
169
-                 * }
170
-                 * }
171
-                 */
172
-                //when registering routes, register all the endpoints' data at the same time
173
-                $multiple_endpoint_args = array();
174
-                foreach ($data_for_multiple_endpoints as $endpoint_key => $data_for_single_endpoint) {
175
-                    /**
176
-                     * @var array     $data_for_single_endpoint {
177
-                     * @type callable $callback
178
-                     * @type string methods
179
-                     * @type array args
180
-                     * @type array _links
181
-                     * @type array    $callback_args            arguments that will be passed to the callback, after the
182
-                     * WP_REST_Request of course
183
-                     * }
184
-                     */
185
-                    //skip route options
186
-                    if (! is_numeric($endpoint_key)) {
187
-                        continue;
188
-                    }
189
-                    if (! isset($data_for_single_endpoint['callback'], $data_for_single_endpoint['methods'])) {
190
-                        throw new EE_Error(
191
-                            esc_html__(
192
-                                // @codingStandardsIgnoreStart
193
-                                'Endpoint configuration data needs to have entries "callback" (callable) and "methods" (comma-separated list of accepts HTTP methods).',
194
-                                // @codingStandardsIgnoreEnd
195
-                                'event_espresso')
196
-                        );
197
-                    }
198
-                    $callback = $data_for_single_endpoint['callback'];
199
-                    $single_endpoint_args = array(
200
-                        'methods' => $data_for_single_endpoint['methods'],
201
-                        'args'    => isset($data_for_single_endpoint['args']) ? $data_for_single_endpoint['args']
202
-                            : array(),
203
-                    );
204
-                    if (isset($data_for_single_endpoint['_links'])) {
205
-                        $single_endpoint_args['_links'] = $data_for_single_endpoint['_links'];
206
-                    }
207
-                    if (isset($data_for_single_endpoint['callback_args'])) {
208
-                        $callback_args = $data_for_single_endpoint['callback_args'];
209
-                        $single_endpoint_args['callback'] = function (\WP_REST_Request $request) use (
210
-                            $callback,
211
-                            $callback_args
212
-                        ) {
213
-                            array_unshift($callback_args, $request);
214
-                            return call_user_func_array(
215
-                                $callback,
216
-                                $callback_args
217
-                            );
218
-                        };
219
-                    } else {
220
-                        $single_endpoint_args['callback'] = $data_for_single_endpoint['callback'];
221
-                    }
222
-                    $multiple_endpoint_args[] = $single_endpoint_args;
223
-                }
224
-                if (isset($data_for_multiple_endpoints['schema'])) {
225
-                    $schema_route_data = $data_for_multiple_endpoints['schema'];
226
-                    $schema_callback = $schema_route_data['schema_callback'];
227
-                    $callback_args = $schema_route_data['callback_args'];
228
-                    $multiple_endpoint_args['schema'] = function () use ($schema_callback, $callback_args) {
229
-                        return call_user_func_array(
230
-                            $schema_callback,
231
-                            $callback_args
232
-                        );
233
-                    };
234
-                }
235
-                register_rest_route(
236
-                    $namespace,
237
-                    $relative_route,
238
-                    $multiple_endpoint_args
239
-                );
240
-            }
241
-        }
242
-    }
243
-
244
-
245
-
246
-    /**
247
-     * Checks if there was a version change or something that merits invalidating the cached
248
-     * route data. If so, invalidates the cached route data so that it gets refreshed
249
-     * next time the WP API is used
250
-     */
251
-    public static function invalidate_cached_route_data_on_version_change()
252
-    {
253
-        if (EE_System::instance()->detect_req_type() !== EE_System::req_type_normal) {
254
-            EED_Core_Rest_Api::invalidate_cached_route_data();
255
-        }
256
-        foreach (EE_Registry::instance()->addons as $addon) {
257
-            if ($addon instanceof EE_Addon && $addon->detect_req_type() !== EE_System::req_type_normal) {
258
-                EED_Core_Rest_Api::invalidate_cached_route_data();
259
-            }
260
-        }
261
-    }
262
-
263
-
264
-
265
-    /**
266
-     * Removes the cached route data so it will get refreshed next time the WP API is used
267
-     */
268
-    public static function invalidate_cached_route_data()
269
-    {
270
-        //delete the saved EE REST API routes
271
-        foreach (EED_Core_Rest_Api::versions_served() as $version => $hidden) {
272
-            delete_option(EED_Core_Rest_Api::saved_routes_option_names . $version);
273
-        }
274
-    }
275
-
276
-
277
-
278
-    /**
279
-     * Gets the EE route data
280
-     *
281
-     * @return array top-level key is the namespace, next-level key is the route and its value is array{
282
-     * @throws \EE_Error
283
-     * @type string|array $callback
284
-     * @type string       $methods
285
-     * @type boolean      $hidden_endpoint
286
-     * }
287
-     */
288
-    public static function get_ee_route_data()
289
-    {
290
-        $ee_routes = array();
291
-        foreach (self::versions_served() as $version => $hidden_endpoints) {
292
-            $ee_routes[self::ee_api_namespace . $version] = self::_get_ee_route_data_for_version(
293
-                $version,
294
-                $hidden_endpoints
295
-            );
296
-        }
297
-        return $ee_routes;
298
-    }
299
-
300
-
301
-
302
-    /**
303
-     * Gets the EE route data from the wp options if it exists already,
304
-     * otherwise re-generates it and saves it to the option
305
-     *
306
-     * @param string  $version
307
-     * @param boolean $hidden_endpoints
308
-     * @return array
309
-     * @throws \EE_Error
310
-     */
311
-    protected static function _get_ee_route_data_for_version($version, $hidden_endpoints = false)
312
-    {
313
-        $ee_routes = get_option(self::saved_routes_option_names . $version, null);
314
-        if (! $ee_routes || (defined('EE_REST_API_DEBUG_MODE') && EE_REST_API_DEBUG_MODE)) {
315
-            $ee_routes = self::_save_ee_route_data_for_version($version, $hidden_endpoints);
316
-        }
317
-        return $ee_routes;
318
-    }
319
-
320
-
321
-
322
-    /**
323
-     * Saves the EE REST API route data to a wp option and returns it
324
-     *
325
-     * @param string  $version
326
-     * @param boolean $hidden_endpoints
327
-     * @return mixed|null
328
-     * @throws \EE_Error
329
-     */
330
-    protected static function _save_ee_route_data_for_version($version, $hidden_endpoints = false)
331
-    {
332
-        $instance = self::instance();
333
-        $routes = apply_filters(
334
-            'EED_Core_Rest_Api__save_ee_route_data_for_version__routes',
335
-            array_replace_recursive(
336
-                $instance->_get_config_route_data_for_version($version, $hidden_endpoints),
337
-                $instance->_get_meta_route_data_for_version($version, $hidden_endpoints),
338
-                $instance->_get_model_route_data_for_version($version, $hidden_endpoints),
339
-                $instance->_get_rpc_route_data_for_version($version, $hidden_endpoints)
340
-            )
341
-        );
342
-        $option_name = self::saved_routes_option_names . $version;
343
-        if (get_option($option_name)) {
344
-            update_option($option_name, $routes, true);
345
-        } else {
346
-            add_option($option_name, $routes, null, 'no');
347
-        }
348
-        return $routes;
349
-    }
350
-
351
-
352
-
353
-    /**
354
-     * Calculates all the EE routes and saves it to a WordPress option so we don't
355
-     * need to calculate it on every request
356
-     *
357
-     * @deprecated since version 4.9.1
358
-     * @return void
359
-     */
360
-    public static function save_ee_routes()
361
-    {
362
-        if (EE_Maintenance_Mode::instance()->models_can_query()) {
363
-            $instance = self::instance();
364
-            $routes = apply_filters(
365
-                'EED_Core_Rest_Api__save_ee_routes__routes',
366
-                array_replace_recursive(
367
-                    $instance->_register_config_routes(),
368
-                    $instance->_register_meta_routes(),
369
-                    $instance->_register_model_routes(),
370
-                    $instance->_register_rpc_routes()
371
-                )
372
-            );
373
-            update_option(self::saved_routes_option_names, $routes, true);
374
-        }
375
-    }
376
-
377
-
378
-
379
-    /**
380
-     * Gets all the route information relating to EE models
381
-     *
382
-     * @return array @see get_ee_route_data
383
-     * @deprecated since version 4.9.1
384
-     */
385
-    protected function _register_model_routes()
386
-    {
387
-        $model_routes = array();
388
-        foreach (self::versions_served() as $version => $hidden_endpoint) {
389
-            $model_routes[EED_Core_Rest_Api::ee_api_namespace
390
-                          . $version] = $this->_get_config_route_data_for_version($version, $hidden_endpoint);
391
-        }
392
-        return $model_routes;
393
-    }
394
-
395
-
396
-
397
-    /**
398
-     * Decides whether or not to add write endpoints for this model.
399
-     *
400
-     * Currently, this defaults to exclude all global tables and models
401
-     * which would allow inserting WP core data (we don't want to duplicate
402
-     * what WP API does, as it's unnecessary, extra work, and potentially extra bugs)
403
-     * @param EEM_Base $model
404
-     * @return bool
405
-     */
406
-    public static function should_have_write_endpoints(EEM_Base $model)
407
-    {
408
-        if ($model->is_wp_core_model()){
409
-            return false;
410
-        }
411
-        foreach($model->get_tables() as $table){
412
-            if( $table->is_global()){
413
-                return false;
414
-            }
415
-        }
416
-        return true;
417
-    }
418
-
419
-
420
-
421
-    /**
422
-     * Gets the names of all models which should have plural routes (eg `ee/v4.8.36/events`)
423
-     * in this versioned namespace of EE4
424
-     * @param $version
425
-     * @return array keys are model names (eg 'Event') and values ar either classnames (eg 'EEM_Event')
426
-     */
427
-    public static function model_names_with_plural_routes($version){
428
-        $model_version_info = new ModelVersionInfo($version);
429
-        $models_to_register = $model_version_info->modelsForRequestedVersion();
430
-        //let's not bother having endpoints for extra metas
431
-        unset(
432
-            $models_to_register['Extra_Meta'],
433
-            $models_to_register['Extra_Join'],
434
-            $models_to_register['Post_Meta']
435
-        );
436
-        return apply_filters(
437
-            'FHEE__EED_Core_REST_API___register_model_routes',
438
-            $models_to_register
439
-        );
440
-    }
441
-
442
-
443
-
444
-    /**
445
-     * Gets the route data for EE models in the specified version
446
-     *
447
-     * @param string  $version
448
-     * @param boolean $hidden_endpoint
449
-     * @return array
450
-     * @throws EE_Error
451
-     */
452
-    protected function _get_model_route_data_for_version($version, $hidden_endpoint = false)
453
-    {
454
-        $model_routes = array();
455
-        $model_version_info = new ModelVersionInfo($version);
456
-        foreach (EED_Core_Rest_Api::model_names_with_plural_routes($version) as $model_name => $model_classname) {
457
-            $model = \EE_Registry::instance()->load_model($model_name);
458
-            //if this isn't a valid model then let's skip iterate to the next item in the loop.
459
-            if (! $model instanceof EEM_Base) {
460
-                continue;
461
-            }
462
-            //yes we could just register one route for ALL models, but then they wouldn't show up in the index
463
-            $plural_model_route = EED_Core_Rest_Api::get_collection_route($model);
464
-            $singular_model_route = EED_Core_Rest_Api::get_entity_route($model, '(?P<id>[^\/]+)');
465
-            $model_routes[$plural_model_route] = array(
466
-                array(
467
-                    'callback'        => array(
468
-                        'EventEspresso\core\libraries\rest_api\controllers\model\Read',
469
-                        'handleRequestGetAll',
470
-                    ),
471
-                    'callback_args'   => array($version, $model_name),
472
-                    'methods'         => WP_REST_Server::READABLE,
473
-                    'hidden_endpoint' => $hidden_endpoint,
474
-                    'args'            => $this->_get_read_query_params($model, $version),
475
-                    '_links'          => array(
476
-                        'self' => rest_url(EED_Core_Rest_Api::ee_api_namespace . $version . $singular_model_route),
477
-                    ),
478
-                ),
479
-                'schema' => array(
480
-                    'schema_callback' => array(
481
-                        'EventEspresso\core\libraries\rest_api\controllers\model\Read',
482
-                        'handleSchemaRequest',
483
-                    ),
484
-                    'callback_args'   => array($version, $model_name),
485
-                ),
486
-            );
487
-            $model_routes[$singular_model_route] = array(
488
-                array(
489
-                    'callback'        => array(
490
-                        'EventEspresso\core\libraries\rest_api\controllers\model\Read',
491
-                        'handleRequestGetOne',
492
-                    ),
493
-                    'callback_args'   => array($version, $model_name),
494
-                    'methods'         => WP_REST_Server::READABLE,
495
-                    'hidden_endpoint' => $hidden_endpoint,
496
-                    'args'            => $this->_get_response_selection_query_params($model, $version),
497
-                ),
498
-            );
499
-            if( apply_filters(
500
-                'FHEE__EED_Core_Rest_Api___get_model_route_data_for_version__add_write_endpoints',
501
-                EED_Core_Rest_Api::should_have_write_endpoints($model),
502
-                $model
503
-            )){
504
-                $model_routes[$plural_model_route][] = array(
505
-                    'callback'        => array(
506
-                        'EventEspresso\core\libraries\rest_api\controllers\model\Write',
507
-                        'handleRequestInsert',
508
-                    ),
509
-                    'callback_args'   => array($version, $model_name),
510
-                    'methods'         => WP_REST_Server::CREATABLE,
511
-                    'hidden_endpoint' => $hidden_endpoint,
512
-                    'args'            => $this->_get_write_params($model_name, $model_version_info, true),
513
-                );
514
-                $model_routes[$singular_model_route] = array_merge(
515
-                    $model_routes[$singular_model_route],
516
-                    array(
517
-                        array(
518
-                            'callback'        => array(
519
-                                'EventEspresso\core\libraries\rest_api\controllers\model\Write',
520
-                                'handleRequestUpdate',
521
-                            ),
522
-                            'callback_args'   => array($version, $model_name),
523
-                            'methods'         => WP_REST_Server::EDITABLE,
524
-                            'hidden_endpoint' => $hidden_endpoint,
525
-                            'args'            => $this->_get_write_params($model_name, $model_version_info),
526
-                        ),
527
-                        array(
528
-                            'callback'        => array(
529
-                                'EventEspresso\core\libraries\rest_api\controllers\model\Write',
530
-                                'handleRequestDelete',
531
-                            ),
532
-                            'callback_args'   => array($version, $model_name),
533
-                            'methods'         => WP_REST_Server::DELETABLE,
534
-                            'hidden_endpoint' => $hidden_endpoint,
535
-                            'args'            => $this->_get_delete_query_params($model, $version),
536
-                        )
537
-                    )
538
-                );
539
-            }
540
-            foreach ($model->relation_settings() as $relation_name => $relation_obj) {
541
-
542
-                $related_route = EED_Core_Rest_Api::get_relation_route_via(
543
-                    $model,
544
-                    '(?P<id>[^\/]+)',
545
-                    $relation_obj
546
-                );
547
-                $endpoints = array(
548
-                    array(
549
-                        'callback'        => array(
550
-                            'EventEspresso\core\libraries\rest_api\controllers\model\Read',
551
-                            'handleRequestGetRelated',
552
-                        ),
553
-                        'callback_args'   => array($version, $model_name, $relation_name),
554
-                        'methods'         => WP_REST_Server::READABLE,
555
-                        'hidden_endpoint' => $hidden_endpoint,
556
-                        'args'            => $this->_get_read_query_params($relation_obj->get_other_model(), $version),
557
-                    ),
558
-                );
559
-                $model_routes[$related_route] = $endpoints;
560
-            }
561
-        }
562
-        return $model_routes;
563
-    }
564
-
565
-
566
-
567
-    /**
568
-     * Gets the relative URI to a model's REST API plural route, after the EE4 versioned namespace,
569
-     * excluding the preceding slash.
570
-     * Eg you pass get_plural_route_to('Event') = 'events'
571
-     *
572
-     * @param EEM_Base $model
573
-     * @return string
574
-     */
575
-    public static function get_collection_route(EEM_Base $model)
576
-    {
577
-        return EEH_Inflector::pluralize_and_lower($model->get_this_model_name());
578
-    }
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
-     * @return string
590
-     */
591
-    public static function get_entity_route($model, $id)
592
-    {
593
-        return EED_Core_Rest_Api::get_collection_route($model). '/' . $id;
594
-    }
595
-
596
-
597
-    /**
598
-     * Gets the relative URI to a model's REST API singular route, after the EE4 versioned namespace,
599
-     * excluding the preceding slash.
600
-     * Eg you pass get_plural_route_to('Event', 12) = 'events/12'
601
-     *
602
-     * @param EEM_Base                 $model eg Event or Venue
603
-     * @param string                 $id
604
-     * @param EE_Model_Relation_Base $relation_obj
605
-     * @return string
606
-     */
607
-    public static function get_relation_route_via(EEM_Base $model, $id, EE_Model_Relation_Base $relation_obj)
608
-    {
609
-        $related_model_name_endpoint_part = ModelRead::getRelatedEntityName(
610
-            $relation_obj->get_other_model()->get_this_model_name(),
611
-            $relation_obj
612
-        );
613
-        return EED_Core_Rest_Api::get_entity_route($model, $id) . '/' . $related_model_name_endpoint_part;
614
-    }
615
-
616
-
617
-
618
-    /**
619
-     * Adds onto the $relative_route the EE4 REST API versioned namespace.
620
-     * Eg if given '4.8.36' and 'events', will return 'ee/v4.8.36/events'
621
-     * @param string $relative_route
622
-     * @param string $version
623
-     * @return string
624
-     */
625
-    public static function get_versioned_route_to($relative_route, $version = '4.8.36'){
626
-        return '/' . EED_Core_Rest_Api::ee_api_namespace . $version . '/' . $relative_route;
627
-    }
628
-
629
-
630
-
631
-    /**
632
-     * Adds all the RPC-style routes (remote procedure call-like routes, ie
633
-     * routes that don't conform to the traditional REST CRUD-style).
634
-     *
635
-     * @deprecated since 4.9.1
636
-     */
637
-    protected function _register_rpc_routes()
638
-    {
639
-        $routes = array();
640
-        foreach (self::versions_served() as $version => $hidden_endpoint) {
641
-            $routes[self::ee_api_namespace . $version] = $this->_get_rpc_route_data_for_version(
642
-                $version,
643
-                $hidden_endpoint
644
-            );
645
-        }
646
-        return $routes;
647
-    }
648
-
649
-
650
-
651
-    /**
652
-     * @param string  $version
653
-     * @param boolean $hidden_endpoint
654
-     * @return array
655
-     */
656
-    protected function _get_rpc_route_data_for_version($version, $hidden_endpoint = false)
657
-    {
658
-        $this_versions_routes = array();
659
-        //checkin endpoint
660
-        $this_versions_routes['registrations/(?P<REG_ID>\d+)/toggle_checkin_for_datetime/(?P<DTT_ID>\d+)'] = array(
661
-            array(
662
-                'callback'        => array(
663
-                    'EventEspresso\core\libraries\rest_api\controllers\rpc\Checkin',
664
-                    'handleRequestToggleCheckin',
665
-                ),
666
-                'methods'         => WP_REST_Server::CREATABLE,
667
-                'hidden_endpoint' => $hidden_endpoint,
668
-                'args'            => array(
669
-                    'force' => array(
670
-                        'required'    => false,
671
-                        'default'     => false,
672
-                        'description' => __(
673
-                            // @codingStandardsIgnoreStart
674
-                            'Whether to force toggle checkin, or to verify the registration status and allowed ticket uses',
675
-                            // @codingStandardsIgnoreEnd
676
-                            'event_espresso'
677
-                        ),
678
-                    ),
679
-                ),
680
-                'callback_args'   => array($version),
681
-            ),
682
-        );
683
-        return apply_filters(
684
-            'FHEE__EED_Core_Rest_Api___register_rpc_routes__this_versions_routes',
685
-            $this_versions_routes,
686
-            $version,
687
-            $hidden_endpoint
688
-        );
689
-    }
690
-
691
-
692
-
693
-    /**
694
-     * Gets the query params that can be used when request one or many
695
-     *
696
-     * @param EEM_Base $model
697
-     * @param string   $version
698
-     * @return array
699
-     */
700
-    protected function _get_response_selection_query_params(\EEM_Base $model, $version)
701
-    {
702
-        return apply_filters(
703
-            'FHEE__EED_Core_Rest_Api___get_response_selection_query_params',
704
-            array(
705
-                'include'   => array(
706
-                    'required' => false,
707
-                    'default'  => '*',
708
-                    'type'     => 'string',
709
-                ),
710
-                'calculate' => array(
711
-                    'required'          => false,
712
-                    'default'           => '',
713
-                    'enum'              => self::$_field_calculator->retrieveCalculatedFieldsForModel($model),
714
-                    'type'              => 'string',
715
-                    //because we accept a CSV'd list of the enumerated strings, WP core validation and sanitization
716
-                    //freaks out. We'll just validate this argument while handling the request
717
-                    'validate_callback' => null,
718
-                    'sanitize_callback' => null,
719
-                ),
720
-            ),
721
-            $model,
722
-            $version
723
-        );
724
-    }
725
-
726
-
727
-
728
-    /**
729
-     * Gets the parameters acceptable for delete requests
730
-     *
731
-     * @param \EEM_Base $model
732
-     * @param string    $version
733
-     * @return array
734
-     */
735
-    protected function _get_delete_query_params(\EEM_Base $model, $version)
736
-    {
737
-        $params_for_delete = array(
738
-            'allow_blocking' => array(
739
-                'required' => false,
740
-                'default'  => true,
741
-                'type'     => 'boolean',
742
-            ),
743
-        );
744
-        $params_for_delete['force'] = array(
745
-            'required' => false,
746
-            'default'  => false,
747
-            'type'     => 'boolean',
748
-        );
749
-        return apply_filters(
750
-            'FHEE__EED_Core_Rest_Api___get_delete_query_params',
751
-            $params_for_delete,
752
-            $model,
753
-            $version
754
-        );
755
-    }
756
-
757
-
758
-
759
-    /**
760
-     * Gets info about reading query params that are acceptable
761
-     *
762
-     * @param \EEM_Base $model eg 'Event' or 'Venue'
763
-     * @param  string   $version
764
-     * @return array    describing the args acceptable when querying this model
765
-     * @throws EE_Error
766
-     */
767
-    protected function _get_read_query_params(\EEM_Base $model, $version)
768
-    {
769
-        $default_orderby = array();
770
-        foreach ($model->get_combined_primary_key_fields() as $key_field) {
771
-            $default_orderby[$key_field->get_name()] = 'ASC';
772
-        }
773
-        return array_merge(
774
-            $this->_get_response_selection_query_params($model, $version),
775
-            array(
776
-                'where'    => array(
777
-                    'required' => false,
778
-                    'default'  => array(),
779
-                    'type'     => 'object',
780
-                ),
781
-                'limit'    => array(
782
-                    'required' => false,
783
-                    'default'  => EED_Core_Rest_Api::get_default_query_limit(),
784
-                    'type'     => array(
785
-                        'object',
786
-                        'string',
787
-                        'integer',
788
-                    ),
789
-                ),
790
-                'order_by' => array(
791
-                    'required' => false,
792
-                    'default'  => $default_orderby,
793
-                    'type'     => array(
794
-                        'object',
795
-                        'string',
796
-                    ),
797
-                ),
798
-                'group_by' => array(
799
-                    'required' => false,
800
-                    'default'  => null,
801
-                    'type'     => array(
802
-                        'object',
803
-                        'string',
804
-                    ),
805
-                ),
806
-                'having'   => array(
807
-                    'required' => false,
808
-                    'default'  => null,
809
-                    'type'     => 'object',
810
-                ),
811
-                'caps'     => array(
812
-                    'required' => false,
813
-                    'default'  => EEM_Base::caps_read,
814
-                    'type'     => 'string',
815
-                ),
816
-            )
817
-        );
818
-    }
819
-
820
-
821
-
822
-    /**
823
-     * Gets parameter information for a model regarding writing data
824
-     *
825
-     * @param string           $model_name
826
-     * @param ModelVersionInfo $model_version_info
827
-     * @param boolean          $create                                       whether this is for request to create (in which case we need
828
-     *                                                                       all required params) or just to update (in which case we don't need those on every request)
829
-     * @return array
830
-     */
831
-    protected function _get_write_params(
832
-        $model_name,
833
-        ModelVersionInfo $model_version_info,
834
-        $create = false
835
-    ) {
836
-        $model = EE_Registry::instance()->load_model($model_name);
837
-        $fields = $model_version_info->fieldsOnModelInThisVersion($model);
838
-        $args_info = array();
839
-        foreach ($fields as $field_name => $field_obj) {
840
-            if ($field_obj->is_auto_increment()) {
841
-                //totally ignore auto increment IDs
842
-                continue;
843
-            }
844
-            $arg_info = $field_obj->getSchema();
845
-            $required = $create && ! $field_obj->is_nullable() && $field_obj->get_default_value() === null;
846
-            $arg_info['required'] = $required;
847
-            //remove the read-only flag. If it were read-only we wouldn't list it as an argument while writing, right?
848
-            unset($arg_info['readonly']);
849
-            $schema_properties = $field_obj->getSchemaProperties();
850
-            if (
851
-                isset($schema_properties['raw'])
852
-                && $field_obj->getSchemaType() === 'object'
853
-            ) {
854
-                //if there's a "raw" form of this argument, use those properties instead
855
-                $arg_info = array_replace(
856
-                    $arg_info,
857
-                    $schema_properties['raw']
858
-                );
859
-            }
860
-            $arg_info['default'] = ModelDataTranslator::prepareFieldValueForJson(
861
-                $field_obj,
862
-                $field_obj->get_default_value(),
863
-                $model_version_info->requestedVersion()
864
-            );
865
-            //we do our own validation and sanitization within the controller
866
-            $arg_info['sanitize_callback'] =
867
-                array(
868
-                    'EED_Core_Rest_Api',
869
-                    'default_sanitize_callback',
870
-                );
871
-            $args_info[$field_name] = $arg_info;
872
-            if ($field_obj instanceof EE_Datetime_Field) {
873
-                $gmt_arg_info = $arg_info;
874
-                $gmt_arg_info['description'] = sprintf(
875
-                    esc_html__(
876
-                        '%1$s - the value for this field in UTC. Ignored if %2$s is provided.',
877
-                        'event_espresso'
878
-                    ),
879
-                    $field_obj->get_nicename(),
880
-                    $field_name
881
-                );
882
-                $args_info[$field_name . '_gmt'] = $gmt_arg_info;
883
-            }
884
-        }
885
-        return $args_info;
886
-    }
887
-
888
-
889
-
890
-    /**
891
-     * Replacement for WP API's 'rest_parse_request_arg'.
892
-     * If the value is blank but not required, don't bother validating it.
893
-     * Also, it uses our email validation instead of WP API's default.
894
-     *
895
-     * @param                 $value
896
-     * @param WP_REST_Request $request
897
-     * @param                 $param
898
-     * @return bool|true|WP_Error
899
-     * @throws InvalidArgumentException
900
-     * @throws InvalidInterfaceException
901
-     * @throws InvalidDataTypeException
902
-     */
903
-    public static function default_sanitize_callback( $value, WP_REST_Request $request, $param)
904
-    {
905
-        $attributes = $request->get_attributes();
906
-        if (! isset($attributes['args'][$param])
907
-            || ! is_array($attributes['args'][$param])) {
908
-            $validation_result = true;
909
-        } else {
910
-            $args = $attributes['args'][$param];
911
-            if ((
912
-                    $value === ''
913
-                    || $value === null
914
-                )
915
-                && (! isset($args['required'])
916
-                    || $args['required'] === false
917
-                )
918
-            ) {
919
-                //not required and not provided? that's cool
920
-                $validation_result = true;
921
-            } elseif (isset($args['format'])
922
-                && $args['format'] === 'email'
923
-            ) {
924
-                $validation_result = true;
925
-                if (! self::_validate_email($value)) {
926
-                    $validation_result = new WP_Error(
927
-                        'rest_invalid_param',
928
-                        esc_html__(
929
-                            'The email address is not valid or does not exist.',
930
-                            'event_espresso'
931
-                        )
932
-                    );
933
-                }
934
-            } else {
935
-                $validation_result = rest_validate_value_from_schema($value, $args, $param);
936
-            }
937
-        }
938
-        if (is_wp_error($validation_result)) {
939
-            return $validation_result;
940
-        }
941
-        return rest_sanitize_request_arg($value, $request, $param);
942
-    }
943
-
944
-
945
-
946
-    /**
947
-     * Returns whether or not this email address is valid. Copied from EE_Email_Validation_Strategy::_validate_email()
948
-     *
949
-     * @param $email
950
-     * @return bool
951
-     * @throws InvalidArgumentException
952
-     * @throws InvalidInterfaceException
953
-     * @throws InvalidDataTypeException
954
-     */
955
-    protected static function _validate_email($email){
956
-        try {
957
-            EmailAddressFactory::create($email);
958
-            return true;
959
-        } catch (EmailValidationException $e) {
960
-            return false;
961
-        }
962
-    }
963
-
964
-
965
-
966
-    /**
967
-     * Gets routes for the config
968
-     *
969
-     * @return array @see _register_model_routes
970
-     * @deprecated since version 4.9.1
971
-     */
972
-    protected function _register_config_routes()
973
-    {
974
-        $config_routes = array();
975
-        foreach (self::versions_served() as $version => $hidden_endpoint) {
976
-            $config_routes[self::ee_api_namespace . $version] = $this->_get_config_route_data_for_version(
977
-                $version,
978
-                $hidden_endpoint
979
-            );
980
-        }
981
-        return $config_routes;
982
-    }
983
-
984
-
985
-
986
-    /**
987
-     * Gets routes for the config for the specified version
988
-     *
989
-     * @param string  $version
990
-     * @param boolean $hidden_endpoint
991
-     * @return array
992
-     */
993
-    protected function _get_config_route_data_for_version($version, $hidden_endpoint)
994
-    {
995
-        return array(
996
-            'config'    => array(
997
-                array(
998
-                    'callback'        => array(
999
-                        'EventEspresso\core\libraries\rest_api\controllers\config\Read',
1000
-                        'handleRequest',
1001
-                    ),
1002
-                    'methods'         => WP_REST_Server::READABLE,
1003
-                    'hidden_endpoint' => $hidden_endpoint,
1004
-                    'callback_args'   => array($version),
1005
-                ),
1006
-            ),
1007
-            'site_info' => array(
1008
-                array(
1009
-                    'callback'        => array(
1010
-                        'EventEspresso\core\libraries\rest_api\controllers\config\Read',
1011
-                        'handleRequestSiteInfo',
1012
-                    ),
1013
-                    'methods'         => WP_REST_Server::READABLE,
1014
-                    'hidden_endpoint' => $hidden_endpoint,
1015
-                    'callback_args'   => array($version),
1016
-                ),
1017
-            ),
1018
-        );
1019
-    }
1020
-
1021
-
1022
-
1023
-    /**
1024
-     * Gets the meta info routes
1025
-     *
1026
-     * @return array @see _register_model_routes
1027
-     * @deprecated since version 4.9.1
1028
-     */
1029
-    protected function _register_meta_routes()
1030
-    {
1031
-        $meta_routes = array();
1032
-        foreach (self::versions_served() as $version => $hidden_endpoint) {
1033
-            $meta_routes[self::ee_api_namespace . $version] = $this->_get_meta_route_data_for_version(
1034
-                $version,
1035
-                $hidden_endpoint
1036
-            );
1037
-        }
1038
-        return $meta_routes;
1039
-    }
1040
-
1041
-
1042
-
1043
-    /**
1044
-     * @param string  $version
1045
-     * @param boolean $hidden_endpoint
1046
-     * @return array
1047
-     */
1048
-    protected function _get_meta_route_data_for_version($version, $hidden_endpoint = false)
1049
-    {
1050
-        return array(
1051
-            'resources' => array(
1052
-                array(
1053
-                    'callback'        => array(
1054
-                        'EventEspresso\core\libraries\rest_api\controllers\model\Meta',
1055
-                        'handleRequestModelsMeta',
1056
-                    ),
1057
-                    'methods'         => WP_REST_Server::READABLE,
1058
-                    'hidden_endpoint' => $hidden_endpoint,
1059
-                    'callback_args'   => array($version),
1060
-                ),
1061
-            ),
1062
-        );
1063
-    }
1064
-
1065
-
1066
-
1067
-    /**
1068
-     * Tries to hide old 4.6 endpoints from the
1069
-     *
1070
-     * @param array $route_data
1071
-     * @return array
1072
-     * @throws \EE_Error
1073
-     */
1074
-    public static function hide_old_endpoints($route_data)
1075
-    {
1076
-        //allow API clients to override which endpoints get hidden, in case
1077
-        //they want to discover particular endpoints
1078
-        //also, we don't have access to the request so we have to just grab it from the superglobal
1079
-        $force_show_ee_namespace = ltrim(
1080
-            EEH_Array::is_set($_REQUEST, 'force_show_ee_namespace', ''),
1081
-            '/'
1082
-        );
1083
-        foreach (EED_Core_Rest_Api::get_ee_route_data() as $namespace => $relative_urls) {
1084
-            foreach ($relative_urls as $resource_name => $endpoints) {
1085
-                foreach ($endpoints as $key => $endpoint) {
1086
-                    //skip schema and other route options
1087
-                    if (! is_numeric($key)) {
1088
-                        continue;
1089
-                    }
1090
-                    //by default, hide "hidden_endpoint"s, unless the request indicates
1091
-                    //to $force_show_ee_namespace, in which case only show that one
1092
-                    //namespace's endpoints (and hide all others)
1093
-                    if (
1094
-                        ($force_show_ee_namespace !== '' && $force_show_ee_namespace !== $namespace)
1095
-                        || ($endpoint['hidden_endpoint'] && $force_show_ee_namespace === '')
1096
-                    ) {
1097
-                        $full_route = '/' . ltrim($namespace, '/');
1098
-                        $full_route .= '/' . ltrim($resource_name, '/');
1099
-                        unset($route_data[$full_route]);
1100
-                    }
1101
-                }
1102
-            }
1103
-        }
1104
-        return $route_data;
1105
-    }
1106
-
1107
-
1108
-
1109
-    /**
1110
-     * Returns an array describing which versions of core support serving requests for.
1111
-     * Keys are core versions' major and minor version, and values are the
1112
-     * LOWEST requested version they can serve. Eg, 4.7 can serve requests for 4.6-like
1113
-     * data by just removing a few models and fields from the responses. However, 4.15 might remove
1114
-     * the answers table entirely, in which case it would be very difficult for
1115
-     * it to serve 4.6-style responses.
1116
-     * Versions of core that are missing from this array are unknowns.
1117
-     * previous ver
1118
-     *
1119
-     * @return array
1120
-     */
1121
-    public static function version_compatibilities()
1122
-    {
1123
-        return apply_filters(
1124
-            'FHEE__EED_Core_REST_API__version_compatibilities',
1125
-            array(
1126
-                '4.8.29' => '4.8.29',
1127
-                '4.8.33' => '4.8.29',
1128
-                '4.8.34' => '4.8.29',
1129
-                '4.8.36' => '4.8.29',
1130
-            )
1131
-        );
1132
-    }
1133
-
1134
-
1135
-
1136
-    /**
1137
-     * Gets the latest API version served. Eg if there
1138
-     * are two versions served of the API, 4.8.29 and 4.8.32, and
1139
-     * we are on core version 4.8.34, it will return the string "4.8.32"
1140
-     *
1141
-     * @return string
1142
-     */
1143
-    public static function latest_rest_api_version()
1144
-    {
1145
-        $versions_served = \EED_Core_Rest_Api::versions_served();
1146
-        $versions_served_keys = array_keys($versions_served);
1147
-        return end($versions_served_keys);
1148
-    }
1149
-
1150
-
1151
-
1152
-    /**
1153
-     * Using EED_Core_Rest_Api::version_compatibilities(), determines what version of
1154
-     * EE the API can serve requests for. Eg, if we are on 4.15 of core, and
1155
-     * we can serve requests from 4.12 or later, this will return array( '4.12', '4.13', '4.14', '4.15' ).
1156
-     * We also indicate whether or not this version should be put in the index or not
1157
-     *
1158
-     * @return array keys are API version numbers (just major and minor numbers), and values
1159
-     * are whether or not they should be hidden
1160
-     */
1161
-    public static function versions_served()
1162
-    {
1163
-        $versions_served = array();
1164
-        $possibly_served_versions = EED_Core_Rest_Api::version_compatibilities();
1165
-        $lowest_compatible_version = end($possibly_served_versions);
1166
-        reset($possibly_served_versions);
1167
-        $versions_served_historically = array_keys($possibly_served_versions);
1168
-        $latest_version = end($versions_served_historically);
1169
-        reset($versions_served_historically);
1170
-        //for each version of core we have ever served:
1171
-        foreach ($versions_served_historically as $key_versioned_endpoint) {
1172
-            //if it's not above the current core version, and it's compatible with the current version of core
1173
-            if ($key_versioned_endpoint === $latest_version) {
1174
-                //don't hide the latest version in the index
1175
-                $versions_served[$key_versioned_endpoint] = false;
1176
-            } elseif (
1177
-                $key_versioned_endpoint >= $lowest_compatible_version
1178
-                && $key_versioned_endpoint < EED_Core_Rest_Api::core_version()
1179
-            ) {
1180
-                //include, but hide, previous versions which are still supported
1181
-                $versions_served[$key_versioned_endpoint] = true;
1182
-            } elseif (apply_filters(
1183
-                'FHEE__EED_Core_Rest_Api__versions_served__include_incompatible_versions',
1184
-                false,
1185
-                $possibly_served_versions
1186
-            )) {
1187
-                //if a version is no longer supported, don't include it in index or list of versions served
1188
-                $versions_served[$key_versioned_endpoint] = true;
1189
-            }
1190
-        }
1191
-        return $versions_served;
1192
-    }
1193
-
1194
-
1195
-
1196
-    /**
1197
-     * Gets the major and minor version of EE core's version string
1198
-     *
1199
-     * @return string
1200
-     */
1201
-    public static function core_version()
1202
-    {
1203
-        return apply_filters(
1204
-            'FHEE__EED_Core_REST_API__core_version',
1205
-            implode(
1206
-                '.',
1207
-                array_slice(
1208
-                    explode(
1209
-                        '.',
1210
-                        espresso_version()
1211
-                    ),
1212
-                0,
1213
-                3
1214
-                )
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
-     *    run - initial module setup
1240
-     *
1241
-     * @access    public
1242
-     * @param  WP $WP
1243
-     * @return    void
1244
-     */
1245
-    public function run($WP)
1246
-    {
1247
-    }
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
+	/**
47
+	 * @return EED_Core_Rest_Api|EED_Module
48
+	 */
49
+	public static function instance()
50
+	{
51
+		self::$_field_calculator = new CalculatedModelFields();
52
+		return parent::get_instance(__CLASS__);
53
+	}
54
+
55
+
56
+
57
+	/**
58
+	 *    set_hooks - for hooking into EE Core, other modules, etc
59
+	 *
60
+	 * @access    public
61
+	 * @return    void
62
+	 */
63
+	public static function set_hooks()
64
+	{
65
+		self::set_hooks_both();
66
+	}
67
+
68
+
69
+
70
+	/**
71
+	 *    set_hooks_admin - for hooking into EE Admin Core, other modules, etc
72
+	 *
73
+	 * @access    public
74
+	 * @return    void
75
+	 */
76
+	public static function set_hooks_admin()
77
+	{
78
+		self::set_hooks_both();
79
+	}
80
+
81
+
82
+
83
+	public static function set_hooks_both()
84
+	{
85
+		add_action('rest_api_init', array('EED_Core_Rest_Api', 'register_routes'), 10);
86
+		add_action('rest_api_init', array('EED_Core_Rest_Api', 'set_hooks_rest_api'), 5);
87
+		add_filter('rest_route_data', array('EED_Core_Rest_Api', 'hide_old_endpoints'), 10, 2);
88
+		add_filter('rest_index',
89
+			array('EventEspresso\core\libraries\rest_api\controllers\model\Meta', 'filterEeMetadataIntoIndex'));
90
+		EED_Core_Rest_Api::invalidate_cached_route_data_on_version_change();
91
+	}
92
+
93
+
94
+
95
+	/**
96
+	 * sets up hooks which only need to be included as part of REST API requests;
97
+	 * other requests like to the frontend or admin etc don't need them
98
+	 *
99
+	 * @throws \EE_Error
100
+	 */
101
+	public static function set_hooks_rest_api()
102
+	{
103
+		//set hooks which account for changes made to the API
104
+		EED_Core_Rest_Api::_set_hooks_for_changes();
105
+	}
106
+
107
+
108
+
109
+	/**
110
+	 * public wrapper of _set_hooks_for_changes.
111
+	 * Loads all the hooks which make requests to old versions of the API
112
+	 * appear the same as they always did
113
+	 *
114
+	 * @throws EE_Error
115
+	 */
116
+	public static function set_hooks_for_changes()
117
+	{
118
+		self::_set_hooks_for_changes();
119
+	}
120
+
121
+
122
+
123
+	/**
124
+	 * Loads all the hooks which make requests to old versions of the API
125
+	 * appear the same as they always did
126
+	 *
127
+	 * @throws EE_Error
128
+	 */
129
+	protected static function _set_hooks_for_changes()
130
+	{
131
+		$folder_contents = EEH_File::get_contents_of_folders(array(EE_LIBRARIES . 'rest_api' . DS . 'changes'), false);
132
+		foreach ($folder_contents as $classname_in_namespace => $filepath) {
133
+			//ignore the base parent class
134
+			//and legacy named classes
135
+			if ($classname_in_namespace === 'ChangesInBase'
136
+				|| strpos($classname_in_namespace, 'Changes_In_') === 0
137
+			) {
138
+				continue;
139
+			}
140
+			$full_classname = 'EventEspresso\core\libraries\rest_api\changes\\' . $classname_in_namespace;
141
+			if (class_exists($full_classname)) {
142
+				$instance_of_class = new $full_classname;
143
+				if ($instance_of_class instanceof ChangesInBase) {
144
+					$instance_of_class->setHooks();
145
+				}
146
+			}
147
+		}
148
+	}
149
+
150
+
151
+
152
+	/**
153
+	 * Filters the WP routes to add our EE-related ones. This takes a bit of time
154
+	 * so we actually prefer to only do it when an EE plugin is activated or upgraded
155
+	 *
156
+	 * @throws \EE_Error
157
+	 */
158
+	public static function register_routes()
159
+	{
160
+		foreach (EED_Core_Rest_Api::get_ee_route_data() as $namespace => $relative_routes) {
161
+			foreach ($relative_routes as $relative_route => $data_for_multiple_endpoints) {
162
+				/**
163
+				 * @var array $data_for_multiple_endpoints numerically indexed array
164
+				 *                                         but can also contain route options like {
165
+				 * @type array    $schema                      {
166
+				 * @type callable $schema_callback
167
+				 * @type array    $callback_args               arguments that will be passed to the callback, after the
168
+				 * WP_REST_Request of course
169
+				 * }
170
+				 * }
171
+				 */
172
+				//when registering routes, register all the endpoints' data at the same time
173
+				$multiple_endpoint_args = array();
174
+				foreach ($data_for_multiple_endpoints as $endpoint_key => $data_for_single_endpoint) {
175
+					/**
176
+					 * @var array     $data_for_single_endpoint {
177
+					 * @type callable $callback
178
+					 * @type string methods
179
+					 * @type array args
180
+					 * @type array _links
181
+					 * @type array    $callback_args            arguments that will be passed to the callback, after the
182
+					 * WP_REST_Request of course
183
+					 * }
184
+					 */
185
+					//skip route options
186
+					if (! is_numeric($endpoint_key)) {
187
+						continue;
188
+					}
189
+					if (! isset($data_for_single_endpoint['callback'], $data_for_single_endpoint['methods'])) {
190
+						throw new EE_Error(
191
+							esc_html__(
192
+								// @codingStandardsIgnoreStart
193
+								'Endpoint configuration data needs to have entries "callback" (callable) and "methods" (comma-separated list of accepts HTTP methods).',
194
+								// @codingStandardsIgnoreEnd
195
+								'event_espresso')
196
+						);
197
+					}
198
+					$callback = $data_for_single_endpoint['callback'];
199
+					$single_endpoint_args = array(
200
+						'methods' => $data_for_single_endpoint['methods'],
201
+						'args'    => isset($data_for_single_endpoint['args']) ? $data_for_single_endpoint['args']
202
+							: array(),
203
+					);
204
+					if (isset($data_for_single_endpoint['_links'])) {
205
+						$single_endpoint_args['_links'] = $data_for_single_endpoint['_links'];
206
+					}
207
+					if (isset($data_for_single_endpoint['callback_args'])) {
208
+						$callback_args = $data_for_single_endpoint['callback_args'];
209
+						$single_endpoint_args['callback'] = function (\WP_REST_Request $request) use (
210
+							$callback,
211
+							$callback_args
212
+						) {
213
+							array_unshift($callback_args, $request);
214
+							return call_user_func_array(
215
+								$callback,
216
+								$callback_args
217
+							);
218
+						};
219
+					} else {
220
+						$single_endpoint_args['callback'] = $data_for_single_endpoint['callback'];
221
+					}
222
+					$multiple_endpoint_args[] = $single_endpoint_args;
223
+				}
224
+				if (isset($data_for_multiple_endpoints['schema'])) {
225
+					$schema_route_data = $data_for_multiple_endpoints['schema'];
226
+					$schema_callback = $schema_route_data['schema_callback'];
227
+					$callback_args = $schema_route_data['callback_args'];
228
+					$multiple_endpoint_args['schema'] = function () use ($schema_callback, $callback_args) {
229
+						return call_user_func_array(
230
+							$schema_callback,
231
+							$callback_args
232
+						);
233
+					};
234
+				}
235
+				register_rest_route(
236
+					$namespace,
237
+					$relative_route,
238
+					$multiple_endpoint_args
239
+				);
240
+			}
241
+		}
242
+	}
243
+
244
+
245
+
246
+	/**
247
+	 * Checks if there was a version change or something that merits invalidating the cached
248
+	 * route data. If so, invalidates the cached route data so that it gets refreshed
249
+	 * next time the WP API is used
250
+	 */
251
+	public static function invalidate_cached_route_data_on_version_change()
252
+	{
253
+		if (EE_System::instance()->detect_req_type() !== EE_System::req_type_normal) {
254
+			EED_Core_Rest_Api::invalidate_cached_route_data();
255
+		}
256
+		foreach (EE_Registry::instance()->addons as $addon) {
257
+			if ($addon instanceof EE_Addon && $addon->detect_req_type() !== EE_System::req_type_normal) {
258
+				EED_Core_Rest_Api::invalidate_cached_route_data();
259
+			}
260
+		}
261
+	}
262
+
263
+
264
+
265
+	/**
266
+	 * Removes the cached route data so it will get refreshed next time the WP API is used
267
+	 */
268
+	public static function invalidate_cached_route_data()
269
+	{
270
+		//delete the saved EE REST API routes
271
+		foreach (EED_Core_Rest_Api::versions_served() as $version => $hidden) {
272
+			delete_option(EED_Core_Rest_Api::saved_routes_option_names . $version);
273
+		}
274
+	}
275
+
276
+
277
+
278
+	/**
279
+	 * Gets the EE route data
280
+	 *
281
+	 * @return array top-level key is the namespace, next-level key is the route and its value is array{
282
+	 * @throws \EE_Error
283
+	 * @type string|array $callback
284
+	 * @type string       $methods
285
+	 * @type boolean      $hidden_endpoint
286
+	 * }
287
+	 */
288
+	public static function get_ee_route_data()
289
+	{
290
+		$ee_routes = array();
291
+		foreach (self::versions_served() as $version => $hidden_endpoints) {
292
+			$ee_routes[self::ee_api_namespace . $version] = self::_get_ee_route_data_for_version(
293
+				$version,
294
+				$hidden_endpoints
295
+			);
296
+		}
297
+		return $ee_routes;
298
+	}
299
+
300
+
301
+
302
+	/**
303
+	 * Gets the EE route data from the wp options if it exists already,
304
+	 * otherwise re-generates it and saves it to the option
305
+	 *
306
+	 * @param string  $version
307
+	 * @param boolean $hidden_endpoints
308
+	 * @return array
309
+	 * @throws \EE_Error
310
+	 */
311
+	protected static function _get_ee_route_data_for_version($version, $hidden_endpoints = false)
312
+	{
313
+		$ee_routes = get_option(self::saved_routes_option_names . $version, null);
314
+		if (! $ee_routes || (defined('EE_REST_API_DEBUG_MODE') && EE_REST_API_DEBUG_MODE)) {
315
+			$ee_routes = self::_save_ee_route_data_for_version($version, $hidden_endpoints);
316
+		}
317
+		return $ee_routes;
318
+	}
319
+
320
+
321
+
322
+	/**
323
+	 * Saves the EE REST API route data to a wp option and returns it
324
+	 *
325
+	 * @param string  $version
326
+	 * @param boolean $hidden_endpoints
327
+	 * @return mixed|null
328
+	 * @throws \EE_Error
329
+	 */
330
+	protected static function _save_ee_route_data_for_version($version, $hidden_endpoints = false)
331
+	{
332
+		$instance = self::instance();
333
+		$routes = apply_filters(
334
+			'EED_Core_Rest_Api__save_ee_route_data_for_version__routes',
335
+			array_replace_recursive(
336
+				$instance->_get_config_route_data_for_version($version, $hidden_endpoints),
337
+				$instance->_get_meta_route_data_for_version($version, $hidden_endpoints),
338
+				$instance->_get_model_route_data_for_version($version, $hidden_endpoints),
339
+				$instance->_get_rpc_route_data_for_version($version, $hidden_endpoints)
340
+			)
341
+		);
342
+		$option_name = self::saved_routes_option_names . $version;
343
+		if (get_option($option_name)) {
344
+			update_option($option_name, $routes, true);
345
+		} else {
346
+			add_option($option_name, $routes, null, 'no');
347
+		}
348
+		return $routes;
349
+	}
350
+
351
+
352
+
353
+	/**
354
+	 * Calculates all the EE routes and saves it to a WordPress option so we don't
355
+	 * need to calculate it on every request
356
+	 *
357
+	 * @deprecated since version 4.9.1
358
+	 * @return void
359
+	 */
360
+	public static function save_ee_routes()
361
+	{
362
+		if (EE_Maintenance_Mode::instance()->models_can_query()) {
363
+			$instance = self::instance();
364
+			$routes = apply_filters(
365
+				'EED_Core_Rest_Api__save_ee_routes__routes',
366
+				array_replace_recursive(
367
+					$instance->_register_config_routes(),
368
+					$instance->_register_meta_routes(),
369
+					$instance->_register_model_routes(),
370
+					$instance->_register_rpc_routes()
371
+				)
372
+			);
373
+			update_option(self::saved_routes_option_names, $routes, true);
374
+		}
375
+	}
376
+
377
+
378
+
379
+	/**
380
+	 * Gets all the route information relating to EE models
381
+	 *
382
+	 * @return array @see get_ee_route_data
383
+	 * @deprecated since version 4.9.1
384
+	 */
385
+	protected function _register_model_routes()
386
+	{
387
+		$model_routes = array();
388
+		foreach (self::versions_served() as $version => $hidden_endpoint) {
389
+			$model_routes[EED_Core_Rest_Api::ee_api_namespace
390
+						  . $version] = $this->_get_config_route_data_for_version($version, $hidden_endpoint);
391
+		}
392
+		return $model_routes;
393
+	}
394
+
395
+
396
+
397
+	/**
398
+	 * Decides whether or not to add write endpoints for this model.
399
+	 *
400
+	 * Currently, this defaults to exclude all global tables and models
401
+	 * which would allow inserting WP core data (we don't want to duplicate
402
+	 * what WP API does, as it's unnecessary, extra work, and potentially extra bugs)
403
+	 * @param EEM_Base $model
404
+	 * @return bool
405
+	 */
406
+	public static function should_have_write_endpoints(EEM_Base $model)
407
+	{
408
+		if ($model->is_wp_core_model()){
409
+			return false;
410
+		}
411
+		foreach($model->get_tables() as $table){
412
+			if( $table->is_global()){
413
+				return false;
414
+			}
415
+		}
416
+		return true;
417
+	}
418
+
419
+
420
+
421
+	/**
422
+	 * Gets the names of all models which should have plural routes (eg `ee/v4.8.36/events`)
423
+	 * in this versioned namespace of EE4
424
+	 * @param $version
425
+	 * @return array keys are model names (eg 'Event') and values ar either classnames (eg 'EEM_Event')
426
+	 */
427
+	public static function model_names_with_plural_routes($version){
428
+		$model_version_info = new ModelVersionInfo($version);
429
+		$models_to_register = $model_version_info->modelsForRequestedVersion();
430
+		//let's not bother having endpoints for extra metas
431
+		unset(
432
+			$models_to_register['Extra_Meta'],
433
+			$models_to_register['Extra_Join'],
434
+			$models_to_register['Post_Meta']
435
+		);
436
+		return apply_filters(
437
+			'FHEE__EED_Core_REST_API___register_model_routes',
438
+			$models_to_register
439
+		);
440
+	}
441
+
442
+
443
+
444
+	/**
445
+	 * Gets the route data for EE models in the specified version
446
+	 *
447
+	 * @param string  $version
448
+	 * @param boolean $hidden_endpoint
449
+	 * @return array
450
+	 * @throws EE_Error
451
+	 */
452
+	protected function _get_model_route_data_for_version($version, $hidden_endpoint = false)
453
+	{
454
+		$model_routes = array();
455
+		$model_version_info = new ModelVersionInfo($version);
456
+		foreach (EED_Core_Rest_Api::model_names_with_plural_routes($version) as $model_name => $model_classname) {
457
+			$model = \EE_Registry::instance()->load_model($model_name);
458
+			//if this isn't a valid model then let's skip iterate to the next item in the loop.
459
+			if (! $model instanceof EEM_Base) {
460
+				continue;
461
+			}
462
+			//yes we could just register one route for ALL models, but then they wouldn't show up in the index
463
+			$plural_model_route = EED_Core_Rest_Api::get_collection_route($model);
464
+			$singular_model_route = EED_Core_Rest_Api::get_entity_route($model, '(?P<id>[^\/]+)');
465
+			$model_routes[$plural_model_route] = array(
466
+				array(
467
+					'callback'        => array(
468
+						'EventEspresso\core\libraries\rest_api\controllers\model\Read',
469
+						'handleRequestGetAll',
470
+					),
471
+					'callback_args'   => array($version, $model_name),
472
+					'methods'         => WP_REST_Server::READABLE,
473
+					'hidden_endpoint' => $hidden_endpoint,
474
+					'args'            => $this->_get_read_query_params($model, $version),
475
+					'_links'          => array(
476
+						'self' => rest_url(EED_Core_Rest_Api::ee_api_namespace . $version . $singular_model_route),
477
+					),
478
+				),
479
+				'schema' => array(
480
+					'schema_callback' => array(
481
+						'EventEspresso\core\libraries\rest_api\controllers\model\Read',
482
+						'handleSchemaRequest',
483
+					),
484
+					'callback_args'   => array($version, $model_name),
485
+				),
486
+			);
487
+			$model_routes[$singular_model_route] = array(
488
+				array(
489
+					'callback'        => array(
490
+						'EventEspresso\core\libraries\rest_api\controllers\model\Read',
491
+						'handleRequestGetOne',
492
+					),
493
+					'callback_args'   => array($version, $model_name),
494
+					'methods'         => WP_REST_Server::READABLE,
495
+					'hidden_endpoint' => $hidden_endpoint,
496
+					'args'            => $this->_get_response_selection_query_params($model, $version),
497
+				),
498
+			);
499
+			if( apply_filters(
500
+				'FHEE__EED_Core_Rest_Api___get_model_route_data_for_version__add_write_endpoints',
501
+				EED_Core_Rest_Api::should_have_write_endpoints($model),
502
+				$model
503
+			)){
504
+				$model_routes[$plural_model_route][] = array(
505
+					'callback'        => array(
506
+						'EventEspresso\core\libraries\rest_api\controllers\model\Write',
507
+						'handleRequestInsert',
508
+					),
509
+					'callback_args'   => array($version, $model_name),
510
+					'methods'         => WP_REST_Server::CREATABLE,
511
+					'hidden_endpoint' => $hidden_endpoint,
512
+					'args'            => $this->_get_write_params($model_name, $model_version_info, true),
513
+				);
514
+				$model_routes[$singular_model_route] = array_merge(
515
+					$model_routes[$singular_model_route],
516
+					array(
517
+						array(
518
+							'callback'        => array(
519
+								'EventEspresso\core\libraries\rest_api\controllers\model\Write',
520
+								'handleRequestUpdate',
521
+							),
522
+							'callback_args'   => array($version, $model_name),
523
+							'methods'         => WP_REST_Server::EDITABLE,
524
+							'hidden_endpoint' => $hidden_endpoint,
525
+							'args'            => $this->_get_write_params($model_name, $model_version_info),
526
+						),
527
+						array(
528
+							'callback'        => array(
529
+								'EventEspresso\core\libraries\rest_api\controllers\model\Write',
530
+								'handleRequestDelete',
531
+							),
532
+							'callback_args'   => array($version, $model_name),
533
+							'methods'         => WP_REST_Server::DELETABLE,
534
+							'hidden_endpoint' => $hidden_endpoint,
535
+							'args'            => $this->_get_delete_query_params($model, $version),
536
+						)
537
+					)
538
+				);
539
+			}
540
+			foreach ($model->relation_settings() as $relation_name => $relation_obj) {
541
+
542
+				$related_route = EED_Core_Rest_Api::get_relation_route_via(
543
+					$model,
544
+					'(?P<id>[^\/]+)',
545
+					$relation_obj
546
+				);
547
+				$endpoints = array(
548
+					array(
549
+						'callback'        => array(
550
+							'EventEspresso\core\libraries\rest_api\controllers\model\Read',
551
+							'handleRequestGetRelated',
552
+						),
553
+						'callback_args'   => array($version, $model_name, $relation_name),
554
+						'methods'         => WP_REST_Server::READABLE,
555
+						'hidden_endpoint' => $hidden_endpoint,
556
+						'args'            => $this->_get_read_query_params($relation_obj->get_other_model(), $version),
557
+					),
558
+				);
559
+				$model_routes[$related_route] = $endpoints;
560
+			}
561
+		}
562
+		return $model_routes;
563
+	}
564
+
565
+
566
+
567
+	/**
568
+	 * Gets the relative URI to a model's REST API plural route, after the EE4 versioned namespace,
569
+	 * excluding the preceding slash.
570
+	 * Eg you pass get_plural_route_to('Event') = 'events'
571
+	 *
572
+	 * @param EEM_Base $model
573
+	 * @return string
574
+	 */
575
+	public static function get_collection_route(EEM_Base $model)
576
+	{
577
+		return EEH_Inflector::pluralize_and_lower($model->get_this_model_name());
578
+	}
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
+	 * @return string
590
+	 */
591
+	public static function get_entity_route($model, $id)
592
+	{
593
+		return EED_Core_Rest_Api::get_collection_route($model). '/' . $id;
594
+	}
595
+
596
+
597
+	/**
598
+	 * Gets the relative URI to a model's REST API singular route, after the EE4 versioned namespace,
599
+	 * excluding the preceding slash.
600
+	 * Eg you pass get_plural_route_to('Event', 12) = 'events/12'
601
+	 *
602
+	 * @param EEM_Base                 $model eg Event or Venue
603
+	 * @param string                 $id
604
+	 * @param EE_Model_Relation_Base $relation_obj
605
+	 * @return string
606
+	 */
607
+	public static function get_relation_route_via(EEM_Base $model, $id, EE_Model_Relation_Base $relation_obj)
608
+	{
609
+		$related_model_name_endpoint_part = ModelRead::getRelatedEntityName(
610
+			$relation_obj->get_other_model()->get_this_model_name(),
611
+			$relation_obj
612
+		);
613
+		return EED_Core_Rest_Api::get_entity_route($model, $id) . '/' . $related_model_name_endpoint_part;
614
+	}
615
+
616
+
617
+
618
+	/**
619
+	 * Adds onto the $relative_route the EE4 REST API versioned namespace.
620
+	 * Eg if given '4.8.36' and 'events', will return 'ee/v4.8.36/events'
621
+	 * @param string $relative_route
622
+	 * @param string $version
623
+	 * @return string
624
+	 */
625
+	public static function get_versioned_route_to($relative_route, $version = '4.8.36'){
626
+		return '/' . EED_Core_Rest_Api::ee_api_namespace . $version . '/' . $relative_route;
627
+	}
628
+
629
+
630
+
631
+	/**
632
+	 * Adds all the RPC-style routes (remote procedure call-like routes, ie
633
+	 * routes that don't conform to the traditional REST CRUD-style).
634
+	 *
635
+	 * @deprecated since 4.9.1
636
+	 */
637
+	protected function _register_rpc_routes()
638
+	{
639
+		$routes = array();
640
+		foreach (self::versions_served() as $version => $hidden_endpoint) {
641
+			$routes[self::ee_api_namespace . $version] = $this->_get_rpc_route_data_for_version(
642
+				$version,
643
+				$hidden_endpoint
644
+			);
645
+		}
646
+		return $routes;
647
+	}
648
+
649
+
650
+
651
+	/**
652
+	 * @param string  $version
653
+	 * @param boolean $hidden_endpoint
654
+	 * @return array
655
+	 */
656
+	protected function _get_rpc_route_data_for_version($version, $hidden_endpoint = false)
657
+	{
658
+		$this_versions_routes = array();
659
+		//checkin endpoint
660
+		$this_versions_routes['registrations/(?P<REG_ID>\d+)/toggle_checkin_for_datetime/(?P<DTT_ID>\d+)'] = array(
661
+			array(
662
+				'callback'        => array(
663
+					'EventEspresso\core\libraries\rest_api\controllers\rpc\Checkin',
664
+					'handleRequestToggleCheckin',
665
+				),
666
+				'methods'         => WP_REST_Server::CREATABLE,
667
+				'hidden_endpoint' => $hidden_endpoint,
668
+				'args'            => array(
669
+					'force' => array(
670
+						'required'    => false,
671
+						'default'     => false,
672
+						'description' => __(
673
+							// @codingStandardsIgnoreStart
674
+							'Whether to force toggle checkin, or to verify the registration status and allowed ticket uses',
675
+							// @codingStandardsIgnoreEnd
676
+							'event_espresso'
677
+						),
678
+					),
679
+				),
680
+				'callback_args'   => array($version),
681
+			),
682
+		);
683
+		return apply_filters(
684
+			'FHEE__EED_Core_Rest_Api___register_rpc_routes__this_versions_routes',
685
+			$this_versions_routes,
686
+			$version,
687
+			$hidden_endpoint
688
+		);
689
+	}
690
+
691
+
692
+
693
+	/**
694
+	 * Gets the query params that can be used when request one or many
695
+	 *
696
+	 * @param EEM_Base $model
697
+	 * @param string   $version
698
+	 * @return array
699
+	 */
700
+	protected function _get_response_selection_query_params(\EEM_Base $model, $version)
701
+	{
702
+		return apply_filters(
703
+			'FHEE__EED_Core_Rest_Api___get_response_selection_query_params',
704
+			array(
705
+				'include'   => array(
706
+					'required' => false,
707
+					'default'  => '*',
708
+					'type'     => 'string',
709
+				),
710
+				'calculate' => array(
711
+					'required'          => false,
712
+					'default'           => '',
713
+					'enum'              => self::$_field_calculator->retrieveCalculatedFieldsForModel($model),
714
+					'type'              => 'string',
715
+					//because we accept a CSV'd list of the enumerated strings, WP core validation and sanitization
716
+					//freaks out. We'll just validate this argument while handling the request
717
+					'validate_callback' => null,
718
+					'sanitize_callback' => null,
719
+				),
720
+			),
721
+			$model,
722
+			$version
723
+		);
724
+	}
725
+
726
+
727
+
728
+	/**
729
+	 * Gets the parameters acceptable for delete requests
730
+	 *
731
+	 * @param \EEM_Base $model
732
+	 * @param string    $version
733
+	 * @return array
734
+	 */
735
+	protected function _get_delete_query_params(\EEM_Base $model, $version)
736
+	{
737
+		$params_for_delete = array(
738
+			'allow_blocking' => array(
739
+				'required' => false,
740
+				'default'  => true,
741
+				'type'     => 'boolean',
742
+			),
743
+		);
744
+		$params_for_delete['force'] = array(
745
+			'required' => false,
746
+			'default'  => false,
747
+			'type'     => 'boolean',
748
+		);
749
+		return apply_filters(
750
+			'FHEE__EED_Core_Rest_Api___get_delete_query_params',
751
+			$params_for_delete,
752
+			$model,
753
+			$version
754
+		);
755
+	}
756
+
757
+
758
+
759
+	/**
760
+	 * Gets info about reading query params that are acceptable
761
+	 *
762
+	 * @param \EEM_Base $model eg 'Event' or 'Venue'
763
+	 * @param  string   $version
764
+	 * @return array    describing the args acceptable when querying this model
765
+	 * @throws EE_Error
766
+	 */
767
+	protected function _get_read_query_params(\EEM_Base $model, $version)
768
+	{
769
+		$default_orderby = array();
770
+		foreach ($model->get_combined_primary_key_fields() as $key_field) {
771
+			$default_orderby[$key_field->get_name()] = 'ASC';
772
+		}
773
+		return array_merge(
774
+			$this->_get_response_selection_query_params($model, $version),
775
+			array(
776
+				'where'    => array(
777
+					'required' => false,
778
+					'default'  => array(),
779
+					'type'     => 'object',
780
+				),
781
+				'limit'    => array(
782
+					'required' => false,
783
+					'default'  => EED_Core_Rest_Api::get_default_query_limit(),
784
+					'type'     => array(
785
+						'object',
786
+						'string',
787
+						'integer',
788
+					),
789
+				),
790
+				'order_by' => array(
791
+					'required' => false,
792
+					'default'  => $default_orderby,
793
+					'type'     => array(
794
+						'object',
795
+						'string',
796
+					),
797
+				),
798
+				'group_by' => array(
799
+					'required' => false,
800
+					'default'  => null,
801
+					'type'     => array(
802
+						'object',
803
+						'string',
804
+					),
805
+				),
806
+				'having'   => array(
807
+					'required' => false,
808
+					'default'  => null,
809
+					'type'     => 'object',
810
+				),
811
+				'caps'     => array(
812
+					'required' => false,
813
+					'default'  => EEM_Base::caps_read,
814
+					'type'     => 'string',
815
+				),
816
+			)
817
+		);
818
+	}
819
+
820
+
821
+
822
+	/**
823
+	 * Gets parameter information for a model regarding writing data
824
+	 *
825
+	 * @param string           $model_name
826
+	 * @param ModelVersionInfo $model_version_info
827
+	 * @param boolean          $create                                       whether this is for request to create (in which case we need
828
+	 *                                                                       all required params) or just to update (in which case we don't need those on every request)
829
+	 * @return array
830
+	 */
831
+	protected function _get_write_params(
832
+		$model_name,
833
+		ModelVersionInfo $model_version_info,
834
+		$create = false
835
+	) {
836
+		$model = EE_Registry::instance()->load_model($model_name);
837
+		$fields = $model_version_info->fieldsOnModelInThisVersion($model);
838
+		$args_info = array();
839
+		foreach ($fields as $field_name => $field_obj) {
840
+			if ($field_obj->is_auto_increment()) {
841
+				//totally ignore auto increment IDs
842
+				continue;
843
+			}
844
+			$arg_info = $field_obj->getSchema();
845
+			$required = $create && ! $field_obj->is_nullable() && $field_obj->get_default_value() === null;
846
+			$arg_info['required'] = $required;
847
+			//remove the read-only flag. If it were read-only we wouldn't list it as an argument while writing, right?
848
+			unset($arg_info['readonly']);
849
+			$schema_properties = $field_obj->getSchemaProperties();
850
+			if (
851
+				isset($schema_properties['raw'])
852
+				&& $field_obj->getSchemaType() === 'object'
853
+			) {
854
+				//if there's a "raw" form of this argument, use those properties instead
855
+				$arg_info = array_replace(
856
+					$arg_info,
857
+					$schema_properties['raw']
858
+				);
859
+			}
860
+			$arg_info['default'] = ModelDataTranslator::prepareFieldValueForJson(
861
+				$field_obj,
862
+				$field_obj->get_default_value(),
863
+				$model_version_info->requestedVersion()
864
+			);
865
+			//we do our own validation and sanitization within the controller
866
+			$arg_info['sanitize_callback'] =
867
+				array(
868
+					'EED_Core_Rest_Api',
869
+					'default_sanitize_callback',
870
+				);
871
+			$args_info[$field_name] = $arg_info;
872
+			if ($field_obj instanceof EE_Datetime_Field) {
873
+				$gmt_arg_info = $arg_info;
874
+				$gmt_arg_info['description'] = sprintf(
875
+					esc_html__(
876
+						'%1$s - the value for this field in UTC. Ignored if %2$s is provided.',
877
+						'event_espresso'
878
+					),
879
+					$field_obj->get_nicename(),
880
+					$field_name
881
+				);
882
+				$args_info[$field_name . '_gmt'] = $gmt_arg_info;
883
+			}
884
+		}
885
+		return $args_info;
886
+	}
887
+
888
+
889
+
890
+	/**
891
+	 * Replacement for WP API's 'rest_parse_request_arg'.
892
+	 * If the value is blank but not required, don't bother validating it.
893
+	 * Also, it uses our email validation instead of WP API's default.
894
+	 *
895
+	 * @param                 $value
896
+	 * @param WP_REST_Request $request
897
+	 * @param                 $param
898
+	 * @return bool|true|WP_Error
899
+	 * @throws InvalidArgumentException
900
+	 * @throws InvalidInterfaceException
901
+	 * @throws InvalidDataTypeException
902
+	 */
903
+	public static function default_sanitize_callback( $value, WP_REST_Request $request, $param)
904
+	{
905
+		$attributes = $request->get_attributes();
906
+		if (! isset($attributes['args'][$param])
907
+			|| ! is_array($attributes['args'][$param])) {
908
+			$validation_result = true;
909
+		} else {
910
+			$args = $attributes['args'][$param];
911
+			if ((
912
+					$value === ''
913
+					|| $value === null
914
+				)
915
+				&& (! isset($args['required'])
916
+					|| $args['required'] === false
917
+				)
918
+			) {
919
+				//not required and not provided? that's cool
920
+				$validation_result = true;
921
+			} elseif (isset($args['format'])
922
+				&& $args['format'] === 'email'
923
+			) {
924
+				$validation_result = true;
925
+				if (! self::_validate_email($value)) {
926
+					$validation_result = new WP_Error(
927
+						'rest_invalid_param',
928
+						esc_html__(
929
+							'The email address is not valid or does not exist.',
930
+							'event_espresso'
931
+						)
932
+					);
933
+				}
934
+			} else {
935
+				$validation_result = rest_validate_value_from_schema($value, $args, $param);
936
+			}
937
+		}
938
+		if (is_wp_error($validation_result)) {
939
+			return $validation_result;
940
+		}
941
+		return rest_sanitize_request_arg($value, $request, $param);
942
+	}
943
+
944
+
945
+
946
+	/**
947
+	 * Returns whether or not this email address is valid. Copied from EE_Email_Validation_Strategy::_validate_email()
948
+	 *
949
+	 * @param $email
950
+	 * @return bool
951
+	 * @throws InvalidArgumentException
952
+	 * @throws InvalidInterfaceException
953
+	 * @throws InvalidDataTypeException
954
+	 */
955
+	protected static function _validate_email($email){
956
+		try {
957
+			EmailAddressFactory::create($email);
958
+			return true;
959
+		} catch (EmailValidationException $e) {
960
+			return false;
961
+		}
962
+	}
963
+
964
+
965
+
966
+	/**
967
+	 * Gets routes for the config
968
+	 *
969
+	 * @return array @see _register_model_routes
970
+	 * @deprecated since version 4.9.1
971
+	 */
972
+	protected function _register_config_routes()
973
+	{
974
+		$config_routes = array();
975
+		foreach (self::versions_served() as $version => $hidden_endpoint) {
976
+			$config_routes[self::ee_api_namespace . $version] = $this->_get_config_route_data_for_version(
977
+				$version,
978
+				$hidden_endpoint
979
+			);
980
+		}
981
+		return $config_routes;
982
+	}
983
+
984
+
985
+
986
+	/**
987
+	 * Gets routes for the config for the specified version
988
+	 *
989
+	 * @param string  $version
990
+	 * @param boolean $hidden_endpoint
991
+	 * @return array
992
+	 */
993
+	protected function _get_config_route_data_for_version($version, $hidden_endpoint)
994
+	{
995
+		return array(
996
+			'config'    => array(
997
+				array(
998
+					'callback'        => array(
999
+						'EventEspresso\core\libraries\rest_api\controllers\config\Read',
1000
+						'handleRequest',
1001
+					),
1002
+					'methods'         => WP_REST_Server::READABLE,
1003
+					'hidden_endpoint' => $hidden_endpoint,
1004
+					'callback_args'   => array($version),
1005
+				),
1006
+			),
1007
+			'site_info' => array(
1008
+				array(
1009
+					'callback'        => array(
1010
+						'EventEspresso\core\libraries\rest_api\controllers\config\Read',
1011
+						'handleRequestSiteInfo',
1012
+					),
1013
+					'methods'         => WP_REST_Server::READABLE,
1014
+					'hidden_endpoint' => $hidden_endpoint,
1015
+					'callback_args'   => array($version),
1016
+				),
1017
+			),
1018
+		);
1019
+	}
1020
+
1021
+
1022
+
1023
+	/**
1024
+	 * Gets the meta info routes
1025
+	 *
1026
+	 * @return array @see _register_model_routes
1027
+	 * @deprecated since version 4.9.1
1028
+	 */
1029
+	protected function _register_meta_routes()
1030
+	{
1031
+		$meta_routes = array();
1032
+		foreach (self::versions_served() as $version => $hidden_endpoint) {
1033
+			$meta_routes[self::ee_api_namespace . $version] = $this->_get_meta_route_data_for_version(
1034
+				$version,
1035
+				$hidden_endpoint
1036
+			);
1037
+		}
1038
+		return $meta_routes;
1039
+	}
1040
+
1041
+
1042
+
1043
+	/**
1044
+	 * @param string  $version
1045
+	 * @param boolean $hidden_endpoint
1046
+	 * @return array
1047
+	 */
1048
+	protected function _get_meta_route_data_for_version($version, $hidden_endpoint = false)
1049
+	{
1050
+		return array(
1051
+			'resources' => array(
1052
+				array(
1053
+					'callback'        => array(
1054
+						'EventEspresso\core\libraries\rest_api\controllers\model\Meta',
1055
+						'handleRequestModelsMeta',
1056
+					),
1057
+					'methods'         => WP_REST_Server::READABLE,
1058
+					'hidden_endpoint' => $hidden_endpoint,
1059
+					'callback_args'   => array($version),
1060
+				),
1061
+			),
1062
+		);
1063
+	}
1064
+
1065
+
1066
+
1067
+	/**
1068
+	 * Tries to hide old 4.6 endpoints from the
1069
+	 *
1070
+	 * @param array $route_data
1071
+	 * @return array
1072
+	 * @throws \EE_Error
1073
+	 */
1074
+	public static function hide_old_endpoints($route_data)
1075
+	{
1076
+		//allow API clients to override which endpoints get hidden, in case
1077
+		//they want to discover particular endpoints
1078
+		//also, we don't have access to the request so we have to just grab it from the superglobal
1079
+		$force_show_ee_namespace = ltrim(
1080
+			EEH_Array::is_set($_REQUEST, 'force_show_ee_namespace', ''),
1081
+			'/'
1082
+		);
1083
+		foreach (EED_Core_Rest_Api::get_ee_route_data() as $namespace => $relative_urls) {
1084
+			foreach ($relative_urls as $resource_name => $endpoints) {
1085
+				foreach ($endpoints as $key => $endpoint) {
1086
+					//skip schema and other route options
1087
+					if (! is_numeric($key)) {
1088
+						continue;
1089
+					}
1090
+					//by default, hide "hidden_endpoint"s, unless the request indicates
1091
+					//to $force_show_ee_namespace, in which case only show that one
1092
+					//namespace's endpoints (and hide all others)
1093
+					if (
1094
+						($force_show_ee_namespace !== '' && $force_show_ee_namespace !== $namespace)
1095
+						|| ($endpoint['hidden_endpoint'] && $force_show_ee_namespace === '')
1096
+					) {
1097
+						$full_route = '/' . ltrim($namespace, '/');
1098
+						$full_route .= '/' . ltrim($resource_name, '/');
1099
+						unset($route_data[$full_route]);
1100
+					}
1101
+				}
1102
+			}
1103
+		}
1104
+		return $route_data;
1105
+	}
1106
+
1107
+
1108
+
1109
+	/**
1110
+	 * Returns an array describing which versions of core support serving requests for.
1111
+	 * Keys are core versions' major and minor version, and values are the
1112
+	 * LOWEST requested version they can serve. Eg, 4.7 can serve requests for 4.6-like
1113
+	 * data by just removing a few models and fields from the responses. However, 4.15 might remove
1114
+	 * the answers table entirely, in which case it would be very difficult for
1115
+	 * it to serve 4.6-style responses.
1116
+	 * Versions of core that are missing from this array are unknowns.
1117
+	 * previous ver
1118
+	 *
1119
+	 * @return array
1120
+	 */
1121
+	public static function version_compatibilities()
1122
+	{
1123
+		return apply_filters(
1124
+			'FHEE__EED_Core_REST_API__version_compatibilities',
1125
+			array(
1126
+				'4.8.29' => '4.8.29',
1127
+				'4.8.33' => '4.8.29',
1128
+				'4.8.34' => '4.8.29',
1129
+				'4.8.36' => '4.8.29',
1130
+			)
1131
+		);
1132
+	}
1133
+
1134
+
1135
+
1136
+	/**
1137
+	 * Gets the latest API version served. Eg if there
1138
+	 * are two versions served of the API, 4.8.29 and 4.8.32, and
1139
+	 * we are on core version 4.8.34, it will return the string "4.8.32"
1140
+	 *
1141
+	 * @return string
1142
+	 */
1143
+	public static function latest_rest_api_version()
1144
+	{
1145
+		$versions_served = \EED_Core_Rest_Api::versions_served();
1146
+		$versions_served_keys = array_keys($versions_served);
1147
+		return end($versions_served_keys);
1148
+	}
1149
+
1150
+
1151
+
1152
+	/**
1153
+	 * Using EED_Core_Rest_Api::version_compatibilities(), determines what version of
1154
+	 * EE the API can serve requests for. Eg, if we are on 4.15 of core, and
1155
+	 * we can serve requests from 4.12 or later, this will return array( '4.12', '4.13', '4.14', '4.15' ).
1156
+	 * We also indicate whether or not this version should be put in the index or not
1157
+	 *
1158
+	 * @return array keys are API version numbers (just major and minor numbers), and values
1159
+	 * are whether or not they should be hidden
1160
+	 */
1161
+	public static function versions_served()
1162
+	{
1163
+		$versions_served = array();
1164
+		$possibly_served_versions = EED_Core_Rest_Api::version_compatibilities();
1165
+		$lowest_compatible_version = end($possibly_served_versions);
1166
+		reset($possibly_served_versions);
1167
+		$versions_served_historically = array_keys($possibly_served_versions);
1168
+		$latest_version = end($versions_served_historically);
1169
+		reset($versions_served_historically);
1170
+		//for each version of core we have ever served:
1171
+		foreach ($versions_served_historically as $key_versioned_endpoint) {
1172
+			//if it's not above the current core version, and it's compatible with the current version of core
1173
+			if ($key_versioned_endpoint === $latest_version) {
1174
+				//don't hide the latest version in the index
1175
+				$versions_served[$key_versioned_endpoint] = false;
1176
+			} elseif (
1177
+				$key_versioned_endpoint >= $lowest_compatible_version
1178
+				&& $key_versioned_endpoint < EED_Core_Rest_Api::core_version()
1179
+			) {
1180
+				//include, but hide, previous versions which are still supported
1181
+				$versions_served[$key_versioned_endpoint] = true;
1182
+			} elseif (apply_filters(
1183
+				'FHEE__EED_Core_Rest_Api__versions_served__include_incompatible_versions',
1184
+				false,
1185
+				$possibly_served_versions
1186
+			)) {
1187
+				//if a version is no longer supported, don't include it in index or list of versions served
1188
+				$versions_served[$key_versioned_endpoint] = true;
1189
+			}
1190
+		}
1191
+		return $versions_served;
1192
+	}
1193
+
1194
+
1195
+
1196
+	/**
1197
+	 * Gets the major and minor version of EE core's version string
1198
+	 *
1199
+	 * @return string
1200
+	 */
1201
+	public static function core_version()
1202
+	{
1203
+		return apply_filters(
1204
+			'FHEE__EED_Core_REST_API__core_version',
1205
+			implode(
1206
+				'.',
1207
+				array_slice(
1208
+					explode(
1209
+						'.',
1210
+						espresso_version()
1211
+					),
1212
+				0,
1213
+				3
1214
+				)
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
+	 *    run - initial module setup
1240
+	 *
1241
+	 * @access    public
1242
+	 * @param  WP $WP
1243
+	 * @return    void
1244
+	 */
1245
+	public function run($WP)
1246
+	{
1247
+	}
1248 1248
 }
1249 1249
 
1250 1250
 // End of file EED_Core_Rest_Api.module.php
Please login to merge, or discard this patch.
Spacing   +35 added lines, -35 removed lines patch added patch discarded remove patch
@@ -128,7 +128,7 @@  discard block
 block discarded – undo
128 128
      */
129 129
     protected static function _set_hooks_for_changes()
130 130
     {
131
-        $folder_contents = EEH_File::get_contents_of_folders(array(EE_LIBRARIES . 'rest_api' . DS . 'changes'), false);
131
+        $folder_contents = EEH_File::get_contents_of_folders(array(EE_LIBRARIES.'rest_api'.DS.'changes'), false);
132 132
         foreach ($folder_contents as $classname_in_namespace => $filepath) {
133 133
             //ignore the base parent class
134 134
             //and legacy named classes
@@ -137,7 +137,7 @@  discard block
 block discarded – undo
137 137
             ) {
138 138
                 continue;
139 139
             }
140
-            $full_classname = 'EventEspresso\core\libraries\rest_api\changes\\' . $classname_in_namespace;
140
+            $full_classname = 'EventEspresso\core\libraries\rest_api\changes\\'.$classname_in_namespace;
141 141
             if (class_exists($full_classname)) {
142 142
                 $instance_of_class = new $full_classname;
143 143
                 if ($instance_of_class instanceof ChangesInBase) {
@@ -183,10 +183,10 @@  discard block
 block discarded – undo
183 183
                      * }
184 184
                      */
185 185
                     //skip route options
186
-                    if (! is_numeric($endpoint_key)) {
186
+                    if ( ! is_numeric($endpoint_key)) {
187 187
                         continue;
188 188
                     }
189
-                    if (! isset($data_for_single_endpoint['callback'], $data_for_single_endpoint['methods'])) {
189
+                    if ( ! isset($data_for_single_endpoint['callback'], $data_for_single_endpoint['methods'])) {
190 190
                         throw new EE_Error(
191 191
                             esc_html__(
192 192
                                 // @codingStandardsIgnoreStart
@@ -206,7 +206,7 @@  discard block
 block discarded – undo
206 206
                     }
207 207
                     if (isset($data_for_single_endpoint['callback_args'])) {
208 208
                         $callback_args = $data_for_single_endpoint['callback_args'];
209
-                        $single_endpoint_args['callback'] = function (\WP_REST_Request $request) use (
209
+                        $single_endpoint_args['callback'] = function(\WP_REST_Request $request) use (
210 210
                             $callback,
211 211
                             $callback_args
212 212
                         ) {
@@ -225,7 +225,7 @@  discard block
 block discarded – undo
225 225
                     $schema_route_data = $data_for_multiple_endpoints['schema'];
226 226
                     $schema_callback = $schema_route_data['schema_callback'];
227 227
                     $callback_args = $schema_route_data['callback_args'];
228
-                    $multiple_endpoint_args['schema'] = function () use ($schema_callback, $callback_args) {
228
+                    $multiple_endpoint_args['schema'] = function() use ($schema_callback, $callback_args) {
229 229
                         return call_user_func_array(
230 230
                             $schema_callback,
231 231
                             $callback_args
@@ -269,7 +269,7 @@  discard block
 block discarded – undo
269 269
     {
270 270
         //delete the saved EE REST API routes
271 271
         foreach (EED_Core_Rest_Api::versions_served() as $version => $hidden) {
272
-            delete_option(EED_Core_Rest_Api::saved_routes_option_names . $version);
272
+            delete_option(EED_Core_Rest_Api::saved_routes_option_names.$version);
273 273
         }
274 274
     }
275 275
 
@@ -289,7 +289,7 @@  discard block
 block discarded – undo
289 289
     {
290 290
         $ee_routes = array();
291 291
         foreach (self::versions_served() as $version => $hidden_endpoints) {
292
-            $ee_routes[self::ee_api_namespace . $version] = self::_get_ee_route_data_for_version(
292
+            $ee_routes[self::ee_api_namespace.$version] = self::_get_ee_route_data_for_version(
293 293
                 $version,
294 294
                 $hidden_endpoints
295 295
             );
@@ -310,8 +310,8 @@  discard block
 block discarded – undo
310 310
      */
311 311
     protected static function _get_ee_route_data_for_version($version, $hidden_endpoints = false)
312 312
     {
313
-        $ee_routes = get_option(self::saved_routes_option_names . $version, null);
314
-        if (! $ee_routes || (defined('EE_REST_API_DEBUG_MODE') && EE_REST_API_DEBUG_MODE)) {
313
+        $ee_routes = get_option(self::saved_routes_option_names.$version, null);
314
+        if ( ! $ee_routes || (defined('EE_REST_API_DEBUG_MODE') && EE_REST_API_DEBUG_MODE)) {
315 315
             $ee_routes = self::_save_ee_route_data_for_version($version, $hidden_endpoints);
316 316
         }
317 317
         return $ee_routes;
@@ -339,7 +339,7 @@  discard block
 block discarded – undo
339 339
                 $instance->_get_rpc_route_data_for_version($version, $hidden_endpoints)
340 340
             )
341 341
         );
342
-        $option_name = self::saved_routes_option_names . $version;
342
+        $option_name = self::saved_routes_option_names.$version;
343 343
         if (get_option($option_name)) {
344 344
             update_option($option_name, $routes, true);
345 345
         } else {
@@ -405,11 +405,11 @@  discard block
 block discarded – undo
405 405
      */
406 406
     public static function should_have_write_endpoints(EEM_Base $model)
407 407
     {
408
-        if ($model->is_wp_core_model()){
408
+        if ($model->is_wp_core_model()) {
409 409
             return false;
410 410
         }
411
-        foreach($model->get_tables() as $table){
412
-            if( $table->is_global()){
411
+        foreach ($model->get_tables() as $table) {
412
+            if ($table->is_global()) {
413 413
                 return false;
414 414
             }
415 415
         }
@@ -424,7 +424,7 @@  discard block
 block discarded – undo
424 424
      * @param $version
425 425
      * @return array keys are model names (eg 'Event') and values ar either classnames (eg 'EEM_Event')
426 426
      */
427
-    public static function model_names_with_plural_routes($version){
427
+    public static function model_names_with_plural_routes($version) {
428 428
         $model_version_info = new ModelVersionInfo($version);
429 429
         $models_to_register = $model_version_info->modelsForRequestedVersion();
430 430
         //let's not bother having endpoints for extra metas
@@ -456,7 +456,7 @@  discard block
 block discarded – undo
456 456
         foreach (EED_Core_Rest_Api::model_names_with_plural_routes($version) as $model_name => $model_classname) {
457 457
             $model = \EE_Registry::instance()->load_model($model_name);
458 458
             //if this isn't a valid model then let's skip iterate to the next item in the loop.
459
-            if (! $model instanceof EEM_Base) {
459
+            if ( ! $model instanceof EEM_Base) {
460 460
                 continue;
461 461
             }
462 462
             //yes we could just register one route for ALL models, but then they wouldn't show up in the index
@@ -473,7 +473,7 @@  discard block
 block discarded – undo
473 473
                     'hidden_endpoint' => $hidden_endpoint,
474 474
                     'args'            => $this->_get_read_query_params($model, $version),
475 475
                     '_links'          => array(
476
-                        'self' => rest_url(EED_Core_Rest_Api::ee_api_namespace . $version . $singular_model_route),
476
+                        'self' => rest_url(EED_Core_Rest_Api::ee_api_namespace.$version.$singular_model_route),
477 477
                     ),
478 478
                 ),
479 479
                 'schema' => array(
@@ -496,11 +496,11 @@  discard block
 block discarded – undo
496 496
                     'args'            => $this->_get_response_selection_query_params($model, $version),
497 497
                 ),
498 498
             );
499
-            if( apply_filters(
499
+            if (apply_filters(
500 500
                 'FHEE__EED_Core_Rest_Api___get_model_route_data_for_version__add_write_endpoints',
501 501
                 EED_Core_Rest_Api::should_have_write_endpoints($model),
502 502
                 $model
503
-            )){
503
+            )) {
504 504
                 $model_routes[$plural_model_route][] = array(
505 505
                     'callback'        => array(
506 506
                         'EventEspresso\core\libraries\rest_api\controllers\model\Write',
@@ -590,7 +590,7 @@  discard block
 block discarded – undo
590 590
      */
591 591
     public static function get_entity_route($model, $id)
592 592
     {
593
-        return EED_Core_Rest_Api::get_collection_route($model). '/' . $id;
593
+        return EED_Core_Rest_Api::get_collection_route($model).'/'.$id;
594 594
     }
595 595
 
596 596
 
@@ -610,7 +610,7 @@  discard block
 block discarded – undo
610 610
             $relation_obj->get_other_model()->get_this_model_name(),
611 611
             $relation_obj
612 612
         );
613
-        return EED_Core_Rest_Api::get_entity_route($model, $id) . '/' . $related_model_name_endpoint_part;
613
+        return EED_Core_Rest_Api::get_entity_route($model, $id).'/'.$related_model_name_endpoint_part;
614 614
     }
615 615
 
616 616
 
@@ -622,8 +622,8 @@  discard block
 block discarded – undo
622 622
      * @param string $version
623 623
      * @return string
624 624
      */
625
-    public static function get_versioned_route_to($relative_route, $version = '4.8.36'){
626
-        return '/' . EED_Core_Rest_Api::ee_api_namespace . $version . '/' . $relative_route;
625
+    public static function get_versioned_route_to($relative_route, $version = '4.8.36') {
626
+        return '/'.EED_Core_Rest_Api::ee_api_namespace.$version.'/'.$relative_route;
627 627
     }
628 628
 
629 629
 
@@ -638,7 +638,7 @@  discard block
 block discarded – undo
638 638
     {
639 639
         $routes = array();
640 640
         foreach (self::versions_served() as $version => $hidden_endpoint) {
641
-            $routes[self::ee_api_namespace . $version] = $this->_get_rpc_route_data_for_version(
641
+            $routes[self::ee_api_namespace.$version] = $this->_get_rpc_route_data_for_version(
642 642
                 $version,
643 643
                 $hidden_endpoint
644 644
             );
@@ -879,7 +879,7 @@  discard block
 block discarded – undo
879 879
                     $field_obj->get_nicename(),
880 880
                     $field_name
881 881
                 );
882
-                $args_info[$field_name . '_gmt'] = $gmt_arg_info;
882
+                $args_info[$field_name.'_gmt'] = $gmt_arg_info;
883 883
             }
884 884
         }
885 885
         return $args_info;
@@ -900,10 +900,10 @@  discard block
 block discarded – undo
900 900
      * @throws InvalidInterfaceException
901 901
      * @throws InvalidDataTypeException
902 902
      */
903
-    public static function default_sanitize_callback( $value, WP_REST_Request $request, $param)
903
+    public static function default_sanitize_callback($value, WP_REST_Request $request, $param)
904 904
     {
905 905
         $attributes = $request->get_attributes();
906
-        if (! isset($attributes['args'][$param])
906
+        if ( ! isset($attributes['args'][$param])
907 907
             || ! is_array($attributes['args'][$param])) {
908 908
             $validation_result = true;
909 909
         } else {
@@ -912,7 +912,7 @@  discard block
 block discarded – undo
912 912
                     $value === ''
913 913
                     || $value === null
914 914
                 )
915
-                && (! isset($args['required'])
915
+                && ( ! isset($args['required'])
916 916
                     || $args['required'] === false
917 917
                 )
918 918
             ) {
@@ -922,7 +922,7 @@  discard block
 block discarded – undo
922 922
                 && $args['format'] === 'email'
923 923
             ) {
924 924
                 $validation_result = true;
925
-                if (! self::_validate_email($value)) {
925
+                if ( ! self::_validate_email($value)) {
926 926
                     $validation_result = new WP_Error(
927 927
                         'rest_invalid_param',
928 928
                         esc_html__(
@@ -952,7 +952,7 @@  discard block
 block discarded – undo
952 952
      * @throws InvalidInterfaceException
953 953
      * @throws InvalidDataTypeException
954 954
      */
955
-    protected static function _validate_email($email){
955
+    protected static function _validate_email($email) {
956 956
         try {
957 957
             EmailAddressFactory::create($email);
958 958
             return true;
@@ -973,7 +973,7 @@  discard block
 block discarded – undo
973 973
     {
974 974
         $config_routes = array();
975 975
         foreach (self::versions_served() as $version => $hidden_endpoint) {
976
-            $config_routes[self::ee_api_namespace . $version] = $this->_get_config_route_data_for_version(
976
+            $config_routes[self::ee_api_namespace.$version] = $this->_get_config_route_data_for_version(
977 977
                 $version,
978 978
                 $hidden_endpoint
979 979
             );
@@ -1030,7 +1030,7 @@  discard block
 block discarded – undo
1030 1030
     {
1031 1031
         $meta_routes = array();
1032 1032
         foreach (self::versions_served() as $version => $hidden_endpoint) {
1033
-            $meta_routes[self::ee_api_namespace . $version] = $this->_get_meta_route_data_for_version(
1033
+            $meta_routes[self::ee_api_namespace.$version] = $this->_get_meta_route_data_for_version(
1034 1034
                 $version,
1035 1035
                 $hidden_endpoint
1036 1036
             );
@@ -1084,7 +1084,7 @@  discard block
 block discarded – undo
1084 1084
             foreach ($relative_urls as $resource_name => $endpoints) {
1085 1085
                 foreach ($endpoints as $key => $endpoint) {
1086 1086
                     //skip schema and other route options
1087
-                    if (! is_numeric($key)) {
1087
+                    if ( ! is_numeric($key)) {
1088 1088
                         continue;
1089 1089
                     }
1090 1090
                     //by default, hide "hidden_endpoint"s, unless the request indicates
@@ -1094,8 +1094,8 @@  discard block
 block discarded – undo
1094 1094
                         ($force_show_ee_namespace !== '' && $force_show_ee_namespace !== $namespace)
1095 1095
                         || ($endpoint['hidden_endpoint'] && $force_show_ee_namespace === '')
1096 1096
                     ) {
1097
-                        $full_route = '/' . ltrim($namespace, '/');
1098
-                        $full_route .= '/' . ltrim($resource_name, '/');
1097
+                        $full_route = '/'.ltrim($namespace, '/');
1098
+                        $full_route .= '/'.ltrim($resource_name, '/');
1099 1099
                         unset($route_data[$full_route]);
1100 1100
                     }
1101 1101
                 }
Please login to merge, or discard this patch.
core/db_models/fields/EE_Email_Field.php 1 patch
Indentation   +31 added lines, -31 removed lines patch added patch discarded remove patch
@@ -16,38 +16,38 @@
 block discarded – undo
16 16
 {
17 17
 
18 18
 
19
-    /**
20
-     * @param string $table_column
21
-     * @param string $nice_name
22
-     * @param bool   $nullable
23
-     * @param null   $default_value
24
-     * @throws InvalidArgumentException
25
-     */
26
-    public function __construct($table_column, $nice_name, $nullable, $default_value = null)
27
-    {
28
-        parent::__construct($table_column, $nice_name, $nullable, $default_value);
29
-        $this->setSchemaFormat('email');
30
-    }
19
+	/**
20
+	 * @param string $table_column
21
+	 * @param string $nice_name
22
+	 * @param bool   $nullable
23
+	 * @param null   $default_value
24
+	 * @throws InvalidArgumentException
25
+	 */
26
+	public function __construct($table_column, $nice_name, $nullable, $default_value = null)
27
+	{
28
+		parent::__construct($table_column, $nice_name, $nullable, $default_value);
29
+		$this->setSchemaFormat('email');
30
+	}
31 31
 
32 32
 
33 33
 
34
-    /**
35
-     * In form inputs, we should have called htmlentities and addslashes() on form inputs,
36
-     * so we need to undo that on setting of these fields
37
-     *
38
-     * @param string $email_address
39
-     * @return string
40
-     * @throws InvalidArgumentException
41
-     * @throws InvalidInterfaceException
42
-     * @throws InvalidDataTypeException
43
-     */
44
-    public function prepare_for_set($email_address)
45
-    {
46
-        try {
47
-            $email_address = EmailAddressFactory::create($email_address);
48
-            return $email_address->get();
49
-        } catch (EmailValidationException $e) {
50
-            return '';
51
-        }
52
-    }
34
+	/**
35
+	 * In form inputs, we should have called htmlentities and addslashes() on form inputs,
36
+	 * so we need to undo that on setting of these fields
37
+	 *
38
+	 * @param string $email_address
39
+	 * @return string
40
+	 * @throws InvalidArgumentException
41
+	 * @throws InvalidInterfaceException
42
+	 * @throws InvalidDataTypeException
43
+	 */
44
+	public function prepare_for_set($email_address)
45
+	{
46
+		try {
47
+			$email_address = EmailAddressFactory::create($email_address);
48
+			return $email_address->get();
49
+		} catch (EmailValidationException $e) {
50
+			return '';
51
+		}
52
+	}
53 53
 }
Please login to merge, or discard this patch.
core/domain/services/factories/FactoryInterface.php 1 patch
Indentation   +5 added lines, -5 removed lines patch added patch discarded remove patch
@@ -15,11 +15,11 @@
 block discarded – undo
15 15
 interface FactoryInterface
16 16
 {
17 17
 
18
-    /**
19
-     * @param mixed $arguments
20
-     * @return mixed
21
-     */
22
-    public static function create($arguments);
18
+	/**
19
+	 * @param mixed $arguments
20
+	 * @return mixed
21
+	 */
22
+	public static function create($arguments);
23 23
 
24 24
 
25 25
 }
Please login to merge, or discard this patch.
core/domain/services/validation/email/strategies/InternationalDNS.php 2 patches
Indentation   +38 added lines, -38 removed lines patch added patch discarded remove patch
@@ -20,44 +20,44 @@
 block discarded – undo
20 20
 class InternationalDNS extends International
21 21
 {
22 22
 
23
-    /**
24
-     * Validates the email in teh same way as the parent, but also
25
-     * verifies the domain exists.
26
-     * @param string $email_address
27
-     * @return bool
28
-     * @throws EmailValidationException
29
-     */
30
-    public function validate($email_address)
31
-    {
32
-        parent::validate($email_address);
33
-        $domain = $this->getDomainPartOfEmail(
34
-            $email_address,
35
-            $this->getAtIndex($email_address)
36
-        );
37
-        if (! checkdnsrr($domain, 'MX')) {
38
-            // domain not found in MX records
39
-            throw new EmailValidationException(
40
-                __(
41
-                    // @codingStandardsIgnoreStart
42
-                    'Although the email address provided is formatted correctly, a valid "MX record" could not be located for that address and domain. Please enter a valid email address.',
43
-                    // @codingStandardsIgnoreEnd
44
-                    'event_espresso'
45
-                )
46
-            );
47
-        }
48
-        if (! checkdnsrr($domain, 'A')) {
49
-            // domain not found in A records
50
-            throw new EmailValidationException(
51
-                __(
52
-                    // @codingStandardsIgnoreStart
53
-                    'Although the email address provided is formatted correctly, a valid "A record" could not be located for that address and domain. Please enter a valid email address.',
54
-                    // @codingStandardsIgnoreEnd
55
-                    'event_espresso'
56
-                )
57
-            );
58
-        }
59
-        return true;
60
-    }
23
+	/**
24
+	 * Validates the email in teh same way as the parent, but also
25
+	 * verifies the domain exists.
26
+	 * @param string $email_address
27
+	 * @return bool
28
+	 * @throws EmailValidationException
29
+	 */
30
+	public function validate($email_address)
31
+	{
32
+		parent::validate($email_address);
33
+		$domain = $this->getDomainPartOfEmail(
34
+			$email_address,
35
+			$this->getAtIndex($email_address)
36
+		);
37
+		if (! checkdnsrr($domain, 'MX')) {
38
+			// domain not found in MX records
39
+			throw new EmailValidationException(
40
+				__(
41
+					// @codingStandardsIgnoreStart
42
+					'Although the email address provided is formatted correctly, a valid "MX record" could not be located for that address and domain. Please enter a valid email address.',
43
+					// @codingStandardsIgnoreEnd
44
+					'event_espresso'
45
+				)
46
+			);
47
+		}
48
+		if (! checkdnsrr($domain, 'A')) {
49
+			// domain not found in A records
50
+			throw new EmailValidationException(
51
+				__(
52
+					// @codingStandardsIgnoreStart
53
+					'Although the email address provided is formatted correctly, a valid "A record" could not be located for that address and domain. Please enter a valid email address.',
54
+					// @codingStandardsIgnoreEnd
55
+					'event_espresso'
56
+				)
57
+			);
58
+		}
59
+		return true;
60
+	}
61 61
 
62 62
 
63 63
 }
Please login to merge, or discard this patch.
Spacing   +2 added lines, -2 removed lines patch added patch discarded remove patch
@@ -34,7 +34,7 @@  discard block
 block discarded – undo
34 34
             $email_address,
35 35
             $this->getAtIndex($email_address)
36 36
         );
37
-        if (! checkdnsrr($domain, 'MX')) {
37
+        if ( ! checkdnsrr($domain, 'MX')) {
38 38
             // domain not found in MX records
39 39
             throw new EmailValidationException(
40 40
                 __(
@@ -45,7 +45,7 @@  discard block
 block discarded – undo
45 45
                 )
46 46
             );
47 47
         }
48
-        if (! checkdnsrr($domain, 'A')) {
48
+        if ( ! checkdnsrr($domain, 'A')) {
49 49
             // domain not found in A records
50 50
             throw new EmailValidationException(
51 51
                 __(
Please login to merge, or discard this patch.
core/domain/services/validation/email/strategies/International.php 1 patch
Indentation   +22 added lines, -22 removed lines patch added patch discarded remove patch
@@ -20,28 +20,28 @@
 block discarded – undo
20 20
 class International extends Basic
21 21
 {
22 22
 
23
-    /**
24
-     * @param string $email_address
25
-     * @return bool
26
-     * @throws \EventEspresso\core\domain\services\validation\email\EmailValidationException
27
-     */
28
-    public function validate($email_address)
29
-    {
30
-        parent::validate($email_address);
31
-        if (// plz see http://stackoverflow.com/a/24817336 re: the following regex
32
-            ! preg_match(
33
-                // @codingStandardsIgnoreStart
34
-                '/^(?!\.)((?!.*\.{2})[a-zA-Z0-9\x{0080}-\x{00FF}\x{0100}-\x{017F}\x{0180}-\x{024F}\x{0250}-\x{02AF}\x{0300}-\x{036F}\x{0370}-\x{03FF}\x{0400}-\x{04FF}\x{0500}-\x{052F}\x{0530}-\x{058F}\x{0590}-\x{05FF}\x{0600}-\x{06FF}\x{0700}-\x{074F}\x{0750}-\x{077F}\x{0780}-\x{07BF}\x{07C0}-\x{07FF}\x{0900}-\x{097F}\x{0980}-\x{09FF}\x{0A00}-\x{0A7F}\x{0A80}-\x{0AFF}\x{0B00}-\x{0B7F}\x{0B80}-\x{0BFF}\x{0C00}-\x{0C7F}\x{0C80}-\x{0CFF}\x{0D00}-\x{0D7F}\x{0D80}-\x{0DFF}\x{0E00}-\x{0E7F}\x{0E80}-\x{0EFF}\x{0F00}-\x{0FFF}\x{1000}-\x{109F}\x{10A0}-\x{10FF}\x{1100}-\x{11FF}\x{1200}-\x{137F}\x{1380}-\x{139F}\x{13A0}-\x{13FF}\x{1400}-\x{167F}\x{1680}-\x{169F}\x{16A0}-\x{16FF}\x{1700}-\x{171F}\x{1720}-\x{173F}\x{1740}-\x{175F}\x{1760}-\x{177F}\x{1780}-\x{17FF}\x{1800}-\x{18AF}\x{1900}-\x{194F}\x{1950}-\x{197F}\x{1980}-\x{19DF}\x{19E0}-\x{19FF}\x{1A00}-\x{1A1F}\x{1B00}-\x{1B7F}\x{1D00}-\x{1D7F}\x{1D80}-\x{1DBF}\x{1DC0}-\x{1DFF}\x{1E00}-\x{1EFF}\x{1F00}-\x{1FFF}\x{20D0}-\x{20FF}\x{2100}-\x{214F}\x{2C00}-\x{2C5F}\x{2C60}-\x{2C7F}\x{2C80}-\x{2CFF}\x{2D00}-\x{2D2F}\x{2D30}-\x{2D7F}\x{2D80}-\x{2DDF}\x{2F00}-\x{2FDF}\x{2FF0}-\x{2FFF}\x{3040}-\x{309F}\x{30A0}-\x{30FF}\x{3100}-\x{312F}\x{3130}-\x{318F}\x{3190}-\x{319F}\x{31C0}-\x{31EF}\x{31F0}-\x{31FF}\x{3200}-\x{32FF}\x{3300}-\x{33FF}\x{3400}-\x{4DBF}\x{4DC0}-\x{4DFF}\x{4E00}-\x{9FFF}\x{A000}-\x{A48F}\x{A490}-\x{A4CF}\x{A700}-\x{A71F}\x{A800}-\x{A82F}\x{A840}-\x{A87F}\x{AC00}-\x{D7AF}\x{F900}-\x{FAFF}\.!#$%&\'*+-\/=?^_`{|}~\-\d]+)@(?!\.)([a-zA-Z0-9\x{0080}-\x{00FF}\x{0100}-\x{017F}\x{0180}-\x{024F}\x{0250}-\x{02AF}\x{0300}-\x{036F}\x{0370}-\x{03FF}\x{0400}-\x{04FF}\x{0500}-\x{052F}\x{0530}-\x{058F}\x{0590}-\x{05FF}\x{0600}-\x{06FF}\x{0700}-\x{074F}\x{0750}-\x{077F}\x{0780}-\x{07BF}\x{07C0}-\x{07FF}\x{0900}-\x{097F}\x{0980}-\x{09FF}\x{0A00}-\x{0A7F}\x{0A80}-\x{0AFF}\x{0B00}-\x{0B7F}\x{0B80}-\x{0BFF}\x{0C00}-\x{0C7F}\x{0C80}-\x{0CFF}\x{0D00}-\x{0D7F}\x{0D80}-\x{0DFF}\x{0E00}-\x{0E7F}\x{0E80}-\x{0EFF}\x{0F00}-\x{0FFF}\x{1000}-\x{109F}\x{10A0}-\x{10FF}\x{1100}-\x{11FF}\x{1200}-\x{137F}\x{1380}-\x{139F}\x{13A0}-\x{13FF}\x{1400}-\x{167F}\x{1680}-\x{169F}\x{16A0}-\x{16FF}\x{1700}-\x{171F}\x{1720}-\x{173F}\x{1740}-\x{175F}\x{1760}-\x{177F}\x{1780}-\x{17FF}\x{1800}-\x{18AF}\x{1900}-\x{194F}\x{1950}-\x{197F}\x{1980}-\x{19DF}\x{19E0}-\x{19FF}\x{1A00}-\x{1A1F}\x{1B00}-\x{1B7F}\x{1D00}-\x{1D7F}\x{1D80}-\x{1DBF}\x{1DC0}-\x{1DFF}\x{1E00}-\x{1EFF}\x{1F00}-\x{1FFF}\x{20D0}-\x{20FF}\x{2100}-\x{214F}\x{2C00}-\x{2C5F}\x{2C60}-\x{2C7F}\x{2C80}-\x{2CFF}\x{2D00}-\x{2D2F}\x{2D30}-\x{2D7F}\x{2D80}-\x{2DDF}\x{2F00}-\x{2FDF}\x{2FF0}-\x{2FFF}\x{3040}-\x{309F}\x{30A0}-\x{30FF}\x{3100}-\x{312F}\x{3130}-\x{318F}\x{3190}-\x{319F}\x{31C0}-\x{31EF}\x{31F0}-\x{31FF}\x{3200}-\x{32FF}\x{3300}-\x{33FF}\x{3400}-\x{4DBF}\x{4DC0}-\x{4DFF}\x{4E00}-\x{9FFF}\x{A000}-\x{A48F}\x{A490}-\x{A4CF}\x{A700}-\x{A71F}\x{A800}-\x{A82F}\x{A840}-\x{A87F}\x{AC00}-\x{D7AF}\x{F900}-\x{FAFF}\-\.\d]+)((\.([a-zA-Z\x{0080}-\x{00FF}\x{0100}-\x{017F}\x{0180}-\x{024F}\x{0250}-\x{02AF}\x{0300}-\x{036F}\x{0370}-\x{03FF}\x{0400}-\x{04FF}\x{0500}-\x{052F}\x{0530}-\x{058F}\x{0590}-\x{05FF}\x{0600}-\x{06FF}\x{0700}-\x{074F}\x{0750}-\x{077F}\x{0780}-\x{07BF}\x{07C0}-\x{07FF}\x{0900}-\x{097F}\x{0980}-\x{09FF}\x{0A00}-\x{0A7F}\x{0A80}-\x{0AFF}\x{0B00}-\x{0B7F}\x{0B80}-\x{0BFF}\x{0C00}-\x{0C7F}\x{0C80}-\x{0CFF}\x{0D00}-\x{0D7F}\x{0D80}-\x{0DFF}\x{0E00}-\x{0E7F}\x{0E80}-\x{0EFF}\x{0F00}-\x{0FFF}\x{1000}-\x{109F}\x{10A0}-\x{10FF}\x{1100}-\x{11FF}\x{1200}-\x{137F}\x{1380}-\x{139F}\x{13A0}-\x{13FF}\x{1400}-\x{167F}\x{1680}-\x{169F}\x{16A0}-\x{16FF}\x{1700}-\x{171F}\x{1720}-\x{173F}\x{1740}-\x{175F}\x{1760}-\x{177F}\x{1780}-\x{17FF}\x{1800}-\x{18AF}\x{1900}-\x{194F}\x{1950}-\x{197F}\x{1980}-\x{19DF}\x{19E0}-\x{19FF}\x{1A00}-\x{1A1F}\x{1B00}-\x{1B7F}\x{1D00}-\x{1D7F}\x{1D80}-\x{1DBF}\x{1DC0}-\x{1DFF}\x{1E00}-\x{1EFF}\x{1F00}-\x{1FFF}\x{20D0}-\x{20FF}\x{2100}-\x{214F}\x{2C00}-\x{2C5F}\x{2C60}-\x{2C7F}\x{2C80}-\x{2CFF}\x{2D00}-\x{2D2F}\x{2D30}-\x{2D7F}\x{2D80}-\x{2DDF}\x{2F00}-\x{2FDF}\x{2FF0}-\x{2FFF}\x{3040}-\x{309F}\x{30A0}-\x{30FF}\x{3100}-\x{312F}\x{3130}-\x{318F}\x{3190}-\x{319F}\x{31C0}-\x{31EF}\x{31F0}-\x{31FF}\x{3200}-\x{32FF}\x{3300}-\x{33FF}\x{3400}-\x{4DBF}\x{4DC0}-\x{4DFF}\x{4E00}-\x{9FFF}\x{A000}-\x{A48F}\x{A490}-\x{A4CF}\x{A700}-\x{A71F}\x{A800}-\x{A82F}\x{A840}-\x{A87F}\x{AC00}-\x{D7AF}\x{F900}-\x{FAFF}]){2,63})+)$/u',
35
-                // @codingStandardsIgnoreEnd
36
-                $email_address
37
-            )
38
-        ) {
39
-            throw new EmailValidationException(
40
-                esc_html__('Email address is invalid.', 'event_espresso')
41
-            );
42
-        }
43
-        return true;
44
-    }
23
+	/**
24
+	 * @param string $email_address
25
+	 * @return bool
26
+	 * @throws \EventEspresso\core\domain\services\validation\email\EmailValidationException
27
+	 */
28
+	public function validate($email_address)
29
+	{
30
+		parent::validate($email_address);
31
+		if (// plz see http://stackoverflow.com/a/24817336 re: the following regex
32
+			! preg_match(
33
+				// @codingStandardsIgnoreStart
34
+				'/^(?!\.)((?!.*\.{2})[a-zA-Z0-9\x{0080}-\x{00FF}\x{0100}-\x{017F}\x{0180}-\x{024F}\x{0250}-\x{02AF}\x{0300}-\x{036F}\x{0370}-\x{03FF}\x{0400}-\x{04FF}\x{0500}-\x{052F}\x{0530}-\x{058F}\x{0590}-\x{05FF}\x{0600}-\x{06FF}\x{0700}-\x{074F}\x{0750}-\x{077F}\x{0780}-\x{07BF}\x{07C0}-\x{07FF}\x{0900}-\x{097F}\x{0980}-\x{09FF}\x{0A00}-\x{0A7F}\x{0A80}-\x{0AFF}\x{0B00}-\x{0B7F}\x{0B80}-\x{0BFF}\x{0C00}-\x{0C7F}\x{0C80}-\x{0CFF}\x{0D00}-\x{0D7F}\x{0D80}-\x{0DFF}\x{0E00}-\x{0E7F}\x{0E80}-\x{0EFF}\x{0F00}-\x{0FFF}\x{1000}-\x{109F}\x{10A0}-\x{10FF}\x{1100}-\x{11FF}\x{1200}-\x{137F}\x{1380}-\x{139F}\x{13A0}-\x{13FF}\x{1400}-\x{167F}\x{1680}-\x{169F}\x{16A0}-\x{16FF}\x{1700}-\x{171F}\x{1720}-\x{173F}\x{1740}-\x{175F}\x{1760}-\x{177F}\x{1780}-\x{17FF}\x{1800}-\x{18AF}\x{1900}-\x{194F}\x{1950}-\x{197F}\x{1980}-\x{19DF}\x{19E0}-\x{19FF}\x{1A00}-\x{1A1F}\x{1B00}-\x{1B7F}\x{1D00}-\x{1D7F}\x{1D80}-\x{1DBF}\x{1DC0}-\x{1DFF}\x{1E00}-\x{1EFF}\x{1F00}-\x{1FFF}\x{20D0}-\x{20FF}\x{2100}-\x{214F}\x{2C00}-\x{2C5F}\x{2C60}-\x{2C7F}\x{2C80}-\x{2CFF}\x{2D00}-\x{2D2F}\x{2D30}-\x{2D7F}\x{2D80}-\x{2DDF}\x{2F00}-\x{2FDF}\x{2FF0}-\x{2FFF}\x{3040}-\x{309F}\x{30A0}-\x{30FF}\x{3100}-\x{312F}\x{3130}-\x{318F}\x{3190}-\x{319F}\x{31C0}-\x{31EF}\x{31F0}-\x{31FF}\x{3200}-\x{32FF}\x{3300}-\x{33FF}\x{3400}-\x{4DBF}\x{4DC0}-\x{4DFF}\x{4E00}-\x{9FFF}\x{A000}-\x{A48F}\x{A490}-\x{A4CF}\x{A700}-\x{A71F}\x{A800}-\x{A82F}\x{A840}-\x{A87F}\x{AC00}-\x{D7AF}\x{F900}-\x{FAFF}\.!#$%&\'*+-\/=?^_`{|}~\-\d]+)@(?!\.)([a-zA-Z0-9\x{0080}-\x{00FF}\x{0100}-\x{017F}\x{0180}-\x{024F}\x{0250}-\x{02AF}\x{0300}-\x{036F}\x{0370}-\x{03FF}\x{0400}-\x{04FF}\x{0500}-\x{052F}\x{0530}-\x{058F}\x{0590}-\x{05FF}\x{0600}-\x{06FF}\x{0700}-\x{074F}\x{0750}-\x{077F}\x{0780}-\x{07BF}\x{07C0}-\x{07FF}\x{0900}-\x{097F}\x{0980}-\x{09FF}\x{0A00}-\x{0A7F}\x{0A80}-\x{0AFF}\x{0B00}-\x{0B7F}\x{0B80}-\x{0BFF}\x{0C00}-\x{0C7F}\x{0C80}-\x{0CFF}\x{0D00}-\x{0D7F}\x{0D80}-\x{0DFF}\x{0E00}-\x{0E7F}\x{0E80}-\x{0EFF}\x{0F00}-\x{0FFF}\x{1000}-\x{109F}\x{10A0}-\x{10FF}\x{1100}-\x{11FF}\x{1200}-\x{137F}\x{1380}-\x{139F}\x{13A0}-\x{13FF}\x{1400}-\x{167F}\x{1680}-\x{169F}\x{16A0}-\x{16FF}\x{1700}-\x{171F}\x{1720}-\x{173F}\x{1740}-\x{175F}\x{1760}-\x{177F}\x{1780}-\x{17FF}\x{1800}-\x{18AF}\x{1900}-\x{194F}\x{1950}-\x{197F}\x{1980}-\x{19DF}\x{19E0}-\x{19FF}\x{1A00}-\x{1A1F}\x{1B00}-\x{1B7F}\x{1D00}-\x{1D7F}\x{1D80}-\x{1DBF}\x{1DC0}-\x{1DFF}\x{1E00}-\x{1EFF}\x{1F00}-\x{1FFF}\x{20D0}-\x{20FF}\x{2100}-\x{214F}\x{2C00}-\x{2C5F}\x{2C60}-\x{2C7F}\x{2C80}-\x{2CFF}\x{2D00}-\x{2D2F}\x{2D30}-\x{2D7F}\x{2D80}-\x{2DDF}\x{2F00}-\x{2FDF}\x{2FF0}-\x{2FFF}\x{3040}-\x{309F}\x{30A0}-\x{30FF}\x{3100}-\x{312F}\x{3130}-\x{318F}\x{3190}-\x{319F}\x{31C0}-\x{31EF}\x{31F0}-\x{31FF}\x{3200}-\x{32FF}\x{3300}-\x{33FF}\x{3400}-\x{4DBF}\x{4DC0}-\x{4DFF}\x{4E00}-\x{9FFF}\x{A000}-\x{A48F}\x{A490}-\x{A4CF}\x{A700}-\x{A71F}\x{A800}-\x{A82F}\x{A840}-\x{A87F}\x{AC00}-\x{D7AF}\x{F900}-\x{FAFF}\-\.\d]+)((\.([a-zA-Z\x{0080}-\x{00FF}\x{0100}-\x{017F}\x{0180}-\x{024F}\x{0250}-\x{02AF}\x{0300}-\x{036F}\x{0370}-\x{03FF}\x{0400}-\x{04FF}\x{0500}-\x{052F}\x{0530}-\x{058F}\x{0590}-\x{05FF}\x{0600}-\x{06FF}\x{0700}-\x{074F}\x{0750}-\x{077F}\x{0780}-\x{07BF}\x{07C0}-\x{07FF}\x{0900}-\x{097F}\x{0980}-\x{09FF}\x{0A00}-\x{0A7F}\x{0A80}-\x{0AFF}\x{0B00}-\x{0B7F}\x{0B80}-\x{0BFF}\x{0C00}-\x{0C7F}\x{0C80}-\x{0CFF}\x{0D00}-\x{0D7F}\x{0D80}-\x{0DFF}\x{0E00}-\x{0E7F}\x{0E80}-\x{0EFF}\x{0F00}-\x{0FFF}\x{1000}-\x{109F}\x{10A0}-\x{10FF}\x{1100}-\x{11FF}\x{1200}-\x{137F}\x{1380}-\x{139F}\x{13A0}-\x{13FF}\x{1400}-\x{167F}\x{1680}-\x{169F}\x{16A0}-\x{16FF}\x{1700}-\x{171F}\x{1720}-\x{173F}\x{1740}-\x{175F}\x{1760}-\x{177F}\x{1780}-\x{17FF}\x{1800}-\x{18AF}\x{1900}-\x{194F}\x{1950}-\x{197F}\x{1980}-\x{19DF}\x{19E0}-\x{19FF}\x{1A00}-\x{1A1F}\x{1B00}-\x{1B7F}\x{1D00}-\x{1D7F}\x{1D80}-\x{1DBF}\x{1DC0}-\x{1DFF}\x{1E00}-\x{1EFF}\x{1F00}-\x{1FFF}\x{20D0}-\x{20FF}\x{2100}-\x{214F}\x{2C00}-\x{2C5F}\x{2C60}-\x{2C7F}\x{2C80}-\x{2CFF}\x{2D00}-\x{2D2F}\x{2D30}-\x{2D7F}\x{2D80}-\x{2DDF}\x{2F00}-\x{2FDF}\x{2FF0}-\x{2FFF}\x{3040}-\x{309F}\x{30A0}-\x{30FF}\x{3100}-\x{312F}\x{3130}-\x{318F}\x{3190}-\x{319F}\x{31C0}-\x{31EF}\x{31F0}-\x{31FF}\x{3200}-\x{32FF}\x{3300}-\x{33FF}\x{3400}-\x{4DBF}\x{4DC0}-\x{4DFF}\x{4E00}-\x{9FFF}\x{A000}-\x{A48F}\x{A490}-\x{A4CF}\x{A700}-\x{A71F}\x{A800}-\x{A82F}\x{A840}-\x{A87F}\x{AC00}-\x{D7AF}\x{F900}-\x{FAFF}]){2,63})+)$/u',
35
+				// @codingStandardsIgnoreEnd
36
+				$email_address
37
+			)
38
+		) {
39
+			throw new EmailValidationException(
40
+				esc_html__('Email address is invalid.', 'event_espresso')
41
+			);
42
+		}
43
+		return true;
44
+	}
45 45
 }
46 46
 // End of file International.php
47 47
 // Location: core\services\validation/International.php
Please login to merge, or discard this patch.
core/domain/services/validation/email/strategies/Basic.php 2 patches
Indentation   +111 added lines, -111 removed lines patch added patch discarded remove patch
@@ -20,117 +20,117 @@
 block discarded – undo
20 20
 class Basic implements EmailValidatorInterface
21 21
 {
22 22
 
23
-    /**
24
-     * @param string $email_address
25
-     * @return bool
26
-     * @throws EmailValidationException
27
-     */
28
-    public function validate($email_address)
29
-    {
30
-        if (! preg_match('/^.+\@\S+$/', $email_address)) {
31
-            // email not in correct {string}@{string} format
32
-            throw new EmailValidationException(
33
-                esc_html__('Email does not have the required @ sign.', 'event_espresso')
34
-            );
35
-        }
36
-        $atIndex = $this->getAtIndex($email_address);
37
-        $local = $this->getLocalPartOfEmail($email_address, $atIndex);
38
-        $localLen = strlen($local);
39
-        if ($localLen < 1) {
40
-            //no local part
41
-            throw new EmailValidationException(
42
-                esc_html__('Email local-part (before the @) is required.', 'event_espresso')
43
-            );
44
-        }
45
-        if ($localLen > 64) {
46
-            // local part length exceeded
47
-            throw new EmailValidationException(
48
-                esc_html__('Email local-part (before the @) is too long.', 'event_espresso')
49
-            );
50
-        }
51
-        if ($local[0] === '.') {
52
-            // local part starts with '.'
53
-            throw new EmailValidationException(
54
-                esc_html__('Email local-part (before the @) must not begin with a period.', 'event_espresso')
55
-            );
56
-        }
57
-        if ($local[$localLen - 1] === '.') {
58
-            // local part starts or ends with '.'
59
-            throw new EmailValidationException(
60
-                esc_html__('Email local-part (before the @) must not end with a period.', 'event_espresso')
61
-            );
62
-        }
63
-        if (preg_match('/\\.\\./', $local)) {
64
-            // local part has two consecutive dots
65
-            throw new EmailValidationException(
66
-                esc_html__(
67
-                    'Email local-part (before the @) must not have two consecutive periods.',
68
-                    'event_espresso'
69
-                )
70
-            );
71
-        }
72
-        $domain = $this->getDomainPartOfEmail($email_address, $atIndex);
73
-        $domainLen = strlen($domain);
74
-        if ($domainLen < 1) {
75
-            throw new EmailValidationException(
76
-                esc_html__('Email domain (after the @) is required.', 'event_espresso')
77
-            );
78
-        }
79
-        if ($domainLen > 255) {
80
-            // domain part length exceeded
81
-            throw new EmailValidationException(
82
-                esc_html__('Email domain (after the @) is too long.', 'event_espresso')
83
-            );
84
-        }
85
-        if (preg_match('/\\.\\./', $domain)) {
86
-            // domain part has two consecutive dots
87
-            throw new EmailValidationException(
88
-                esc_html__('Email domain (after the @) must not have two consecutive periods.', 'event_espresso')
89
-            );
90
-        }
91
-        return true;
92
-    }
93
-
94
-
95
-
96
-    /**
97
-     * returns the location of the @ symbol
98
-     *
99
-     * @param string $email_address
100
-     * @return bool|string
101
-     */
102
-    protected function getAtIndex($email_address)
103
-    {
104
-        return strrpos($email_address, '@');
105
-    }
106
-
107
-
108
-
109
-    /**
110
-     * Gets the local part of the email
111
-     *
112
-     * @param string $email_address
113
-     * @param bool|int $atIndex
114
-     * @return bool|string
115
-     */
116
-    protected function getLocalPartOfEmail($email_address, $atIndex)
117
-    {
118
-        return substr($email_address, 0, $atIndex);
119
-    }
120
-
121
-
122
-
123
-    /**
124
-     * Gets the domain part of the email
125
-     *
126
-     * @param string   $email_address
127
-     * @param bool|int $atIndex
128
-     * @return bool|string
129
-     */
130
-    protected function getDomainPartOfEmail($email_address, $atIndex)
131
-    {
132
-        return substr($email_address, $atIndex + 1);
133
-    }
23
+	/**
24
+	 * @param string $email_address
25
+	 * @return bool
26
+	 * @throws EmailValidationException
27
+	 */
28
+	public function validate($email_address)
29
+	{
30
+		if (! preg_match('/^.+\@\S+$/', $email_address)) {
31
+			// email not in correct {string}@{string} format
32
+			throw new EmailValidationException(
33
+				esc_html__('Email does not have the required @ sign.', 'event_espresso')
34
+			);
35
+		}
36
+		$atIndex = $this->getAtIndex($email_address);
37
+		$local = $this->getLocalPartOfEmail($email_address, $atIndex);
38
+		$localLen = strlen($local);
39
+		if ($localLen < 1) {
40
+			//no local part
41
+			throw new EmailValidationException(
42
+				esc_html__('Email local-part (before the @) is required.', 'event_espresso')
43
+			);
44
+		}
45
+		if ($localLen > 64) {
46
+			// local part length exceeded
47
+			throw new EmailValidationException(
48
+				esc_html__('Email local-part (before the @) is too long.', 'event_espresso')
49
+			);
50
+		}
51
+		if ($local[0] === '.') {
52
+			// local part starts with '.'
53
+			throw new EmailValidationException(
54
+				esc_html__('Email local-part (before the @) must not begin with a period.', 'event_espresso')
55
+			);
56
+		}
57
+		if ($local[$localLen - 1] === '.') {
58
+			// local part starts or ends with '.'
59
+			throw new EmailValidationException(
60
+				esc_html__('Email local-part (before the @) must not end with a period.', 'event_espresso')
61
+			);
62
+		}
63
+		if (preg_match('/\\.\\./', $local)) {
64
+			// local part has two consecutive dots
65
+			throw new EmailValidationException(
66
+				esc_html__(
67
+					'Email local-part (before the @) must not have two consecutive periods.',
68
+					'event_espresso'
69
+				)
70
+			);
71
+		}
72
+		$domain = $this->getDomainPartOfEmail($email_address, $atIndex);
73
+		$domainLen = strlen($domain);
74
+		if ($domainLen < 1) {
75
+			throw new EmailValidationException(
76
+				esc_html__('Email domain (after the @) is required.', 'event_espresso')
77
+			);
78
+		}
79
+		if ($domainLen > 255) {
80
+			// domain part length exceeded
81
+			throw new EmailValidationException(
82
+				esc_html__('Email domain (after the @) is too long.', 'event_espresso')
83
+			);
84
+		}
85
+		if (preg_match('/\\.\\./', $domain)) {
86
+			// domain part has two consecutive dots
87
+			throw new EmailValidationException(
88
+				esc_html__('Email domain (after the @) must not have two consecutive periods.', 'event_espresso')
89
+			);
90
+		}
91
+		return true;
92
+	}
93
+
94
+
95
+
96
+	/**
97
+	 * returns the location of the @ symbol
98
+	 *
99
+	 * @param string $email_address
100
+	 * @return bool|string
101
+	 */
102
+	protected function getAtIndex($email_address)
103
+	{
104
+		return strrpos($email_address, '@');
105
+	}
106
+
107
+
108
+
109
+	/**
110
+	 * Gets the local part of the email
111
+	 *
112
+	 * @param string $email_address
113
+	 * @param bool|int $atIndex
114
+	 * @return bool|string
115
+	 */
116
+	protected function getLocalPartOfEmail($email_address, $atIndex)
117
+	{
118
+		return substr($email_address, 0, $atIndex);
119
+	}
120
+
121
+
122
+
123
+	/**
124
+	 * Gets the domain part of the email
125
+	 *
126
+	 * @param string   $email_address
127
+	 * @param bool|int $atIndex
128
+	 * @return bool|string
129
+	 */
130
+	protected function getDomainPartOfEmail($email_address, $atIndex)
131
+	{
132
+		return substr($email_address, $atIndex + 1);
133
+	}
134 134
 }
135 135
 // End of file Basic.php
136 136
 // Location: core\services\validation/Basic.php
Please login to merge, or discard this patch.
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -27,7 +27,7 @@
 block discarded – undo
27 27
      */
28 28
     public function validate($email_address)
29 29
     {
30
-        if (! preg_match('/^.+\@\S+$/', $email_address)) {
30
+        if ( ! preg_match('/^.+\@\S+$/', $email_address)) {
31 31
             // email not in correct {string}@{string} format
32 32
             throw new EmailValidationException(
33 33
                 esc_html__('Email does not have the required @ sign.', 'event_espresso')
Please login to merge, or discard this patch.
core/domain/services/validation/email/strategies/WordPress.php 2 patches
Indentation   +16 added lines, -16 removed lines patch added patch discarded remove patch
@@ -20,22 +20,22 @@
 block discarded – undo
20 20
 class WordPress extends Basic
21 21
 {
22 22
 
23
-    /**
24
-     *
25
-     * @param string $email_address
26
-     * @return boolean
27
-     * @throws EmailValidationException
28
-     */
29
-    public function validate($email_address)
30
-    {
31
-        parent::validate($email_address);
32
-        if( ! is_email($email_address)){
33
-            throw new EmailValidationException(
34
-                esc_html__('The email address provided is not valid.', 'event_espresso')
35
-            );
36
-        }
37
-        return true;
38
-    }
23
+	/**
24
+	 *
25
+	 * @param string $email_address
26
+	 * @return boolean
27
+	 * @throws EmailValidationException
28
+	 */
29
+	public function validate($email_address)
30
+	{
31
+		parent::validate($email_address);
32
+		if( ! is_email($email_address)){
33
+			throw new EmailValidationException(
34
+				esc_html__('The email address provided is not valid.', 'event_espresso')
35
+			);
36
+		}
37
+		return true;
38
+	}
39 39
 
40 40
 
41 41
 }
Please login to merge, or discard this patch.
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -29,7 +29,7 @@
 block discarded – undo
29 29
     public function validate($email_address)
30 30
     {
31 31
         parent::validate($email_address);
32
-        if( ! is_email($email_address)){
32
+        if ( ! is_email($email_address)) {
33 33
             throw new EmailValidationException(
34 34
                 esc_html__('The email address provided is not valid.', 'event_espresso')
35 35
             );
Please login to merge, or discard this patch.