Completed
Branch BUG-10851-events-shortcode (46b271)
by
unknown
22:11 queued 11:10
created
core/libraries/rest_api/controllers/model/Read.php 2 patches
Indentation   +1272 added lines, -1272 removed lines patch added patch discarded remove patch
@@ -9,7 +9,7 @@  discard block
 block discarded – undo
9 9
 use EE_Datetime_Field;
10 10
 
11 11
 if (! defined('EVENT_ESPRESSO_VERSION')) {
12
-    exit('No direct script access allowed');
12
+	exit('No direct script access allowed');
13 13
 }
14 14
 
15 15
 
@@ -27,1277 +27,1277 @@  discard block
 block discarded – undo
27 27
 
28 28
 
29 29
 
30
-    /**
31
-     * @var Calculated_Model_Fields
32
-     */
33
-    protected $_fields_calculator;
34
-
35
-
36
-
37
-    /**
38
-     * Read constructor.
39
-     */
40
-    public function __construct()
41
-    {
42
-        parent::__construct();
43
-        $this->_fields_calculator = new Calculated_Model_Fields();
44
-    }
45
-
46
-
47
-
48
-    /**
49
-     * Handles requests to get all (or a filtered subset) of entities for a particular model
50
-     *
51
-     * @param \WP_REST_Request $request
52
-     * @return \WP_REST_Response|\WP_Error
53
-     */
54
-    public static function handle_request_get_all(\WP_REST_Request $request)
55
-    {
56
-        $controller = new Read();
57
-        try {
58
-            $matches = $controller->parse_route(
59
-                $request->get_route(),
60
-                '~' . \EED_Core_Rest_Api::ee_api_namespace_for_regex . '(.*)~',
61
-                array('version', 'model')
62
-            );
63
-            $controller->set_requested_version($matches['version']);
64
-            $model_name_singular = \EEH_Inflector::singularize_and_upper($matches['model']);
65
-            if (! $controller->get_model_version_info()->is_model_name_in_this_version($model_name_singular)) {
66
-                return $controller->send_response(
67
-                    new \WP_Error(
68
-                        'endpoint_parsing_error',
69
-                        sprintf(
70
-                            __('There is no model for endpoint %s. Please contact event espresso support',
71
-                                'event_espresso'),
72
-                            $model_name_singular
73
-                        )
74
-                    )
75
-                );
76
-            }
77
-            return $controller->send_response(
78
-                $controller->get_entities_from_model(
79
-                    $controller->get_model_version_info()->load_model($model_name_singular),
80
-                    $request
81
-                )
82
-            );
83
-        } catch (\Exception $e) {
84
-            return $controller->send_response($e);
85
-        }
86
-    }
87
-
88
-
89
-
90
-    /**
91
-     * Prepares and returns schema for any OPTIONS request.
92
-     *
93
-     * @param string $model_name Something like `Event` or `Registration`
94
-     * @param string $version    The API endpoint version being used.
95
-     * @return array
96
-     */
97
-    public static function handle_schema_request($model_name, $version)
98
-    {
99
-        $controller = new Read();
100
-        try {
101
-            $controller->set_requested_version($version);
102
-            if (! $controller->get_model_version_info()->is_model_name_in_this_version($model_name)) {
103
-                return array();
104
-            }
105
-            //get the model for this version
106
-            $model = $controller->get_model_version_info()->load_model($model_name);
107
-            $model_schema = new JsonModelSchema($model);
108
-            return $model_schema->getModelSchemaForRelations(
109
-                $controller->get_model_version_info()->relation_settings($model),
110
-                $controller->_customize_schema_for_rest_response(
111
-                    $model,
112
-                    $model_schema->getModelSchemaForFields(
113
-                        $controller->get_model_version_info()->fields_on_model_in_this_version($model),
114
-                        $model_schema->getInitialSchemaStructure()
115
-                    )
116
-                )
117
-            );
118
-        } catch (\Exception $e) {
119
-            return array();
120
-        }
121
-    }
122
-
123
-
124
-
125
-    /**
126
-     * This loops through each field in the given schema for the model and does the following:
127
-     * - add any extra fields that are REST API specific and related to existing fields.
128
-     * - transform default values into the correct format for a REST API response.
129
-     *
130
-     * @param \EEM_Base $model
131
-     * @param array     $schema
132
-     * @return array  The final schema.
133
-     */
134
-    protected function _customize_schema_for_rest_response(\EEM_Base $model, array $schema)
135
-    {
136
-        foreach ($this->get_model_version_info()->fields_on_model_in_this_version($model) as $field_name => $field) {
137
-            $schema = $this->_translate_defaults_for_rest_response(
138
-                $field_name,
139
-                $field,
140
-                $this->_maybe_add_extra_fields_to_schema($field_name, $field, $schema)
141
-            );
142
-        }
143
-        return $schema;
144
-    }
145
-
146
-
147
-
148
-    /**
149
-     * This is used to ensure that the 'default' value set in the schema response is formatted correctly for the REST
150
-     * response.
151
-     *
152
-     * @param                      $field_name
153
-     * @param \EE_Model_Field_Base $field
154
-     * @param array                $schema
155
-     * @return array
156
-     */
157
-    protected function _translate_defaults_for_rest_response($field_name, \EE_Model_Field_Base $field, array $schema)
158
-    {
159
-        if (isset($schema['properties'][$field_name]['default'])) {
160
-            if (is_array($schema['properties'][$field_name]['default'])) {
161
-                foreach ($schema['properties'][$field_name]['default'] as $default_key => $default_value) {
162
-                    if ($default_key === 'raw') {
163
-                        $schema['properties'][$field_name]['default'][$default_key] = Model_Data_Translator::prepare_field_value_for_json(
164
-                            $field,
165
-                            $default_value,
166
-                            $this->get_model_version_info()->requested_version()
167
-                        );
168
-                    }
169
-                }
170
-            } else {
171
-                $schema['properties'][$field_name]['default'] = Model_Data_Translator::prepare_field_value_for_json(
172
-                    $field,
173
-                    $schema['properties'][$field_name]['default'],
174
-                    $this->get_model_version_info()->requested_version()
175
-                );
176
-            }
177
-        }
178
-        return $schema;
179
-    }
180
-
181
-
182
-
183
-    /**
184
-     * Adds additional fields to the schema
185
-     * The REST API returns a GMT value field for each datetime field in the resource.  Thus the description about this
186
-     * needs to be added to the schema.
187
-     *
188
-     * @param                      $field_name
189
-     * @param \EE_Model_Field_Base $field
190
-     * @param array                $schema
191
-     * @return array
192
-     */
193
-    protected function _maybe_add_extra_fields_to_schema($field_name, \EE_Model_Field_Base $field, array $schema)
194
-    {
195
-        if ($field instanceof EE_Datetime_Field) {
196
-            $schema['properties'][$field_name . '_gmt'] = $field->getSchema();
197
-            //modify the description
198
-            $schema['properties'][$field_name . '_gmt']['description'] = sprintf(
199
-                esc_html__('%s - the value for this field is in GMT.', 'event_espresso'),
200
-                $field->get_nicename()
201
-            );
202
-        }
203
-        return $schema;
204
-    }
205
-
206
-
207
-
208
-    /**
209
-     * Used to figure out the route from the request when a `WP_REST_Request` object is not available
210
-     *
211
-     * @return string
212
-     */
213
-    protected function get_route_from_request()
214
-    {
215
-        if (isset($GLOBALS['wp'])
216
-            && $GLOBALS['wp'] instanceof \WP
217
-            && isset($GLOBALS['wp']->query_vars['rest_route'])
218
-        ) {
219
-            return $GLOBALS['wp']->query_vars['rest_route'];
220
-        } else {
221
-            return isset($_SERVER['PATH_INFO']) ? $_SERVER['PATH_INFO'] : '/';
222
-        }
223
-    }
224
-
225
-
226
-
227
-    /**
228
-     * Gets a single entity related to the model indicated in the path and its id
229
-     *
230
-     * @param \WP_REST_Request $request
231
-     * @return \WP_REST_Response|\WP_Error
232
-     */
233
-    public static function handle_request_get_one(\WP_REST_Request $request)
234
-    {
235
-        $controller = new Read();
236
-        try {
237
-            $matches = $controller->parse_route(
238
-                $request->get_route(),
239
-                '~' . \EED_Core_Rest_Api::ee_api_namespace_for_regex . '(.*)/(.*)~',
240
-                array('version', 'model', 'id'));
241
-            $controller->set_requested_version($matches['version']);
242
-            $model_name_singular = \EEH_Inflector::singularize_and_upper($matches['model']);
243
-            if (! $controller->get_model_version_info()->is_model_name_in_this_version($model_name_singular)) {
244
-                return $controller->send_response(
245
-                    new \WP_Error(
246
-                        'endpoint_parsing_error',
247
-                        sprintf(
248
-                            __('There is no model for endpoint %s. Please contact event espresso support',
249
-                                'event_espresso'),
250
-                            $model_name_singular
251
-                        )
252
-                    )
253
-                );
254
-            }
255
-            return $controller->send_response(
256
-                $controller->get_entity_from_model(
257
-                    $controller->get_model_version_info()->load_model($model_name_singular),
258
-                    $request
259
-                )
260
-            );
261
-        } catch (\Exception $e) {
262
-            return $controller->send_response($e);
263
-        }
264
-    }
265
-
266
-
267
-
268
-    /**
269
-     * Gets all the related entities (or if its a belongs-to relation just the one)
270
-     * to the item with the given id
271
-     *
272
-     * @param \WP_REST_Request $request
273
-     * @return \WP_REST_Response|\WP_Error
274
-     */
275
-    public static function handle_request_get_related(\WP_REST_Request $request)
276
-    {
277
-        $controller = new Read();
278
-        try {
279
-            $matches = $controller->parse_route(
280
-                $request->get_route(),
281
-                '~' . \EED_Core_Rest_Api::ee_api_namespace_for_regex . '(.*)/(.*)/(.*)~',
282
-                array('version', 'model', 'id', 'related_model')
283
-            );
284
-            $controller->set_requested_version($matches['version']);
285
-            $main_model_name_singular = \EEH_Inflector::singularize_and_upper($matches['model']);
286
-            if (! $controller->get_model_version_info()->is_model_name_in_this_version($main_model_name_singular)) {
287
-                return $controller->send_response(
288
-                    new \WP_Error(
289
-                        'endpoint_parsing_error',
290
-                        sprintf(
291
-                            __('There is no model for endpoint %s. Please contact event espresso support',
292
-                                'event_espresso'),
293
-                            $main_model_name_singular
294
-                        )
295
-                    )
296
-                );
297
-            }
298
-            $main_model = $controller->get_model_version_info()->load_model($main_model_name_singular);
299
-            //assume the related model name is plural and try to find the model's name
300
-            $related_model_name_singular = \EEH_Inflector::singularize_and_upper($matches['related_model']);
301
-            if (! $controller->get_model_version_info()->is_model_name_in_this_version($related_model_name_singular)) {
302
-                //so the word didn't singularize well. Maybe that's just because it's a singular word?
303
-                $related_model_name_singular = \EEH_Inflector::humanize($matches['related_model']);
304
-            }
305
-            if (! $controller->get_model_version_info()->is_model_name_in_this_version($related_model_name_singular)) {
306
-                return $controller->send_response(
307
-                    new \WP_Error(
308
-                        'endpoint_parsing_error',
309
-                        sprintf(
310
-                            __('There is no model for endpoint %s. Please contact event espresso support',
311
-                                'event_espresso'),
312
-                            $related_model_name_singular
313
-                        )
314
-                    )
315
-                );
316
-            }
317
-            return $controller->send_response(
318
-                $controller->get_entities_from_relation(
319
-                    $request->get_param('id'),
320
-                    $main_model->related_settings_for($related_model_name_singular),
321
-                    $request
322
-                )
323
-            );
324
-        } catch (\Exception $e) {
325
-            return $controller->send_response($e);
326
-        }
327
-    }
328
-
329
-
330
-
331
-    /**
332
-     * Gets a collection for the given model and filters
333
-     *
334
-     * @param \EEM_Base        $model
335
-     * @param \WP_REST_Request $request
336
-     * @return array|\WP_Error
337
-     */
338
-    public function get_entities_from_model($model, $request)
339
-    {
340
-        $query_params = $this->create_model_query_params($model, $request->get_params());
341
-        if (! Capabilities::current_user_has_partial_access_to($model, $query_params['caps'])) {
342
-            $model_name_plural = \EEH_Inflector::pluralize_and_lower($model->get_this_model_name());
343
-            return new \WP_Error(
344
-                sprintf('rest_%s_cannot_list', $model_name_plural),
345
-                sprintf(
346
-                    __('Sorry, you are not allowed to list %1$s. Missing permissions: %2$s', 'event_espresso'),
347
-                    $model_name_plural,
348
-                    Capabilities::get_missing_permissions_string($model, $query_params['caps'])
349
-                ),
350
-                array('status' => 403)
351
-            );
352
-        }
353
-        if (! $request->get_header('no_rest_headers')) {
354
-            $this->_set_headers_from_query_params($model, $query_params);
355
-        }
356
-        /** @type array $results */
357
-        $results = $model->get_all_wpdb_results($query_params);
358
-        $nice_results = array();
359
-        foreach ($results as $result) {
360
-            $nice_results[] = $this->create_entity_from_wpdb_result(
361
-                $model,
362
-                $result,
363
-                $request
364
-            );
365
-        }
366
-        return $nice_results;
367
-    }
368
-
369
-
370
-
371
-    /**
372
-     * @param array                   $primary_model_query_params query params for finding the item from which
373
-     *                                                            relations will be based
374
-     * @param \EE_Model_Relation_Base $relation
375
-     * @param \WP_REST_Request        $request
376
-     * @return \WP_Error|array
377
-     */
378
-    protected function _get_entities_from_relation($primary_model_query_params, $relation, $request)
379
-    {
380
-        $context = $this->validate_context($request->get_param('caps'));
381
-        $model = $relation->get_this_model();
382
-        $related_model = $relation->get_other_model();
383
-        if (! isset($primary_model_query_params[0])) {
384
-            $primary_model_query_params[0] = array();
385
-        }
386
-        //check if they can access the 1st model object
387
-        $primary_model_query_params = array(
388
-            0       => $primary_model_query_params[0],
389
-            'limit' => 1,
390
-        );
391
-        if ($model instanceof \EEM_Soft_Delete_Base) {
392
-            $primary_model_query_params = $model->alter_query_params_so_deleted_and_undeleted_items_included($primary_model_query_params);
393
-        }
394
-        $restricted_query_params = $primary_model_query_params;
395
-        $restricted_query_params['caps'] = $context;
396
-        $this->_set_debug_info('main model query params', $restricted_query_params);
397
-        $this->_set_debug_info('missing caps', Capabilities::get_missing_permissions_string($related_model, $context));
398
-        if (
399
-        ! (
400
-            Capabilities::current_user_has_partial_access_to($related_model, $context)
401
-            && $model->exists($restricted_query_params)
402
-        )
403
-        ) {
404
-            if ($relation instanceof \EE_Belongs_To_Relation) {
405
-                $related_model_name_maybe_plural = strtolower($related_model->get_this_model_name());
406
-            } else {
407
-                $related_model_name_maybe_plural = \EEH_Inflector::pluralize_and_lower($related_model->get_this_model_name());
408
-            }
409
-            return new \WP_Error(
410
-                sprintf('rest_%s_cannot_list', $related_model_name_maybe_plural),
411
-                sprintf(
412
-                    __('Sorry, you are not allowed to list %1$s related to %2$s. Missing permissions: %3$s',
413
-                        'event_espresso'),
414
-                    $related_model_name_maybe_plural,
415
-                    $relation->get_this_model()->get_this_model_name(),
416
-                    implode(
417
-                        ',',
418
-                        array_keys(
419
-                            Capabilities::get_missing_permissions($related_model, $context)
420
-                        )
421
-                    )
422
-                ),
423
-                array('status' => 403)
424
-            );
425
-        }
426
-        $query_params = $this->create_model_query_params($relation->get_other_model(), $request->get_params());
427
-        foreach ($primary_model_query_params[0] as $where_condition_key => $where_condition_value) {
428
-            $query_params[0][$relation->get_this_model()->get_this_model_name()
429
-                             . '.'
430
-                             . $where_condition_key] = $where_condition_value;
431
-        }
432
-        $query_params['default_where_conditions'] = 'none';
433
-        $query_params['caps'] = $context;
434
-        if (! $request->get_header('no_rest_headers')) {
435
-            $this->_set_headers_from_query_params($relation->get_other_model(), $query_params);
436
-        }
437
-        /** @type array $results */
438
-        $results = $relation->get_other_model()->get_all_wpdb_results($query_params);
439
-        $nice_results = array();
440
-        foreach ($results as $result) {
441
-            $nice_result = $this->create_entity_from_wpdb_result(
442
-                $relation->get_other_model(),
443
-                $result,
444
-                $request
445
-            );
446
-            if ($relation instanceof \EE_HABTM_Relation) {
447
-                //put the unusual stuff (properties from the HABTM relation) first, and make sure
448
-                //if there are conflicts we prefer the properties from the main model
449
-                $join_model_result = $this->create_entity_from_wpdb_result(
450
-                    $relation->get_join_model(),
451
-                    $result,
452
-                    $request
453
-                );
454
-                $joined_result = array_merge($nice_result, $join_model_result);
455
-                //but keep the meta stuff from the main model
456
-                if (isset($nice_result['meta'])) {
457
-                    $joined_result['meta'] = $nice_result['meta'];
458
-                }
459
-                $nice_result = $joined_result;
460
-            }
461
-            $nice_results[] = $nice_result;
462
-        }
463
-        if ($relation instanceof \EE_Belongs_To_Relation) {
464
-            return array_shift($nice_results);
465
-        } else {
466
-            return $nice_results;
467
-        }
468
-    }
469
-
470
-
471
-
472
-    /**
473
-     * Gets the collection for given relation object
474
-     * The same as Read::get_entities_from_model(), except if the relation
475
-     * is a HABTM relation, in which case it merges any non-foreign-key fields from
476
-     * the join-model-object into the results
477
-     *
478
-     * @param string                  $id the ID of the thing we are fetching related stuff from
479
-     * @param \EE_Model_Relation_Base $relation
480
-     * @param \WP_REST_Request        $request
481
-     * @return array|\WP_Error
482
-     * @throws \EE_Error
483
-     */
484
-    public function get_entities_from_relation($id, $relation, $request)
485
-    {
486
-        if (! $relation->get_this_model()->has_primary_key_field()) {
487
-            throw new \EE_Error(
488
-                sprintf(
489
-                    __('Read::get_entities_from_relation should only be called from a model with a primary key, it was called from %1$s',
490
-                        'event_espresso'),
491
-                    $relation->get_this_model()->get_this_model_name()
492
-                )
493
-            );
494
-        }
495
-        return $this->_get_entities_from_relation(
496
-            array(
497
-                array(
498
-                    $relation->get_this_model()->primary_key_name() => $id,
499
-                ),
500
-            ),
501
-            $relation,
502
-            $request
503
-        );
504
-    }
505
-
506
-
507
-
508
-    /**
509
-     * Sets the headers that are based on the model and query params,
510
-     * like the total records. This should only be called on the original request
511
-     * from the client, not on subsequent internal
512
-     *
513
-     * @param \EEM_Base $model
514
-     * @param array     $query_params
515
-     * @return void
516
-     */
517
-    protected function _set_headers_from_query_params($model, $query_params)
518
-    {
519
-        $this->_set_debug_info('model query params', $query_params);
520
-        $this->_set_debug_info('missing caps',
521
-            Capabilities::get_missing_permissions_string($model, $query_params['caps']));
522
-        //normally the limit to a 2-part array, where the 2nd item is the limit
523
-        if (! isset($query_params['limit'])) {
524
-            $query_params['limit'] = \EED_Core_Rest_Api::get_default_query_limit();
525
-        }
526
-        if (is_array($query_params['limit'])) {
527
-            $limit_parts = $query_params['limit'];
528
-        } else {
529
-            $limit_parts = explode(',', $query_params['limit']);
530
-            if (count($limit_parts) == 1) {
531
-                $limit_parts = array(0, $limit_parts[0]);
532
-            }
533
-        }
534
-        //remove the group by and having parts of the query, as those will
535
-        //make the sql query return an array of values, instead of just a single value
536
-        unset($query_params['group_by'], $query_params['having'], $query_params['limit']);
537
-        $count = $model->count($query_params, null, true);
538
-        $pages = $count / $limit_parts[1];
539
-        $this->_set_response_header('Total', $count, false);
540
-        $this->_set_response_header('PageSize', $limit_parts[1], false);
541
-        $this->_set_response_header('TotalPages', ceil($pages), false);
542
-    }
543
-
544
-
545
-
546
-    /**
547
-     * Changes database results into REST API entities
548
-     *
549
-     * @param \EEM_Base        $model
550
-     * @param array            $db_row     like results from $wpdb->get_results()
551
-     * @param \WP_REST_Request $rest_request
552
-     * @param string           $deprecated no longer used
553
-     * @return array ready for being converted into json for sending to client
554
-     */
555
-    public function create_entity_from_wpdb_result($model, $db_row, $rest_request, $deprecated = null)
556
-    {
557
-        if (! $rest_request instanceof \WP_REST_Request) {
558
-            //ok so this was called in the old style, where the 3rd arg was
559
-            //$include, and the 4th arg was $context
560
-            //now setup the request just to avoid fatal errors, although we won't be able
561
-            //to truly make use of it because it's kinda devoid of info
562
-            $rest_request = new \WP_REST_Request();
563
-            $rest_request->set_param('include', $rest_request);
564
-            $rest_request->set_param('caps', $deprecated);
565
-        }
566
-        if ($rest_request->get_param('caps') == null) {
567
-            $rest_request->set_param('caps', \EEM_Base::caps_read);
568
-        }
569
-        $entity_array = $this->_create_bare_entity_from_wpdb_results($model, $db_row);
570
-        $entity_array = $this->_add_extra_fields($model, $db_row, $entity_array);
571
-        $entity_array['_links'] = $this->_get_entity_links($model, $db_row, $entity_array);
572
-        $entity_array['_calculated_fields'] = $this->_get_entity_calculations($model, $db_row, $rest_request);
573
-        $entity_array = apply_filters('FHEE__Read__create_entity_from_wpdb_results__entity_before_including_requested_models',
574
-            $entity_array, $model, $rest_request->get_param('caps'), $rest_request, $this);
575
-        $entity_array = $this->_include_requested_models($model, $rest_request, $entity_array, $db_row);
576
-        $entity_array = apply_filters(
577
-            'FHEE__Read__create_entity_from_wpdb_results__entity_before_inaccessible_field_removal',
578
-            $entity_array,
579
-            $model,
580
-            $rest_request->get_param('caps'),
581
-            $rest_request,
582
-            $this
583
-        );
584
-        $result_without_inaccessible_fields = Capabilities::filter_out_inaccessible_entity_fields(
585
-            $entity_array,
586
-            $model,
587
-            $rest_request->get_param('caps'),
588
-            $this->get_model_version_info(),
589
-            $model->get_index_primary_key_string(
590
-                $model->deduce_fields_n_values_from_cols_n_values($db_row)
591
-            )
592
-        );
593
-        $this->_set_debug_info(
594
-            'inaccessible fields',
595
-            array_keys(array_diff_key($entity_array, $result_without_inaccessible_fields))
596
-        );
597
-        return apply_filters(
598
-            'FHEE__Read__create_entity_from_wpdb_results__entity_return',
599
-            $result_without_inaccessible_fields,
600
-            $model,
601
-            $rest_request->get_param('caps')
602
-        );
603
-    }
604
-
605
-
606
-
607
-    /**
608
-     * Creates a REST entity array (JSON object we're going to return in the response, but
609
-     * for now still a PHP array, but soon enough we'll call json_encode on it, don't worry),
610
-     * from $wpdb->get_row( $sql, ARRAY_A)
611
-     *
612
-     * @param \EEM_Base $model
613
-     * @param array     $db_row
614
-     * @return array entity mostly ready for converting to JSON and sending in the response
615
-     */
616
-    protected function _create_bare_entity_from_wpdb_results(\EEM_Base $model, $db_row)
617
-    {
618
-        $result = $model->deduce_fields_n_values_from_cols_n_values($db_row);
619
-        $result = array_intersect_key($result,
620
-            $this->get_model_version_info()->fields_on_model_in_this_version($model));
621
-        //if this is a CPT, we need to set the global $post to it,
622
-        //otherwise shortcodes etc won't work properly while rendering it
623
-        if ($model instanceof \EEM_CPT_Base) {
624
-            $do_chevy_shuffle = true;
625
-        } else {
626
-            $do_chevy_shuffle = false;
627
-        }
628
-        if ($do_chevy_shuffle) {
629
-            global $post;
630
-            $old_post = $post;
631
-            $post = get_post($result[$model->primary_key_name()]);
632
-            if (! $post instanceof \WP_Post) {
633
-                //well that's weird, because $result is what we JUST fetched from the database
634
-                throw new Rest_Exception(
635
-                    'error_fetching_post_from_database_results',
636
-                    esc_html__(
637
-                        'An item was retrieved from the database but it\'s not a WP_Post like it should be.',
638
-                        'event_espresso'
639
-                    )
640
-                );
641
-            }
642
-            $model_object_classname = 'EE_' . $model->get_this_model_name();
643
-            $post->{$model_object_classname} = \EE_Registry::instance()->load_class(
644
-                $model_object_classname,
645
-                $result,
646
-                false,
647
-                false
648
-                );
649
-        }
650
-        foreach ($result as $field_name => $raw_field_value) {
651
-            $field_obj = $model->field_settings_for($field_name);
652
-            $field_value = $field_obj->prepare_for_set_from_db($raw_field_value);
653
-            if ($this->is_subclass_of_one($field_obj, $this->get_model_version_info()->fields_ignored())) {
654
-                unset($result[$field_name]);
655
-            } elseif (
656
-            $this->is_subclass_of_one($field_obj, $this->get_model_version_info()->fields_that_have_rendered_format())
657
-            ) {
658
-                $result[$field_name] = array(
659
-                    'raw'      => $field_obj->prepare_for_get($field_value),
660
-                    'rendered' => $field_obj->prepare_for_pretty_echoing($field_value),
661
-                );
662
-            } elseif (
663
-            $this->is_subclass_of_one($field_obj, $this->get_model_version_info()->fields_that_have_pretty_format())
664
-            ) {
665
-                $result[$field_name] = array(
666
-                    'raw'    => $field_obj->prepare_for_get($field_value),
667
-                    'pretty' => $field_obj->prepare_for_pretty_echoing($field_value),
668
-                );
669
-            } elseif ($field_obj instanceof \EE_Datetime_Field) {
670
-                if ($field_value instanceof \DateTime) {
671
-                    $timezone = $field_value->getTimezone();
672
-                    $field_value->setTimezone(new \DateTimeZone('UTC'));
673
-                    $result[$field_name . '_gmt'] = Model_Data_Translator::prepare_field_value_for_json(
674
-                        $field_obj,
675
-                        $field_value,
676
-                        $this->get_model_version_info()->requested_version()
677
-                    );
678
-                    $field_value->setTimezone($timezone);
679
-                    $result[$field_name] = Model_Data_Translator::prepare_field_value_for_json(
680
-                        $field_obj,
681
-                        $field_value,
682
-                        $this->get_model_version_info()->requested_version()
683
-                    );
684
-                }
685
-            } else {
686
-                $result[$field_name] = Model_Data_Translator::prepare_field_value_for_json(
687
-                    $field_obj,
688
-                    $field_obj->prepare_for_get($field_value),
689
-                    $this->get_model_version_info()->requested_version()
690
-                );
691
-            }
692
-        }
693
-        if ($do_chevy_shuffle) {
694
-            $post = $old_post;
695
-        }
696
-        return $result;
697
-    }
698
-
699
-
700
-
701
-    /**
702
-     * Adds a few extra fields to the entity response
703
-     *
704
-     * @param \EEM_Base $model
705
-     * @param array     $db_row
706
-     * @param array     $entity_array
707
-     * @return array modified entity
708
-     */
709
-    protected function _add_extra_fields(\EEM_Base $model, $db_row, $entity_array)
710
-    {
711
-        if ($model instanceof \EEM_CPT_Base) {
712
-            $entity_array['link'] = get_permalink($db_row[$model->get_primary_key_field()->get_qualified_column()]);
713
-        }
714
-        return $entity_array;
715
-    }
716
-
717
-
718
-
719
-    /**
720
-     * Gets links we want to add to the response
721
-     *
722
-     * @global \WP_REST_Server $wp_rest_server
723
-     * @param \EEM_Base        $model
724
-     * @param array            $db_row
725
-     * @param array            $entity_array
726
-     * @return array the _links item in the entity
727
-     */
728
-    protected function _get_entity_links($model, $db_row, $entity_array)
729
-    {
730
-        //add basic links
731
-        $links = array();
732
-        if ($model->has_primary_key_field()) {
733
-            $links['self'] = array(
734
-                array(
735
-                    'href' => $this->get_versioned_link_to(
736
-                        \EEH_Inflector::pluralize_and_lower($model->get_this_model_name())
737
-                        . '/'
738
-                        . $entity_array[$model->primary_key_name()]
739
-                    ),
740
-                ),
741
-            );
742
-        }
743
-        $links['collection'] = array(
744
-            array(
745
-                'href' => $this->get_versioned_link_to(
746
-                    \EEH_Inflector::pluralize_and_lower($model->get_this_model_name())
747
-                ),
748
-            ),
749
-        );
750
-        //add links to related models
751
-        if ($model->has_primary_key_field()) {
752
-            foreach ($this->get_model_version_info()->relation_settings($model) as $relation_name => $relation_obj) {
753
-                $related_model_part = Read::get_related_entity_name($relation_name, $relation_obj);
754
-                $links[\EED_Core_Rest_Api::ee_api_link_namespace . $related_model_part] = array(
755
-                    array(
756
-                        'href'   => $this->get_versioned_link_to(
757
-                            \EEH_Inflector::pluralize_and_lower($model->get_this_model_name())
758
-                            . '/'
759
-                            . $entity_array[$model->primary_key_name()]
760
-                            . '/'
761
-                            . $related_model_part
762
-                        ),
763
-                        'single' => $relation_obj instanceof \EE_Belongs_To_Relation ? true : false,
764
-                    ),
765
-                );
766
-            }
767
-        }
768
-        return $links;
769
-    }
770
-
771
-
772
-
773
-    /**
774
-     * Adds the included models indicated in the request to the entity provided
775
-     *
776
-     * @param \EEM_Base        $model
777
-     * @param \WP_REST_Request $rest_request
778
-     * @param array            $entity_array
779
-     * @param array            $db_row
780
-     * @return array the modified entity
781
-     */
782
-    protected function _include_requested_models(
783
-        \EEM_Base $model,
784
-        \WP_REST_Request $rest_request,
785
-        $entity_array,
786
-        $db_row = array()
787
-    ) {
788
-        //if $db_row not included, hope the entity array has what we need
789
-        if (! $db_row) {
790
-            $db_row = $entity_array;
791
-        }
792
-        $includes_for_this_model = $this->explode_and_get_items_prefixed_with($rest_request->get_param('include'), '');
793
-        $includes_for_this_model = $this->_remove_model_names_from_array($includes_for_this_model);
794
-        //if they passed in * or didn't specify any includes, return everything
795
-        if (! in_array('*', $includes_for_this_model)
796
-            && ! empty($includes_for_this_model)
797
-        ) {
798
-            if ($model->has_primary_key_field()) {
799
-                //always include the primary key. ya just gotta know that at least
800
-                $includes_for_this_model[] = $model->primary_key_name();
801
-            }
802
-            if ($this->explode_and_get_items_prefixed_with($rest_request->get_param('calculate'), '')) {
803
-                $includes_for_this_model[] = '_calculated_fields';
804
-            }
805
-            $entity_array = array_intersect_key($entity_array, array_flip($includes_for_this_model));
806
-        }
807
-        $relation_settings = $this->get_model_version_info()->relation_settings($model);
808
-        foreach ($relation_settings as $relation_name => $relation_obj) {
809
-            $related_fields_to_include = $this->explode_and_get_items_prefixed_with(
810
-                $rest_request->get_param('include'),
811
-                $relation_name
812
-            );
813
-            $related_fields_to_calculate = $this->explode_and_get_items_prefixed_with(
814
-                $rest_request->get_param('calculate'),
815
-                $relation_name
816
-            );
817
-            //did they specify they wanted to include a related model, or
818
-            //specific fields from a related model?
819
-            //or did they specify to calculate a field from a related model?
820
-            if ($related_fields_to_include || $related_fields_to_calculate) {
821
-                //if so, we should include at least some part of the related model
822
-                $pretend_related_request = new \WP_REST_Request();
823
-                $pretend_related_request->set_query_params(
824
-                    array(
825
-                        'caps'      => $rest_request->get_param('caps'),
826
-                        'include'   => $related_fields_to_include,
827
-                        'calculate' => $related_fields_to_calculate,
828
-                    )
829
-                );
830
-                $pretend_related_request->add_header('no_rest_headers', true);
831
-                $primary_model_query_params = $model->alter_query_params_to_restrict_by_ID(
832
-                    $model->get_index_primary_key_string(
833
-                        $model->deduce_fields_n_values_from_cols_n_values($db_row)
834
-                    )
835
-                );
836
-                $related_results = $this->_get_entities_from_relation(
837
-                    $primary_model_query_params,
838
-                    $relation_obj,
839
-                    $pretend_related_request
840
-                );
841
-                $entity_array[Read::get_related_entity_name($relation_name, $relation_obj)] = $related_results
842
-                                                                                              instanceof
843
-                                                                                              \WP_Error
844
-                    ? null
845
-                    : $related_results;
846
-            }
847
-        }
848
-        return $entity_array;
849
-    }
850
-
851
-
852
-
853
-    /**
854
-     * Returns a new array with all the names of models removed. Eg
855
-     * array( 'Event', 'Datetime.*', 'foobar' ) would become array( 'Datetime.*', 'foobar' )
856
-     *
857
-     * @param array $arr
858
-     * @return array
859
-     */
860
-    private function _remove_model_names_from_array($arr)
861
-    {
862
-        return array_diff($arr, array_keys(\EE_Registry::instance()->non_abstract_db_models));
863
-    }
864
-
865
-
866
-
867
-    /**
868
-     * Gets the calculated fields for the response
869
-     *
870
-     * @param \EEM_Base        $model
871
-     * @param array            $wpdb_row
872
-     * @param \WP_REST_Request $rest_request
873
-     * @return \stdClass the _calculations item in the entity
874
-     */
875
-    protected function _get_entity_calculations($model, $wpdb_row, $rest_request)
876
-    {
877
-        $calculated_fields = $this->explode_and_get_items_prefixed_with(
878
-            $rest_request->get_param('calculate'),
879
-            ''
880
-        );
881
-        //note: setting calculate=* doesn't do anything
882
-        $calculated_fields_to_return = new \stdClass();
883
-        foreach ($calculated_fields as $field_to_calculate) {
884
-            try {
885
-                $calculated_fields_to_return->$field_to_calculate = Model_Data_Translator::prepare_field_value_for_json(
886
-                    null,
887
-                    $this->_fields_calculator->retrieve_calculated_field_value(
888
-                        $model,
889
-                        $field_to_calculate,
890
-                        $wpdb_row,
891
-                        $rest_request,
892
-                        $this
893
-                    ),
894
-                    $this->get_model_version_info()->requested_version()
895
-                );
896
-            } catch (Rest_Exception $e) {
897
-                //if we don't have permission to read it, just leave it out. but let devs know about the problem
898
-                $this->_set_response_header(
899
-                    'Notices-Field-Calculation-Errors['
900
-                    . $e->get_string_code()
901
-                    . ']['
902
-                    . $model->get_this_model_name()
903
-                    . ']['
904
-                    . $field_to_calculate
905
-                    . ']',
906
-                    $e->getMessage(),
907
-                    true
908
-                );
909
-            }
910
-        }
911
-        return $calculated_fields_to_return;
912
-    }
913
-
914
-
915
-
916
-    /**
917
-     * Gets the full URL to the resource, taking the requested version into account
918
-     *
919
-     * @param string $link_part_after_version_and_slash eg "events/10/datetimes"
920
-     * @return string url eg "http://mysite.com/wp-json/ee/v4.6/events/10/datetimes"
921
-     */
922
-    public function get_versioned_link_to($link_part_after_version_and_slash)
923
-    {
924
-        return rest_url(
925
-            \EED_Core_Rest_Api::ee_api_namespace
926
-            . $this->get_model_version_info()->requested_version()
927
-            . '/'
928
-            . $link_part_after_version_and_slash
929
-        );
930
-    }
931
-
932
-
933
-
934
-    /**
935
-     * Gets the correct lowercase name for the relation in the API according
936
-     * to the relation's type
937
-     *
938
-     * @param string                  $relation_name
939
-     * @param \EE_Model_Relation_Base $relation_obj
940
-     * @return string
941
-     */
942
-    public static function get_related_entity_name($relation_name, $relation_obj)
943
-    {
944
-        if ($relation_obj instanceof \EE_Belongs_To_Relation) {
945
-            return strtolower($relation_name);
946
-        } else {
947
-            return \EEH_Inflector::pluralize_and_lower($relation_name);
948
-        }
949
-    }
950
-
951
-
952
-
953
-    /**
954
-     * Gets the one model object with the specified id for the specified model
955
-     *
956
-     * @param \EEM_Base        $model
957
-     * @param \WP_REST_Request $request
958
-     * @return array|\WP_Error
959
-     */
960
-    public function get_entity_from_model($model, $request)
961
-    {
962
-        $query_params = array(array($model->primary_key_name() => $request->get_param('id')), 'limit' => 1);
963
-        if ($model instanceof \EEM_Soft_Delete_Base) {
964
-            $query_params = $model->alter_query_params_so_deleted_and_undeleted_items_included($query_params);
965
-        }
966
-        $restricted_query_params = $query_params;
967
-        $restricted_query_params['caps'] = $this->validate_context($request->get_param('caps'));
968
-        $this->_set_debug_info('model query params', $restricted_query_params);
969
-        $model_rows = $model->get_all_wpdb_results($restricted_query_params);
970
-        if (! empty ($model_rows)) {
971
-            return $this->create_entity_from_wpdb_result(
972
-                $model,
973
-                array_shift($model_rows),
974
-                $request);
975
-        } else {
976
-            //ok let's test to see if we WOULD have found it, had we not had restrictions from missing capabilities
977
-            $lowercase_model_name = strtolower($model->get_this_model_name());
978
-            $model_rows_found_sans_restrictions = $model->get_all_wpdb_results($query_params);
979
-            if (! empty($model_rows_found_sans_restrictions)) {
980
-                //you got shafted- it existed but we didn't want to tell you!
981
-                return new \WP_Error(
982
-                    'rest_user_cannot_read',
983
-                    sprintf(
984
-                        __('Sorry, you cannot read this %1$s. Missing permissions are: %2$s', 'event_espresso'),
985
-                        strtolower($model->get_this_model_name()),
986
-                        Capabilities::get_missing_permissions_string(
987
-                            $model,
988
-                            $this->validate_context($request->get_param('caps')))
989
-                    ),
990
-                    array('status' => 403)
991
-                );
992
-            } else {
993
-                //it's not you. It just doesn't exist
994
-                return new \WP_Error(
995
-                    sprintf('rest_%s_invalid_id', $lowercase_model_name),
996
-                    sprintf(__('Invalid %s ID.', 'event_espresso'), $lowercase_model_name),
997
-                    array('status' => 404)
998
-                );
999
-            }
1000
-        }
1001
-    }
1002
-
1003
-
1004
-
1005
-    /**
1006
-     * If a context is provided which isn't valid, maybe it was added in a future
1007
-     * version so just treat it as a default read
1008
-     *
1009
-     * @param string $context
1010
-     * @return string array key of EEM_Base::cap_contexts_to_cap_action_map()
1011
-     */
1012
-    public function validate_context($context)
1013
-    {
1014
-        if (! $context) {
1015
-            $context = \EEM_Base::caps_read;
1016
-        }
1017
-        $valid_contexts = \EEM_Base::valid_cap_contexts();
1018
-        if (in_array($context, $valid_contexts)) {
1019
-            return $context;
1020
-        } else {
1021
-            return \EEM_Base::caps_read;
1022
-        }
1023
-    }
1024
-
1025
-
1026
-
1027
-    /**
1028
-     * Verifies the passed in value is an allowable default where conditions value.
1029
-     *
1030
-     * @param $default_query_params
1031
-     * @return string
1032
-     */
1033
-    public function validate_default_query_params($default_query_params)
1034
-    {
1035
-        $valid_default_where_conditions_for_api_calls = array(
1036
-            \EEM_Base::default_where_conditions_all,
1037
-            \EEM_Base::default_where_conditions_minimum_all,
1038
-            \EEM_Base::default_where_conditions_minimum_others,
1039
-        );
1040
-        if (! $default_query_params) {
1041
-            $default_query_params = \EEM_Base::default_where_conditions_all;
1042
-        }
1043
-        if (
1044
-        in_array(
1045
-            $default_query_params,
1046
-            $valid_default_where_conditions_for_api_calls,
1047
-            true
1048
-        )
1049
-        ) {
1050
-            return $default_query_params;
1051
-        } else {
1052
-            return \EEM_Base::default_where_conditions_all;
1053
-        }
1054
-    }
1055
-
1056
-
1057
-
1058
-    /**
1059
-     * Translates API filter get parameter into $query_params array used by EEM_Base::get_all().
1060
-     * Note: right now the query parameter keys for fields (and related fields)
1061
-     * can be left as-is, but it's quite possible this will change someday.
1062
-     * Also, this method's contents might be candidate for moving to Model_Data_Translator
1063
-     *
1064
-     * @param \EEM_Base $model
1065
-     * @param array     $query_parameters from $_GET parameter @see Read:handle_request_get_all
1066
-     * @return array like what EEM_Base::get_all() expects or FALSE to indicate
1067
-     *                                    that absolutely no results should be returned
1068
-     * @throws \EE_Error
1069
-     */
1070
-    public function create_model_query_params($model, $query_parameters)
1071
-    {
1072
-        $model_query_params = array();
1073
-        if (isset($query_parameters['where'])) {
1074
-            $model_query_params[0] = Model_Data_Translator::prepare_conditions_query_params_for_models(
1075
-                $query_parameters['where'],
1076
-                $model,
1077
-                $this->get_model_version_info()->requested_version()
1078
-            );
1079
-        }
1080
-        if (isset($query_parameters['order_by'])) {
1081
-            $order_by = $query_parameters['order_by'];
1082
-        } elseif (isset($query_parameters['orderby'])) {
1083
-            $order_by = $query_parameters['orderby'];
1084
-        } else {
1085
-            $order_by = null;
1086
-        }
1087
-        if ($order_by !== null) {
1088
-            if (is_array($order_by)) {
1089
-                $order_by = Model_Data_Translator::prepare_field_names_in_array_keys_from_json($order_by);
1090
-            } else {
1091
-                //it's a single item
1092
-                $order_by = Model_Data_Translator::prepare_field_name_from_json($order_by);
1093
-            }
1094
-            $model_query_params['order_by'] = $order_by;
1095
-        }
1096
-        if (isset($query_parameters['group_by'])) {
1097
-            $group_by = $query_parameters['group_by'];
1098
-        } elseif (isset($query_parameters['groupby'])) {
1099
-            $group_by = $query_parameters['groupby'];
1100
-        } else {
1101
-            $group_by = array_keys($model->get_combined_primary_key_fields());
1102
-        }
1103
-        //make sure they're all real names
1104
-        if (is_array($group_by)) {
1105
-            $group_by = Model_Data_Translator::prepare_field_names_from_json($group_by);
1106
-        }
1107
-        if ($group_by !== null) {
1108
-            $model_query_params['group_by'] = $group_by;
1109
-        }
1110
-        if (isset($query_parameters['having'])) {
1111
-            $model_query_params['having'] = Model_Data_Translator::prepare_conditions_query_params_for_models(
1112
-                $query_parameters['having'],
1113
-                $model,
1114
-                $this->get_model_version_info()->requested_version()
1115
-            );
1116
-        }
1117
-        if (isset($query_parameters['order'])) {
1118
-            $model_query_params['order'] = $query_parameters['order'];
1119
-        }
1120
-        if (isset($query_parameters['mine'])) {
1121
-            $model_query_params = $model->alter_query_params_to_only_include_mine($model_query_params);
1122
-        }
1123
-        if (isset($query_parameters['limit'])) {
1124
-            //limit should be either a string like '23' or '23,43', or an array with two items in it
1125
-            if (! is_array($query_parameters['limit'])) {
1126
-                $limit_array = explode(',', (string)$query_parameters['limit']);
1127
-            } else {
1128
-                $limit_array = $query_parameters['limit'];
1129
-            }
1130
-            $sanitized_limit = array();
1131
-            foreach ($limit_array as $key => $limit_part) {
1132
-                if ($this->_debug_mode && (! is_numeric($limit_part) || count($sanitized_limit) > 2)) {
1133
-                    throw new \EE_Error(
1134
-                        sprintf(
1135
-                            __('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.',
1136
-                                'event_espresso'),
1137
-                            wp_json_encode($query_parameters['limit'])
1138
-                        )
1139
-                    );
1140
-                }
1141
-                $sanitized_limit[] = (int)$limit_part;
1142
-            }
1143
-            $model_query_params['limit'] = implode(',', $sanitized_limit);
1144
-        } else {
1145
-            $model_query_params['limit'] = \EED_Core_Rest_Api::get_default_query_limit();
1146
-        }
1147
-        if (isset($query_parameters['caps'])) {
1148
-            $model_query_params['caps'] = $this->validate_context($query_parameters['caps']);
1149
-        } else {
1150
-            $model_query_params['caps'] = \EEM_Base::caps_read;
1151
-        }
1152
-        if (isset($query_parameters['default_where_conditions'])) {
1153
-            $model_query_params['default_where_conditions'] = $this->validate_default_query_params($query_parameters['default_where_conditions']);
1154
-        }
1155
-        return apply_filters('FHEE__Read__create_model_query_params', $model_query_params, $query_parameters, $model);
1156
-    }
1157
-
1158
-
1159
-
1160
-    /**
1161
-     * Changes the REST-style query params for use in the models
1162
-     *
1163
-     * @deprecated
1164
-     * @param \EEM_Base $model
1165
-     * @param array     $query_params sub-array from @see EEM_Base::get_all()
1166
-     * @return array
1167
-     */
1168
-    public function prepare_rest_query_params_key_for_models($model, $query_params)
1169
-    {
1170
-        $model_ready_query_params = array();
1171
-        foreach ($query_params as $key => $value) {
1172
-            if (is_array($value)) {
1173
-                $model_ready_query_params[$key] = $this->prepare_rest_query_params_key_for_models($model, $value);
1174
-            } else {
1175
-                $model_ready_query_params[$key] = $value;
1176
-            }
1177
-        }
1178
-        return $model_ready_query_params;
1179
-    }
1180
-
1181
-
1182
-
1183
-    /**
1184
-     * @deprecated
1185
-     * @param $model
1186
-     * @param $query_params
1187
-     * @return array
1188
-     */
1189
-    public function prepare_rest_query_params_values_for_models($model, $query_params)
1190
-    {
1191
-        $model_ready_query_params = array();
1192
-        foreach ($query_params as $key => $value) {
1193
-            if (is_array($value)) {
1194
-                $model_ready_query_params[$key] = $this->prepare_rest_query_params_values_for_models($model, $value);
1195
-            } else {
1196
-                $model_ready_query_params[$key] = $value;
1197
-            }
1198
-        }
1199
-        return $model_ready_query_params;
1200
-    }
1201
-
1202
-
1203
-
1204
-    /**
1205
-     * Explodes the string on commas, and only returns items with $prefix followed by a period.
1206
-     * If no prefix is specified, returns items with no period.
1207
-     *
1208
-     * @param string|array $string_to_explode eg "jibba,jabba, blah, blaabla" or array('jibba', 'jabba' )
1209
-     * @param string       $prefix            "Event" or "foobar"
1210
-     * @return array $string_to_exploded exploded on COMMAS, and if a prefix was specified
1211
-     *                                        we only return strings starting with that and a period; if no prefix was
1212
-     *                                        specified we return all items containing NO periods
1213
-     */
1214
-    public function explode_and_get_items_prefixed_with($string_to_explode, $prefix)
1215
-    {
1216
-        if (is_string($string_to_explode)) {
1217
-            $exploded_contents = explode(',', $string_to_explode);
1218
-        } else if (is_array($string_to_explode)) {
1219
-            $exploded_contents = $string_to_explode;
1220
-        } else {
1221
-            $exploded_contents = array();
1222
-        }
1223
-        //if the string was empty, we want an empty array
1224
-        $exploded_contents = array_filter($exploded_contents);
1225
-        $contents_with_prefix = array();
1226
-        foreach ($exploded_contents as $item) {
1227
-            $item = trim($item);
1228
-            //if no prefix was provided, so we look for items with no "." in them
1229
-            if (! $prefix) {
1230
-                //does this item have a period?
1231
-                if (strpos($item, '.') === false) {
1232
-                    //if not, then its what we're looking for
1233
-                    $contents_with_prefix[] = $item;
1234
-                }
1235
-            } else if (strpos($item, $prefix . '.') === 0) {
1236
-                //this item has the prefix and a period, grab it
1237
-                $contents_with_prefix[] = substr(
1238
-                    $item,
1239
-                    strpos($item, $prefix . '.') + strlen($prefix . '.')
1240
-                );
1241
-            } else if ($item === $prefix) {
1242
-                //this item is JUST the prefix
1243
-                //so let's grab everything after, which is a blank string
1244
-                $contents_with_prefix[] = '';
1245
-            }
1246
-        }
1247
-        return $contents_with_prefix;
1248
-    }
1249
-
1250
-
1251
-
1252
-    /**
1253
-     * @deprecated since 4.8.36.rc.001 You should instead use Read::explode_and_get_items_prefixed_with.
1254
-     * Deprecated because its return values were really quite confusing- sometimes it returned
1255
-     * an empty array (when the include string was blank or '*') or sometimes it returned
1256
-     * array('*') (when you provided a model and a model of that kind was found).
1257
-     * Parses the $include_string so we fetch all the field names relating to THIS model
1258
-     * (ie have NO period in them), or for the provided model (ie start with the model
1259
-     * name and then a period).
1260
-     * @param string $include_string @see Read:handle_request_get_all
1261
-     * @param string $model_name
1262
-     * @return array of fields for this model. If $model_name is provided, then
1263
-     *                               the fields for that model, with the model's name removed from each.
1264
-     *                               If $include_string was blank or '*' returns an empty array
1265
-     */
1266
-    public function extract_includes_for_this_model($include_string, $model_name = null)
1267
-    {
1268
-        if (is_array($include_string)) {
1269
-            $include_string = implode(',', $include_string);
1270
-        }
1271
-        if ($include_string === '*' || $include_string === '') {
1272
-            return array();
1273
-        }
1274
-        $includes = explode(',', $include_string);
1275
-        $extracted_fields_to_include = array();
1276
-        if ($model_name) {
1277
-            foreach ($includes as $field_to_include) {
1278
-                $field_to_include = trim($field_to_include);
1279
-                if (strpos($field_to_include, $model_name . '.') === 0) {
1280
-                    //found the model name at the exact start
1281
-                    $field_sans_model_name = str_replace($model_name . '.', '', $field_to_include);
1282
-                    $extracted_fields_to_include[] = $field_sans_model_name;
1283
-                } elseif ($field_to_include == $model_name) {
1284
-                    $extracted_fields_to_include[] = '*';
1285
-                }
1286
-            }
1287
-        } else {
1288
-            //look for ones with no period
1289
-            foreach ($includes as $field_to_include) {
1290
-                $field_to_include = trim($field_to_include);
1291
-                if (
1292
-                    strpos($field_to_include, '.') === false
1293
-                    && ! $this->get_model_version_info()->is_model_name_in_this_version($field_to_include)
1294
-                ) {
1295
-                    $extracted_fields_to_include[] = $field_to_include;
1296
-                }
1297
-            }
1298
-        }
1299
-        return $extracted_fields_to_include;
1300
-    }
30
+	/**
31
+	 * @var Calculated_Model_Fields
32
+	 */
33
+	protected $_fields_calculator;
34
+
35
+
36
+
37
+	/**
38
+	 * Read constructor.
39
+	 */
40
+	public function __construct()
41
+	{
42
+		parent::__construct();
43
+		$this->_fields_calculator = new Calculated_Model_Fields();
44
+	}
45
+
46
+
47
+
48
+	/**
49
+	 * Handles requests to get all (or a filtered subset) of entities for a particular model
50
+	 *
51
+	 * @param \WP_REST_Request $request
52
+	 * @return \WP_REST_Response|\WP_Error
53
+	 */
54
+	public static function handle_request_get_all(\WP_REST_Request $request)
55
+	{
56
+		$controller = new Read();
57
+		try {
58
+			$matches = $controller->parse_route(
59
+				$request->get_route(),
60
+				'~' . \EED_Core_Rest_Api::ee_api_namespace_for_regex . '(.*)~',
61
+				array('version', 'model')
62
+			);
63
+			$controller->set_requested_version($matches['version']);
64
+			$model_name_singular = \EEH_Inflector::singularize_and_upper($matches['model']);
65
+			if (! $controller->get_model_version_info()->is_model_name_in_this_version($model_name_singular)) {
66
+				return $controller->send_response(
67
+					new \WP_Error(
68
+						'endpoint_parsing_error',
69
+						sprintf(
70
+							__('There is no model for endpoint %s. Please contact event espresso support',
71
+								'event_espresso'),
72
+							$model_name_singular
73
+						)
74
+					)
75
+				);
76
+			}
77
+			return $controller->send_response(
78
+				$controller->get_entities_from_model(
79
+					$controller->get_model_version_info()->load_model($model_name_singular),
80
+					$request
81
+				)
82
+			);
83
+		} catch (\Exception $e) {
84
+			return $controller->send_response($e);
85
+		}
86
+	}
87
+
88
+
89
+
90
+	/**
91
+	 * Prepares and returns schema for any OPTIONS request.
92
+	 *
93
+	 * @param string $model_name Something like `Event` or `Registration`
94
+	 * @param string $version    The API endpoint version being used.
95
+	 * @return array
96
+	 */
97
+	public static function handle_schema_request($model_name, $version)
98
+	{
99
+		$controller = new Read();
100
+		try {
101
+			$controller->set_requested_version($version);
102
+			if (! $controller->get_model_version_info()->is_model_name_in_this_version($model_name)) {
103
+				return array();
104
+			}
105
+			//get the model for this version
106
+			$model = $controller->get_model_version_info()->load_model($model_name);
107
+			$model_schema = new JsonModelSchema($model);
108
+			return $model_schema->getModelSchemaForRelations(
109
+				$controller->get_model_version_info()->relation_settings($model),
110
+				$controller->_customize_schema_for_rest_response(
111
+					$model,
112
+					$model_schema->getModelSchemaForFields(
113
+						$controller->get_model_version_info()->fields_on_model_in_this_version($model),
114
+						$model_schema->getInitialSchemaStructure()
115
+					)
116
+				)
117
+			);
118
+		} catch (\Exception $e) {
119
+			return array();
120
+		}
121
+	}
122
+
123
+
124
+
125
+	/**
126
+	 * This loops through each field in the given schema for the model and does the following:
127
+	 * - add any extra fields that are REST API specific and related to existing fields.
128
+	 * - transform default values into the correct format for a REST API response.
129
+	 *
130
+	 * @param \EEM_Base $model
131
+	 * @param array     $schema
132
+	 * @return array  The final schema.
133
+	 */
134
+	protected function _customize_schema_for_rest_response(\EEM_Base $model, array $schema)
135
+	{
136
+		foreach ($this->get_model_version_info()->fields_on_model_in_this_version($model) as $field_name => $field) {
137
+			$schema = $this->_translate_defaults_for_rest_response(
138
+				$field_name,
139
+				$field,
140
+				$this->_maybe_add_extra_fields_to_schema($field_name, $field, $schema)
141
+			);
142
+		}
143
+		return $schema;
144
+	}
145
+
146
+
147
+
148
+	/**
149
+	 * This is used to ensure that the 'default' value set in the schema response is formatted correctly for the REST
150
+	 * response.
151
+	 *
152
+	 * @param                      $field_name
153
+	 * @param \EE_Model_Field_Base $field
154
+	 * @param array                $schema
155
+	 * @return array
156
+	 */
157
+	protected function _translate_defaults_for_rest_response($field_name, \EE_Model_Field_Base $field, array $schema)
158
+	{
159
+		if (isset($schema['properties'][$field_name]['default'])) {
160
+			if (is_array($schema['properties'][$field_name]['default'])) {
161
+				foreach ($schema['properties'][$field_name]['default'] as $default_key => $default_value) {
162
+					if ($default_key === 'raw') {
163
+						$schema['properties'][$field_name]['default'][$default_key] = Model_Data_Translator::prepare_field_value_for_json(
164
+							$field,
165
+							$default_value,
166
+							$this->get_model_version_info()->requested_version()
167
+						);
168
+					}
169
+				}
170
+			} else {
171
+				$schema['properties'][$field_name]['default'] = Model_Data_Translator::prepare_field_value_for_json(
172
+					$field,
173
+					$schema['properties'][$field_name]['default'],
174
+					$this->get_model_version_info()->requested_version()
175
+				);
176
+			}
177
+		}
178
+		return $schema;
179
+	}
180
+
181
+
182
+
183
+	/**
184
+	 * Adds additional fields to the schema
185
+	 * The REST API returns a GMT value field for each datetime field in the resource.  Thus the description about this
186
+	 * needs to be added to the schema.
187
+	 *
188
+	 * @param                      $field_name
189
+	 * @param \EE_Model_Field_Base $field
190
+	 * @param array                $schema
191
+	 * @return array
192
+	 */
193
+	protected function _maybe_add_extra_fields_to_schema($field_name, \EE_Model_Field_Base $field, array $schema)
194
+	{
195
+		if ($field instanceof EE_Datetime_Field) {
196
+			$schema['properties'][$field_name . '_gmt'] = $field->getSchema();
197
+			//modify the description
198
+			$schema['properties'][$field_name . '_gmt']['description'] = sprintf(
199
+				esc_html__('%s - the value for this field is in GMT.', 'event_espresso'),
200
+				$field->get_nicename()
201
+			);
202
+		}
203
+		return $schema;
204
+	}
205
+
206
+
207
+
208
+	/**
209
+	 * Used to figure out the route from the request when a `WP_REST_Request` object is not available
210
+	 *
211
+	 * @return string
212
+	 */
213
+	protected function get_route_from_request()
214
+	{
215
+		if (isset($GLOBALS['wp'])
216
+			&& $GLOBALS['wp'] instanceof \WP
217
+			&& isset($GLOBALS['wp']->query_vars['rest_route'])
218
+		) {
219
+			return $GLOBALS['wp']->query_vars['rest_route'];
220
+		} else {
221
+			return isset($_SERVER['PATH_INFO']) ? $_SERVER['PATH_INFO'] : '/';
222
+		}
223
+	}
224
+
225
+
226
+
227
+	/**
228
+	 * Gets a single entity related to the model indicated in the path and its id
229
+	 *
230
+	 * @param \WP_REST_Request $request
231
+	 * @return \WP_REST_Response|\WP_Error
232
+	 */
233
+	public static function handle_request_get_one(\WP_REST_Request $request)
234
+	{
235
+		$controller = new Read();
236
+		try {
237
+			$matches = $controller->parse_route(
238
+				$request->get_route(),
239
+				'~' . \EED_Core_Rest_Api::ee_api_namespace_for_regex . '(.*)/(.*)~',
240
+				array('version', 'model', 'id'));
241
+			$controller->set_requested_version($matches['version']);
242
+			$model_name_singular = \EEH_Inflector::singularize_and_upper($matches['model']);
243
+			if (! $controller->get_model_version_info()->is_model_name_in_this_version($model_name_singular)) {
244
+				return $controller->send_response(
245
+					new \WP_Error(
246
+						'endpoint_parsing_error',
247
+						sprintf(
248
+							__('There is no model for endpoint %s. Please contact event espresso support',
249
+								'event_espresso'),
250
+							$model_name_singular
251
+						)
252
+					)
253
+				);
254
+			}
255
+			return $controller->send_response(
256
+				$controller->get_entity_from_model(
257
+					$controller->get_model_version_info()->load_model($model_name_singular),
258
+					$request
259
+				)
260
+			);
261
+		} catch (\Exception $e) {
262
+			return $controller->send_response($e);
263
+		}
264
+	}
265
+
266
+
267
+
268
+	/**
269
+	 * Gets all the related entities (or if its a belongs-to relation just the one)
270
+	 * to the item with the given id
271
+	 *
272
+	 * @param \WP_REST_Request $request
273
+	 * @return \WP_REST_Response|\WP_Error
274
+	 */
275
+	public static function handle_request_get_related(\WP_REST_Request $request)
276
+	{
277
+		$controller = new Read();
278
+		try {
279
+			$matches = $controller->parse_route(
280
+				$request->get_route(),
281
+				'~' . \EED_Core_Rest_Api::ee_api_namespace_for_regex . '(.*)/(.*)/(.*)~',
282
+				array('version', 'model', 'id', 'related_model')
283
+			);
284
+			$controller->set_requested_version($matches['version']);
285
+			$main_model_name_singular = \EEH_Inflector::singularize_and_upper($matches['model']);
286
+			if (! $controller->get_model_version_info()->is_model_name_in_this_version($main_model_name_singular)) {
287
+				return $controller->send_response(
288
+					new \WP_Error(
289
+						'endpoint_parsing_error',
290
+						sprintf(
291
+							__('There is no model for endpoint %s. Please contact event espresso support',
292
+								'event_espresso'),
293
+							$main_model_name_singular
294
+						)
295
+					)
296
+				);
297
+			}
298
+			$main_model = $controller->get_model_version_info()->load_model($main_model_name_singular);
299
+			//assume the related model name is plural and try to find the model's name
300
+			$related_model_name_singular = \EEH_Inflector::singularize_and_upper($matches['related_model']);
301
+			if (! $controller->get_model_version_info()->is_model_name_in_this_version($related_model_name_singular)) {
302
+				//so the word didn't singularize well. Maybe that's just because it's a singular word?
303
+				$related_model_name_singular = \EEH_Inflector::humanize($matches['related_model']);
304
+			}
305
+			if (! $controller->get_model_version_info()->is_model_name_in_this_version($related_model_name_singular)) {
306
+				return $controller->send_response(
307
+					new \WP_Error(
308
+						'endpoint_parsing_error',
309
+						sprintf(
310
+							__('There is no model for endpoint %s. Please contact event espresso support',
311
+								'event_espresso'),
312
+							$related_model_name_singular
313
+						)
314
+					)
315
+				);
316
+			}
317
+			return $controller->send_response(
318
+				$controller->get_entities_from_relation(
319
+					$request->get_param('id'),
320
+					$main_model->related_settings_for($related_model_name_singular),
321
+					$request
322
+				)
323
+			);
324
+		} catch (\Exception $e) {
325
+			return $controller->send_response($e);
326
+		}
327
+	}
328
+
329
+
330
+
331
+	/**
332
+	 * Gets a collection for the given model and filters
333
+	 *
334
+	 * @param \EEM_Base        $model
335
+	 * @param \WP_REST_Request $request
336
+	 * @return array|\WP_Error
337
+	 */
338
+	public function get_entities_from_model($model, $request)
339
+	{
340
+		$query_params = $this->create_model_query_params($model, $request->get_params());
341
+		if (! Capabilities::current_user_has_partial_access_to($model, $query_params['caps'])) {
342
+			$model_name_plural = \EEH_Inflector::pluralize_and_lower($model->get_this_model_name());
343
+			return new \WP_Error(
344
+				sprintf('rest_%s_cannot_list', $model_name_plural),
345
+				sprintf(
346
+					__('Sorry, you are not allowed to list %1$s. Missing permissions: %2$s', 'event_espresso'),
347
+					$model_name_plural,
348
+					Capabilities::get_missing_permissions_string($model, $query_params['caps'])
349
+				),
350
+				array('status' => 403)
351
+			);
352
+		}
353
+		if (! $request->get_header('no_rest_headers')) {
354
+			$this->_set_headers_from_query_params($model, $query_params);
355
+		}
356
+		/** @type array $results */
357
+		$results = $model->get_all_wpdb_results($query_params);
358
+		$nice_results = array();
359
+		foreach ($results as $result) {
360
+			$nice_results[] = $this->create_entity_from_wpdb_result(
361
+				$model,
362
+				$result,
363
+				$request
364
+			);
365
+		}
366
+		return $nice_results;
367
+	}
368
+
369
+
370
+
371
+	/**
372
+	 * @param array                   $primary_model_query_params query params for finding the item from which
373
+	 *                                                            relations will be based
374
+	 * @param \EE_Model_Relation_Base $relation
375
+	 * @param \WP_REST_Request        $request
376
+	 * @return \WP_Error|array
377
+	 */
378
+	protected function _get_entities_from_relation($primary_model_query_params, $relation, $request)
379
+	{
380
+		$context = $this->validate_context($request->get_param('caps'));
381
+		$model = $relation->get_this_model();
382
+		$related_model = $relation->get_other_model();
383
+		if (! isset($primary_model_query_params[0])) {
384
+			$primary_model_query_params[0] = array();
385
+		}
386
+		//check if they can access the 1st model object
387
+		$primary_model_query_params = array(
388
+			0       => $primary_model_query_params[0],
389
+			'limit' => 1,
390
+		);
391
+		if ($model instanceof \EEM_Soft_Delete_Base) {
392
+			$primary_model_query_params = $model->alter_query_params_so_deleted_and_undeleted_items_included($primary_model_query_params);
393
+		}
394
+		$restricted_query_params = $primary_model_query_params;
395
+		$restricted_query_params['caps'] = $context;
396
+		$this->_set_debug_info('main model query params', $restricted_query_params);
397
+		$this->_set_debug_info('missing caps', Capabilities::get_missing_permissions_string($related_model, $context));
398
+		if (
399
+		! (
400
+			Capabilities::current_user_has_partial_access_to($related_model, $context)
401
+			&& $model->exists($restricted_query_params)
402
+		)
403
+		) {
404
+			if ($relation instanceof \EE_Belongs_To_Relation) {
405
+				$related_model_name_maybe_plural = strtolower($related_model->get_this_model_name());
406
+			} else {
407
+				$related_model_name_maybe_plural = \EEH_Inflector::pluralize_and_lower($related_model->get_this_model_name());
408
+			}
409
+			return new \WP_Error(
410
+				sprintf('rest_%s_cannot_list', $related_model_name_maybe_plural),
411
+				sprintf(
412
+					__('Sorry, you are not allowed to list %1$s related to %2$s. Missing permissions: %3$s',
413
+						'event_espresso'),
414
+					$related_model_name_maybe_plural,
415
+					$relation->get_this_model()->get_this_model_name(),
416
+					implode(
417
+						',',
418
+						array_keys(
419
+							Capabilities::get_missing_permissions($related_model, $context)
420
+						)
421
+					)
422
+				),
423
+				array('status' => 403)
424
+			);
425
+		}
426
+		$query_params = $this->create_model_query_params($relation->get_other_model(), $request->get_params());
427
+		foreach ($primary_model_query_params[0] as $where_condition_key => $where_condition_value) {
428
+			$query_params[0][$relation->get_this_model()->get_this_model_name()
429
+							 . '.'
430
+							 . $where_condition_key] = $where_condition_value;
431
+		}
432
+		$query_params['default_where_conditions'] = 'none';
433
+		$query_params['caps'] = $context;
434
+		if (! $request->get_header('no_rest_headers')) {
435
+			$this->_set_headers_from_query_params($relation->get_other_model(), $query_params);
436
+		}
437
+		/** @type array $results */
438
+		$results = $relation->get_other_model()->get_all_wpdb_results($query_params);
439
+		$nice_results = array();
440
+		foreach ($results as $result) {
441
+			$nice_result = $this->create_entity_from_wpdb_result(
442
+				$relation->get_other_model(),
443
+				$result,
444
+				$request
445
+			);
446
+			if ($relation instanceof \EE_HABTM_Relation) {
447
+				//put the unusual stuff (properties from the HABTM relation) first, and make sure
448
+				//if there are conflicts we prefer the properties from the main model
449
+				$join_model_result = $this->create_entity_from_wpdb_result(
450
+					$relation->get_join_model(),
451
+					$result,
452
+					$request
453
+				);
454
+				$joined_result = array_merge($nice_result, $join_model_result);
455
+				//but keep the meta stuff from the main model
456
+				if (isset($nice_result['meta'])) {
457
+					$joined_result['meta'] = $nice_result['meta'];
458
+				}
459
+				$nice_result = $joined_result;
460
+			}
461
+			$nice_results[] = $nice_result;
462
+		}
463
+		if ($relation instanceof \EE_Belongs_To_Relation) {
464
+			return array_shift($nice_results);
465
+		} else {
466
+			return $nice_results;
467
+		}
468
+	}
469
+
470
+
471
+
472
+	/**
473
+	 * Gets the collection for given relation object
474
+	 * The same as Read::get_entities_from_model(), except if the relation
475
+	 * is a HABTM relation, in which case it merges any non-foreign-key fields from
476
+	 * the join-model-object into the results
477
+	 *
478
+	 * @param string                  $id the ID of the thing we are fetching related stuff from
479
+	 * @param \EE_Model_Relation_Base $relation
480
+	 * @param \WP_REST_Request        $request
481
+	 * @return array|\WP_Error
482
+	 * @throws \EE_Error
483
+	 */
484
+	public function get_entities_from_relation($id, $relation, $request)
485
+	{
486
+		if (! $relation->get_this_model()->has_primary_key_field()) {
487
+			throw new \EE_Error(
488
+				sprintf(
489
+					__('Read::get_entities_from_relation should only be called from a model with a primary key, it was called from %1$s',
490
+						'event_espresso'),
491
+					$relation->get_this_model()->get_this_model_name()
492
+				)
493
+			);
494
+		}
495
+		return $this->_get_entities_from_relation(
496
+			array(
497
+				array(
498
+					$relation->get_this_model()->primary_key_name() => $id,
499
+				),
500
+			),
501
+			$relation,
502
+			$request
503
+		);
504
+	}
505
+
506
+
507
+
508
+	/**
509
+	 * Sets the headers that are based on the model and query params,
510
+	 * like the total records. This should only be called on the original request
511
+	 * from the client, not on subsequent internal
512
+	 *
513
+	 * @param \EEM_Base $model
514
+	 * @param array     $query_params
515
+	 * @return void
516
+	 */
517
+	protected function _set_headers_from_query_params($model, $query_params)
518
+	{
519
+		$this->_set_debug_info('model query params', $query_params);
520
+		$this->_set_debug_info('missing caps',
521
+			Capabilities::get_missing_permissions_string($model, $query_params['caps']));
522
+		//normally the limit to a 2-part array, where the 2nd item is the limit
523
+		if (! isset($query_params['limit'])) {
524
+			$query_params['limit'] = \EED_Core_Rest_Api::get_default_query_limit();
525
+		}
526
+		if (is_array($query_params['limit'])) {
527
+			$limit_parts = $query_params['limit'];
528
+		} else {
529
+			$limit_parts = explode(',', $query_params['limit']);
530
+			if (count($limit_parts) == 1) {
531
+				$limit_parts = array(0, $limit_parts[0]);
532
+			}
533
+		}
534
+		//remove the group by and having parts of the query, as those will
535
+		//make the sql query return an array of values, instead of just a single value
536
+		unset($query_params['group_by'], $query_params['having'], $query_params['limit']);
537
+		$count = $model->count($query_params, null, true);
538
+		$pages = $count / $limit_parts[1];
539
+		$this->_set_response_header('Total', $count, false);
540
+		$this->_set_response_header('PageSize', $limit_parts[1], false);
541
+		$this->_set_response_header('TotalPages', ceil($pages), false);
542
+	}
543
+
544
+
545
+
546
+	/**
547
+	 * Changes database results into REST API entities
548
+	 *
549
+	 * @param \EEM_Base        $model
550
+	 * @param array            $db_row     like results from $wpdb->get_results()
551
+	 * @param \WP_REST_Request $rest_request
552
+	 * @param string           $deprecated no longer used
553
+	 * @return array ready for being converted into json for sending to client
554
+	 */
555
+	public function create_entity_from_wpdb_result($model, $db_row, $rest_request, $deprecated = null)
556
+	{
557
+		if (! $rest_request instanceof \WP_REST_Request) {
558
+			//ok so this was called in the old style, where the 3rd arg was
559
+			//$include, and the 4th arg was $context
560
+			//now setup the request just to avoid fatal errors, although we won't be able
561
+			//to truly make use of it because it's kinda devoid of info
562
+			$rest_request = new \WP_REST_Request();
563
+			$rest_request->set_param('include', $rest_request);
564
+			$rest_request->set_param('caps', $deprecated);
565
+		}
566
+		if ($rest_request->get_param('caps') == null) {
567
+			$rest_request->set_param('caps', \EEM_Base::caps_read);
568
+		}
569
+		$entity_array = $this->_create_bare_entity_from_wpdb_results($model, $db_row);
570
+		$entity_array = $this->_add_extra_fields($model, $db_row, $entity_array);
571
+		$entity_array['_links'] = $this->_get_entity_links($model, $db_row, $entity_array);
572
+		$entity_array['_calculated_fields'] = $this->_get_entity_calculations($model, $db_row, $rest_request);
573
+		$entity_array = apply_filters('FHEE__Read__create_entity_from_wpdb_results__entity_before_including_requested_models',
574
+			$entity_array, $model, $rest_request->get_param('caps'), $rest_request, $this);
575
+		$entity_array = $this->_include_requested_models($model, $rest_request, $entity_array, $db_row);
576
+		$entity_array = apply_filters(
577
+			'FHEE__Read__create_entity_from_wpdb_results__entity_before_inaccessible_field_removal',
578
+			$entity_array,
579
+			$model,
580
+			$rest_request->get_param('caps'),
581
+			$rest_request,
582
+			$this
583
+		);
584
+		$result_without_inaccessible_fields = Capabilities::filter_out_inaccessible_entity_fields(
585
+			$entity_array,
586
+			$model,
587
+			$rest_request->get_param('caps'),
588
+			$this->get_model_version_info(),
589
+			$model->get_index_primary_key_string(
590
+				$model->deduce_fields_n_values_from_cols_n_values($db_row)
591
+			)
592
+		);
593
+		$this->_set_debug_info(
594
+			'inaccessible fields',
595
+			array_keys(array_diff_key($entity_array, $result_without_inaccessible_fields))
596
+		);
597
+		return apply_filters(
598
+			'FHEE__Read__create_entity_from_wpdb_results__entity_return',
599
+			$result_without_inaccessible_fields,
600
+			$model,
601
+			$rest_request->get_param('caps')
602
+		);
603
+	}
604
+
605
+
606
+
607
+	/**
608
+	 * Creates a REST entity array (JSON object we're going to return in the response, but
609
+	 * for now still a PHP array, but soon enough we'll call json_encode on it, don't worry),
610
+	 * from $wpdb->get_row( $sql, ARRAY_A)
611
+	 *
612
+	 * @param \EEM_Base $model
613
+	 * @param array     $db_row
614
+	 * @return array entity mostly ready for converting to JSON and sending in the response
615
+	 */
616
+	protected function _create_bare_entity_from_wpdb_results(\EEM_Base $model, $db_row)
617
+	{
618
+		$result = $model->deduce_fields_n_values_from_cols_n_values($db_row);
619
+		$result = array_intersect_key($result,
620
+			$this->get_model_version_info()->fields_on_model_in_this_version($model));
621
+		//if this is a CPT, we need to set the global $post to it,
622
+		//otherwise shortcodes etc won't work properly while rendering it
623
+		if ($model instanceof \EEM_CPT_Base) {
624
+			$do_chevy_shuffle = true;
625
+		} else {
626
+			$do_chevy_shuffle = false;
627
+		}
628
+		if ($do_chevy_shuffle) {
629
+			global $post;
630
+			$old_post = $post;
631
+			$post = get_post($result[$model->primary_key_name()]);
632
+			if (! $post instanceof \WP_Post) {
633
+				//well that's weird, because $result is what we JUST fetched from the database
634
+				throw new Rest_Exception(
635
+					'error_fetching_post_from_database_results',
636
+					esc_html__(
637
+						'An item was retrieved from the database but it\'s not a WP_Post like it should be.',
638
+						'event_espresso'
639
+					)
640
+				);
641
+			}
642
+			$model_object_classname = 'EE_' . $model->get_this_model_name();
643
+			$post->{$model_object_classname} = \EE_Registry::instance()->load_class(
644
+				$model_object_classname,
645
+				$result,
646
+				false,
647
+				false
648
+				);
649
+		}
650
+		foreach ($result as $field_name => $raw_field_value) {
651
+			$field_obj = $model->field_settings_for($field_name);
652
+			$field_value = $field_obj->prepare_for_set_from_db($raw_field_value);
653
+			if ($this->is_subclass_of_one($field_obj, $this->get_model_version_info()->fields_ignored())) {
654
+				unset($result[$field_name]);
655
+			} elseif (
656
+			$this->is_subclass_of_one($field_obj, $this->get_model_version_info()->fields_that_have_rendered_format())
657
+			) {
658
+				$result[$field_name] = array(
659
+					'raw'      => $field_obj->prepare_for_get($field_value),
660
+					'rendered' => $field_obj->prepare_for_pretty_echoing($field_value),
661
+				);
662
+			} elseif (
663
+			$this->is_subclass_of_one($field_obj, $this->get_model_version_info()->fields_that_have_pretty_format())
664
+			) {
665
+				$result[$field_name] = array(
666
+					'raw'    => $field_obj->prepare_for_get($field_value),
667
+					'pretty' => $field_obj->prepare_for_pretty_echoing($field_value),
668
+				);
669
+			} elseif ($field_obj instanceof \EE_Datetime_Field) {
670
+				if ($field_value instanceof \DateTime) {
671
+					$timezone = $field_value->getTimezone();
672
+					$field_value->setTimezone(new \DateTimeZone('UTC'));
673
+					$result[$field_name . '_gmt'] = Model_Data_Translator::prepare_field_value_for_json(
674
+						$field_obj,
675
+						$field_value,
676
+						$this->get_model_version_info()->requested_version()
677
+					);
678
+					$field_value->setTimezone($timezone);
679
+					$result[$field_name] = Model_Data_Translator::prepare_field_value_for_json(
680
+						$field_obj,
681
+						$field_value,
682
+						$this->get_model_version_info()->requested_version()
683
+					);
684
+				}
685
+			} else {
686
+				$result[$field_name] = Model_Data_Translator::prepare_field_value_for_json(
687
+					$field_obj,
688
+					$field_obj->prepare_for_get($field_value),
689
+					$this->get_model_version_info()->requested_version()
690
+				);
691
+			}
692
+		}
693
+		if ($do_chevy_shuffle) {
694
+			$post = $old_post;
695
+		}
696
+		return $result;
697
+	}
698
+
699
+
700
+
701
+	/**
702
+	 * Adds a few extra fields to the entity response
703
+	 *
704
+	 * @param \EEM_Base $model
705
+	 * @param array     $db_row
706
+	 * @param array     $entity_array
707
+	 * @return array modified entity
708
+	 */
709
+	protected function _add_extra_fields(\EEM_Base $model, $db_row, $entity_array)
710
+	{
711
+		if ($model instanceof \EEM_CPT_Base) {
712
+			$entity_array['link'] = get_permalink($db_row[$model->get_primary_key_field()->get_qualified_column()]);
713
+		}
714
+		return $entity_array;
715
+	}
716
+
717
+
718
+
719
+	/**
720
+	 * Gets links we want to add to the response
721
+	 *
722
+	 * @global \WP_REST_Server $wp_rest_server
723
+	 * @param \EEM_Base        $model
724
+	 * @param array            $db_row
725
+	 * @param array            $entity_array
726
+	 * @return array the _links item in the entity
727
+	 */
728
+	protected function _get_entity_links($model, $db_row, $entity_array)
729
+	{
730
+		//add basic links
731
+		$links = array();
732
+		if ($model->has_primary_key_field()) {
733
+			$links['self'] = array(
734
+				array(
735
+					'href' => $this->get_versioned_link_to(
736
+						\EEH_Inflector::pluralize_and_lower($model->get_this_model_name())
737
+						. '/'
738
+						. $entity_array[$model->primary_key_name()]
739
+					),
740
+				),
741
+			);
742
+		}
743
+		$links['collection'] = array(
744
+			array(
745
+				'href' => $this->get_versioned_link_to(
746
+					\EEH_Inflector::pluralize_and_lower($model->get_this_model_name())
747
+				),
748
+			),
749
+		);
750
+		//add links to related models
751
+		if ($model->has_primary_key_field()) {
752
+			foreach ($this->get_model_version_info()->relation_settings($model) as $relation_name => $relation_obj) {
753
+				$related_model_part = Read::get_related_entity_name($relation_name, $relation_obj);
754
+				$links[\EED_Core_Rest_Api::ee_api_link_namespace . $related_model_part] = array(
755
+					array(
756
+						'href'   => $this->get_versioned_link_to(
757
+							\EEH_Inflector::pluralize_and_lower($model->get_this_model_name())
758
+							. '/'
759
+							. $entity_array[$model->primary_key_name()]
760
+							. '/'
761
+							. $related_model_part
762
+						),
763
+						'single' => $relation_obj instanceof \EE_Belongs_To_Relation ? true : false,
764
+					),
765
+				);
766
+			}
767
+		}
768
+		return $links;
769
+	}
770
+
771
+
772
+
773
+	/**
774
+	 * Adds the included models indicated in the request to the entity provided
775
+	 *
776
+	 * @param \EEM_Base        $model
777
+	 * @param \WP_REST_Request $rest_request
778
+	 * @param array            $entity_array
779
+	 * @param array            $db_row
780
+	 * @return array the modified entity
781
+	 */
782
+	protected function _include_requested_models(
783
+		\EEM_Base $model,
784
+		\WP_REST_Request $rest_request,
785
+		$entity_array,
786
+		$db_row = array()
787
+	) {
788
+		//if $db_row not included, hope the entity array has what we need
789
+		if (! $db_row) {
790
+			$db_row = $entity_array;
791
+		}
792
+		$includes_for_this_model = $this->explode_and_get_items_prefixed_with($rest_request->get_param('include'), '');
793
+		$includes_for_this_model = $this->_remove_model_names_from_array($includes_for_this_model);
794
+		//if they passed in * or didn't specify any includes, return everything
795
+		if (! in_array('*', $includes_for_this_model)
796
+			&& ! empty($includes_for_this_model)
797
+		) {
798
+			if ($model->has_primary_key_field()) {
799
+				//always include the primary key. ya just gotta know that at least
800
+				$includes_for_this_model[] = $model->primary_key_name();
801
+			}
802
+			if ($this->explode_and_get_items_prefixed_with($rest_request->get_param('calculate'), '')) {
803
+				$includes_for_this_model[] = '_calculated_fields';
804
+			}
805
+			$entity_array = array_intersect_key($entity_array, array_flip($includes_for_this_model));
806
+		}
807
+		$relation_settings = $this->get_model_version_info()->relation_settings($model);
808
+		foreach ($relation_settings as $relation_name => $relation_obj) {
809
+			$related_fields_to_include = $this->explode_and_get_items_prefixed_with(
810
+				$rest_request->get_param('include'),
811
+				$relation_name
812
+			);
813
+			$related_fields_to_calculate = $this->explode_and_get_items_prefixed_with(
814
+				$rest_request->get_param('calculate'),
815
+				$relation_name
816
+			);
817
+			//did they specify they wanted to include a related model, or
818
+			//specific fields from a related model?
819
+			//or did they specify to calculate a field from a related model?
820
+			if ($related_fields_to_include || $related_fields_to_calculate) {
821
+				//if so, we should include at least some part of the related model
822
+				$pretend_related_request = new \WP_REST_Request();
823
+				$pretend_related_request->set_query_params(
824
+					array(
825
+						'caps'      => $rest_request->get_param('caps'),
826
+						'include'   => $related_fields_to_include,
827
+						'calculate' => $related_fields_to_calculate,
828
+					)
829
+				);
830
+				$pretend_related_request->add_header('no_rest_headers', true);
831
+				$primary_model_query_params = $model->alter_query_params_to_restrict_by_ID(
832
+					$model->get_index_primary_key_string(
833
+						$model->deduce_fields_n_values_from_cols_n_values($db_row)
834
+					)
835
+				);
836
+				$related_results = $this->_get_entities_from_relation(
837
+					$primary_model_query_params,
838
+					$relation_obj,
839
+					$pretend_related_request
840
+				);
841
+				$entity_array[Read::get_related_entity_name($relation_name, $relation_obj)] = $related_results
842
+																							  instanceof
843
+																							  \WP_Error
844
+					? null
845
+					: $related_results;
846
+			}
847
+		}
848
+		return $entity_array;
849
+	}
850
+
851
+
852
+
853
+	/**
854
+	 * Returns a new array with all the names of models removed. Eg
855
+	 * array( 'Event', 'Datetime.*', 'foobar' ) would become array( 'Datetime.*', 'foobar' )
856
+	 *
857
+	 * @param array $arr
858
+	 * @return array
859
+	 */
860
+	private function _remove_model_names_from_array($arr)
861
+	{
862
+		return array_diff($arr, array_keys(\EE_Registry::instance()->non_abstract_db_models));
863
+	}
864
+
865
+
866
+
867
+	/**
868
+	 * Gets the calculated fields for the response
869
+	 *
870
+	 * @param \EEM_Base        $model
871
+	 * @param array            $wpdb_row
872
+	 * @param \WP_REST_Request $rest_request
873
+	 * @return \stdClass the _calculations item in the entity
874
+	 */
875
+	protected function _get_entity_calculations($model, $wpdb_row, $rest_request)
876
+	{
877
+		$calculated_fields = $this->explode_and_get_items_prefixed_with(
878
+			$rest_request->get_param('calculate'),
879
+			''
880
+		);
881
+		//note: setting calculate=* doesn't do anything
882
+		$calculated_fields_to_return = new \stdClass();
883
+		foreach ($calculated_fields as $field_to_calculate) {
884
+			try {
885
+				$calculated_fields_to_return->$field_to_calculate = Model_Data_Translator::prepare_field_value_for_json(
886
+					null,
887
+					$this->_fields_calculator->retrieve_calculated_field_value(
888
+						$model,
889
+						$field_to_calculate,
890
+						$wpdb_row,
891
+						$rest_request,
892
+						$this
893
+					),
894
+					$this->get_model_version_info()->requested_version()
895
+				);
896
+			} catch (Rest_Exception $e) {
897
+				//if we don't have permission to read it, just leave it out. but let devs know about the problem
898
+				$this->_set_response_header(
899
+					'Notices-Field-Calculation-Errors['
900
+					. $e->get_string_code()
901
+					. ']['
902
+					. $model->get_this_model_name()
903
+					. ']['
904
+					. $field_to_calculate
905
+					. ']',
906
+					$e->getMessage(),
907
+					true
908
+				);
909
+			}
910
+		}
911
+		return $calculated_fields_to_return;
912
+	}
913
+
914
+
915
+
916
+	/**
917
+	 * Gets the full URL to the resource, taking the requested version into account
918
+	 *
919
+	 * @param string $link_part_after_version_and_slash eg "events/10/datetimes"
920
+	 * @return string url eg "http://mysite.com/wp-json/ee/v4.6/events/10/datetimes"
921
+	 */
922
+	public function get_versioned_link_to($link_part_after_version_and_slash)
923
+	{
924
+		return rest_url(
925
+			\EED_Core_Rest_Api::ee_api_namespace
926
+			. $this->get_model_version_info()->requested_version()
927
+			. '/'
928
+			. $link_part_after_version_and_slash
929
+		);
930
+	}
931
+
932
+
933
+
934
+	/**
935
+	 * Gets the correct lowercase name for the relation in the API according
936
+	 * to the relation's type
937
+	 *
938
+	 * @param string                  $relation_name
939
+	 * @param \EE_Model_Relation_Base $relation_obj
940
+	 * @return string
941
+	 */
942
+	public static function get_related_entity_name($relation_name, $relation_obj)
943
+	{
944
+		if ($relation_obj instanceof \EE_Belongs_To_Relation) {
945
+			return strtolower($relation_name);
946
+		} else {
947
+			return \EEH_Inflector::pluralize_and_lower($relation_name);
948
+		}
949
+	}
950
+
951
+
952
+
953
+	/**
954
+	 * Gets the one model object with the specified id for the specified model
955
+	 *
956
+	 * @param \EEM_Base        $model
957
+	 * @param \WP_REST_Request $request
958
+	 * @return array|\WP_Error
959
+	 */
960
+	public function get_entity_from_model($model, $request)
961
+	{
962
+		$query_params = array(array($model->primary_key_name() => $request->get_param('id')), 'limit' => 1);
963
+		if ($model instanceof \EEM_Soft_Delete_Base) {
964
+			$query_params = $model->alter_query_params_so_deleted_and_undeleted_items_included($query_params);
965
+		}
966
+		$restricted_query_params = $query_params;
967
+		$restricted_query_params['caps'] = $this->validate_context($request->get_param('caps'));
968
+		$this->_set_debug_info('model query params', $restricted_query_params);
969
+		$model_rows = $model->get_all_wpdb_results($restricted_query_params);
970
+		if (! empty ($model_rows)) {
971
+			return $this->create_entity_from_wpdb_result(
972
+				$model,
973
+				array_shift($model_rows),
974
+				$request);
975
+		} else {
976
+			//ok let's test to see if we WOULD have found it, had we not had restrictions from missing capabilities
977
+			$lowercase_model_name = strtolower($model->get_this_model_name());
978
+			$model_rows_found_sans_restrictions = $model->get_all_wpdb_results($query_params);
979
+			if (! empty($model_rows_found_sans_restrictions)) {
980
+				//you got shafted- it existed but we didn't want to tell you!
981
+				return new \WP_Error(
982
+					'rest_user_cannot_read',
983
+					sprintf(
984
+						__('Sorry, you cannot read this %1$s. Missing permissions are: %2$s', 'event_espresso'),
985
+						strtolower($model->get_this_model_name()),
986
+						Capabilities::get_missing_permissions_string(
987
+							$model,
988
+							$this->validate_context($request->get_param('caps')))
989
+					),
990
+					array('status' => 403)
991
+				);
992
+			} else {
993
+				//it's not you. It just doesn't exist
994
+				return new \WP_Error(
995
+					sprintf('rest_%s_invalid_id', $lowercase_model_name),
996
+					sprintf(__('Invalid %s ID.', 'event_espresso'), $lowercase_model_name),
997
+					array('status' => 404)
998
+				);
999
+			}
1000
+		}
1001
+	}
1002
+
1003
+
1004
+
1005
+	/**
1006
+	 * If a context is provided which isn't valid, maybe it was added in a future
1007
+	 * version so just treat it as a default read
1008
+	 *
1009
+	 * @param string $context
1010
+	 * @return string array key of EEM_Base::cap_contexts_to_cap_action_map()
1011
+	 */
1012
+	public function validate_context($context)
1013
+	{
1014
+		if (! $context) {
1015
+			$context = \EEM_Base::caps_read;
1016
+		}
1017
+		$valid_contexts = \EEM_Base::valid_cap_contexts();
1018
+		if (in_array($context, $valid_contexts)) {
1019
+			return $context;
1020
+		} else {
1021
+			return \EEM_Base::caps_read;
1022
+		}
1023
+	}
1024
+
1025
+
1026
+
1027
+	/**
1028
+	 * Verifies the passed in value is an allowable default where conditions value.
1029
+	 *
1030
+	 * @param $default_query_params
1031
+	 * @return string
1032
+	 */
1033
+	public function validate_default_query_params($default_query_params)
1034
+	{
1035
+		$valid_default_where_conditions_for_api_calls = array(
1036
+			\EEM_Base::default_where_conditions_all,
1037
+			\EEM_Base::default_where_conditions_minimum_all,
1038
+			\EEM_Base::default_where_conditions_minimum_others,
1039
+		);
1040
+		if (! $default_query_params) {
1041
+			$default_query_params = \EEM_Base::default_where_conditions_all;
1042
+		}
1043
+		if (
1044
+		in_array(
1045
+			$default_query_params,
1046
+			$valid_default_where_conditions_for_api_calls,
1047
+			true
1048
+		)
1049
+		) {
1050
+			return $default_query_params;
1051
+		} else {
1052
+			return \EEM_Base::default_where_conditions_all;
1053
+		}
1054
+	}
1055
+
1056
+
1057
+
1058
+	/**
1059
+	 * Translates API filter get parameter into $query_params array used by EEM_Base::get_all().
1060
+	 * Note: right now the query parameter keys for fields (and related fields)
1061
+	 * can be left as-is, but it's quite possible this will change someday.
1062
+	 * Also, this method's contents might be candidate for moving to Model_Data_Translator
1063
+	 *
1064
+	 * @param \EEM_Base $model
1065
+	 * @param array     $query_parameters from $_GET parameter @see Read:handle_request_get_all
1066
+	 * @return array like what EEM_Base::get_all() expects or FALSE to indicate
1067
+	 *                                    that absolutely no results should be returned
1068
+	 * @throws \EE_Error
1069
+	 */
1070
+	public function create_model_query_params($model, $query_parameters)
1071
+	{
1072
+		$model_query_params = array();
1073
+		if (isset($query_parameters['where'])) {
1074
+			$model_query_params[0] = Model_Data_Translator::prepare_conditions_query_params_for_models(
1075
+				$query_parameters['where'],
1076
+				$model,
1077
+				$this->get_model_version_info()->requested_version()
1078
+			);
1079
+		}
1080
+		if (isset($query_parameters['order_by'])) {
1081
+			$order_by = $query_parameters['order_by'];
1082
+		} elseif (isset($query_parameters['orderby'])) {
1083
+			$order_by = $query_parameters['orderby'];
1084
+		} else {
1085
+			$order_by = null;
1086
+		}
1087
+		if ($order_by !== null) {
1088
+			if (is_array($order_by)) {
1089
+				$order_by = Model_Data_Translator::prepare_field_names_in_array_keys_from_json($order_by);
1090
+			} else {
1091
+				//it's a single item
1092
+				$order_by = Model_Data_Translator::prepare_field_name_from_json($order_by);
1093
+			}
1094
+			$model_query_params['order_by'] = $order_by;
1095
+		}
1096
+		if (isset($query_parameters['group_by'])) {
1097
+			$group_by = $query_parameters['group_by'];
1098
+		} elseif (isset($query_parameters['groupby'])) {
1099
+			$group_by = $query_parameters['groupby'];
1100
+		} else {
1101
+			$group_by = array_keys($model->get_combined_primary_key_fields());
1102
+		}
1103
+		//make sure they're all real names
1104
+		if (is_array($group_by)) {
1105
+			$group_by = Model_Data_Translator::prepare_field_names_from_json($group_by);
1106
+		}
1107
+		if ($group_by !== null) {
1108
+			$model_query_params['group_by'] = $group_by;
1109
+		}
1110
+		if (isset($query_parameters['having'])) {
1111
+			$model_query_params['having'] = Model_Data_Translator::prepare_conditions_query_params_for_models(
1112
+				$query_parameters['having'],
1113
+				$model,
1114
+				$this->get_model_version_info()->requested_version()
1115
+			);
1116
+		}
1117
+		if (isset($query_parameters['order'])) {
1118
+			$model_query_params['order'] = $query_parameters['order'];
1119
+		}
1120
+		if (isset($query_parameters['mine'])) {
1121
+			$model_query_params = $model->alter_query_params_to_only_include_mine($model_query_params);
1122
+		}
1123
+		if (isset($query_parameters['limit'])) {
1124
+			//limit should be either a string like '23' or '23,43', or an array with two items in it
1125
+			if (! is_array($query_parameters['limit'])) {
1126
+				$limit_array = explode(',', (string)$query_parameters['limit']);
1127
+			} else {
1128
+				$limit_array = $query_parameters['limit'];
1129
+			}
1130
+			$sanitized_limit = array();
1131
+			foreach ($limit_array as $key => $limit_part) {
1132
+				if ($this->_debug_mode && (! is_numeric($limit_part) || count($sanitized_limit) > 2)) {
1133
+					throw new \EE_Error(
1134
+						sprintf(
1135
+							__('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.',
1136
+								'event_espresso'),
1137
+							wp_json_encode($query_parameters['limit'])
1138
+						)
1139
+					);
1140
+				}
1141
+				$sanitized_limit[] = (int)$limit_part;
1142
+			}
1143
+			$model_query_params['limit'] = implode(',', $sanitized_limit);
1144
+		} else {
1145
+			$model_query_params['limit'] = \EED_Core_Rest_Api::get_default_query_limit();
1146
+		}
1147
+		if (isset($query_parameters['caps'])) {
1148
+			$model_query_params['caps'] = $this->validate_context($query_parameters['caps']);
1149
+		} else {
1150
+			$model_query_params['caps'] = \EEM_Base::caps_read;
1151
+		}
1152
+		if (isset($query_parameters['default_where_conditions'])) {
1153
+			$model_query_params['default_where_conditions'] = $this->validate_default_query_params($query_parameters['default_where_conditions']);
1154
+		}
1155
+		return apply_filters('FHEE__Read__create_model_query_params', $model_query_params, $query_parameters, $model);
1156
+	}
1157
+
1158
+
1159
+
1160
+	/**
1161
+	 * Changes the REST-style query params for use in the models
1162
+	 *
1163
+	 * @deprecated
1164
+	 * @param \EEM_Base $model
1165
+	 * @param array     $query_params sub-array from @see EEM_Base::get_all()
1166
+	 * @return array
1167
+	 */
1168
+	public function prepare_rest_query_params_key_for_models($model, $query_params)
1169
+	{
1170
+		$model_ready_query_params = array();
1171
+		foreach ($query_params as $key => $value) {
1172
+			if (is_array($value)) {
1173
+				$model_ready_query_params[$key] = $this->prepare_rest_query_params_key_for_models($model, $value);
1174
+			} else {
1175
+				$model_ready_query_params[$key] = $value;
1176
+			}
1177
+		}
1178
+		return $model_ready_query_params;
1179
+	}
1180
+
1181
+
1182
+
1183
+	/**
1184
+	 * @deprecated
1185
+	 * @param $model
1186
+	 * @param $query_params
1187
+	 * @return array
1188
+	 */
1189
+	public function prepare_rest_query_params_values_for_models($model, $query_params)
1190
+	{
1191
+		$model_ready_query_params = array();
1192
+		foreach ($query_params as $key => $value) {
1193
+			if (is_array($value)) {
1194
+				$model_ready_query_params[$key] = $this->prepare_rest_query_params_values_for_models($model, $value);
1195
+			} else {
1196
+				$model_ready_query_params[$key] = $value;
1197
+			}
1198
+		}
1199
+		return $model_ready_query_params;
1200
+	}
1201
+
1202
+
1203
+
1204
+	/**
1205
+	 * Explodes the string on commas, and only returns items with $prefix followed by a period.
1206
+	 * If no prefix is specified, returns items with no period.
1207
+	 *
1208
+	 * @param string|array $string_to_explode eg "jibba,jabba, blah, blaabla" or array('jibba', 'jabba' )
1209
+	 * @param string       $prefix            "Event" or "foobar"
1210
+	 * @return array $string_to_exploded exploded on COMMAS, and if a prefix was specified
1211
+	 *                                        we only return strings starting with that and a period; if no prefix was
1212
+	 *                                        specified we return all items containing NO periods
1213
+	 */
1214
+	public function explode_and_get_items_prefixed_with($string_to_explode, $prefix)
1215
+	{
1216
+		if (is_string($string_to_explode)) {
1217
+			$exploded_contents = explode(',', $string_to_explode);
1218
+		} else if (is_array($string_to_explode)) {
1219
+			$exploded_contents = $string_to_explode;
1220
+		} else {
1221
+			$exploded_contents = array();
1222
+		}
1223
+		//if the string was empty, we want an empty array
1224
+		$exploded_contents = array_filter($exploded_contents);
1225
+		$contents_with_prefix = array();
1226
+		foreach ($exploded_contents as $item) {
1227
+			$item = trim($item);
1228
+			//if no prefix was provided, so we look for items with no "." in them
1229
+			if (! $prefix) {
1230
+				//does this item have a period?
1231
+				if (strpos($item, '.') === false) {
1232
+					//if not, then its what we're looking for
1233
+					$contents_with_prefix[] = $item;
1234
+				}
1235
+			} else if (strpos($item, $prefix . '.') === 0) {
1236
+				//this item has the prefix and a period, grab it
1237
+				$contents_with_prefix[] = substr(
1238
+					$item,
1239
+					strpos($item, $prefix . '.') + strlen($prefix . '.')
1240
+				);
1241
+			} else if ($item === $prefix) {
1242
+				//this item is JUST the prefix
1243
+				//so let's grab everything after, which is a blank string
1244
+				$contents_with_prefix[] = '';
1245
+			}
1246
+		}
1247
+		return $contents_with_prefix;
1248
+	}
1249
+
1250
+
1251
+
1252
+	/**
1253
+	 * @deprecated since 4.8.36.rc.001 You should instead use Read::explode_and_get_items_prefixed_with.
1254
+	 * Deprecated because its return values were really quite confusing- sometimes it returned
1255
+	 * an empty array (when the include string was blank or '*') or sometimes it returned
1256
+	 * array('*') (when you provided a model and a model of that kind was found).
1257
+	 * Parses the $include_string so we fetch all the field names relating to THIS model
1258
+	 * (ie have NO period in them), or for the provided model (ie start with the model
1259
+	 * name and then a period).
1260
+	 * @param string $include_string @see Read:handle_request_get_all
1261
+	 * @param string $model_name
1262
+	 * @return array of fields for this model. If $model_name is provided, then
1263
+	 *                               the fields for that model, with the model's name removed from each.
1264
+	 *                               If $include_string was blank or '*' returns an empty array
1265
+	 */
1266
+	public function extract_includes_for_this_model($include_string, $model_name = null)
1267
+	{
1268
+		if (is_array($include_string)) {
1269
+			$include_string = implode(',', $include_string);
1270
+		}
1271
+		if ($include_string === '*' || $include_string === '') {
1272
+			return array();
1273
+		}
1274
+		$includes = explode(',', $include_string);
1275
+		$extracted_fields_to_include = array();
1276
+		if ($model_name) {
1277
+			foreach ($includes as $field_to_include) {
1278
+				$field_to_include = trim($field_to_include);
1279
+				if (strpos($field_to_include, $model_name . '.') === 0) {
1280
+					//found the model name at the exact start
1281
+					$field_sans_model_name = str_replace($model_name . '.', '', $field_to_include);
1282
+					$extracted_fields_to_include[] = $field_sans_model_name;
1283
+				} elseif ($field_to_include == $model_name) {
1284
+					$extracted_fields_to_include[] = '*';
1285
+				}
1286
+			}
1287
+		} else {
1288
+			//look for ones with no period
1289
+			foreach ($includes as $field_to_include) {
1290
+				$field_to_include = trim($field_to_include);
1291
+				if (
1292
+					strpos($field_to_include, '.') === false
1293
+					&& ! $this->get_model_version_info()->is_model_name_in_this_version($field_to_include)
1294
+				) {
1295
+					$extracted_fields_to_include[] = $field_to_include;
1296
+				}
1297
+			}
1298
+		}
1299
+		return $extracted_fields_to_include;
1300
+	}
1301 1301
 }
