Completed
Branch FET/rest-relation-endpoints (02db8d)
by
unknown
27:05 queued 18:22
created
core/libraries/rest_api/controllers/model/Read.php 3 patches
Doc Comments   +3 added lines, -3 removed lines patch added patch discarded remove patch
@@ -723,7 +723,7 @@  discard block
 block discarded – undo
723 723
      * @since 4.9.74.p
724 724
      * @param $model
725 725
      * @param $results_so_far
726
-     * @param $protected
726
+     * @param boolean $protected
727 727
      * @return array results
728 728
      */
729 729
     protected function addProtectedProperty(EEM_Base $model, $results_so_far, $protected)
@@ -1262,7 +1262,7 @@  discard block
 block discarded – undo
1262 1262
      * Also, this method's contents might be candidate for moving to Model_Data_Translator
1263 1263
      *
1264 1264
      * @param EEM_Base $model
1265
-     * @param array    $query_parameters  from $_GET parameter @see Read:handle_request_get_all
1265
+     * @param array    $query_params  from $_GET parameter @see Read:handle_request_get_all
1266 1266
      * @return array model query params (@see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md#0-where-conditions)
1267 1267
      *                                    or FALSE to indicate that absolutely no results should be returned
1268 1268
      * @throws EE_Error
@@ -1519,7 +1519,7 @@  discard block
 block discarded – undo
1519 1519
      *
1520 1520
      * @param EEM_Base $model
1521 1521
      * @param WP_REST_Request $request
1522
-     * @param null $context
1522
+     * @param string $context
1523 1523
      * @return array
1524 1524
      * @throws EE_Error
1525 1525
      */
Please login to merge, or discard this patch.
Indentation   +1548 added lines, -1548 removed lines patch added patch discarded remove patch
@@ -45,1552 +45,1552 @@
 block discarded – undo
45 45
 {
46 46
 
47 47
 
48
-    /**
49
-     * @var CalculatedModelFields
50
-     */
51
-    protected $fields_calculator;
52
-
53
-
54
-    /**
55
-     * Read constructor.
56
-     * @param CalculatedModelFields $fields_calculator
57
-     */
58
-    public function __construct(CalculatedModelFields $fields_calculator)
59
-    {
60
-        parent::__construct();
61
-        $this->fields_calculator = $fields_calculator;
62
-    }
63
-
64
-
65
-    /**
66
-     * Handles requests to get all (or a filtered subset) of entities for a particular model
67
-     *
68
-     * @param WP_REST_Request $request
69
-     * @param string $version
70
-     * @param string $model_name
71
-     * @return WP_REST_Response|WP_Error
72
-     * @throws InvalidArgumentException
73
-     * @throws InvalidDataTypeException
74
-     * @throws InvalidInterfaceException
75
-     */
76
-    public static function handleRequestGetAll(WP_REST_Request $request, $version, $model_name)
77
-    {
78
-        $controller = LoaderFactory::getLoader()->getNew('EventEspresso\core\libraries\rest_api\controllers\model\Read');
79
-        try {
80
-            $controller->setRequestedVersion($version);
81
-            if (! $controller->getModelVersionInfo()->isModelNameInThisVersion($model_name)) {
82
-                return $controller->sendResponse(
83
-                    new WP_Error(
84
-                        'endpoint_parsing_error',
85
-                        sprintf(
86
-                            __(
87
-                                'There is no model for endpoint %s. Please contact event espresso support',
88
-                                'event_espresso'
89
-                            ),
90
-                            $model_name
91
-                        )
92
-                    )
93
-                );
94
-            }
95
-            return $controller->sendResponse(
96
-                $controller->getEntitiesFromModel(
97
-                    $controller->getModelVersionInfo()->loadModel($model_name),
98
-                    $request
99
-                )
100
-            );
101
-        } catch (Exception $e) {
102
-            return $controller->sendResponse($e);
103
-        }
104
-    }
105
-
106
-
107
-    /**
108
-     * Prepares and returns schema for any OPTIONS request.
109
-     *
110
-     * @param string $version The API endpoint version being used.
111
-     * @param string $model_name Something like `Event` or `Registration`
112
-     * @return array
113
-     * @throws InvalidArgumentException
114
-     * @throws InvalidDataTypeException
115
-     * @throws InvalidInterfaceException
116
-     */
117
-    public static function handleSchemaRequest($version, $model_name)
118
-    {
119
-        $controller = LoaderFactory::getLoader()->getNew('EventEspresso\core\libraries\rest_api\controllers\model\Read');
120
-        try {
121
-            $controller->setRequestedVersion($version);
122
-            if (! $controller->getModelVersionInfo()->isModelNameInThisVersion($model_name)) {
123
-                return array();
124
-            }
125
-            // get the model for this version
126
-            $model = $controller->getModelVersionInfo()->loadModel($model_name);
127
-            $model_schema = new JsonModelSchema($model, LoaderFactory::getLoader()->getShared('EventEspresso\core\libraries\rest_api\CalculatedModelFields'));
128
-            return $model_schema->getModelSchemaForRelations(
129
-                $controller->getModelVersionInfo()->relationSettings($model),
130
-                $controller->customizeSchemaForRestResponse(
131
-                    $model,
132
-                    $model_schema->getModelSchemaForFields(
133
-                        $controller->getModelVersionInfo()->fieldsOnModelInThisVersion($model),
134
-                        $model_schema->getInitialSchemaStructure()
135
-                    )
136
-                )
137
-            );
138
-        } catch (Exception $e) {
139
-            return array();
140
-        }
141
-    }
142
-
143
-
144
-    /**
145
-     * This loops through each field in the given schema for the model and does the following:
146
-     * - add any extra fields that are REST API specific and related to existing fields.
147
-     * - transform default values into the correct format for a REST API response.
148
-     *
149
-     * @param EEM_Base $model
150
-     * @param array    $schema
151
-     * @return array  The final schema.
152
-     */
153
-    protected function customizeSchemaForRestResponse(EEM_Base $model, array $schema)
154
-    {
155
-        foreach ($this->getModelVersionInfo()->fieldsOnModelInThisVersion($model) as $field_name => $field) {
156
-            $schema = $this->translateDefaultsForRestResponse(
157
-                $field_name,
158
-                $field,
159
-                $this->maybeAddExtraFieldsToSchema($field_name, $field, $schema)
160
-            );
161
-        }
162
-        return $schema;
163
-    }
164
-
165
-
166
-    /**
167
-     * This is used to ensure that the 'default' value set in the schema response is formatted correctly for the REST
168
-     * response.
169
-     *
170
-     * @param                      $field_name
171
-     * @param EE_Model_Field_Base  $field
172
-     * @param array                $schema
173
-     * @return array
174
-     * @throws ObjectDetectedException if a default value has a PHP object, which should never do (and if we
175
-     * did, let's know about it ASAP, so let the exception bubble up)
176
-     */
177
-    protected function translateDefaultsForRestResponse($field_name, EE_Model_Field_Base $field, array $schema)
178
-    {
179
-        if (isset($schema['properties'][ $field_name ]['default'])) {
180
-            if (is_array($schema['properties'][ $field_name ]['default'])) {
181
-                foreach ($schema['properties'][ $field_name ]['default'] as $default_key => $default_value) {
182
-                    if ($default_key === 'raw') {
183
-                        $schema['properties'][ $field_name ]['default'][ $default_key ] =
184
-                            ModelDataTranslator::prepareFieldValueForJson(
185
-                                $field,
186
-                                $default_value,
187
-                                $this->getModelVersionInfo()->requestedVersion()
188
-                            );
189
-                    }
190
-                }
191
-            } else {
192
-                $schema['properties'][ $field_name ]['default'] = ModelDataTranslator::prepareFieldValueForJson(
193
-                    $field,
194
-                    $schema['properties'][ $field_name ]['default'],
195
-                    $this->getModelVersionInfo()->requestedVersion()
196
-                );
197
-            }
198
-        }
199
-        return $schema;
200
-    }
201
-
202
-
203
-    /**
204
-     * Adds additional fields to the schema
205
-     * The REST API returns a GMT value field for each datetime field in the resource.  Thus the description about this
206
-     * needs to be added to the schema.
207
-     *
208
-     * @param                      $field_name
209
-     * @param EE_Model_Field_Base  $field
210
-     * @param array                $schema
211
-     * @return array
212
-     */
213
-    protected function maybeAddExtraFieldsToSchema($field_name, EE_Model_Field_Base $field, array $schema)
214
-    {
215
-        if ($field instanceof EE_Datetime_Field) {
216
-            $schema['properties'][ $field_name . '_gmt' ] = $field->getSchema();
217
-            // modify the description
218
-            $schema['properties'][ $field_name . '_gmt' ]['description'] = sprintf(
219
-                esc_html__('%s - the value for this field is in GMT.', 'event_espresso'),
220
-                wp_specialchars_decode($field->get_nicename(), ENT_QUOTES)
221
-            );
222
-        }
223
-        return $schema;
224
-    }
225
-
226
-
227
-    /**
228
-     * Used to figure out the route from the request when a `WP_REST_Request` object is not available
229
-     *
230
-     * @return string
231
-     */
232
-    protected function getRouteFromRequest()
233
-    {
234
-        if (isset($GLOBALS['wp'])
235
-            && $GLOBALS['wp'] instanceof \WP
236
-            && isset($GLOBALS['wp']->query_vars['rest_route'])
237
-        ) {
238
-            return $GLOBALS['wp']->query_vars['rest_route'];
239
-        } else {
240
-            return isset($_SERVER['PATH_INFO']) ? $_SERVER['PATH_INFO'] : '/';
241
-        }
242
-    }
243
-
244
-
245
-    /**
246
-     * Gets a single entity related to the model indicated in the path and its id
247
-     *
248
-     * @param WP_REST_Request $request
249
-     * @param string $version
250
-     * @param string $model_name
251
-     * @return WP_REST_Response|WP_Error
252
-     * @throws InvalidDataTypeException
253
-     * @throws InvalidInterfaceException
254
-     * @throws InvalidArgumentException
255
-     */
256
-    public static function handleRequestGetOne(WP_REST_Request $request, $version, $model_name)
257
-    {
258
-        $controller = LoaderFactory::getLoader()->getNew('EventEspresso\core\libraries\rest_api\controllers\model\Read');
259
-        try {
260
-            $controller->setRequestedVersion($version);
261
-            if (! $controller->getModelVersionInfo()->isModelNameInThisVersion($model_name)) {
262
-                return $controller->sendResponse(
263
-                    new WP_Error(
264
-                        'endpoint_parsing_error',
265
-                        sprintf(
266
-                            __(
267
-                                'There is no model for endpoint %s. Please contact event espresso support',
268
-                                'event_espresso'
269
-                            ),
270
-                            $model_name
271
-                        )
272
-                    )
273
-                );
274
-            }
275
-            return $controller->sendResponse(
276
-                $controller->getEntityFromModel(
277
-                    $controller->getModelVersionInfo()->loadModel($model_name),
278
-                    $request
279
-                )
280
-            );
281
-        } catch (Exception $e) {
282
-            return $controller->sendResponse($e);
283
-        }
284
-    }
285
-
286
-
287
-    /**
288
-     * Gets all the related entities (or if its a belongs-to relation just the one)
289
-     * to the item with the given id
290
-     *
291
-     * @param WP_REST_Request $request
292
-     * @param string $version
293
-     * @param string $model_name
294
-     * @param string $related_model_name
295
-     * @return WP_REST_Response|WP_Error
296
-     * @throws InvalidDataTypeException
297
-     * @throws InvalidInterfaceException
298
-     * @throws InvalidArgumentException
299
-     */
300
-    public static function handleRequestGetRelated(
301
-        WP_REST_Request $request,
302
-        $version,
303
-        $model_name,
304
-        $related_model_name
305
-    ) {
306
-        $controller = LoaderFactory::getLoader()->getNew('EventEspresso\core\libraries\rest_api\controllers\model\Read');
307
-        try {
308
-            $controller->setRequestedVersion($version);
309
-            $main_model = $controller->validateModel($model_name);
310
-            $controller->validateModel($related_model_name);
311
-            return $controller->sendResponse(
312
-                $controller->getEntitiesFromRelation(
313
-                    $request->get_param('id'),
314
-                    $main_model->related_settings_for($related_model_name),
315
-                    $request
316
-                )
317
-            );
318
-        } catch (Exception $e) {
319
-            return $controller->sendResponse($e);
320
-        }
321
-    }
322
-
323
-
324
-    /**
325
-     * Gets a collection for the given model and filters
326
-     *
327
-     * @param EEM_Base $model
328
-     * @param WP_REST_Request $request
329
-     * @return array
330
-     * @throws EE_Error
331
-     * @throws InvalidArgumentException
332
-     * @throws InvalidDataTypeException
333
-     * @throws InvalidInterfaceException
334
-     * @throws ReflectionException
335
-     * @throws RestException
336
-     */
337
-    public function getEntitiesFromModel($model, $request)
338
-    {
339
-        $query_params = $this->createModelQueryParams($model, $request->get_params());
340
-        if (! Capabilities::currentUserHasPartialAccessTo($model, $query_params['caps'])) {
341
-            $model_name_plural = EEH_Inflector::pluralize_and_lower($model->get_this_model_name());
342
-            throw new RestException(
343
-                sprintf('rest_%s_cannot_list', $model_name_plural),
344
-                sprintf(
345
-                    __('Sorry, you are not allowed to list %1$s. Missing permissions: %2$s', 'event_espresso'),
346
-                    $model_name_plural,
347
-                    Capabilities::getMissingPermissionsString($model, $query_params['caps'])
348
-                ),
349
-                array('status' => 403)
350
-            );
351
-        }
352
-        if (! $request->get_header('no_rest_headers')) {
353
-            $this->setHeadersFromQueryParams($model, $query_params);
354
-        }
355
-        /** @type array $results */
356
-        $results = $model->get_all_wpdb_results($query_params);
357
-        $nice_results = array();
358
-        foreach ($results as $result) {
359
-            $nice_results[] =  $this->createEntityFromWpdbResult(
360
-                $model,
361
-                $result,
362
-                $request
363
-            );
364
-        }
365
-        return $nice_results;
366
-    }
367
-
368
-
369
-    /**
370
-     * Gets the collection for given relation object
371
-     * The same as Read::get_entities_from_model(), except if the relation
372
-     * is a HABTM relation, in which case it merges any non-foreign-key fields from
373
-     * the join-model-object into the results
374
-     *
375
-     * @param array $primary_model_query_params query params for finding the item from which
376
-     *                                                            relations will be based
377
-     * @param \EE_Model_Relation_Base $relation
378
-     * @param WP_REST_Request $request
379
-     * @return array
380
-     * @throws EE_Error
381
-     * @throws InvalidArgumentException
382
-     * @throws InvalidDataTypeException
383
-     * @throws InvalidInterfaceException
384
-     * @throws ReflectionException
385
-     * @throws RestException
386
-     * @throws \EventEspresso\core\exceptions\ModelConfigurationException
387
-     */
388
-    protected function getEntitiesFromRelationUsingModelQueryParams($primary_model_query_params, $relation, $request)
389
-    {
390
-        $context = $this->validateContext($request->get_param('caps'));
391
-        $model = $relation->get_this_model();
392
-        $related_model = $relation->get_other_model();
393
-        if (! isset($primary_model_query_params[0])) {
394
-            $primary_model_query_params[0] = array();
395
-        }
396
-        // check if they can access the 1st model object
397
-        $primary_model_query_params = array(
398
-            0       => $primary_model_query_params[0],
399
-            'limit' => 1,
400
-        );
401
-        if ($model instanceof EEM_Soft_Delete_Base) {
402
-            $primary_model_query_params = $model->alter_query_params_so_deleted_and_undeleted_items_included(
403
-                $primary_model_query_params
404
-            );
405
-        }
406
-        $restricted_query_params = $primary_model_query_params;
407
-        $restricted_query_params['caps'] = $context;
408
-        $restricted_query_params['limit'] = 1;
409
-        $this->setDebugInfo('main model query params', $restricted_query_params);
410
-        $this->setDebugInfo('missing caps', Capabilities::getMissingPermissionsString($related_model, $context));
411
-        $primary_model_rows = $model->get_all_wpdb_results($restricted_query_params);
412
-        $primary_model_row = null;
413
-        if (is_array($primary_model_rows)) {
414
-            $primary_model_row = reset($primary_model_rows);
415
-        }
416
-        if (! (
417
-            Capabilities::currentUserHasPartialAccessTo($related_model, $context)
418
-            && $primary_model_row
419
-        )
420
-        ) {
421
-            if ($relation instanceof EE_Belongs_To_Relation) {
422
-                $related_model_name_maybe_plural = strtolower($related_model->get_this_model_name());
423
-            } else {
424
-                $related_model_name_maybe_plural = EEH_Inflector::pluralize_and_lower(
425
-                    $related_model->get_this_model_name()
426
-                );
427
-            }
428
-            throw new RestException(
429
-                sprintf('rest_%s_cannot_list', $related_model_name_maybe_plural),
430
-                sprintf(
431
-                    __(
432
-                        'Sorry, you are not allowed to list %1$s related to %2$s. Missing permissions: %3$s',
433
-                        'event_espresso'
434
-                    ),
435
-                    $related_model_name_maybe_plural,
436
-                    $relation->get_this_model()->get_this_model_name(),
437
-                    implode(
438
-                        ',',
439
-                        array_keys(
440
-                            Capabilities::getMissingPermissions($related_model, $context)
441
-                        )
442
-                    )
443
-                ),
444
-                array('status' => 403)
445
-            );
446
-        }
447
-
448
-        $this->checkPassword(
449
-            $model,
450
-            $primary_model_row,
451
-            $restricted_query_params,
452
-            $request
453
-        );
454
-        $query_params = $this->createModelQueryParams($relation->get_other_model(), $request->get_params());
455
-        foreach ($primary_model_query_params[0] as $where_condition_key => $where_condition_value) {
456
-            $query_params[0][ $relation->get_this_model()->get_this_model_name()
457
-                              . '.'
458
-                              . $where_condition_key ] = $where_condition_value;
459
-        }
460
-        $query_params['default_where_conditions'] = 'none';
461
-        $query_params['caps'] = $context;
462
-        if (! $request->get_header('no_rest_headers')) {
463
-            $this->setHeadersFromQueryParams($relation->get_other_model(), $query_params);
464
-        }
465
-        /** @type array $results */
466
-        $results = $relation->get_other_model()->get_all_wpdb_results($query_params);
467
-        $nice_results = array();
468
-        foreach ($results as $result) {
469
-            $nice_result = $this->createEntityFromWpdbResult(
470
-                $relation->get_other_model(),
471
-                $result,
472
-                $request
473
-            );
474
-            if ($relation instanceof \EE_HABTM_Relation) {
475
-                // put the unusual stuff (properties from the HABTM relation) first, and make sure
476
-                // if there are conflicts we prefer the properties from the main model
477
-                $join_model_result = $this->createEntityFromWpdbResult(
478
-                    $relation->get_join_model(),
479
-                    $result,
480
-                    $request
481
-                );
482
-                $joined_result = array_merge($nice_result, $join_model_result);
483
-                // but keep the meta stuff from the main model
484
-                if (isset($nice_result['meta'])) {
485
-                    $joined_result['meta'] = $nice_result['meta'];
486
-                }
487
-                $nice_result = $joined_result;
488
-            }
489
-            $nice_results[] = $nice_result;
490
-        }
491
-        if ($relation instanceof EE_Belongs_To_Relation) {
492
-            return array_shift($nice_results);
493
-        } else {
494
-            return $nice_results;
495
-        }
496
-    }
497
-
498
-
499
-    /**
500
-     * Gets the collection for given relation object
501
-     * The same as Read::get_entities_from_model(), except if the relation
502
-     * is a HABTM relation, in which case it merges any non-foreign-key fields from
503
-     * the join-model-object into the results
504
-     *
505
-     * @param string                  $id the ID of the thing we are fetching related stuff from
506
-     * @param \EE_Model_Relation_Base $relation
507
-     * @param WP_REST_Request         $request
508
-     * @return array
509
-     * @throws EE_Error
510
-     */
511
-    public function getEntitiesFromRelation($id, $relation, $request)
512
-    {
513
-        if (! $relation->get_this_model()->has_primary_key_field()) {
514
-            throw new EE_Error(
515
-                sprintf(
516
-                    __(
517
-                    // @codingStandardsIgnoreStart
518
-                        'Read::get_entities_from_relation should only be called from a model with a primary key, it was called from %1$s',
519
-                        // @codingStandardsIgnoreEnd
520
-                        'event_espresso'
521
-                    ),
522
-                    $relation->get_this_model()->get_this_model_name()
523
-                )
524
-            );
525
-        }
526
-        // can we edit that main item?
527
-        // if not, show nothing but an error
528
-        // otherwise, please proceed
529
-        return $this->getEntitiesFromRelationUsingModelQueryParams(
530
-            array(
531
-                array(
532
-                    $relation->get_this_model()->primary_key_name() => $id,
533
-                ),
534
-            ),
535
-            $relation,
536
-            $request
537
-        );
538
-    }
539
-
540
-
541
-    /**
542
-     * Sets the headers that are based on the model and query params,
543
-     * like the total records. This should only be called on the original request
544
-     * from the client, not on subsequent internal
545
-     *
546
-     * @param EEM_Base $model
547
-     * @param array    $query_params
548
-     * @return void
549
-     */
550
-    protected function setHeadersFromQueryParams($model, $query_params)
551
-    {
552
-        $this->setDebugInfo('model query params', $query_params);
553
-        $this->setDebugInfo(
554
-            'missing caps',
555
-            Capabilities::getMissingPermissionsString($model, $query_params['caps'])
556
-        );
557
-        // normally the limit to a 2-part array, where the 2nd item is the limit
558
-        if (! isset($query_params['limit'])) {
559
-            $query_params['limit'] = EED_Core_Rest_Api::get_default_query_limit();
560
-        }
561
-        if (is_array($query_params['limit'])) {
562
-            $limit_parts = $query_params['limit'];
563
-        } else {
564
-            $limit_parts = explode(',', $query_params['limit']);
565
-            if (count($limit_parts) == 1) {
566
-                $limit_parts = array(0, $limit_parts[0]);
567
-            }
568
-        }
569
-        // remove the group by and having parts of the query, as those will
570
-        // make the sql query return an array of values, instead of just a single value
571
-        unset($query_params['group_by'], $query_params['having'], $query_params['limit']);
572
-        $count = $model->count($query_params, null, true);
573
-        $pages = $count / $limit_parts[1];
574
-        $this->setResponseHeader('Total', $count, false);
575
-        $this->setResponseHeader('PageSize', $limit_parts[1], false);
576
-        $this->setResponseHeader('TotalPages', ceil($pages), false);
577
-    }
578
-
579
-
580
-    /**
581
-     * Changes database results into REST API entities
582
-     *
583
-     * @param EEM_Base $model
584
-     * @param array $db_row like results from $wpdb->get_results()
585
-     * @param WP_REST_Request $rest_request
586
-     * @param string $deprecated no longer used
587
-     * @return array ready for being converted into json for sending to client
588
-     * @throws EE_Error
589
-     * @throws RestException
590
-     * @throws InvalidDataTypeException
591
-     * @throws InvalidInterfaceException
592
-     * @throws InvalidArgumentException
593
-     * @throws ReflectionException
594
-     */
595
-    public function createEntityFromWpdbResult($model, $db_row, $rest_request, $deprecated = null)
596
-    {
597
-        if (! $rest_request instanceof WP_REST_Request) {
598
-            // ok so this was called in the old style, where the 3rd arg was
599
-            // $include, and the 4th arg was $context
600
-            // now setup the request just to avoid fatal errors, although we won't be able
601
-            // to truly make use of it because it's kinda devoid of info
602
-            $rest_request = new WP_REST_Request();
603
-            $rest_request->set_param('include', $rest_request);
604
-            $rest_request->set_param('caps', $deprecated);
605
-        }
606
-        if ($rest_request->get_param('caps') == null) {
607
-            $rest_request->set_param('caps', EEM_Base::caps_read);
608
-        }
609
-        $current_user_full_access_to_entity = $model->currentUserCan(
610
-            EEM_Base::caps_read_admin,
611
-            $model->deduce_fields_n_values_from_cols_n_values($db_row)
612
-        );
613
-        $entity_array = $this->createBareEntityFromWpdbResults($model, $db_row);
614
-        $entity_array = $this->addExtraFields($model, $db_row, $entity_array);
615
-        $entity_array['_links'] = $this->getEntityLinks($model, $db_row, $entity_array);
616
-        // when it's a regular read request for a model with a password and the password wasn't provided
617
-        // remove the password protected fields
618
-        $has_protected_fields = false;
619
-        try {
620
-            $this->checkPassword(
621
-                $model,
622
-                $db_row,
623
-                array(
624
-                    0 => array(
625
-                        $model->primary_key_name() => $db_row[ $model->get_primary_key_field()->get_qualified_column() ]
626
-                    )
627
-                ),
628
-                $rest_request
629
-            );
630
-        } catch (RestPasswordRequiredException $e) {
631
-            if ($model->hasPassword()) {
632
-                // just remove protected fields
633
-                $has_protected_fields = true;
634
-                $entity_array = Capabilities::filterOutPasswordProtectedFields(
635
-                    $entity_array,
636
-                    $model,
637
-                    $this->getModelVersionInfo()
638
-                );
639
-            } else {
640
-                // that's a problem. None of this should be accessible if no password was provided
641
-                throw $e;
642
-            }
643
-        }
644
-
645
-        $entity_array['_calculated_fields'] = $this->getEntityCalculations($model, $db_row, $rest_request, $has_protected_fields);
646
-        $entity_array = apply_filters(
647
-            'FHEE__Read__create_entity_from_wpdb_results__entity_before_including_requested_models',
648
-            $entity_array,
649
-            $model,
650
-            $rest_request->get_param('caps'),
651
-            $rest_request,
652
-            $this
653
-        );
654
-        // add an empty protected property for now. If it's still around after we remove everything the request didn't
655
-        // want, we'll populate it then. k?
656
-        $entity_array['_protected'] = array();
657
-        // remove any properties the request didn't want. This way _protected won't bother mentioning them
658
-        $entity_array = $this->includeOnlyRequestedProperties($model, $rest_request, $entity_array);
659
-        $entity_array = $this->includeRequestedModels($model, $rest_request, $entity_array, $db_row, $has_protected_fields);
660
-        // if they still wanted the _protected property, add it.
661
-        if (isset($entity_array['_protected'])) {
662
-            $entity_array = $this->addProtectedProperty($model, $entity_array, $has_protected_fields);
663
-        }
664
-        $entity_array = apply_filters(
665
-            'FHEE__Read__create_entity_from_wpdb_results__entity_before_inaccessible_field_removal',
666
-            $entity_array,
667
-            $model,
668
-            $rest_request->get_param('caps'),
669
-            $rest_request,
670
-            $this
671
-        );
672
-        if (! $current_user_full_access_to_entity) {
673
-            $result_without_inaccessible_fields = Capabilities::filterOutInaccessibleEntityFields(
674
-                $entity_array,
675
-                $model,
676
-                $rest_request->get_param('caps'),
677
-                $this->getModelVersionInfo()
678
-            );
679
-        } else {
680
-            $result_without_inaccessible_fields = $entity_array;
681
-        }
682
-        $this->setDebugInfo(
683
-            'inaccessible fields',
684
-            array_keys(array_diff_key((array) $entity_array, (array) $result_without_inaccessible_fields))
685
-        );
686
-        return apply_filters(
687
-            'FHEE__Read__create_entity_from_wpdb_results__entity_return',
688
-            $result_without_inaccessible_fields,
689
-            $model,
690
-            $rest_request->get_param('caps')
691
-        );
692
-    }
693
-
694
-    /**
695
-     * Returns an array describing which fields can be protected, and which actually were removed this request
696
-     * @since 4.9.74.p
697
-     * @param $model
698
-     * @param $results_so_far
699
-     * @param $protected
700
-     * @return array results
701
-     */
702
-    protected function addProtectedProperty(EEM_Base $model, $results_so_far, $protected)
703
-    {
704
-        if (! $model->hasPassword() || ! $protected) {
705
-            return $results_so_far;
706
-        }
707
-        $password_field = $model->getPasswordField();
708
-        $all_protected = array_merge(
709
-            array($password_field->get_name()),
710
-            $password_field->protectedFields()
711
-        );
712
-        $fields_included = array_keys($results_so_far);
713
-        $fields_included = array_intersect(
714
-            $all_protected,
715
-            $fields_included
716
-        );
717
-        foreach ($fields_included as $field_name) {
718
-            $results_so_far['_protected'][] = $field_name ;
719
-        }
720
-        return $results_so_far;
721
-    }
722
-
723
-    /**
724
-     * Creates a REST entity array (JSON object we're going to return in the response, but
725
-     * for now still a PHP array, but soon enough we'll call json_encode on it, don't worry),
726
-     * from $wpdb->get_row( $sql, ARRAY_A)
727
-     *
728
-     * @param EEM_Base $model
729
-     * @param array    $db_row
730
-     * @return array entity mostly ready for converting to JSON and sending in the response
731
-     */
732
-    protected function createBareEntityFromWpdbResults(EEM_Base $model, $db_row)
733
-    {
734
-        $result = $model->deduce_fields_n_values_from_cols_n_values($db_row);
735
-        $result = array_intersect_key(
736
-            $result,
737
-            $this->getModelVersionInfo()->fieldsOnModelInThisVersion($model)
738
-        );
739
-        // if this is a CPT, we need to set the global $post to it,
740
-        // otherwise shortcodes etc won't work properly while rendering it
741
-        if ($model instanceof \EEM_CPT_Base) {
742
-            $do_chevy_shuffle = true;
743
-        } else {
744
-            $do_chevy_shuffle = false;
745
-        }
746
-        if ($do_chevy_shuffle) {
747
-            global $post;
748
-            $old_post = $post;
749
-            $post = get_post($result[ $model->primary_key_name() ]);
750
-            if (! $post instanceof \WP_Post) {
751
-                // well that's weird, because $result is what we JUST fetched from the database
752
-                throw new RestException(
753
-                    'error_fetching_post_from_database_results',
754
-                    esc_html__(
755
-                        'An item was retrieved from the database but it\'s not a WP_Post like it should be.',
756
-                        'event_espresso'
757
-                    )
758
-                );
759
-            }
760
-            $model_object_classname = 'EE_' . $model->get_this_model_name();
761
-            $post->{$model_object_classname} = \EE_Registry::instance()->load_class(
762
-                $model_object_classname,
763
-                $result,
764
-                false,
765
-                false
766
-            );
767
-        }
768
-        foreach ($result as $field_name => $field_value) {
769
-            $field_obj = $model->field_settings_for($field_name);
770
-            if ($this->isSubclassOfOne($field_obj, $this->getModelVersionInfo()->fieldsIgnored())) {
771
-                unset($result[ $field_name ]);
772
-            } elseif ($this->isSubclassOfOne(
773
-                $field_obj,
774
-                $this->getModelVersionInfo()->fieldsThatHaveRenderedFormat()
775
-            )
776
-            ) {
777
-                $result[ $field_name ] = array(
778
-                    'raw'      => $this->prepareFieldObjValueForJson($field_obj, $field_value),
779
-                    'rendered' => $this->prepareFieldObjValueForJson($field_obj, $field_value, 'pretty'),
780
-                );
781
-            } elseif ($this->isSubclassOfOne(
782
-                $field_obj,
783
-                $this->getModelVersionInfo()->fieldsThatHavePrettyFormat()
784
-            )
785
-            ) {
786
-                $result[ $field_name ] = array(
787
-                    'raw'    => $this->prepareFieldObjValueForJson($field_obj, $field_value),
788
-                    'pretty' => $this->prepareFieldObjValueForJson($field_obj, $field_value, 'pretty'),
789
-                );
790
-            } elseif ($field_obj instanceof \EE_Datetime_Field) {
791
-                $field_value = $field_obj->prepare_for_set_from_db($field_value);
792
-                // if the value is null, but we're not supposed to permit null, then set to the field's default
793
-                if (is_null($field_value)) {
794
-                    $field_value = $field_obj->getDefaultDateTimeObj();
795
-                }
796
-                if (is_null($field_value)) {
797
-                    $gmt_date = $local_date = ModelDataTranslator::prepareFieldValuesForJson(
798
-                        $field_obj,
799
-                        $field_value,
800
-                        $this->getModelVersionInfo()->requestedVersion()
801
-                    );
802
-                } else {
803
-                    $timezone = $field_value->getTimezone();
804
-                    EEH_DTT_Helper::setTimezone($field_value, new DateTimeZone('UTC'));
805
-                    $gmt_date = ModelDataTranslator::prepareFieldValuesForJson(
806
-                        $field_obj,
807
-                        $field_value,
808
-                        $this->getModelVersionInfo()->requestedVersion()
809
-                    );
810
-                    EEH_DTT_Helper::setTimezone($field_value, $timezone);
811
-                    $local_date = ModelDataTranslator::prepareFieldValuesForJson(
812
-                        $field_obj,
813
-                        $field_value,
814
-                        $this->getModelVersionInfo()->requestedVersion()
815
-                    );
816
-                }
817
-                $result[ $field_name . '_gmt' ] = $gmt_date;
818
-                $result[ $field_name ] = $local_date;
819
-            } else {
820
-                $result[ $field_name ] = $this->prepareFieldObjValueForJson($field_obj, $field_value);
821
-            }
822
-        }
823
-        if ($do_chevy_shuffle) {
824
-            $post = $old_post;
825
-        }
826
-        return $result;
827
-    }
828
-
829
-
830
-    /**
831
-     * Takes a value all the way from the DB representation, to the model object's representation, to the
832
-     * user-facing PHP representation, to the REST API representation. (Assumes you've already taken from the DB
833
-     * representation using $field_obj->prepare_for_set_from_db())
834
-     *
835
-     * @param EE_Model_Field_Base $field_obj
836
-     * @param mixed               $value  as it's stored on a model object
837
-     * @param string              $format valid values are 'normal' (default), 'pretty', 'datetime_obj'
838
-     * @return mixed
839
-     * @throws ObjectDetectedException if $value contains a PHP object
840
-     */
841
-    protected function prepareFieldObjValueForJson(EE_Model_Field_Base $field_obj, $value, $format = 'normal')
842
-    {
843
-        $value = $field_obj->prepare_for_set_from_db($value);
844
-        switch ($format) {
845
-            case 'pretty':
846
-                $value = $field_obj->prepare_for_pretty_echoing($value);
847
-                break;
848
-            case 'normal':
849
-            default:
850
-                $value = $field_obj->prepare_for_get($value);
851
-                break;
852
-        }
853
-        return ModelDataTranslator::prepareFieldValuesForJson(
854
-            $field_obj,
855
-            $value,
856
-            $this->getModelVersionInfo()->requestedVersion()
857
-        );
858
-    }
859
-
860
-
861
-    /**
862
-     * Adds a few extra fields to the entity response
863
-     *
864
-     * @param EEM_Base $model
865
-     * @param array    $db_row
866
-     * @param array    $entity_array
867
-     * @return array modified entity
868
-     */
869
-    protected function addExtraFields(EEM_Base $model, $db_row, $entity_array)
870
-    {
871
-        if ($model instanceof EEM_CPT_Base) {
872
-            $entity_array['link'] = get_permalink($db_row[ $model->get_primary_key_field()->get_qualified_column() ]);
873
-        }
874
-        return $entity_array;
875
-    }
876
-
877
-
878
-    /**
879
-     * Gets links we want to add to the response
880
-     *
881
-     * @global \WP_REST_Server $wp_rest_server
882
-     * @param EEM_Base         $model
883
-     * @param array            $db_row
884
-     * @param array            $entity_array
885
-     * @return array the _links item in the entity
886
-     */
887
-    protected function getEntityLinks($model, $db_row, $entity_array)
888
-    {
889
-        // add basic links
890
-        $links = array();
891
-        if ($model->has_primary_key_field()) {
892
-            $links['self'] = array(
893
-                array(
894
-                    'href' => $this->getVersionedLinkTo(
895
-                        EEH_Inflector::pluralize_and_lower($model->get_this_model_name())
896
-                        . '/'
897
-                        . $entity_array[ $model->primary_key_name() ]
898
-                    ),
899
-                ),
900
-            );
901
-        }
902
-        $links['collection'] = array(
903
-            array(
904
-                'href' => $this->getVersionedLinkTo(
905
-                    EEH_Inflector::pluralize_and_lower($model->get_this_model_name())
906
-                ),
907
-            ),
908
-        );
909
-        // add links to related models
910
-        if ($model->has_primary_key_field()) {
911
-            foreach ($this->getModelVersionInfo()->relationSettings($model) as $relation_name => $relation_obj) {
912
-                $related_model_part = Read::getRelatedEntityName($relation_name, $relation_obj);
913
-                $links[ EED_Core_Rest_Api::ee_api_link_namespace . $related_model_part ] = array(
914
-                    array(
915
-                        'href'   => $this->getVersionedLinkTo(
916
-                            EEH_Inflector::pluralize_and_lower($model->get_this_model_name())
917
-                            . '/'
918
-                            . $entity_array[ $model->primary_key_name() ]
919
-                            . '/'
920
-                            . $related_model_part
921
-                        ),
922
-                        'single' => $relation_obj instanceof EE_Belongs_To_Relation ? true : false,
923
-                    ),
924
-                );
925
-            }
926
-        }
927
-        return $links;
928
-    }
929
-
930
-
931
-    /**
932
-     * Adds the included models indicated in the request to the entity provided
933
-     *
934
-     * @param EEM_Base $model
935
-     * @param WP_REST_Request $rest_request
936
-     * @param array $entity_array
937
-     * @param array $db_row
938
-     * @param boolean $included_items_protected if the original item is password protected, don't include any related models.
939
-     * @return array the modified entity
940
-     * @throws RestException
941
-     */
942
-    protected function includeRequestedModels(
943
-        EEM_Base $model,
944
-        WP_REST_Request $rest_request,
945
-        $entity_array,
946
-        $db_row = array(),
947
-        $included_items_protected = false
948
-    ) {
949
-        // if $db_row not included, hope the entity array has what we need
950
-        if (! $db_row) {
951
-            $db_row = $entity_array;
952
-        }
953
-        $relation_settings = $this->getModelVersionInfo()->relationSettings($model);
954
-        foreach ($relation_settings as $relation_name => $relation_obj) {
955
-            $related_fields_to_include = $this->explodeAndGetItemsPrefixedWith(
956
-                $rest_request->get_param('include'),
957
-                $relation_name
958
-            );
959
-            $related_fields_to_calculate = $this->explodeAndGetItemsPrefixedWith(
960
-                $rest_request->get_param('calculate'),
961
-                $relation_name
962
-            );
963
-            // did they specify they wanted to include a related model, or
964
-            // specific fields from a related model?
965
-            // or did they specify to calculate a field from a related model?
966
-            if ($related_fields_to_include || $related_fields_to_calculate) {
967
-                // if so, we should include at least some part of the related model
968
-                $pretend_related_request = new WP_REST_Request();
969
-                $pretend_related_request->set_query_params(
970
-                    array(
971
-                        'caps'      => $rest_request->get_param('caps'),
972
-                        'include'   => $related_fields_to_include,
973
-                        'calculate' => $related_fields_to_calculate,
974
-                        'password' => $rest_request->get_param('password')
975
-                    )
976
-                );
977
-                $pretend_related_request->add_header('no_rest_headers', true);
978
-                $primary_model_query_params = $model->alter_query_params_to_restrict_by_ID(
979
-                    $model->get_index_primary_key_string(
980
-                        $model->deduce_fields_n_values_from_cols_n_values($db_row)
981
-                    )
982
-                );
983
-                if (! $included_items_protected) {
984
-                    $related_results = $this->getEntitiesFromRelationUsingModelQueryParams(
985
-                        $primary_model_query_params,
986
-                        $relation_obj,
987
-                        $pretend_related_request
988
-                    );
989
-                } else {
990
-                    // they're protected, hide them.
991
-                    $related_results = $relation_obj instanceof EE_Belongs_To_Relation ? null : array();
992
-                    $entity_array['_protected'][] = Read::getRelatedEntityName($relation_name, $relation_obj);
993
-                }
994
-                if ($related_results instanceof WP_Error) {
995
-                    $related_results = null;
996
-                }
997
-                $entity_array[ Read::getRelatedEntityName($relation_name, $relation_obj) ] = $related_results;
998
-            }
999
-        }
1000
-        return $entity_array;
1001
-    }
1002
-
1003
-    /**
1004
-     * If the user has requested only specific properties (including meta properties like _links or _protected)
1005
-     * remove everything else.
1006
-     * @since 4.9.74.p
1007
-     * @param EEM_Base $model
1008
-     * @param WP_REST_Request $rest_request
1009
-     * @param $entity_array
1010
-     * @return array
1011
-     * @throws EE_Error
1012
-     */
1013
-    protected function includeOnlyRequestedProperties(
1014
-        EEM_Base $model,
1015
-        WP_REST_Request $rest_request,
1016
-        $entity_array
1017
-    ) {
1018
-
1019
-        $includes_for_this_model = $this->explodeAndGetItemsPrefixedWith($rest_request->get_param('include'), '');
1020
-        $includes_for_this_model = $this->removeModelNamesFromArray($includes_for_this_model);
1021
-        // if they passed in * or didn't specify any includes, return everything
1022
-        if (! in_array('*', $includes_for_this_model)
1023
-            && ! empty($includes_for_this_model)
1024
-        ) {
1025
-            if ($model->has_primary_key_field()) {
1026
-                // always include the primary key. ya just gotta know that at least
1027
-                $includes_for_this_model[] = $model->primary_key_name();
1028
-            }
1029
-            if ($this->explodeAndGetItemsPrefixedWith($rest_request->get_param('calculate'), '')) {
1030
-                $includes_for_this_model[] = '_calculated_fields';
1031
-            }
1032
-            $entity_array = array_intersect_key($entity_array, array_flip($includes_for_this_model));
1033
-        }
1034
-        return $entity_array;
1035
-    }
1036
-
1037
-
1038
-    /**
1039
-     * Returns a new array with all the names of models removed. Eg
1040
-     * array( 'Event', 'Datetime.*', 'foobar' ) would become array( 'Datetime.*', 'foobar' )
1041
-     *
1042
-     * @param array $arr
1043
-     * @return array
1044
-     */
1045
-    private function removeModelNamesFromArray($arr)
1046
-    {
1047
-        return array_diff($arr, array_keys(EE_Registry::instance()->non_abstract_db_models));
1048
-    }
1049
-
1050
-
1051
-    /**
1052
-     * Gets the calculated fields for the response
1053
-     *
1054
-     * @param EEM_Base        $model
1055
-     * @param array           $wpdb_row
1056
-     * @param WP_REST_Request $rest_request
1057
-     * @param boolean $row_is_protected whether this row is password protected or not
1058
-     * @return \stdClass the _calculations item in the entity
1059
-     * @throws ObjectDetectedException if a default value has a PHP object, which should never do (and if we
1060
-     * did, let's know about it ASAP, so let the exception bubble up)
1061
-     */
1062
-    protected function getEntityCalculations($model, $wpdb_row, $rest_request, $row_is_protected = false)
1063
-    {
1064
-        $calculated_fields = $this->explodeAndGetItemsPrefixedWith(
1065
-            $rest_request->get_param('calculate'),
1066
-            ''
1067
-        );
1068
-        // note: setting calculate=* doesn't do anything
1069
-        $calculated_fields_to_return = new \stdClass();
1070
-        $protected_fields = array();
1071
-        foreach ($calculated_fields as $field_to_calculate) {
1072
-            try {
1073
-                // it's password protected, so they shouldn't be able to read this. Remove the value
1074
-                $schema = $this->fields_calculator->getJsonSchemaForModel($model);
1075
-                if ($row_is_protected
1076
-                    && isset($schema['properties'][ $field_to_calculate ]['protected'])
1077
-                    && $schema['properties'][ $field_to_calculate ]['protected']) {
1078
-                    $calculated_value = null;
1079
-                    $protected_fields[] = $field_to_calculate;
1080
-                    if ($schema['properties'][ $field_to_calculate ]['type']) {
1081
-                        switch ($schema['properties'][ $field_to_calculate ]['type']) {
1082
-                            case 'boolean':
1083
-                                $calculated_value = false;
1084
-                                break;
1085
-                            case 'integer':
1086
-                                $calculated_value = 0;
1087
-                                break;
1088
-                            case 'string':
1089
-                                $calculated_value = '';
1090
-                                break;
1091
-                            case 'array':
1092
-                                $calculated_value = array();
1093
-                                break;
1094
-                            case 'object':
1095
-                                $calculated_value = new stdClass();
1096
-                                break;
1097
-                        }
1098
-                    }
1099
-                } else {
1100
-                    $calculated_value = ModelDataTranslator::prepareFieldValueForJson(
1101
-                        null,
1102
-                        $this->fields_calculator->retrieveCalculatedFieldValue(
1103
-                            $model,
1104
-                            $field_to_calculate,
1105
-                            $wpdb_row,
1106
-                            $rest_request,
1107
-                            $this
1108
-                        ),
1109
-                        $this->getModelVersionInfo()->requestedVersion()
1110
-                    );
1111
-                }
1112
-                $calculated_fields_to_return->{$field_to_calculate} = $calculated_value;
1113
-            } catch (RestException $e) {
1114
-                // if we don't have permission to read it, just leave it out. but let devs know about the problem
1115
-                $this->setResponseHeader(
1116
-                    'Notices-Field-Calculation-Errors['
1117
-                    . $e->getStringCode()
1118
-                    . ']['
1119
-                    . $model->get_this_model_name()
1120
-                    . ']['
1121
-                    . $field_to_calculate
1122
-                    . ']',
1123
-                    $e->getMessage(),
1124
-                    true
1125
-                );
1126
-            }
1127
-        }
1128
-        $calculated_fields_to_return->_protected = $protected_fields;
1129
-        return $calculated_fields_to_return;
1130
-    }
1131
-
1132
-
1133
-    /**
1134
-     * Gets the full URL to the resource, taking the requested version into account
1135
-     *
1136
-     * @param string $link_part_after_version_and_slash eg "events/10/datetimes"
1137
-     * @return string url eg "http://mysite.com/wp-json/ee/v4.6/events/10/datetimes"
1138
-     */
1139
-    public function getVersionedLinkTo($link_part_after_version_and_slash)
1140
-    {
1141
-        return rest_url(
1142
-            EED_Core_Rest_Api::get_versioned_route_to(
1143
-                $link_part_after_version_and_slash,
1144
-                $this->getModelVersionInfo()->requestedVersion()
1145
-            )
1146
-        );
1147
-    }
1148
-
1149
-
1150
-    /**
1151
-     * Gets the correct lowercase name for the relation in the API according
1152
-     * to the relation's type
1153
-     *
1154
-     * @param string                  $relation_name
1155
-     * @param \EE_Model_Relation_Base $relation_obj
1156
-     * @return string
1157
-     */
1158
-    public static function getRelatedEntityName($relation_name, $relation_obj)
1159
-    {
1160
-        if ($relation_obj instanceof EE_Belongs_To_Relation) {
1161
-            return strtolower($relation_name);
1162
-        } else {
1163
-            return EEH_Inflector::pluralize_and_lower($relation_name);
1164
-        }
1165
-    }
1166
-
1167
-
1168
-    /**
1169
-     * Gets the one model object with the specified id for the specified model
1170
-     *
1171
-     * @param EEM_Base        $model
1172
-     * @param WP_REST_Request $request
1173
-     * @return array
1174
-     */
1175
-    public function getEntityFromModel($model, $request)
1176
-    {
1177
-        $context = $this->validateContext($request->get_param('caps'));
1178
-        return $this->getOneOrReportPermissionError($model, $request, $context);
1179
-    }
1180
-
1181
-
1182
-    /**
1183
-     * If a context is provided which isn't valid, maybe it was added in a future
1184
-     * version so just treat it as a default read
1185
-     *
1186
-     * @param string $context
1187
-     * @return string array key of EEM_Base::cap_contexts_to_cap_action_map()
1188
-     */
1189
-    public function validateContext($context)
1190
-    {
1191
-        if (! $context) {
1192
-            $context = EEM_Base::caps_read;
1193
-        }
1194
-        $valid_contexts = EEM_Base::valid_cap_contexts();
1195
-        if (in_array($context, $valid_contexts)) {
1196
-            return $context;
1197
-        } else {
1198
-            return EEM_Base::caps_read;
1199
-        }
1200
-    }
1201
-
1202
-
1203
-    /**
1204
-     * Verifies the passed in value is an allowable default where conditions value.
1205
-     *
1206
-     * @param $default_query_params
1207
-     * @return string
1208
-     */
1209
-    public function validateDefaultQueryParams($default_query_params)
1210
-    {
1211
-        $valid_default_where_conditions_for_api_calls = array(
1212
-            EEM_Base::default_where_conditions_all,
1213
-            EEM_Base::default_where_conditions_minimum_all,
1214
-            EEM_Base::default_where_conditions_minimum_others,
1215
-        );
1216
-        if (! $default_query_params) {
1217
-            $default_query_params = EEM_Base::default_where_conditions_all;
1218
-        }
1219
-        if (in_array(
1220
-            $default_query_params,
1221
-            $valid_default_where_conditions_for_api_calls,
1222
-            true
1223
-        )) {
1224
-            return $default_query_params;
1225
-        } else {
1226
-            return EEM_Base::default_where_conditions_all;
1227
-        }
1228
-    }
1229
-
1230
-
1231
-    /**
1232
-     * Translates API filter get parameter into model query params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md#0-where-conditions.
1233
-     * Note: right now the query parameter keys for fields (and related fields)
1234
-     * can be left as-is, but it's quite possible this will change someday.
1235
-     * Also, this method's contents might be candidate for moving to Model_Data_Translator
1236
-     *
1237
-     * @param EEM_Base $model
1238
-     * @param array    $query_parameters  from $_GET parameter @see Read:handle_request_get_all
1239
-     * @return array model query params (@see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md#0-where-conditions)
1240
-     *                                    or FALSE to indicate that absolutely no results should be returned
1241
-     * @throws EE_Error
1242
-     * @throws RestException
1243
-     */
1244
-    public function createModelQueryParams($model, $query_params)
1245
-    {
1246
-        $model_query_params = array();
1247
-        if (isset($query_params['where'])) {
1248
-            $model_query_params[0] = ModelDataTranslator::prepareConditionsQueryParamsForModels(
1249
-                $query_params['where'],
1250
-                $model,
1251
-                $this->getModelVersionInfo()->requestedVersion()
1252
-            );
1253
-        }
1254
-        if (isset($query_params['order_by'])) {
1255
-            $order_by = $query_params['order_by'];
1256
-        } elseif (isset($query_params['orderby'])) {
1257
-            $order_by = $query_params['orderby'];
1258
-        } else {
1259
-            $order_by = null;
1260
-        }
1261
-        if ($order_by !== null) {
1262
-            if (is_array($order_by)) {
1263
-                $order_by = ModelDataTranslator::prepareFieldNamesInArrayKeysFromJson($order_by);
1264
-            } else {
1265
-                // it's a single item
1266
-                $order_by = ModelDataTranslator::prepareFieldNameFromJson($order_by);
1267
-            }
1268
-            $model_query_params['order_by'] = $order_by;
1269
-        }
1270
-        if (isset($query_params['group_by'])) {
1271
-            $group_by = $query_params['group_by'];
1272
-        } elseif (isset($query_params['groupby'])) {
1273
-            $group_by = $query_params['groupby'];
1274
-        } else {
1275
-            $group_by = array_keys($model->get_combined_primary_key_fields());
1276
-        }
1277
-        // make sure they're all real names
1278
-        if (is_array($group_by)) {
1279
-            $group_by = ModelDataTranslator::prepareFieldNamesFromJson($group_by);
1280
-        }
1281
-        if ($group_by !== null) {
1282
-            $model_query_params['group_by'] = $group_by;
1283
-        }
1284
-        if (isset($query_params['having'])) {
1285
-            $model_query_params['having'] = ModelDataTranslator::prepareConditionsQueryParamsForModels(
1286
-                $query_params['having'],
1287
-                $model,
1288
-                $this->getModelVersionInfo()->requestedVersion()
1289
-            );
1290
-        }
1291
-        if (isset($query_params['order'])) {
1292
-            $model_query_params['order'] = $query_params['order'];
1293
-        }
1294
-        if (isset($query_params['mine'])) {
1295
-            $model_query_params = $model->alter_query_params_to_only_include_mine($model_query_params);
1296
-        }
1297
-        if (isset($query_params['limit'])) {
1298
-            // limit should be either a string like '23' or '23,43', or an array with two items in it
1299
-            if (! is_array($query_params['limit'])) {
1300
-                $limit_array = explode(',', (string) $query_params['limit']);
1301
-            } else {
1302
-                $limit_array = $query_params['limit'];
1303
-            }
1304
-            $sanitized_limit = array();
1305
-            foreach ($limit_array as $key => $limit_part) {
1306
-                if ($this->debug_mode && (! is_numeric($limit_part) || count($sanitized_limit) > 2)) {
1307
-                    throw new EE_Error(
1308
-                        sprintf(
1309
-                            __(
1310
-                            // @codingStandardsIgnoreStart
1311
-                                'An invalid limit filter was provided. It was: %s. If the EE4 JSON REST API weren\'t in debug mode, this message would not appear.',
1312
-                                // @codingStandardsIgnoreEnd
1313
-                                'event_espresso'
1314
-                            ),
1315
-                            wp_json_encode($query_params['limit'])
1316
-                        )
1317
-                    );
1318
-                }
1319
-                $sanitized_limit[] = (int) $limit_part;
1320
-            }
1321
-            $model_query_params['limit'] = implode(',', $sanitized_limit);
1322
-        } else {
1323
-            $model_query_params['limit'] = EED_Core_Rest_Api::get_default_query_limit();
1324
-        }
1325
-        if (isset($query_params['caps'])) {
1326
-            $model_query_params['caps'] = $this->validateContext($query_params['caps']);
1327
-        } else {
1328
-            $model_query_params['caps'] = EEM_Base::caps_read;
1329
-        }
1330
-        if (isset($query_params['default_where_conditions'])) {
1331
-            $model_query_params['default_where_conditions'] = $this->validateDefaultQueryParams(
1332
-                $query_params['default_where_conditions']
1333
-            );
1334
-        }
1335
-        // if this is a model protected by a password on another model, exclude the password protected
1336
-        // entities by default. But if they passed in a password, try to show them all. If the password is wrong,
1337
-        // though, they'll get an error (see Read::createEntityFromWpdbResult() which calls Read::checkPassword)
1338
-        if (! $model->hasPassword()
1339
-            && $model->restrictedByRelatedModelPassword()
1340
-            && $model_query_params['caps'] === EEM_Base::caps_read) {
1341
-            if (empty($query_params['password'])) {
1342
-                $model_query_params['exclude_protected'] = true;
1343
-            }
1344
-        }
1345
-
1346
-        return apply_filters('FHEE__Read__create_model_query_params', $model_query_params, $query_params, $model);
1347
-    }
1348
-
1349
-
1350
-    /**
1351
-     * Changes the REST-style query params for use in the models
1352
-     *
1353
-     * @deprecated
1354
-     * @param EEM_Base $model
1355
-     * @param array    $query_params sub-array from @see EEM_Base::get_all()
1356
-     * @return array
1357
-     */
1358
-    public function prepareRestQueryParamsKeyForModels($model, $query_params)
1359
-    {
1360
-        $model_ready_query_params = array();
1361
-        foreach ($query_params as $key => $value) {
1362
-            if (is_array($value)) {
1363
-                $model_ready_query_params[ $key ] = $this->prepareRestQueryParamsKeyForModels($model, $value);
1364
-            } else {
1365
-                $model_ready_query_params[ $key ] = $value;
1366
-            }
1367
-        }
1368
-        return $model_ready_query_params;
1369
-    }
1370
-
1371
-
1372
-    /**
1373
-     * @deprecated instead use ModelDataTranslator::prepareFieldValuesFromJson()
1374
-     * @param $model
1375
-     * @param $query_params
1376
-     * @return array
1377
-     */
1378
-    public function prepareRestQueryParamsValuesForModels($model, $query_params)
1379
-    {
1380
-        $model_ready_query_params = array();
1381
-        foreach ($query_params as $key => $value) {
1382
-            if (is_array($value)) {
1383
-                $model_ready_query_params[ $key ] = $this->prepareRestQueryParamsValuesForModels($model, $value);
1384
-            } else {
1385
-                $model_ready_query_params[ $key ] = $value;
1386
-            }
1387
-        }
1388
-        return $model_ready_query_params;
1389
-    }
1390
-
1391
-
1392
-    /**
1393
-     * Explodes the string on commas, and only returns items with $prefix followed by a period.
1394
-     * If no prefix is specified, returns items with no period.
1395
-     *
1396
-     * @param string|array $string_to_explode eg "jibba,jabba, blah, blah, blah" or array('jibba', 'jabba' )
1397
-     * @param string       $prefix            "Event" or "foobar"
1398
-     * @return array $string_to_exploded exploded on COMMAS, and if a prefix was specified
1399
-     *                                        we only return strings starting with that and a period; if no prefix was
1400
-     *                                        specified we return all items containing NO periods
1401
-     */
1402
-    public function explodeAndGetItemsPrefixedWith($string_to_explode, $prefix)
1403
-    {
1404
-        if (is_string($string_to_explode)) {
1405
-            $exploded_contents = explode(',', $string_to_explode);
1406
-        } elseif (is_array($string_to_explode)) {
1407
-            $exploded_contents = $string_to_explode;
1408
-        } else {
1409
-            $exploded_contents = array();
1410
-        }
1411
-        // if the string was empty, we want an empty array
1412
-        $exploded_contents = array_filter($exploded_contents);
1413
-        $contents_with_prefix = array();
1414
-        foreach ($exploded_contents as $item) {
1415
-            $item = trim($item);
1416
-            // if no prefix was provided, so we look for items with no "." in them
1417
-            if (! $prefix) {
1418
-                // does this item have a period?
1419
-                if (strpos($item, '.') === false) {
1420
-                    // if not, then its what we're looking for
1421
-                    $contents_with_prefix[] = $item;
1422
-                }
1423
-            } elseif (strpos($item, $prefix . '.') === 0) {
1424
-                // this item has the prefix and a period, grab it
1425
-                $contents_with_prefix[] = substr(
1426
-                    $item,
1427
-                    strpos($item, $prefix . '.') + strlen($prefix . '.')
1428
-                );
1429
-            } elseif ($item === $prefix) {
1430
-                // this item is JUST the prefix
1431
-                // so let's grab everything after, which is a blank string
1432
-                $contents_with_prefix[] = '';
1433
-            }
1434
-        }
1435
-        return $contents_with_prefix;
1436
-    }
1437
-
1438
-
1439
-    /**
1440
-     * @deprecated since 4.8.36.rc.001 You should instead use Read::explode_and_get_items_prefixed_with.
1441
-     * Deprecated because its return values were really quite confusing- sometimes it returned
1442
-     * an empty array (when the include string was blank or '*') or sometimes it returned
1443
-     * array('*') (when you provided a model and a model of that kind was found).
1444
-     * Parses the $include_string so we fetch all the field names relating to THIS model
1445
-     * (ie have NO period in them), or for the provided model (ie start with the model
1446
-     * name and then a period).
1447
-     * @param string $include_string @see Read:handle_request_get_all
1448
-     * @param string $model_name
1449
-     * @return array of fields for this model. If $model_name is provided, then
1450
-     *                               the fields for that model, with the model's name removed from each.
1451
-     *                               If $include_string was blank or '*' returns an empty array
1452
-     */
1453
-    public function extractIncludesForThisModel($include_string, $model_name = null)
1454
-    {
1455
-        if (is_array($include_string)) {
1456
-            $include_string = implode(',', $include_string);
1457
-        }
1458
-        if ($include_string === '*' || $include_string === '') {
1459
-            return array();
1460
-        }
1461
-        $includes = explode(',', $include_string);
1462
-        $extracted_fields_to_include = array();
1463
-        if ($model_name) {
1464
-            foreach ($includes as $field_to_include) {
1465
-                $field_to_include = trim($field_to_include);
1466
-                if (strpos($field_to_include, $model_name . '.') === 0) {
1467
-                    // found the model name at the exact start
1468
-                    $field_sans_model_name = str_replace($model_name . '.', '', $field_to_include);
1469
-                    $extracted_fields_to_include[] = $field_sans_model_name;
1470
-                } elseif ($field_to_include == $model_name) {
1471
-                    $extracted_fields_to_include[] = '*';
1472
-                }
1473
-            }
1474
-        } else {
1475
-            // look for ones with no period
1476
-            foreach ($includes as $field_to_include) {
1477
-                $field_to_include = trim($field_to_include);
1478
-                if (strpos($field_to_include, '.') === false
1479
-                    && ! $this->getModelVersionInfo()->isModelNameInThisVersion($field_to_include)
1480
-                ) {
1481
-                    $extracted_fields_to_include[] = $field_to_include;
1482
-                }
1483
-            }
1484
-        }
1485
-        return $extracted_fields_to_include;
1486
-    }
1487
-
1488
-
1489
-    /**
1490
-     * Gets the single item using the model according to the request in the context given, otherwise
1491
-     * returns that it's inaccessible to the current user
1492
-     *
1493
-     * @param EEM_Base $model
1494
-     * @param WP_REST_Request $request
1495
-     * @param null $context
1496
-     * @return array
1497
-     * @throws EE_Error
1498
-     */
1499
-    public function getOneOrReportPermissionError(EEM_Base $model, WP_REST_Request $request, $context = null)
1500
-    {
1501
-        $query_params = array(array($model->primary_key_name() => $request->get_param('id')), 'limit' => 1);
1502
-        if ($model instanceof EEM_Soft_Delete_Base) {
1503
-            $query_params = $model->alter_query_params_so_deleted_and_undeleted_items_included($query_params);
1504
-        }
1505
-        $restricted_query_params = $query_params;
1506
-        $restricted_query_params['caps'] = $context;
1507
-        $this->setDebugInfo('model query params', $restricted_query_params);
1508
-        $model_rows = $model->get_all_wpdb_results($restricted_query_params);
1509
-        if (! empty($model_rows)) {
1510
-            return $this->createEntityFromWpdbResult(
1511
-                $model,
1512
-                reset($model_rows),
1513
-                $request
1514
-            );
1515
-        } else {
1516
-            // ok let's test to see if we WOULD have found it, had we not had restrictions from missing capabilities
1517
-            $lowercase_model_name = strtolower($model->get_this_model_name());
1518
-            if ($model->exists($query_params)) {
1519
-                // you got shafted- it existed but we didn't want to tell you!
1520
-                throw new RestException(
1521
-                    'rest_user_cannot_' . $context,
1522
-                    sprintf(
1523
-                        __('Sorry, you cannot %1$s this %2$s. Missing permissions are: %3$s', 'event_espresso'),
1524
-                        $context,
1525
-                        $lowercase_model_name,
1526
-                        Capabilities::getMissingPermissionsString(
1527
-                            $model,
1528
-                            $context
1529
-                        )
1530
-                    ),
1531
-                    array('status' => 403)
1532
-                );
1533
-            } else {
1534
-                // it's not you. It just doesn't exist
1535
-                throw new RestException(
1536
-                    sprintf('rest_%s_invalid_id', $lowercase_model_name),
1537
-                    sprintf(__('Invalid %s ID.', 'event_espresso'), $lowercase_model_name),
1538
-                    array('status' => 404)
1539
-                );
1540
-            }
1541
-        }
1542
-    }
1543
-
1544
-    /**
1545
-     * Checks that if this content requires a password to be read, that it's been provided and is correct.
1546
-     * @since 4.9.74.p
1547
-     * @param EEM_Base $model
1548
-     * @param $model_row
1549
-     * @param $query_params
1550
-     * @param WP_REST_Request $request
1551
-     * @throws EE_Error
1552
-     * @throws InvalidArgumentException
1553
-     * @throws InvalidDataTypeException
1554
-     * @throws InvalidInterfaceException
1555
-     * @throws RestPasswordRequiredException
1556
-     * @throws RestPasswordIncorrectException
1557
-     * @throws \EventEspresso\core\exceptions\ModelConfigurationException
1558
-     * @throws ReflectionException
1559
-     */
1560
-    protected function checkPassword(EEM_Base $model, $model_row, $query_params, WP_REST_Request $request)
1561
-    {
1562
-        // stuff is only "protected" for front-end requests. Elsewhere, you either get full permission to access the object
1563
-        // or you don't.
1564
-        $request_caps = $request->get_param('caps');
1565
-        if (isset($request_caps) && $request_caps !== EEM_Base::caps_read) {
1566
-            return;
1567
-        }
1568
-        // if this entity requires a password, they better give it and it better be right!
1569
-        if ($model->hasPassword()
1570
-            && $model_row[ $model->getPasswordField()->get_qualified_column() ] !== '') {
1571
-            if (empty($request['password'])) {
1572
-                throw new RestPasswordRequiredException();
1573
-            } elseif (!hash_equals(
1574
-                $model_row[ $model->getPasswordField()->get_qualified_column() ],
1575
-                $request['password']
1576
-            )) {
1577
-                throw new RestPasswordIncorrectException();
1578
-            }
1579
-        } // wait! maybe this content is password protected
1580
-        elseif ($model->restrictedByRelatedModelPassword()
1581
-            && $request->get_param('caps') === EEM_Base::caps_read) {
1582
-            $password_supplied = $request->get_param('password');
1583
-            if (empty($password_supplied)) {
1584
-                $query_params['exclude_protected'] = true;
1585
-                if (!$model->exists($query_params)) {
1586
-                    throw new RestPasswordRequiredException();
1587
-                }
1588
-            } else {
1589
-                $query_params[0][ $model->modelChainAndPassword() ] = $password_supplied;
1590
-                if (!$model->exists($query_params)) {
1591
-                    throw new RestPasswordIncorrectException();
1592
-                }
1593
-            }
1594
-        }
1595
-    }
48
+	/**
49
+	 * @var CalculatedModelFields
50
+	 */
51
+	protected $fields_calculator;
52
+
53
+
54
+	/**
55
+	 * Read constructor.
56
+	 * @param CalculatedModelFields $fields_calculator
57
+	 */
58
+	public function __construct(CalculatedModelFields $fields_calculator)
59
+	{
60
+		parent::__construct();
61
+		$this->fields_calculator = $fields_calculator;
62
+	}
63
+
64
+
65
+	/**
66
+	 * Handles requests to get all (or a filtered subset) of entities for a particular model
67
+	 *
68
+	 * @param WP_REST_Request $request
69
+	 * @param string $version
70
+	 * @param string $model_name
71
+	 * @return WP_REST_Response|WP_Error
72
+	 * @throws InvalidArgumentException
73
+	 * @throws InvalidDataTypeException
74
+	 * @throws InvalidInterfaceException
75
+	 */
76
+	public static function handleRequestGetAll(WP_REST_Request $request, $version, $model_name)
77
+	{
78
+		$controller = LoaderFactory::getLoader()->getNew('EventEspresso\core\libraries\rest_api\controllers\model\Read');
79
+		try {
80
+			$controller->setRequestedVersion($version);
81
+			if (! $controller->getModelVersionInfo()->isModelNameInThisVersion($model_name)) {
82
+				return $controller->sendResponse(
83
+					new WP_Error(
84
+						'endpoint_parsing_error',
85
+						sprintf(
86
+							__(
87
+								'There is no model for endpoint %s. Please contact event espresso support',
88
+								'event_espresso'
89
+							),
90
+							$model_name
91
+						)
92
+					)
93
+				);
94
+			}
95
+			return $controller->sendResponse(
96
+				$controller->getEntitiesFromModel(
97
+					$controller->getModelVersionInfo()->loadModel($model_name),
98
+					$request
99
+				)
100
+			);
101
+		} catch (Exception $e) {
102
+			return $controller->sendResponse($e);
103
+		}
104
+	}
105
+
106
+
107
+	/**
108
+	 * Prepares and returns schema for any OPTIONS request.
109
+	 *
110
+	 * @param string $version The API endpoint version being used.
111
+	 * @param string $model_name Something like `Event` or `Registration`
112
+	 * @return array
113
+	 * @throws InvalidArgumentException
114
+	 * @throws InvalidDataTypeException
115
+	 * @throws InvalidInterfaceException
116
+	 */
117
+	public static function handleSchemaRequest($version, $model_name)
118
+	{
119
+		$controller = LoaderFactory::getLoader()->getNew('EventEspresso\core\libraries\rest_api\controllers\model\Read');
120
+		try {
121
+			$controller->setRequestedVersion($version);
122
+			if (! $controller->getModelVersionInfo()->isModelNameInThisVersion($model_name)) {
123
+				return array();
124
+			}
125
+			// get the model for this version
126
+			$model = $controller->getModelVersionInfo()->loadModel($model_name);
127
+			$model_schema = new JsonModelSchema($model, LoaderFactory::getLoader()->getShared('EventEspresso\core\libraries\rest_api\CalculatedModelFields'));
128
+			return $model_schema->getModelSchemaForRelations(
129
+				$controller->getModelVersionInfo()->relationSettings($model),
130
+				$controller->customizeSchemaForRestResponse(
131
+					$model,
132
+					$model_schema->getModelSchemaForFields(
133
+						$controller->getModelVersionInfo()->fieldsOnModelInThisVersion($model),
134
+						$model_schema->getInitialSchemaStructure()
135
+					)
136
+				)
137
+			);
138
+		} catch (Exception $e) {
139
+			return array();
140
+		}
141
+	}
142
+
143
+
144
+	/**
145
+	 * This loops through each field in the given schema for the model and does the following:
146
+	 * - add any extra fields that are REST API specific and related to existing fields.
147
+	 * - transform default values into the correct format for a REST API response.
148
+	 *
149
+	 * @param EEM_Base $model
150
+	 * @param array    $schema
151
+	 * @return array  The final schema.
152
+	 */
153
+	protected function customizeSchemaForRestResponse(EEM_Base $model, array $schema)
154
+	{
155
+		foreach ($this->getModelVersionInfo()->fieldsOnModelInThisVersion($model) as $field_name => $field) {
156
+			$schema = $this->translateDefaultsForRestResponse(
157
+				$field_name,
158
+				$field,
159
+				$this->maybeAddExtraFieldsToSchema($field_name, $field, $schema)
160
+			);
161
+		}
162
+		return $schema;
163
+	}
164
+
165
+
166
+	/**
167
+	 * This is used to ensure that the 'default' value set in the schema response is formatted correctly for the REST
168
+	 * response.
169
+	 *
170
+	 * @param                      $field_name
171
+	 * @param EE_Model_Field_Base  $field
172
+	 * @param array                $schema
173
+	 * @return array
174
+	 * @throws ObjectDetectedException if a default value has a PHP object, which should never do (and if we
175
+	 * did, let's know about it ASAP, so let the exception bubble up)
176
+	 */
177
+	protected function translateDefaultsForRestResponse($field_name, EE_Model_Field_Base $field, array $schema)
178
+	{
179
+		if (isset($schema['properties'][ $field_name ]['default'])) {
180
+			if (is_array($schema['properties'][ $field_name ]['default'])) {
181
+				foreach ($schema['properties'][ $field_name ]['default'] as $default_key => $default_value) {
182
+					if ($default_key === 'raw') {
183
+						$schema['properties'][ $field_name ]['default'][ $default_key ] =
184
+							ModelDataTranslator::prepareFieldValueForJson(
185
+								$field,
186
+								$default_value,
187
+								$this->getModelVersionInfo()->requestedVersion()
188
+							);
189
+					}
190
+				}
191
+			} else {
192
+				$schema['properties'][ $field_name ]['default'] = ModelDataTranslator::prepareFieldValueForJson(
193
+					$field,
194
+					$schema['properties'][ $field_name ]['default'],
195
+					$this->getModelVersionInfo()->requestedVersion()
196
+				);
197
+			}
198
+		}
199
+		return $schema;
200
+	}
201
+
202
+
203
+	/**
204
+	 * Adds additional fields to the schema
205
+	 * The REST API returns a GMT value field for each datetime field in the resource.  Thus the description about this
206
+	 * needs to be added to the schema.
207
+	 *
208
+	 * @param                      $field_name
209
+	 * @param EE_Model_Field_Base  $field
210
+	 * @param array                $schema
211
+	 * @return array
212
+	 */
213
+	protected function maybeAddExtraFieldsToSchema($field_name, EE_Model_Field_Base $field, array $schema)
214
+	{
215
+		if ($field instanceof EE_Datetime_Field) {
216
+			$schema['properties'][ $field_name . '_gmt' ] = $field->getSchema();
217
+			// modify the description
218
+			$schema['properties'][ $field_name . '_gmt' ]['description'] = sprintf(
219
+				esc_html__('%s - the value for this field is in GMT.', 'event_espresso'),
220
+				wp_specialchars_decode($field->get_nicename(), ENT_QUOTES)
221
+			);
222
+		}
223
+		return $schema;
224
+	}
225
+
226
+
227
+	/**
228
+	 * Used to figure out the route from the request when a `WP_REST_Request` object is not available
229
+	 *
230
+	 * @return string
231
+	 */
232
+	protected function getRouteFromRequest()
233
+	{
234
+		if (isset($GLOBALS['wp'])
235
+			&& $GLOBALS['wp'] instanceof \WP
236
+			&& isset($GLOBALS['wp']->query_vars['rest_route'])
237
+		) {
238
+			return $GLOBALS['wp']->query_vars['rest_route'];
239
+		} else {
240
+			return isset($_SERVER['PATH_INFO']) ? $_SERVER['PATH_INFO'] : '/';
241
+		}
242
+	}
243
+
244
+
245
+	/**
246
+	 * Gets a single entity related to the model indicated in the path and its id
247
+	 *
248
+	 * @param WP_REST_Request $request
249
+	 * @param string $version
250
+	 * @param string $model_name
251
+	 * @return WP_REST_Response|WP_Error
252
+	 * @throws InvalidDataTypeException
253
+	 * @throws InvalidInterfaceException
254
+	 * @throws InvalidArgumentException
255
+	 */
256
+	public static function handleRequestGetOne(WP_REST_Request $request, $version, $model_name)
257
+	{
258
+		$controller = LoaderFactory::getLoader()->getNew('EventEspresso\core\libraries\rest_api\controllers\model\Read');
259
+		try {
260
+			$controller->setRequestedVersion($version);
261
+			if (! $controller->getModelVersionInfo()->isModelNameInThisVersion($model_name)) {
262
+				return $controller->sendResponse(
263
+					new WP_Error(
264
+						'endpoint_parsing_error',
265
+						sprintf(
266
+							__(
267
+								'There is no model for endpoint %s. Please contact event espresso support',
268
+								'event_espresso'
269
+							),
270
+							$model_name
271
+						)
272
+					)
273
+				);
274
+			}
275
+			return $controller->sendResponse(
276
+				$controller->getEntityFromModel(
277
+					$controller->getModelVersionInfo()->loadModel($model_name),
278
+					$request
279
+				)
280
+			);
281
+		} catch (Exception $e) {
282
+			return $controller->sendResponse($e);
283
+		}
284
+	}
285
+
286
+
287
+	/**
288
+	 * Gets all the related entities (or if its a belongs-to relation just the one)
289
+	 * to the item with the given id
290
+	 *
291
+	 * @param WP_REST_Request $request
292
+	 * @param string $version
293
+	 * @param string $model_name
294
+	 * @param string $related_model_name
295
+	 * @return WP_REST_Response|WP_Error
296
+	 * @throws InvalidDataTypeException
297
+	 * @throws InvalidInterfaceException
298
+	 * @throws InvalidArgumentException
299
+	 */
300
+	public static function handleRequestGetRelated(
301
+		WP_REST_Request $request,
302
+		$version,
303
+		$model_name,
304
+		$related_model_name
305
+	) {
306
+		$controller = LoaderFactory::getLoader()->getNew('EventEspresso\core\libraries\rest_api\controllers\model\Read');
307
+		try {
308
+			$controller->setRequestedVersion($version);
309
+			$main_model = $controller->validateModel($model_name);
310
+			$controller->validateModel($related_model_name);
311
+			return $controller->sendResponse(
312
+				$controller->getEntitiesFromRelation(
313
+					$request->get_param('id'),
314
+					$main_model->related_settings_for($related_model_name),
315
+					$request
316
+				)
317
+			);
318
+		} catch (Exception $e) {
319
+			return $controller->sendResponse($e);
320
+		}
321
+	}
322
+
323
+
324
+	/**
325
+	 * Gets a collection for the given model and filters
326
+	 *
327
+	 * @param EEM_Base $model
328
+	 * @param WP_REST_Request $request
329
+	 * @return array
330
+	 * @throws EE_Error
331
+	 * @throws InvalidArgumentException
332
+	 * @throws InvalidDataTypeException
333
+	 * @throws InvalidInterfaceException
334
+	 * @throws ReflectionException
335
+	 * @throws RestException
336
+	 */
337
+	public function getEntitiesFromModel($model, $request)
338
+	{
339
+		$query_params = $this->createModelQueryParams($model, $request->get_params());
340
+		if (! Capabilities::currentUserHasPartialAccessTo($model, $query_params['caps'])) {
341
+			$model_name_plural = EEH_Inflector::pluralize_and_lower($model->get_this_model_name());
342
+			throw new RestException(
343
+				sprintf('rest_%s_cannot_list', $model_name_plural),
344
+				sprintf(
345
+					__('Sorry, you are not allowed to list %1$s. Missing permissions: %2$s', 'event_espresso'),
346
+					$model_name_plural,
347
+					Capabilities::getMissingPermissionsString($model, $query_params['caps'])
348
+				),
349
+				array('status' => 403)
350
+			);
351
+		}
352
+		if (! $request->get_header('no_rest_headers')) {
353
+			$this->setHeadersFromQueryParams($model, $query_params);
354
+		}
355
+		/** @type array $results */
356
+		$results = $model->get_all_wpdb_results($query_params);
357
+		$nice_results = array();
358
+		foreach ($results as $result) {
359
+			$nice_results[] =  $this->createEntityFromWpdbResult(
360
+				$model,
361
+				$result,
362
+				$request
363
+			);
364
+		}
365
+		return $nice_results;
366
+	}
367
+
368
+
369
+	/**
370
+	 * Gets the collection for given relation object
371
+	 * The same as Read::get_entities_from_model(), except if the relation
372
+	 * is a HABTM relation, in which case it merges any non-foreign-key fields from
373
+	 * the join-model-object into the results
374
+	 *
375
+	 * @param array $primary_model_query_params query params for finding the item from which
376
+	 *                                                            relations will be based
377
+	 * @param \EE_Model_Relation_Base $relation
378
+	 * @param WP_REST_Request $request
379
+	 * @return array
380
+	 * @throws EE_Error
381
+	 * @throws InvalidArgumentException
382
+	 * @throws InvalidDataTypeException
383
+	 * @throws InvalidInterfaceException
384
+	 * @throws ReflectionException
385
+	 * @throws RestException
386
+	 * @throws \EventEspresso\core\exceptions\ModelConfigurationException
387
+	 */
388
+	protected function getEntitiesFromRelationUsingModelQueryParams($primary_model_query_params, $relation, $request)
389
+	{
390
+		$context = $this->validateContext($request->get_param('caps'));
391
+		$model = $relation->get_this_model();
392
+		$related_model = $relation->get_other_model();
393
+		if (! isset($primary_model_query_params[0])) {
394
+			$primary_model_query_params[0] = array();
395
+		}
396
+		// check if they can access the 1st model object
397
+		$primary_model_query_params = array(
398
+			0       => $primary_model_query_params[0],
399
+			'limit' => 1,
400
+		);
401
+		if ($model instanceof EEM_Soft_Delete_Base) {
402
+			$primary_model_query_params = $model->alter_query_params_so_deleted_and_undeleted_items_included(
403
+				$primary_model_query_params
404
+			);
405
+		}
406
+		$restricted_query_params = $primary_model_query_params;
407
+		$restricted_query_params['caps'] = $context;
408
+		$restricted_query_params['limit'] = 1;
409
+		$this->setDebugInfo('main model query params', $restricted_query_params);
410
+		$this->setDebugInfo('missing caps', Capabilities::getMissingPermissionsString($related_model, $context));
411
+		$primary_model_rows = $model->get_all_wpdb_results($restricted_query_params);
412
+		$primary_model_row = null;
413
+		if (is_array($primary_model_rows)) {
414
+			$primary_model_row = reset($primary_model_rows);
415
+		}
416
+		if (! (
417
+			Capabilities::currentUserHasPartialAccessTo($related_model, $context)
418
+			&& $primary_model_row
419
+		)
420
+		) {
421
+			if ($relation instanceof EE_Belongs_To_Relation) {
422
+				$related_model_name_maybe_plural = strtolower($related_model->get_this_model_name());
423
+			} else {
424
+				$related_model_name_maybe_plural = EEH_Inflector::pluralize_and_lower(
425
+					$related_model->get_this_model_name()
426
+				);
427
+			}
428
+			throw new RestException(
429
+				sprintf('rest_%s_cannot_list', $related_model_name_maybe_plural),
430
+				sprintf(
431
+					__(
432
+						'Sorry, you are not allowed to list %1$s related to %2$s. Missing permissions: %3$s',
433
+						'event_espresso'
434
+					),
435
+					$related_model_name_maybe_plural,
436
+					$relation->get_this_model()->get_this_model_name(),
437
+					implode(
438
+						',',
439
+						array_keys(
440
+							Capabilities::getMissingPermissions($related_model, $context)
441
+						)
442
+					)
443
+				),
444
+				array('status' => 403)
445
+			);
446
+		}
447
+
448
+		$this->checkPassword(
449
+			$model,
450
+			$primary_model_row,
451
+			$restricted_query_params,
452
+			$request
453
+		);
454
+		$query_params = $this->createModelQueryParams($relation->get_other_model(), $request->get_params());
455
+		foreach ($primary_model_query_params[0] as $where_condition_key => $where_condition_value) {
456
+			$query_params[0][ $relation->get_this_model()->get_this_model_name()
457
+							  . '.'
458
+							  . $where_condition_key ] = $where_condition_value;
459
+		}
460
+		$query_params['default_where_conditions'] = 'none';
461
+		$query_params['caps'] = $context;
462
+		if (! $request->get_header('no_rest_headers')) {
463
+			$this->setHeadersFromQueryParams($relation->get_other_model(), $query_params);
464
+		}
465
+		/** @type array $results */
466
+		$results = $relation->get_other_model()->get_all_wpdb_results($query_params);
467
+		$nice_results = array();
468
+		foreach ($results as $result) {
469
+			$nice_result = $this->createEntityFromWpdbResult(
470
+				$relation->get_other_model(),
471
+				$result,
472
+				$request
473
+			);
474
+			if ($relation instanceof \EE_HABTM_Relation) {
475
+				// put the unusual stuff (properties from the HABTM relation) first, and make sure
476
+				// if there are conflicts we prefer the properties from the main model
477
+				$join_model_result = $this->createEntityFromWpdbResult(
478
+					$relation->get_join_model(),
479
+					$result,
480
+					$request
481
+				);
482
+				$joined_result = array_merge($nice_result, $join_model_result);
483
+				// but keep the meta stuff from the main model
484
+				if (isset($nice_result['meta'])) {
485
+					$joined_result['meta'] = $nice_result['meta'];
486
+				}
487
+				$nice_result = $joined_result;
488
+			}
489
+			$nice_results[] = $nice_result;
490
+		}
491
+		if ($relation instanceof EE_Belongs_To_Relation) {
492
+			return array_shift($nice_results);
493
+		} else {
494
+			return $nice_results;
495
+		}
496
+	}
497
+
498
+
499
+	/**
500
+	 * Gets the collection for given relation object
501
+	 * The same as Read::get_entities_from_model(), except if the relation
502
+	 * is a HABTM relation, in which case it merges any non-foreign-key fields from
503
+	 * the join-model-object into the results
504
+	 *
505
+	 * @param string                  $id the ID of the thing we are fetching related stuff from
506
+	 * @param \EE_Model_Relation_Base $relation
507
+	 * @param WP_REST_Request         $request
508
+	 * @return array
509
+	 * @throws EE_Error
510
+	 */
511
+	public function getEntitiesFromRelation($id, $relation, $request)
512
+	{
513
+		if (! $relation->get_this_model()->has_primary_key_field()) {
514
+			throw new EE_Error(
515
+				sprintf(
516
+					__(
517
+					// @codingStandardsIgnoreStart
518
+						'Read::get_entities_from_relation should only be called from a model with a primary key, it was called from %1$s',
519
+						// @codingStandardsIgnoreEnd
520
+						'event_espresso'
521
+					),
522
+					$relation->get_this_model()->get_this_model_name()
523
+				)
524
+			);
525
+		}
526
+		// can we edit that main item?
527
+		// if not, show nothing but an error
528
+		// otherwise, please proceed
529
+		return $this->getEntitiesFromRelationUsingModelQueryParams(
530
+			array(
531
+				array(
532
+					$relation->get_this_model()->primary_key_name() => $id,
533
+				),
534
+			),
535
+			$relation,
536
+			$request
537
+		);
538
+	}
539
+
540
+
541
+	/**
542
+	 * Sets the headers that are based on the model and query params,
543
+	 * like the total records. This should only be called on the original request
544
+	 * from the client, not on subsequent internal
545
+	 *
546
+	 * @param EEM_Base $model
547
+	 * @param array    $query_params
548
+	 * @return void
549
+	 */
550
+	protected function setHeadersFromQueryParams($model, $query_params)
551
+	{
552
+		$this->setDebugInfo('model query params', $query_params);
553
+		$this->setDebugInfo(
554
+			'missing caps',
555
+			Capabilities::getMissingPermissionsString($model, $query_params['caps'])
556
+		);
557
+		// normally the limit to a 2-part array, where the 2nd item is the limit
558
+		if (! isset($query_params['limit'])) {
559
+			$query_params['limit'] = EED_Core_Rest_Api::get_default_query_limit();
560
+		}
561
+		if (is_array($query_params['limit'])) {
562
+			$limit_parts = $query_params['limit'];
563
+		} else {
564
+			$limit_parts = explode(',', $query_params['limit']);
565
+			if (count($limit_parts) == 1) {
566
+				$limit_parts = array(0, $limit_parts[0]);
567
+			}
568
+		}
569
+		// remove the group by and having parts of the query, as those will
570
+		// make the sql query return an array of values, instead of just a single value
571
+		unset($query_params['group_by'], $query_params['having'], $query_params['limit']);
572
+		$count = $model->count($query_params, null, true);
573
+		$pages = $count / $limit_parts[1];
574
+		$this->setResponseHeader('Total', $count, false);
575
+		$this->setResponseHeader('PageSize', $limit_parts[1], false);
576
+		$this->setResponseHeader('TotalPages', ceil($pages), false);
577
+	}
578
+
579
+
580
+	/**
581
+	 * Changes database results into REST API entities
582
+	 *
583
+	 * @param EEM_Base $model
584
+	 * @param array $db_row like results from $wpdb->get_results()
585
+	 * @param WP_REST_Request $rest_request
586
+	 * @param string $deprecated no longer used
587
+	 * @return array ready for being converted into json for sending to client
588
+	 * @throws EE_Error
589
+	 * @throws RestException
590
+	 * @throws InvalidDataTypeException
591
+	 * @throws InvalidInterfaceException
592
+	 * @throws InvalidArgumentException
593
+	 * @throws ReflectionException
594
+	 */
595
+	public function createEntityFromWpdbResult($model, $db_row, $rest_request, $deprecated = null)
596
+	{
597
+		if (! $rest_request instanceof WP_REST_Request) {
598
+			// ok so this was called in the old style, where the 3rd arg was
599
+			// $include, and the 4th arg was $context
600
+			// now setup the request just to avoid fatal errors, although we won't be able
601
+			// to truly make use of it because it's kinda devoid of info
602
+			$rest_request = new WP_REST_Request();
603
+			$rest_request->set_param('include', $rest_request);
604
+			$rest_request->set_param('caps', $deprecated);
605
+		}
606
+		if ($rest_request->get_param('caps') == null) {
607
+			$rest_request->set_param('caps', EEM_Base::caps_read);
608
+		}
609
+		$current_user_full_access_to_entity = $model->currentUserCan(
610
+			EEM_Base::caps_read_admin,
611
+			$model->deduce_fields_n_values_from_cols_n_values($db_row)
612
+		);
613
+		$entity_array = $this->createBareEntityFromWpdbResults($model, $db_row);
614
+		$entity_array = $this->addExtraFields($model, $db_row, $entity_array);
615
+		$entity_array['_links'] = $this->getEntityLinks($model, $db_row, $entity_array);
616
+		// when it's a regular read request for a model with a password and the password wasn't provided
617
+		// remove the password protected fields
618
+		$has_protected_fields = false;
619
+		try {
620
+			$this->checkPassword(
621
+				$model,
622
+				$db_row,
623
+				array(
624
+					0 => array(
625
+						$model->primary_key_name() => $db_row[ $model->get_primary_key_field()->get_qualified_column() ]
626
+					)
627
+				),
628
+				$rest_request
629
+			);
630
+		} catch (RestPasswordRequiredException $e) {
631
+			if ($model->hasPassword()) {
632
+				// just remove protected fields
633
+				$has_protected_fields = true;
634
+				$entity_array = Capabilities::filterOutPasswordProtectedFields(
635
+					$entity_array,
636
+					$model,
637
+					$this->getModelVersionInfo()
638
+				);
639
+			} else {
640
+				// that's a problem. None of this should be accessible if no password was provided
641
+				throw $e;
642
+			}
643
+		}
644
+
645
+		$entity_array['_calculated_fields'] = $this->getEntityCalculations($model, $db_row, $rest_request, $has_protected_fields);
646
+		$entity_array = apply_filters(
647
+			'FHEE__Read__create_entity_from_wpdb_results__entity_before_including_requested_models',
648
+			$entity_array,
649
+			$model,
650
+			$rest_request->get_param('caps'),
651
+			$rest_request,
652
+			$this
653
+		);
654
+		// add an empty protected property for now. If it's still around after we remove everything the request didn't
655
+		// want, we'll populate it then. k?
656
+		$entity_array['_protected'] = array();
657
+		// remove any properties the request didn't want. This way _protected won't bother mentioning them
658
+		$entity_array = $this->includeOnlyRequestedProperties($model, $rest_request, $entity_array);
659
+		$entity_array = $this->includeRequestedModels($model, $rest_request, $entity_array, $db_row, $has_protected_fields);
660
+		// if they still wanted the _protected property, add it.
661
+		if (isset($entity_array['_protected'])) {
662
+			$entity_array = $this->addProtectedProperty($model, $entity_array, $has_protected_fields);
663
+		}
664
+		$entity_array = apply_filters(
665
+			'FHEE__Read__create_entity_from_wpdb_results__entity_before_inaccessible_field_removal',
666
+			$entity_array,
667
+			$model,
668
+			$rest_request->get_param('caps'),
669
+			$rest_request,
670
+			$this
671
+		);
672
+		if (! $current_user_full_access_to_entity) {
673
+			$result_without_inaccessible_fields = Capabilities::filterOutInaccessibleEntityFields(
674
+				$entity_array,
675
+				$model,
676
+				$rest_request->get_param('caps'),
677
+				$this->getModelVersionInfo()
678
+			);
679
+		} else {
680
+			$result_without_inaccessible_fields = $entity_array;
681
+		}
682
+		$this->setDebugInfo(
683
+			'inaccessible fields',
684
+			array_keys(array_diff_key((array) $entity_array, (array) $result_without_inaccessible_fields))
685
+		);
686
+		return apply_filters(
687
+			'FHEE__Read__create_entity_from_wpdb_results__entity_return',
688
+			$result_without_inaccessible_fields,
689
+			$model,
690
+			$rest_request->get_param('caps')
691
+		);
692
+	}
693
+
694
+	/**
695
+	 * Returns an array describing which fields can be protected, and which actually were removed this request
696
+	 * @since 4.9.74.p
697
+	 * @param $model
698
+	 * @param $results_so_far
699
+	 * @param $protected
700
+	 * @return array results
701
+	 */
702
+	protected function addProtectedProperty(EEM_Base $model, $results_so_far, $protected)
703
+	{
704
+		if (! $model->hasPassword() || ! $protected) {
705
+			return $results_so_far;
706
+		}
707
+		$password_field = $model->getPasswordField();
708
+		$all_protected = array_merge(
709
+			array($password_field->get_name()),
710
+			$password_field->protectedFields()
711
+		);
712
+		$fields_included = array_keys($results_so_far);
713
+		$fields_included = array_intersect(
714
+			$all_protected,
715
+			$fields_included
716
+		);
717
+		foreach ($fields_included as $field_name) {
718
+			$results_so_far['_protected'][] = $field_name ;
719
+		}
720
+		return $results_so_far;
721
+	}
722
+
723
+	/**
724
+	 * Creates a REST entity array (JSON object we're going to return in the response, but
725
+	 * for now still a PHP array, but soon enough we'll call json_encode on it, don't worry),
726
+	 * from $wpdb->get_row( $sql, ARRAY_A)
727
+	 *
728
+	 * @param EEM_Base $model
729
+	 * @param array    $db_row
730
+	 * @return array entity mostly ready for converting to JSON and sending in the response
731
+	 */
732
+	protected function createBareEntityFromWpdbResults(EEM_Base $model, $db_row)
733
+	{
734
+		$result = $model->deduce_fields_n_values_from_cols_n_values($db_row);
735
+		$result = array_intersect_key(
736
+			$result,
737
+			$this->getModelVersionInfo()->fieldsOnModelInThisVersion($model)
738
+		);
739
+		// if this is a CPT, we need to set the global $post to it,
740
+		// otherwise shortcodes etc won't work properly while rendering it
741
+		if ($model instanceof \EEM_CPT_Base) {
742
+			$do_chevy_shuffle = true;
743
+		} else {
744
+			$do_chevy_shuffle = false;
745
+		}
746
+		if ($do_chevy_shuffle) {
747
+			global $post;
748
+			$old_post = $post;
749
+			$post = get_post($result[ $model->primary_key_name() ]);
750
+			if (! $post instanceof \WP_Post) {
751
+				// well that's weird, because $result is what we JUST fetched from the database
752
+				throw new RestException(
753
+					'error_fetching_post_from_database_results',
754
+					esc_html__(
755
+						'An item was retrieved from the database but it\'s not a WP_Post like it should be.',
756
+						'event_espresso'
757
+					)
758
+				);
759
+			}
760
+			$model_object_classname = 'EE_' . $model->get_this_model_name();
761
+			$post->{$model_object_classname} = \EE_Registry::instance()->load_class(
762
+				$model_object_classname,
763
+				$result,
764
+				false,
765
+				false
766
+			);
767
+		}
768
+		foreach ($result as $field_name => $field_value) {
769
+			$field_obj = $model->field_settings_for($field_name);
770
+			if ($this->isSubclassOfOne($field_obj, $this->getModelVersionInfo()->fieldsIgnored())) {
771
+				unset($result[ $field_name ]);
772
+			} elseif ($this->isSubclassOfOne(
773
+				$field_obj,
774
+				$this->getModelVersionInfo()->fieldsThatHaveRenderedFormat()
775
+			)
776
+			) {
777
+				$result[ $field_name ] = array(
778
+					'raw'      => $this->prepareFieldObjValueForJson($field_obj, $field_value),
779
+					'rendered' => $this->prepareFieldObjValueForJson($field_obj, $field_value, 'pretty'),
780
+				);
781
+			} elseif ($this->isSubclassOfOne(
782
+				$field_obj,
783
+				$this->getModelVersionInfo()->fieldsThatHavePrettyFormat()
784
+			)
785
+			) {
786
+				$result[ $field_name ] = array(
787
+					'raw'    => $this->prepareFieldObjValueForJson($field_obj, $field_value),
788
+					'pretty' => $this->prepareFieldObjValueForJson($field_obj, $field_value, 'pretty'),
789
+				);
790
+			} elseif ($field_obj instanceof \EE_Datetime_Field) {
791
+				$field_value = $field_obj->prepare_for_set_from_db($field_value);
792
+				// if the value is null, but we're not supposed to permit null, then set to the field's default
793
+				if (is_null($field_value)) {
794
+					$field_value = $field_obj->getDefaultDateTimeObj();
795
+				}
796
+				if (is_null($field_value)) {
797
+					$gmt_date = $local_date = ModelDataTranslator::prepareFieldValuesForJson(
798
+						$field_obj,
799
+						$field_value,
800
+						$this->getModelVersionInfo()->requestedVersion()
801
+					);
802
+				} else {
803
+					$timezone = $field_value->getTimezone();
804
+					EEH_DTT_Helper::setTimezone($field_value, new DateTimeZone('UTC'));
805
+					$gmt_date = ModelDataTranslator::prepareFieldValuesForJson(
806
+						$field_obj,
807
+						$field_value,
808
+						$this->getModelVersionInfo()->requestedVersion()
809
+					);
810
+					EEH_DTT_Helper::setTimezone($field_value, $timezone);
811
+					$local_date = ModelDataTranslator::prepareFieldValuesForJson(
812
+						$field_obj,
813
+						$field_value,
814
+						$this->getModelVersionInfo()->requestedVersion()
815
+					);
816
+				}
817
+				$result[ $field_name . '_gmt' ] = $gmt_date;
818
+				$result[ $field_name ] = $local_date;
819
+			} else {
820
+				$result[ $field_name ] = $this->prepareFieldObjValueForJson($field_obj, $field_value);
821
+			}
822
+		}
823
+		if ($do_chevy_shuffle) {
824
+			$post = $old_post;
825
+		}
826
+		return $result;
827
+	}
828
+
829
+
830
+	/**
831
+	 * Takes a value all the way from the DB representation, to the model object's representation, to the
832
+	 * user-facing PHP representation, to the REST API representation. (Assumes you've already taken from the DB
833
+	 * representation using $field_obj->prepare_for_set_from_db())
834
+	 *
835
+	 * @param EE_Model_Field_Base $field_obj
836
+	 * @param mixed               $value  as it's stored on a model object
837
+	 * @param string              $format valid values are 'normal' (default), 'pretty', 'datetime_obj'
838
+	 * @return mixed
839
+	 * @throws ObjectDetectedException if $value contains a PHP object
840
+	 */
841
+	protected function prepareFieldObjValueForJson(EE_Model_Field_Base $field_obj, $value, $format = 'normal')
842
+	{
843
+		$value = $field_obj->prepare_for_set_from_db($value);
844
+		switch ($format) {
845
+			case 'pretty':
846
+				$value = $field_obj->prepare_for_pretty_echoing($value);
847
+				break;
848
+			case 'normal':
849
+			default:
850
+				$value = $field_obj->prepare_for_get($value);
851
+				break;
852
+		}
853
+		return ModelDataTranslator::prepareFieldValuesForJson(
854
+			$field_obj,
855
+			$value,
856
+			$this->getModelVersionInfo()->requestedVersion()
857
+		);
858
+	}
859
+
860
+
861
+	/**
862
+	 * Adds a few extra fields to the entity response
863
+	 *
864
+	 * @param EEM_Base $model
865
+	 * @param array    $db_row
866
+	 * @param array    $entity_array
867
+	 * @return array modified entity
868
+	 */
869
+	protected function addExtraFields(EEM_Base $model, $db_row, $entity_array)
870
+	{
871
+		if ($model instanceof EEM_CPT_Base) {
872
+			$entity_array['link'] = get_permalink($db_row[ $model->get_primary_key_field()->get_qualified_column() ]);
873
+		}
874
+		return $entity_array;
875
+	}
876
+
877
+
878
+	/**
879
+	 * Gets links we want to add to the response
880
+	 *
881
+	 * @global \WP_REST_Server $wp_rest_server
882
+	 * @param EEM_Base         $model
883
+	 * @param array            $db_row
884
+	 * @param array            $entity_array
885
+	 * @return array the _links item in the entity
886
+	 */
887
+	protected function getEntityLinks($model, $db_row, $entity_array)
888
+	{
889
+		// add basic links
890
+		$links = array();
891
+		if ($model->has_primary_key_field()) {
892
+			$links['self'] = array(
893
+				array(
894
+					'href' => $this->getVersionedLinkTo(
895
+						EEH_Inflector::pluralize_and_lower($model->get_this_model_name())
896
+						. '/'
897
+						. $entity_array[ $model->primary_key_name() ]
898
+					),
899
+				),
900
+			);
901
+		}
902
+		$links['collection'] = array(
903
+			array(
904
+				'href' => $this->getVersionedLinkTo(
905
+					EEH_Inflector::pluralize_and_lower($model->get_this_model_name())
906
+				),
907
+			),
908
+		);
909
+		// add links to related models
910
+		if ($model->has_primary_key_field()) {
911
+			foreach ($this->getModelVersionInfo()->relationSettings($model) as $relation_name => $relation_obj) {
912
+				$related_model_part = Read::getRelatedEntityName($relation_name, $relation_obj);
913
+				$links[ EED_Core_Rest_Api::ee_api_link_namespace . $related_model_part ] = array(
914
+					array(
915
+						'href'   => $this->getVersionedLinkTo(
916
+							EEH_Inflector::pluralize_and_lower($model->get_this_model_name())
917
+							. '/'
918
+							. $entity_array[ $model->primary_key_name() ]
919
+							. '/'
920
+							. $related_model_part
921
+						),
922
+						'single' => $relation_obj instanceof EE_Belongs_To_Relation ? true : false,
923
+					),
924
+				);
925
+			}
926
+		}
927
+		return $links;
928
+	}
929
+
930
+
931
+	/**
932
+	 * Adds the included models indicated in the request to the entity provided
933
+	 *
934
+	 * @param EEM_Base $model
935
+	 * @param WP_REST_Request $rest_request
936
+	 * @param array $entity_array
937
+	 * @param array $db_row
938
+	 * @param boolean $included_items_protected if the original item is password protected, don't include any related models.
939
+	 * @return array the modified entity
940
+	 * @throws RestException
941
+	 */
942
+	protected function includeRequestedModels(
943
+		EEM_Base $model,
944
+		WP_REST_Request $rest_request,
945
+		$entity_array,
946
+		$db_row = array(),
947
+		$included_items_protected = false
948
+	) {
949
+		// if $db_row not included, hope the entity array has what we need
950
+		if (! $db_row) {
951
+			$db_row = $entity_array;
952
+		}
953
+		$relation_settings = $this->getModelVersionInfo()->relationSettings($model);
954
+		foreach ($relation_settings as $relation_name => $relation_obj) {
955
+			$related_fields_to_include = $this->explodeAndGetItemsPrefixedWith(
956
+				$rest_request->get_param('include'),
957
+				$relation_name
958
+			);
959
+			$related_fields_to_calculate = $this->explodeAndGetItemsPrefixedWith(
960
+				$rest_request->get_param('calculate'),
961
+				$relation_name
962
+			);
963
+			// did they specify they wanted to include a related model, or
964
+			// specific fields from a related model?
965
+			// or did they specify to calculate a field from a related model?
966
+			if ($related_fields_to_include || $related_fields_to_calculate) {
967
+				// if so, we should include at least some part of the related model
968
+				$pretend_related_request = new WP_REST_Request();
969
+				$pretend_related_request->set_query_params(
970
+					array(
971
+						'caps'      => $rest_request->get_param('caps'),
972
+						'include'   => $related_fields_to_include,
973
+						'calculate' => $related_fields_to_calculate,
974
+						'password' => $rest_request->get_param('password')
975
+					)
976
+				);
977
+				$pretend_related_request->add_header('no_rest_headers', true);
978
+				$primary_model_query_params = $model->alter_query_params_to_restrict_by_ID(
979
+					$model->get_index_primary_key_string(
980
+						$model->deduce_fields_n_values_from_cols_n_values($db_row)
981
+					)
982
+				);
983
+				if (! $included_items_protected) {
984
+					$related_results = $this->getEntitiesFromRelationUsingModelQueryParams(
985
+						$primary_model_query_params,
986
+						$relation_obj,
987
+						$pretend_related_request
988
+					);
989
+				} else {
990
+					// they're protected, hide them.
991
+					$related_results = $relation_obj instanceof EE_Belongs_To_Relation ? null : array();
992
+					$entity_array['_protected'][] = Read::getRelatedEntityName($relation_name, $relation_obj);
993
+				}
994
+				if ($related_results instanceof WP_Error) {
995
+					$related_results = null;
996
+				}
997
+				$entity_array[ Read::getRelatedEntityName($relation_name, $relation_obj) ] = $related_results;
998
+			}
999
+		}
1000
+		return $entity_array;
1001
+	}
1002
+
1003
+	/**
1004
+	 * If the user has requested only specific properties (including meta properties like _links or _protected)
1005
+	 * remove everything else.
1006
+	 * @since 4.9.74.p
1007
+	 * @param EEM_Base $model
1008
+	 * @param WP_REST_Request $rest_request
1009
+	 * @param $entity_array
1010
+	 * @return array
1011
+	 * @throws EE_Error
1012
+	 */
1013
+	protected function includeOnlyRequestedProperties(
1014
+		EEM_Base $model,
1015
+		WP_REST_Request $rest_request,
1016
+		$entity_array
1017
+	) {
1018
+
1019
+		$includes_for_this_model = $this->explodeAndGetItemsPrefixedWith($rest_request->get_param('include'), '');
1020
+		$includes_for_this_model = $this->removeModelNamesFromArray($includes_for_this_model);
1021
+		// if they passed in * or didn't specify any includes, return everything
1022
+		if (! in_array('*', $includes_for_this_model)
1023
+			&& ! empty($includes_for_this_model)
1024
+		) {
1025
+			if ($model->has_primary_key_field()) {
1026
+				// always include the primary key. ya just gotta know that at least
1027
+				$includes_for_this_model[] = $model->primary_key_name();
1028
+			}
1029
+			if ($this->explodeAndGetItemsPrefixedWith($rest_request->get_param('calculate'), '')) {
1030
+				$includes_for_this_model[] = '_calculated_fields';
1031
+			}
1032
+			$entity_array = array_intersect_key($entity_array, array_flip($includes_for_this_model));
1033
+		}
1034
+		return $entity_array;
1035
+	}
1036
+
1037
+
1038
+	/**
1039
+	 * Returns a new array with all the names of models removed. Eg
1040
+	 * array( 'Event', 'Datetime.*', 'foobar' ) would become array( 'Datetime.*', 'foobar' )
1041
+	 *
1042
+	 * @param array $arr
1043
+	 * @return array
1044
+	 */
1045
+	private function removeModelNamesFromArray($arr)
1046
+	{
1047
+		return array_diff($arr, array_keys(EE_Registry::instance()->non_abstract_db_models));
1048
+	}
1049
+
1050
+
1051
+	/**
1052
+	 * Gets the calculated fields for the response
1053
+	 *
1054
+	 * @param EEM_Base        $model
1055
+	 * @param array           $wpdb_row
1056
+	 * @param WP_REST_Request $rest_request
1057
+	 * @param boolean $row_is_protected whether this row is password protected or not
1058
+	 * @return \stdClass the _calculations item in the entity
1059
+	 * @throws ObjectDetectedException if a default value has a PHP object, which should never do (and if we
1060
+	 * did, let's know about it ASAP, so let the exception bubble up)
1061
+	 */
1062
+	protected function getEntityCalculations($model, $wpdb_row, $rest_request, $row_is_protected = false)
1063
+	{
1064
+		$calculated_fields = $this->explodeAndGetItemsPrefixedWith(
1065
+			$rest_request->get_param('calculate'),
1066
+			''
1067
+		);
1068
+		// note: setting calculate=* doesn't do anything
1069
+		$calculated_fields_to_return = new \stdClass();
1070
+		$protected_fields = array();
1071
+		foreach ($calculated_fields as $field_to_calculate) {
1072
+			try {
1073
+				// it's password protected, so they shouldn't be able to read this. Remove the value
1074
+				$schema = $this->fields_calculator->getJsonSchemaForModel($model);
1075
+				if ($row_is_protected
1076
+					&& isset($schema['properties'][ $field_to_calculate ]['protected'])
1077
+					&& $schema['properties'][ $field_to_calculate ]['protected']) {
1078
+					$calculated_value = null;
1079
+					$protected_fields[] = $field_to_calculate;
1080
+					if ($schema['properties'][ $field_to_calculate ]['type']) {
1081
+						switch ($schema['properties'][ $field_to_calculate ]['type']) {
1082
+							case 'boolean':
1083
+								$calculated_value = false;
1084
+								break;
1085
+							case 'integer':
1086
+								$calculated_value = 0;
1087
+								break;
1088
+							case 'string':
1089
+								$calculated_value = '';
1090
+								break;
1091
+							case 'array':
1092
+								$calculated_value = array();
1093
+								break;
1094
+							case 'object':
1095
+								$calculated_value = new stdClass();
1096
+								break;
1097
+						}
1098
+					}
1099
+				} else {
1100
+					$calculated_value = ModelDataTranslator::prepareFieldValueForJson(
1101
+						null,
1102
+						$this->fields_calculator->retrieveCalculatedFieldValue(
1103
+							$model,
1104
+							$field_to_calculate,
1105
+							$wpdb_row,
1106
+							$rest_request,
1107
+							$this
1108
+						),
1109
+						$this->getModelVersionInfo()->requestedVersion()
1110
+					);
1111
+				}
1112
+				$calculated_fields_to_return->{$field_to_calculate} = $calculated_value;
1113
+			} catch (RestException $e) {
1114
+				// if we don't have permission to read it, just leave it out. but let devs know about the problem
1115
+				$this->setResponseHeader(
1116
+					'Notices-Field-Calculation-Errors['
1117
+					. $e->getStringCode()
1118
+					. ']['
1119
+					. $model->get_this_model_name()
1120
+					. ']['
1121
+					. $field_to_calculate
1122
+					. ']',
1123
+					$e->getMessage(),
1124
+					true
1125
+				);
1126
+			}
1127
+		}
1128
+		$calculated_fields_to_return->_protected = $protected_fields;
1129
+		return $calculated_fields_to_return;
1130
+	}
1131
+
1132
+
1133
+	/**
1134
+	 * Gets the full URL to the resource, taking the requested version into account
1135
+	 *
1136
+	 * @param string $link_part_after_version_and_slash eg "events/10/datetimes"
1137
+	 * @return string url eg "http://mysite.com/wp-json/ee/v4.6/events/10/datetimes"
1138
+	 */
1139
+	public function getVersionedLinkTo($link_part_after_version_and_slash)
1140
+	{
1141
+		return rest_url(
1142
+			EED_Core_Rest_Api::get_versioned_route_to(
1143
+				$link_part_after_version_and_slash,
1144
+				$this->getModelVersionInfo()->requestedVersion()
1145
+			)
1146
+		);
1147
+	}
1148
+
1149
+
1150
+	/**
1151
+	 * Gets the correct lowercase name for the relation in the API according
1152
+	 * to the relation's type
1153
+	 *
1154
+	 * @param string                  $relation_name
1155
+	 * @param \EE_Model_Relation_Base $relation_obj
1156
+	 * @return string
1157
+	 */
1158
+	public static function getRelatedEntityName($relation_name, $relation_obj)
1159
+	{
1160
+		if ($relation_obj instanceof EE_Belongs_To_Relation) {
1161
+			return strtolower($relation_name);
1162
+		} else {
1163
+			return EEH_Inflector::pluralize_and_lower($relation_name);
1164
+		}
1165
+	}
1166
+
1167
+
1168
+	/**
1169
+	 * Gets the one model object with the specified id for the specified model
1170
+	 *
1171
+	 * @param EEM_Base        $model
1172
+	 * @param WP_REST_Request $request
1173
+	 * @return array
1174
+	 */
1175
+	public function getEntityFromModel($model, $request)
1176
+	{
1177
+		$context = $this->validateContext($request->get_param('caps'));
1178
+		return $this->getOneOrReportPermissionError($model, $request, $context);
1179
+	}
1180
+
1181
+
1182
+	/**
1183
+	 * If a context is provided which isn't valid, maybe it was added in a future
1184
+	 * version so just treat it as a default read
1185
+	 *
1186
+	 * @param string $context
1187
+	 * @return string array key of EEM_Base::cap_contexts_to_cap_action_map()
1188
+	 */
1189
+	public function validateContext($context)
1190
+	{
1191
+		if (! $context) {
1192
+			$context = EEM_Base::caps_read;
1193
+		}
1194
+		$valid_contexts = EEM_Base::valid_cap_contexts();
1195
+		if (in_array($context, $valid_contexts)) {
1196
+			return $context;
1197
+		} else {
1198
+			return EEM_Base::caps_read;
1199
+		}
1200
+	}
1201
+
1202
+
1203
+	/**
1204
+	 * Verifies the passed in value is an allowable default where conditions value.
1205
+	 *
1206
+	 * @param $default_query_params
1207
+	 * @return string
1208
+	 */
1209
+	public function validateDefaultQueryParams($default_query_params)
1210
+	{
1211
+		$valid_default_where_conditions_for_api_calls = array(
1212
+			EEM_Base::default_where_conditions_all,
1213
+			EEM_Base::default_where_conditions_minimum_all,
1214
+			EEM_Base::default_where_conditions_minimum_others,
1215
+		);
1216
+		if (! $default_query_params) {
1217
+			$default_query_params = EEM_Base::default_where_conditions_all;
1218
+		}
1219
+		if (in_array(
1220
+			$default_query_params,
1221
+			$valid_default_where_conditions_for_api_calls,
1222
+			true
1223
+		)) {
1224
+			return $default_query_params;
1225
+		} else {
1226
+			return EEM_Base::default_where_conditions_all;
1227
+		}
1228
+	}
1229
+
1230
+
1231
+	/**
1232
+	 * Translates API filter get parameter into model query params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md#0-where-conditions.
1233
+	 * Note: right now the query parameter keys for fields (and related fields)
1234
+	 * can be left as-is, but it's quite possible this will change someday.
1235
+	 * Also, this method's contents might be candidate for moving to Model_Data_Translator
1236
+	 *
1237
+	 * @param EEM_Base $model
1238
+	 * @param array    $query_parameters  from $_GET parameter @see Read:handle_request_get_all
1239
+	 * @return array model query params (@see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md#0-where-conditions)
1240
+	 *                                    or FALSE to indicate that absolutely no results should be returned
1241
+	 * @throws EE_Error
1242
+	 * @throws RestException
1243
+	 */
1244
+	public function createModelQueryParams($model, $query_params)
1245
+	{
1246
+		$model_query_params = array();
1247
+		if (isset($query_params['where'])) {
1248
+			$model_query_params[0] = ModelDataTranslator::prepareConditionsQueryParamsForModels(
1249
+				$query_params['where'],
1250
+				$model,
1251
+				$this->getModelVersionInfo()->requestedVersion()
1252
+			);
1253
+		}
1254
+		if (isset($query_params['order_by'])) {
1255
+			$order_by = $query_params['order_by'];
1256
+		} elseif (isset($query_params['orderby'])) {
1257
+			$order_by = $query_params['orderby'];
1258
+		} else {
1259
+			$order_by = null;
1260
+		}
1261
+		if ($order_by !== null) {
1262
+			if (is_array($order_by)) {
1263
+				$order_by = ModelDataTranslator::prepareFieldNamesInArrayKeysFromJson($order_by);
1264
+			} else {
1265
+				// it's a single item
1266
+				$order_by = ModelDataTranslator::prepareFieldNameFromJson($order_by);
1267
+			}
1268
+			$model_query_params['order_by'] = $order_by;
1269
+		}
1270
+		if (isset($query_params['group_by'])) {
1271
+			$group_by = $query_params['group_by'];
1272
+		} elseif (isset($query_params['groupby'])) {
1273
+			$group_by = $query_params['groupby'];
1274
+		} else {
1275
+			$group_by = array_keys($model->get_combined_primary_key_fields());
1276
+		}
1277
+		// make sure they're all real names
1278
+		if (is_array($group_by)) {
1279
+			$group_by = ModelDataTranslator::prepareFieldNamesFromJson($group_by);
1280
+		}
1281
+		if ($group_by !== null) {
1282
+			$model_query_params['group_by'] = $group_by;
1283
+		}
1284
+		if (isset($query_params['having'])) {
1285
+			$model_query_params['having'] = ModelDataTranslator::prepareConditionsQueryParamsForModels(
1286
+				$query_params['having'],
1287
+				$model,
1288
+				$this->getModelVersionInfo()->requestedVersion()
1289
+			);
1290
+		}
1291
+		if (isset($query_params['order'])) {
1292
+			$model_query_params['order'] = $query_params['order'];
1293
+		}
1294
+		if (isset($query_params['mine'])) {
1295
+			$model_query_params = $model->alter_query_params_to_only_include_mine($model_query_params);
1296
+		}
1297
+		if (isset($query_params['limit'])) {
1298
+			// limit should be either a string like '23' or '23,43', or an array with two items in it
1299
+			if (! is_array($query_params['limit'])) {
1300
+				$limit_array = explode(',', (string) $query_params['limit']);
1301
+			} else {
1302
+				$limit_array = $query_params['limit'];
1303
+			}
1304
+			$sanitized_limit = array();
1305
+			foreach ($limit_array as $key => $limit_part) {
1306
+				if ($this->debug_mode && (! is_numeric($limit_part) || count($sanitized_limit) > 2)) {
1307
+					throw new EE_Error(
1308
+						sprintf(
1309
+							__(
1310
+							// @codingStandardsIgnoreStart
1311
+								'An invalid limit filter was provided. It was: %s. If the EE4 JSON REST API weren\'t in debug mode, this message would not appear.',
1312
+								// @codingStandardsIgnoreEnd
1313
+								'event_espresso'
1314
+							),
1315
+							wp_json_encode($query_params['limit'])
1316
+						)
1317
+					);
1318
+				}
1319
+				$sanitized_limit[] = (int) $limit_part;
1320
+			}
1321
+			$model_query_params['limit'] = implode(',', $sanitized_limit);
1322
+		} else {
1323
+			$model_query_params['limit'] = EED_Core_Rest_Api::get_default_query_limit();
1324
+		}
1325
+		if (isset($query_params['caps'])) {
1326
+			$model_query_params['caps'] = $this->validateContext($query_params['caps']);
1327
+		} else {
1328
+			$model_query_params['caps'] = EEM_Base::caps_read;
1329
+		}
1330
+		if (isset($query_params['default_where_conditions'])) {
1331
+			$model_query_params['default_where_conditions'] = $this->validateDefaultQueryParams(
1332
+				$query_params['default_where_conditions']
1333
+			);
1334
+		}
1335
+		// if this is a model protected by a password on another model, exclude the password protected
1336
+		// entities by default. But if they passed in a password, try to show them all. If the password is wrong,
1337
+		// though, they'll get an error (see Read::createEntityFromWpdbResult() which calls Read::checkPassword)
1338
+		if (! $model->hasPassword()
1339
+			&& $model->restrictedByRelatedModelPassword()
1340
+			&& $model_query_params['caps'] === EEM_Base::caps_read) {
1341
+			if (empty($query_params['password'])) {
1342
+				$model_query_params['exclude_protected'] = true;
1343
+			}
1344
+		}
1345
+
1346
+		return apply_filters('FHEE__Read__create_model_query_params', $model_query_params, $query_params, $model);
1347
+	}
1348
+
1349
+
1350
+	/**
1351
+	 * Changes the REST-style query params for use in the models
1352
+	 *
1353
+	 * @deprecated
1354
+	 * @param EEM_Base $model
1355
+	 * @param array    $query_params sub-array from @see EEM_Base::get_all()
1356
+	 * @return array
1357
+	 */
1358
+	public function prepareRestQueryParamsKeyForModels($model, $query_params)
1359
+	{
1360
+		$model_ready_query_params = array();
1361
+		foreach ($query_params as $key => $value) {
1362
+			if (is_array($value)) {
1363
+				$model_ready_query_params[ $key ] = $this->prepareRestQueryParamsKeyForModels($model, $value);
1364
+			} else {
1365
+				$model_ready_query_params[ $key ] = $value;
1366
+			}
1367
+		}
1368
+		return $model_ready_query_params;
1369
+	}
1370
+
1371
+
1372
+	/**
1373
+	 * @deprecated instead use ModelDataTranslator::prepareFieldValuesFromJson()
1374
+	 * @param $model
1375
+	 * @param $query_params
1376
+	 * @return array
1377
+	 */
1378
+	public function prepareRestQueryParamsValuesForModels($model, $query_params)
1379
+	{
1380
+		$model_ready_query_params = array();
1381
+		foreach ($query_params as $key => $value) {
1382
+			if (is_array($value)) {
1383
+				$model_ready_query_params[ $key ] = $this->prepareRestQueryParamsValuesForModels($model, $value);
1384
+			} else {
1385
+				$model_ready_query_params[ $key ] = $value;
1386
+			}
1387
+		}
1388
+		return $model_ready_query_params;
1389
+	}
1390
+
1391
+
1392
+	/**
1393
+	 * Explodes the string on commas, and only returns items with $prefix followed by a period.
1394
+	 * If no prefix is specified, returns items with no period.
1395
+	 *
1396
+	 * @param string|array $string_to_explode eg "jibba,jabba, blah, blah, blah" or array('jibba', 'jabba' )
1397
+	 * @param string       $prefix            "Event" or "foobar"
1398
+	 * @return array $string_to_exploded exploded on COMMAS, and if a prefix was specified
1399
+	 *                                        we only return strings starting with that and a period; if no prefix was
1400
+	 *                                        specified we return all items containing NO periods
1401
+	 */
1402
+	public function explodeAndGetItemsPrefixedWith($string_to_explode, $prefix)
1403
+	{
1404
+		if (is_string($string_to_explode)) {
1405
+			$exploded_contents = explode(',', $string_to_explode);
1406
+		} elseif (is_array($string_to_explode)) {
1407
+			$exploded_contents = $string_to_explode;
1408
+		} else {
1409
+			$exploded_contents = array();
1410
+		}
1411
+		// if the string was empty, we want an empty array
1412
+		$exploded_contents = array_filter($exploded_contents);
1413
+		$contents_with_prefix = array();
1414
+		foreach ($exploded_contents as $item) {
1415
+			$item = trim($item);
1416
+			// if no prefix was provided, so we look for items with no "." in them
1417
+			if (! $prefix) {
1418
+				// does this item have a period?
1419
+				if (strpos($item, '.') === false) {
1420
+					// if not, then its what we're looking for
1421
+					$contents_with_prefix[] = $item;
1422
+				}
1423
+			} elseif (strpos($item, $prefix . '.') === 0) {
1424
+				// this item has the prefix and a period, grab it
1425
+				$contents_with_prefix[] = substr(
1426
+					$item,
1427
+					strpos($item, $prefix . '.') + strlen($prefix . '.')
1428
+				);
1429
+			} elseif ($item === $prefix) {
1430
+				// this item is JUST the prefix
1431
+				// so let's grab everything after, which is a blank string
1432
+				$contents_with_prefix[] = '';
1433
+			}
1434
+		}
1435
+		return $contents_with_prefix;
1436
+	}
1437
+
1438
+
1439
+	/**
1440
+	 * @deprecated since 4.8.36.rc.001 You should instead use Read::explode_and_get_items_prefixed_with.
1441
+	 * Deprecated because its return values were really quite confusing- sometimes it returned
1442
+	 * an empty array (when the include string was blank or '*') or sometimes it returned
1443
+	 * array('*') (when you provided a model and a model of that kind was found).
1444
+	 * Parses the $include_string so we fetch all the field names relating to THIS model
1445
+	 * (ie have NO period in them), or for the provided model (ie start with the model
1446
+	 * name and then a period).
1447
+	 * @param string $include_string @see Read:handle_request_get_all
1448
+	 * @param string $model_name
1449
+	 * @return array of fields for this model. If $model_name is provided, then
1450
+	 *                               the fields for that model, with the model's name removed from each.
1451
+	 *                               If $include_string was blank or '*' returns an empty array
1452
+	 */
1453
+	public function extractIncludesForThisModel($include_string, $model_name = null)
1454
+	{
1455
+		if (is_array($include_string)) {
1456
+			$include_string = implode(',', $include_string);
1457
+		}
1458
+		if ($include_string === '*' || $include_string === '') {
1459
+			return array();
1460
+		}
1461
+		$includes = explode(',', $include_string);
1462
+		$extracted_fields_to_include = array();
1463
+		if ($model_name) {
1464
+			foreach ($includes as $field_to_include) {
1465
+				$field_to_include = trim($field_to_include);
1466
+				if (strpos($field_to_include, $model_name . '.') === 0) {
1467
+					// found the model name at the exact start
1468
+					$field_sans_model_name = str_replace($model_name . '.', '', $field_to_include);
1469
+					$extracted_fields_to_include[] = $field_sans_model_name;
1470
+				} elseif ($field_to_include == $model_name) {
1471
+					$extracted_fields_to_include[] = '*';
1472
+				}
1473
+			}
1474
+		} else {
1475
+			// look for ones with no period
1476
+			foreach ($includes as $field_to_include) {
1477
+				$field_to_include = trim($field_to_include);
1478
+				if (strpos($field_to_include, '.') === false
1479
+					&& ! $this->getModelVersionInfo()->isModelNameInThisVersion($field_to_include)
1480
+				) {
1481
+					$extracted_fields_to_include[] = $field_to_include;
1482
+				}
1483
+			}
1484
+		}
1485
+		return $extracted_fields_to_include;
1486
+	}
1487
+
1488
+
1489
+	/**
1490
+	 * Gets the single item using the model according to the request in the context given, otherwise
1491
+	 * returns that it's inaccessible to the current user
1492
+	 *
1493
+	 * @param EEM_Base $model
1494
+	 * @param WP_REST_Request $request
1495
+	 * @param null $context
1496
+	 * @return array
1497
+	 * @throws EE_Error
1498
+	 */
1499
+	public function getOneOrReportPermissionError(EEM_Base $model, WP_REST_Request $request, $context = null)
1500
+	{
1501
+		$query_params = array(array($model->primary_key_name() => $request->get_param('id')), 'limit' => 1);
1502
+		if ($model instanceof EEM_Soft_Delete_Base) {
1503
+			$query_params = $model->alter_query_params_so_deleted_and_undeleted_items_included($query_params);
1504
+		}
1505
+		$restricted_query_params = $query_params;
1506
+		$restricted_query_params['caps'] = $context;
1507
+		$this->setDebugInfo('model query params', $restricted_query_params);
1508
+		$model_rows = $model->get_all_wpdb_results($restricted_query_params);
1509
+		if (! empty($model_rows)) {
1510
+			return $this->createEntityFromWpdbResult(
1511
+				$model,
1512
+				reset($model_rows),
1513
+				$request
1514
+			);
1515
+		} else {
1516
+			// ok let's test to see if we WOULD have found it, had we not had restrictions from missing capabilities
1517
+			$lowercase_model_name = strtolower($model->get_this_model_name());
1518
+			if ($model->exists($query_params)) {
1519
+				// you got shafted- it existed but we didn't want to tell you!
1520
+				throw new RestException(
1521
+					'rest_user_cannot_' . $context,
1522
+					sprintf(
1523
+						__('Sorry, you cannot %1$s this %2$s. Missing permissions are: %3$s', 'event_espresso'),
1524
+						$context,
1525
+						$lowercase_model_name,
1526
+						Capabilities::getMissingPermissionsString(
1527
+							$model,
1528
+							$context
1529
+						)
1530
+					),
1531
+					array('status' => 403)
1532
+				);
1533
+			} else {
1534
+				// it's not you. It just doesn't exist
1535
+				throw new RestException(
1536
+					sprintf('rest_%s_invalid_id', $lowercase_model_name),
1537
+					sprintf(__('Invalid %s ID.', 'event_espresso'), $lowercase_model_name),
1538
+					array('status' => 404)
1539
+				);
1540
+			}
1541
+		}
1542
+	}
1543
+
1544
+	/**
1545
+	 * Checks that if this content requires a password to be read, that it's been provided and is correct.
1546
+	 * @since 4.9.74.p
1547
+	 * @param EEM_Base $model
1548
+	 * @param $model_row
1549
+	 * @param $query_params
1550
+	 * @param WP_REST_Request $request
1551
+	 * @throws EE_Error
1552
+	 * @throws InvalidArgumentException
1553
+	 * @throws InvalidDataTypeException
1554
+	 * @throws InvalidInterfaceException
1555
+	 * @throws RestPasswordRequiredException
1556
+	 * @throws RestPasswordIncorrectException
1557
+	 * @throws \EventEspresso\core\exceptions\ModelConfigurationException
1558
+	 * @throws ReflectionException
1559
+	 */
1560
+	protected function checkPassword(EEM_Base $model, $model_row, $query_params, WP_REST_Request $request)
1561
+	{
1562
+		// stuff is only "protected" for front-end requests. Elsewhere, you either get full permission to access the object
1563
+		// or you don't.
1564
+		$request_caps = $request->get_param('caps');
1565
+		if (isset($request_caps) && $request_caps !== EEM_Base::caps_read) {
1566
+			return;
1567
+		}
1568
+		// if this entity requires a password, they better give it and it better be right!
1569
+		if ($model->hasPassword()
1570
+			&& $model_row[ $model->getPasswordField()->get_qualified_column() ] !== '') {
1571
+			if (empty($request['password'])) {
1572
+				throw new RestPasswordRequiredException();
1573
+			} elseif (!hash_equals(
1574
+				$model_row[ $model->getPasswordField()->get_qualified_column() ],
1575
+				$request['password']
1576
+			)) {
1577
+				throw new RestPasswordIncorrectException();
1578
+			}
1579
+		} // wait! maybe this content is password protected
1580
+		elseif ($model->restrictedByRelatedModelPassword()
1581
+			&& $request->get_param('caps') === EEM_Base::caps_read) {
1582
+			$password_supplied = $request->get_param('password');
1583
+			if (empty($password_supplied)) {
1584
+				$query_params['exclude_protected'] = true;
1585
+				if (!$model->exists($query_params)) {
1586
+					throw new RestPasswordRequiredException();
1587
+				}
1588
+			} else {
1589
+				$query_params[0][ $model->modelChainAndPassword() ] = $password_supplied;
1590
+				if (!$model->exists($query_params)) {
1591
+					throw new RestPasswordIncorrectException();
1592
+				}
1593
+			}
1594
+		}
1595
+	}
1596 1596
 }
Please login to merge, or discard this patch.
Spacing   +69 added lines, -69 removed lines patch added patch discarded remove patch
@@ -78,7 +78,7 @@  discard block
 block discarded – undo
78 78
         $controller = LoaderFactory::getLoader()->getNew('EventEspresso\core\libraries\rest_api\controllers\model\Read');
79 79
         try {
80 80
             $controller->setRequestedVersion($version);
81
-            if (! $controller->getModelVersionInfo()->isModelNameInThisVersion($model_name)) {
81
+            if ( ! $controller->getModelVersionInfo()->isModelNameInThisVersion($model_name)) {
82 82
                 return $controller->sendResponse(
83 83
                     new WP_Error(
84 84
                         'endpoint_parsing_error',
@@ -119,7 +119,7 @@  discard block
 block discarded – undo
119 119
         $controller = LoaderFactory::getLoader()->getNew('EventEspresso\core\libraries\rest_api\controllers\model\Read');
120 120
         try {
121 121
             $controller->setRequestedVersion($version);
122
-            if (! $controller->getModelVersionInfo()->isModelNameInThisVersion($model_name)) {
122
+            if ( ! $controller->getModelVersionInfo()->isModelNameInThisVersion($model_name)) {
123 123
                 return array();
124 124
             }
125 125
             // get the model for this version
@@ -176,11 +176,11 @@  discard block
 block discarded – undo
176 176
      */
177 177
     protected function translateDefaultsForRestResponse($field_name, EE_Model_Field_Base $field, array $schema)
178 178
     {
179
-        if (isset($schema['properties'][ $field_name ]['default'])) {
180
-            if (is_array($schema['properties'][ $field_name ]['default'])) {
181
-                foreach ($schema['properties'][ $field_name ]['default'] as $default_key => $default_value) {
179
+        if (isset($schema['properties'][$field_name]['default'])) {
180
+            if (is_array($schema['properties'][$field_name]['default'])) {
181
+                foreach ($schema['properties'][$field_name]['default'] as $default_key => $default_value) {
182 182
                     if ($default_key === 'raw') {
183
-                        $schema['properties'][ $field_name ]['default'][ $default_key ] =
183
+                        $schema['properties'][$field_name]['default'][$default_key] =
184 184
                             ModelDataTranslator::prepareFieldValueForJson(
185 185
                                 $field,
186 186
                                 $default_value,
@@ -189,9 +189,9 @@  discard block
 block discarded – undo
189 189
                     }
190 190
                 }
191 191
             } else {
192
-                $schema['properties'][ $field_name ]['default'] = ModelDataTranslator::prepareFieldValueForJson(
192
+                $schema['properties'][$field_name]['default'] = ModelDataTranslator::prepareFieldValueForJson(
193 193
                     $field,
194
-                    $schema['properties'][ $field_name ]['default'],
194
+                    $schema['properties'][$field_name]['default'],
195 195
                     $this->getModelVersionInfo()->requestedVersion()
196 196
                 );
197 197
             }
@@ -213,9 +213,9 @@  discard block
 block discarded – undo
213 213
     protected function maybeAddExtraFieldsToSchema($field_name, EE_Model_Field_Base $field, array $schema)
214 214
     {
215 215
         if ($field instanceof EE_Datetime_Field) {
216
-            $schema['properties'][ $field_name . '_gmt' ] = $field->getSchema();
216
+            $schema['properties'][$field_name.'_gmt'] = $field->getSchema();
217 217
             // modify the description
218
-            $schema['properties'][ $field_name . '_gmt' ]['description'] = sprintf(
218
+            $schema['properties'][$field_name.'_gmt']['description'] = sprintf(
219 219
                 esc_html__('%s - the value for this field is in GMT.', 'event_espresso'),
220 220
                 wp_specialchars_decode($field->get_nicename(), ENT_QUOTES)
221 221
             );
@@ -258,7 +258,7 @@  discard block
 block discarded – undo
258 258
         $controller = LoaderFactory::getLoader()->getNew('EventEspresso\core\libraries\rest_api\controllers\model\Read');
259 259
         try {
260 260
             $controller->setRequestedVersion($version);
261
-            if (! $controller->getModelVersionInfo()->isModelNameInThisVersion($model_name)) {
261
+            if ( ! $controller->getModelVersionInfo()->isModelNameInThisVersion($model_name)) {
262 262
                 return $controller->sendResponse(
263 263
                     new WP_Error(
264 264
                         'endpoint_parsing_error',
@@ -337,7 +337,7 @@  discard block
 block discarded – undo
337 337
     public function getEntitiesFromModel($model, $request)
338 338
     {
339 339
         $query_params = $this->createModelQueryParams($model, $request->get_params());
340
-        if (! Capabilities::currentUserHasPartialAccessTo($model, $query_params['caps'])) {
340
+        if ( ! Capabilities::currentUserHasPartialAccessTo($model, $query_params['caps'])) {
341 341
             $model_name_plural = EEH_Inflector::pluralize_and_lower($model->get_this_model_name());
342 342
             throw new RestException(
343 343
                 sprintf('rest_%s_cannot_list', $model_name_plural),
@@ -349,14 +349,14 @@  discard block
 block discarded – undo
349 349
                 array('status' => 403)
350 350
             );
351 351
         }
352
-        if (! $request->get_header('no_rest_headers')) {
352
+        if ( ! $request->get_header('no_rest_headers')) {
353 353
             $this->setHeadersFromQueryParams($model, $query_params);
354 354
         }
355 355
         /** @type array $results */
356 356
         $results = $model->get_all_wpdb_results($query_params);
357 357
         $nice_results = array();
358 358
         foreach ($results as $result) {
359
-            $nice_results[] =  $this->createEntityFromWpdbResult(
359
+            $nice_results[] = $this->createEntityFromWpdbResult(
360 360
                 $model,
361 361
                 $result,
362 362
                 $request
@@ -390,7 +390,7 @@  discard block
 block discarded – undo
390 390
         $context = $this->validateContext($request->get_param('caps'));
391 391
         $model = $relation->get_this_model();
392 392
         $related_model = $relation->get_other_model();
393
-        if (! isset($primary_model_query_params[0])) {
393
+        if ( ! isset($primary_model_query_params[0])) {
394 394
             $primary_model_query_params[0] = array();
395 395
         }
396 396
         // check if they can access the 1st model object
@@ -413,7 +413,7 @@  discard block
 block discarded – undo
413 413
         if (is_array($primary_model_rows)) {
414 414
             $primary_model_row = reset($primary_model_rows);
415 415
         }
416
-        if (! (
416
+        if ( ! (
417 417
             Capabilities::currentUserHasPartialAccessTo($related_model, $context)
418 418
             && $primary_model_row
419 419
         )
@@ -453,13 +453,13 @@  discard block
 block discarded – undo
453 453
         );
454 454
         $query_params = $this->createModelQueryParams($relation->get_other_model(), $request->get_params());
455 455
         foreach ($primary_model_query_params[0] as $where_condition_key => $where_condition_value) {
456
-            $query_params[0][ $relation->get_this_model()->get_this_model_name()
456
+            $query_params[0][$relation->get_this_model()->get_this_model_name()
457 457
                               . '.'
458
-                              . $where_condition_key ] = $where_condition_value;
458
+                              . $where_condition_key] = $where_condition_value;
459 459
         }
460 460
         $query_params['default_where_conditions'] = 'none';
461 461
         $query_params['caps'] = $context;
462
-        if (! $request->get_header('no_rest_headers')) {
462
+        if ( ! $request->get_header('no_rest_headers')) {
463 463
             $this->setHeadersFromQueryParams($relation->get_other_model(), $query_params);
464 464
         }
465 465
         /** @type array $results */
@@ -510,7 +510,7 @@  discard block
 block discarded – undo
510 510
      */
511 511
     public function getEntitiesFromRelation($id, $relation, $request)
512 512
     {
513
-        if (! $relation->get_this_model()->has_primary_key_field()) {
513
+        if ( ! $relation->get_this_model()->has_primary_key_field()) {
514 514
             throw new EE_Error(
515 515
                 sprintf(
516 516
                     __(
@@ -555,7 +555,7 @@  discard block
 block discarded – undo
555 555
             Capabilities::getMissingPermissionsString($model, $query_params['caps'])
556 556
         );
557 557
         // normally the limit to a 2-part array, where the 2nd item is the limit
558
-        if (! isset($query_params['limit'])) {
558
+        if ( ! isset($query_params['limit'])) {
559 559
             $query_params['limit'] = EED_Core_Rest_Api::get_default_query_limit();
560 560
         }
561 561
         if (is_array($query_params['limit'])) {
@@ -594,7 +594,7 @@  discard block
 block discarded – undo
594 594
      */
595 595
     public function createEntityFromWpdbResult($model, $db_row, $rest_request, $deprecated = null)
596 596
     {
597
-        if (! $rest_request instanceof WP_REST_Request) {
597
+        if ( ! $rest_request instanceof WP_REST_Request) {
598 598
             // ok so this was called in the old style, where the 3rd arg was
599 599
             // $include, and the 4th arg was $context
600 600
             // now setup the request just to avoid fatal errors, although we won't be able
@@ -622,7 +622,7 @@  discard block
 block discarded – undo
622 622
                 $db_row,
623 623
                 array(
624 624
                     0 => array(
625
-                        $model->primary_key_name() => $db_row[ $model->get_primary_key_field()->get_qualified_column() ]
625
+                        $model->primary_key_name() => $db_row[$model->get_primary_key_field()->get_qualified_column()]
626 626
                     )
627 627
                 ),
628 628
                 $rest_request
@@ -669,7 +669,7 @@  discard block
 block discarded – undo
669 669
             $rest_request,
670 670
             $this
671 671
         );
672
-        if (! $current_user_full_access_to_entity) {
672
+        if ( ! $current_user_full_access_to_entity) {
673 673
             $result_without_inaccessible_fields = Capabilities::filterOutInaccessibleEntityFields(
674 674
                 $entity_array,
675 675
                 $model,
@@ -701,7 +701,7 @@  discard block
 block discarded – undo
701 701
      */
702 702
     protected function addProtectedProperty(EEM_Base $model, $results_so_far, $protected)
703 703
     {
704
-        if (! $model->hasPassword() || ! $protected) {
704
+        if ( ! $model->hasPassword() || ! $protected) {
705 705
             return $results_so_far;
706 706
         }
707 707
         $password_field = $model->getPasswordField();
@@ -715,7 +715,7 @@  discard block
 block discarded – undo
715 715
             $fields_included
716 716
         );
717 717
         foreach ($fields_included as $field_name) {
718
-            $results_so_far['_protected'][] = $field_name ;
718
+            $results_so_far['_protected'][] = $field_name;
719 719
         }
720 720
         return $results_so_far;
721 721
     }
@@ -746,8 +746,8 @@  discard block
 block discarded – undo
746 746
         if ($do_chevy_shuffle) {
747 747
             global $post;
748 748
             $old_post = $post;
749
-            $post = get_post($result[ $model->primary_key_name() ]);
750
-            if (! $post instanceof \WP_Post) {
749
+            $post = get_post($result[$model->primary_key_name()]);
750
+            if ( ! $post instanceof \WP_Post) {
751 751
                 // well that's weird, because $result is what we JUST fetched from the database
752 752
                 throw new RestException(
753 753
                     'error_fetching_post_from_database_results',
@@ -757,7 +757,7 @@  discard block
 block discarded – undo
757 757
                     )
758 758
                 );
759 759
             }
760
-            $model_object_classname = 'EE_' . $model->get_this_model_name();
760
+            $model_object_classname = 'EE_'.$model->get_this_model_name();
761 761
             $post->{$model_object_classname} = \EE_Registry::instance()->load_class(
762 762
                 $model_object_classname,
763 763
                 $result,
@@ -768,13 +768,13 @@  discard block
 block discarded – undo
768 768
         foreach ($result as $field_name => $field_value) {
769 769
             $field_obj = $model->field_settings_for($field_name);
770 770
             if ($this->isSubclassOfOne($field_obj, $this->getModelVersionInfo()->fieldsIgnored())) {
771
-                unset($result[ $field_name ]);
771
+                unset($result[$field_name]);
772 772
             } elseif ($this->isSubclassOfOne(
773 773
                 $field_obj,
774 774
                 $this->getModelVersionInfo()->fieldsThatHaveRenderedFormat()
775 775
             )
776 776
             ) {
777
-                $result[ $field_name ] = array(
777
+                $result[$field_name] = array(
778 778
                     'raw'      => $this->prepareFieldObjValueForJson($field_obj, $field_value),
779 779
                     'rendered' => $this->prepareFieldObjValueForJson($field_obj, $field_value, 'pretty'),
780 780
                 );
@@ -783,7 +783,7 @@  discard block
 block discarded – undo
783 783
                 $this->getModelVersionInfo()->fieldsThatHavePrettyFormat()
784 784
             )
785 785
             ) {
786
-                $result[ $field_name ] = array(
786
+                $result[$field_name] = array(
787 787
                     'raw'    => $this->prepareFieldObjValueForJson($field_obj, $field_value),
788 788
                     'pretty' => $this->prepareFieldObjValueForJson($field_obj, $field_value, 'pretty'),
789 789
                 );
@@ -814,10 +814,10 @@  discard block
 block discarded – undo
814 814
                         $this->getModelVersionInfo()->requestedVersion()
815 815
                     );
816 816
                 }
817
-                $result[ $field_name . '_gmt' ] = $gmt_date;
818
-                $result[ $field_name ] = $local_date;
817
+                $result[$field_name.'_gmt'] = $gmt_date;
818
+                $result[$field_name] = $local_date;
819 819
             } else {
820
-                $result[ $field_name ] = $this->prepareFieldObjValueForJson($field_obj, $field_value);
820
+                $result[$field_name] = $this->prepareFieldObjValueForJson($field_obj, $field_value);
821 821
             }
822 822
         }
823 823
         if ($do_chevy_shuffle) {
@@ -869,7 +869,7 @@  discard block
 block discarded – undo
869 869
     protected function addExtraFields(EEM_Base $model, $db_row, $entity_array)
870 870
     {
871 871
         if ($model instanceof EEM_CPT_Base) {
872
-            $entity_array['link'] = get_permalink($db_row[ $model->get_primary_key_field()->get_qualified_column() ]);
872
+            $entity_array['link'] = get_permalink($db_row[$model->get_primary_key_field()->get_qualified_column()]);
873 873
         }
874 874
         return $entity_array;
875 875
     }
@@ -894,7 +894,7 @@  discard block
 block discarded – undo
894 894
                     'href' => $this->getVersionedLinkTo(
895 895
                         EEH_Inflector::pluralize_and_lower($model->get_this_model_name())
896 896
                         . '/'
897
-                        . $entity_array[ $model->primary_key_name() ]
897
+                        . $entity_array[$model->primary_key_name()]
898 898
                     ),
899 899
                 ),
900 900
             );
@@ -910,12 +910,12 @@  discard block
 block discarded – undo
910 910
         if ($model->has_primary_key_field()) {
911 911
             foreach ($this->getModelVersionInfo()->relationSettings($model) as $relation_name => $relation_obj) {
912 912
                 $related_model_part = Read::getRelatedEntityName($relation_name, $relation_obj);
913
-                $links[ EED_Core_Rest_Api::ee_api_link_namespace . $related_model_part ] = array(
913
+                $links[EED_Core_Rest_Api::ee_api_link_namespace.$related_model_part] = array(
914 914
                     array(
915 915
                         'href'   => $this->getVersionedLinkTo(
916 916
                             EEH_Inflector::pluralize_and_lower($model->get_this_model_name())
917 917
                             . '/'
918
-                            . $entity_array[ $model->primary_key_name() ]
918
+                            . $entity_array[$model->primary_key_name()]
919 919
                             . '/'
920 920
                             . $related_model_part
921 921
                         ),
@@ -947,7 +947,7 @@  discard block
 block discarded – undo
947 947
         $included_items_protected = false
948 948
     ) {
949 949
         // if $db_row not included, hope the entity array has what we need
950
-        if (! $db_row) {
950
+        if ( ! $db_row) {
951 951
             $db_row = $entity_array;
952 952
         }
953 953
         $relation_settings = $this->getModelVersionInfo()->relationSettings($model);
@@ -980,7 +980,7 @@  discard block
 block discarded – undo
980 980
                         $model->deduce_fields_n_values_from_cols_n_values($db_row)
981 981
                     )
982 982
                 );
983
-                if (! $included_items_protected) {
983
+                if ( ! $included_items_protected) {
984 984
                     $related_results = $this->getEntitiesFromRelationUsingModelQueryParams(
985 985
                         $primary_model_query_params,
986 986
                         $relation_obj,
@@ -994,7 +994,7 @@  discard block
 block discarded – undo
994 994
                 if ($related_results instanceof WP_Error) {
995 995
                     $related_results = null;
996 996
                 }
997
-                $entity_array[ Read::getRelatedEntityName($relation_name, $relation_obj) ] = $related_results;
997
+                $entity_array[Read::getRelatedEntityName($relation_name, $relation_obj)] = $related_results;
998 998
             }
999 999
         }
1000 1000
         return $entity_array;
@@ -1019,7 +1019,7 @@  discard block
 block discarded – undo
1019 1019
         $includes_for_this_model = $this->explodeAndGetItemsPrefixedWith($rest_request->get_param('include'), '');
1020 1020
         $includes_for_this_model = $this->removeModelNamesFromArray($includes_for_this_model);
1021 1021
         // if they passed in * or didn't specify any includes, return everything
1022
-        if (! in_array('*', $includes_for_this_model)
1022
+        if ( ! in_array('*', $includes_for_this_model)
1023 1023
             && ! empty($includes_for_this_model)
1024 1024
         ) {
1025 1025
             if ($model->has_primary_key_field()) {
@@ -1073,12 +1073,12 @@  discard block
 block discarded – undo
1073 1073
                 // it's password protected, so they shouldn't be able to read this. Remove the value
1074 1074
                 $schema = $this->fields_calculator->getJsonSchemaForModel($model);
1075 1075
                 if ($row_is_protected
1076
-                    && isset($schema['properties'][ $field_to_calculate ]['protected'])
1077
-                    && $schema['properties'][ $field_to_calculate ]['protected']) {
1076
+                    && isset($schema['properties'][$field_to_calculate]['protected'])
1077
+                    && $schema['properties'][$field_to_calculate]['protected']) {
1078 1078
                     $calculated_value = null;
1079 1079
                     $protected_fields[] = $field_to_calculate;
1080
-                    if ($schema['properties'][ $field_to_calculate ]['type']) {
1081
-                        switch ($schema['properties'][ $field_to_calculate ]['type']) {
1080
+                    if ($schema['properties'][$field_to_calculate]['type']) {
1081
+                        switch ($schema['properties'][$field_to_calculate]['type']) {
1082 1082
                             case 'boolean':
1083 1083
                                 $calculated_value = false;
1084 1084
                                 break;
@@ -1188,7 +1188,7 @@  discard block
 block discarded – undo
1188 1188
      */
1189 1189
     public function validateContext($context)
1190 1190
     {
1191
-        if (! $context) {
1191
+        if ( ! $context) {
1192 1192
             $context = EEM_Base::caps_read;
1193 1193
         }
1194 1194
         $valid_contexts = EEM_Base::valid_cap_contexts();
@@ -1213,7 +1213,7 @@  discard block
 block discarded – undo
1213 1213
             EEM_Base::default_where_conditions_minimum_all,
1214 1214
             EEM_Base::default_where_conditions_minimum_others,
1215 1215
         );
1216
-        if (! $default_query_params) {
1216
+        if ( ! $default_query_params) {
1217 1217
             $default_query_params = EEM_Base::default_where_conditions_all;
1218 1218
         }
1219 1219
         if (in_array(
@@ -1296,14 +1296,14 @@  discard block
 block discarded – undo
1296 1296
         }
1297 1297
         if (isset($query_params['limit'])) {
1298 1298
             // limit should be either a string like '23' or '23,43', or an array with two items in it
1299
-            if (! is_array($query_params['limit'])) {
1299
+            if ( ! is_array($query_params['limit'])) {
1300 1300
                 $limit_array = explode(',', (string) $query_params['limit']);
1301 1301
             } else {
1302 1302
                 $limit_array = $query_params['limit'];
1303 1303
             }
1304 1304
             $sanitized_limit = array();
1305 1305
             foreach ($limit_array as $key => $limit_part) {
1306
-                if ($this->debug_mode && (! is_numeric($limit_part) || count($sanitized_limit) > 2)) {
1306
+                if ($this->debug_mode && ( ! is_numeric($limit_part) || count($sanitized_limit) > 2)) {
1307 1307
                     throw new EE_Error(
1308 1308
                         sprintf(
1309 1309
                             __(
@@ -1335,7 +1335,7 @@  discard block
 block discarded – undo
1335 1335
         // if this is a model protected by a password on another model, exclude the password protected
1336 1336
         // entities by default. But if they passed in a password, try to show them all. If the password is wrong,
1337 1337
         // though, they'll get an error (see Read::createEntityFromWpdbResult() which calls Read::checkPassword)
1338
-        if (! $model->hasPassword()
1338
+        if ( ! $model->hasPassword()
1339 1339
             && $model->restrictedByRelatedModelPassword()
1340 1340
             && $model_query_params['caps'] === EEM_Base::caps_read) {
1341 1341
             if (empty($query_params['password'])) {
@@ -1360,9 +1360,9 @@  discard block
 block discarded – undo
1360 1360
         $model_ready_query_params = array();
1361 1361
         foreach ($query_params as $key => $value) {
1362 1362
             if (is_array($value)) {
1363
-                $model_ready_query_params[ $key ] = $this->prepareRestQueryParamsKeyForModels($model, $value);
1363
+                $model_ready_query_params[$key] = $this->prepareRestQueryParamsKeyForModels($model, $value);
1364 1364
             } else {
1365
-                $model_ready_query_params[ $key ] = $value;
1365
+                $model_ready_query_params[$key] = $value;
1366 1366
             }
1367 1367
         }
1368 1368
         return $model_ready_query_params;
@@ -1380,9 +1380,9 @@  discard block
 block discarded – undo
1380 1380
         $model_ready_query_params = array();
1381 1381
         foreach ($query_params as $key => $value) {
1382 1382
             if (is_array($value)) {
1383
-                $model_ready_query_params[ $key ] = $this->prepareRestQueryParamsValuesForModels($model, $value);
1383
+                $model_ready_query_params[$key] = $this->prepareRestQueryParamsValuesForModels($model, $value);
1384 1384
             } else {
1385
-                $model_ready_query_params[ $key ] = $value;
1385
+                $model_ready_query_params[$key] = $value;
1386 1386
             }
1387 1387
         }
1388 1388
         return $model_ready_query_params;
@@ -1414,17 +1414,17 @@  discard block
 block discarded – undo
1414 1414
         foreach ($exploded_contents as $item) {
1415 1415
             $item = trim($item);
1416 1416
             // if no prefix was provided, so we look for items with no "." in them
1417
-            if (! $prefix) {
1417
+            if ( ! $prefix) {
1418 1418
                 // does this item have a period?
1419 1419
                 if (strpos($item, '.') === false) {
1420 1420
                     // if not, then its what we're looking for
1421 1421
                     $contents_with_prefix[] = $item;
1422 1422
                 }
1423
-            } elseif (strpos($item, $prefix . '.') === 0) {
1423
+            } elseif (strpos($item, $prefix.'.') === 0) {
1424 1424
                 // this item has the prefix and a period, grab it
1425 1425
                 $contents_with_prefix[] = substr(
1426 1426
                     $item,
1427
-                    strpos($item, $prefix . '.') + strlen($prefix . '.')
1427
+                    strpos($item, $prefix.'.') + strlen($prefix.'.')
1428 1428
                 );
1429 1429
             } elseif ($item === $prefix) {
1430 1430
                 // this item is JUST the prefix
@@ -1463,9 +1463,9 @@  discard block
 block discarded – undo
1463 1463
         if ($model_name) {
1464 1464
             foreach ($includes as $field_to_include) {
1465 1465
                 $field_to_include = trim($field_to_include);
1466
-                if (strpos($field_to_include, $model_name . '.') === 0) {
1466
+                if (strpos($field_to_include, $model_name.'.') === 0) {
1467 1467
                     // found the model name at the exact start
1468
-                    $field_sans_model_name = str_replace($model_name . '.', '', $field_to_include);
1468
+                    $field_sans_model_name = str_replace($model_name.'.', '', $field_to_include);
1469 1469
                     $extracted_fields_to_include[] = $field_sans_model_name;
1470 1470
                 } elseif ($field_to_include == $model_name) {
1471 1471
                     $extracted_fields_to_include[] = '*';
@@ -1506,7 +1506,7 @@  discard block
 block discarded – undo
1506 1506
         $restricted_query_params['caps'] = $context;
1507 1507
         $this->setDebugInfo('model query params', $restricted_query_params);
1508 1508
         $model_rows = $model->get_all_wpdb_results($restricted_query_params);
1509
-        if (! empty($model_rows)) {
1509
+        if ( ! empty($model_rows)) {
1510 1510
             return $this->createEntityFromWpdbResult(
1511 1511
                 $model,
1512 1512
                 reset($model_rows),
@@ -1518,7 +1518,7 @@  discard block
 block discarded – undo
1518 1518
             if ($model->exists($query_params)) {
1519 1519
                 // you got shafted- it existed but we didn't want to tell you!
1520 1520
                 throw new RestException(
1521
-                    'rest_user_cannot_' . $context,
1521
+                    'rest_user_cannot_'.$context,
1522 1522
                     sprintf(
1523 1523
                         __('Sorry, you cannot %1$s this %2$s. Missing permissions are: %3$s', 'event_espresso'),
1524 1524
                         $context,
@@ -1567,11 +1567,11 @@  discard block
 block discarded – undo
1567 1567
         }
1568 1568
         // if this entity requires a password, they better give it and it better be right!
1569 1569
         if ($model->hasPassword()
1570
-            && $model_row[ $model->getPasswordField()->get_qualified_column() ] !== '') {
1570
+            && $model_row[$model->getPasswordField()->get_qualified_column()] !== '') {
1571 1571
             if (empty($request['password'])) {
1572 1572
                 throw new RestPasswordRequiredException();
1573
-            } elseif (!hash_equals(
1574
-                $model_row[ $model->getPasswordField()->get_qualified_column() ],
1573
+            } elseif ( ! hash_equals(
1574
+                $model_row[$model->getPasswordField()->get_qualified_column()],
1575 1575
                 $request['password']
1576 1576
             )) {
1577 1577
                 throw new RestPasswordIncorrectException();
@@ -1582,12 +1582,12 @@  discard block
 block discarded – undo
1582 1582
             $password_supplied = $request->get_param('password');
1583 1583
             if (empty($password_supplied)) {
1584 1584
                 $query_params['exclude_protected'] = true;
1585
-                if (!$model->exists($query_params)) {
1585
+                if ( ! $model->exists($query_params)) {
1586 1586
                     throw new RestPasswordRequiredException();
1587 1587
                 }
1588 1588
             } else {
1589
-                $query_params[0][ $model->modelChainAndPassword() ] = $password_supplied;
1590
-                if (!$model->exists($query_params)) {
1589
+                $query_params[0][$model->modelChainAndPassword()] = $password_supplied;
1590
+                if ( ! $model->exists($query_params)) {
1591 1591
                     throw new RestPasswordIncorrectException();
1592 1592
                 }
1593 1593
             }
Please login to merge, or discard this patch.
espresso.php 1 patch
Indentation   +80 added lines, -80 removed lines patch added patch discarded remove patch
@@ -38,103 +38,103 @@
 block discarded – undo
38 38
  * @since           4.0
39 39
  */
40 40
 if (function_exists('espresso_version')) {
41
-    if (! function_exists('espresso_duplicate_plugin_error')) {
42
-        /**
43
-         *    espresso_duplicate_plugin_error
44
-         *    displays if more than one version of EE is activated at the same time
45
-         */
46
-        function espresso_duplicate_plugin_error()
47
-        {
48
-            ?>
41
+	if (! function_exists('espresso_duplicate_plugin_error')) {
42
+		/**
43
+		 *    espresso_duplicate_plugin_error
44
+		 *    displays if more than one version of EE is activated at the same time
45
+		 */
46
+		function espresso_duplicate_plugin_error()
47
+		{
48
+			?>
49 49
             <div class="error">
50 50
                 <p>
51 51
                     <?php
52
-                    echo esc_html__(
53
-                        'Can not run multiple versions of Event Espresso! One version has been automatically deactivated. Please verify that you have the correct version you want still active.',
54
-                        'event_espresso'
55
-                    ); ?>
52
+					echo esc_html__(
53
+						'Can not run multiple versions of Event Espresso! One version has been automatically deactivated. Please verify that you have the correct version you want still active.',
54
+						'event_espresso'
55
+					); ?>
56 56
                 </p>
57 57
             </div>
58 58
             <?php
59
-            espresso_deactivate_plugin(plugin_basename(__FILE__));
60
-        }
61
-    }
62
-    add_action('admin_notices', 'espresso_duplicate_plugin_error', 1);
59
+			espresso_deactivate_plugin(plugin_basename(__FILE__));
60
+		}
61
+	}
62
+	add_action('admin_notices', 'espresso_duplicate_plugin_error', 1);
63 63
 } else {
64
-    define('EE_MIN_PHP_VER_REQUIRED', '5.4.0');
65
-    if (! version_compare(PHP_VERSION, EE_MIN_PHP_VER_REQUIRED, '>=')) {
66
-        /**
67
-         * espresso_minimum_php_version_error
68
-         *
69
-         * @return void
70
-         */
71
-        function espresso_minimum_php_version_error()
72
-        {
73
-            ?>
64
+	define('EE_MIN_PHP_VER_REQUIRED', '5.4.0');
65
+	if (! version_compare(PHP_VERSION, EE_MIN_PHP_VER_REQUIRED, '>=')) {
66
+		/**
67
+		 * espresso_minimum_php_version_error
68
+		 *
69
+		 * @return void
70
+		 */
71
+		function espresso_minimum_php_version_error()
72
+		{
73
+			?>
74 74
             <div class="error">
75 75
                 <p>
76 76
                     <?php
77
-                    printf(
78
-                        esc_html__(
79
-                            'We\'re sorry, but Event Espresso requires PHP version %1$s or greater in order to operate. You are currently running version %2$s.%3$sIn order to update your version of PHP, you will need to contact your current hosting provider.%3$sFor information on stable PHP versions, please go to %4$s.',
80
-                            'event_espresso'
81
-                        ),
82
-                        EE_MIN_PHP_VER_REQUIRED,
83
-                        PHP_VERSION,
84
-                        '<br/>',
85
-                        '<a href="http://php.net/downloads.php">http://php.net/downloads.php</a>'
86
-                    );
87
-                    ?>
77
+					printf(
78
+						esc_html__(
79
+							'We\'re sorry, but Event Espresso requires PHP version %1$s or greater in order to operate. You are currently running version %2$s.%3$sIn order to update your version of PHP, you will need to contact your current hosting provider.%3$sFor information on stable PHP versions, please go to %4$s.',
80
+							'event_espresso'
81
+						),
82
+						EE_MIN_PHP_VER_REQUIRED,
83
+						PHP_VERSION,
84
+						'<br/>',
85
+						'<a href="http://php.net/downloads.php">http://php.net/downloads.php</a>'
86
+					);
87
+					?>
88 88
                 </p>
89 89
             </div>
90 90
             <?php
91
-            espresso_deactivate_plugin(plugin_basename(__FILE__));
92
-        }
91
+			espresso_deactivate_plugin(plugin_basename(__FILE__));
92
+		}
93 93
 
94
-        add_action('admin_notices', 'espresso_minimum_php_version_error', 1);
95
-    } else {
96
-        define('EVENT_ESPRESSO_MAIN_FILE', __FILE__);
97
-        /**
98
-         * espresso_version
99
-         * Returns the plugin version
100
-         *
101
-         * @return string
102
-         */
103
-        function espresso_version()
104
-        {
105
-            return apply_filters('FHEE__espresso__espresso_version', '4.9.75.rc.000');
106
-        }
94
+		add_action('admin_notices', 'espresso_minimum_php_version_error', 1);
95
+	} else {
96
+		define('EVENT_ESPRESSO_MAIN_FILE', __FILE__);
97
+		/**
98
+		 * espresso_version
99
+		 * Returns the plugin version
100
+		 *
101
+		 * @return string
102
+		 */
103
+		function espresso_version()
104
+		{
105
+			return apply_filters('FHEE__espresso__espresso_version', '4.9.75.rc.000');
106
+		}
107 107
 
108
-        /**
109
-         * espresso_plugin_activation
110
-         * adds a wp-option to indicate that EE has been activated via the WP admin plugins page
111
-         */
112
-        function espresso_plugin_activation()
113
-        {
114
-            update_option('ee_espresso_activation', true);
115
-        }
108
+		/**
109
+		 * espresso_plugin_activation
110
+		 * adds a wp-option to indicate that EE has been activated via the WP admin plugins page
111
+		 */
112
+		function espresso_plugin_activation()
113
+		{
114
+			update_option('ee_espresso_activation', true);
115
+		}
116 116
 
117
-        register_activation_hook(EVENT_ESPRESSO_MAIN_FILE, 'espresso_plugin_activation');
117
+		register_activation_hook(EVENT_ESPRESSO_MAIN_FILE, 'espresso_plugin_activation');
118 118
 
119
-        require_once __DIR__ . '/core/bootstrap_espresso.php';
120
-        bootstrap_espresso();
121
-    }
119
+		require_once __DIR__ . '/core/bootstrap_espresso.php';
120
+		bootstrap_espresso();
121
+	}
122 122
 }
123 123
 if (! function_exists('espresso_deactivate_plugin')) {
124
-    /**
125
-     *    deactivate_plugin
126
-     * usage:  espresso_deactivate_plugin( plugin_basename( __FILE__ ));
127
-     *
128
-     * @access public
129
-     * @param string $plugin_basename - the results of plugin_basename( __FILE__ ) for the plugin's main file
130
-     * @return    void
131
-     */
132
-    function espresso_deactivate_plugin($plugin_basename = '')
133
-    {
134
-        if (! function_exists('deactivate_plugins')) {
135
-            require_once ABSPATH . 'wp-admin/includes/plugin.php';
136
-        }
137
-        unset($_GET['activate'], $_REQUEST['activate']);
138
-        deactivate_plugins($plugin_basename);
139
-    }
124
+	/**
125
+	 *    deactivate_plugin
126
+	 * usage:  espresso_deactivate_plugin( plugin_basename( __FILE__ ));
127
+	 *
128
+	 * @access public
129
+	 * @param string $plugin_basename - the results of plugin_basename( __FILE__ ) for the plugin's main file
130
+	 * @return    void
131
+	 */
132
+	function espresso_deactivate_plugin($plugin_basename = '')
133
+	{
134
+		if (! function_exists('deactivate_plugins')) {
135
+			require_once ABSPATH . 'wp-admin/includes/plugin.php';
136
+		}
137
+		unset($_GET['activate'], $_REQUEST['activate']);
138
+		deactivate_plugins($plugin_basename);
139
+	}
140 140
 }
Please login to merge, or discard this patch.
core/entities/models/JsonModelSchema.php 1 patch
Indentation   +234 added lines, -234 removed lines patch added patch discarded remove patch
@@ -25,255 +25,255 @@
 block discarded – undo
25 25
 class JsonModelSchema
26 26
 {
27 27
 
28
-    /**
29
-     * @var EEM_Base
30
-     */
31
-    protected $model;
28
+	/**
29
+	 * @var EEM_Base
30
+	 */
31
+	protected $model;
32 32
 
33
-    /**
34
-     * @var CalculatedModelFields
35
-     */
36
-    protected $fields_calculator;
33
+	/**
34
+	 * @var CalculatedModelFields
35
+	 */
36
+	protected $fields_calculator;
37 37
 
38 38
 
39
-    /**
40
-     * JsonModelSchema constructor.
41
-     *
42
-     * @param EEM_Base              $model
43
-     * @param CalculatedModelFields $fields_calculator
44
-     */
45
-    public function __construct(EEM_Base $model, CalculatedModelFields $fields_calculator)
46
-    {
47
-        $this->model = $model;
48
-        $this->fields_calculator = $fields_calculator;
49
-    }
39
+	/**
40
+	 * JsonModelSchema constructor.
41
+	 *
42
+	 * @param EEM_Base              $model
43
+	 * @param CalculatedModelFields $fields_calculator
44
+	 */
45
+	public function __construct(EEM_Base $model, CalculatedModelFields $fields_calculator)
46
+	{
47
+		$this->model = $model;
48
+		$this->fields_calculator = $fields_calculator;
49
+	}
50 50
 
51 51
 
52
-    /**
53
-     * Return the schema for a given model from a given model.
54
-     *
55
-     * @return array
56
-     */
57
-    public function getModelSchema()
58
-    {
59
-        return $this->getModelSchemaForRelations(
60
-            $this->model->relation_settings(),
61
-            $this->getModelSchemaForFields(
62
-                $this->model->field_settings(),
63
-                $this->getInitialSchemaStructure()
64
-            )
65
-        );
66
-    }
52
+	/**
53
+	 * Return the schema for a given model from a given model.
54
+	 *
55
+	 * @return array
56
+	 */
57
+	public function getModelSchema()
58
+	{
59
+		return $this->getModelSchemaForRelations(
60
+			$this->model->relation_settings(),
61
+			$this->getModelSchemaForFields(
62
+				$this->model->field_settings(),
63
+				$this->getInitialSchemaStructure()
64
+			)
65
+		);
66
+	}
67 67
 
68 68
 
69
-    /**
70
-     * Get the schema for a given set of model fields.
71
-     *
72
-     * @param EE_Model_Field_Base[] $model_fields
73
-     * @param array                  $schema
74
-     * @return array
75
-     */
76
-    public function getModelSchemaForFields(array $model_fields, array $schema)
77
-    {
78
-        foreach ($model_fields as $field => $model_field) {
79
-            if (! $model_field instanceof EE_Model_Field_Base) {
80
-                continue;
81
-            }
82
-            $schema['properties'][ $field ] = $model_field->getSchema();
69
+	/**
70
+	 * Get the schema for a given set of model fields.
71
+	 *
72
+	 * @param EE_Model_Field_Base[] $model_fields
73
+	 * @param array                  $schema
74
+	 * @return array
75
+	 */
76
+	public function getModelSchemaForFields(array $model_fields, array $schema)
77
+	{
78
+		foreach ($model_fields as $field => $model_field) {
79
+			if (! $model_field instanceof EE_Model_Field_Base) {
80
+				continue;
81
+			}
82
+			$schema['properties'][ $field ] = $model_field->getSchema();
83 83
 
84
-            // if this is a primary key field add the primary key item
85
-            if ($model_field instanceof EE_Primary_Key_Field_Base) {
86
-                $schema['properties'][ $field ]['primary_key'] = true;
87
-                if ($model_field instanceof EE_Primary_Key_Int_Field) {
88
-                    $schema['properties'][ $field ]['readonly'] = true;
89
-                }
90
-            }
84
+			// if this is a primary key field add the primary key item
85
+			if ($model_field instanceof EE_Primary_Key_Field_Base) {
86
+				$schema['properties'][ $field ]['primary_key'] = true;
87
+				if ($model_field instanceof EE_Primary_Key_Int_Field) {
88
+					$schema['properties'][ $field ]['readonly'] = true;
89
+				}
90
+			}
91 91
 
92
-            // if this is a foreign key field add the foreign key item
93
-            if ($model_field instanceof EE_Foreign_Key_Field_Base) {
94
-                $schema['properties'][ $field ]['foreign_key'] = array(
95
-                    'description' => esc_html__(
96
-                        'This is a foreign key the points to the given models.',
97
-                        'event_espresso'
98
-                    ),
99
-                    'type'        => 'array',
100
-                    'enum'        => $model_field->get_model_class_names_pointed_to(),
101
-                );
102
-            }
103
-        }
104
-        return $schema;
105
-    }
92
+			// if this is a foreign key field add the foreign key item
93
+			if ($model_field instanceof EE_Foreign_Key_Field_Base) {
94
+				$schema['properties'][ $field ]['foreign_key'] = array(
95
+					'description' => esc_html__(
96
+						'This is a foreign key the points to the given models.',
97
+						'event_espresso'
98
+					),
99
+					'type'        => 'array',
100
+					'enum'        => $model_field->get_model_class_names_pointed_to(),
101
+				);
102
+			}
103
+		}
104
+		return $schema;
105
+	}
106 106
 
107 107
 
108
-    /**
109
-     * Get the schema for a given set of model relations
110
-     *
111
-     * @param EE_Model_Relation_Base[] $relations_on_model
112
-     * @param array                    $schema
113
-     * @return array
114
-     */
115
-    public function getModelSchemaForRelations(array $relations_on_model, array $schema)
116
-    {
117
-        foreach ($relations_on_model as $model_name => $relation) {
118
-            if (! $relation instanceof EE_Model_Relation_Base) {
119
-                continue;
120
-            }
121
-            $model_name_for_schema = $relation instanceof EE_Belongs_To_Relation
122
-                ? strtolower($model_name)
123
-                : EEH_Inflector::pluralize_and_lower($model_name);
124
-            $schema['properties'][ $model_name_for_schema ] = $relation->getSchema();
125
-            $schema['properties'][ $model_name_for_schema ]['relation_model'] = $model_name;
108
+	/**
109
+	 * Get the schema for a given set of model relations
110
+	 *
111
+	 * @param EE_Model_Relation_Base[] $relations_on_model
112
+	 * @param array                    $schema
113
+	 * @return array
114
+	 */
115
+	public function getModelSchemaForRelations(array $relations_on_model, array $schema)
116
+	{
117
+		foreach ($relations_on_model as $model_name => $relation) {
118
+			if (! $relation instanceof EE_Model_Relation_Base) {
119
+				continue;
120
+			}
121
+			$model_name_for_schema = $relation instanceof EE_Belongs_To_Relation
122
+				? strtolower($model_name)
123
+				: EEH_Inflector::pluralize_and_lower($model_name);
124
+			$schema['properties'][ $model_name_for_schema ] = $relation->getSchema();
125
+			$schema['properties'][ $model_name_for_schema ]['relation_model'] = $model_name;
126 126
 
127
-            // links schema
128
-            $links_key = 'https://api.eventespresso.com/' . strtolower($model_name);
129
-            $schema['properties']['_links']['properties'][ $links_key ] = array(
130
-                'description' => esc_html__(
131
-                    'Array of objects describing the link(s) for this relation resource.',
132
-                    'event_espresso'
133
-                ),
134
-                'type' => 'array',
135
-                'readonly' => true,
136
-                'items' => array(
137
-                    'type' => 'object',
138
-                    'properties' => array(
139
-                        'href' => array(
140
-                            'type' => 'string',
141
-                            'description' => sprintf(
142
-                                // translators: placeholder is the model name for the relation.
143
-                                esc_html__(
144
-                                    'The link to the resource for the %s relation(s) to this entity',
145
-                                    'event_espresso'
146
-                                ),
147
-                                $model_name
148
-                            ),
149
-                        ),
150
-                        'single' => array(
151
-                            'type' => 'boolean',
152
-                            'description' => sprintf(
153
-                                // translators: placeholder is the model name for the relation.
154
-                                esc_html__(
155
-                                    'Whether or not there is only a single %s relation to this entity',
156
-                                    'event_espresso'
157
-                                ),
158
-                                $model_name
159
-                            ),
160
-                        ),
161
-                    ),
162
-                    'additionalProperties' => false
163
-                ),
164
-            );
165
-        }
166
-        return $schema;
167
-    }
127
+			// links schema
128
+			$links_key = 'https://api.eventespresso.com/' . strtolower($model_name);
129
+			$schema['properties']['_links']['properties'][ $links_key ] = array(
130
+				'description' => esc_html__(
131
+					'Array of objects describing the link(s) for this relation resource.',
132
+					'event_espresso'
133
+				),
134
+				'type' => 'array',
135
+				'readonly' => true,
136
+				'items' => array(
137
+					'type' => 'object',
138
+					'properties' => array(
139
+						'href' => array(
140
+							'type' => 'string',
141
+							'description' => sprintf(
142
+								// translators: placeholder is the model name for the relation.
143
+								esc_html__(
144
+									'The link to the resource for the %s relation(s) to this entity',
145
+									'event_espresso'
146
+								),
147
+								$model_name
148
+							),
149
+						),
150
+						'single' => array(
151
+							'type' => 'boolean',
152
+							'description' => sprintf(
153
+								// translators: placeholder is the model name for the relation.
154
+								esc_html__(
155
+									'Whether or not there is only a single %s relation to this entity',
156
+									'event_espresso'
157
+								),
158
+								$model_name
159
+							),
160
+						),
161
+					),
162
+					'additionalProperties' => false
163
+				),
164
+			);
165
+		}
166
+		return $schema;
167
+	}
168 168
 
169 169
 
170
-    /**
171
-     * Outputs the schema header for a model.
172
-     *
173
-     * @return array
174
-     */
175
-    public function getInitialSchemaStructure()
176
-    {
177
-        return array(
178
-            '$schema'    => 'http://json-schema.org/draft-04/schema#',
179
-            'title'      => $this->model->get_this_model_name(),
180
-            'type'       => 'object',
181
-            'properties' => array(
182
-                'link' => array(
183
-                    'description' => esc_html__(
184
-                        'Link to event on WordPress site hosting events.',
185
-                        'event_espresso'
186
-                    ),
187
-                    'type' => 'string',
188
-                    'readonly' => true,
189
-                ),
190
-                '_links' => array(
191
-                    'description' => esc_html__(
192
-                        'Various links for resources related to the entity.',
193
-                        'event_espresso'
194
-                    ),
195
-                    'type' => 'object',
196
-                    'readonly' => true,
197
-                    'properties' => array(
198
-                        'self' => array(
199
-                            'description' => esc_html__(
200
-                                'Link to this entities resource.',
201
-                                'event_espresso'
202
-                            ),
203
-                            'type' => 'array',
204
-                            'items' => array(
205
-                                'type' => 'object',
206
-                                'properties' => array(
207
-                                    'href' => array(
208
-                                        'type' => 'string',
209
-                                    ),
210
-                                ),
211
-                                'additionalProperties' => false
212
-                            ),
213
-                            'readonly' => true
214
-                        ),
215
-                        'collection' => array(
216
-                            'description' => esc_html__(
217
-                                'Link to this entities collection resource.',
218
-                                'event_espresso'
219
-                            ),
220
-                            'type' => 'array',
221
-                            'items' => array(
222
-                                'type' => 'object',
223
-                                'properties' => array(
224
-                                    'href' => array(
225
-                                        'type' => 'string'
226
-                                    ),
227
-                                ),
228
-                                'additionalProperties' => false
229
-                            ),
230
-                            'readonly' => true
231
-                        ),
232
-                    ),
233
-                    'additionalProperties' => false,
234
-                ),
235
-                '_calculated_fields' => array_merge(
236
-                    $this->fields_calculator->getJsonSchemaForModel($this->model),
237
-                    array(
238
-                        '_protected' => $this->getProtectedFieldsSchema()
239
-                    )
240
-                ),
241
-                '_protected' => $this->getProtectedFieldsSchema()
242
-            ),
243
-            'additionalProperties' => false,
244
-        );
245
-    }
170
+	/**
171
+	 * Outputs the schema header for a model.
172
+	 *
173
+	 * @return array
174
+	 */
175
+	public function getInitialSchemaStructure()
176
+	{
177
+		return array(
178
+			'$schema'    => 'http://json-schema.org/draft-04/schema#',
179
+			'title'      => $this->model->get_this_model_name(),
180
+			'type'       => 'object',
181
+			'properties' => array(
182
+				'link' => array(
183
+					'description' => esc_html__(
184
+						'Link to event on WordPress site hosting events.',
185
+						'event_espresso'
186
+					),
187
+					'type' => 'string',
188
+					'readonly' => true,
189
+				),
190
+				'_links' => array(
191
+					'description' => esc_html__(
192
+						'Various links for resources related to the entity.',
193
+						'event_espresso'
194
+					),
195
+					'type' => 'object',
196
+					'readonly' => true,
197
+					'properties' => array(
198
+						'self' => array(
199
+							'description' => esc_html__(
200
+								'Link to this entities resource.',
201
+								'event_espresso'
202
+							),
203
+							'type' => 'array',
204
+							'items' => array(
205
+								'type' => 'object',
206
+								'properties' => array(
207
+									'href' => array(
208
+										'type' => 'string',
209
+									),
210
+								),
211
+								'additionalProperties' => false
212
+							),
213
+							'readonly' => true
214
+						),
215
+						'collection' => array(
216
+							'description' => esc_html__(
217
+								'Link to this entities collection resource.',
218
+								'event_espresso'
219
+							),
220
+							'type' => 'array',
221
+							'items' => array(
222
+								'type' => 'object',
223
+								'properties' => array(
224
+									'href' => array(
225
+										'type' => 'string'
226
+									),
227
+								),
228
+								'additionalProperties' => false
229
+							),
230
+							'readonly' => true
231
+						),
232
+					),
233
+					'additionalProperties' => false,
234
+				),
235
+				'_calculated_fields' => array_merge(
236
+					$this->fields_calculator->getJsonSchemaForModel($this->model),
237
+					array(
238
+						'_protected' => $this->getProtectedFieldsSchema()
239
+					)
240
+				),
241
+				'_protected' => $this->getProtectedFieldsSchema()
242
+			),
243
+			'additionalProperties' => false,
244
+		);
245
+	}
246 246
 
247
-    /**
248
-     * Returns an array of JSON schema to describe the _protected property on responses
249
-     * @since 4.9.74.p
250
-     * @return array
251
-     */
252
-    protected function getProtectedFieldsSchema()
253
-    {
254
-        return array(
255
-            'description' => esc_html__('Array of property names whose values were replaced with their default (because they are related to a password-protected entity.)', 'event_espresso'),
256
-            'type' => 'array',
257
-            'items' => array(
258
-                'description' => esc_html__('Each name corresponds to a property that is protected by password for this entity and has its default value returned in the response.', 'event_espresso'),
259
-                'type' => 'string',
260
-                'readonly' => true,
261
-            ),
262
-            'readonly' => true
263
-        );
264
-    }
247
+	/**
248
+	 * Returns an array of JSON schema to describe the _protected property on responses
249
+	 * @since 4.9.74.p
250
+	 * @return array
251
+	 */
252
+	protected function getProtectedFieldsSchema()
253
+	{
254
+		return array(
255
+			'description' => esc_html__('Array of property names whose values were replaced with their default (because they are related to a password-protected entity.)', 'event_espresso'),
256
+			'type' => 'array',
257
+			'items' => array(
258
+				'description' => esc_html__('Each name corresponds to a property that is protected by password for this entity and has its default value returned in the response.', 'event_espresso'),
259
+				'type' => 'string',
260
+				'readonly' => true,
261
+			),
262
+			'readonly' => true
263
+		);
264
+	}
265 265
 
266 266
 
267
-    /**
268
-     * Allows one to just use the object as a string to get the json.
269
-     * eg.
270
-     * $json_schema = new JsonModelSchema(EEM_Event::instance(), new CalculatedModelFields);
271
-     * echo $json_schema; //outputs the schema as a json formatted string.
272
-     *
273
-     * @return bool|false|mixed|string
274
-     */
275
-    public function __toString()
276
-    {
277
-        return wp_json_encode($this->getModelSchema());
278
-    }
267
+	/**
268
+	 * Allows one to just use the object as a string to get the json.
269
+	 * eg.
270
+	 * $json_schema = new JsonModelSchema(EEM_Event::instance(), new CalculatedModelFields);
271
+	 * echo $json_schema; //outputs the schema as a json formatted string.
272
+	 *
273
+	 * @return bool|false|mixed|string
274
+	 */
275
+	public function __toString()
276
+	{
277
+		return wp_json_encode($this->getModelSchema());
278
+	}
279 279
 }
Please login to merge, or discard this patch.
core/db_models/fields/EE_Password_Field.php 1 patch
Indentation   +36 added lines, -36 removed lines patch added patch discarded remove patch
@@ -12,45 +12,45 @@
 block discarded – undo
12 12
  */
13 13
 class EE_Password_Field extends EE_Text_Field_Base
14 14
 {
15
-    /**
16
-     * @var array
17
-     */
18
-    protected $protected_fields;
15
+	/**
16
+	 * @var array
17
+	 */
18
+	protected $protected_fields;
19 19
 
20
-    /**
21
-     * EE_Password_Field constructor.
22
-     * @param $table_column
23
-     * @param $nicename
24
-     * @param $nullable
25
-     * @param null $default_value
26
-     * @param array $protected_fields
27
-     */
28
-    public function __construct($table_column, $nicename, $nullable, $default_value = null, $protected_fields = array())
29
-    {
30
-        $this->protected_fields = $protected_fields;
31
-        parent::__construct($table_column, $nicename, $nullable, $default_value);
32
-    }
20
+	/**
21
+	 * EE_Password_Field constructor.
22
+	 * @param $table_column
23
+	 * @param $nicename
24
+	 * @param $nullable
25
+	 * @param null $default_value
26
+	 * @param array $protected_fields
27
+	 */
28
+	public function __construct($table_column, $nicename, $nullable, $default_value = null, $protected_fields = array())
29
+	{
30
+		$this->protected_fields = $protected_fields;
31
+		parent::__construct($table_column, $nicename, $nullable, $default_value);
32
+	}
33 33
 
34
-    /**
35
-     * Returns the names of the fields on this model that this password field should protect
36
-     * @since 4.9.74.p
37
-     * @return array
38
-     */
39
-    public function protectedFields()
40
-    {
41
-        return $this->protected_fields;
42
-    }
34
+	/**
35
+	 * Returns the names of the fields on this model that this password field should protect
36
+	 * @since 4.9.74.p
37
+	 * @return array
38
+	 */
39
+	public function protectedFields()
40
+	{
41
+		return $this->protected_fields;
42
+	}
43 43
 
44
-    /**
45
-     * Returns whether or not the specified field is protected by this model
46
-     * @since 4.9.74.p
47
-     * @param $field_name
48
-     * @return bool
49
-     */
50
-    public function fieldIsProtected($field_name)
51
-    {
52
-        return in_array($field_name, $this->protectedFields(), true);
53
-    }
44
+	/**
45
+	 * Returns whether or not the specified field is protected by this model
46
+	 * @since 4.9.74.p
47
+	 * @param $field_name
48
+	 * @return bool
49
+	 */
50
+	public function fieldIsProtected($field_name)
51
+	{
52
+		return in_array($field_name, $this->protectedFields(), true);
53
+	}
54 54
 }
55 55
 // End of file EE_Password_Field.php
56 56
 // Location: ${NAMESPACE}/EE_Password_Field.php
Please login to merge, or discard this patch.
core/libraries/rest_api/RestIncomingQueryParamMetadata.php 1 patch
Indentation   +698 added lines, -698 removed lines patch added patch discarded remove patch
@@ -28,704 +28,704 @@
 block discarded – undo
28 28
  */
29 29
 class RestIncomingQueryParamMetadata
30 30
 {
31
-    private $query_param_key;
32
-    private $query_param_value;
33
-    /**
34
-     * @var RestIncomingQueryParamContext
35
-     */
36
-    private $context;
37
-
38
-    /**
39
-     * @var EE_Model_Field_Base|null
40
-     */
41
-    private $field;
42
-
43
-    /**
44
-     * @var string same as $query_param_key but has the * and anything after it removed
45
-     */
46
-    private $query_param_key_sans_stars;
47
-
48
-    /**
49
-     * @var string for timezone or timezone offset
50
-     */
51
-    private $timezone;
52
-
53
-    /**
54
-     * @var boolean if the field in $query_param_key is for a GMT field (eg `EVT_modified_gmt`)
55
-     */
56
-    private $is_gmt_field = false;
57
-
58
-    /**
59
-     * RestIncomingQueryParamMetadata constructor.
60
-     * You probably want to call
61
-     * @param string $query_param_key
62
-     * @param string $query_param_value
63
-     * @param RestIncomingQueryParamContext $context
64
-     */
65
-    public function __construct($query_param_key, $query_param_value, RestIncomingQueryParamContext $context)
66
-    {
67
-        $this->query_param_key = $query_param_key;
68
-        $this->query_param_value = $query_param_value;
69
-        $this->context = $context;
70
-        $this->determineFieldAndTimezone();
71
-    }
72
-
73
-    /**
74
-     * Gets the query parameter key. This may have been modified (see setQueryParamValue())
75
-     * @return string
76
-     */
77
-    public function getQueryParamKey()
78
-    {
79
-        return $this->query_param_key;
80
-    }
81
-
82
-    /**
83
-     * Modifies the query parameter key passed in (Eg this is done when rewriting the simplified specified operator REST
84
-     * query parameters into the legacy structure)
85
-     * @param string|array|int|float $query_param_value
86
-     */
87
-    private function setQueryParamValue($query_param_value)
88
-    {
89
-        $this->query_param_value = $query_param_value;
90
-    }
91
-
92
-    /**
93
-     * Gets the original query parameter value passed in.
94
-     * @return string
95
-     */
96
-    public function getQueryParamValue()
97
-    {
98
-        return $this->query_param_value;
99
-    }
100
-
101
-    /**
102
-     * Gets the context object.
103
-     * @return RestIncomingQueryParamContext
104
-     */
105
-    public function getContext()
106
-    {
107
-        return $this->context;
108
-    }
109
-
110
-    /**
111
-     * Sets the query parameter key. This may be used to rewrite a key into its non-GMT alternative.
112
-     * @param string $query_param_key
113
-     */
114
-    private function setQueryParamKey($query_param_key)
115
-    {
116
-        $this->query_param_key = $query_param_key;
117
-    }
118
-
119
-    /**
120
-     * Gets the field the query parameter key indicated. This may be null (in cases where the query parameter key
121
-     * did not indicate a field, eg if it were `OR`).
122
-     * @return EE_Model_Field_Base|null
123
-     */
124
-    public function getField()
125
-    {
126
-        return $this->field;
127
-    }
128
-
129
-    /**
130
-     * Gets the query parameter key (with the star and everything afterwards removed).
131
-     * @return string
132
-     */
133
-    public function getQueryParamKeySansStars()
134
-    {
135
-        return $this->query_param_key_sans_stars;
136
-    }
137
-
138
-    /**
139
-     * Gets the timezone associated with this model (the site timezone, except for GMT datetime fields).
140
-     * @return string
141
-     */
142
-    public function getTimezone()
143
-    {
144
-        return $this->timezone;
145
-    }
146
-
147
-    /**
148
-     * Returns whether or not this is a GMT field
149
-     * @return boolean
150
-     */
151
-    public function isGmtField()
152
-    {
153
-        return $this->is_gmt_field;
154
-    }
155
-
156
-    /**
157
-     * Sets the field indicated by the query parameter key (might be null).
158
-     * @param EE_Model_Field_Base|null $field
159
-     */
160
-    private function setField(EE_Model_Field_Base $field = null)
161
-    {
162
-        $this->field = $field;
163
-    }
164
-
165
-    /**
166
-     * Sets the query parameter key-with-stars-removed.
167
-     * @param string $query_param_key_sans_stars
168
-     */
169
-    private function setQueryParamKeySansStars($query_param_key_sans_stars)
170
-    {
171
-        $this->query_param_key_sans_stars = $query_param_key_sans_stars;
172
-    }
173
-
174
-    /**
175
-     * Sets the timezone (this could be a timezeon offset string).
176
-     * @param string $timezone
177
-     */
178
-    private function setTimezone($timezone)
179
-    {
180
-        $this->timezone = $timezone;
181
-    }
182
-
183
-    /**
184
-     * @param mixed $is_gmt_field
185
-     */
186
-    private function setIsGmtField($is_gmt_field)
187
-    {
188
-        $this->is_gmt_field = $is_gmt_field;
189
-    }
190
-
191
-    /**
192
-     * Determines what field, query param name, and query param name without stars, and timezone to use.
193
-     * @since 4.9.72.p
194
-     * @type EE_Model_Field_Base $field
195
-     * @return void {
196
-     * @throws EE_Error
197
-     * @throws InvalidDataTypeException
198
-     * @throws InvalidInterfaceException
199
-     * @throws InvalidArgumentException
200
-     */
201
-    private function determineFieldAndTimezone()
202
-    {
203
-        $this->setQueryParamKeySansStars(ModelDataTranslator::removeStarsAndAnythingAfterFromConditionQueryParamKey(
204
-            $this->getQueryParamKey()
205
-        ));
206
-        $this->setField(ModelDataTranslator::deduceFieldFromQueryParam(
207
-            $this->getQueryParamKeySansStars(),
208
-            $this->getContext()->getModel()
209
-        ));
210
-        // double-check is it a *_gmt field?
211
-        if (!$this->getField() instanceof EE_Model_Field_Base
212
-            && ModelDataTranslator::isGmtDateFieldName($this->getQueryParamKeySansStars())
213
-        ) {
214
-            // yep, take off '_gmt', and find the field
215
-            $this->setQueryParamKey(ModelDataTranslator::removeGmtFromFieldName($this->getQueryParamKeySansStars()));
216
-            $this->setField(ModelDataTranslator::deduceFieldFromQueryParam(
217
-                $this->getQueryParamKey(),
218
-                $this->context->getModel()
219
-            ));
220
-            $this->setTimezone('UTC');
221
-            $this->setIsGmtField(true);
222
-        } elseif ($this->getField() instanceof EE_Datetime_Field) {
223
-            // so it's not a GMT field. Set the timezone on the model to the default
224
-            $this->setTimezone(EEH_DTT_Helper::get_valid_timezone_string());
225
-        } else {
226
-            // just keep using what's already set for the timezone
227
-            $this->setTimezone($this->context->getModel()->get_timezone());
228
-        }
229
-        $this->assertOnlyAdminCanReadPasswordFields();
230
-    }
231
-
232
-    /**
233
-     * Throws an exception if a non-admin is trying to query by password.
234
-     * @since 4.9.74.p
235
-     * @throws RestException
236
-     */
237
-    private function assertOnlyAdminCanReadPasswordFields()
238
-    {
239
-        if ($this->getField() instanceof EE_Password_Field
240
-            && ! current_user_can(EE_Restriction_Generator_Base::get_default_restrictions_cap())) {
241
-            // only full admins can query by password. sorry bub!
242
-            throw new RestException(
243
-                'only_admins_can_query_by_password',
244
-                // @codingStandardsIgnoreStart
245
-                esc_html__('You attempted to filter by a password field without the needed privileges. Only a full admin is allowed to do that.', 'event_espresso'),
246
-                // @codingStandardsIgnoreEnd
247
-                array(
248
-                    'status' => 403
249
-                )
250
-            );
251
-        }
252
-    }
253
-
254
-    /**
255
-     * Given a ton of input, determines the value to use for the models.
256
-     * @since 4.9.72.p
257
-     * @return array|null
258
-     * @throws DomainException
259
-     * @throws EE_Error
260
-     * @throws RestException
261
-     * @throws DomainException
262
-     */
263
-    public function determineConditionsQueryParameterValue()
264
-    {
265
-        if ($this->valueIsArrayDuringRead()) {
266
-            return $this->determineModelValueGivenRestInputArray();
267
-        }
268
-        return ModelDataTranslator::prepareFieldValueFromJson(
269
-            $this->getField(),
270
-            $this->getQueryParamValue(),
271
-            $this->getContext()->getRequestedVersion(),
272
-            $this->getTimezone()
273
-        );
274
-    }
275
-
276
-    /**
277
-     * Given that the array value provided was itself an array, handles finding the correct value to pass to the model.
278
-     * @since 4.9.72.p
279
-     * @return array|null
280
-     * @throws RestException
281
-     */
282
-    private function determineModelValueGivenRestInputArray()
283
-    {
284
-        $this->transformSimplifiedSpecifiedOperatorSyntaxIntoStandardSyntax();
285
-        // did they specify an operator?
286
-        if ($this->valueIsLegacySpecifiedOperator()) {
287
-            $query_param_value = $this->getQueryParamValue();
288
-            $sub_array_key = $query_param_value[0];
289
-            $translated_value = array($sub_array_key);
290
-            if ($this->operatorIsNAry($sub_array_key)) {
291
-                $translated_value[] = $this->prepareValuesFromJson($query_param_value[1]);
292
-            } elseif ($this->operatorIsTernary($sub_array_key)) {
293
-                $translated_value[] = array(
294
-                    $this->prepareValuesFromJson($query_param_value[1][0]),
295
-                    $this->prepareValuesFromJson($query_param_value[1][1])
296
-                );
297
-            } elseif ($this->operatorIsLike($sub_array_key)) {
298
-                // we want to leave this value mostly-as-is (eg don't force it to be a float
299
-                // or a boolean or an enum value. Leave it as-is with wildcards etc)
300
-                // but do verify it at least doesn't have any serialized data
301
-                ModelDataTranslator::throwExceptionIfContainsSerializedData($query_param_value[1]);
302
-                $translated_value[] = $query_param_value[1];
303
-            } elseif ($this->operatorIsUnary($sub_array_key)) {
304
-                // no arguments should have been provided, so don't look for any
305
-            } elseif ($this->operatorisBinary($sub_array_key)) {
306
-                // it's a valid operator, but none of the exceptions. Treat it normally.
307
-                $translated_value[] = $this->prepareValuesFromJson($query_param_value[1]);
308
-            } else {
309
-                // so they provided a valid operator, but wrong number of arguments
310
-                $this->throwWrongNumberOfArgsExceptionIfDebugging($sub_array_key);
311
-                $translated_value = null;
312
-            }
313
-        } else {
314
-            // so they didn't provide a valid operator
315
-            // if we aren't in debug mode, then just try our best to fulfill the user's request
316
-            $this->throwInvalidOperatorExceptionIfDebugging();
317
-            $translated_value = null;
318
-        }
319
-        return $translated_value;
320
-    }
321
-
322
-    /**
323
-     * Returns if this request is a "read" request and the value provided was an array.
324
-     * This will indicate is such things as `array('<', 123)` and `array('IN', array(1,2,3))` are acceptable or not.
325
-     * @since 4.9.72.p
326
-     * @return boolean
327
-     */
328
-    private function valueIsArrayDuringRead()
329
-    {
330
-        return !$this->getContext()->isWriting() && is_array($this->getQueryParamValue());
331
-    }
332
-
333
-    /**
334
-     * Returns if the value provided was an associative array (we should have already verified it's an array of some
335
-     * sort). If the value is an associative array, it had better be in the simplified specified operator structure.
336
-     * @since 4.9.72.p
337
-     * @return boolean
338
-     */
339
-    private function valueIsAssociativeArray()
340
-    {
341
-        return !EEH_Array::is_array_numerically_and_sequentially_indexed($this->getQueryParamValue());
342
-    }
343
-
344
-    /**
345
-     * Checks if the array value is itself an array that fits into the simplified specified operator structure
346
-     * (eg `array('!=' => 123)`).
347
-     * @since 4.9.72.p
348
-     * @return boolean
349
-     */
350
-    private function valueIsSimplifiedSpecifiedOperator()
351
-    {
352
-        return count($this->getQueryParamValue()) === 1
353
-            && array_key_exists(
354
-                key($this->getQueryParamValue()),
355
-                $this->getContext()->getModel()->valid_operators()
356
-            );
357
-    }
358
-
359
-    /**
360
-     * Throws an exception if the sub-value is an array (eg `array('!=' => array())`). It needs to just be a string,
361
-     * of either comma-separated-values, or a JSON array.
362
-     * @since 4.9.72.p
363
-     * @param $sub_array_key
364
-     * @param $sub_array_value
365
-     * @throws RestException
366
-     */
367
-    private function assertSubValueIsntArray($sub_array_key, $sub_array_value)
368
-    {
369
-        if (is_array($sub_array_value) && EED_Core_Rest_Api::debugMode()) {
370
-            throw new RestException(
371
-                'csv_or_json_string_only',
372
-                sprintf(
373
-                    /* translators: 1: variable name*/
374
-                    esc_html__(
375
-                        'The value provided for the operator "%1$s" should be comma-separated value string or a JSON array.',
376
-                        'event_espresso'
377
-                    ),
378
-                    $sub_array_key
379
-                ),
380
-                array(
381
-                    'status' => 400,
382
-                )
383
-            );
384
-        }
385
-    }
386
-
387
-    /**
388
-     * Determines if the sub-array key is an operator taking 3 or more operators.
389
-     * @since 4.9.72.p
390
-     * @param $sub_array_key
391
-     * @return boolean
392
-     */
393
-    private function subArrayKeyIsNonBinaryOperator($sub_array_key)
394
-    {
395
-        return array_key_exists(
396
-            $sub_array_key,
397
-            array_merge(
398
-                $this->getContext()->getModel()->valid_in_style_operators(),
399
-                $this->getContext()->getModel()->valid_between_style_operators()
400
-            )
401
-        );
402
-    }
403
-
404
-    /**
405
-     * Given that the $sub_array_key is a string, checks if it's an operator taking only 1 argument.
406
-     * @since 4.9.72.p
407
-     * @param string $sub_array_key
408
-     * @return boolean
409
-     */
410
-    private function subArrayKeyIsUnaryOperator($sub_array_key)
411
-    {
412
-        return array_key_exists(
413
-            $sub_array_key,
414
-            $this->getContext()->getModel()->valid_null_style_operators()
415
-        );
416
-    }
417
-
418
-    /**
419
-     * Parses the $sub_array_value string into an array (given it could either be a comma-separated-list or a JSON
420
-     * array). eg `"1,2,3"` or `"[1,2,3]"` into `array(1,2,3)`.
421
-     * @since 4.9.72.p
422
-     * @param $sub_array_value
423
-     * @return array|mixed|object
424
-     */
425
-    private function extractQuickStyleSpecifiedOperatorValue($sub_array_value)
426
-    {
427
-        // the value should be JSON or CSV
428
-        $values = json_decode($sub_array_value);
429
-        if (!is_array($values)) {
430
-            $values = array_filter(
431
-                array_map(
432
-                    'trim',
433
-                    explode(
434
-                        ',',
435
-                        $sub_array_value
436
-                    )
437
-                )
438
-            );
439
-        }
440
-        return $values;
441
-    }
442
-
443
-    /**
444
-     * Throws an exception if the value isn't a simplified specified operator (only called when we expect that).
445
-     * @since 4.9.72.p
446
-     * @throws RestException
447
-     */
448
-    private function assertSimplifiedSpecifiedOperator()
449
-    {
450
-        if (!$this->valueIsSimplifiedSpecifiedOperator() && EED_Core_Rest_Api::debugMode()) {
451
-            throw new RestException(
452
-                'numerically_indexed_array_of_values_only',
453
-                sprintf(
454
-                    /* translators: 1: variable name*/
455
-                    esc_html__(
456
-                        'The array provided for the parameter "%1$s" should be numerically indexed.',
457
-                        'event_espresso'
458
-                    ),
459
-                    $this->getQueryParamKey()
460
-                ),
461
-                array(
462
-                    'status' => 400,
463
-                )
464
-            );
465
-        }
466
-    }
467
-
468
-    /**
469
-     * If query_param_value were in the simplified specific operator structure, change it into the legacy structure.
470
-     * @since 4.9.72.p
471
-     * @throws RestException
472
-     */
473
-    private function transformSimplifiedSpecifiedOperatorSyntaxIntoStandardSyntax()
474
-    {
475
-        if ($this->valueIsAssociativeArray()) {
476
-            $this->assertSimplifiedSpecifiedOperator();
477
-            $query_param_value = $this->getQueryParamValue();
478
-            $sub_array_value = reset($query_param_value);
479
-            $sub_array_key = key($query_param_value);
480
-            $this->assertSubValueIsntArray($sub_array_key, $sub_array_value);
481
-            // they're doing something like "&where[EVT_ID][IN]=1,2,3" or "&where[EVT_ID][>]=5"
482
-            if ($this->subArrayKeyIsNonBinaryOperator($sub_array_key)) {
483
-                $this->setQueryParamValue(array(
484
-                    $sub_array_key,
485
-                    $this->extractQuickStyleSpecifiedOperatorValue($sub_array_value)
486
-                ));
487
-            } elseif ($this->subArrayKeyIsUnaryOperator($sub_array_key)) {
488
-                $this->setQueryParamValue(array($sub_array_key));
489
-            } else {
490
-                $this->setQueryParamValue(array($sub_array_key, $sub_array_value));
491
-            }
492
-        }
493
-    }
494
-
495
-    /**
496
-     * Returns true is the value is an array using the legacy structure to specify the operator. Eg `array('!=',123)`.
497
-     * @since 4.9.72.p
498
-     * @return boolean
499
-     */
500
-    private function valueIsLegacySpecifiedOperator()
501
-    {
502
-        $valid_operators = $this->getContext()->getModel()->valid_operators();
503
-        $query_param_value = $this->getQueryParamValue();
504
-        return isset($query_param_value[0])
505
-            && isset($valid_operators[ $query_param_value[0] ]);
506
-    }
507
-
508
-    /**
509
-     * Returns true if the value specified operator accepts arbitrary number of arguments, like "IN".
510
-     * @since 4.9.72.p
511
-     * @param $operator
512
-     * @return boolean
513
-     */
514
-    private function operatorIsNAry($operator)
515
-    {
516
-        $valueArray = $this->getQueryParamValue();
517
-        return array_key_exists(
518
-            $operator,
519
-            $this->getContext()->getModel()->valid_in_style_operators()
520
-        )
521
-            && isset($valueArray[1])
522
-            && is_array($valueArray[1])
523
-            && !isset($valueArray[2]);
524
-    }
525
-
526
-    /**
527
-     * Returns true if the operator accepts 3 arguments (eg "BETWEEN").
528
-     * So we're looking for a value that looks like
529
-     * `array('BETWEEN', array('2015-01-01T00:00:00', '2016-01-01T00:00:00'))`.
530
-     * @since 4.9.72.p
531
-     * @param $operator
532
-     * @return boolean
533
-     */
534
-    private function operatorIsTernary($operator)
535
-    {
536
-        $query_param_value = $this->getQueryParamValue();
537
-        return array_key_exists($operator, $this->getContext()->getModel()->valid_between_style_operators())
538
-            && isset($query_param_value[1])
539
-            && is_array($query_param_value[1])
540
-            && isset($query_param_value[1][0], $query_param_value[1][1])
541
-            && !isset($query_param_value[1][2])
542
-            && !isset($query_param_value[2]);
543
-    }
544
-
545
-    /**
546
-     * Returns true if the operator is a similar to LIKE, indicating the value may have wildcards we should leave alone.
547
-     * @since 4.9.72.p
548
-     * @param $operator
549
-     * @return boolean
550
-     */
551
-    private function operatorIsLike($operator)
552
-    {
553
-        $query_param_value = $this->getQueryParamValue();
554
-        return array_key_exists($operator, $this->getContext()->getModel()->valid_like_style_operators())
555
-            && isset($query_param_value[1])
556
-            && !isset($query_param_value[2]);
557
-    }
558
-
559
-    /**
560
-     * Returns true if the operator only takes one argument (eg it's like `IS NULL`).
561
-     * @since 4.9.72.p
562
-     * @param $operator
563
-     * @return boolean
564
-     */
565
-    private function operatorIsUnary($operator)
566
-    {
567
-        $query_param_value = $this->getQueryParamValue();
568
-        return array_key_exists($operator, $this->getContext()->getModel()->valid_null_style_operators())
569
-            && !isset($query_param_value[1]);
570
-    }
571
-
572
-    /**
573
-     * Returns true if the operator specified is a binary opeator (eg `=`, `!=`)
574
-     * @since 4.9.72.p
575
-     * @param $operator
576
-     * @return boolean
577
-     */
578
-    private function operatorisBinary($operator)
579
-    {
580
-        $query_param_value = $this->getQueryParamValue();
581
-        $model = $this->getContext()->getModel();
582
-        return isset($query_param_value[1])
583
-            && !isset($query_param_value[2])
584
-            && !array_key_exists(
585
-                $operator,
586
-                array_merge(
587
-                    $model->valid_in_style_operators(),
588
-                    $model->valid_null_style_operators(),
589
-                    $model->valid_like_style_operators(),
590
-                    $model->valid_between_style_operators()
591
-                )
592
-            );
593
-    }
594
-
595
-    /**
596
-     * If we're debugging, throws an exception saying that the wrong number of arguments was provided.
597
-     * @since 4.9.72.p
598
-     * @param $operator
599
-     * @throws RestException
600
-     */
601
-    private function throwWrongNumberOfArgsExceptionIfDebugging($operator)
602
-    {
603
-        if (EED_Core_Rest_Api::debugMode()) {
604
-            throw new RestException(
605
-                'wrong_number_of_arguments',
606
-                sprintf(
607
-                    esc_html__(
608
-                        'The operator you provided, "%1$s" had the wrong number of arguments',
609
-                        'event_espresso'
610
-                    ),
611
-                    $operator
612
-                ),
613
-                array(
614
-                    'status' => 400,
615
-                )
616
-            );
617
-        }
618
-    }
619
-
620
-    /**
621
-     * Wrapper for ModelDataTranslator::prepareFieldValuesFromJson(), just a tad more DRY.
622
-     * @since 4.9.72.p
623
-     * @param $value
624
-     * @return mixed
625
-     * @throws RestException
626
-     */
627
-    private function prepareValuesFromJson($value)
628
-    {
629
-        return ModelDataTranslator::prepareFieldValuesFromJson(
630
-            $this->getField(),
631
-            $value,
632
-            $this->getContext()->getRequestedVersion(),
633
-            $this->getTimezone()
634
-        );
635
-    }
636
-
637
-    /**
638
-     * Throws an exception if an invalid operator was specified and we're debugging.
639
-     * @since 4.9.72.p
640
-     * @throws RestException
641
-     */
642
-    private function throwInvalidOperatorExceptionIfDebugging()
643
-    {
644
-        // so they didn't provide a valid operator
645
-        if (EED_Core_Rest_Api::debugMode()) {
646
-            throw new RestException(
647
-                'invalid_operator',
648
-                sprintf(
649
-                    esc_html__(
650
-                        'You provided an invalid parameter, with key "%1$s" and value "%2$s"',
651
-                        'event_espresso'
652
-                    ),
653
-                    $this->getQueryParamKey(),
654
-                    $this->getQueryParamValue()
655
-                ),
656
-                array(
657
-                    'status' => 400,
658
-                )
659
-            );
660
-        }
661
-    }
662
-
663
-    /**
664
-     * Returns true if the query_param_key was a logic query parameter, eg `OR`, `AND`, `NOT`, `OR*`, etc.
665
-     * @since 4.9.72.p
666
-     * @return boolean
667
-     */
668
-    private function isLogicQueryParam()
669
-    {
670
-        return in_array($this->getQueryParamKeySansStars(), $this->getContext()->getModel()->logic_query_param_keys());
671
-    }
672
-
673
-
674
-    /**
675
-     * If the query param isn't for a field, it must be a nested query parameter which requires different logic.
676
-     * @since 4.9.72.p
677
-     * @return array
678
-     * @throws DomainException
679
-     * @throws EE_Error
680
-     * @throws RestException
681
-     * @throws InvalidDataTypeException
682
-     * @throws InvalidInterfaceException
683
-     * @throws InvalidArgumentException
684
-     */
685
-    public function determineNestedConditionQueryParameters()
686
-    {
687
-
688
-        // so this param doesn't correspond to a field eh?
689
-        if ($this->getContext()->isWriting()) {
690
-            // always tell API clients about invalid parameters when they're creating data. Otherwise,
691
-            // they are probably going to create invalid data
692
-            throw new RestException(
693
-                'invalid_field',
694
-                sprintf(
695
-                    /* translators: 1: variable name */
696
-                    esc_html__('You have provided an invalid parameter: "%1$s"', 'event_espresso'),
697
-                    $this->getQueryParamKey()
698
-                )
699
-            );
700
-        }
701
-        // so it's not for a field, is it a logic query param key?
702
-        if ($this->isLogicQueryParam()) {
703
-            return ModelDataTranslator::prepareConditionsQueryParamsForModels(
704
-                $this->getQueryParamValue(),
705
-                $this->getContext()->getModel(),
706
-                $this->getContext()->getRequestedVersion()
707
-            );
708
-        }
709
-        if (EED_Core_Rest_Api::debugMode()) {
710
-            // only tell API clients they got it wrong if we're in debug mode
711
-            // otherwise try our best ot fulfill their request by ignoring this invalid data
712
-            throw new RestException(
713
-                'invalid_parameter',
714
-                sprintf(
715
-                    /* translators: 1: variable name */
716
-                    esc_html__(
717
-                        'You provided an invalid parameter, with key "%1$s"',
718
-                        'event_espresso'
719
-                    ),
720
-                    $this->getQueryParamKey()
721
-                ),
722
-                array(
723
-                    'status' => 400,
724
-                )
725
-            );
726
-        }
727
-        return null;
728
-    }
31
+	private $query_param_key;
32
+	private $query_param_value;
33
+	/**
34
+	 * @var RestIncomingQueryParamContext
35
+	 */
36
+	private $context;
37
+
38
+	/**
39
+	 * @var EE_Model_Field_Base|null
40
+	 */
41
+	private $field;
42
+
43
+	/**
44
+	 * @var string same as $query_param_key but has the * and anything after it removed
45
+	 */
46
+	private $query_param_key_sans_stars;
47
+
48
+	/**
49
+	 * @var string for timezone or timezone offset
50
+	 */
51
+	private $timezone;
52
+
53
+	/**
54
+	 * @var boolean if the field in $query_param_key is for a GMT field (eg `EVT_modified_gmt`)
55
+	 */
56
+	private $is_gmt_field = false;
57
+
58
+	/**
59
+	 * RestIncomingQueryParamMetadata constructor.
60
+	 * You probably want to call
61
+	 * @param string $query_param_key
62
+	 * @param string $query_param_value
63
+	 * @param RestIncomingQueryParamContext $context
64
+	 */
65
+	public function __construct($query_param_key, $query_param_value, RestIncomingQueryParamContext $context)
66
+	{
67
+		$this->query_param_key = $query_param_key;
68
+		$this->query_param_value = $query_param_value;
69
+		$this->context = $context;
70
+		$this->determineFieldAndTimezone();
71
+	}
72
+
73
+	/**
74
+	 * Gets the query parameter key. This may have been modified (see setQueryParamValue())
75
+	 * @return string
76
+	 */
77
+	public function getQueryParamKey()
78
+	{
79
+		return $this->query_param_key;
80
+	}
81
+
82
+	/**
83
+	 * Modifies the query parameter key passed in (Eg this is done when rewriting the simplified specified operator REST
84
+	 * query parameters into the legacy structure)
85
+	 * @param string|array|int|float $query_param_value
86
+	 */
87
+	private function setQueryParamValue($query_param_value)
88
+	{
89
+		$this->query_param_value = $query_param_value;
90
+	}
91
+
92
+	/**
93
+	 * Gets the original query parameter value passed in.
94
+	 * @return string
95
+	 */
96
+	public function getQueryParamValue()
97
+	{
98
+		return $this->query_param_value;
99
+	}
100
+
101
+	/**
102
+	 * Gets the context object.
103
+	 * @return RestIncomingQueryParamContext
104
+	 */
105
+	public function getContext()
106
+	{
107
+		return $this->context;
108
+	}
109
+
110
+	/**
111
+	 * Sets the query parameter key. This may be used to rewrite a key into its non-GMT alternative.
112
+	 * @param string $query_param_key
113
+	 */
114
+	private function setQueryParamKey($query_param_key)
115
+	{
116
+		$this->query_param_key = $query_param_key;
117
+	}
118
+
119
+	/**
120
+	 * Gets the field the query parameter key indicated. This may be null (in cases where the query parameter key
121
+	 * did not indicate a field, eg if it were `OR`).
122
+	 * @return EE_Model_Field_Base|null
123
+	 */
124
+	public function getField()
125
+	{
126
+		return $this->field;
127
+	}
128
+
129
+	/**
130
+	 * Gets the query parameter key (with the star and everything afterwards removed).
131
+	 * @return string
132
+	 */
133
+	public function getQueryParamKeySansStars()
134
+	{
135
+		return $this->query_param_key_sans_stars;
136
+	}
137
+
138
+	/**
139
+	 * Gets the timezone associated with this model (the site timezone, except for GMT datetime fields).
140
+	 * @return string
141
+	 */
142
+	public function getTimezone()
143
+	{
144
+		return $this->timezone;
145
+	}
146
+
147
+	/**
148
+	 * Returns whether or not this is a GMT field
149
+	 * @return boolean
150
+	 */
151
+	public function isGmtField()
152
+	{
153
+		return $this->is_gmt_field;
154
+	}
155
+
156
+	/**
157
+	 * Sets the field indicated by the query parameter key (might be null).
158
+	 * @param EE_Model_Field_Base|null $field
159
+	 */
160
+	private function setField(EE_Model_Field_Base $field = null)
161
+	{
162
+		$this->field = $field;
163
+	}
164
+
165
+	/**
166
+	 * Sets the query parameter key-with-stars-removed.
167
+	 * @param string $query_param_key_sans_stars
168
+	 */
169
+	private function setQueryParamKeySansStars($query_param_key_sans_stars)
170
+	{
171
+		$this->query_param_key_sans_stars = $query_param_key_sans_stars;
172
+	}
173
+
174
+	/**
175
+	 * Sets the timezone (this could be a timezeon offset string).
176
+	 * @param string $timezone
177
+	 */
178
+	private function setTimezone($timezone)
179
+	{
180
+		$this->timezone = $timezone;
181
+	}
182
+
183
+	/**
184
+	 * @param mixed $is_gmt_field
185
+	 */
186
+	private function setIsGmtField($is_gmt_field)
187
+	{
188
+		$this->is_gmt_field = $is_gmt_field;
189
+	}
190
+
191
+	/**
192
+	 * Determines what field, query param name, and query param name without stars, and timezone to use.
193
+	 * @since 4.9.72.p
194
+	 * @type EE_Model_Field_Base $field
195
+	 * @return void {
196
+	 * @throws EE_Error
197
+	 * @throws InvalidDataTypeException
198
+	 * @throws InvalidInterfaceException
199
+	 * @throws InvalidArgumentException
200
+	 */
201
+	private function determineFieldAndTimezone()
202
+	{
203
+		$this->setQueryParamKeySansStars(ModelDataTranslator::removeStarsAndAnythingAfterFromConditionQueryParamKey(
204
+			$this->getQueryParamKey()
205
+		));
206
+		$this->setField(ModelDataTranslator::deduceFieldFromQueryParam(
207
+			$this->getQueryParamKeySansStars(),
208
+			$this->getContext()->getModel()
209
+		));
210
+		// double-check is it a *_gmt field?
211
+		if (!$this->getField() instanceof EE_Model_Field_Base
212
+			&& ModelDataTranslator::isGmtDateFieldName($this->getQueryParamKeySansStars())
213
+		) {
214
+			// yep, take off '_gmt', and find the field
215
+			$this->setQueryParamKey(ModelDataTranslator::removeGmtFromFieldName($this->getQueryParamKeySansStars()));
216
+			$this->setField(ModelDataTranslator::deduceFieldFromQueryParam(
217
+				$this->getQueryParamKey(),
218
+				$this->context->getModel()
219
+			));
220
+			$this->setTimezone('UTC');
221
+			$this->setIsGmtField(true);
222
+		} elseif ($this->getField() instanceof EE_Datetime_Field) {
223
+			// so it's not a GMT field. Set the timezone on the model to the default
224
+			$this->setTimezone(EEH_DTT_Helper::get_valid_timezone_string());
225
+		} else {
226
+			// just keep using what's already set for the timezone
227
+			$this->setTimezone($this->context->getModel()->get_timezone());
228
+		}
229
+		$this->assertOnlyAdminCanReadPasswordFields();
230
+	}
231
+
232
+	/**
233
+	 * Throws an exception if a non-admin is trying to query by password.
234
+	 * @since 4.9.74.p
235
+	 * @throws RestException
236
+	 */
237
+	private function assertOnlyAdminCanReadPasswordFields()
238
+	{
239
+		if ($this->getField() instanceof EE_Password_Field
240
+			&& ! current_user_can(EE_Restriction_Generator_Base::get_default_restrictions_cap())) {
241
+			// only full admins can query by password. sorry bub!
242
+			throw new RestException(
243
+				'only_admins_can_query_by_password',
244
+				// @codingStandardsIgnoreStart
245
+				esc_html__('You attempted to filter by a password field without the needed privileges. Only a full admin is allowed to do that.', 'event_espresso'),
246
+				// @codingStandardsIgnoreEnd
247
+				array(
248
+					'status' => 403
249
+				)
250
+			);
251
+		}
252
+	}
253
+
254
+	/**
255
+	 * Given a ton of input, determines the value to use for the models.
256
+	 * @since 4.9.72.p
257
+	 * @return array|null
258
+	 * @throws DomainException
259
+	 * @throws EE_Error
260
+	 * @throws RestException
261
+	 * @throws DomainException
262
+	 */
263
+	public function determineConditionsQueryParameterValue()
264
+	{
265
+		if ($this->valueIsArrayDuringRead()) {
266
+			return $this->determineModelValueGivenRestInputArray();
267
+		}
268
+		return ModelDataTranslator::prepareFieldValueFromJson(
269
+			$this->getField(),
270
+			$this->getQueryParamValue(),
271
+			$this->getContext()->getRequestedVersion(),
272
+			$this->getTimezone()
273
+		);
274
+	}
275
+
276
+	/**
277
+	 * Given that the array value provided was itself an array, handles finding the correct value to pass to the model.
278
+	 * @since 4.9.72.p
279
+	 * @return array|null
280
+	 * @throws RestException
281
+	 */
282
+	private function determineModelValueGivenRestInputArray()
283
+	{
284
+		$this->transformSimplifiedSpecifiedOperatorSyntaxIntoStandardSyntax();
285
+		// did they specify an operator?
286
+		if ($this->valueIsLegacySpecifiedOperator()) {
287
+			$query_param_value = $this->getQueryParamValue();
288
+			$sub_array_key = $query_param_value[0];
289
+			$translated_value = array($sub_array_key);
290
+			if ($this->operatorIsNAry($sub_array_key)) {
291
+				$translated_value[] = $this->prepareValuesFromJson($query_param_value[1]);
292
+			} elseif ($this->operatorIsTernary($sub_array_key)) {
293
+				$translated_value[] = array(
294
+					$this->prepareValuesFromJson($query_param_value[1][0]),
295
+					$this->prepareValuesFromJson($query_param_value[1][1])
296
+				);
297
+			} elseif ($this->operatorIsLike($sub_array_key)) {
298
+				// we want to leave this value mostly-as-is (eg don't force it to be a float
299
+				// or a boolean or an enum value. Leave it as-is with wildcards etc)
300
+				// but do verify it at least doesn't have any serialized data
301
+				ModelDataTranslator::throwExceptionIfContainsSerializedData($query_param_value[1]);
302
+				$translated_value[] = $query_param_value[1];
303
+			} elseif ($this->operatorIsUnary($sub_array_key)) {
304
+				// no arguments should have been provided, so don't look for any
305
+			} elseif ($this->operatorisBinary($sub_array_key)) {
306
+				// it's a valid operator, but none of the exceptions. Treat it normally.
307
+				$translated_value[] = $this->prepareValuesFromJson($query_param_value[1]);
308
+			} else {
309
+				// so they provided a valid operator, but wrong number of arguments
310
+				$this->throwWrongNumberOfArgsExceptionIfDebugging($sub_array_key);
311
+				$translated_value = null;
312
+			}
313
+		} else {
314
+			// so they didn't provide a valid operator
315
+			// if we aren't in debug mode, then just try our best to fulfill the user's request
316
+			$this->throwInvalidOperatorExceptionIfDebugging();
317
+			$translated_value = null;
318
+		}
319
+		return $translated_value;
320
+	}
321
+
322
+	/**
323
+	 * Returns if this request is a "read" request and the value provided was an array.
324
+	 * This will indicate is such things as `array('<', 123)` and `array('IN', array(1,2,3))` are acceptable or not.
325
+	 * @since 4.9.72.p
326
+	 * @return boolean
327
+	 */
328
+	private function valueIsArrayDuringRead()
329
+	{
330
+		return !$this->getContext()->isWriting() && is_array($this->getQueryParamValue());
331
+	}
332
+
333
+	/**
334
+	 * Returns if the value provided was an associative array (we should have already verified it's an array of some
335
+	 * sort). If the value is an associative array, it had better be in the simplified specified operator structure.
336
+	 * @since 4.9.72.p
337
+	 * @return boolean
338
+	 */
339
+	private function valueIsAssociativeArray()
340
+	{
341
+		return !EEH_Array::is_array_numerically_and_sequentially_indexed($this->getQueryParamValue());
342
+	}
343
+
344
+	/**
345
+	 * Checks if the array value is itself an array that fits into the simplified specified operator structure
346
+	 * (eg `array('!=' => 123)`).
347
+	 * @since 4.9.72.p
348
+	 * @return boolean
349
+	 */
350
+	private function valueIsSimplifiedSpecifiedOperator()
351
+	{
352
+		return count($this->getQueryParamValue()) === 1
353
+			&& array_key_exists(
354
+				key($this->getQueryParamValue()),
355
+				$this->getContext()->getModel()->valid_operators()
356
+			);
357
+	}
358
+
359
+	/**
360
+	 * Throws an exception if the sub-value is an array (eg `array('!=' => array())`). It needs to just be a string,
361
+	 * of either comma-separated-values, or a JSON array.
362
+	 * @since 4.9.72.p
363
+	 * @param $sub_array_key
364
+	 * @param $sub_array_value
365
+	 * @throws RestException
366
+	 */
367
+	private function assertSubValueIsntArray($sub_array_key, $sub_array_value)
368
+	{
369
+		if (is_array($sub_array_value) && EED_Core_Rest_Api::debugMode()) {
370
+			throw new RestException(
371
+				'csv_or_json_string_only',
372
+				sprintf(
373
+					/* translators: 1: variable name*/
374
+					esc_html__(
375
+						'The value provided for the operator "%1$s" should be comma-separated value string or a JSON array.',
376
+						'event_espresso'
377
+					),
378
+					$sub_array_key
379
+				),
380
+				array(
381
+					'status' => 400,
382
+				)
383
+			);
384
+		}
385
+	}
386
+
387
+	/**
388
+	 * Determines if the sub-array key is an operator taking 3 or more operators.
389
+	 * @since 4.9.72.p
390
+	 * @param $sub_array_key
391
+	 * @return boolean
392
+	 */
393
+	private function subArrayKeyIsNonBinaryOperator($sub_array_key)
394
+	{
395
+		return array_key_exists(
396
+			$sub_array_key,
397
+			array_merge(
398
+				$this->getContext()->getModel()->valid_in_style_operators(),
399
+				$this->getContext()->getModel()->valid_between_style_operators()
400
+			)
401
+		);
402
+	}
403
+
404
+	/**
405
+	 * Given that the $sub_array_key is a string, checks if it's an operator taking only 1 argument.
406
+	 * @since 4.9.72.p
407
+	 * @param string $sub_array_key
408
+	 * @return boolean
409
+	 */
410
+	private function subArrayKeyIsUnaryOperator($sub_array_key)
411
+	{
412
+		return array_key_exists(
413
+			$sub_array_key,
414
+			$this->getContext()->getModel()->valid_null_style_operators()
415
+		);
416
+	}
417
+
418
+	/**
419
+	 * Parses the $sub_array_value string into an array (given it could either be a comma-separated-list or a JSON
420
+	 * array). eg `"1,2,3"` or `"[1,2,3]"` into `array(1,2,3)`.
421
+	 * @since 4.9.72.p
422
+	 * @param $sub_array_value
423
+	 * @return array|mixed|object
424
+	 */
425
+	private function extractQuickStyleSpecifiedOperatorValue($sub_array_value)
426
+	{
427
+		// the value should be JSON or CSV
428
+		$values = json_decode($sub_array_value);
429
+		if (!is_array($values)) {
430
+			$values = array_filter(
431
+				array_map(
432
+					'trim',
433
+					explode(
434
+						',',
435
+						$sub_array_value
436
+					)
437
+				)
438
+			);
439
+		}
440
+		return $values;
441
+	}
442
+
443
+	/**
444
+	 * Throws an exception if the value isn't a simplified specified operator (only called when we expect that).
445
+	 * @since 4.9.72.p
446
+	 * @throws RestException
447
+	 */
448
+	private function assertSimplifiedSpecifiedOperator()
449
+	{
450
+		if (!$this->valueIsSimplifiedSpecifiedOperator() && EED_Core_Rest_Api::debugMode()) {
451
+			throw new RestException(
452
+				'numerically_indexed_array_of_values_only',
453
+				sprintf(
454
+					/* translators: 1: variable name*/
455
+					esc_html__(
456
+						'The array provided for the parameter "%1$s" should be numerically indexed.',
457
+						'event_espresso'
458
+					),
459
+					$this->getQueryParamKey()
460
+				),
461
+				array(
462
+					'status' => 400,
463
+				)
464
+			);
465
+		}
466
+	}
467
+
468
+	/**
469
+	 * If query_param_value were in the simplified specific operator structure, change it into the legacy structure.
470
+	 * @since 4.9.72.p
471
+	 * @throws RestException
472
+	 */
473
+	private function transformSimplifiedSpecifiedOperatorSyntaxIntoStandardSyntax()
474
+	{
475
+		if ($this->valueIsAssociativeArray()) {
476
+			$this->assertSimplifiedSpecifiedOperator();
477
+			$query_param_value = $this->getQueryParamValue();
478
+			$sub_array_value = reset($query_param_value);
479
+			$sub_array_key = key($query_param_value);
480
+			$this->assertSubValueIsntArray($sub_array_key, $sub_array_value);
481
+			// they're doing something like "&where[EVT_ID][IN]=1,2,3" or "&where[EVT_ID][>]=5"
482
+			if ($this->subArrayKeyIsNonBinaryOperator($sub_array_key)) {
483
+				$this->setQueryParamValue(array(
484
+					$sub_array_key,
485
+					$this->extractQuickStyleSpecifiedOperatorValue($sub_array_value)
486
+				));
487
+			} elseif ($this->subArrayKeyIsUnaryOperator($sub_array_key)) {
488
+				$this->setQueryParamValue(array($sub_array_key));
489
+			} else {
490
+				$this->setQueryParamValue(array($sub_array_key, $sub_array_value));
491
+			}
492
+		}
493
+	}
494
+
495
+	/**
496
+	 * Returns true is the value is an array using the legacy structure to specify the operator. Eg `array('!=',123)`.
497
+	 * @since 4.9.72.p
498
+	 * @return boolean
499
+	 */
500
+	private function valueIsLegacySpecifiedOperator()
501
+	{
502
+		$valid_operators = $this->getContext()->getModel()->valid_operators();
503
+		$query_param_value = $this->getQueryParamValue();
504
+		return isset($query_param_value[0])
505
+			&& isset($valid_operators[ $query_param_value[0] ]);
506
+	}
507
+
508
+	/**
509
+	 * Returns true if the value specified operator accepts arbitrary number of arguments, like "IN".
510
+	 * @since 4.9.72.p
511
+	 * @param $operator
512
+	 * @return boolean
513
+	 */
514
+	private function operatorIsNAry($operator)
515
+	{
516
+		$valueArray = $this->getQueryParamValue();
517
+		return array_key_exists(
518
+			$operator,
519
+			$this->getContext()->getModel()->valid_in_style_operators()
520
+		)
521
+			&& isset($valueArray[1])
522
+			&& is_array($valueArray[1])
523
+			&& !isset($valueArray[2]);
524
+	}
525
+
526
+	/**
527
+	 * Returns true if the operator accepts 3 arguments (eg "BETWEEN").
528
+	 * So we're looking for a value that looks like
529
+	 * `array('BETWEEN', array('2015-01-01T00:00:00', '2016-01-01T00:00:00'))`.
530
+	 * @since 4.9.72.p
531
+	 * @param $operator
532
+	 * @return boolean
533
+	 */
534
+	private function operatorIsTernary($operator)
535
+	{
536
+		$query_param_value = $this->getQueryParamValue();
537
+		return array_key_exists($operator, $this->getContext()->getModel()->valid_between_style_operators())
538
+			&& isset($query_param_value[1])
539
+			&& is_array($query_param_value[1])
540
+			&& isset($query_param_value[1][0], $query_param_value[1][1])
541
+			&& !isset($query_param_value[1][2])
542
+			&& !isset($query_param_value[2]);
543
+	}
544
+
545
+	/**
546
+	 * Returns true if the operator is a similar to LIKE, indicating the value may have wildcards we should leave alone.
547
+	 * @since 4.9.72.p
548
+	 * @param $operator
549
+	 * @return boolean
550
+	 */
551
+	private function operatorIsLike($operator)
552
+	{
553
+		$query_param_value = $this->getQueryParamValue();
554
+		return array_key_exists($operator, $this->getContext()->getModel()->valid_like_style_operators())
555
+			&& isset($query_param_value[1])
556
+			&& !isset($query_param_value[2]);
557
+	}
558
+
559
+	/**
560
+	 * Returns true if the operator only takes one argument (eg it's like `IS NULL`).
561
+	 * @since 4.9.72.p
562
+	 * @param $operator
563
+	 * @return boolean
564
+	 */
565
+	private function operatorIsUnary($operator)
566
+	{
567
+		$query_param_value = $this->getQueryParamValue();
568
+		return array_key_exists($operator, $this->getContext()->getModel()->valid_null_style_operators())
569
+			&& !isset($query_param_value[1]);
570
+	}
571
+
572
+	/**
573
+	 * Returns true if the operator specified is a binary opeator (eg `=`, `!=`)
574
+	 * @since 4.9.72.p
575
+	 * @param $operator
576
+	 * @return boolean
577
+	 */
578
+	private function operatorisBinary($operator)
579
+	{
580
+		$query_param_value = $this->getQueryParamValue();
581
+		$model = $this->getContext()->getModel();
582
+		return isset($query_param_value[1])
583
+			&& !isset($query_param_value[2])
584
+			&& !array_key_exists(
585
+				$operator,
586
+				array_merge(
587
+					$model->valid_in_style_operators(),
588
+					$model->valid_null_style_operators(),
589
+					$model->valid_like_style_operators(),
590
+					$model->valid_between_style_operators()
591
+				)
592
+			);
593
+	}
594
+
595
+	/**
596
+	 * If we're debugging, throws an exception saying that the wrong number of arguments was provided.
597
+	 * @since 4.9.72.p
598
+	 * @param $operator
599
+	 * @throws RestException
600
+	 */
601
+	private function throwWrongNumberOfArgsExceptionIfDebugging($operator)
602
+	{
603
+		if (EED_Core_Rest_Api::debugMode()) {
604
+			throw new RestException(
605
+				'wrong_number_of_arguments',
606
+				sprintf(
607
+					esc_html__(
608
+						'The operator you provided, "%1$s" had the wrong number of arguments',
609
+						'event_espresso'
610
+					),
611
+					$operator
612
+				),
613
+				array(
614
+					'status' => 400,
615
+				)
616
+			);
617
+		}
618
+	}
619
+
620
+	/**
621
+	 * Wrapper for ModelDataTranslator::prepareFieldValuesFromJson(), just a tad more DRY.
622
+	 * @since 4.9.72.p
623
+	 * @param $value
624
+	 * @return mixed
625
+	 * @throws RestException
626
+	 */
627
+	private function prepareValuesFromJson($value)
628
+	{
629
+		return ModelDataTranslator::prepareFieldValuesFromJson(
630
+			$this->getField(),
631
+			$value,
632
+			$this->getContext()->getRequestedVersion(),
633
+			$this->getTimezone()
634
+		);
635
+	}
636
+
637
+	/**
638
+	 * Throws an exception if an invalid operator was specified and we're debugging.
639
+	 * @since 4.9.72.p
640
+	 * @throws RestException
641
+	 */
642
+	private function throwInvalidOperatorExceptionIfDebugging()
643
+	{
644
+		// so they didn't provide a valid operator
645
+		if (EED_Core_Rest_Api::debugMode()) {
646
+			throw new RestException(
647
+				'invalid_operator',
648
+				sprintf(
649
+					esc_html__(
650
+						'You provided an invalid parameter, with key "%1$s" and value "%2$s"',
651
+						'event_espresso'
652
+					),
653
+					$this->getQueryParamKey(),
654
+					$this->getQueryParamValue()
655
+				),
656
+				array(
657
+					'status' => 400,
658
+				)
659
+			);
660
+		}
661
+	}
662
+
663
+	/**
664
+	 * Returns true if the query_param_key was a logic query parameter, eg `OR`, `AND`, `NOT`, `OR*`, etc.
665
+	 * @since 4.9.72.p
666
+	 * @return boolean
667
+	 */
668
+	private function isLogicQueryParam()
669
+	{
670
+		return in_array($this->getQueryParamKeySansStars(), $this->getContext()->getModel()->logic_query_param_keys());
671
+	}
672
+
673
+
674
+	/**
675
+	 * If the query param isn't for a field, it must be a nested query parameter which requires different logic.
676
+	 * @since 4.9.72.p
677
+	 * @return array
678
+	 * @throws DomainException
679
+	 * @throws EE_Error
680
+	 * @throws RestException
681
+	 * @throws InvalidDataTypeException
682
+	 * @throws InvalidInterfaceException
683
+	 * @throws InvalidArgumentException
684
+	 */
685
+	public function determineNestedConditionQueryParameters()
686
+	{
687
+
688
+		// so this param doesn't correspond to a field eh?
689
+		if ($this->getContext()->isWriting()) {
690
+			// always tell API clients about invalid parameters when they're creating data. Otherwise,
691
+			// they are probably going to create invalid data
692
+			throw new RestException(
693
+				'invalid_field',
694
+				sprintf(
695
+					/* translators: 1: variable name */
696
+					esc_html__('You have provided an invalid parameter: "%1$s"', 'event_espresso'),
697
+					$this->getQueryParamKey()
698
+				)
699
+			);
700
+		}
701
+		// so it's not for a field, is it a logic query param key?
702
+		if ($this->isLogicQueryParam()) {
703
+			return ModelDataTranslator::prepareConditionsQueryParamsForModels(
704
+				$this->getQueryParamValue(),
705
+				$this->getContext()->getModel(),
706
+				$this->getContext()->getRequestedVersion()
707
+			);
708
+		}
709
+		if (EED_Core_Rest_Api::debugMode()) {
710
+			// only tell API clients they got it wrong if we're in debug mode
711
+			// otherwise try our best ot fulfill their request by ignoring this invalid data
712
+			throw new RestException(
713
+				'invalid_parameter',
714
+				sprintf(
715
+					/* translators: 1: variable name */
716
+					esc_html__(
717
+						'You provided an invalid parameter, with key "%1$s"',
718
+						'event_espresso'
719
+					),
720
+					$this->getQueryParamKey()
721
+				),
722
+				array(
723
+					'status' => 400,
724
+				)
725
+			);
726
+		}
727
+		return null;
728
+	}
729 729
 }
730 730
 // End of file RestQueryParamMetadata.php
731 731
 // Location: EventEspresso\core\libraries\rest_api/RestQueryParamMetadata.php
Please login to merge, or discard this patch.
core/libraries/rest_api/controllers/model/Write.php 1 patch
Indentation   +546 added lines, -546 removed lines patch added patch discarded remove patch
@@ -39,573 +39,573 @@
 block discarded – undo
39 39
 {
40 40
 
41 41
 
42
-    public function __construct()
43
-    {
44
-        parent::__construct();
45
-        EE_Registry::instance()->load_helper('Inflector');
46
-    }
42
+	public function __construct()
43
+	{
44
+		parent::__construct();
45
+		EE_Registry::instance()->load_helper('Inflector');
46
+	}
47 47
 
48 48
 
49
-    /**
50
-     * Handles requests to get all (or a filtered subset) of entities for a particular model
51
-     *
52
-     * @param WP_REST_Request $request
53
-     * @param string          $version
54
-     * @param string          $model_name
55
-     * @return WP_REST_Response|\WP_Error
56
-     */
57
-    public static function handleRequestInsert(WP_REST_Request $request, $version, $model_name)
58
-    {
59
-        $controller = new Write();
60
-        try {
61
-            $controller->setRequestedVersion($version);
62
-            return $controller->sendResponse(
63
-                $controller->insert(
64
-                    $controller->getModelVersionInfo()->loadModel($model_name),
65
-                    $request
66
-                )
67
-            );
68
-        } catch (Exception $e) {
69
-            return $controller->sendResponse($e);
70
-        }
71
-    }
49
+	/**
50
+	 * Handles requests to get all (or a filtered subset) of entities for a particular model
51
+	 *
52
+	 * @param WP_REST_Request $request
53
+	 * @param string          $version
54
+	 * @param string          $model_name
55
+	 * @return WP_REST_Response|\WP_Error
56
+	 */
57
+	public static function handleRequestInsert(WP_REST_Request $request, $version, $model_name)
58
+	{
59
+		$controller = new Write();
60
+		try {
61
+			$controller->setRequestedVersion($version);
62
+			return $controller->sendResponse(
63
+				$controller->insert(
64
+					$controller->getModelVersionInfo()->loadModel($model_name),
65
+					$request
66
+				)
67
+			);
68
+		} catch (Exception $e) {
69
+			return $controller->sendResponse($e);
70
+		}
71
+	}
72 72
 
73 73
 
74
-    /**
75
-     * Handles a request from \WP_REST_Server to update an EE model
76
-     *
77
-     * @param WP_REST_Request $request
78
-     * @param string          $version
79
-     * @param string          $model_name
80
-     * @return WP_REST_Response|\WP_Error
81
-     */
82
-    public static function handleRequestUpdate(WP_REST_Request $request, $version, $model_name)
83
-    {
84
-        $controller = new Write();
85
-        try {
86
-            $controller->setRequestedVersion($version);
87
-            return $controller->sendResponse(
88
-                $controller->update(
89
-                    $controller->getModelVersionInfo()->loadModel($model_name),
90
-                    $request
91
-                )
92
-            );
93
-        } catch (Exception $e) {
94
-            return $controller->sendResponse($e);
95
-        }
96
-    }
74
+	/**
75
+	 * Handles a request from \WP_REST_Server to update an EE model
76
+	 *
77
+	 * @param WP_REST_Request $request
78
+	 * @param string          $version
79
+	 * @param string          $model_name
80
+	 * @return WP_REST_Response|\WP_Error
81
+	 */
82
+	public static function handleRequestUpdate(WP_REST_Request $request, $version, $model_name)
83
+	{
84
+		$controller = new Write();
85
+		try {
86
+			$controller->setRequestedVersion($version);
87
+			return $controller->sendResponse(
88
+				$controller->update(
89
+					$controller->getModelVersionInfo()->loadModel($model_name),
90
+					$request
91
+				)
92
+			);
93
+		} catch (Exception $e) {
94
+			return $controller->sendResponse($e);
95
+		}
96
+	}
97 97
 
98 98
 
99
-    /**
100
-     * Deletes a single model object and returns it. Unless
101
-     *
102
-     * @param WP_REST_Request $request
103
-     * @param string          $version
104
-     * @param string          $model_name
105
-     * @return WP_REST_Response|\WP_Error
106
-     */
107
-    public static function handleRequestDelete(WP_REST_Request $request, $version, $model_name)
108
-    {
109
-        $controller = new Write();
110
-        try {
111
-            $controller->setRequestedVersion($version);
112
-            return $controller->sendResponse(
113
-                $controller->delete(
114
-                    $controller->getModelVersionInfo()->loadModel($model_name),
115
-                    $request
116
-                )
117
-            );
118
-        } catch (Exception $e) {
119
-            return $controller->sendResponse($e);
120
-        }
121
-    }
99
+	/**
100
+	 * Deletes a single model object and returns it. Unless
101
+	 *
102
+	 * @param WP_REST_Request $request
103
+	 * @param string          $version
104
+	 * @param string          $model_name
105
+	 * @return WP_REST_Response|\WP_Error
106
+	 */
107
+	public static function handleRequestDelete(WP_REST_Request $request, $version, $model_name)
108
+	{
109
+		$controller = new Write();
110
+		try {
111
+			$controller->setRequestedVersion($version);
112
+			return $controller->sendResponse(
113
+				$controller->delete(
114
+					$controller->getModelVersionInfo()->loadModel($model_name),
115
+					$request
116
+				)
117
+			);
118
+		} catch (Exception $e) {
119
+			return $controller->sendResponse($e);
120
+		}
121
+	}
122 122
 
123 123
 
124
-    /**
125
-     * Inserts a new model object according to the $request
126
-     *
127
-     * @param EEM_Base        $model
128
-     * @param WP_REST_Request $request
129
-     * @return array
130
-     * @throws EE_Error
131
-     * @throws RestException
132
-     */
133
-    public function insert(EEM_Base $model, WP_REST_Request $request)
134
-    {
135
-        Capabilities::verifyAtLeastPartialAccessTo($model, EEM_Base::caps_edit, 'create');
136
-        $default_cap_to_check_for = EE_Restriction_Generator_Base::get_default_restrictions_cap();
137
-        if (! current_user_can($default_cap_to_check_for)) {
138
-            throw new RestException(
139
-                'rest_cannot_create_' . EEH_Inflector::pluralize_and_lower(($model->get_this_model_name())),
140
-                sprintf(
141
-                    esc_html__(
142
-                    // @codingStandardsIgnoreStart
143
-                        'For now, only those with the admin capability to "%1$s" are allowed to use the REST API to insert data into Event Espresso.',
144
-                        // @codingStandardsIgnoreEnd
145
-                        'event_espresso'
146
-                    ),
147
-                    $default_cap_to_check_for
148
-                ),
149
-                array('status' => 403)
150
-            );
151
-        }
152
-        $submitted_json_data = array_merge((array) $request->get_body_params(), (array) $request->get_json_params());
153
-        $model_data = ModelDataTranslator::prepareConditionsQueryParamsForModels(
154
-            $submitted_json_data,
155
-            $model,
156
-            $this->getModelVersionInfo()->requestedVersion(),
157
-            true
158
-        );
159
-        $model_obj = EE_Registry::instance()->load_class(
160
-            $model->get_this_model_name(),
161
-            array($model_data, $model->get_timezone()),
162
-            false,
163
-            false
164
-        );
165
-        $model_obj->save();
166
-        $new_id = $model_obj->ID();
167
-        if (! $new_id) {
168
-            throw new RestException(
169
-                'rest_insertion_failed',
170
-                sprintf(__('Could not insert new %1$s', 'event_espresso'), $model->get_this_model_name())
171
-            );
172
-        }
173
-        return $this->returnModelObjAsJsonResponse($model_obj, $request);
174
-    }
124
+	/**
125
+	 * Inserts a new model object according to the $request
126
+	 *
127
+	 * @param EEM_Base        $model
128
+	 * @param WP_REST_Request $request
129
+	 * @return array
130
+	 * @throws EE_Error
131
+	 * @throws RestException
132
+	 */
133
+	public function insert(EEM_Base $model, WP_REST_Request $request)
134
+	{
135
+		Capabilities::verifyAtLeastPartialAccessTo($model, EEM_Base::caps_edit, 'create');
136
+		$default_cap_to_check_for = EE_Restriction_Generator_Base::get_default_restrictions_cap();
137
+		if (! current_user_can($default_cap_to_check_for)) {
138
+			throw new RestException(
139
+				'rest_cannot_create_' . EEH_Inflector::pluralize_and_lower(($model->get_this_model_name())),
140
+				sprintf(
141
+					esc_html__(
142
+					// @codingStandardsIgnoreStart
143
+						'For now, only those with the admin capability to "%1$s" are allowed to use the REST API to insert data into Event Espresso.',
144
+						// @codingStandardsIgnoreEnd
145
+						'event_espresso'
146
+					),
147
+					$default_cap_to_check_for
148
+				),
149
+				array('status' => 403)
150
+			);
151
+		}
152
+		$submitted_json_data = array_merge((array) $request->get_body_params(), (array) $request->get_json_params());
153
+		$model_data = ModelDataTranslator::prepareConditionsQueryParamsForModels(
154
+			$submitted_json_data,
155
+			$model,
156
+			$this->getModelVersionInfo()->requestedVersion(),
157
+			true
158
+		);
159
+		$model_obj = EE_Registry::instance()->load_class(
160
+			$model->get_this_model_name(),
161
+			array($model_data, $model->get_timezone()),
162
+			false,
163
+			false
164
+		);
165
+		$model_obj->save();
166
+		$new_id = $model_obj->ID();
167
+		if (! $new_id) {
168
+			throw new RestException(
169
+				'rest_insertion_failed',
170
+				sprintf(__('Could not insert new %1$s', 'event_espresso'), $model->get_this_model_name())
171
+			);
172
+		}
173
+		return $this->returnModelObjAsJsonResponse($model_obj, $request);
174
+	}
175 175
 
176 176
 
177
-    /**
178
-     * Updates an existing model object according to the $request
179
-     *
180
-     * @param EEM_Base        $model
181
-     * @param WP_REST_Request $request
182
-     * @return array
183
-     * @throws EE_Error
184
-     */
185
-    public function update(EEM_Base $model, WP_REST_Request $request)
186
-    {
187
-        Capabilities::verifyAtLeastPartialAccessTo($model, EEM_Base::caps_edit, 'edit');
188
-        $default_cap_to_check_for = EE_Restriction_Generator_Base::get_default_restrictions_cap();
189
-        if (! current_user_can($default_cap_to_check_for)) {
190
-            throw new RestException(
191
-                'rest_cannot_edit_' . EEH_Inflector::pluralize_and_lower(($model->get_this_model_name())),
192
-                sprintf(
193
-                    esc_html__(
194
-                    // @codingStandardsIgnoreStart
195
-                        'For now, only those with the admin capability to "%1$s" are allowed to use the REST API to update data into Event Espresso.',
196
-                        // @codingStandardsIgnoreEnd
197
-                        'event_espresso'
198
-                    ),
199
-                    $default_cap_to_check_for
200
-                ),
201
-                array('status' => 403)
202
-            );
203
-        }
204
-        $obj_id = $request->get_param('id');
205
-        if (! $obj_id) {
206
-            throw new RestException(
207
-                'rest_edit_failed',
208
-                sprintf(__('Could not edit %1$s', 'event_espresso'), $model->get_this_model_name())
209
-            );
210
-        }
211
-        $model_data = ModelDataTranslator::prepareConditionsQueryParamsForModels(
212
-            $this->getBodyParams($request),
213
-            $model,
214
-            $this->getModelVersionInfo()->requestedVersion(),
215
-            true
216
-        );
217
-        $model_obj = $model->get_one_by_ID($obj_id);
218
-        if (! $model_obj instanceof EE_Base_Class) {
219
-            $lowercase_model_name = strtolower($model->get_this_model_name());
220
-            throw new RestException(
221
-                sprintf('rest_%s_invalid_id', $lowercase_model_name),
222
-                sprintf(__('Invalid %s ID.', 'event_espresso'), $lowercase_model_name),
223
-                array('status' => 404)
224
-            );
225
-        }
226
-        $model_obj->save($model_data);
227
-        return $this->returnModelObjAsJsonResponse($model_obj, $request);
228
-    }
177
+	/**
178
+	 * Updates an existing model object according to the $request
179
+	 *
180
+	 * @param EEM_Base        $model
181
+	 * @param WP_REST_Request $request
182
+	 * @return array
183
+	 * @throws EE_Error
184
+	 */
185
+	public function update(EEM_Base $model, WP_REST_Request $request)
186
+	{
187
+		Capabilities::verifyAtLeastPartialAccessTo($model, EEM_Base::caps_edit, 'edit');
188
+		$default_cap_to_check_for = EE_Restriction_Generator_Base::get_default_restrictions_cap();
189
+		if (! current_user_can($default_cap_to_check_for)) {
190
+			throw new RestException(
191
+				'rest_cannot_edit_' . EEH_Inflector::pluralize_and_lower(($model->get_this_model_name())),
192
+				sprintf(
193
+					esc_html__(
194
+					// @codingStandardsIgnoreStart
195
+						'For now, only those with the admin capability to "%1$s" are allowed to use the REST API to update data into Event Espresso.',
196
+						// @codingStandardsIgnoreEnd
197
+						'event_espresso'
198
+					),
199
+					$default_cap_to_check_for
200
+				),
201
+				array('status' => 403)
202
+			);
203
+		}
204
+		$obj_id = $request->get_param('id');
205
+		if (! $obj_id) {
206
+			throw new RestException(
207
+				'rest_edit_failed',
208
+				sprintf(__('Could not edit %1$s', 'event_espresso'), $model->get_this_model_name())
209
+			);
210
+		}
211
+		$model_data = ModelDataTranslator::prepareConditionsQueryParamsForModels(
212
+			$this->getBodyParams($request),
213
+			$model,
214
+			$this->getModelVersionInfo()->requestedVersion(),
215
+			true
216
+		);
217
+		$model_obj = $model->get_one_by_ID($obj_id);
218
+		if (! $model_obj instanceof EE_Base_Class) {
219
+			$lowercase_model_name = strtolower($model->get_this_model_name());
220
+			throw new RestException(
221
+				sprintf('rest_%s_invalid_id', $lowercase_model_name),
222
+				sprintf(__('Invalid %s ID.', 'event_espresso'), $lowercase_model_name),
223
+				array('status' => 404)
224
+			);
225
+		}
226
+		$model_obj->save($model_data);
227
+		return $this->returnModelObjAsJsonResponse($model_obj, $request);
228
+	}
229 229
 
230 230
 
231
-    /**
232
-     * Updates an existing model object according to the $request
233
-     *
234
-     * @param EEM_Base        $model
235
-     * @param WP_REST_Request $request
236
-     * @return array of either the soft-deleted item, or
237
-     * @throws EE_Error
238
-     */
239
-    public function delete(EEM_Base $model, WP_REST_Request $request)
240
-    {
241
-        Capabilities::verifyAtLeastPartialAccessTo($model, EEM_Base::caps_delete, 'delete');
242
-        $default_cap_to_check_for = EE_Restriction_Generator_Base::get_default_restrictions_cap();
243
-        if (! current_user_can($default_cap_to_check_for)) {
244
-            throw new RestException(
245
-                'rest_cannot_delete_' . EEH_Inflector::pluralize_and_lower(($model->get_this_model_name())),
246
-                sprintf(
247
-                    esc_html__(
248
-                    // @codingStandardsIgnoreStart
249
-                        'For now, only those with the admin capability to "%1$s" are allowed to use the REST API to delete data into Event Espresso.',
250
-                        // @codingStandardsIgnoreEnd
251
-                        'event_espresso'
252
-                    ),
253
-                    $default_cap_to_check_for
254
-                ),
255
-                array('status' => 403)
256
-            );
257
-        }
258
-        $obj_id = $request->get_param('id');
259
-        // this is where we would apply more fine-grained caps
260
-        $model_obj = $model->get_one_by_ID($obj_id);
261
-        if (! $model_obj instanceof EE_Base_Class) {
262
-            $lowercase_model_name = strtolower($model->get_this_model_name());
263
-            throw new RestException(
264
-                sprintf('rest_%s_invalid_id', $lowercase_model_name),
265
-                sprintf(__('Invalid %s ID.', 'event_espresso'), $lowercase_model_name),
266
-                array('status' => 404)
267
-            );
268
-        }
269
-        $requested_permanent_delete = filter_var($request->get_param('force'), FILTER_VALIDATE_BOOLEAN);
270
-        $requested_allow_blocking = filter_var($request->get_param('allow_blocking'), FILTER_VALIDATE_BOOLEAN);
271
-        if ($requested_permanent_delete) {
272
-            $previous = $this->returnModelObjAsJsonResponse($model_obj, $request);
273
-            $deleted = (bool) $model->delete_permanently_by_ID($obj_id, $requested_allow_blocking);
274
-            return array(
275
-                'deleted'  => $deleted,
276
-                'previous' => $previous,
277
-            );
278
-        } else {
279
-            if ($model instanceof EEM_Soft_Delete_Base) {
280
-                $model->delete_by_ID($obj_id, $requested_allow_blocking);
281
-                return $this->returnModelObjAsJsonResponse($model_obj, $request);
282
-            } else {
283
-                throw new RestException(
284
-                    'rest_trash_not_supported',
285
-                    501,
286
-                    sprintf(
287
-                        esc_html__('%1$s do not support trashing. Set force=1 to delete.', 'event_espresso'),
288
-                        EEH_Inflector::pluralize($model->get_this_model_name())
289
-                    )
290
-                );
291
-            }
292
-        }
293
-    }
231
+	/**
232
+	 * Updates an existing model object according to the $request
233
+	 *
234
+	 * @param EEM_Base        $model
235
+	 * @param WP_REST_Request $request
236
+	 * @return array of either the soft-deleted item, or
237
+	 * @throws EE_Error
238
+	 */
239
+	public function delete(EEM_Base $model, WP_REST_Request $request)
240
+	{
241
+		Capabilities::verifyAtLeastPartialAccessTo($model, EEM_Base::caps_delete, 'delete');
242
+		$default_cap_to_check_for = EE_Restriction_Generator_Base::get_default_restrictions_cap();
243
+		if (! current_user_can($default_cap_to_check_for)) {
244
+			throw new RestException(
245
+				'rest_cannot_delete_' . EEH_Inflector::pluralize_and_lower(($model->get_this_model_name())),
246
+				sprintf(
247
+					esc_html__(
248
+					// @codingStandardsIgnoreStart
249
+						'For now, only those with the admin capability to "%1$s" are allowed to use the REST API to delete data into Event Espresso.',
250
+						// @codingStandardsIgnoreEnd
251
+						'event_espresso'
252
+					),
253
+					$default_cap_to_check_for
254
+				),
255
+				array('status' => 403)
256
+			);
257
+		}
258
+		$obj_id = $request->get_param('id');
259
+		// this is where we would apply more fine-grained caps
260
+		$model_obj = $model->get_one_by_ID($obj_id);
261
+		if (! $model_obj instanceof EE_Base_Class) {
262
+			$lowercase_model_name = strtolower($model->get_this_model_name());
263
+			throw new RestException(
264
+				sprintf('rest_%s_invalid_id', $lowercase_model_name),
265
+				sprintf(__('Invalid %s ID.', 'event_espresso'), $lowercase_model_name),
266
+				array('status' => 404)
267
+			);
268
+		}
269
+		$requested_permanent_delete = filter_var($request->get_param('force'), FILTER_VALIDATE_BOOLEAN);
270
+		$requested_allow_blocking = filter_var($request->get_param('allow_blocking'), FILTER_VALIDATE_BOOLEAN);
271
+		if ($requested_permanent_delete) {
272
+			$previous = $this->returnModelObjAsJsonResponse($model_obj, $request);
273
+			$deleted = (bool) $model->delete_permanently_by_ID($obj_id, $requested_allow_blocking);
274
+			return array(
275
+				'deleted'  => $deleted,
276
+				'previous' => $previous,
277
+			);
278
+		} else {
279
+			if ($model instanceof EEM_Soft_Delete_Base) {
280
+				$model->delete_by_ID($obj_id, $requested_allow_blocking);
281
+				return $this->returnModelObjAsJsonResponse($model_obj, $request);
282
+			} else {
283
+				throw new RestException(
284
+					'rest_trash_not_supported',
285
+					501,
286
+					sprintf(
287
+						esc_html__('%1$s do not support trashing. Set force=1 to delete.', 'event_espresso'),
288
+						EEH_Inflector::pluralize($model->get_this_model_name())
289
+					)
290
+				);
291
+			}
292
+		}
293
+	}
294 294
 
295 295
 
296
-    /**
297
-     * Returns an array ready to be converted into a JSON response, based solely on the model object
298
-     *
299
-     * @param EE_Base_Class   $model_obj
300
-     * @param WP_REST_Request $request
301
-     * @return array ready for a response
302
-     */
303
-    protected function returnModelObjAsJsonResponse(EE_Base_Class $model_obj, WP_REST_Request $request)
304
-    {
305
-        $model = $model_obj->get_model();
306
-        // create an array exactly like the wpdb results row,
307
-        // so we can pass it to controllers/model/Read::create_entity_from_wpdb_result()
308
-        $simulated_db_row = array();
309
-        foreach ($model->field_settings(true) as $field_name => $field_obj) {
310
-            // we need to reconstruct the normal wpdb results, including the db-only fields
311
-            // like a secondary table's primary key. The models expect those (but don't care what value they have)
312
-            if ($field_obj instanceof EE_DB_Only_Field_Base) {
313
-                $raw_value = true;
314
-            } elseif ($field_obj instanceof EE_Datetime_Field) {
315
-                $raw_value = $model_obj->get_DateTime_object($field_name);
316
-            } else {
317
-                $raw_value = $model_obj->get_raw($field_name);
318
-            }
319
-            $simulated_db_row[ $field_obj->get_qualified_column() ] = $field_obj->prepare_for_use_in_db($raw_value);
320
-        }
321
-        $read_controller = LoaderFactory::getLoader()->getNew('EventEspresso\core\libraries\rest_api\controllers\model\Read');
322
-        $read_controller->setRequestedVersion($this->getRequestedVersion());
323
-        // the simulates request really doesn't need any info downstream
324
-        $simulated_request = new WP_REST_Request('GET');
325
-        // set the caps context on the simulated according to the original request.
326
-        switch ($request->get_method()) {
327
-            case 'POST':
328
-            case 'PUT':
329
-                $caps_context = EEM_Base::caps_edit;
330
-                break;
331
-            case 'DELETE':
332
-                $caps_context = EEM_Base::caps_delete;
333
-                break;
334
-            default:
335
-                $caps_context = EEM_Base::caps_read_admin;
336
-        }
337
-        $simulated_request->set_param('caps', $caps_context);
338
-        return $read_controller->createEntityFromWpdbResult(
339
-            $model_obj->get_model(),
340
-            $simulated_db_row,
341
-            $simulated_request
342
-        );
343
-    }
296
+	/**
297
+	 * Returns an array ready to be converted into a JSON response, based solely on the model object
298
+	 *
299
+	 * @param EE_Base_Class   $model_obj
300
+	 * @param WP_REST_Request $request
301
+	 * @return array ready for a response
302
+	 */
303
+	protected function returnModelObjAsJsonResponse(EE_Base_Class $model_obj, WP_REST_Request $request)
304
+	{
305
+		$model = $model_obj->get_model();
306
+		// create an array exactly like the wpdb results row,
307
+		// so we can pass it to controllers/model/Read::create_entity_from_wpdb_result()
308
+		$simulated_db_row = array();
309
+		foreach ($model->field_settings(true) as $field_name => $field_obj) {
310
+			// we need to reconstruct the normal wpdb results, including the db-only fields
311
+			// like a secondary table's primary key. The models expect those (but don't care what value they have)
312
+			if ($field_obj instanceof EE_DB_Only_Field_Base) {
313
+				$raw_value = true;
314
+			} elseif ($field_obj instanceof EE_Datetime_Field) {
315
+				$raw_value = $model_obj->get_DateTime_object($field_name);
316
+			} else {
317
+				$raw_value = $model_obj->get_raw($field_name);
318
+			}
319
+			$simulated_db_row[ $field_obj->get_qualified_column() ] = $field_obj->prepare_for_use_in_db($raw_value);
320
+		}
321
+		$read_controller = LoaderFactory::getLoader()->getNew('EventEspresso\core\libraries\rest_api\controllers\model\Read');
322
+		$read_controller->setRequestedVersion($this->getRequestedVersion());
323
+		// the simulates request really doesn't need any info downstream
324
+		$simulated_request = new WP_REST_Request('GET');
325
+		// set the caps context on the simulated according to the original request.
326
+		switch ($request->get_method()) {
327
+			case 'POST':
328
+			case 'PUT':
329
+				$caps_context = EEM_Base::caps_edit;
330
+				break;
331
+			case 'DELETE':
332
+				$caps_context = EEM_Base::caps_delete;
333
+				break;
334
+			default:
335
+				$caps_context = EEM_Base::caps_read_admin;
336
+		}
337
+		$simulated_request->set_param('caps', $caps_context);
338
+		return $read_controller->createEntityFromWpdbResult(
339
+			$model_obj->get_model(),
340
+			$simulated_db_row,
341
+			$simulated_request
342
+		);
343
+	}
344 344
 
345 345
 
346
-    /**
347
-     * Gets the item affected by this request
348
-     *
349
-     * @param EEM_Base        $model
350
-     * @param WP_REST_Request $request
351
-     * @param  int|string     $obj_id
352
-     * @return \WP_Error|array
353
-     */
354
-    protected function getOneBasedOnRequest(EEM_Base $model, WP_REST_Request $request, $obj_id)
355
-    {
356
-        $requested_version = $this->getRequestedVersion($request->get_route());
357
-        $get_request = new WP_REST_Request(
358
-            'GET',
359
-            EED_Core_Rest_Api::ee_api_namespace
360
-            . $requested_version
361
-            . '/'
362
-            . EEH_Inflector::pluralize_and_lower($model->get_this_model_name())
363
-            . '/'
364
-            . $obj_id
365
-        );
366
-        $get_request->set_url_params(
367
-            array(
368
-                'id'      => $obj_id,
369
-                'include' => $request->get_param('include'),
370
-            )
371
-        );
372
-        $read_controller = new Read();
373
-        $read_controller->setRequestedVersion($this->getRequestedVersion());
374
-        return $read_controller->getEntityFromModel($model, $get_request);
375
-    }
346
+	/**
347
+	 * Gets the item affected by this request
348
+	 *
349
+	 * @param EEM_Base        $model
350
+	 * @param WP_REST_Request $request
351
+	 * @param  int|string     $obj_id
352
+	 * @return \WP_Error|array
353
+	 */
354
+	protected function getOneBasedOnRequest(EEM_Base $model, WP_REST_Request $request, $obj_id)
355
+	{
356
+		$requested_version = $this->getRequestedVersion($request->get_route());
357
+		$get_request = new WP_REST_Request(
358
+			'GET',
359
+			EED_Core_Rest_Api::ee_api_namespace
360
+			. $requested_version
361
+			. '/'
362
+			. EEH_Inflector::pluralize_and_lower($model->get_this_model_name())
363
+			. '/'
364
+			. $obj_id
365
+		);
366
+		$get_request->set_url_params(
367
+			array(
368
+				'id'      => $obj_id,
369
+				'include' => $request->get_param('include'),
370
+			)
371
+		);
372
+		$read_controller = new Read();
373
+		$read_controller->setRequestedVersion($this->getRequestedVersion());
374
+		return $read_controller->getEntityFromModel($model, $get_request);
375
+	}
376 376
 
377
-    /**
378
-     * Adds a relation between the specified models (if it doesn't already exist.)
379
-     * @since $VID:$
380
-     * @param WP_REST_Request $request
381
-     * @return WP_REST_Response
382
-     */
383
-    public static function handleRequestAddRelation(WP_REST_Request $request, $version, $model_name, $related_model_name)
384
-    {
385
-        $controller = new Write();
386
-        try {
387
-            $controller->setRequestedVersion($version);
388
-            $main_model = $controller->validateModel($model_name);
389
-            $controller->validateModel($related_model_name);
390
-            return $controller->sendResponse(
391
-                $controller->addRelation(
392
-                    $main_model,
393
-                    $main_model->related_settings_for($related_model_name),
394
-                    $request
395
-                )
396
-            );
397
-        } catch (Exception $e) {
398
-            return $controller->sendResponse($e);
399
-        }
400
-    }
377
+	/**
378
+	 * Adds a relation between the specified models (if it doesn't already exist.)
379
+	 * @since $VID:$
380
+	 * @param WP_REST_Request $request
381
+	 * @return WP_REST_Response
382
+	 */
383
+	public static function handleRequestAddRelation(WP_REST_Request $request, $version, $model_name, $related_model_name)
384
+	{
385
+		$controller = new Write();
386
+		try {
387
+			$controller->setRequestedVersion($version);
388
+			$main_model = $controller->validateModel($model_name);
389
+			$controller->validateModel($related_model_name);
390
+			return $controller->sendResponse(
391
+				$controller->addRelation(
392
+					$main_model,
393
+					$main_model->related_settings_for($related_model_name),
394
+					$request
395
+				)
396
+			);
397
+		} catch (Exception $e) {
398
+			return $controller->sendResponse($e);
399
+		}
400
+	}
401 401
 
402
-    /**
403
-     * Adds a relation between the two model specified model objects.
404
-     * @since $VID:$
405
-     * @param EEM_Base $model
406
-     * @param EE_Model_Relation_Base $relation
407
-     * @param WP_REST_Request $request
408
-     * @return array
409
-     * @throws EE_Error
410
-     * @throws InvalidArgumentException
411
-     * @throws InvalidDataTypeException
412
-     * @throws InvalidInterfaceException
413
-     * @throws RestException
414
-     * @throws DomainException
415
-     */
416
-    public function addRelation(EEM_Base $model, EE_Model_Relation_Base $relation, WP_REST_Request $request)
417
-    {
418
-        list($model_obj, $other_obj) = $this->getBothModelObjects($model, $relation, $request);
419
-        $extra_params = array();
420
-        if ($relation instanceof EE_HABTM_Relation) {
421
-            $extra_params = array_intersect_key(
422
-                ModelDataTranslator::prepareConditionsQueryParamsForModels(
423
-                    $request->get_body_params(),
424
-                    $relation->get_join_model(),
425
-                    $this->getModelVersionInfo()->requestedVersion(),
426
-                    true
427
-                ),
428
-                $relation->getNonKeyFields()
429
-            );
430
-        }
431
-        // Add a relation.
432
-        $related_obj = $model_obj->_add_relation_to(
433
-            $other_obj,
434
-            $relation->get_other_model()->get_this_model_name(),
435
-            $extra_params
436
-        );
437
-        $response = array(
438
-            strtolower($model->get_this_model_name()) => $this->returnModelObjAsJsonResponse($model_obj, $request),
439
-            strtolower($relation->get_other_model()->get_this_model_name()) => $this->returnModelObjAsJsonResponse($related_obj, $request),
440
-        );
441
-        if ($relation instanceof EE_HABTM_Relation) {
442
-            $join_model_obj = $relation->get_join_model()->get_one(
443
-                array(
444
-                    array(
445
-                        $model->primary_key_name() => $model_obj->ID(),
446
-                        $relation->get_other_model()->primary_key_name() => $related_obj->ID()
447
-                    )
448
-                )
449
-            );
450
-            $response['join'][ strtolower($relation->get_join_model()->get_this_model_name()) ] = $this->returnModelObjAsJsonResponse($join_model_obj, $request);
451
-        }
452
-        return $response;
453
-    }
402
+	/**
403
+	 * Adds a relation between the two model specified model objects.
404
+	 * @since $VID:$
405
+	 * @param EEM_Base $model
406
+	 * @param EE_Model_Relation_Base $relation
407
+	 * @param WP_REST_Request $request
408
+	 * @return array
409
+	 * @throws EE_Error
410
+	 * @throws InvalidArgumentException
411
+	 * @throws InvalidDataTypeException
412
+	 * @throws InvalidInterfaceException
413
+	 * @throws RestException
414
+	 * @throws DomainException
415
+	 */
416
+	public function addRelation(EEM_Base $model, EE_Model_Relation_Base $relation, WP_REST_Request $request)
417
+	{
418
+		list($model_obj, $other_obj) = $this->getBothModelObjects($model, $relation, $request);
419
+		$extra_params = array();
420
+		if ($relation instanceof EE_HABTM_Relation) {
421
+			$extra_params = array_intersect_key(
422
+				ModelDataTranslator::prepareConditionsQueryParamsForModels(
423
+					$request->get_body_params(),
424
+					$relation->get_join_model(),
425
+					$this->getModelVersionInfo()->requestedVersion(),
426
+					true
427
+				),
428
+				$relation->getNonKeyFields()
429
+			);
430
+		}
431
+		// Add a relation.
432
+		$related_obj = $model_obj->_add_relation_to(
433
+			$other_obj,
434
+			$relation->get_other_model()->get_this_model_name(),
435
+			$extra_params
436
+		);
437
+		$response = array(
438
+			strtolower($model->get_this_model_name()) => $this->returnModelObjAsJsonResponse($model_obj, $request),
439
+			strtolower($relation->get_other_model()->get_this_model_name()) => $this->returnModelObjAsJsonResponse($related_obj, $request),
440
+		);
441
+		if ($relation instanceof EE_HABTM_Relation) {
442
+			$join_model_obj = $relation->get_join_model()->get_one(
443
+				array(
444
+					array(
445
+						$model->primary_key_name() => $model_obj->ID(),
446
+						$relation->get_other_model()->primary_key_name() => $related_obj->ID()
447
+					)
448
+				)
449
+			);
450
+			$response['join'][ strtolower($relation->get_join_model()->get_this_model_name()) ] = $this->returnModelObjAsJsonResponse($join_model_obj, $request);
451
+		}
452
+		return $response;
453
+	}
454 454
 
455 455
 
456
-    /**
457
-     * Removes the relation between the specified models (if it exists).
458
-     * @since $VID:$
459
-     * @param WP_REST_Request $request
460
-     * @return WP_REST_Response
461
-     */
462
-    public static function handleRequestRemoveRelation(WP_REST_Request $request, $version, $model_name, $related_model_name)
463
-    {
464
-        $controller = new Write();
465
-        try {
466
-            $controller->setRequestedVersion($version);
467
-            $main_model = $controller->getModelVersionInfo()->loadModel($model_name);
468
-            return $controller->sendResponse(
469
-                $controller->removeRelation(
470
-                    $main_model,
471
-                    $main_model->related_settings_for($related_model_name),
472
-                    $request
473
-                )
474
-            );
475
-        } catch (Exception $e) {
476
-            return $controller->sendResponse($e);
477
-        }
478
-    }
456
+	/**
457
+	 * Removes the relation between the specified models (if it exists).
458
+	 * @since $VID:$
459
+	 * @param WP_REST_Request $request
460
+	 * @return WP_REST_Response
461
+	 */
462
+	public static function handleRequestRemoveRelation(WP_REST_Request $request, $version, $model_name, $related_model_name)
463
+	{
464
+		$controller = new Write();
465
+		try {
466
+			$controller->setRequestedVersion($version);
467
+			$main_model = $controller->getModelVersionInfo()->loadModel($model_name);
468
+			return $controller->sendResponse(
469
+				$controller->removeRelation(
470
+					$main_model,
471
+					$main_model->related_settings_for($related_model_name),
472
+					$request
473
+				)
474
+			);
475
+		} catch (Exception $e) {
476
+			return $controller->sendResponse($e);
477
+		}
478
+	}
479 479
 
480
-    /**
481
-     * Adds a relation between the two model specified model objects.
482
-     * @since $VID:$
483
-     * @param EEM_Base $model
484
-     * @param EE_Model_Relation_Base $relation
485
-     * @param WP_REST_Request $request
486
-     * @return array
487
-     * @throws DomainException
488
-     * @throws EE_Error
489
-     * @throws InvalidArgumentException
490
-     * @throws InvalidDataTypeException
491
-     * @throws InvalidInterfaceException
492
-     * @throws RestException
493
-     */
494
-    public function removeRelation(EEM_Base $model, EE_Model_Relation_Base $relation, WP_REST_Request $request)
495
-    {
496
-        // This endpoint doesn't accept body parameters (it's understandable to think it might, so let developers know
497
-        // up-front that it doesn't.)
498
-        if (!empty($request->get_body_params())) {
499
-            $body_params = $request->get_body_params();
500
-            throw new RestException(
501
-                'invalid_field',
502
-                sprintf(
503
-                    esc_html__('This endpoint doesn\'t accept post body arguments, you sent in %1$s', 'event_espresso'),
504
-                    implode(array_keys($body_params))
505
-                )
506
-            );
507
-        }
508
-        list($model_obj, $other_obj) = $this->getBothModelObjects($model, $relation, $request);
509
-        // Remember the old relation, if it used a join entry.
510
-        $join_model_obj = null;
511
-        if ($relation instanceof EE_HABTM_Relation) {
512
-            $join_model_obj = $relation->get_join_model()->get_one(
513
-                array(
514
-                    array(
515
-                        $model->primary_key_name() => $model_obj->ID(),
516
-                        $relation->get_other_model()->primary_key_name() => $other_obj->ID()
517
-                    )
518
-                )
519
-            );
520
-        }
521
-        // Remove the relation.
522
-        $related_obj = $model_obj->_remove_relation_to(
523
-            $other_obj,
524
-            $relation->get_other_model()->get_this_model_name()
525
-        );
526
-        $response = array(
527
-            strtolower($model->get_this_model_name()) => $this->returnModelObjAsJsonResponse($model_obj, $request),
528
-            strtolower($relation->get_other_model()->get_this_model_name()) => $this->returnModelObjAsJsonResponse($related_obj, $request),
529
-        );
530
-        if ($relation instanceof EE_HABTM_Relation) {
531
-            $join_model_obj_after_removal = $relation->get_join_model()->get_one(
532
-                array(
533
-                    array(
534
-                        $model->primary_key_name() => $model_obj->ID(),
535
-                        $relation->get_other_model()->primary_key_name() => $other_obj->ID()
536
-                    )
537
-                )
538
-            );
539
-            if ($join_model_obj instanceof EE_Base_Class) {
540
-                $response['join'][ strtolower($relation->get_join_model()->get_this_model_name()) ] = $this->returnModelObjAsJsonResponse($join_model_obj, $request);
541
-            } else {
542
-                $response['join'][ strtolower($relation->get_join_model()->get_this_model_name()) ] = null;
543
-            }
544
-        }
545
-        return $response;
546
-    }
480
+	/**
481
+	 * Adds a relation between the two model specified model objects.
482
+	 * @since $VID:$
483
+	 * @param EEM_Base $model
484
+	 * @param EE_Model_Relation_Base $relation
485
+	 * @param WP_REST_Request $request
486
+	 * @return array
487
+	 * @throws DomainException
488
+	 * @throws EE_Error
489
+	 * @throws InvalidArgumentException
490
+	 * @throws InvalidDataTypeException
491
+	 * @throws InvalidInterfaceException
492
+	 * @throws RestException
493
+	 */
494
+	public function removeRelation(EEM_Base $model, EE_Model_Relation_Base $relation, WP_REST_Request $request)
495
+	{
496
+		// This endpoint doesn't accept body parameters (it's understandable to think it might, so let developers know
497
+		// up-front that it doesn't.)
498
+		if (!empty($request->get_body_params())) {
499
+			$body_params = $request->get_body_params();
500
+			throw new RestException(
501
+				'invalid_field',
502
+				sprintf(
503
+					esc_html__('This endpoint doesn\'t accept post body arguments, you sent in %1$s', 'event_espresso'),
504
+					implode(array_keys($body_params))
505
+				)
506
+			);
507
+		}
508
+		list($model_obj, $other_obj) = $this->getBothModelObjects($model, $relation, $request);
509
+		// Remember the old relation, if it used a join entry.
510
+		$join_model_obj = null;
511
+		if ($relation instanceof EE_HABTM_Relation) {
512
+			$join_model_obj = $relation->get_join_model()->get_one(
513
+				array(
514
+					array(
515
+						$model->primary_key_name() => $model_obj->ID(),
516
+						$relation->get_other_model()->primary_key_name() => $other_obj->ID()
517
+					)
518
+				)
519
+			);
520
+		}
521
+		// Remove the relation.
522
+		$related_obj = $model_obj->_remove_relation_to(
523
+			$other_obj,
524
+			$relation->get_other_model()->get_this_model_name()
525
+		);
526
+		$response = array(
527
+			strtolower($model->get_this_model_name()) => $this->returnModelObjAsJsonResponse($model_obj, $request),
528
+			strtolower($relation->get_other_model()->get_this_model_name()) => $this->returnModelObjAsJsonResponse($related_obj, $request),
529
+		);
530
+		if ($relation instanceof EE_HABTM_Relation) {
531
+			$join_model_obj_after_removal = $relation->get_join_model()->get_one(
532
+				array(
533
+					array(
534
+						$model->primary_key_name() => $model_obj->ID(),
535
+						$relation->get_other_model()->primary_key_name() => $other_obj->ID()
536
+					)
537
+				)
538
+			);
539
+			if ($join_model_obj instanceof EE_Base_Class) {
540
+				$response['join'][ strtolower($relation->get_join_model()->get_this_model_name()) ] = $this->returnModelObjAsJsonResponse($join_model_obj, $request);
541
+			} else {
542
+				$response['join'][ strtolower($relation->get_join_model()->get_this_model_name()) ] = null;
543
+			}
544
+		}
545
+		return $response;
546
+	}
547 547
 
548
-    /**
549
-     * Gets the model objects indicated by the model, relation object, and request.
550
-     * Throws an exception if the first object doesn't exist, and currently if the related object also doesn't exist.
551
-     * However, this behaviour may change, as we may add support for simultaneously creating and relating data.
552
-     * @since $VID:$
553
-     * @param EEM_Base $model
554
-     * @param EE_Model_Relation_Base $relation
555
-     * @param WP_REST_Request $request
556
-     * @return array {
557
-     * @type EE_Base_Class $model_obj
558
-     * @type EE_Base_Class|null $other_model_obj
559
-     * }
560
-     * @throws RestException
561
-     */
562
-    protected function getBothModelObjects(EEM_Base $model, EE_Model_Relation_Base $relation, WP_REST_Request $request)
563
-    {
564
-        // Check generic caps. For now, we're only allowing access to this endpoint to full admins.
565
-        Capabilities::verifyAtLeastPartialAccessTo($model, EEM_Base::caps_edit, 'edit');
566
-        $default_cap_to_check_for = EE_Restriction_Generator_Base::get_default_restrictions_cap();
567
-        if (! current_user_can($default_cap_to_check_for)) {
568
-            throw new RestException(
569
-                'rest_cannot_edit_' . EEH_Inflector::pluralize_and_lower(($model->get_this_model_name())),
570
-                sprintf(
571
-                    esc_html__(
572
-                        // @codingStandardsIgnoreStart
573
-                        'For now, only those with the admin capability to "%1$s" are allowed to use the REST API to add relations in Event Espresso.',
574
-                        // @codingStandardsIgnoreEnd
575
-                        'event_espresso'
576
-                    ),
577
-                    $default_cap_to_check_for
578
-                ),
579
-                array('status' => 403)
580
-            );
581
-        }
582
-        // Get the main model object.
583
-        $model_obj = $this->getOneOrThrowException($model, $request->get_param('id'));
584
-        // For now, we require the other model object to exist too. This might be relaxed later.
585
-        $other_obj = $this->getOneOrThrowException($relation->get_other_model(), $request->get_param('related_id'));
586
-        return array($model_obj,$other_obj);
587
-    }
548
+	/**
549
+	 * Gets the model objects indicated by the model, relation object, and request.
550
+	 * Throws an exception if the first object doesn't exist, and currently if the related object also doesn't exist.
551
+	 * However, this behaviour may change, as we may add support for simultaneously creating and relating data.
552
+	 * @since $VID:$
553
+	 * @param EEM_Base $model
554
+	 * @param EE_Model_Relation_Base $relation
555
+	 * @param WP_REST_Request $request
556
+	 * @return array {
557
+	 * @type EE_Base_Class $model_obj
558
+	 * @type EE_Base_Class|null $other_model_obj
559
+	 * }
560
+	 * @throws RestException
561
+	 */
562
+	protected function getBothModelObjects(EEM_Base $model, EE_Model_Relation_Base $relation, WP_REST_Request $request)
563
+	{
564
+		// Check generic caps. For now, we're only allowing access to this endpoint to full admins.
565
+		Capabilities::verifyAtLeastPartialAccessTo($model, EEM_Base::caps_edit, 'edit');
566
+		$default_cap_to_check_for = EE_Restriction_Generator_Base::get_default_restrictions_cap();
567
+		if (! current_user_can($default_cap_to_check_for)) {
568
+			throw new RestException(
569
+				'rest_cannot_edit_' . EEH_Inflector::pluralize_and_lower(($model->get_this_model_name())),
570
+				sprintf(
571
+					esc_html__(
572
+						// @codingStandardsIgnoreStart
573
+						'For now, only those with the admin capability to "%1$s" are allowed to use the REST API to add relations in Event Espresso.',
574
+						// @codingStandardsIgnoreEnd
575
+						'event_espresso'
576
+					),
577
+					$default_cap_to_check_for
578
+				),
579
+				array('status' => 403)
580
+			);
581
+		}
582
+		// Get the main model object.
583
+		$model_obj = $this->getOneOrThrowException($model, $request->get_param('id'));
584
+		// For now, we require the other model object to exist too. This might be relaxed later.
585
+		$other_obj = $this->getOneOrThrowException($relation->get_other_model(), $request->get_param('related_id'));
586
+		return array($model_obj,$other_obj);
587
+	}
588 588
 
589
-    /**
590
-     * Gets the model with that ID or throws a REST exception.
591
-     * @since $VID:$
592
-     * @param EEM_Base $model
593
-     * @param $id
594
-     * @return EE_Base_Class
595
-     * @throws RestException
596
-     */
597
-    protected function getOneOrThrowException(EEM_Base $model, $id)
598
-    {
599
-        $model_obj = $model->get_one_by_ID($id);
600
-        // @todo: check they can permission for it. For now unnecessary because only full admins can use this endpoint.
601
-        if ($model_obj instanceof EE_Base_Class) {
602
-            return $model_obj;
603
-        }
604
-        $lowercase_model_name = strtolower($model->get_this_model_name());
605
-        throw new RestException(
606
-            sprintf('rest_%s_invalid_id', $lowercase_model_name),
607
-            sprintf(__('Invalid %s ID.', 'event_espresso'), $lowercase_model_name),
608
-            array('status' => 404)
609
-        );
610
-    }
589
+	/**
590
+	 * Gets the model with that ID or throws a REST exception.
591
+	 * @since $VID:$
592
+	 * @param EEM_Base $model
593
+	 * @param $id
594
+	 * @return EE_Base_Class
595
+	 * @throws RestException
596
+	 */
597
+	protected function getOneOrThrowException(EEM_Base $model, $id)
598
+	{
599
+		$model_obj = $model->get_one_by_ID($id);
600
+		// @todo: check they can permission for it. For now unnecessary because only full admins can use this endpoint.
601
+		if ($model_obj instanceof EE_Base_Class) {
602
+			return $model_obj;
603
+		}
604
+		$lowercase_model_name = strtolower($model->get_this_model_name());
605
+		throw new RestException(
606
+			sprintf('rest_%s_invalid_id', $lowercase_model_name),
607
+			sprintf(__('Invalid %s ID.', 'event_espresso'), $lowercase_model_name),
608
+			array('status' => 404)
609
+		);
610
+	}
611 611
 }
Please login to merge, or discard this patch.