1302 1302
 
1303 1303
 
Please login to merge, or discard this patch.
Spacing   +38 added lines, -38 removed lines patch added patch discarded remove patch
@@ -8,7 +8,7 @@  discard block
 block discarded – undo
8 8
 use EventEspresso\core\entities\models\JsonModelSchema;
9 9
 use EE_Datetime_Field;
10 10
 
11
-if (! defined('EVENT_ESPRESSO_VERSION')) {
11
+if ( ! defined('EVENT_ESPRESSO_VERSION')) {
12 12
     exit('No direct script access allowed');
13 13
 }
14 14
 
@@ -57,12 +57,12 @@  discard block
 block discarded – undo
57 57
         try {
58 58
             $matches = $controller->parse_route(
59 59
                 $request->get_route(),
60
-                '~' . \EED_Core_Rest_Api::ee_api_namespace_for_regex . '(.*)~',
60
+                '~'.\EED_Core_Rest_Api::ee_api_namespace_for_regex.'(.*)~',
61 61
                 array('version', 'model')
62 62
             );
63 63
             $controller->set_requested_version($matches['version']);
64 64
             $model_name_singular = \EEH_Inflector::singularize_and_upper($matches['model']);
65
-            if (! $controller->get_model_version_info()->is_model_name_in_this_version($model_name_singular)) {
65
+            if ( ! $controller->get_model_version_info()->is_model_name_in_this_version($model_name_singular)) {
66 66
                 return $controller->send_response(
67 67
                     new \WP_Error(
68 68
                         'endpoint_parsing_error',
@@ -99,7 +99,7 @@  discard block
 block discarded – undo
99 99
         $controller = new Read();
100 100
         try {
101 101
             $controller->set_requested_version($version);
102
-            if (! $controller->get_model_version_info()->is_model_name_in_this_version($model_name)) {
102
+            if ( ! $controller->get_model_version_info()->is_model_name_in_this_version($model_name)) {
103 103
                 return array();
104 104
             }
105 105
             //get the model for this version
@@ -193,9 +193,9 @@  discard block
 block discarded – undo
193 193
     protected function _maybe_add_extra_fields_to_schema($field_name, \EE_Model_Field_Base $field, array $schema)
194 194
     {
195 195
         if ($field instanceof EE_Datetime_Field) {
196
-            $schema['properties'][$field_name . '_gmt'] = $field->getSchema();
196
+            $schema['properties'][$field_name.'_gmt'] = $field->getSchema();
197 197
             //modify the description
198
-            $schema['properties'][$field_name . '_gmt']['description'] = sprintf(
198
+            $schema['properties'][$field_name.'_gmt']['description'] = sprintf(
199 199
                 esc_html__('%s - the value for this field is in GMT.', 'event_espresso'),
200 200
                 $field->get_nicename()
201 201
             );
@@ -236,11 +236,11 @@  discard block
 block discarded – undo
236 236
         try {
237 237
             $matches = $controller->parse_route(
238 238
                 $request->get_route(),
239
-                '~' . \EED_Core_Rest_Api::ee_api_namespace_for_regex . '(.*)/(.*)~',
239
+                '~'.\EED_Core_Rest_Api::ee_api_namespace_for_regex.'(.*)/(.*)~',
240 240
                 array('version', 'model', 'id'));
241 241
             $controller->set_requested_version($matches['version']);
242 242
             $model_name_singular = \EEH_Inflector::singularize_and_upper($matches['model']);
243
-            if (! $controller->get_model_version_info()->is_model_name_in_this_version($model_name_singular)) {
243
+            if ( ! $controller->get_model_version_info()->is_model_name_in_this_version($model_name_singular)) {
244 244
                 return $controller->send_response(
245 245
                     new \WP_Error(
246 246
                         'endpoint_parsing_error',
@@ -278,12 +278,12 @@  discard block
 block discarded – undo
278 278
         try {
279 279
             $matches = $controller->parse_route(
280 280
                 $request->get_route(),
281
-                '~' . \EED_Core_Rest_Api::ee_api_namespace_for_regex . '(.*)/(.*)/(.*)~',
281
+                '~'.\EED_Core_Rest_Api::ee_api_namespace_for_regex.'(.*)/(.*)/(.*)~',
282 282
                 array('version', 'model', 'id', 'related_model')
283 283
             );
284 284
             $controller->set_requested_version($matches['version']);
285 285
             $main_model_name_singular = \EEH_Inflector::singularize_and_upper($matches['model']);
286
-            if (! $controller->get_model_version_info()->is_model_name_in_this_version($main_model_name_singular)) {
286
+            if ( ! $controller->get_model_version_info()->is_model_name_in_this_version($main_model_name_singular)) {
287 287
                 return $controller->send_response(
288 288
                     new \WP_Error(
289 289
                         'endpoint_parsing_error',
@@ -298,11 +298,11 @@  discard block
 block discarded – undo
298 298
             $main_model = $controller->get_model_version_info()->load_model($main_model_name_singular);
299 299
             //assume the related model name is plural and try to find the model's name
300 300
             $related_model_name_singular = \EEH_Inflector::singularize_and_upper($matches['related_model']);
301
-            if (! $controller->get_model_version_info()->is_model_name_in_this_version($related_model_name_singular)) {
301
+            if ( ! $controller->get_model_version_info()->is_model_name_in_this_version($related_model_name_singular)) {
302 302
                 //so the word didn't singularize well. Maybe that's just because it's a singular word?
303 303
                 $related_model_name_singular = \EEH_Inflector::humanize($matches['related_model']);
304 304
             }
305
-            if (! $controller->get_model_version_info()->is_model_name_in_this_version($related_model_name_singular)) {
305
+            if ( ! $controller->get_model_version_info()->is_model_name_in_this_version($related_model_name_singular)) {
306 306
                 return $controller->send_response(
307 307
                     new \WP_Error(
308 308
                         'endpoint_parsing_error',
@@ -338,7 +338,7 @@  discard block
 block discarded – undo
338 338
     public function get_entities_from_model($model, $request)
339 339
     {
340 340
         $query_params = $this->create_model_query_params($model, $request->get_params());
341
-        if (! Capabilities::current_user_has_partial_access_to($model, $query_params['caps'])) {
341
+        if ( ! Capabilities::current_user_has_partial_access_to($model, $query_params['caps'])) {
342 342
             $model_name_plural = \EEH_Inflector::pluralize_and_lower($model->get_this_model_name());
343 343
             return new \WP_Error(
344 344
                 sprintf('rest_%s_cannot_list', $model_name_plural),
@@ -350,7 +350,7 @@  discard block
 block discarded – undo
350 350
                 array('status' => 403)
351 351
             );
352 352
         }
353
-        if (! $request->get_header('no_rest_headers')) {
353
+        if ( ! $request->get_header('no_rest_headers')) {
354 354
             $this->_set_headers_from_query_params($model, $query_params);
355 355
         }
356 356
         /** @type array $results */
@@ -380,7 +380,7 @@  discard block
 block discarded – undo
380 380
         $context = $this->validate_context($request->get_param('caps'));
381 381
         $model = $relation->get_this_model();
382 382
         $related_model = $relation->get_other_model();
383
-        if (! isset($primary_model_query_params[0])) {
383
+        if ( ! isset($primary_model_query_params[0])) {
384 384
             $primary_model_query_params[0] = array();
385 385
         }
386 386
         //check if they can access the 1st model object
@@ -431,7 +431,7 @@  discard block
 block discarded – undo
431 431
         }
432 432
         $query_params['default_where_conditions'] = 'none';
433 433
         $query_params['caps'] = $context;
434
-        if (! $request->get_header('no_rest_headers')) {
434
+        if ( ! $request->get_header('no_rest_headers')) {
435 435
             $this->_set_headers_from_query_params($relation->get_other_model(), $query_params);
436 436
         }
437 437
         /** @type array $results */
@@ -483,7 +483,7 @@  discard block
 block discarded – undo
483 483
      */
484 484
     public function get_entities_from_relation($id, $relation, $request)
485 485
     {
486
-        if (! $relation->get_this_model()->has_primary_key_field()) {
486
+        if ( ! $relation->get_this_model()->has_primary_key_field()) {
487 487
             throw new \EE_Error(
488 488
                 sprintf(
489 489
                     __('Read::get_entities_from_relation should only be called from a model with a primary key, it was called from %1$s',
@@ -520,7 +520,7 @@  discard block
 block discarded – undo
520 520
         $this->_set_debug_info('missing caps',
521 521
             Capabilities::get_missing_permissions_string($model, $query_params['caps']));
522 522
         //normally the limit to a 2-part array, where the 2nd item is the limit
523
-        if (! isset($query_params['limit'])) {
523
+        if ( ! isset($query_params['limit'])) {
524 524
             $query_params['limit'] = \EED_Core_Rest_Api::get_default_query_limit();
525 525
         }
526 526
         if (is_array($query_params['limit'])) {
@@ -554,7 +554,7 @@  discard block
 block discarded – undo
554 554
      */
555 555
     public function create_entity_from_wpdb_result($model, $db_row, $rest_request, $deprecated = null)
556 556
     {
557
-        if (! $rest_request instanceof \WP_REST_Request) {
557
+        if ( ! $rest_request instanceof \WP_REST_Request) {
558 558
             //ok so this was called in the old style, where the 3rd arg was
559 559
             //$include, and the 4th arg was $context
560 560
             //now setup the request just to avoid fatal errors, although we won't be able
@@ -629,7 +629,7 @@  discard block
 block discarded – undo
629 629
             global $post;
630 630
             $old_post = $post;
631 631
             $post = get_post($result[$model->primary_key_name()]);
632
-            if (! $post instanceof \WP_Post) {
632
+            if ( ! $post instanceof \WP_Post) {
633 633
                 //well that's weird, because $result is what we JUST fetched from the database
634 634
                 throw new Rest_Exception(
635 635
                     'error_fetching_post_from_database_results',
@@ -639,7 +639,7 @@  discard block
 block discarded – undo
639 639
                     )
640 640
                 );
641 641
             }
642
-            $model_object_classname = 'EE_' . $model->get_this_model_name();
642
+            $model_object_classname = 'EE_'.$model->get_this_model_name();
643 643
             $post->{$model_object_classname} = \EE_Registry::instance()->load_class(
644 644
                 $model_object_classname,
645 645
                 $result,
@@ -670,7 +670,7 @@  discard block
 block discarded – undo
670 670
                 if ($field_value instanceof \DateTime) {
671 671
                     $timezone = $field_value->getTimezone();
672 672
                     $field_value->setTimezone(new \DateTimeZone('UTC'));
673
-                    $result[$field_name . '_gmt'] = Model_Data_Translator::prepare_field_value_for_json(
673
+                    $result[$field_name.'_gmt'] = Model_Data_Translator::prepare_field_value_for_json(
674 674
                         $field_obj,
675 675
                         $field_value,
676 676
                         $this->get_model_version_info()->requested_version()
@@ -751,7 +751,7 @@  discard block
 block discarded – undo
751 751
         if ($model->has_primary_key_field()) {
752 752
             foreach ($this->get_model_version_info()->relation_settings($model) as $relation_name => $relation_obj) {
753 753
                 $related_model_part = Read::get_related_entity_name($relation_name, $relation_obj);
754
-                $links[\EED_Core_Rest_Api::ee_api_link_namespace . $related_model_part] = array(
754
+                $links[\EED_Core_Rest_Api::ee_api_link_namespace.$related_model_part] = array(
755 755
                     array(
756 756
                         'href'   => $this->get_versioned_link_to(
757 757
                             \EEH_Inflector::pluralize_and_lower($model->get_this_model_name())
@@ -786,13 +786,13 @@  discard block
 block discarded – undo
786 786
         $db_row = array()
787 787
     ) {
788 788
         //if $db_row not included, hope the entity array has what we need
789
-        if (! $db_row) {
789
+        if ( ! $db_row) {
790 790
             $db_row = $entity_array;
791 791
         }
792 792
         $includes_for_this_model = $this->explode_and_get_items_prefixed_with($rest_request->get_param('include'), '');
793 793
         $includes_for_this_model = $this->_remove_model_names_from_array($includes_for_this_model);
794 794
         //if they passed in * or didn't specify any includes, return everything
795
-        if (! in_array('*', $includes_for_this_model)
795
+        if ( ! in_array('*', $includes_for_this_model)
796 796
             && ! empty($includes_for_this_model)
797 797
         ) {
798 798
             if ($model->has_primary_key_field()) {
@@ -967,7 +967,7 @@  discard block
 block discarded – undo
967 967
         $restricted_query_params['caps'] = $this->validate_context($request->get_param('caps'));
968 968
         $this->_set_debug_info('model query params', $restricted_query_params);
969 969
         $model_rows = $model->get_all_wpdb_results($restricted_query_params);
970
-        if (! empty ($model_rows)) {
970
+        if ( ! empty ($model_rows)) {
971 971
             return $this->create_entity_from_wpdb_result(
972 972
                 $model,
973 973
                 array_shift($model_rows),
@@ -976,7 +976,7 @@  discard block
 block discarded – undo
976 976
             //ok let's test to see if we WOULD have found it, had we not had restrictions from missing capabilities
977 977
             $lowercase_model_name = strtolower($model->get_this_model_name());
978 978
             $model_rows_found_sans_restrictions = $model->get_all_wpdb_results($query_params);
979
-            if (! empty($model_rows_found_sans_restrictions)) {
979
+            if ( ! empty($model_rows_found_sans_restrictions)) {
980 980
                 //you got shafted- it existed but we didn't want to tell you!
981 981
                 return new \WP_Error(
982 982
                     'rest_user_cannot_read',
@@ -1011,7 +1011,7 @@  discard block
 block discarded – undo
1011 1011
      */
1012 1012
     public function validate_context($context)
1013 1013
     {
1014
-        if (! $context) {
1014
+        if ( ! $context) {
1015 1015
             $context = \EEM_Base::caps_read;
1016 1016
         }
1017 1017
         $valid_contexts = \EEM_Base::valid_cap_contexts();
@@ -1037,7 +1037,7 @@  discard block
 block discarded – undo
1037 1037
             \EEM_Base::default_where_conditions_minimum_all,
1038 1038
             \EEM_Base::default_where_conditions_minimum_others,
1039 1039
         );
1040
-        if (! $default_query_params) {
1040
+        if ( ! $default_query_params) {
1041 1041
             $default_query_params = \EEM_Base::default_where_conditions_all;
1042 1042
         }
1043 1043
         if (
@@ -1122,14 +1122,14 @@  discard block
 block discarded – undo
1122 1122
         }
1123 1123
         if (isset($query_parameters['limit'])) {
1124 1124
             //limit should be either a string like '23' or '23,43', or an array with two items in it
1125
-            if (! is_array($query_parameters['limit'])) {
1126
-                $limit_array = explode(',', (string)$query_parameters['limit']);
1125
+            if ( ! is_array($query_parameters['limit'])) {
1126
+                $limit_array = explode(',', (string) $query_parameters['limit']);
1127 1127
             } else {
1128 1128
                 $limit_array = $query_parameters['limit'];
1129 1129
             }
1130 1130
             $sanitized_limit = array();
1131 1131
             foreach ($limit_array as $key => $limit_part) {
1132
-                if ($this->_debug_mode && (! is_numeric($limit_part) || count($sanitized_limit) > 2)) {
1132
+                if ($this->_debug_mode && ( ! is_numeric($limit_part) || count($sanitized_limit) > 2)) {
1133 1133
                     throw new \EE_Error(
1134 1134
                         sprintf(
1135 1135
                             __('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.',
@@ -1138,7 +1138,7 @@  discard block
 block discarded – undo
1138 1138
                         )
1139 1139
                     );
1140 1140
                 }
1141
-                $sanitized_limit[] = (int)$limit_part;
1141
+                $sanitized_limit[] = (int) $limit_part;
1142 1142
             }
1143 1143
             $model_query_params['limit'] = implode(',', $sanitized_limit);
1144 1144
         } else {
@@ -1226,17 +1226,17 @@  discard block
 block discarded – undo
1226 1226
         foreach ($exploded_contents as $item) {
1227 1227
             $item = trim($item);
1228 1228
             //if no prefix was provided, so we look for items with no "." in them
1229
-            if (! $prefix) {
1229
+            if ( ! $prefix) {
1230 1230
                 //does this item have a period?
1231 1231
                 if (strpos($item, '.') === false) {
1232 1232
                     //if not, then its what we're looking for
1233 1233
                     $contents_with_prefix[] = $item;
1234 1234
                 }
1235
-            } else if (strpos($item, $prefix . '.') === 0) {
1235
+            } else if (strpos($item, $prefix.'.') === 0) {
1236 1236
                 //this item has the prefix and a period, grab it
1237 1237
                 $contents_with_prefix[] = substr(
1238 1238
                     $item,
1239
-                    strpos($item, $prefix . '.') + strlen($prefix . '.')
1239
+                    strpos($item, $prefix.'.') + strlen($prefix.'.')
1240 1240
                 );
1241 1241
             } else if ($item === $prefix) {
1242 1242
                 //this item is JUST the prefix
@@ -1276,9 +1276,9 @@  discard block
 block discarded – undo
1276 1276
         if ($model_name) {
1277 1277
             foreach ($includes as $field_to_include) {
1278 1278
                 $field_to_include = trim($field_to_include);
1279
-                if (strpos($field_to_include, $model_name . '.') === 0) {
1279
+                if (strpos($field_to_include, $model_name.'.') === 0) {
1280 1280
                     //found the model name at the exact start
1281
-                    $field_sans_model_name = str_replace($model_name . '.', '', $field_to_include);
1281
+                    $field_sans_model_name = str_replace($model_name.'.', '', $field_to_include);
1282 1282
                     $extracted_fields_to_include[] = $field_sans_model_name;
1283 1283
                 } elseif ($field_to_include == $model_name) {
1284 1284
                     $extracted_fields_to_include[] = '*';
Please login to merge, or discard this patch.
core/services/shortcodes/EspressoShortcode.php 2 patches
Indentation   +227 added lines, -227 removed lines patch added patch discarded remove patch
@@ -23,233 +23,233 @@
 block discarded – undo
23 23
 abstract class EspressoShortcode implements ShortcodeInterface
24 24
 {
25 25
 
26
-    /**
27
-     * transient prefix
28
-     *
29
-     * @type string
30
-     */
31
-    const CACHE_TRANSIENT_PREFIX = 'ee_sc_';
32
-
33
-    /**
34
-     * @var PostRelatedCacheManager $cache_manager
35
-     */
36
-    private $cache_manager;
37
-
38
-    /**
39
-     * true if ShortcodeInterface::initializeShortcode() has been called
40
-     * if false, then that will get called before processing
41
-     *
42
-     * @var boolean $initialized
43
-     */
44
-    private $initialized = false;
45
-
46
-
47
-
48
-    /**
49
-     * EspressoShortcode constructor
50
-     *
51
-     * @param PostRelatedCacheManager $cache_manager
52
-     */
53
-    public function __construct(PostRelatedCacheManager $cache_manager)
54
-    {
55
-        $this->cache_manager = $cache_manager;
56
-    }
57
-
58
-
59
-
60
-    /**
61
-     * @return void
62
-     */
63
-    public function shortcodeHasBeenInitialized()
64
-    {
65
-        $this->initialized = true;
66
-    }
67
-
68
-
69
-
70
-    /**
71
-     * enqueues scripts then processes the shortcode
72
-     *
73
-     * @param array $attributes
74
-     * @return string
75
-     * @throws EE_Error
76
-     */
77
-    final public function processShortcodeCallback($attributes = array())
78
-    {
79
-        if ($this instanceof EnqueueAssetsInterface) {
80
-            if (is_admin()) {
81
-                $this->enqueueAdminScripts();
82
-            } else {
83
-                $this->enqueueScripts();
84
-            }
85
-        }
86
-        return $this->shortcodeContent(
87
-            $this->sanitizeAttributes((array)$attributes)
88
-        );
89
-    }
90
-
91
-
92
-
93
-    /**
94
-     * If shortcode caching is enabled for the shortcode,
95
-     * and cached results exist, then that will be returned
96
-     * else new content will be generated.
97
-     * If caching is enabled, then the new content will be cached for later.
98
-     *
99
-     * @param array $attributes
100
-     * @return mixed|string
101
-     * @throws EE_Error
102
-     */
103
-    private function shortcodeContent(array $attributes)
104
-    {
105
-        $shortcode = $this;
106
-        $post_ID = $this->currentPostID();
107
-        // something like "SC_EVENTS-123"
108
-        $cache_ID = $this->shortcodeCacheID($post_ID);
109
-        $this->cache_manager->clearPostRelatedCacheOnUpdate($post_ID, $cache_ID);
110
-        return $this->cache_manager->get(
111
-            $cache_ID,
112
-            // serialized attributes
113
-            wp_json_encode($attributes),
114
-            // Closure for generating content if cache is expired
115
-            function () use ($shortcode, $attributes) {
116
-                if($shortcode->initialized() === false){
117
-                    $shortcode->initializeShortcode();
118
-                }
119
-                return $shortcode->processShortcode($attributes);
120
-            },
121
-            // filterable cache expiration set by each shortcode
122
-            apply_filters(
123
-                'FHEE__EventEspresso_core_services_shortcodes_EspressoShortcode__shortcodeContent__cache_expiration',
124
-                $this->cacheExpiration(),
125
-                $this->getTag(),
126
-                $this
127
-            )
128
-        );
129
-    }
130
-
131
-
132
-
133
-    /**
134
-     * @return int
135
-     * @throws EE_Error
136
-     */
137
-    private function currentPostID()
138
-    {
139
-        // try to get EE_Event any way we can
140
-        $event = EEH_Event_View::get_event();
141
-        // then get some kind of ID
142
-        if ($event instanceof EE_Event) {
143
-            return $event->ID();
144
-        }
145
-        global $post;
146
-        if ($post instanceof EE_Event) {
147
-            return $post->ID();
148
-        }
149
-        if ($post instanceof WP_Post) {
150
-            return $post->ID;
151
-        }
152
-        return 0;
153
-    }
154
-
155
-
156
-
157
-    /**
158
-     * @param int $post_ID
159
-     * @return string
160
-     * @throws EE_Error
161
-     */
162
-    private function shortcodeCacheID($post_ID)
163
-    {
164
-        $tag = str_replace('ESPRESSO_', '', $this->getTag());
165
-        return "SC_{$tag}-{$post_ID}";
166
-    }
167
-
168
-
169
-
170
-    /**
171
-     * array for defining custom attribute sanitization callbacks,
172
-     * where keys match keys in your attributes array,
173
-     * and values represent the sanitization function you wish to be applied to that attribute.
174
-     * So for example, if you had an integer attribute named "event_id"
175
-     * that you wanted to be sanitized using absint(),
176
-     * then you would return the following:
177
-     *      array('event_id' => 'absint')
178
-     * Entering 'skip_sanitization' for the callback value
179
-     * means that no sanitization will be applied
180
-     * on the assumption that the attribute
181
-     * will be sanitized at some point... right?
182
-     * You wouldn't pass around unsanitized attributes would you?
183
-     * That would be very Tom Foolery of you!!!
184
-     *
185
-     * @return array
186
-     */
187
-    protected function customAttributeSanitizationMap()
188
-    {
189
-        return array();
190
-    }
191
-
192
-
193
-
194
-    /**
195
-     * Performs basic sanitization on shortcode attributes
196
-     * Since incoming attributes from the shortcode usage in the WP editor will all be strings,
197
-     * most attributes will by default be sanitized using the sanitize_text_field() function.
198
-     * This can be overridden using the customAttributeSanitizationMap() method (see above),
199
-     * all other attributes would be sanitized using the defaults in the switch statement below
200
-     *
201
-     * @param array $attributes
202
-     * @return array
203
-     */
204
-    private function sanitizeAttributes(array $attributes)
205
-    {
206
-        $custom_sanitization = $this->customAttributeSanitizationMap();
207
-        foreach ($attributes as $key => $value) {
208
-            // is a custom sanitization callback specified ?
209
-            if (isset($custom_sanitization[$key])) {
210
-                $callback = $custom_sanitization[$key];
211
-                if ($callback === 'skip_sanitization') {
212
-                    $attributes[$key] = $value;
213
-                    continue;
214
-                }
215
-                if (function_exists($callback)) {
216
-                    $attributes[$key] = $callback($value);
217
-                    continue;
218
-                }
219
-            }
220
-            switch (true) {
221
-                case $value === null :
222
-                case is_int($value) :
223
-                case is_float($value) :
224
-                    // typical booleans
225
-                case in_array($value, array(true, 'true', '1', 'on', 'yes', false, 'false', '0', 'off', 'no'), true) :
226
-                    $attributes[$key] = $value;
227
-                    break;
228
-                case is_string($value) :
229
-                    $attributes[$key] = sanitize_text_field($value);
230
-                    break;
231
-                case is_array($value) :
232
-                    $attributes[$key] = $this->sanitizeAttributes($value);
233
-                    break;
234
-                default :
235
-                    // only remaining data types are Object and Resource
236
-                    // which are not allowed as shortcode attributes
237
-                    $attributes[$key] = null;
238
-                    break;
239
-            }
240
-        }
241
-        return $attributes;
242
-    }
243
-
244
-
245
-
246
-    /**
247
-     * Returns whether or not this shortcode has been initialized
248
-     * @return boolean
249
-     */
250
-    public function initialized(){
251
-        return $this->initialized;
252
-    }
26
+	/**
27
+	 * transient prefix
28
+	 *
29
+	 * @type string
30
+	 */
31
+	const CACHE_TRANSIENT_PREFIX = 'ee_sc_';
32
+
33
+	/**
34
+	 * @var PostRelatedCacheManager $cache_manager
35
+	 */
36
+	private $cache_manager;
37
+
38
+	/**
39
+	 * true if ShortcodeInterface::initializeShortcode() has been called
40
+	 * if false, then that will get called before processing
41
+	 *
42
+	 * @var boolean $initialized
43
+	 */
44
+	private $initialized = false;
45
+
46
+
47
+
48
+	/**
49
+	 * EspressoShortcode constructor
50
+	 *
51
+	 * @param PostRelatedCacheManager $cache_manager
52
+	 */
53
+	public function __construct(PostRelatedCacheManager $cache_manager)
54
+	{
55
+		$this->cache_manager = $cache_manager;
56
+	}
57
+
58
+
59
+
60
+	/**
61
+	 * @return void
62
+	 */
63
+	public function shortcodeHasBeenInitialized()
64
+	{
65
+		$this->initialized = true;
66
+	}
67
+
68
+
69
+
70
+	/**
71
+	 * enqueues scripts then processes the shortcode
72
+	 *
73
+	 * @param array $attributes
74
+	 * @return string
75
+	 * @throws EE_Error
76
+	 */
77
+	final public function processShortcodeCallback($attributes = array())
78
+	{
79
+		if ($this instanceof EnqueueAssetsInterface) {
80
+			if (is_admin()) {
81
+				$this->enqueueAdminScripts();
82
+			} else {
83
+				$this->enqueueScripts();
84
+			}
85
+		}
86
+		return $this->shortcodeContent(
87
+			$this->sanitizeAttributes((array)$attributes)
88
+		);
89
+	}
90
+
91
+
92
+
93
+	/**
94
+	 * If shortcode caching is enabled for the shortcode,
95
+	 * and cached results exist, then that will be returned
96
+	 * else new content will be generated.
97
+	 * If caching is enabled, then the new content will be cached for later.
98
+	 *
99
+	 * @param array $attributes
100
+	 * @return mixed|string
101
+	 * @throws EE_Error
102
+	 */
103
+	private function shortcodeContent(array $attributes)
104
+	{
105
+		$shortcode = $this;
106
+		$post_ID = $this->currentPostID();
107
+		// something like "SC_EVENTS-123"
108
+		$cache_ID = $this->shortcodeCacheID($post_ID);
109
+		$this->cache_manager->clearPostRelatedCacheOnUpdate($post_ID, $cache_ID);
110
+		return $this->cache_manager->get(
111
+			$cache_ID,
112
+			// serialized attributes
113
+			wp_json_encode($attributes),
114
+			// Closure for generating content if cache is expired
115
+			function () use ($shortcode, $attributes) {
116
+				if($shortcode->initialized() === false){
117
+					$shortcode->initializeShortcode();
118
+				}
119
+				return $shortcode->processShortcode($attributes);
120
+			},
121
+			// filterable cache expiration set by each shortcode
122
+			apply_filters(
123
+				'FHEE__EventEspresso_core_services_shortcodes_EspressoShortcode__shortcodeContent__cache_expiration',
124
+				$this->cacheExpiration(),
125
+				$this->getTag(),
126
+				$this
127
+			)
128
+		);
129
+	}
130
+
131
+
132
+
133
+	/**
134
+	 * @return int
135
+	 * @throws EE_Error
136
+	 */
137
+	private function currentPostID()
138
+	{
139
+		// try to get EE_Event any way we can
140
+		$event = EEH_Event_View::get_event();
141
+		// then get some kind of ID
142
+		if ($event instanceof EE_Event) {
143
+			return $event->ID();
144
+		}
145
+		global $post;
146
+		if ($post instanceof EE_Event) {
147
+			return $post->ID();
148
+		}
149
+		if ($post instanceof WP_Post) {
150
+			return $post->ID;
151
+		}
152
+		return 0;
153
+	}
154
+
155
+
156
+
157
+	/**
158
+	 * @param int $post_ID
159
+	 * @return string
160
+	 * @throws EE_Error
161
+	 */
162
+	private function shortcodeCacheID($post_ID)
163
+	{
164
+		$tag = str_replace('ESPRESSO_', '', $this->getTag());
165
+		return "SC_{$tag}-{$post_ID}";
166
+	}
167
+
168
+
169
+
170
+	/**
171
+	 * array for defining custom attribute sanitization callbacks,
172
+	 * where keys match keys in your attributes array,
173
+	 * and values represent the sanitization function you wish to be applied to that attribute.
174
+	 * So for example, if you had an integer attribute named "event_id"
175
+	 * that you wanted to be sanitized using absint(),
176
+	 * then you would return the following:
177
+	 *      array('event_id' => 'absint')
178
+	 * Entering 'skip_sanitization' for the callback value
179
+	 * means that no sanitization will be applied
180
+	 * on the assumption that the attribute
181
+	 * will be sanitized at some point... right?
182
+	 * You wouldn't pass around unsanitized attributes would you?
183
+	 * That would be very Tom Foolery of you!!!
184
+	 *
185
+	 * @return array
186
+	 */
187
+	protected function customAttributeSanitizationMap()
188
+	{
189
+		return array();
190
+	}
191
+
192
+
193
+
194
+	/**
195
+	 * Performs basic sanitization on shortcode attributes
196
+	 * Since incoming attributes from the shortcode usage in the WP editor will all be strings,
197
+	 * most attributes will by default be sanitized using the sanitize_text_field() function.
198
+	 * This can be overridden using the customAttributeSanitizationMap() method (see above),
199
+	 * all other attributes would be sanitized using the defaults in the switch statement below
200
+	 *
201
+	 * @param array $attributes
202
+	 * @return array
203
+	 */
204
+	private function sanitizeAttributes(array $attributes)
205
+	{
206
+		$custom_sanitization = $this->customAttributeSanitizationMap();
207
+		foreach ($attributes as $key => $value) {
208
+			// is a custom sanitization callback specified ?
209
+			if (isset($custom_sanitization[$key])) {
210
+				$callback = $custom_sanitization[$key];
211
+				if ($callback === 'skip_sanitization') {
212
+					$attributes[$key] = $value;
213
+					continue;
214
+				}
215
+				if (function_exists($callback)) {
216
+					$attributes[$key] = $callback($value);
217
+					continue;
218
+				}
219
+			}
220
+			switch (true) {
221
+				case $value === null :
222
+				case is_int($value) :
223
+				case is_float($value) :
224
+					// typical booleans
225
+				case in_array($value, array(true, 'true', '1', 'on', 'yes', false, 'false', '0', 'off', 'no'), true) :
226
+					$attributes[$key] = $value;
227
+					break;
228
+				case is_string($value) :
229
+					$attributes[$key] = sanitize_text_field($value);
230
+					break;
231
+				case is_array($value) :
232
+					$attributes[$key] = $this->sanitizeAttributes($value);
233
+					break;
234
+				default :
235
+					// only remaining data types are Object and Resource
236
+					// which are not allowed as shortcode attributes
237
+					$attributes[$key] = null;
238
+					break;
239
+			}
240
+		}
241
+		return $attributes;
242
+	}
243
+
244
+
245
+
246
+	/**
247
+	 * Returns whether or not this shortcode has been initialized
248
+	 * @return boolean
249
+	 */
250
+	public function initialized(){
251
+		return $this->initialized;
252
+	}
253 253
 
254 254
 
255 255
 
Please login to merge, or discard this patch.
Spacing   +4 added lines, -4 removed lines patch added patch discarded remove patch
@@ -84,7 +84,7 @@  discard block
 block discarded – undo
84 84
             }
85 85
         }
86 86
         return $this->shortcodeContent(
87
-            $this->sanitizeAttributes((array)$attributes)
87
+            $this->sanitizeAttributes((array) $attributes)
88 88
         );
89 89
     }
90 90
 
@@ -112,8 +112,8 @@  discard block
 block discarded – undo
112 112
             // serialized attributes
113 113
             wp_json_encode($attributes),
114 114
             // Closure for generating content if cache is expired
115
-            function () use ($shortcode, $attributes) {
116
-                if($shortcode->initialized() === false){
115
+            function() use ($shortcode, $attributes) {
116
+                if ($shortcode->initialized() === false) {
117 117
                     $shortcode->initializeShortcode();
118 118
                 }
119 119
                 return $shortcode->processShortcode($attributes);
@@ -247,7 +247,7 @@  discard block
 block discarded – undo
247 247
      * Returns whether or not this shortcode has been initialized
248 248
      * @return boolean
249 249
      */
250
-    public function initialized(){
250
+    public function initialized() {
251 251
         return $this->initialized;
252 252
     }
253 253
 
Please login to merge, or discard this patch.
core/services/shortcodes/ShortcodeInterface.php 1 patch
Indentation   +46 added lines, -46 removed lines patch added patch discarded remove patch
@@ -8,52 +8,52 @@
 block discarded – undo
8 8
 interface ShortcodeInterface
9 9
 {
10 10
 
11
-    /**
12
-     * the actual shortcode tag that gets registered with WordPress
13
-     *
14
-     * @return string
15
-     */
16
-    public function getTag();
17
-
18
-    /**
19
-     * the length of time in seconds to cache the results of the processShortcode() method
20
-     * 0 means the processShortcode() results will NOT be cached at all
21
-     *
22
-     * @return int
23
-     */
24
-    public function cacheExpiration();
25
-
26
-    /**
27
-     * a place for adding any initialization code that needs to run prior to wp_header().
28
-     * this may be required for shortcodes that utilize a corresponding module,
29
-     * and need to enqueue assets for that module
30
-     *
31
-     * !!! IMPORTANT !!!
32
-     * After performing any logic within this method required for initialization
33
-     *         $this->shortcodeHasBeenInitialized();
34
-     * should be called to ensure that the shortcode is setup correctly.
35
-     *
36
-     * @return void
37
-     */
38
-    public function initializeShortcode();
39
-
40
-    /**
41
-     * callback that runs when the shortcode is encountered in post content.
42
-     * IMPORTANT !!!
43
-     * remember that shortcode content should be RETURNED and NOT echoed out
44
-     *
45
-     * @param array $attributes
46
-     * @return string
47
-     */
48
-    public function processShortcode($attributes = array());
49
-
50
-
51
-
52
-    /**
53
-     * Returns whether or not this shortcode class has already been initialized
54
-     * @return boolean
55
-     */
56
-    public function initialized();
11
+	/**
12
+	 * the actual shortcode tag that gets registered with WordPress
13
+	 *
14
+	 * @return string
15
+	 */
16
+	public function getTag();
17
+
18
+	/**
19
+	 * the length of time in seconds to cache the results of the processShortcode() method
20
+	 * 0 means the processShortcode() results will NOT be cached at all
21
+	 *
22
+	 * @return int
23
+	 */
24
+	public function cacheExpiration();
25
+
26
+	/**
27
+	 * a place for adding any initialization code that needs to run prior to wp_header().
28
+	 * this may be required for shortcodes that utilize a corresponding module,
29
+	 * and need to enqueue assets for that module
30
+	 *
31
+	 * !!! IMPORTANT !!!
32
+	 * After performing any logic within this method required for initialization
33
+	 *         $this->shortcodeHasBeenInitialized();
34
+	 * should be called to ensure that the shortcode is setup correctly.
35
+	 *
36
+	 * @return void
37
+	 */
38
+	public function initializeShortcode();
39
+
40
+	/**
41
+	 * callback that runs when the shortcode is encountered in post content.
42
+	 * IMPORTANT !!!
43
+	 * remember that shortcode content should be RETURNED and NOT echoed out
44
+	 *
45
+	 * @param array $attributes
46
+	 * @return string
47
+	 */
48
+	public function processShortcode($attributes = array());
49
+
50
+
51
+
52
+	/**
53
+	 * Returns whether or not this shortcode class has already been initialized
54
+	 * @return boolean
55
+	 */
56
+	public function initialized();
57 57
 
58 58
 }
59 59
 // End of file ShortcodeInterface.php
Please login to merge, or discard this patch.