Completed
Branch BUG-11108-ticket-reserved-coun... (144d27)
by
unknown
14:21 queued 17s
created
core/libraries/rest_api/ModelDataTranslator.php 3 patches
Unused Use Statements   -2 removed lines patch added patch discarded remove patch
@@ -2,11 +2,9 @@
 block discarded – undo
2 2
 namespace EventEspresso\core\libraries\rest_api;
3 3
 
4 4
 use DomainException;
5
-use EE_Capabilities;
6 5
 use EE_Datetime_Field;
7 6
 use EE_Error;
8 7
 use EE_Infinite_Integer_Field;
9
-use EE_Maybe_Serialized_Simple_HTML_Field;
10 8
 use EE_Model_Field_Base;
11 9
 use EE_Serialized_Text_Field;
12 10
 use EEM_Base;
Please login to merge, or discard this patch.
Indentation   +834 added lines, -834 removed lines patch added patch discarded remove patch
@@ -12,7 +12,7 @@  discard block
 block discarded – undo
12 12
 use EEM_Base;
13 13
 
14 14
 if (! defined('EVENT_ESPRESSO_VERSION')) {
15
-    exit('No direct script access allowed');
15
+	exit('No direct script access allowed');
16 16
 }
17 17
 
18 18
 
@@ -37,837 +37,837 @@  discard block
 block discarded – undo
37 37
 class ModelDataTranslator
38 38
 {
39 39
 
40
-    /**
41
-     * We used to use -1 for infinity in the rest api, but that's ambiguous for
42
-     * fields that COULD contain -1; so we use null
43
-     */
44
-    const EE_INF_IN_REST = null;
45
-
46
-
47
-
48
-    /**
49
-     * Prepares a possible array of input values from JSON for use by the models
50
-     *
51
-     * @param EE_Model_Field_Base $field_obj
52
-     * @param mixed                $original_value_maybe_array
53
-     * @param string               $requested_version
54
-     * @param string               $timezone_string treat values as being in this timezone
55
-     * @return mixed
56
-     * @throws RestException
57
-     */
58
-    public static function prepareFieldValuesFromJson(
59
-        $field_obj,
60
-        $original_value_maybe_array,
61
-        $requested_version,
62
-        $timezone_string = 'UTC'
63
-    ) {
64
-        if (is_array($original_value_maybe_array)
65
-            && ! $field_obj instanceof EE_Serialized_Text_Field
66
-        ) {
67
-            $new_value_maybe_array = array();
68
-            foreach ($original_value_maybe_array as $array_key => $array_item) {
69
-                $new_value_maybe_array[$array_key] = ModelDataTranslator::prepareFieldValueFromJson(
70
-                    $field_obj,
71
-                    $array_item,
72
-                    $requested_version,
73
-                    $timezone_string
74
-                );
75
-            }
76
-        } else {
77
-            $new_value_maybe_array = ModelDataTranslator::prepareFieldValueFromJson(
78
-                $field_obj,
79
-                $original_value_maybe_array,
80
-                $requested_version,
81
-                $timezone_string
82
-            );
83
-        }
84
-        return $new_value_maybe_array;
85
-    }
86
-
87
-
88
-
89
-    /**
90
-     * Prepares an array of field values FOR use in JSON/REST API
91
-     *
92
-     * @param EE_Model_Field_Base $field_obj
93
-     * @param mixed                $original_value_maybe_array
94
-     * @param string               $request_version (eg 4.8.36)
95
-     * @return array
96
-     */
97
-    public static function prepareFieldValuesForJson($field_obj, $original_value_maybe_array, $request_version)
98
-    {
99
-        if (is_array($original_value_maybe_array)) {
100
-            $new_value = array();
101
-            foreach ($original_value_maybe_array as $key => $value) {
102
-                $new_value[$key] = ModelDataTranslator::prepareFieldValuesForJson($field_obj, $value, $request_version);
103
-            }
104
-        } else {
105
-            $new_value = ModelDataTranslator::prepareFieldValueForJson(
106
-                $field_obj,
107
-                $original_value_maybe_array,
108
-                $request_version
109
-            );
110
-        }
111
-        return $new_value;
112
-    }
113
-
114
-
115
-    /**
116
-     * Prepares incoming data from the json or $_REQUEST parameters for the models'
117
-     * "$query_params".
118
-     *
119
-     * @param EE_Model_Field_Base $field_obj
120
-     * @param mixed               $original_value
121
-     * @param string              $requested_version
122
-     * @param string              $timezone_string treat values as being in this timezone
123
-     * @return mixed
124
-     * @throws RestException
125
-     * @throws DomainException
126
-     * @throws EE_Error
127
-     */
128
-    public static function prepareFieldValueFromJson(
129
-        $field_obj,
130
-        $original_value,
131
-        $requested_version,
132
-        $timezone_string = 'UTC' // UTC
133
-    ) {
134
-        //check if they accidentally submitted an error value. If so throw an exception
135
-        if (is_array($original_value)
136
-            && isset($original_value['error_code'], $original_value['error_message'])) {
137
-            throw new RestException(
138
-                'rest_submitted_error_value',
139
-                sprintf(
140
-                    esc_html__(
141
-                        'You tried to submit a JSON error object as a value for %1$s. That\'s not allowed.',
142
-                        'event_espresso'
143
-                    ),
144
-                    $field_obj->get_name()
145
-                ),
146
-                array(
147
-                    'status' => 400,
148
-                )
149
-            );
150
-        }
151
-        //double-check for serialized PHP. We never accept serialized PHP. No way Jose.
152
-        ModelDataTranslator::throwExceptionIfContainsSerializedData($original_value);
153
-        $timezone_string = $timezone_string !== '' ? $timezone_string : get_option('timezone_string', '');
154
-        $new_value = null;
155
-        //walk through the submitted data and double-check for serialized PHP. We never accept serialized PHP. No
156
-        // way Jose.
157
-        ModelDataTranslator::throwExceptionIfContainsSerializedData($original_value);
158
-        if ($field_obj instanceof EE_Infinite_Integer_Field
159
-            && in_array($original_value, array(null, ''), true)
160
-        ) {
161
-            $new_value = EE_INF;
162
-        } elseif ($field_obj instanceof EE_Datetime_Field) {
163
-            $new_value = rest_parse_date(
164
-                self::getTimestampWithTimezoneOffset($original_value, $field_obj, $timezone_string)
165
-            );
166
-            if ($new_value === false) {
167
-                throw new RestException(
168
-                    'invalid_format_for_timestamp',
169
-                    sprintf(
170
-                        esc_html__(
171
-                            'Timestamps received on a request as the value for Date and Time fields must be in %1$s/%2$s format.  The timestamp provided (%3$s) is not that format.',
172
-                            'event_espresso'
173
-                        ),
174
-                        'RFC3339',
175
-                        'ISO8601',
176
-                        $original_value
177
-                    ),
178
-                    array(
179
-                        'status' => 400
180
-                    )
181
-                );
182
-            }
183
-        } else {
184
-            $new_value = $original_value;
185
-        }
186
-        return $new_value;
187
-    }
188
-
189
-
190
-    /**
191
-     * This checks if the incoming timestamp has timezone information already on it and if it doesn't then adds timezone
192
-     * information via details obtained from the host site.
193
-     *
194
-     * @param string            $original_timestamp
195
-     * @param EE_Datetime_Field $datetime_field
196
-     * @param                   $timezone_string
197
-     * @return string
198
-     * @throws DomainException
199
-     */
200
-    private static function getTimestampWithTimezoneOffset(
201
-        $original_timestamp,
202
-        EE_Datetime_Field $datetime_field,
203
-        $timezone_string
204
-    ) {
205
-        //already have timezone information?
206
-        if (preg_match('/Z|(\+|\-)(\d{2}:\d{2})/', $original_timestamp)) {
207
-            //yes, we're ignoring the timezone.
208
-            return $original_timestamp;
209
-        }
210
-        //need to append timezone
211
-        list($offset_sign, $offset_secs) = self::parseTimezoneOffset(
212
-            $datetime_field->get_timezone_offset(
213
-                new \DateTimeZone($timezone_string),
214
-                $original_timestamp
215
-            )
216
-        );
217
-        $offset_string =
218
-            str_pad(
219
-                floor($offset_secs / HOUR_IN_SECONDS),
220
-                2,
221
-                '0',
222
-                STR_PAD_LEFT
223
-            )
224
-            . ':'
225
-            . str_pad(
226
-                ($offset_secs % HOUR_IN_SECONDS) / MINUTE_IN_SECONDS,
227
-                2,
228
-                '0',
229
-                STR_PAD_LEFT
230
-            );
231
-        return $original_timestamp . $offset_sign . $offset_string;
232
-    }
233
-
234
-
235
-
236
-    /**
237
-     * Throws an exception if $data is a serialized PHP string (or somehow an actually PHP object, although I don't
238
-     * think that can happen). If $data is an array, recurses into its keys and values
239
-     * @param mixed $data
240
-     * @throws RestException
241
-     * @return void
242
-     */
243
-    public static function throwExceptionIfContainsSerializedData($data)
244
-    {
245
-        if (is_array($data)) {
246
-            foreach ($data as $key => $value) {
247
-                ModelDataTranslator::throwExceptionIfContainsSerializedData($key);
248
-                ModelDataTranslator::throwExceptionIfContainsSerializedData($value);
249
-            }
250
-        } else {
251
-            if (is_serialized($data) || is_object($data)) {
252
-                throw new RestException(
253
-                    'serialized_data_submission_prohibited',
254
-                    esc_html__(
255
-                        // @codingStandardsIgnoreStart
256
-                        'You tried to submit a string of serialized text. Serialized PHP is prohibited over the EE4 REST API.',
257
-                        // @codingStandardsIgnoreEnd
258
-                        'event_espresso'
259
-                    )
260
-                );
261
-            }
262
-        }
263
-    }
264
-
265
-
266
-
267
-    /**
268
-     * determines what's going on with them timezone strings
269
-     *
270
-     * @param int $timezone_offset
271
-     * @return array
272
-     */
273
-    private static function parseTimezoneOffset($timezone_offset)
274
-    {
275
-        $first_char = substr((string)$timezone_offset, 0, 1);
276
-        if ($first_char === '+' || $first_char === '-') {
277
-            $offset_sign = $first_char;
278
-            $offset_secs = substr((string)$timezone_offset, 1);
279
-        } else {
280
-            $offset_sign = '+';
281
-            $offset_secs = $timezone_offset;
282
-        }
283
-        return array($offset_sign, $offset_secs);
284
-    }
285
-
286
-
287
-
288
-    /**
289
-     * Prepares a field's value for display in the API
290
-     *
291
-     * @param EE_Model_Field_Base $field_obj
292
-     * @param mixed                $original_value
293
-     * @param string               $requested_version
294
-     * @return mixed
295
-     */
296
-    public static function prepareFieldValueForJson($field_obj, $original_value, $requested_version)
297
-    {
298
-        if ($original_value === EE_INF) {
299
-            $new_value = ModelDataTranslator::EE_INF_IN_REST;
300
-        } elseif ($field_obj instanceof EE_Datetime_Field) {
301
-            if (is_string($original_value)) {
302
-                //did they submit a string of a unix timestamp?
303
-                if (is_numeric($original_value)) {
304
-                    $datetime_obj = new \DateTime();
305
-                    $datetime_obj->setTimestamp((int)$original_value);
306
-                } else {
307
-                    //first, check if its a MySQL timestamp in GMT
308
-                    $datetime_obj = \DateTime::createFromFormat('Y-m-d H:i:s', $original_value);
309
-                }
310
-                if (! $datetime_obj instanceof \DateTime) {
311
-                    //so it's not a unix timestamp or a MySQL timestamp. Maybe its in the field's date/time format?
312
-                    $datetime_obj = $field_obj->prepare_for_set($original_value);
313
-                }
314
-                $original_value = $datetime_obj;
315
-            }
316
-            if ($original_value instanceof \DateTime) {
317
-                $new_value = $original_value->format('Y-m-d H:i:s');
318
-            } elseif (is_int($original_value) || is_float($original_value)) {
319
-                $new_value = date('Y-m-d H:i:s', $original_value);
320
-            } elseif($original_value === null || $original_value === '') {
321
-                $new_value = null;
322
-            } else {
323
-                //so it's not a datetime object, unix timestamp (as string or int),
324
-                //MySQL timestamp, or even a string in the field object's format. So no idea what it is
325
-                throw new \EE_Error(
326
-                    sprintf(
327
-                        esc_html__(
328
-                        // @codingStandardsIgnoreStart
329
-                            'The value "%1$s" for the field "%2$s" on model "%3$s" could not be understood. It should be a PHP DateTime, unix timestamp, MySQL date, or string in the format "%4$s".',
330
-                            // @codingStandardsIgnoreEnd
331
-                            'event_espressso'
332
-                        ),
333
-                        $original_value,
334
-                        $field_obj->get_name(),
335
-                        $field_obj->get_model_name(),
336
-                        $field_obj->get_time_format() . ' ' . $field_obj->get_time_format()
337
-                    )
338
-                );
339
-            }
340
-            $new_value = mysql_to_rfc3339($new_value);
341
-        } else {
342
-            $new_value = $original_value;
343
-        }
344
-        //are we about to send an object? just don't. We have no good way to represent it in JSON.
345
-        // can't just check using is_object() because that missed PHP incomplete objects
346
-        if (! ModelDataTranslator::isRepresentableInJson($new_value)) {
347
-            $new_value = array(
348
-                'error_code' => 'php_object_not_return',
349
-                'error_message' => esc_html__('The value of this field in the database is a PHP object, which can\'t be represented in JSON.', 'event_espresso')
350
-            );
351
-        }
352
-        return apply_filters(
353
-            'FHEE__EventEspresso\core\libraries\rest_api\Model_Data_Translator__prepare_field_for_rest_api',
354
-            $new_value,
355
-            $field_obj,
356
-            $original_value,
357
-            $requested_version
358
-        );
359
-    }
360
-
361
-
362
-
363
-    /**
364
-     * Prepares condition-query-parameters (like what's in where and having) from
365
-     * the format expected in the API to use in the models
366
-     *
367
-     * @param array     $inputted_query_params_of_this_type
368
-     * @param EEM_Base $model
369
-     * @param string    $requested_version
370
-     * @param boolean $writing whether this data will be written to the DB, or if we're just building a query.
371
-     *                         If we're writing to the DB, we don't expect any operators, or any logic query parameters,
372
-     *                         and we also won't accept serialized data unless the current user has unfiltered_html.
373
-     * @return array
374
-     * @throws DomainException
375
-     * @throws RestException
376
-     * @throws EE_Error
377
-     */
378
-    public static function prepareConditionsQueryParamsForModels(
379
-        $inputted_query_params_of_this_type,
380
-        EEM_Base $model,
381
-        $requested_version,
382
-        $writing = false
383
-    ) {
384
-        $query_param_for_models = array();
385
-        $valid_operators = $model->valid_operators();
386
-        foreach ($inputted_query_params_of_this_type as $query_param_key => $query_param_value) {
387
-            $is_gmt_datetime_field = false;
388
-            $query_param_sans_stars = ModelDataTranslator::removeStarsAndAnythingAfterFromConditionQueryParamKey(
389
-                $query_param_key
390
-            );
391
-            $field = ModelDataTranslator::deduceFieldFromQueryParam(
392
-                $query_param_sans_stars,
393
-                $model
394
-            );
395
-            //double-check is it a *_gmt field?
396
-            if (! $field instanceof EE_Model_Field_Base
397
-                && ModelDataTranslator::isGmtDateFieldName($query_param_sans_stars)
398
-            ) {
399
-                //yep, take off '_gmt', and find the field
400
-                $query_param_key = ModelDataTranslator::removeGmtFromFieldName($query_param_sans_stars);
401
-                $field = ModelDataTranslator::deduceFieldFromQueryParam(
402
-                    $query_param_key,
403
-                    $model
404
-                );
405
-                $timezone = 'UTC';
406
-                $is_gmt_datetime_field = true;
407
-            } elseif ($field instanceof EE_Datetime_Field) {
408
-                //so it's not a GMT field. Set the timezone on the model to the default
409
-                $timezone = \EEH_DTT_Helper::get_valid_timezone_string();
410
-            } else {
411
-                //just keep using what's already set for the timezone
412
-                $timezone = $model->get_timezone();
413
-            }
414
-            if ($field instanceof EE_Model_Field_Base) {
415
-                if (! $writing && is_array($query_param_value)) {
416
-                    if (! \EEH_Array::is_array_numerically_and_sequentially_indexed($query_param_value)) {
417
-                        if (defined('EE_REST_API_DEBUG_MODE') && EE_REST_API_DEBUG_MODE) {
418
-                            throw new RestException(
419
-                                'numerically_indexed_array_of_values_only',
420
-                                sprintf(
421
-                                    esc_html__(
422
-                                        'The array provided for the parameter "%1$s" should be numerically indexed.',
423
-                                        'event_espresso'
424
-                                    ),
425
-                                    $query_param_key
426
-                                ),
427
-                                array(
428
-                                    'status' => 400,
429
-                                )
430
-                            );
431
-                        }
432
-                    }
433
-                    //did they specify an operator?
434
-                    if (isset($query_param_value[0])
435
-                        && isset($valid_operators[$query_param_value[0]])
436
-                    ) {
437
-                        $op = $query_param_value[0];
438
-                        $translated_value = array($op);
439
-                        if (array_key_exists($op, $model->valid_in_style_operators())
440
-                            && isset($query_param_value[1])
441
-                            && ! isset($query_param_value[2])
442
-                        ) {
443
-                            $translated_value[] = ModelDataTranslator::prepareFieldValuesFromJson(
444
-                                $field,
445
-                                $query_param_value[1],
446
-                                $requested_version,
447
-                                $timezone
448
-                            );
449
-                        } elseif (array_key_exists($op, $model->valid_between_style_operators())
450
-                            && isset($query_param_value[1], $query_param_value[2])
451
-                            && !isset($query_param_value[3])
452
-                        ) {
453
-                            $translated_value[] = ModelDataTranslator::prepareFieldValuesFromJson(
454
-                                $field,
455
-                                $query_param_value[1],
456
-                                $requested_version,
457
-                                $timezone
458
-                            );
459
-                            $translated_value[] = ModelDataTranslator::prepareFieldValuesFromJson(
460
-                                $field,
461
-                                $query_param_value[2],
462
-                                $requested_version,
463
-                                $timezone
464
-                            );
465
-                        } elseif (array_key_exists($op, $model->valid_like_style_operators())
466
-                            && isset($query_param_value[1])
467
-                            && ! isset($query_param_value[2])
468
-                        ) {
469
-                            //we want to leave this value mostly-as-is (eg don't force it to be a float
470
-                            //or a boolean or an enum value. Leave it as-is with wildcards etc)
471
-                            //but do verify it at least doesn't have any serialized data
472
-                            ModelDataTranslator::throwExceptionIfContainsSerializedData($query_param_value[1]);
473
-                            $translated_value[] = $query_param_value[1];
474
-                        } elseif (array_key_exists($op, $model->valid_null_style_operators())
475
-                            && !isset($query_param_value[1])) {
476
-                            //no arguments should have been provided, so don't look for any
477
-                        } elseif (isset($query_param_value[1])
478
-                            && !isset($query_param_value[2])
479
-                            && ! array_key_exists(
480
-                                $op,
481
-                                array_merge(
482
-                                    $model->valid_in_style_operators(),
483
-                                    $model->valid_null_style_operators(),
484
-                                    $model->valid_like_style_operators(),
485
-                                    $model->valid_between_style_operators()
486
-                                )
487
-                            )
488
-                        ) {
489
-                            //it's a valid operator, but none of the exceptions. Treat it normally.
490
-                            $translated_value[] = ModelDataTranslator::prepareFieldValuesFromJson(
491
-                                $field,
492
-                                $query_param_value[1],
493
-                                $requested_version,
494
-                                $timezone
495
-                            );
496
-                        } else {
497
-                            //so they provided a valid operator, but wrong number of arguments
498
-                            if (defined('EE_REST_API_DEBUG_MODE') && EE_REST_API_DEBUG_MODE) {
499
-                                throw new RestException(
500
-                                    'wrong_number_of_arguments',
501
-                                    sprintf(
502
-                                        esc_html__(
503
-                                            'The operator you provided, "%1$s" had the wrong number of arguments',
504
-                                            'event_espresso'
505
-                                        ),
506
-                                        $op
507
-                                    ),
508
-                                    array(
509
-                                        'status' => 400,
510
-                                    )
511
-                                );
512
-                            }
513
-                            $translated_value = null;
514
-                        }
515
-                    } else {
516
-                        //so they didn't provide a valid operator
517
-                        if (defined('EE_REST_API_DEBUG_MODE') && EE_REST_API_DEBUG_MODE) {
518
-                            throw new RestException(
519
-                                'invalid_operator',
520
-                                sprintf(
521
-                                    esc_html__(
522
-                                        'You provided an invalid parameter, with key "%1$s" and value "%2$s"',
523
-                                        'event_espresso'
524
-                                    ),
525
-                                    $query_param_key,
526
-                                    $query_param_value
527
-                                ),
528
-                                array(
529
-                                    'status' => 400,
530
-                                )
531
-                            );
532
-                        }
533
-                        //if we aren't in debug mode, then just try our best to fulfill the user's request
534
-                        $translated_value = null;
535
-                    }
536
-                } else {
537
-                    $translated_value = ModelDataTranslator::prepareFieldValueFromJson(
538
-                        $field,
539
-                        $query_param_value,
540
-                        $requested_version,
541
-                        $timezone
542
-                    );
543
-                }
544
-                if (
545
-                    (isset($query_param_for_models[$query_param_key]) && $is_gmt_datetime_field)
546
-                    ||
547
-                    $translated_value === null
548
-                ) {
549
-                    //they have already provided a non-gmt field, ignore the gmt one. That's what WP core
550
-                    //currently does (they might change it though). See https://core.trac.wordpress.org/ticket/39954
551
-                    //OR we couldn't create a translated value from their input
552
-                    continue;
553
-                }
554
-                $query_param_for_models[$query_param_key] = $translated_value;
555
-            } else {
556
-                //so this param doesn't correspond to a field eh?
557
-                if ($writing) {
558
-                    //always tell API clients about invalid parameters when they're creating data. Otherwise,
559
-                    //they are probably going to create invalid data
560
-                    throw new RestException(
561
-                        'invalid_field',
562
-                        sprintf(
563
-                            esc_html__('You have provided an invalid parameter: "%1$s"', 'event_espresso'),
564
-                            $query_param_key
565
-                        )
566
-                    );
567
-                } else {
568
-                    //so it's not for a field, is it a logic query param key?
569
-                    if (in_array(
570
-                        $query_param_sans_stars,
571
-                        $model->logic_query_param_keys()
572
-                    )) {
573
-                        $query_param_for_models[$query_param_key] = ModelDataTranslator::prepareConditionsQueryParamsForModels(
574
-                            $query_param_value,
575
-                            $model,
576
-                            $requested_version
577
-                        );
578
-                    } elseif (defined('EE_REST_API_DEBUG_MODE') && EE_REST_API_DEBUG_MODE) {
579
-                        //only tell API clients they got it wrong if we're in debug mode
580
-                        //otherwise try our best ot fulfill their request by ignoring this invalid data
581
-                        throw new RestException(
582
-                            'invalid_parameter',
583
-                            sprintf(
584
-                                esc_html__(
585
-                                    'You provided an invalid parameter, with key "%1$s"',
586
-                                    'event_espresso'
587
-                                ),
588
-                                $query_param_sans_stars
589
-                            ),
590
-                            array(
591
-                                'status' => 400,
592
-                            )
593
-                        );
594
-                    }
595
-                }
596
-            }
597
-        }
598
-        return $query_param_for_models;
599
-    }
600
-
601
-
602
-
603
-    /**
604
-     * Mostly checks if the last 4 characters are "_gmt", indicating its a
605
-     * gmt date field name
606
-     *
607
-     * @param string $field_name
608
-     * @return boolean
609
-     */
610
-    public static function isGmtDateFieldName($field_name)
611
-    {
612
-        return substr(
613
-            ModelDataTranslator::removeStarsAndAnythingAfterFromConditionQueryParamKey($field_name),
614
-            -4,
615
-            4
616
-        ) === '_gmt';
617
-    }
618
-
619
-
620
-
621
-    /**
622
-     * Removes the last "_gmt" part of a field name (and if there is no "_gmt" at the end, leave it alone)
623
-     *
624
-     * @param string $field_name
625
-     * @return string
626
-     */
627
-    public static function removeGmtFromFieldName($field_name)
628
-    {
629
-        if (! ModelDataTranslator::isGmtDateFieldName($field_name)) {
630
-            return $field_name;
631
-        }
632
-        $query_param_sans_stars = ModelDataTranslator::removeStarsAndAnythingAfterFromConditionQueryParamKey(
633
-            $field_name
634
-        );
635
-        $query_param_sans_gmt_and_sans_stars = substr(
636
-            $query_param_sans_stars,
637
-            0,
638
-            strrpos(
639
-                $field_name,
640
-                '_gmt'
641
-            )
642
-        );
643
-        return str_replace($query_param_sans_stars, $query_param_sans_gmt_and_sans_stars, $field_name);
644
-    }
645
-
646
-
647
-
648
-    /**
649
-     * Takes a field name from the REST API and prepares it for the model querying
650
-     *
651
-     * @param string $field_name
652
-     * @return string
653
-     */
654
-    public static function prepareFieldNameFromJson($field_name)
655
-    {
656
-        if (ModelDataTranslator::isGmtDateFieldName($field_name)) {
657
-            return ModelDataTranslator::removeGmtFromFieldName($field_name);
658
-        }
659
-        return $field_name;
660
-    }
661
-
662
-
663
-
664
-    /**
665
-     * Takes array of field names from REST API and prepares for models
666
-     *
667
-     * @param array $field_names
668
-     * @return array of field names (possibly include model prefixes)
669
-     */
670
-    public static function prepareFieldNamesFromJson(array $field_names)
671
-    {
672
-        $new_array = array();
673
-        foreach ($field_names as $key => $field_name) {
674
-            $new_array[$key] = ModelDataTranslator::prepareFieldNameFromJson($field_name);
675
-        }
676
-        return $new_array;
677
-    }
678
-
679
-
680
-
681
-    /**
682
-     * Takes array where array keys are field names (possibly with model path prefixes)
683
-     * from the REST API and prepares them for model querying
684
-     *
685
-     * @param array $field_names_as_keys
686
-     * @return array
687
-     */
688
-    public static function prepareFieldNamesInArrayKeysFromJson(array $field_names_as_keys)
689
-    {
690
-        $new_array = array();
691
-        foreach ($field_names_as_keys as $field_name => $value) {
692
-            $new_array[ModelDataTranslator::prepareFieldNameFromJson($field_name)] = $value;
693
-        }
694
-        return $new_array;
695
-    }
696
-
697
-
698
-
699
-    /**
700
-     * Prepares an array of model query params for use in the REST API
701
-     *
702
-     * @param array     $model_query_params
703
-     * @param EEM_Base $model
704
-     * @param string    $requested_version eg "4.8.36". If null is provided, defaults to the latest release of the EE4
705
-     *                                     REST API
706
-     * @return array which can be passed into the EE4 REST API when querying a model resource
707
-     * @throws EE_Error
708
-     */
709
-    public static function prepareQueryParamsForRestApi(
710
-        array $model_query_params,
711
-        EEM_Base $model,
712
-        $requested_version = null
713
-    ) {
714
-        if ($requested_version === null) {
715
-            $requested_version = \EED_Core_Rest_Api::latest_rest_api_version();
716
-        }
717
-        $rest_query_params = $model_query_params;
718
-        if (isset($model_query_params[0])) {
719
-            $rest_query_params['where'] = ModelDataTranslator::prepareConditionsQueryParamsForRestApi(
720
-                $model_query_params[0],
721
-                $model,
722
-                $requested_version
723
-            );
724
-            unset($rest_query_params[0]);
725
-        }
726
-        if (isset($model_query_params['having'])) {
727
-            $rest_query_params['having'] = ModelDataTranslator::prepareConditionsQueryParamsForRestApi(
728
-                $model_query_params['having'],
729
-                $model,
730
-                $requested_version
731
-            );
732
-        }
733
-        return apply_filters(
734
-            'FHEE__EventEspresso\core\libraries\rest_api\Model_Data_Translator__prepare_query_params_for_rest_api',
735
-            $rest_query_params,
736
-            $model_query_params,
737
-            $model,
738
-            $requested_version
739
-        );
740
-    }
741
-
742
-
743
-
744
-    /**
745
-     * Prepares all the sub-conditions query parameters (eg having or where conditions) for use in the rest api
746
-     *
747
-     * @param array     $inputted_query_params_of_this_type eg like the "where" or "having" conditions query params
748
-     *                                                      passed into EEM_Base::get_all()
749
-     * @param EEM_Base $model
750
-     * @param string    $requested_version                  eg "4.8.36"
751
-     * @return array ready for use in the rest api query params
752
-     * @throws EE_Error
753
-     * @throws ObjectDetectedException if somehow a PHP object were in the query params' values,
754
-     *                                     (which would be really unusual)
755
-     */
756
-    public static function prepareConditionsQueryParamsForRestApi(
757
-        $inputted_query_params_of_this_type,
758
-        EEM_Base $model,
759
-        $requested_version
760
-    ) {
761
-        $query_param_for_models = array();
762
-        foreach ($inputted_query_params_of_this_type as $query_param_key => $query_param_value) {
763
-            $field = ModelDataTranslator::deduceFieldFromQueryParam(
764
-                ModelDataTranslator::removeStarsAndAnythingAfterFromConditionQueryParamKey($query_param_key),
765
-                $model
766
-            );
767
-            if ($field instanceof EE_Model_Field_Base) {
768
-                //did they specify an operator?
769
-                if (is_array($query_param_value)) {
770
-                    $op = $query_param_value[0];
771
-                    $translated_value = array($op);
772
-                    if (isset($query_param_value[1])) {
773
-                        $value = $query_param_value[1];
774
-                        $translated_value[1] = ModelDataTranslator::prepareFieldValuesForJson(
775
-                            $field,
776
-                            $value,
777
-                            $requested_version
778
-                        );
779
-                    }
780
-                } else {
781
-                    $translated_value = ModelDataTranslator::prepareFieldValueForJson(
782
-                        $field,
783
-                        $query_param_value,
784
-                        $requested_version
785
-                    );
786
-                }
787
-                $query_param_for_models[$query_param_key] = $translated_value;
788
-            } else {
789
-                //so it's not for a field, assume it's a logic query param key
790
-                $query_param_for_models[$query_param_key] = ModelDataTranslator::prepareConditionsQueryParamsForRestApi(
791
-                    $query_param_value,
792
-                    $model,
793
-                    $requested_version
794
-                );
795
-            }
796
-        }
797
-        return $query_param_for_models;
798
-    }
799
-
800
-
801
-
802
-    /**
803
-     * @param $condition_query_param_key
804
-     * @return string
805
-     */
806
-    public static function removeStarsAndAnythingAfterFromConditionQueryParamKey($condition_query_param_key)
807
-    {
808
-        $pos_of_star = strpos($condition_query_param_key, '*');
809
-        if ($pos_of_star === false) {
810
-            return $condition_query_param_key;
811
-        } else {
812
-            $condition_query_param_sans_star = substr($condition_query_param_key, 0, $pos_of_star);
813
-            return $condition_query_param_sans_star;
814
-        }
815
-    }
816
-
817
-
818
-
819
-    /**
820
-     * Takes the input parameter and finds the model field that it indicates.
821
-     *
822
-     * @param string    $query_param_name like Registration.Transaction.TXN_ID, Event.Datetime.start_time, or REG_ID
823
-     * @param EEM_Base $model
824
-     * @return EE_Model_Field_Base
825
-     * @throws EE_Error
826
-     */
827
-    public static function deduceFieldFromQueryParam($query_param_name, EEM_Base $model)
828
-    {
829
-        //ok, now proceed with deducing which part is the model's name, and which is the field's name
830
-        //which will help us find the database table and column
831
-        $query_param_parts = explode('.', $query_param_name);
832
-        if (empty($query_param_parts)) {
833
-            throw new EE_Error(
834
-                sprintf(
835
-                    __(
836
-                        '_extract_column_name is empty when trying to extract column and table name from %s',
837
-                        'event_espresso'
838
-                    ),
839
-                    $query_param_name
840
-                )
841
-            );
842
-        }
843
-        $number_of_parts = count($query_param_parts);
844
-        $last_query_param_part = $query_param_parts[count($query_param_parts) - 1];
845
-        if ($number_of_parts === 1) {
846
-            $field_name = $last_query_param_part;
847
-        } else {// $number_of_parts >= 2
848
-            //the last part is the column name, and there are only 2parts. therefore...
849
-            $field_name = $last_query_param_part;
850
-            $model = \EE_Registry::instance()->load_model($query_param_parts[$number_of_parts - 2]);
851
-        }
852
-        try {
853
-            return $model->field_settings_for($field_name, false);
854
-        } catch (EE_Error $e) {
855
-            return null;
856
-        }
857
-    }
858
-
859
-
860
-
861
-    /**
862
-     * Returns true if $data can be easily represented in JSON.
863
-     * Basically, objects and resources can't be represented in JSON easily.
864
-     * @param mixed $data
865
-     * @return bool
866
-     */
867
-    protected static function isRepresentableInJson($data)
868
-    {
869
-        return is_scalar($data)
870
-               || is_array($data)
871
-               || is_null($data);
872
-    }
40
+	/**
41
+	 * We used to use -1 for infinity in the rest api, but that's ambiguous for
42
+	 * fields that COULD contain -1; so we use null
43
+	 */
44
+	const EE_INF_IN_REST = null;
45
+
46
+
47
+
48
+	/**
49
+	 * Prepares a possible array of input values from JSON for use by the models
50
+	 *
51
+	 * @param EE_Model_Field_Base $field_obj
52
+	 * @param mixed                $original_value_maybe_array
53
+	 * @param string               $requested_version
54
+	 * @param string               $timezone_string treat values as being in this timezone
55
+	 * @return mixed
56
+	 * @throws RestException
57
+	 */
58
+	public static function prepareFieldValuesFromJson(
59
+		$field_obj,
60
+		$original_value_maybe_array,
61
+		$requested_version,
62
+		$timezone_string = 'UTC'
63
+	) {
64
+		if (is_array($original_value_maybe_array)
65
+			&& ! $field_obj instanceof EE_Serialized_Text_Field
66
+		) {
67
+			$new_value_maybe_array = array();
68
+			foreach ($original_value_maybe_array as $array_key => $array_item) {
69
+				$new_value_maybe_array[$array_key] = ModelDataTranslator::prepareFieldValueFromJson(
70
+					$field_obj,
71
+					$array_item,
72
+					$requested_version,
73
+					$timezone_string
74
+				);
75
+			}
76
+		} else {
77
+			$new_value_maybe_array = ModelDataTranslator::prepareFieldValueFromJson(
78
+				$field_obj,
79
+				$original_value_maybe_array,
80
+				$requested_version,
81
+				$timezone_string
82
+			);
83
+		}
84
+		return $new_value_maybe_array;
85
+	}
86
+
87
+
88
+
89
+	/**
90
+	 * Prepares an array of field values FOR use in JSON/REST API
91
+	 *
92
+	 * @param EE_Model_Field_Base $field_obj
93
+	 * @param mixed                $original_value_maybe_array
94
+	 * @param string               $request_version (eg 4.8.36)
95
+	 * @return array
96
+	 */
97
+	public static function prepareFieldValuesForJson($field_obj, $original_value_maybe_array, $request_version)
98
+	{
99
+		if (is_array($original_value_maybe_array)) {
100
+			$new_value = array();
101
+			foreach ($original_value_maybe_array as $key => $value) {
102
+				$new_value[$key] = ModelDataTranslator::prepareFieldValuesForJson($field_obj, $value, $request_version);
103
+			}
104
+		} else {
105
+			$new_value = ModelDataTranslator::prepareFieldValueForJson(
106
+				$field_obj,
107
+				$original_value_maybe_array,
108
+				$request_version
109
+			);
110
+		}
111
+		return $new_value;
112
+	}
113
+
114
+
115
+	/**
116
+	 * Prepares incoming data from the json or $_REQUEST parameters for the models'
117
+	 * "$query_params".
118
+	 *
119
+	 * @param EE_Model_Field_Base $field_obj
120
+	 * @param mixed               $original_value
121
+	 * @param string              $requested_version
122
+	 * @param string              $timezone_string treat values as being in this timezone
123
+	 * @return mixed
124
+	 * @throws RestException
125
+	 * @throws DomainException
126
+	 * @throws EE_Error
127
+	 */
128
+	public static function prepareFieldValueFromJson(
129
+		$field_obj,
130
+		$original_value,
131
+		$requested_version,
132
+		$timezone_string = 'UTC' // UTC
133
+	) {
134
+		//check if they accidentally submitted an error value. If so throw an exception
135
+		if (is_array($original_value)
136
+			&& isset($original_value['error_code'], $original_value['error_message'])) {
137
+			throw new RestException(
138
+				'rest_submitted_error_value',
139
+				sprintf(
140
+					esc_html__(
141
+						'You tried to submit a JSON error object as a value for %1$s. That\'s not allowed.',
142
+						'event_espresso'
143
+					),
144
+					$field_obj->get_name()
145
+				),
146
+				array(
147
+					'status' => 400,
148
+				)
149
+			);
150
+		}
151
+		//double-check for serialized PHP. We never accept serialized PHP. No way Jose.
152
+		ModelDataTranslator::throwExceptionIfContainsSerializedData($original_value);
153
+		$timezone_string = $timezone_string !== '' ? $timezone_string : get_option('timezone_string', '');
154
+		$new_value = null;
155
+		//walk through the submitted data and double-check for serialized PHP. We never accept serialized PHP. No
156
+		// way Jose.
157
+		ModelDataTranslator::throwExceptionIfContainsSerializedData($original_value);
158
+		if ($field_obj instanceof EE_Infinite_Integer_Field
159
+			&& in_array($original_value, array(null, ''), true)
160
+		) {
161
+			$new_value = EE_INF;
162
+		} elseif ($field_obj instanceof EE_Datetime_Field) {
163
+			$new_value = rest_parse_date(
164
+				self::getTimestampWithTimezoneOffset($original_value, $field_obj, $timezone_string)
165
+			);
166
+			if ($new_value === false) {
167
+				throw new RestException(
168
+					'invalid_format_for_timestamp',
169
+					sprintf(
170
+						esc_html__(
171
+							'Timestamps received on a request as the value for Date and Time fields must be in %1$s/%2$s format.  The timestamp provided (%3$s) is not that format.',
172
+							'event_espresso'
173
+						),
174
+						'RFC3339',
175
+						'ISO8601',
176
+						$original_value
177
+					),
178
+					array(
179
+						'status' => 400
180
+					)
181
+				);
182
+			}
183
+		} else {
184
+			$new_value = $original_value;
185
+		}
186
+		return $new_value;
187
+	}
188
+
189
+
190
+	/**
191
+	 * This checks if the incoming timestamp has timezone information already on it and if it doesn't then adds timezone
192
+	 * information via details obtained from the host site.
193
+	 *
194
+	 * @param string            $original_timestamp
195
+	 * @param EE_Datetime_Field $datetime_field
196
+	 * @param                   $timezone_string
197
+	 * @return string
198
+	 * @throws DomainException
199
+	 */
200
+	private static function getTimestampWithTimezoneOffset(
201
+		$original_timestamp,
202
+		EE_Datetime_Field $datetime_field,
203
+		$timezone_string
204
+	) {
205
+		//already have timezone information?
206
+		if (preg_match('/Z|(\+|\-)(\d{2}:\d{2})/', $original_timestamp)) {
207
+			//yes, we're ignoring the timezone.
208
+			return $original_timestamp;
209
+		}
210
+		//need to append timezone
211
+		list($offset_sign, $offset_secs) = self::parseTimezoneOffset(
212
+			$datetime_field->get_timezone_offset(
213
+				new \DateTimeZone($timezone_string),
214
+				$original_timestamp
215
+			)
216
+		);
217
+		$offset_string =
218
+			str_pad(
219
+				floor($offset_secs / HOUR_IN_SECONDS),
220
+				2,
221
+				'0',
222
+				STR_PAD_LEFT
223
+			)
224
+			. ':'
225
+			. str_pad(
226
+				($offset_secs % HOUR_IN_SECONDS) / MINUTE_IN_SECONDS,
227
+				2,
228
+				'0',
229
+				STR_PAD_LEFT
230
+			);
231
+		return $original_timestamp . $offset_sign . $offset_string;
232
+	}
233
+
234
+
235
+
236
+	/**
237
+	 * Throws an exception if $data is a serialized PHP string (or somehow an actually PHP object, although I don't
238
+	 * think that can happen). If $data is an array, recurses into its keys and values
239
+	 * @param mixed $data
240
+	 * @throws RestException
241
+	 * @return void
242
+	 */
243
+	public static function throwExceptionIfContainsSerializedData($data)
244
+	{
245
+		if (is_array($data)) {
246
+			foreach ($data as $key => $value) {
247
+				ModelDataTranslator::throwExceptionIfContainsSerializedData($key);
248
+				ModelDataTranslator::throwExceptionIfContainsSerializedData($value);
249
+			}
250
+		} else {
251
+			if (is_serialized($data) || is_object($data)) {
252
+				throw new RestException(
253
+					'serialized_data_submission_prohibited',
254
+					esc_html__(
255
+						// @codingStandardsIgnoreStart
256
+						'You tried to submit a string of serialized text. Serialized PHP is prohibited over the EE4 REST API.',
257
+						// @codingStandardsIgnoreEnd
258
+						'event_espresso'
259
+					)
260
+				);
261
+			}
262
+		}
263
+	}
264
+
265
+
266
+
267
+	/**
268
+	 * determines what's going on with them timezone strings
269
+	 *
270
+	 * @param int $timezone_offset
271
+	 * @return array
272
+	 */
273
+	private static function parseTimezoneOffset($timezone_offset)
274
+	{
275
+		$first_char = substr((string)$timezone_offset, 0, 1);
276
+		if ($first_char === '+' || $first_char === '-') {
277
+			$offset_sign = $first_char;
278
+			$offset_secs = substr((string)$timezone_offset, 1);
279
+		} else {
280
+			$offset_sign = '+';
281
+			$offset_secs = $timezone_offset;
282
+		}
283
+		return array($offset_sign, $offset_secs);
284
+	}
285
+
286
+
287
+
288
+	/**
289
+	 * Prepares a field's value for display in the API
290
+	 *
291
+	 * @param EE_Model_Field_Base $field_obj
292
+	 * @param mixed                $original_value
293
+	 * @param string               $requested_version
294
+	 * @return mixed
295
+	 */
296
+	public static function prepareFieldValueForJson($field_obj, $original_value, $requested_version)
297
+	{
298
+		if ($original_value === EE_INF) {
299
+			$new_value = ModelDataTranslator::EE_INF_IN_REST;
300
+		} elseif ($field_obj instanceof EE_Datetime_Field) {
301
+			if (is_string($original_value)) {
302
+				//did they submit a string of a unix timestamp?
303
+				if (is_numeric($original_value)) {
304
+					$datetime_obj = new \DateTime();
305
+					$datetime_obj->setTimestamp((int)$original_value);
306
+				} else {
307
+					//first, check if its a MySQL timestamp in GMT
308
+					$datetime_obj = \DateTime::createFromFormat('Y-m-d H:i:s', $original_value);
309
+				}
310
+				if (! $datetime_obj instanceof \DateTime) {
311
+					//so it's not a unix timestamp or a MySQL timestamp. Maybe its in the field's date/time format?
312
+					$datetime_obj = $field_obj->prepare_for_set($original_value);
313
+				}
314
+				$original_value = $datetime_obj;
315
+			}
316
+			if ($original_value instanceof \DateTime) {
317
+				$new_value = $original_value->format('Y-m-d H:i:s');
318
+			} elseif (is_int($original_value) || is_float($original_value)) {
319
+				$new_value = date('Y-m-d H:i:s', $original_value);
320
+			} elseif($original_value === null || $original_value === '') {
321
+				$new_value = null;
322
+			} else {
323
+				//so it's not a datetime object, unix timestamp (as string or int),
324
+				//MySQL timestamp, or even a string in the field object's format. So no idea what it is
325
+				throw new \EE_Error(
326
+					sprintf(
327
+						esc_html__(
328
+						// @codingStandardsIgnoreStart
329
+							'The value "%1$s" for the field "%2$s" on model "%3$s" could not be understood. It should be a PHP DateTime, unix timestamp, MySQL date, or string in the format "%4$s".',
330
+							// @codingStandardsIgnoreEnd
331
+							'event_espressso'
332
+						),
333
+						$original_value,
334
+						$field_obj->get_name(),
335
+						$field_obj->get_model_name(),
336
+						$field_obj->get_time_format() . ' ' . $field_obj->get_time_format()
337
+					)
338
+				);
339
+			}
340
+			$new_value = mysql_to_rfc3339($new_value);
341
+		} else {
342
+			$new_value = $original_value;
343
+		}
344
+		//are we about to send an object? just don't. We have no good way to represent it in JSON.
345
+		// can't just check using is_object() because that missed PHP incomplete objects
346
+		if (! ModelDataTranslator::isRepresentableInJson($new_value)) {
347
+			$new_value = array(
348
+				'error_code' => 'php_object_not_return',
349
+				'error_message' => esc_html__('The value of this field in the database is a PHP object, which can\'t be represented in JSON.', 'event_espresso')
350
+			);
351
+		}
352
+		return apply_filters(
353
+			'FHEE__EventEspresso\core\libraries\rest_api\Model_Data_Translator__prepare_field_for_rest_api',
354
+			$new_value,
355
+			$field_obj,
356
+			$original_value,
357
+			$requested_version
358
+		);
359
+	}
360
+
361
+
362
+
363
+	/**
364
+	 * Prepares condition-query-parameters (like what's in where and having) from
365
+	 * the format expected in the API to use in the models
366
+	 *
367
+	 * @param array     $inputted_query_params_of_this_type
368
+	 * @param EEM_Base $model
369
+	 * @param string    $requested_version
370
+	 * @param boolean $writing whether this data will be written to the DB, or if we're just building a query.
371
+	 *                         If we're writing to the DB, we don't expect any operators, or any logic query parameters,
372
+	 *                         and we also won't accept serialized data unless the current user has unfiltered_html.
373
+	 * @return array
374
+	 * @throws DomainException
375
+	 * @throws RestException
376
+	 * @throws EE_Error
377
+	 */
378
+	public static function prepareConditionsQueryParamsForModels(
379
+		$inputted_query_params_of_this_type,
380
+		EEM_Base $model,
381
+		$requested_version,
382
+		$writing = false
383
+	) {
384
+		$query_param_for_models = array();
385
+		$valid_operators = $model->valid_operators();
386
+		foreach ($inputted_query_params_of_this_type as $query_param_key => $query_param_value) {
387
+			$is_gmt_datetime_field = false;
388
+			$query_param_sans_stars = ModelDataTranslator::removeStarsAndAnythingAfterFromConditionQueryParamKey(
389
+				$query_param_key
390
+			);
391
+			$field = ModelDataTranslator::deduceFieldFromQueryParam(
392
+				$query_param_sans_stars,
393
+				$model
394
+			);
395
+			//double-check is it a *_gmt field?
396
+			if (! $field instanceof EE_Model_Field_Base
397
+				&& ModelDataTranslator::isGmtDateFieldName($query_param_sans_stars)
398
+			) {
399
+				//yep, take off '_gmt', and find the field
400
+				$query_param_key = ModelDataTranslator::removeGmtFromFieldName($query_param_sans_stars);
401
+				$field = ModelDataTranslator::deduceFieldFromQueryParam(
402
+					$query_param_key,
403
+					$model
404
+				);
405
+				$timezone = 'UTC';
406
+				$is_gmt_datetime_field = true;
407
+			} elseif ($field instanceof EE_Datetime_Field) {
408
+				//so it's not a GMT field. Set the timezone on the model to the default
409
+				$timezone = \EEH_DTT_Helper::get_valid_timezone_string();
410
+			} else {
411
+				//just keep using what's already set for the timezone
412
+				$timezone = $model->get_timezone();
413
+			}
414
+			if ($field instanceof EE_Model_Field_Base) {
415
+				if (! $writing && is_array($query_param_value)) {
416
+					if (! \EEH_Array::is_array_numerically_and_sequentially_indexed($query_param_value)) {
417
+						if (defined('EE_REST_API_DEBUG_MODE') && EE_REST_API_DEBUG_MODE) {
418
+							throw new RestException(
419
+								'numerically_indexed_array_of_values_only',
420
+								sprintf(
421
+									esc_html__(
422
+										'The array provided for the parameter "%1$s" should be numerically indexed.',
423
+										'event_espresso'
424
+									),
425
+									$query_param_key
426
+								),
427
+								array(
428
+									'status' => 400,
429
+								)
430
+							);
431
+						}
432
+					}
433
+					//did they specify an operator?
434
+					if (isset($query_param_value[0])
435
+						&& isset($valid_operators[$query_param_value[0]])
436
+					) {
437
+						$op = $query_param_value[0];
438
+						$translated_value = array($op);
439
+						if (array_key_exists($op, $model->valid_in_style_operators())
440
+							&& isset($query_param_value[1])
441
+							&& ! isset($query_param_value[2])
442
+						) {
443
+							$translated_value[] = ModelDataTranslator::prepareFieldValuesFromJson(
444
+								$field,
445
+								$query_param_value[1],
446
+								$requested_version,
447
+								$timezone
448
+							);
449
+						} elseif (array_key_exists($op, $model->valid_between_style_operators())
450
+							&& isset($query_param_value[1], $query_param_value[2])
451
+							&& !isset($query_param_value[3])
452
+						) {
453
+							$translated_value[] = ModelDataTranslator::prepareFieldValuesFromJson(
454
+								$field,
455
+								$query_param_value[1],
456
+								$requested_version,
457
+								$timezone
458
+							);
459
+							$translated_value[] = ModelDataTranslator::prepareFieldValuesFromJson(
460
+								$field,
461
+								$query_param_value[2],
462
+								$requested_version,
463
+								$timezone
464
+							);
465
+						} elseif (array_key_exists($op, $model->valid_like_style_operators())
466
+							&& isset($query_param_value[1])
467
+							&& ! isset($query_param_value[2])
468
+						) {
469
+							//we want to leave this value mostly-as-is (eg don't force it to be a float
470
+							//or a boolean or an enum value. Leave it as-is with wildcards etc)
471
+							//but do verify it at least doesn't have any serialized data
472
+							ModelDataTranslator::throwExceptionIfContainsSerializedData($query_param_value[1]);
473
+							$translated_value[] = $query_param_value[1];
474
+						} elseif (array_key_exists($op, $model->valid_null_style_operators())
475
+							&& !isset($query_param_value[1])) {
476
+							//no arguments should have been provided, so don't look for any
477
+						} elseif (isset($query_param_value[1])
478
+							&& !isset($query_param_value[2])
479
+							&& ! array_key_exists(
480
+								$op,
481
+								array_merge(
482
+									$model->valid_in_style_operators(),
483
+									$model->valid_null_style_operators(),
484
+									$model->valid_like_style_operators(),
485
+									$model->valid_between_style_operators()
486
+								)
487
+							)
488
+						) {
489
+							//it's a valid operator, but none of the exceptions. Treat it normally.
490
+							$translated_value[] = ModelDataTranslator::prepareFieldValuesFromJson(
491
+								$field,
492
+								$query_param_value[1],
493
+								$requested_version,
494
+								$timezone
495
+							);
496
+						} else {
497
+							//so they provided a valid operator, but wrong number of arguments
498
+							if (defined('EE_REST_API_DEBUG_MODE') && EE_REST_API_DEBUG_MODE) {
499
+								throw new RestException(
500
+									'wrong_number_of_arguments',
501
+									sprintf(
502
+										esc_html__(
503
+											'The operator you provided, "%1$s" had the wrong number of arguments',
504
+											'event_espresso'
505
+										),
506
+										$op
507
+									),
508
+									array(
509
+										'status' => 400,
510
+									)
511
+								);
512
+							}
513
+							$translated_value = null;
514
+						}
515
+					} else {
516
+						//so they didn't provide a valid operator
517
+						if (defined('EE_REST_API_DEBUG_MODE') && EE_REST_API_DEBUG_MODE) {
518
+							throw new RestException(
519
+								'invalid_operator',
520
+								sprintf(
521
+									esc_html__(
522
+										'You provided an invalid parameter, with key "%1$s" and value "%2$s"',
523
+										'event_espresso'
524
+									),
525
+									$query_param_key,
526
+									$query_param_value
527
+								),
528
+								array(
529
+									'status' => 400,
530
+								)
531
+							);
532
+						}
533
+						//if we aren't in debug mode, then just try our best to fulfill the user's request
534
+						$translated_value = null;
535
+					}
536
+				} else {
537
+					$translated_value = ModelDataTranslator::prepareFieldValueFromJson(
538
+						$field,
539
+						$query_param_value,
540
+						$requested_version,
541
+						$timezone
542
+					);
543
+				}
544
+				if (
545
+					(isset($query_param_for_models[$query_param_key]) && $is_gmt_datetime_field)
546
+					||
547
+					$translated_value === null
548
+				) {
549
+					//they have already provided a non-gmt field, ignore the gmt one. That's what WP core
550
+					//currently does (they might change it though). See https://core.trac.wordpress.org/ticket/39954
551
+					//OR we couldn't create a translated value from their input
552
+					continue;
553
+				}
554
+				$query_param_for_models[$query_param_key] = $translated_value;
555
+			} else {
556
+				//so this param doesn't correspond to a field eh?
557
+				if ($writing) {
558
+					//always tell API clients about invalid parameters when they're creating data. Otherwise,
559
+					//they are probably going to create invalid data
560
+					throw new RestException(
561
+						'invalid_field',
562
+						sprintf(
563
+							esc_html__('You have provided an invalid parameter: "%1$s"', 'event_espresso'),
564
+							$query_param_key
565
+						)
566
+					);
567
+				} else {
568
+					//so it's not for a field, is it a logic query param key?
569
+					if (in_array(
570
+						$query_param_sans_stars,
571
+						$model->logic_query_param_keys()
572
+					)) {
573
+						$query_param_for_models[$query_param_key] = ModelDataTranslator::prepareConditionsQueryParamsForModels(
574
+							$query_param_value,
575
+							$model,
576
+							$requested_version
577
+						);
578
+					} elseif (defined('EE_REST_API_DEBUG_MODE') && EE_REST_API_DEBUG_MODE) {
579
+						//only tell API clients they got it wrong if we're in debug mode
580
+						//otherwise try our best ot fulfill their request by ignoring this invalid data
581
+						throw new RestException(
582
+							'invalid_parameter',
583
+							sprintf(
584
+								esc_html__(
585
+									'You provided an invalid parameter, with key "%1$s"',
586
+									'event_espresso'
587
+								),
588
+								$query_param_sans_stars
589
+							),
590
+							array(
591
+								'status' => 400,
592
+							)
593
+						);
594
+					}
595
+				}
596
+			}
597
+		}
598
+		return $query_param_for_models;
599
+	}
600
+
601
+
602
+
603
+	/**
604
+	 * Mostly checks if the last 4 characters are "_gmt", indicating its a
605
+	 * gmt date field name
606
+	 *
607
+	 * @param string $field_name
608
+	 * @return boolean
609
+	 */
610
+	public static function isGmtDateFieldName($field_name)
611
+	{
612
+		return substr(
613
+			ModelDataTranslator::removeStarsAndAnythingAfterFromConditionQueryParamKey($field_name),
614
+			-4,
615
+			4
616
+		) === '_gmt';
617
+	}
618
+
619
+
620
+
621
+	/**
622
+	 * Removes the last "_gmt" part of a field name (and if there is no "_gmt" at the end, leave it alone)
623
+	 *
624
+	 * @param string $field_name
625
+	 * @return string
626
+	 */
627
+	public static function removeGmtFromFieldName($field_name)
628
+	{
629
+		if (! ModelDataTranslator::isGmtDateFieldName($field_name)) {
630
+			return $field_name;
631
+		}
632
+		$query_param_sans_stars = ModelDataTranslator::removeStarsAndAnythingAfterFromConditionQueryParamKey(
633
+			$field_name
634
+		);
635
+		$query_param_sans_gmt_and_sans_stars = substr(
636
+			$query_param_sans_stars,
637
+			0,
638
+			strrpos(
639
+				$field_name,
640
+				'_gmt'
641
+			)
642
+		);
643
+		return str_replace($query_param_sans_stars, $query_param_sans_gmt_and_sans_stars, $field_name);
644
+	}
645
+
646
+
647
+
648
+	/**
649
+	 * Takes a field name from the REST API and prepares it for the model querying
650
+	 *
651
+	 * @param string $field_name
652
+	 * @return string
653
+	 */
654
+	public static function prepareFieldNameFromJson($field_name)
655
+	{
656
+		if (ModelDataTranslator::isGmtDateFieldName($field_name)) {
657
+			return ModelDataTranslator::removeGmtFromFieldName($field_name);
658
+		}
659
+		return $field_name;
660
+	}
661
+
662
+
663
+
664
+	/**
665
+	 * Takes array of field names from REST API and prepares for models
666
+	 *
667
+	 * @param array $field_names
668
+	 * @return array of field names (possibly include model prefixes)
669
+	 */
670
+	public static function prepareFieldNamesFromJson(array $field_names)
671
+	{
672
+		$new_array = array();
673
+		foreach ($field_names as $key => $field_name) {
674
+			$new_array[$key] = ModelDataTranslator::prepareFieldNameFromJson($field_name);
675
+		}
676
+		return $new_array;
677
+	}
678
+
679
+
680
+
681
+	/**
682
+	 * Takes array where array keys are field names (possibly with model path prefixes)
683
+	 * from the REST API and prepares them for model querying
684
+	 *
685
+	 * @param array $field_names_as_keys
686
+	 * @return array
687
+	 */
688
+	public static function prepareFieldNamesInArrayKeysFromJson(array $field_names_as_keys)
689
+	{
690
+		$new_array = array();
691
+		foreach ($field_names_as_keys as $field_name => $value) {
692
+			$new_array[ModelDataTranslator::prepareFieldNameFromJson($field_name)] = $value;
693
+		}
694
+		return $new_array;
695
+	}
696
+
697
+
698
+
699
+	/**
700
+	 * Prepares an array of model query params for use in the REST API
701
+	 *
702
+	 * @param array     $model_query_params
703
+	 * @param EEM_Base $model
704
+	 * @param string    $requested_version eg "4.8.36". If null is provided, defaults to the latest release of the EE4
705
+	 *                                     REST API
706
+	 * @return array which can be passed into the EE4 REST API when querying a model resource
707
+	 * @throws EE_Error
708
+	 */
709
+	public static function prepareQueryParamsForRestApi(
710
+		array $model_query_params,
711
+		EEM_Base $model,
712
+		$requested_version = null
713
+	) {
714
+		if ($requested_version === null) {
715
+			$requested_version = \EED_Core_Rest_Api::latest_rest_api_version();
716
+		}
717
+		$rest_query_params = $model_query_params;
718
+		if (isset($model_query_params[0])) {
719
+			$rest_query_params['where'] = ModelDataTranslator::prepareConditionsQueryParamsForRestApi(
720
+				$model_query_params[0],
721
+				$model,
722
+				$requested_version
723
+			);
724
+			unset($rest_query_params[0]);
725
+		}
726
+		if (isset($model_query_params['having'])) {
727
+			$rest_query_params['having'] = ModelDataTranslator::prepareConditionsQueryParamsForRestApi(
728
+				$model_query_params['having'],
729
+				$model,
730
+				$requested_version
731
+			);
732
+		}
733
+		return apply_filters(
734
+			'FHEE__EventEspresso\core\libraries\rest_api\Model_Data_Translator__prepare_query_params_for_rest_api',
735
+			$rest_query_params,
736
+			$model_query_params,
737
+			$model,
738
+			$requested_version
739
+		);
740
+	}
741
+
742
+
743
+
744
+	/**
745
+	 * Prepares all the sub-conditions query parameters (eg having or where conditions) for use in the rest api
746
+	 *
747
+	 * @param array     $inputted_query_params_of_this_type eg like the "where" or "having" conditions query params
748
+	 *                                                      passed into EEM_Base::get_all()
749
+	 * @param EEM_Base $model
750
+	 * @param string    $requested_version                  eg "4.8.36"
751
+	 * @return array ready for use in the rest api query params
752
+	 * @throws EE_Error
753
+	 * @throws ObjectDetectedException if somehow a PHP object were in the query params' values,
754
+	 *                                     (which would be really unusual)
755
+	 */
756
+	public static function prepareConditionsQueryParamsForRestApi(
757
+		$inputted_query_params_of_this_type,
758
+		EEM_Base $model,
759
+		$requested_version
760
+	) {
761
+		$query_param_for_models = array();
762
+		foreach ($inputted_query_params_of_this_type as $query_param_key => $query_param_value) {
763
+			$field = ModelDataTranslator::deduceFieldFromQueryParam(
764
+				ModelDataTranslator::removeStarsAndAnythingAfterFromConditionQueryParamKey($query_param_key),
765
+				$model
766
+			);
767
+			if ($field instanceof EE_Model_Field_Base) {
768
+				//did they specify an operator?
769
+				if (is_array($query_param_value)) {
770
+					$op = $query_param_value[0];
771
+					$translated_value = array($op);
772
+					if (isset($query_param_value[1])) {
773
+						$value = $query_param_value[1];
774
+						$translated_value[1] = ModelDataTranslator::prepareFieldValuesForJson(
775
+							$field,
776
+							$value,
777
+							$requested_version
778
+						);
779
+					}
780
+				} else {
781
+					$translated_value = ModelDataTranslator::prepareFieldValueForJson(
782
+						$field,
783
+						$query_param_value,
784
+						$requested_version
785
+					);
786
+				}
787
+				$query_param_for_models[$query_param_key] = $translated_value;
788
+			} else {
789
+				//so it's not for a field, assume it's a logic query param key
790
+				$query_param_for_models[$query_param_key] = ModelDataTranslator::prepareConditionsQueryParamsForRestApi(
791
+					$query_param_value,
792
+					$model,
793
+					$requested_version
794
+				);
795
+			}
796
+		}
797
+		return $query_param_for_models;
798
+	}
799
+
800
+
801
+
802
+	/**
803
+	 * @param $condition_query_param_key
804
+	 * @return string
805
+	 */
806
+	public static function removeStarsAndAnythingAfterFromConditionQueryParamKey($condition_query_param_key)
807
+	{
808
+		$pos_of_star = strpos($condition_query_param_key, '*');
809
+		if ($pos_of_star === false) {
810
+			return $condition_query_param_key;
811
+		} else {
812
+			$condition_query_param_sans_star = substr($condition_query_param_key, 0, $pos_of_star);
813
+			return $condition_query_param_sans_star;
814
+		}
815
+	}
816
+
817
+
818
+
819
+	/**
820
+	 * Takes the input parameter and finds the model field that it indicates.
821
+	 *
822
+	 * @param string    $query_param_name like Registration.Transaction.TXN_ID, Event.Datetime.start_time, or REG_ID
823
+	 * @param EEM_Base $model
824
+	 * @return EE_Model_Field_Base
825
+	 * @throws EE_Error
826
+	 */
827
+	public static function deduceFieldFromQueryParam($query_param_name, EEM_Base $model)
828
+	{
829
+		//ok, now proceed with deducing which part is the model's name, and which is the field's name
830
+		//which will help us find the database table and column
831
+		$query_param_parts = explode('.', $query_param_name);
832
+		if (empty($query_param_parts)) {
833
+			throw new EE_Error(
834
+				sprintf(
835
+					__(
836
+						'_extract_column_name is empty when trying to extract column and table name from %s',
837
+						'event_espresso'
838
+					),
839
+					$query_param_name
840
+				)
841
+			);
842
+		}
843
+		$number_of_parts = count($query_param_parts);
844
+		$last_query_param_part = $query_param_parts[count($query_param_parts) - 1];
845
+		if ($number_of_parts === 1) {
846
+			$field_name = $last_query_param_part;
847
+		} else {// $number_of_parts >= 2
848
+			//the last part is the column name, and there are only 2parts. therefore...
849
+			$field_name = $last_query_param_part;
850
+			$model = \EE_Registry::instance()->load_model($query_param_parts[$number_of_parts - 2]);
851
+		}
852
+		try {
853
+			return $model->field_settings_for($field_name, false);
854
+		} catch (EE_Error $e) {
855
+			return null;
856
+		}
857
+	}
858
+
859
+
860
+
861
+	/**
862
+	 * Returns true if $data can be easily represented in JSON.
863
+	 * Basically, objects and resources can't be represented in JSON easily.
864
+	 * @param mixed $data
865
+	 * @return bool
866
+	 */
867
+	protected static function isRepresentableInJson($data)
868
+	{
869
+		return is_scalar($data)
870
+			   || is_array($data)
871
+			   || is_null($data);
872
+	}
873 873
 }
Please login to merge, or discard this patch.
Spacing   +16 added lines, -16 removed lines patch added patch discarded remove patch
@@ -11,7 +11,7 @@  discard block
 block discarded – undo
11 11
 use EE_Serialized_Text_Field;
12 12
 use EEM_Base;
13 13
 
14
-if (! defined('EVENT_ESPRESSO_VERSION')) {
14
+if ( ! defined('EVENT_ESPRESSO_VERSION')) {
15 15
     exit('No direct script access allowed');
16 16
 }
17 17
 
@@ -228,7 +228,7 @@  discard block
 block discarded – undo
228 228
                 '0',
229 229
                 STR_PAD_LEFT
230 230
             );
231
-        return $original_timestamp . $offset_sign . $offset_string;
231
+        return $original_timestamp.$offset_sign.$offset_string;
232 232
     }
233 233
 
234 234
 
@@ -272,10 +272,10 @@  discard block
 block discarded – undo
272 272
      */
273 273
     private static function parseTimezoneOffset($timezone_offset)
274 274
     {
275
-        $first_char = substr((string)$timezone_offset, 0, 1);
275
+        $first_char = substr((string) $timezone_offset, 0, 1);
276 276
         if ($first_char === '+' || $first_char === '-') {
277 277
             $offset_sign = $first_char;
278
-            $offset_secs = substr((string)$timezone_offset, 1);
278
+            $offset_secs = substr((string) $timezone_offset, 1);
279 279
         } else {
280 280
             $offset_sign = '+';
281 281
             $offset_secs = $timezone_offset;
@@ -302,12 +302,12 @@  discard block
 block discarded – undo
302 302
                 //did they submit a string of a unix timestamp?
303 303
                 if (is_numeric($original_value)) {
304 304
                     $datetime_obj = new \DateTime();
305
-                    $datetime_obj->setTimestamp((int)$original_value);
305
+                    $datetime_obj->setTimestamp((int) $original_value);
306 306
                 } else {
307 307
                     //first, check if its a MySQL timestamp in GMT
308 308
                     $datetime_obj = \DateTime::createFromFormat('Y-m-d H:i:s', $original_value);
309 309
                 }
310
-                if (! $datetime_obj instanceof \DateTime) {
310
+                if ( ! $datetime_obj instanceof \DateTime) {
311 311
                     //so it's not a unix timestamp or a MySQL timestamp. Maybe its in the field's date/time format?
312 312
                     $datetime_obj = $field_obj->prepare_for_set($original_value);
313 313
                 }
@@ -317,7 +317,7 @@  discard block
 block discarded – undo
317 317
                 $new_value = $original_value->format('Y-m-d H:i:s');
318 318
             } elseif (is_int($original_value) || is_float($original_value)) {
319 319
                 $new_value = date('Y-m-d H:i:s', $original_value);
320
-            } elseif($original_value === null || $original_value === '') {
320
+            } elseif ($original_value === null || $original_value === '') {
321 321
                 $new_value = null;
322 322
             } else {
323 323
                 //so it's not a datetime object, unix timestamp (as string or int),
@@ -333,7 +333,7 @@  discard block
 block discarded – undo
333 333
                         $original_value,
334 334
                         $field_obj->get_name(),
335 335
                         $field_obj->get_model_name(),
336
-                        $field_obj->get_time_format() . ' ' . $field_obj->get_time_format()
336
+                        $field_obj->get_time_format().' '.$field_obj->get_time_format()
337 337
                     )
338 338
                 );
339 339
             }
@@ -343,7 +343,7 @@  discard block
 block discarded – undo
343 343
         }
344 344
         //are we about to send an object? just don't. We have no good way to represent it in JSON.
345 345
         // can't just check using is_object() because that missed PHP incomplete objects
346
-        if (! ModelDataTranslator::isRepresentableInJson($new_value)) {
346
+        if ( ! ModelDataTranslator::isRepresentableInJson($new_value)) {
347 347
             $new_value = array(
348 348
                 'error_code' => 'php_object_not_return',
349 349
                 'error_message' => esc_html__('The value of this field in the database is a PHP object, which can\'t be represented in JSON.', 'event_espresso')
@@ -393,7 +393,7 @@  discard block
 block discarded – undo
393 393
                 $model
394 394
             );
395 395
             //double-check is it a *_gmt field?
396
-            if (! $field instanceof EE_Model_Field_Base
396
+            if ( ! $field instanceof EE_Model_Field_Base
397 397
                 && ModelDataTranslator::isGmtDateFieldName($query_param_sans_stars)
398 398
             ) {
399 399
                 //yep, take off '_gmt', and find the field
@@ -412,8 +412,8 @@  discard block
 block discarded – undo
412 412
                 $timezone = $model->get_timezone();
413 413
             }
414 414
             if ($field instanceof EE_Model_Field_Base) {
415
-                if (! $writing && is_array($query_param_value)) {
416
-                    if (! \EEH_Array::is_array_numerically_and_sequentially_indexed($query_param_value)) {
415
+                if ( ! $writing && is_array($query_param_value)) {
416
+                    if ( ! \EEH_Array::is_array_numerically_and_sequentially_indexed($query_param_value)) {
417 417
                         if (defined('EE_REST_API_DEBUG_MODE') && EE_REST_API_DEBUG_MODE) {
418 418
                             throw new RestException(
419 419
                                 'numerically_indexed_array_of_values_only',
@@ -448,7 +448,7 @@  discard block
 block discarded – undo
448 448
                             );
449 449
                         } elseif (array_key_exists($op, $model->valid_between_style_operators())
450 450
                             && isset($query_param_value[1], $query_param_value[2])
451
-                            && !isset($query_param_value[3])
451
+                            && ! isset($query_param_value[3])
452 452
                         ) {
453 453
                             $translated_value[] = ModelDataTranslator::prepareFieldValuesFromJson(
454 454
                                 $field,
@@ -472,10 +472,10 @@  discard block
 block discarded – undo
472 472
                             ModelDataTranslator::throwExceptionIfContainsSerializedData($query_param_value[1]);
473 473
                             $translated_value[] = $query_param_value[1];
474 474
                         } elseif (array_key_exists($op, $model->valid_null_style_operators())
475
-                            && !isset($query_param_value[1])) {
475
+                            && ! isset($query_param_value[1])) {
476 476
                             //no arguments should have been provided, so don't look for any
477 477
                         } elseif (isset($query_param_value[1])
478
-                            && !isset($query_param_value[2])
478
+                            && ! isset($query_param_value[2])
479 479
                             && ! array_key_exists(
480 480
                                 $op,
481 481
                                 array_merge(
@@ -626,7 +626,7 @@  discard block
 block discarded – undo
626 626
      */
627 627
     public static function removeGmtFromFieldName($field_name)
628 628
     {
629
-        if (! ModelDataTranslator::isGmtDateFieldName($field_name)) {
629
+        if ( ! ModelDataTranslator::isGmtDateFieldName($field_name)) {
630 630
             return $field_name;
631 631
         }
632 632
         $query_param_sans_stars = ModelDataTranslator::removeStarsAndAnythingAfterFromConditionQueryParamKey(
Please login to merge, or discard this patch.
core/admin/EE_Admin_Page_CPT.core.php 2 patches
Indentation   +1432 added lines, -1432 removed lines patch added patch discarded remove patch
@@ -1,5 +1,5 @@  discard block
 block discarded – undo
1 1
 <?php if ( ! defined('EVENT_ESPRESSO_VERSION')) {
2
-    exit('No direct script access allowed');
2
+	exit('No direct script access allowed');
3 3
 }
4 4
 
5 5
 /**
@@ -24,470 +24,470 @@  discard block
 block discarded – undo
24 24
 {
25 25
 
26 26
 
27
-    /**
28
-     * This gets set in _setup_cpt
29
-     * It will contain the object for the custom post type.
30
-     *
31
-     * @var EE_CPT_Base
32
-     */
33
-    protected $_cpt_object;
34
-
35
-
36
-
37
-    /**
38
-     * a boolean flag to set whether the current route is a cpt route or not.
39
-     *
40
-     * @var bool
41
-     */
42
-    protected $_cpt_route = false;
43
-
44
-
45
-
46
-    /**
47
-     * This property allows cpt classes to define multiple routes as cpt routes.
48
-     * //in this array we define what the custom post type for this route is.
49
-     * array(
50
-     * 'route_name' => 'custom_post_type_slug'
51
-     * )
52
-     *
53
-     * @var array
54
-     */
55
-    protected $_cpt_routes = array();
56
-
27
+	/**
28
+	 * This gets set in _setup_cpt
29
+	 * It will contain the object for the custom post type.
30
+	 *
31
+	 * @var EE_CPT_Base
32
+	 */
33
+	protected $_cpt_object;
34
+
35
+
36
+
37
+	/**
38
+	 * a boolean flag to set whether the current route is a cpt route or not.
39
+	 *
40
+	 * @var bool
41
+	 */
42
+	protected $_cpt_route = false;
43
+
44
+
45
+
46
+	/**
47
+	 * This property allows cpt classes to define multiple routes as cpt routes.
48
+	 * //in this array we define what the custom post type for this route is.
49
+	 * array(
50
+	 * 'route_name' => 'custom_post_type_slug'
51
+	 * )
52
+	 *
53
+	 * @var array
54
+	 */
55
+	protected $_cpt_routes = array();
56
+
57 57
 
58 58
 
59
-    /**
60
-     * This simply defines what the corresponding routes WP will be redirected to after completing a post save/update.
61
-     * in this format:
62
-     * array(
63
-     * 'post_type_slug' => 'edit_route'
64
-     * )
65
-     *
66
-     * @var array
67
-     */
68
-    protected $_cpt_edit_routes = array();
69
-
70
-
71
-
72
-    /**
73
-     * If child classes set the name of their main model via the $_cpt_obj_models property, EE_Admin_Page_CPT will
74
-     * attempt to retrieve the related object model for the edit pages and assign it to _cpt_page_object. the
75
-     * _cpt_model_names property should be in the following format: array(
76
-     * 'route_defined_by_action_param' => 'Model_Name')
77
-     *
78
-     * @var array $_cpt_model_names
79
-     */
80
-    protected $_cpt_model_names = array();
81
-
82
-
83
-    /**
84
-     * @var EE_CPT_Base
85
-     */
86
-    protected $_cpt_model_obj = false;
87
-
88
-
89
-
90
-    /**
91
-     * This will hold an array of autosave containers that will be used to obtain input values and hook into the WP
92
-     * autosave so we can save our inputs on the save_post hook!  Children classes should add to this array by using
93
-     * the _register_autosave_containers() method so that we don't override any other containers already registered.
94
-     * Registration of containers should be done before load_page_dependencies() is run.
95
-     *
96
-     * @var array()
97
-     */
98
-    protected $_autosave_containers = array();
99
-    protected $_autosave_fields = array();
100
-
101
-    /**
102
-     * Array mapping from admin actions to their equivalent wp core pages for custom post types. So when a user visits
103
-     * a page for an action, it will appear as if they were visiting the wp core page for that custom post type
104
-     *
105
-     * @var array
106
-     */
107
-    protected $_pagenow_map;
108
-
109
-
110
-
111
-    /**
112
-     * This is hooked into the WordPress do_action('save_post') hook and runs after the custom post type has been
113
-     * saved.  Child classes are required to declare this method.  Typically you would use this to save any additional
114
-     * data. Keep in mind also that "save_post" runs on EVERY post update to the database. ALSO very important.  When a
115
-     * post transitions from scheduled to published, the save_post action is fired but you will NOT have any _POST data
116
-     * containing any extra info you may have from other meta saves.  So MAKE sure that you handle this accordingly.
117
-     *
118
-     * @access protected
119
-     * @abstract
120
-     * @param  string $post_id The ID of the cpt that was saved (so you can link relationally)
121
-     * @param  EE_CPT_Base $post    The post object of the cpt that was saved.
122
-     * @return void
123
-     */
124
-    abstract protected function _insert_update_cpt_item($post_id, $post);
125
-
126
-
127
-
128
-    /**
129
-     * This is hooked into the WordPress do_action('trashed_post') hook and runs after a cpt has been trashed.
130
-     *
131
-     * @abstract
132
-     * @access public
133
-     * @param  string $post_id The ID of the cpt that was trashed
134
-     * @return void
135
-     */
136
-    abstract public function trash_cpt_item($post_id);
137
-
138
-
139
-
140
-    /**
141
-     * This is hooked into the WordPress do_action('untrashed_post') hook and runs after a cpt has been untrashed
142
-     *
143
-     * @param  string $post_id theID of the cpt that was untrashed
144
-     * @return void
145
-     */
146
-    abstract public function restore_cpt_item($post_id);
147
-
148
-
149
-
150
-    /**
151
-     * This is hooked into the WordPress do_action('delete_cpt_item') hook and runs after a cpt has been fully deleted
152
-     * from the db
153
-     *
154
-     * @param  string $post_id the ID of the cpt that was deleted
155
-     * @return void
156
-     */
157
-    abstract public function delete_cpt_item($post_id);
158
-
159
-
160
-
161
-    /**
162
-     * Just utilizing the method EE_Admin exposes for doing things before page setup.
163
-     *
164
-     * @access protected
165
-     * @return void
166
-     */
167
-    protected function _before_page_setup()
168
-    {
169
-        $page = isset($this->_req_data['page']) ? $this->_req_data['page'] : $this->page_slug;
170
-        $this->_cpt_routes = array_merge(array(
171
-            'create_new' => $this->page_slug,
172
-            'edit'       => $this->page_slug,
173
-            'trash'      => $this->page_slug,
174
-        ), $this->_cpt_routes);
175
-        //let's see if the current route has a value for cpt_object_slug if it does we use that instead of the page
176
-        $this->_cpt_object = isset($this->_req_data['action']) && isset($this->_cpt_routes[$this->_req_data['action']])
177
-            ? get_post_type_object($this->_cpt_routes[$this->_req_data['action']])
178
-            : get_post_type_object($page);
179
-        //tweak pagenow for page loading.
180
-        if ( ! $this->_pagenow_map) {
181
-            $this->_pagenow_map = array(
182
-                'create_new' => 'post-new.php',
183
-                'edit'       => 'post.php',
184
-                'trash'      => 'post.php',
185
-            );
186
-        }
187
-        add_action('current_screen', array($this, 'modify_pagenow'));
188
-        //TODO the below will need to be reworked to account for the cpt routes that are NOT based off of page but action param.
189
-        //get current page from autosave
190
-        $current_page = isset($this->_req_data['ee_autosave_data']['ee-cpt-hidden-inputs']['current_page'])
191
-            ? $this->_req_data['ee_autosave_data']['ee-cpt-hidden-inputs']['current_page']
192
-            : null;
193
-        $this->_current_page = isset($this->_req_data['current_page'])
194
-            ? $this->_req_data['current_page']
195
-            : $current_page;
196
-        //autosave... make sure its only for the correct page
197
-        //if ( ! empty($this->_current_page) && $this->_current_page == $this->page_slug) {
198
-            //setup autosave ajax hook
199
-            //add_action('wp_ajax_ee-autosave', array( $this, 'do_extra_autosave_stuff' ), 10 ); //TODO reactivate when 4.2 autosave is implemented
200
-        //}
201
-    }
202
-
203
-
204
-
205
-    /**
206
-     * Simply ensure that we simulate the correct post route for cpt screens
207
-     *
208
-     * @param WP_Screen $current_screen
209
-     * @return void
210
-     */
211
-    public function modify_pagenow($current_screen)
212
-    {
213
-        global $pagenow, $hook_suffix;
214
-        //possibly reset pagenow.
215
-        if ( ! empty($this->_req_data['page'])
216
-             && $this->_req_data['page'] == $this->page_slug
217
-             && ! empty($this->_req_data['action'])
218
-             && isset($this->_pagenow_map[$this->_req_data['action']])
219
-        ) {
220
-            $pagenow = $this->_pagenow_map[$this->_req_data['action']];
221
-            $hook_suffix = $pagenow;
222
-        }
223
-    }
224
-
225
-
226
-
227
-    /**
228
-     * This method is used to register additional autosave containers to the _autosave_containers property.
229
-     *
230
-     * @todo We should automate this at some point by creating a wrapper for add_post_metabox and in our wrapper we
231
-     *       automatically register the id for the post metabox as a container.
232
-     * @param  array $ids an array of ids for containers that hold form inputs we want autosave to pickup.  Typically
233
-     *                    you would send along the id of a metabox container.
234
-     * @return void
235
-     */
236
-    protected function _register_autosave_containers($ids)
237
-    {
238
-        $this->_autosave_containers = array_merge($this->_autosave_fields, (array)$ids);
239
-    }
240
-
241
-
242
-
243
-    /**
244
-     * Something nifty.  We're going to loop through all the registered metaboxes and if the CALLBACK is an instance of
245
-     * EE_Admin_Page OR EE_Admin_Hooks, then we'll add the id to our _autosave_containers array.
246
-     */
247
-    protected function _set_autosave_containers()
248
-    {
249
-        global $wp_meta_boxes;
250
-        $containers = array();
251
-        if (empty($wp_meta_boxes)) {
252
-            return;
253
-        }
254
-        $current_metaboxes = isset($wp_meta_boxes[$this->page_slug]) ? $wp_meta_boxes[$this->page_slug] : array();
255
-        foreach ($current_metaboxes as $box_context) {
256
-            foreach ($box_context as $box_details) {
257
-                foreach ($box_details as $box) {
258
-                    if (
259
-                        is_array($box['callback'])
260
-                        && (
261
-                            $box['callback'][0] instanceof EE_Admin_Page
262
-                            || $box['callback'][0] instanceof EE_Admin_Hooks
263
-                        )
264
-                    ) {
265
-                        $containers[] = $box['id'];
266
-                    }
267
-                }
268
-            }
269
-        }
270
-        $this->_autosave_containers = array_merge($this->_autosave_containers, $containers);
271
-        //add hidden inputs container
272
-        $this->_autosave_containers[] = 'ee-cpt-hidden-inputs';
273
-    }
274
-
275
-
276
-
277
-    protected function _load_autosave_scripts_styles()
278
-    {
279
-        /*wp_register_script('cpt-autosave', EE_ADMIN_URL . 'assets/ee-cpt-autosave.js', array('ee-serialize-full-array', 'event_editor_js'), EVENT_ESPRESSO_VERSION, TRUE );
59
+	/**
60
+	 * This simply defines what the corresponding routes WP will be redirected to after completing a post save/update.
61
+	 * in this format:
62
+	 * array(
63
+	 * 'post_type_slug' => 'edit_route'
64
+	 * )
65
+	 *
66
+	 * @var array
67
+	 */
68
+	protected $_cpt_edit_routes = array();
69
+
70
+
71
+
72
+	/**
73
+	 * If child classes set the name of their main model via the $_cpt_obj_models property, EE_Admin_Page_CPT will
74
+	 * attempt to retrieve the related object model for the edit pages and assign it to _cpt_page_object. the
75
+	 * _cpt_model_names property should be in the following format: array(
76
+	 * 'route_defined_by_action_param' => 'Model_Name')
77
+	 *
78
+	 * @var array $_cpt_model_names
79
+	 */
80
+	protected $_cpt_model_names = array();
81
+
82
+
83
+	/**
84
+	 * @var EE_CPT_Base
85
+	 */
86
+	protected $_cpt_model_obj = false;
87
+
88
+
89
+
90
+	/**
91
+	 * This will hold an array of autosave containers that will be used to obtain input values and hook into the WP
92
+	 * autosave so we can save our inputs on the save_post hook!  Children classes should add to this array by using
93
+	 * the _register_autosave_containers() method so that we don't override any other containers already registered.
94
+	 * Registration of containers should be done before load_page_dependencies() is run.
95
+	 *
96
+	 * @var array()
97
+	 */
98
+	protected $_autosave_containers = array();
99
+	protected $_autosave_fields = array();
100
+
101
+	/**
102
+	 * Array mapping from admin actions to their equivalent wp core pages for custom post types. So when a user visits
103
+	 * a page for an action, it will appear as if they were visiting the wp core page for that custom post type
104
+	 *
105
+	 * @var array
106
+	 */
107
+	protected $_pagenow_map;
108
+
109
+
110
+
111
+	/**
112
+	 * This is hooked into the WordPress do_action('save_post') hook and runs after the custom post type has been
113
+	 * saved.  Child classes are required to declare this method.  Typically you would use this to save any additional
114
+	 * data. Keep in mind also that "save_post" runs on EVERY post update to the database. ALSO very important.  When a
115
+	 * post transitions from scheduled to published, the save_post action is fired but you will NOT have any _POST data
116
+	 * containing any extra info you may have from other meta saves.  So MAKE sure that you handle this accordingly.
117
+	 *
118
+	 * @access protected
119
+	 * @abstract
120
+	 * @param  string $post_id The ID of the cpt that was saved (so you can link relationally)
121
+	 * @param  EE_CPT_Base $post    The post object of the cpt that was saved.
122
+	 * @return void
123
+	 */
124
+	abstract protected function _insert_update_cpt_item($post_id, $post);
125
+
126
+
127
+
128
+	/**
129
+	 * This is hooked into the WordPress do_action('trashed_post') hook and runs after a cpt has been trashed.
130
+	 *
131
+	 * @abstract
132
+	 * @access public
133
+	 * @param  string $post_id The ID of the cpt that was trashed
134
+	 * @return void
135
+	 */
136
+	abstract public function trash_cpt_item($post_id);
137
+
138
+
139
+
140
+	/**
141
+	 * This is hooked into the WordPress do_action('untrashed_post') hook and runs after a cpt has been untrashed
142
+	 *
143
+	 * @param  string $post_id theID of the cpt that was untrashed
144
+	 * @return void
145
+	 */
146
+	abstract public function restore_cpt_item($post_id);
147
+
148
+
149
+
150
+	/**
151
+	 * This is hooked into the WordPress do_action('delete_cpt_item') hook and runs after a cpt has been fully deleted
152
+	 * from the db
153
+	 *
154
+	 * @param  string $post_id the ID of the cpt that was deleted
155
+	 * @return void
156
+	 */
157
+	abstract public function delete_cpt_item($post_id);
158
+
159
+
160
+
161
+	/**
162
+	 * Just utilizing the method EE_Admin exposes for doing things before page setup.
163
+	 *
164
+	 * @access protected
165
+	 * @return void
166
+	 */
167
+	protected function _before_page_setup()
168
+	{
169
+		$page = isset($this->_req_data['page']) ? $this->_req_data['page'] : $this->page_slug;
170
+		$this->_cpt_routes = array_merge(array(
171
+			'create_new' => $this->page_slug,
172
+			'edit'       => $this->page_slug,
173
+			'trash'      => $this->page_slug,
174
+		), $this->_cpt_routes);
175
+		//let's see if the current route has a value for cpt_object_slug if it does we use that instead of the page
176
+		$this->_cpt_object = isset($this->_req_data['action']) && isset($this->_cpt_routes[$this->_req_data['action']])
177
+			? get_post_type_object($this->_cpt_routes[$this->_req_data['action']])
178
+			: get_post_type_object($page);
179
+		//tweak pagenow for page loading.
180
+		if ( ! $this->_pagenow_map) {
181
+			$this->_pagenow_map = array(
182
+				'create_new' => 'post-new.php',
183
+				'edit'       => 'post.php',
184
+				'trash'      => 'post.php',
185
+			);
186
+		}
187
+		add_action('current_screen', array($this, 'modify_pagenow'));
188
+		//TODO the below will need to be reworked to account for the cpt routes that are NOT based off of page but action param.
189
+		//get current page from autosave
190
+		$current_page = isset($this->_req_data['ee_autosave_data']['ee-cpt-hidden-inputs']['current_page'])
191
+			? $this->_req_data['ee_autosave_data']['ee-cpt-hidden-inputs']['current_page']
192
+			: null;
193
+		$this->_current_page = isset($this->_req_data['current_page'])
194
+			? $this->_req_data['current_page']
195
+			: $current_page;
196
+		//autosave... make sure its only for the correct page
197
+		//if ( ! empty($this->_current_page) && $this->_current_page == $this->page_slug) {
198
+			//setup autosave ajax hook
199
+			//add_action('wp_ajax_ee-autosave', array( $this, 'do_extra_autosave_stuff' ), 10 ); //TODO reactivate when 4.2 autosave is implemented
200
+		//}
201
+	}
202
+
203
+
204
+
205
+	/**
206
+	 * Simply ensure that we simulate the correct post route for cpt screens
207
+	 *
208
+	 * @param WP_Screen $current_screen
209
+	 * @return void
210
+	 */
211
+	public function modify_pagenow($current_screen)
212
+	{
213
+		global $pagenow, $hook_suffix;
214
+		//possibly reset pagenow.
215
+		if ( ! empty($this->_req_data['page'])
216
+			 && $this->_req_data['page'] == $this->page_slug
217
+			 && ! empty($this->_req_data['action'])
218
+			 && isset($this->_pagenow_map[$this->_req_data['action']])
219
+		) {
220
+			$pagenow = $this->_pagenow_map[$this->_req_data['action']];
221
+			$hook_suffix = $pagenow;
222
+		}
223
+	}
224
+
225
+
226
+
227
+	/**
228
+	 * This method is used to register additional autosave containers to the _autosave_containers property.
229
+	 *
230
+	 * @todo We should automate this at some point by creating a wrapper for add_post_metabox and in our wrapper we
231
+	 *       automatically register the id for the post metabox as a container.
232
+	 * @param  array $ids an array of ids for containers that hold form inputs we want autosave to pickup.  Typically
233
+	 *                    you would send along the id of a metabox container.
234
+	 * @return void
235
+	 */
236
+	protected function _register_autosave_containers($ids)
237
+	{
238
+		$this->_autosave_containers = array_merge($this->_autosave_fields, (array)$ids);
239
+	}
240
+
241
+
242
+
243
+	/**
244
+	 * Something nifty.  We're going to loop through all the registered metaboxes and if the CALLBACK is an instance of
245
+	 * EE_Admin_Page OR EE_Admin_Hooks, then we'll add the id to our _autosave_containers array.
246
+	 */
247
+	protected function _set_autosave_containers()
248
+	{
249
+		global $wp_meta_boxes;
250
+		$containers = array();
251
+		if (empty($wp_meta_boxes)) {
252
+			return;
253
+		}
254
+		$current_metaboxes = isset($wp_meta_boxes[$this->page_slug]) ? $wp_meta_boxes[$this->page_slug] : array();
255
+		foreach ($current_metaboxes as $box_context) {
256
+			foreach ($box_context as $box_details) {
257
+				foreach ($box_details as $box) {
258
+					if (
259
+						is_array($box['callback'])
260
+						&& (
261
+							$box['callback'][0] instanceof EE_Admin_Page
262
+							|| $box['callback'][0] instanceof EE_Admin_Hooks
263
+						)
264
+					) {
265
+						$containers[] = $box['id'];
266
+					}
267
+				}
268
+			}
269
+		}
270
+		$this->_autosave_containers = array_merge($this->_autosave_containers, $containers);
271
+		//add hidden inputs container
272
+		$this->_autosave_containers[] = 'ee-cpt-hidden-inputs';
273
+	}
274
+
275
+
276
+
277
+	protected function _load_autosave_scripts_styles()
278
+	{
279
+		/*wp_register_script('cpt-autosave', EE_ADMIN_URL . 'assets/ee-cpt-autosave.js', array('ee-serialize-full-array', 'event_editor_js'), EVENT_ESPRESSO_VERSION, TRUE );
280 280
         wp_enqueue_script('cpt-autosave');/**/ //todo re-enable when we start doing autosave again in 4.2
281 281
 
282
-        //filter _autosave_containers
283
-        $containers = apply_filters('FHEE__EE_Admin_Page_CPT___load_autosave_scripts_styles__containers',
284
-            $this->_autosave_containers, $this);
285
-        $containers = apply_filters('FHEE__EE_Admin_Page_CPT__' . get_class($this) . '___load_autosave_scripts_styles__containers',
286
-            $containers, $this);
287
-
288
-        wp_localize_script('event_editor_js', 'EE_AUTOSAVE_IDS',
289
-            $containers); //todo once we enable autosaves, this needs to be switched to localize with "cpt-autosave"
290
-
291
-        $unsaved_data_msg = array(
292
-            'eventmsg'     => sprintf(__("The changes you made to this %s will be lost if you navigate away from this page.",
293
-                'event_espresso'), $this->_cpt_object->labels->singular_name),
294
-            'inputChanged' => 0,
295
-        );
296
-        wp_localize_script('event_editor_js', 'UNSAVED_DATA_MSG', $unsaved_data_msg);
297
-    }
298
-
299
-
300
-
301
-    public function load_page_dependencies()
302
-    {
303
-        try {
304
-            $this->_load_page_dependencies();
305
-        } catch (EE_Error $e) {
306
-            $e->get_error();
307
-        }
308
-    }
309
-
310
-
311
-
312
-    /**
313
-     * overloading the EE_Admin_Page parent load_page_dependencies so we can get the cpt stuff added in appropriately
314
-     *
315
-     * @access protected
316
-     * @return void
317
-     */
318
-    protected function _load_page_dependencies()
319
-    {
320
-        //we only add stuff if this is a cpt_route!
321
-        if ( ! $this->_cpt_route) {
322
-            parent::_load_page_dependencies();
323
-            return;
324
-        }
325
-        // now let's do some automatic filters into the wp_system
326
-        // and we'll check to make sure the CHILD class
327
-        // automatically has the required methods in place.
328
-        // the following filters are for setting all the redirects
329
-        // on DEFAULT WP custom post type actions
330
-        // let's add a hidden input to the post-edit form
331
-        // so we know when we have to trigger our custom redirects!
332
-        // Otherwise the redirects will happen on ALL post saves which wouldn't be good of course!
333
-        add_action('edit_form_after_title', array($this, 'cpt_post_form_hidden_input'));
334
-        // inject our Admin page nav tabs...
335
-        // let's make sure the nav tabs are set if they aren't already
336
-        // if ( empty( $this->_nav_tabs ) ) $this->_set_nav_tabs();
337
-        add_action('post_edit_form_tag', array($this, 'inject_nav_tabs'));
338
-        // modify the post_updated messages array
339
-        add_action('post_updated_messages', array($this, 'post_update_messages'), 10);
340
-        // add shortlink button to cpt edit screens.  We can do this as a universal thing BECAUSE,
341
-        // cpts use the same format for shortlinks as posts!
342
-        add_filter('pre_get_shortlink', array($this, 'add_shortlink_button_to_editor'), 10, 4);
343
-        // This basically allows us to change the title of the "publish" metabox area
344
-        // on CPT pages by setting a 'publishbox' value in the $_labels property array in the child class.
345
-        if ( ! empty($this->_labels['publishbox'])) {
346
-            $box_label = is_array($this->_labels['publishbox'])
347
-                         && isset($this->_labels['publishbox'][$this->_req_action])
348
-                    ? $this->_labels['publishbox'][$this->_req_action]
349
-                    : $this->_labels['publishbox'];
350
-            add_meta_box(
351
-                'submitdiv',
352
-                $box_label,
353
-                'post_submit_meta_box',
354
-                $this->_cpt_routes[$this->_req_action],
355
-                'side',
356
-                'core'
357
-            );
358
-        }
359
-        //let's add page_templates metabox if this cpt added support for it.
360
-        if ($this->_supports_page_templates($this->_cpt_object->name)) {
361
-            add_meta_box(
362
-                'page_templates',
363
-                __('Page Template', 'event_espresso'),
364
-                array($this, 'page_template_meta_box'),
365
-                $this->_cpt_routes[$this->_req_action],
366
-                'side',
367
-                'default'
368
-            );
369
-        }
370
-        //this is a filter that allows the addition of extra html after the permalink field on the wp post edit-form
371
-        if (method_exists($this, 'extra_permalink_field_buttons')) {
372
-            add_filter('get_sample_permalink_html', array($this, 'extra_permalink_field_buttons'), 10, 4);
373
-        }
374
-        //add preview button
375
-        add_filter('get_sample_permalink_html', array($this, 'preview_button_html'), 5, 4);
376
-        //insert our own post_stati dropdown
377
-        add_action('post_submitbox_misc_actions', array($this, 'custom_post_stati_dropdown'), 10);
378
-        //This allows adding additional information to the publish post submitbox on the wp post edit form
379
-        if (method_exists($this, 'extra_misc_actions_publish_box')) {
380
-            add_action('post_submitbox_misc_actions', array($this, 'extra_misc_actions_publish_box'), 10);
381
-        }
382
-        // This allows for adding additional stuff after the title field on the wp post edit form.
383
-        // This is also before the wp_editor for post description field.
384
-        if (method_exists($this, 'edit_form_after_title')) {
385
-            add_action('edit_form_after_title', array($this, 'edit_form_after_title'), 10);
386
-        }
387
-        /**
388
-         * Filtering WP's esc_url to capture urls pointing to core wp routes so they point to our route.
389
-         */
390
-        add_filter('clean_url', array($this, 'switch_core_wp_urls_with_ours'), 10, 3);
391
-        parent::_load_page_dependencies();
392
-        // notice we are ALSO going to load the pagenow hook set for this route
393
-        // (see _before_page_setup for the reset of the pagenow global ).
394
-        // This is for any plugins that are doing things properly
395
-        // and hooking into the load page hook for core wp cpt routes.
396
-        global $pagenow;
397
-        do_action('load-' . $pagenow);
398
-        $this->modify_current_screen();
399
-        add_action('admin_enqueue_scripts', array($this, 'setup_autosave_hooks'), 30);
400
-        //we route REALLY early.
401
-        try {
402
-            $this->_route_admin_request();
403
-        } catch (EE_Error $e) {
404
-            $e->get_error();
405
-        }
406
-    }
407
-
408
-
409
-
410
-    /**
411
-     * Since we don't want users going to default core wp routes, this will check any wp urls run through the
412
-     * esc_url() method and if we see a url matching a pattern for our routes, we'll modify it to point to OUR
413
-     * route instead.
414
-     *
415
-     * @param string $good_protocol_url The escaped url.
416
-     * @param string $original_url      The original url.
417
-     * @param string $_context          The context sent to the esc_url method.
418
-     * @return string possibly a new url for our route.
419
-     */
420
-    public function switch_core_wp_urls_with_ours($good_protocol_url, $original_url, $_context)
421
-    {
422
-        $routes_to_match = array(
423
-            0 => array(
424
-                'edit.php?post_type=espresso_attendees',
425
-                'admin.php?page=espresso_registrations&action=contact_list',
426
-            ),
427
-            1 => array(
428
-                'edit.php?post_type=' . $this->_cpt_object->name,
429
-                'admin.php?page=' . $this->_cpt_object->name,
430
-            ),
431
-        );
432
-        foreach ($routes_to_match as $route_matches) {
433
-            if (strpos($good_protocol_url, $route_matches[0]) !== false) {
434
-                return str_replace($route_matches[0], $route_matches[1], $good_protocol_url);
435
-            }
436
-        }
437
-        return $good_protocol_url;
438
-    }
439
-
440
-
441
-
442
-    /**
443
-     * Determine whether the current cpt supports page templates or not.
444
-     *
445
-     * @since %VER%
446
-     * @param string $cpt_name The cpt slug we're checking on.
447
-     * @return bool True supported, false not.
448
-     */
449
-    private function _supports_page_templates($cpt_name)
450
-    {
451
-
452
-        $cpt_args = EE_Register_CPTs::get_CPTs();
453
-        $cpt_args = isset($cpt_args[$cpt_name]) ? $cpt_args[$cpt_name]['args'] : array();
454
-        $cpt_has_support = ! empty($cpt_args['page_templates']);
455
-
456
-        //if the installed version of WP is > 4.7 we do some additional checks.
457
-        if (EE_Recommended_Versions::check_wp_version('4.7','>=')) {
458
-            $post_templates = wp_get_theme()->get_post_templates();
459
-            //if there are $post_templates for this cpt, then we return false for this method because
460
-            //that means we aren't going to load our page template manager and leave that up to the native
461
-            //cpt template manager.
462
-            $cpt_has_support = ! isset($post_templates[$cpt_name]) ? $cpt_has_support : false;
463
-        }
464
-
465
-        return $cpt_has_support;
466
-    }
467
-
468
-
469
-    /**
470
-     * Callback for the page_templates metabox selector.
471
-     *
472
-     * @since %VER%
473
-     * @return void
474
-     */
475
-    public function page_template_meta_box()
476
-    {
477
-        global $post;
478
-        $template = '';
479
-
480
-        if (EE_Recommended_Versions::check_wp_version('4.7','>=')) {
481
-            $page_template_count = count(get_page_templates());
482
-        } else {
483
-            $page_template_count = count(get_page_templates($post));
484
-        };
485
-
486
-        if ($page_template_count) {
487
-            $page_template = get_post_meta($post->ID, '_wp_page_template', true);
488
-            $template      = ! empty($page_template) ? $page_template : '';
489
-        }
490
-        ?>
282
+		//filter _autosave_containers
283
+		$containers = apply_filters('FHEE__EE_Admin_Page_CPT___load_autosave_scripts_styles__containers',
284
+			$this->_autosave_containers, $this);
285
+		$containers = apply_filters('FHEE__EE_Admin_Page_CPT__' . get_class($this) . '___load_autosave_scripts_styles__containers',
286
+			$containers, $this);
287
+
288
+		wp_localize_script('event_editor_js', 'EE_AUTOSAVE_IDS',
289
+			$containers); //todo once we enable autosaves, this needs to be switched to localize with "cpt-autosave"
290
+
291
+		$unsaved_data_msg = array(
292
+			'eventmsg'     => sprintf(__("The changes you made to this %s will be lost if you navigate away from this page.",
293
+				'event_espresso'), $this->_cpt_object->labels->singular_name),
294
+			'inputChanged' => 0,
295
+		);
296
+		wp_localize_script('event_editor_js', 'UNSAVED_DATA_MSG', $unsaved_data_msg);
297
+	}
298
+
299
+
300
+
301
+	public function load_page_dependencies()
302
+	{
303
+		try {
304
+			$this->_load_page_dependencies();
305
+		} catch (EE_Error $e) {
306
+			$e->get_error();
307
+		}
308
+	}
309
+
310
+
311
+
312
+	/**
313
+	 * overloading the EE_Admin_Page parent load_page_dependencies so we can get the cpt stuff added in appropriately
314
+	 *
315
+	 * @access protected
316
+	 * @return void
317
+	 */
318
+	protected function _load_page_dependencies()
319
+	{
320
+		//we only add stuff if this is a cpt_route!
321
+		if ( ! $this->_cpt_route) {
322
+			parent::_load_page_dependencies();
323
+			return;
324
+		}
325
+		// now let's do some automatic filters into the wp_system
326
+		// and we'll check to make sure the CHILD class
327
+		// automatically has the required methods in place.
328
+		// the following filters are for setting all the redirects
329
+		// on DEFAULT WP custom post type actions
330
+		// let's add a hidden input to the post-edit form
331
+		// so we know when we have to trigger our custom redirects!
332
+		// Otherwise the redirects will happen on ALL post saves which wouldn't be good of course!
333
+		add_action('edit_form_after_title', array($this, 'cpt_post_form_hidden_input'));
334
+		// inject our Admin page nav tabs...
335
+		// let's make sure the nav tabs are set if they aren't already
336
+		// if ( empty( $this->_nav_tabs ) ) $this->_set_nav_tabs();
337
+		add_action('post_edit_form_tag', array($this, 'inject_nav_tabs'));
338
+		// modify the post_updated messages array
339
+		add_action('post_updated_messages', array($this, 'post_update_messages'), 10);
340
+		// add shortlink button to cpt edit screens.  We can do this as a universal thing BECAUSE,
341
+		// cpts use the same format for shortlinks as posts!
342
+		add_filter('pre_get_shortlink', array($this, 'add_shortlink_button_to_editor'), 10, 4);
343
+		// This basically allows us to change the title of the "publish" metabox area
344
+		// on CPT pages by setting a 'publishbox' value in the $_labels property array in the child class.
345
+		if ( ! empty($this->_labels['publishbox'])) {
346
+			$box_label = is_array($this->_labels['publishbox'])
347
+						 && isset($this->_labels['publishbox'][$this->_req_action])
348
+					? $this->_labels['publishbox'][$this->_req_action]
349
+					: $this->_labels['publishbox'];
350
+			add_meta_box(
351
+				'submitdiv',
352
+				$box_label,
353
+				'post_submit_meta_box',
354
+				$this->_cpt_routes[$this->_req_action],
355
+				'side',
356
+				'core'
357
+			);
358
+		}
359
+		//let's add page_templates metabox if this cpt added support for it.
360
+		if ($this->_supports_page_templates($this->_cpt_object->name)) {
361
+			add_meta_box(
362
+				'page_templates',
363
+				__('Page Template', 'event_espresso'),
364
+				array($this, 'page_template_meta_box'),
365
+				$this->_cpt_routes[$this->_req_action],
366
+				'side',
367
+				'default'
368
+			);
369
+		}
370
+		//this is a filter that allows the addition of extra html after the permalink field on the wp post edit-form
371
+		if (method_exists($this, 'extra_permalink_field_buttons')) {
372
+			add_filter('get_sample_permalink_html', array($this, 'extra_permalink_field_buttons'), 10, 4);
373
+		}
374
+		//add preview button
375
+		add_filter('get_sample_permalink_html', array($this, 'preview_button_html'), 5, 4);
376
+		//insert our own post_stati dropdown
377
+		add_action('post_submitbox_misc_actions', array($this, 'custom_post_stati_dropdown'), 10);
378
+		//This allows adding additional information to the publish post submitbox on the wp post edit form
379
+		if (method_exists($this, 'extra_misc_actions_publish_box')) {
380
+			add_action('post_submitbox_misc_actions', array($this, 'extra_misc_actions_publish_box'), 10);
381
+		}
382
+		// This allows for adding additional stuff after the title field on the wp post edit form.
383
+		// This is also before the wp_editor for post description field.
384
+		if (method_exists($this, 'edit_form_after_title')) {
385
+			add_action('edit_form_after_title', array($this, 'edit_form_after_title'), 10);
386
+		}
387
+		/**
388
+		 * Filtering WP's esc_url to capture urls pointing to core wp routes so they point to our route.
389
+		 */
390
+		add_filter('clean_url', array($this, 'switch_core_wp_urls_with_ours'), 10, 3);
391
+		parent::_load_page_dependencies();
392
+		// notice we are ALSO going to load the pagenow hook set for this route
393
+		// (see _before_page_setup for the reset of the pagenow global ).
394
+		// This is for any plugins that are doing things properly
395
+		// and hooking into the load page hook for core wp cpt routes.
396
+		global $pagenow;
397
+		do_action('load-' . $pagenow);
398
+		$this->modify_current_screen();
399
+		add_action('admin_enqueue_scripts', array($this, 'setup_autosave_hooks'), 30);
400
+		//we route REALLY early.
401
+		try {
402
+			$this->_route_admin_request();
403
+		} catch (EE_Error $e) {
404
+			$e->get_error();
405
+		}
406
+	}
407
+
408
+
409
+
410
+	/**
411
+	 * Since we don't want users going to default core wp routes, this will check any wp urls run through the
412
+	 * esc_url() method and if we see a url matching a pattern for our routes, we'll modify it to point to OUR
413
+	 * route instead.
414
+	 *
415
+	 * @param string $good_protocol_url The escaped url.
416
+	 * @param string $original_url      The original url.
417
+	 * @param string $_context          The context sent to the esc_url method.
418
+	 * @return string possibly a new url for our route.
419
+	 */
420
+	public function switch_core_wp_urls_with_ours($good_protocol_url, $original_url, $_context)
421
+	{
422
+		$routes_to_match = array(
423
+			0 => array(
424
+				'edit.php?post_type=espresso_attendees',
425
+				'admin.php?page=espresso_registrations&action=contact_list',
426
+			),
427
+			1 => array(
428
+				'edit.php?post_type=' . $this->_cpt_object->name,
429
+				'admin.php?page=' . $this->_cpt_object->name,
430
+			),
431
+		);
432
+		foreach ($routes_to_match as $route_matches) {
433
+			if (strpos($good_protocol_url, $route_matches[0]) !== false) {
434
+				return str_replace($route_matches[0], $route_matches[1], $good_protocol_url);
435
+			}
436
+		}
437
+		return $good_protocol_url;
438
+	}
439
+
440
+
441
+
442
+	/**
443
+	 * Determine whether the current cpt supports page templates or not.
444
+	 *
445
+	 * @since %VER%
446
+	 * @param string $cpt_name The cpt slug we're checking on.
447
+	 * @return bool True supported, false not.
448
+	 */
449
+	private function _supports_page_templates($cpt_name)
450
+	{
451
+
452
+		$cpt_args = EE_Register_CPTs::get_CPTs();
453
+		$cpt_args = isset($cpt_args[$cpt_name]) ? $cpt_args[$cpt_name]['args'] : array();
454
+		$cpt_has_support = ! empty($cpt_args['page_templates']);
455
+
456
+		//if the installed version of WP is > 4.7 we do some additional checks.
457
+		if (EE_Recommended_Versions::check_wp_version('4.7','>=')) {
458
+			$post_templates = wp_get_theme()->get_post_templates();
459
+			//if there are $post_templates for this cpt, then we return false for this method because
460
+			//that means we aren't going to load our page template manager and leave that up to the native
461
+			//cpt template manager.
462
+			$cpt_has_support = ! isset($post_templates[$cpt_name]) ? $cpt_has_support : false;
463
+		}
464
+
465
+		return $cpt_has_support;
466
+	}
467
+
468
+
469
+	/**
470
+	 * Callback for the page_templates metabox selector.
471
+	 *
472
+	 * @since %VER%
473
+	 * @return void
474
+	 */
475
+	public function page_template_meta_box()
476
+	{
477
+		global $post;
478
+		$template = '';
479
+
480
+		if (EE_Recommended_Versions::check_wp_version('4.7','>=')) {
481
+			$page_template_count = count(get_page_templates());
482
+		} else {
483
+			$page_template_count = count(get_page_templates($post));
484
+		};
485
+
486
+		if ($page_template_count) {
487
+			$page_template = get_post_meta($post->ID, '_wp_page_template', true);
488
+			$template      = ! empty($page_template) ? $page_template : '';
489
+		}
490
+		?>
491 491
         <p><strong><?php _e('Template') ?></strong></p>
492 492
         <label class="screen-reader-text" for="page_template"><?php _e('Page Template') ?></label><select
493 493
             name="page_template" id="page_template">
@@ -495,450 +495,450 @@  discard block
 block discarded – undo
495 495
         <?php page_template_dropdown($template); ?>
496 496
     </select>
497 497
         <?php
498
-    }
499
-
500
-
501
-
502
-    /**
503
-     * if this post is a draft or scheduled post then we provide a preview button for user to click
504
-     * Method is called from parent and is hooked into the wp 'get_sample_permalink_html' filter.
505
-     *
506
-     * @param  string $return    the current html
507
-     * @param  int    $id        the post id for the page
508
-     * @param  string $new_title What the title is
509
-     * @param  string $new_slug  what the slug is
510
-     * @return string            The new html string for the permalink area
511
-     */
512
-    public function preview_button_html($return, $id, $new_title, $new_slug)
513
-    {
514
-        $post = get_post($id);
515
-        if ('publish' !== get_post_status($post)) {
516
-            //include shims for the `get_preview_post_link` function
517
-            require_once( EE_CORE . 'wordpress-shims.php' );
518
-            $return .= '<span_id="view-post-btn"><a target="_blank" href="'
519
-                       . get_preview_post_link($id)
520
-                       . '" class="button button-small">'
521
-                       . __('Preview', 'event_espresso')
522
-                       . '</a></span>'
523
-                       . "\n";
524
-        }
525
-        return $return;
526
-    }
527
-
528
-
529
-
530
-    /**
531
-     * add our custom post stati dropdown on the wp post page for this cpt
532
-     *
533
-     * @return void
534
-     */
535
-    public function custom_post_stati_dropdown()
536
-    {
537
-
538
-        $statuses         = $this->_cpt_model_obj->get_custom_post_statuses();
539
-        $cur_status_label = array_key_exists($this->_cpt_model_obj->status(), $statuses)
540
-            ? $statuses[$this->_cpt_model_obj->status()]
541
-            : '';
542
-        $template_args    = array(
543
-            'cur_status'            => $this->_cpt_model_obj->status(),
544
-            'statuses'              => $statuses,
545
-            'cur_status_label'      => $cur_status_label,
546
-            'localized_status_save' => sprintf(__('Save %s', 'event_espresso'), $cur_status_label),
547
-        );
548
-        //we'll add a trash post status (WP doesn't add one for some reason)
549
-        if ($this->_cpt_model_obj->status() === 'trash') {
550
-            $template_args['cur_status_label'] = __('Trashed', 'event_espresso');
551
-            $statuses['trash']                 = __('Trashed', 'event_espresso');
552
-            $template_args['statuses']         = $statuses;
553
-        }
554
-
555
-        $template = EE_ADMIN_TEMPLATE . 'status_dropdown.template.php';
556
-        EEH_Template::display_template($template, $template_args);
557
-    }
558
-
559
-
560
-
561
-    public function setup_autosave_hooks()
562
-    {
563
-        $this->_set_autosave_containers();
564
-        $this->_load_autosave_scripts_styles();
565
-    }
566
-
567
-
568
-
569
-    /**
570
-     * This is run on all WordPress autosaves AFTER the autosave is complete and sends along a $_POST object (available
571
-     * in $this->_req_data) containing: post_ID of the saved post autosavenonce for the saved post We'll do the check
572
-     * for the nonce in here, but then this method looks for two things:
573
-     * 1. Execute a method (if exists) matching 'ee_autosave_' and appended with the given route. OR
574
-     * 2. do_actions() for global or class specific actions that have been registered (for plugins/addons not in an
575
-     * EE_Admin_Page class. PLEASE NOTE: Data will be returned using the _return_json() object and so the
576
-     * $_template_args property should be used to hold the $data array.  We're expecting the following things set in
577
-     * template args.
578
-     *    1. $template_args['error'] = IF there is an error you can add the message in here.
579
-     *    2. $template_args['data']['items'] = an array of items that are setup in key index pairs of 'where_values_go'
580
-     *    => 'values_to_add'.  In other words, for the datetime metabox we'll have something like
581
-     *    $this->_template_args['data']['items'] = array(
582
-     *        'event-datetime-ids' => '1,2,3';
583
-     *    );
584
-     *    Keep in mind the following things:
585
-     *    - "where" index is for the input with the id as that string.
586
-     *    - "what" index is what will be used for the value of that input.
587
-     *
588
-     * @return void
589
-     */
590
-    public function do_extra_autosave_stuff()
591
-    {
592
-        //next let's check for the autosave nonce (we'll use _verify_nonce )
593
-        $nonce = isset($this->_req_data['autosavenonce'])
594
-                ? $this->_req_data['autosavenonce']
595
-                : null;
596
-        $this->_verify_nonce($nonce, 'autosave');
597
-        //make sure we define doing autosave (cause WP isn't triggering this we want to make sure we define it)
598
-        if ( ! defined('DOING_AUTOSAVE')) {
599
-            define('DOING_AUTOSAVE', true);
600
-        }
601
-        //if we made it here then the nonce checked out.  Let's run our methods and actions
602
-        $autosave = "_ee_autosave_{$this->_current_view}";
603
-        if (method_exists($this, $autosave)) {
604
-            $this->$autosave();
605
-        } else {
606
-            $this->_template_args['success'] = true;
607
-        }
608
-        do_action('AHEE__EE_Admin_Page_CPT__do_extra_autosave_stuff__global_after', $this);
609
-        do_action('AHEE__EE_Admin_Page_CPT__do_extra_autosave_stuff__after_' . get_class($this), $this);
610
-        //now let's return json
611
-        $this->_return_json();
612
-    }
613
-
614
-
615
-
616
-    /**
617
-     * This takes care of setting up default routes and pages that utilize the core WP admin pages.
618
-     * Child classes can override the defaults (in cases for adding metaboxes etc.)
619
-     * but take care that you include the defaults here otherwise your core WP admin pages for the cpt won't work!
620
-     *
621
-     * @access protected
622
-     * @throws EE_Error
623
-     * @return void
624
-     */
625
-    protected function _extend_page_config_for_cpt()
626
-    {
627
-        //before doing anything we need to make sure this runs ONLY when the loaded page matches the set page_slug
628
-        if (isset($this->_req_data['page']) && $this->_req_data['page'] !== $this->page_slug) {
629
-            return;
630
-        }
631
-        //set page routes and page config but ONLY if we're not viewing a custom setup cpt route as defined in _cpt_routes
632
-        if ( ! empty($this->_cpt_object)) {
633
-            $this->_page_routes = array_merge(array(
634
-                'create_new' => '_create_new_cpt_item',
635
-                'edit'       => '_edit_cpt_item',
636
-            ), $this->_page_routes);
637
-            $this->_page_config = array_merge(array(
638
-                'create_new' => array(
639
-                    'nav'           => array(
640
-                        'label' => $this->_cpt_object->labels->add_new_item,
641
-                        'order' => 5,
642
-                    ),
643
-                    'require_nonce' => false,
644
-                ),
645
-                'edit'       => array(
646
-                    'nav'           => array(
647
-                        'label'      => $this->_cpt_object->labels->edit_item,
648
-                        'order'      => 5,
649
-                        'persistent' => false,
650
-                        'url'        => '',
651
-                    ),
652
-                    'require_nonce' => false,
653
-                ),
654
-            ),
655
-                $this->_page_config
656
-            );
657
-        }
658
-        //load the next section only if this is a matching cpt route as set in the cpt routes array.
659
-        if ( ! isset($this->_cpt_routes[$this->_req_action])) {
660
-            return;
661
-        }
662
-        $this->_cpt_route = isset($this->_cpt_routes[$this->_req_action]) ? true : false;
663
-        //add_action('FHEE__EE_Admin_Page___load_page_dependencies__after_load', array( $this, 'modify_current_screen') );
664
-        if (empty($this->_cpt_object)) {
665
-            $msg = sprintf(__('This page has been set as being related to a registered custom post type, however, the custom post type object could not be retrieved. There are two possible reasons for this:  1. The "%s" does not match a registered post type. or 2. The custom post type is not registered for the "%s" action as indexed in the "$_cpt_routes" property on this class (%s).'),
666
-                $this->page_slug, $this->_req_action, get_class($this));
667
-            throw new EE_Error($msg);
668
-        }
669
-        if ($this->_cpt_route) {
670
-            $id = isset($this->_req_data['post']) ? $this->_req_data['post'] : null;
671
-            $this->_set_model_object($id);
672
-        }
673
-    }
674
-
675
-
676
-
677
-    /**
678
-     * Sets the _cpt_model_object property using what has been set for the _cpt_model_name and a given id.
679
-     *
680
-     * @access protected
681
-     * @param int  $id The id to retrieve the model object for. If empty we set a default object.
682
-     * @param bool $ignore_route_check
683
-     * @param string $req_type whether the current route is for inserting, updating, or deleting the CPT
684
-     * @throws EE_Error
685
-     */
686
-    protected function _set_model_object($id = null, $ignore_route_check = false, $req_type = '')
687
-    {
688
-        $model = null;
689
-        if (
690
-            empty($this->_cpt_model_names)
691
-            || (
692
-                ! $ignore_route_check
693
-                && ! isset($this->_cpt_routes[$this->_req_action])
694
-            ) || (
695
-                $this->_cpt_model_obj instanceof EE_CPT_Base
696
-                && $this->_cpt_model_obj->ID() === $id
697
-            )
698
-        ) {
699
-            //get out cuz we either don't have a model name OR the object has already been set and it has the same id as what has been sent.
700
-            return;
701
-        }
702
-        //if ignore_route_check is true, then get the model name via EE_Register_CPTs
703
-        if ($ignore_route_check) {
704
-            $model_names = EE_Register_CPTs::get_cpt_model_names();
705
-            $post_type   = get_post_type($id);
706
-            if (isset($model_names[$post_type])) {
707
-                $model = EE_Registry::instance()->load_model($model_names[$post_type]);
708
-            }
709
-        } else {
710
-            $model = EE_Registry::instance()->load_model($this->_cpt_model_names[$this->_req_action]);
711
-        }
712
-        if ($model instanceof EEM_Base) {
713
-            $this->_cpt_model_obj = ! empty($id) ? $model->get_one_by_ID($id) : $model->create_default_object();
714
-        }
715
-        do_action(
716
-            'AHEE__EE_Admin_Page_CPT__set_model_object__after_set_object',
717
-            $this->_cpt_model_obj,
718
-            $req_type
719
-        );
720
-    }
721
-
722
-
723
-
724
-    /**
725
-     * admin_init_global
726
-     * This runs all the code that we want executed within the WP admin_init hook.
727
-     * This method executes for ALL EE Admin pages.
728
-     *
729
-     * @access public
730
-     * @return void
731
-     */
732
-    public function admin_init_global()
733
-    {
734
-        $post = isset($this->_req_data['post']) ? get_post($this->_req_data['post']) : null;
735
-        //its possible this is a new save so let's catch that instead
736
-        $post = isset($this->_req_data['post_ID']) ? get_post($this->_req_data['post_ID']) : $post;
737
-        $post_type = $post ? $post->post_type : false;
738
-        $current_route = isset($this->_req_data['current_route'])
739
-            ? $this->_req_data['current_route']
740
-            : 'shouldneverwork';
741
-        $route_to_check = $post_type && isset($this->_cpt_routes[$current_route])
742
-            ? $this->_cpt_routes[$current_route]
743
-            : '';
744
-        add_filter('get_delete_post_link', array($this, 'modify_delete_post_link'), 10, 3);
745
-        add_filter('get_edit_post_link', array($this, 'modify_edit_post_link'), 10, 3);
746
-        if ($post_type === $route_to_check) {
747
-            add_filter('redirect_post_location', array($this, 'cpt_post_location_redirect'), 10, 2);
748
-        }
749
-        //now let's filter redirect if we're on a revision page and the revision is for an event CPT.
750
-        $revision = isset($this->_req_data['revision']) ? $this->_req_data['revision'] : null;
751
-        if ( ! empty($revision)) {
752
-            $action = isset($this->_req_data['action']) ? $this->_req_data['action'] : null;
753
-            //doing a restore?
754
-            if ( ! empty($action) && $action === 'restore') {
755
-                //get post for revision
756
-                $rev_post = get_post($revision);
757
-                $rev_parent = get_post($rev_post->post_parent);
758
-                //only do our redirect filter AND our restore revision action if the post_type for the parent is one of our cpts.
759
-                if ($rev_parent && $rev_parent->post_type === $this->page_slug) {
760
-                    add_filter('wp_redirect', array($this, 'revision_redirect'), 10, 2);
761
-                    //restores of revisions
762
-                    add_action('wp_restore_post_revision', array($this, 'restore_revision'), 10, 2);
763
-                }
764
-            }
765
-        }
766
-        //NOTE we ONLY want to run these hooks if we're on the right class for the given post type.  Otherwise we could see some really freaky things happen!
767
-        if ($post_type && $post_type === $route_to_check) {
768
-            //$post_id, $post
769
-            add_action('save_post', array($this, 'insert_update'), 10, 3);
770
-            //$post_id
771
-            add_action('trashed_post', array($this, 'before_trash_cpt_item'), 10);
772
-            add_action('trashed_post', array($this, 'dont_permanently_delete_ee_cpts'), 10);
773
-            add_action('untrashed_post', array($this, 'before_restore_cpt_item'), 10);
774
-            add_action('after_delete_post', array($this, 'before_delete_cpt_item'), 10);
775
-        }
776
-    }
777
-
778
-
779
-
780
-    /**
781
-     * Callback for the WordPress trashed_post hook.
782
-     * Execute some basic checks before calling the trash_cpt_item declared in the child class.
783
-     *
784
-     * @param int $post_id
785
-     * @throws \EE_Error
786
-     */
787
-    public function before_trash_cpt_item($post_id)
788
-    {
789
-        $this->_set_model_object($post_id, true, 'trash');
790
-        //if our cpt object isn't existent then get out immediately.
791
-        if ( ! $this->_cpt_model_obj instanceof EE_CPT_Base || $this->_cpt_model_obj->ID() !== $post_id) {
792
-            return;
793
-        }
794
-        $this->trash_cpt_item($post_id);
795
-    }
796
-
797
-
798
-
799
-    /**
800
-     * Callback for the WordPress untrashed_post hook.
801
-     * Execute some basic checks before calling the restore_cpt_method in the child class.
802
-     *
803
-     * @param $post_id
804
-     * @throws \EE_Error
805
-     */
806
-    public function before_restore_cpt_item($post_id)
807
-    {
808
-        $this->_set_model_object($post_id, true, 'restore');
809
-        //if our cpt object isn't existent then get out immediately.
810
-        if ( ! $this->_cpt_model_obj instanceof EE_CPT_Base || $this->_cpt_model_obj->ID() !== $post_id) {
811
-            return;
812
-        }
813
-        $this->restore_cpt_item($post_id);
814
-    }
815
-
816
-
817
-
818
-    /**
819
-     * Callback for the WordPress after_delete_post hook.
820
-     * Execute some basic checks before calling the delete_cpt_item method in the child class.
821
-     *
822
-     * @param $post_id
823
-     * @throws \EE_Error
824
-     */
825
-    public function before_delete_cpt_item($post_id)
826
-    {
827
-        $this->_set_model_object($post_id, true, 'delete');
828
-        //if our cpt object isn't existent then get out immediately.
829
-        if ( ! $this->_cpt_model_obj instanceof EE_CPT_Base || $this->_cpt_model_obj->ID() !== $post_id) {
830
-            return;
831
-        }
832
-        $this->delete_cpt_item($post_id);
833
-    }
834
-
835
-
836
-
837
-    /**
838
-     * This simply verifies if the cpt_model_object is instantiated for the given page and throws an error message
839
-     * accordingly.
840
-     *
841
-     * @access public
842
-     * @throws EE_Error
843
-     * @return void
844
-     */
845
-    public function verify_cpt_object()
846
-    {
847
-        $label = ! empty($this->_cpt_object) ? $this->_cpt_object->labels->singular_name : $this->page_label;
848
-        // verify event object
849
-        if ( ! $this->_cpt_model_obj instanceof EE_CPT_Base) {
850
-            throw new EE_Error(sprintf(__('Something has gone wrong with the page load because we are unable to set up the object for the %1$s.  This usually happens when the given id for the page route is NOT for the correct custom post type for this page',
851
-                    'event_espresso'), $label));
852
-        }
853
-        //if auto-draft then throw an error
854
-        if ($this->_cpt_model_obj->get('status') === 'auto-draft') {
855
-            EE_Error::overwrite_errors();
856
-            EE_Error::add_error(sprintf(__('This %1$s was saved without a title, description, or excerpt which means that none of the extra details you added were saved properly.  All autodrafts will show up in the "draft" view of your event list table.  You can delete them from there. Please click the "Add %1$s" button to refresh and restart.'),
857
-                    $label), __FILE__, __FUNCTION__, __LINE__);
858
-        }
859
-    }
860
-
861
-
862
-
863
-    /**
864
-     * admin_footer_scripts_global
865
-     * Anything triggered by the 'admin_print_footer_scripts' WP hook should be put in here. This particular method
866
-     * will apply on ALL EE_Admin pages.
867
-     *
868
-     * @access public
869
-     * @return void
870
-     */
871
-    public function admin_footer_scripts_global()
872
-    {
873
-        $this->_add_admin_page_ajax_loading_img();
874
-        $this->_add_admin_page_overlay();
875
-    }
876
-
877
-
878
-
879
-    /**
880
-     * add in any global scripts for cpt routes
881
-     *
882
-     * @return void
883
-     */
884
-    public function load_global_scripts_styles()
885
-    {
886
-        parent::load_global_scripts_styles();
887
-        if ($this->_cpt_model_obj instanceof EE_CPT_Base) {
888
-            //setup custom post status object for localize script but only if we've got a cpt object
889
-            $statuses = $this->_cpt_model_obj->get_custom_post_statuses();
890
-            if ( ! empty($statuses)) {
891
-                //get ALL statuses!
892
-                $statuses = $this->_cpt_model_obj->get_all_post_statuses();
893
-                //setup object
894
-                $ee_cpt_statuses = array();
895
-                foreach ($statuses as $status => $label) {
896
-                    $ee_cpt_statuses[$status] = array(
897
-                        'label'      => $label,
898
-                        'save_label' => sprintf(__('Save as %s', 'event_espresso'), $label),
899
-                    );
900
-                }
901
-                wp_localize_script('ee_admin_js', 'eeCPTstatuses', $ee_cpt_statuses);
902
-            }
903
-        }
904
-    }
905
-
906
-
907
-
908
-    /**
909
-     * This is a wrapper for the insert/update routes for cpt items so we can add things that are common to ALL
910
-     * insert/updates
911
-     *
912
-     * @param  int     $post_id ID of post being updated
913
-     * @param  WP_Post $post    Post object from WP
914
-     * @param  bool    $update  Whether this is an update or a new save.
915
-     * @return void
916
-     * @throws \EE_Error
917
-     */
918
-    public function insert_update($post_id, $post, $update)
919
-    {
920
-        //make sure that if this is a revision OR trash action that we don't do any updates!
921
-        if (
922
-            isset($this->_req_data['action'])
923
-            && (
924
-                $this->_req_data['action'] === 'restore'
925
-                || $this->_req_data['action'] === 'trash'
926
-            )
927
-        ) {
928
-            return;
929
-        }
930
-        $this->_set_model_object($post_id, true, 'insert_update');
931
-        //if our cpt object is not instantiated and its NOT the same post_id as what is triggering this callback, then exit.
932
-        if ($update
933
-            && (
934
-                ! $this->_cpt_model_obj instanceof EE_CPT_Base
935
-                || $this->_cpt_model_obj->ID() !== $post_id
936
-            )
937
-        ) {
938
-            return;
939
-        }
940
-        //check for autosave and update our req_data property accordingly.
941
-        /*if ( defined('DOING_AUTOSAVE') && DOING_AUTOSAVE && isset( $this->_req_data['ee_autosave_data'] ) ) {
498
+	}
499
+
500
+
501
+
502
+	/**
503
+	 * if this post is a draft or scheduled post then we provide a preview button for user to click
504
+	 * Method is called from parent and is hooked into the wp 'get_sample_permalink_html' filter.
505
+	 *
506
+	 * @param  string $return    the current html
507
+	 * @param  int    $id        the post id for the page
508
+	 * @param  string $new_title What the title is
509
+	 * @param  string $new_slug  what the slug is
510
+	 * @return string            The new html string for the permalink area
511
+	 */
512
+	public function preview_button_html($return, $id, $new_title, $new_slug)
513
+	{
514
+		$post = get_post($id);
515
+		if ('publish' !== get_post_status($post)) {
516
+			//include shims for the `get_preview_post_link` function
517
+			require_once( EE_CORE . 'wordpress-shims.php' );
518
+			$return .= '<span_id="view-post-btn"><a target="_blank" href="'
519
+					   . get_preview_post_link($id)
520
+					   . '" class="button button-small">'
521
+					   . __('Preview', 'event_espresso')
522
+					   . '</a></span>'
523
+					   . "\n";
524
+		}
525
+		return $return;
526
+	}
527
+
528
+
529
+
530
+	/**
531
+	 * add our custom post stati dropdown on the wp post page for this cpt
532
+	 *
533
+	 * @return void
534
+	 */
535
+	public function custom_post_stati_dropdown()
536
+	{
537
+
538
+		$statuses         = $this->_cpt_model_obj->get_custom_post_statuses();
539
+		$cur_status_label = array_key_exists($this->_cpt_model_obj->status(), $statuses)
540
+			? $statuses[$this->_cpt_model_obj->status()]
541
+			: '';
542
+		$template_args    = array(
543
+			'cur_status'            => $this->_cpt_model_obj->status(),
544
+			'statuses'              => $statuses,
545
+			'cur_status_label'      => $cur_status_label,
546
+			'localized_status_save' => sprintf(__('Save %s', 'event_espresso'), $cur_status_label),
547
+		);
548
+		//we'll add a trash post status (WP doesn't add one for some reason)
549
+		if ($this->_cpt_model_obj->status() === 'trash') {
550
+			$template_args['cur_status_label'] = __('Trashed', 'event_espresso');
551
+			$statuses['trash']                 = __('Trashed', 'event_espresso');
552
+			$template_args['statuses']         = $statuses;
553
+		}
554
+
555
+		$template = EE_ADMIN_TEMPLATE . 'status_dropdown.template.php';
556
+		EEH_Template::display_template($template, $template_args);
557
+	}
558
+
559
+
560
+
561
+	public function setup_autosave_hooks()
562
+	{
563
+		$this->_set_autosave_containers();
564
+		$this->_load_autosave_scripts_styles();
565
+	}
566
+
567
+
568
+
569
+	/**
570
+	 * This is run on all WordPress autosaves AFTER the autosave is complete and sends along a $_POST object (available
571
+	 * in $this->_req_data) containing: post_ID of the saved post autosavenonce for the saved post We'll do the check
572
+	 * for the nonce in here, but then this method looks for two things:
573
+	 * 1. Execute a method (if exists) matching 'ee_autosave_' and appended with the given route. OR
574
+	 * 2. do_actions() for global or class specific actions that have been registered (for plugins/addons not in an
575
+	 * EE_Admin_Page class. PLEASE NOTE: Data will be returned using the _return_json() object and so the
576
+	 * $_template_args property should be used to hold the $data array.  We're expecting the following things set in
577
+	 * template args.
578
+	 *    1. $template_args['error'] = IF there is an error you can add the message in here.
579
+	 *    2. $template_args['data']['items'] = an array of items that are setup in key index pairs of 'where_values_go'
580
+	 *    => 'values_to_add'.  In other words, for the datetime metabox we'll have something like
581
+	 *    $this->_template_args['data']['items'] = array(
582
+	 *        'event-datetime-ids' => '1,2,3';
583
+	 *    );
584
+	 *    Keep in mind the following things:
585
+	 *    - "where" index is for the input with the id as that string.
586
+	 *    - "what" index is what will be used for the value of that input.
587
+	 *
588
+	 * @return void
589
+	 */
590
+	public function do_extra_autosave_stuff()
591
+	{
592
+		//next let's check for the autosave nonce (we'll use _verify_nonce )
593
+		$nonce = isset($this->_req_data['autosavenonce'])
594
+				? $this->_req_data['autosavenonce']
595
+				: null;
596
+		$this->_verify_nonce($nonce, 'autosave');
597
+		//make sure we define doing autosave (cause WP isn't triggering this we want to make sure we define it)
598
+		if ( ! defined('DOING_AUTOSAVE')) {
599
+			define('DOING_AUTOSAVE', true);
600
+		}
601
+		//if we made it here then the nonce checked out.  Let's run our methods and actions
602
+		$autosave = "_ee_autosave_{$this->_current_view}";
603
+		if (method_exists($this, $autosave)) {
604
+			$this->$autosave();
605
+		} else {
606
+			$this->_template_args['success'] = true;
607
+		}
608
+		do_action('AHEE__EE_Admin_Page_CPT__do_extra_autosave_stuff__global_after', $this);
609
+		do_action('AHEE__EE_Admin_Page_CPT__do_extra_autosave_stuff__after_' . get_class($this), $this);
610
+		//now let's return json
611
+		$this->_return_json();
612
+	}
613
+
614
+
615
+
616
+	/**
617
+	 * This takes care of setting up default routes and pages that utilize the core WP admin pages.
618
+	 * Child classes can override the defaults (in cases for adding metaboxes etc.)
619
+	 * but take care that you include the defaults here otherwise your core WP admin pages for the cpt won't work!
620
+	 *
621
+	 * @access protected
622
+	 * @throws EE_Error
623
+	 * @return void
624
+	 */
625
+	protected function _extend_page_config_for_cpt()
626
+	{
627
+		//before doing anything we need to make sure this runs ONLY when the loaded page matches the set page_slug
628
+		if (isset($this->_req_data['page']) && $this->_req_data['page'] !== $this->page_slug) {
629
+			return;
630
+		}
631
+		//set page routes and page config but ONLY if we're not viewing a custom setup cpt route as defined in _cpt_routes
632
+		if ( ! empty($this->_cpt_object)) {
633
+			$this->_page_routes = array_merge(array(
634
+				'create_new' => '_create_new_cpt_item',
635
+				'edit'       => '_edit_cpt_item',
636
+			), $this->_page_routes);
637
+			$this->_page_config = array_merge(array(
638
+				'create_new' => array(
639
+					'nav'           => array(
640
+						'label' => $this->_cpt_object->labels->add_new_item,
641
+						'order' => 5,
642
+					),
643
+					'require_nonce' => false,
644
+				),
645
+				'edit'       => array(
646
+					'nav'           => array(
647
+						'label'      => $this->_cpt_object->labels->edit_item,
648
+						'order'      => 5,
649
+						'persistent' => false,
650
+						'url'        => '',
651
+					),
652
+					'require_nonce' => false,
653
+				),
654
+			),
655
+				$this->_page_config
656
+			);
657
+		}
658
+		//load the next section only if this is a matching cpt route as set in the cpt routes array.
659
+		if ( ! isset($this->_cpt_routes[$this->_req_action])) {
660
+			return;
661
+		}
662
+		$this->_cpt_route = isset($this->_cpt_routes[$this->_req_action]) ? true : false;
663
+		//add_action('FHEE__EE_Admin_Page___load_page_dependencies__after_load', array( $this, 'modify_current_screen') );
664
+		if (empty($this->_cpt_object)) {
665
+			$msg = sprintf(__('This page has been set as being related to a registered custom post type, however, the custom post type object could not be retrieved. There are two possible reasons for this:  1. The "%s" does not match a registered post type. or 2. The custom post type is not registered for the "%s" action as indexed in the "$_cpt_routes" property on this class (%s).'),
666
+				$this->page_slug, $this->_req_action, get_class($this));
667
+			throw new EE_Error($msg);
668
+		}
669
+		if ($this->_cpt_route) {
670
+			$id = isset($this->_req_data['post']) ? $this->_req_data['post'] : null;
671
+			$this->_set_model_object($id);
672
+		}
673
+	}
674
+
675
+
676
+
677
+	/**
678
+	 * Sets the _cpt_model_object property using what has been set for the _cpt_model_name and a given id.
679
+	 *
680
+	 * @access protected
681
+	 * @param int  $id The id to retrieve the model object for. If empty we set a default object.
682
+	 * @param bool $ignore_route_check
683
+	 * @param string $req_type whether the current route is for inserting, updating, or deleting the CPT
684
+	 * @throws EE_Error
685
+	 */
686
+	protected function _set_model_object($id = null, $ignore_route_check = false, $req_type = '')
687
+	{
688
+		$model = null;
689
+		if (
690
+			empty($this->_cpt_model_names)
691
+			|| (
692
+				! $ignore_route_check
693
+				&& ! isset($this->_cpt_routes[$this->_req_action])
694
+			) || (
695
+				$this->_cpt_model_obj instanceof EE_CPT_Base
696
+				&& $this->_cpt_model_obj->ID() === $id
697
+			)
698
+		) {
699
+			//get out cuz we either don't have a model name OR the object has already been set and it has the same id as what has been sent.
700
+			return;
701
+		}
702
+		//if ignore_route_check is true, then get the model name via EE_Register_CPTs
703
+		if ($ignore_route_check) {
704
+			$model_names = EE_Register_CPTs::get_cpt_model_names();
705
+			$post_type   = get_post_type($id);
706
+			if (isset($model_names[$post_type])) {
707
+				$model = EE_Registry::instance()->load_model($model_names[$post_type]);
708
+			}
709
+		} else {
710
+			$model = EE_Registry::instance()->load_model($this->_cpt_model_names[$this->_req_action]);
711
+		}
712
+		if ($model instanceof EEM_Base) {
713
+			$this->_cpt_model_obj = ! empty($id) ? $model->get_one_by_ID($id) : $model->create_default_object();
714
+		}
715
+		do_action(
716
+			'AHEE__EE_Admin_Page_CPT__set_model_object__after_set_object',
717
+			$this->_cpt_model_obj,
718
+			$req_type
719
+		);
720
+	}
721
+
722
+
723
+
724
+	/**
725
+	 * admin_init_global
726
+	 * This runs all the code that we want executed within the WP admin_init hook.
727
+	 * This method executes for ALL EE Admin pages.
728
+	 *
729
+	 * @access public
730
+	 * @return void
731
+	 */
732
+	public function admin_init_global()
733
+	{
734
+		$post = isset($this->_req_data['post']) ? get_post($this->_req_data['post']) : null;
735
+		//its possible this is a new save so let's catch that instead
736
+		$post = isset($this->_req_data['post_ID']) ? get_post($this->_req_data['post_ID']) : $post;
737
+		$post_type = $post ? $post->post_type : false;
738
+		$current_route = isset($this->_req_data['current_route'])
739
+			? $this->_req_data['current_route']
740
+			: 'shouldneverwork';
741
+		$route_to_check = $post_type && isset($this->_cpt_routes[$current_route])
742
+			? $this->_cpt_routes[$current_route]
743
+			: '';
744
+		add_filter('get_delete_post_link', array($this, 'modify_delete_post_link'), 10, 3);
745
+		add_filter('get_edit_post_link', array($this, 'modify_edit_post_link'), 10, 3);
746
+		if ($post_type === $route_to_check) {
747
+			add_filter('redirect_post_location', array($this, 'cpt_post_location_redirect'), 10, 2);
748
+		}
749
+		//now let's filter redirect if we're on a revision page and the revision is for an event CPT.
750
+		$revision = isset($this->_req_data['revision']) ? $this->_req_data['revision'] : null;
751
+		if ( ! empty($revision)) {
752
+			$action = isset($this->_req_data['action']) ? $this->_req_data['action'] : null;
753
+			//doing a restore?
754
+			if ( ! empty($action) && $action === 'restore') {
755
+				//get post for revision
756
+				$rev_post = get_post($revision);
757
+				$rev_parent = get_post($rev_post->post_parent);
758
+				//only do our redirect filter AND our restore revision action if the post_type for the parent is one of our cpts.
759
+				if ($rev_parent && $rev_parent->post_type === $this->page_slug) {
760
+					add_filter('wp_redirect', array($this, 'revision_redirect'), 10, 2);
761
+					//restores of revisions
762
+					add_action('wp_restore_post_revision', array($this, 'restore_revision'), 10, 2);
763
+				}
764
+			}
765
+		}
766
+		//NOTE we ONLY want to run these hooks if we're on the right class for the given post type.  Otherwise we could see some really freaky things happen!
767
+		if ($post_type && $post_type === $route_to_check) {
768
+			//$post_id, $post
769
+			add_action('save_post', array($this, 'insert_update'), 10, 3);
770
+			//$post_id
771
+			add_action('trashed_post', array($this, 'before_trash_cpt_item'), 10);
772
+			add_action('trashed_post', array($this, 'dont_permanently_delete_ee_cpts'), 10);
773
+			add_action('untrashed_post', array($this, 'before_restore_cpt_item'), 10);
774
+			add_action('after_delete_post', array($this, 'before_delete_cpt_item'), 10);
775
+		}
776
+	}
777
+
778
+
779
+
780
+	/**
781
+	 * Callback for the WordPress trashed_post hook.
782
+	 * Execute some basic checks before calling the trash_cpt_item declared in the child class.
783
+	 *
784
+	 * @param int $post_id
785
+	 * @throws \EE_Error
786
+	 */
787
+	public function before_trash_cpt_item($post_id)
788
+	{
789
+		$this->_set_model_object($post_id, true, 'trash');
790
+		//if our cpt object isn't existent then get out immediately.
791
+		if ( ! $this->_cpt_model_obj instanceof EE_CPT_Base || $this->_cpt_model_obj->ID() !== $post_id) {
792
+			return;
793
+		}
794
+		$this->trash_cpt_item($post_id);
795
+	}
796
+
797
+
798
+
799
+	/**
800
+	 * Callback for the WordPress untrashed_post hook.
801
+	 * Execute some basic checks before calling the restore_cpt_method in the child class.
802
+	 *
803
+	 * @param $post_id
804
+	 * @throws \EE_Error
805
+	 */
806
+	public function before_restore_cpt_item($post_id)
807
+	{
808
+		$this->_set_model_object($post_id, true, 'restore');
809
+		//if our cpt object isn't existent then get out immediately.
810
+		if ( ! $this->_cpt_model_obj instanceof EE_CPT_Base || $this->_cpt_model_obj->ID() !== $post_id) {
811
+			return;
812
+		}
813
+		$this->restore_cpt_item($post_id);
814
+	}
815
+
816
+
817
+
818
+	/**
819
+	 * Callback for the WordPress after_delete_post hook.
820
+	 * Execute some basic checks before calling the delete_cpt_item method in the child class.
821
+	 *
822
+	 * @param $post_id
823
+	 * @throws \EE_Error
824
+	 */
825
+	public function before_delete_cpt_item($post_id)
826
+	{
827
+		$this->_set_model_object($post_id, true, 'delete');
828
+		//if our cpt object isn't existent then get out immediately.
829
+		if ( ! $this->_cpt_model_obj instanceof EE_CPT_Base || $this->_cpt_model_obj->ID() !== $post_id) {
830
+			return;
831
+		}
832
+		$this->delete_cpt_item($post_id);
833
+	}
834
+
835
+
836
+
837
+	/**
838
+	 * This simply verifies if the cpt_model_object is instantiated for the given page and throws an error message
839
+	 * accordingly.
840
+	 *
841
+	 * @access public
842
+	 * @throws EE_Error
843
+	 * @return void
844
+	 */
845
+	public function verify_cpt_object()
846
+	{
847
+		$label = ! empty($this->_cpt_object) ? $this->_cpt_object->labels->singular_name : $this->page_label;
848
+		// verify event object
849
+		if ( ! $this->_cpt_model_obj instanceof EE_CPT_Base) {
850
+			throw new EE_Error(sprintf(__('Something has gone wrong with the page load because we are unable to set up the object for the %1$s.  This usually happens when the given id for the page route is NOT for the correct custom post type for this page',
851
+					'event_espresso'), $label));
852
+		}
853
+		//if auto-draft then throw an error
854
+		if ($this->_cpt_model_obj->get('status') === 'auto-draft') {
855
+			EE_Error::overwrite_errors();
856
+			EE_Error::add_error(sprintf(__('This %1$s was saved without a title, description, or excerpt which means that none of the extra details you added were saved properly.  All autodrafts will show up in the "draft" view of your event list table.  You can delete them from there. Please click the "Add %1$s" button to refresh and restart.'),
857
+					$label), __FILE__, __FUNCTION__, __LINE__);
858
+		}
859
+	}
860
+
861
+
862
+
863
+	/**
864
+	 * admin_footer_scripts_global
865
+	 * Anything triggered by the 'admin_print_footer_scripts' WP hook should be put in here. This particular method
866
+	 * will apply on ALL EE_Admin pages.
867
+	 *
868
+	 * @access public
869
+	 * @return void
870
+	 */
871
+	public function admin_footer_scripts_global()
872
+	{
873
+		$this->_add_admin_page_ajax_loading_img();
874
+		$this->_add_admin_page_overlay();
875
+	}
876
+
877
+
878
+
879
+	/**
880
+	 * add in any global scripts for cpt routes
881
+	 *
882
+	 * @return void
883
+	 */
884
+	public function load_global_scripts_styles()
885
+	{
886
+		parent::load_global_scripts_styles();
887
+		if ($this->_cpt_model_obj instanceof EE_CPT_Base) {
888
+			//setup custom post status object for localize script but only if we've got a cpt object
889
+			$statuses = $this->_cpt_model_obj->get_custom_post_statuses();
890
+			if ( ! empty($statuses)) {
891
+				//get ALL statuses!
892
+				$statuses = $this->_cpt_model_obj->get_all_post_statuses();
893
+				//setup object
894
+				$ee_cpt_statuses = array();
895
+				foreach ($statuses as $status => $label) {
896
+					$ee_cpt_statuses[$status] = array(
897
+						'label'      => $label,
898
+						'save_label' => sprintf(__('Save as %s', 'event_espresso'), $label),
899
+					);
900
+				}
901
+				wp_localize_script('ee_admin_js', 'eeCPTstatuses', $ee_cpt_statuses);
902
+			}
903
+		}
904
+	}
905
+
906
+
907
+
908
+	/**
909
+	 * This is a wrapper for the insert/update routes for cpt items so we can add things that are common to ALL
910
+	 * insert/updates
911
+	 *
912
+	 * @param  int     $post_id ID of post being updated
913
+	 * @param  WP_Post $post    Post object from WP
914
+	 * @param  bool    $update  Whether this is an update or a new save.
915
+	 * @return void
916
+	 * @throws \EE_Error
917
+	 */
918
+	public function insert_update($post_id, $post, $update)
919
+	{
920
+		//make sure that if this is a revision OR trash action that we don't do any updates!
921
+		if (
922
+			isset($this->_req_data['action'])
923
+			&& (
924
+				$this->_req_data['action'] === 'restore'
925
+				|| $this->_req_data['action'] === 'trash'
926
+			)
927
+		) {
928
+			return;
929
+		}
930
+		$this->_set_model_object($post_id, true, 'insert_update');
931
+		//if our cpt object is not instantiated and its NOT the same post_id as what is triggering this callback, then exit.
932
+		if ($update
933
+			&& (
934
+				! $this->_cpt_model_obj instanceof EE_CPT_Base
935
+				|| $this->_cpt_model_obj->ID() !== $post_id
936
+			)
937
+		) {
938
+			return;
939
+		}
940
+		//check for autosave and update our req_data property accordingly.
941
+		/*if ( defined('DOING_AUTOSAVE') && DOING_AUTOSAVE && isset( $this->_req_data['ee_autosave_data'] ) ) {
942 942
             foreach( (array) $this->_req_data['ee_autosave_data'] as $id => $values ) {
943 943
 
944 944
                 foreach ( (array) $values as $key => $value ) {
@@ -948,542 +948,542 @@  discard block
 block discarded – undo
948 948
 
949 949
         }/**/ //TODO reactivate after autosave is implemented in 4.2
950 950
 
951
-        //take care of updating any selected page_template IF this cpt supports it.
952
-        if ($this->_supports_page_templates($post->post_type) && ! empty($this->_req_data['page_template'])) {
953
-            //wp version aware.
954
-            if (EE_Recommended_Versions::check_wp_version('4.7', '>=')) {
955
-                $page_templates = wp_get_theme()->get_page_templates();
956
-            } else {
957
-                $post->page_template = $this->_req_data['page_template'];
958
-                $page_templates      = wp_get_theme()->get_page_templates($post);
959
-            }
960
-            if ('default' != $this->_req_data['page_template'] && ! isset($page_templates[$this->_req_data['page_template']])) {
961
-                EE_Error::add_error(__('Invalid Page Template.', 'event_espresso'), __FILE__, __FUNCTION__, __LINE__);
962
-            } else {
963
-                update_post_meta($post_id, '_wp_page_template', $this->_req_data['page_template']);
964
-            }
965
-        }
966
-        if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
967
-            return;
968
-        } //TODO we'll remove this after reimplementing autosave in 4.2
969
-        $this->_insert_update_cpt_item($post_id, $post);
970
-    }
971
-
972
-
973
-
974
-    /**
975
-     * This hooks into the wp_trash_post() function and removes the `_wp_trash_meta_status` and `_wp_trash_meta_time`
976
-     * post meta IF the trashed post is one of our CPT's - note this method should only be called with our cpt routes
977
-     * so we don't have to check for our CPT.
978
-     *
979
-     * @param  int $post_id ID of the post
980
-     * @return void
981
-     */
982
-    public function dont_permanently_delete_ee_cpts($post_id)
983
-    {
984
-        //only do this if we're actually processing one of our CPTs
985
-        //if our cpt object isn't existent then get out immediately.
986
-        if ( ! $this->_cpt_model_obj instanceof EE_CPT_Base) {
987
-            return;
988
-        }
989
-        delete_post_meta($post_id, '_wp_trash_meta_status');
990
-        delete_post_meta($post_id, '_wp_trash_meta_time');
991
-        //our cpts may have comments so let's take care of that too
992
-        delete_post_meta($post_id, '_wp_trash_meta_comments_status');
993
-    }
994
-
995
-
996
-
997
-    /**
998
-     * This is a wrapper for the restore_cpt_revision route for cpt items so we can make sure that when a revision is
999
-     * triggered that we restore related items.  In order to work cpt classes MUST have a restore_cpt_revision method
1000
-     * in them. We also have our OWN action in here so addons can hook into the restore process easily.
1001
-     *
1002
-     * @param  int $post_id     ID of cpt item
1003
-     * @param  int $revision_id ID of revision being restored
1004
-     * @return void
1005
-     */
1006
-    public function restore_revision($post_id, $revision_id)
1007
-    {
1008
-        $this->_restore_cpt_item($post_id, $revision_id);
1009
-        //global action
1010
-        do_action('AHEE_EE_Admin_Page_CPT__restore_revision', $post_id, $revision_id);
1011
-        //class specific action so you can limit hooking into a specific page.
1012
-        do_action('AHEE_EE_Admin_Page_CPT_' . get_class($this) . '__restore_revision', $post_id, $revision_id);
1013
-    }
1014
-
1015
-
1016
-
1017
-    /**
1018
-     * @see restore_revision() for details
1019
-     * @param  int $post_id     ID of cpt item
1020
-     * @param  int $revision_id ID of revision for item
1021
-     * @return void
1022
-     */
1023
-    abstract protected function _restore_cpt_item($post_id, $revision_id);
1024
-
1025
-
1026
-
1027
-    /**
1028
-     * Execution of this method is added to the end of the load_page_dependencies method in the parent
1029
-     * so that we can fix a bug where default core metaboxes were not being called in the sidebar.
1030
-     * To fix we have to reset the current_screen using the page_slug
1031
-     * (which is identical - or should be - to our registered_post_type id.)
1032
-     * Also, since the core WP file loads the admin_header.php for WP
1033
-     * (and there are a bunch of other things edit-form-advanced.php loads that need to happen really early)
1034
-     * we need to load it NOW, hence our _route_admin_request in here. (Otherwise screen options won't be set).
1035
-     *
1036
-     * @return void
1037
-     */
1038
-    public function modify_current_screen()
1039
-    {
1040
-        //ONLY do this if the current page_route IS a cpt route
1041
-        if ( ! $this->_cpt_route) {
1042
-            return;
1043
-        }
1044
-        //routing things REALLY early b/c this is a cpt admin page
1045
-        set_current_screen($this->_cpt_routes[$this->_req_action]);
1046
-        $this->_current_screen       = get_current_screen();
1047
-        $this->_current_screen->base = 'event-espresso';
1048
-        $this->_add_help_tabs(); //we make sure we add any help tabs back in!
1049
-        /*try {
951
+		//take care of updating any selected page_template IF this cpt supports it.
952
+		if ($this->_supports_page_templates($post->post_type) && ! empty($this->_req_data['page_template'])) {
953
+			//wp version aware.
954
+			if (EE_Recommended_Versions::check_wp_version('4.7', '>=')) {
955
+				$page_templates = wp_get_theme()->get_page_templates();
956
+			} else {
957
+				$post->page_template = $this->_req_data['page_template'];
958
+				$page_templates      = wp_get_theme()->get_page_templates($post);
959
+			}
960
+			if ('default' != $this->_req_data['page_template'] && ! isset($page_templates[$this->_req_data['page_template']])) {
961
+				EE_Error::add_error(__('Invalid Page Template.', 'event_espresso'), __FILE__, __FUNCTION__, __LINE__);
962
+			} else {
963
+				update_post_meta($post_id, '_wp_page_template', $this->_req_data['page_template']);
964
+			}
965
+		}
966
+		if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
967
+			return;
968
+		} //TODO we'll remove this after reimplementing autosave in 4.2
969
+		$this->_insert_update_cpt_item($post_id, $post);
970
+	}
971
+
972
+
973
+
974
+	/**
975
+	 * This hooks into the wp_trash_post() function and removes the `_wp_trash_meta_status` and `_wp_trash_meta_time`
976
+	 * post meta IF the trashed post is one of our CPT's - note this method should only be called with our cpt routes
977
+	 * so we don't have to check for our CPT.
978
+	 *
979
+	 * @param  int $post_id ID of the post
980
+	 * @return void
981
+	 */
982
+	public function dont_permanently_delete_ee_cpts($post_id)
983
+	{
984
+		//only do this if we're actually processing one of our CPTs
985
+		//if our cpt object isn't existent then get out immediately.
986
+		if ( ! $this->_cpt_model_obj instanceof EE_CPT_Base) {
987
+			return;
988
+		}
989
+		delete_post_meta($post_id, '_wp_trash_meta_status');
990
+		delete_post_meta($post_id, '_wp_trash_meta_time');
991
+		//our cpts may have comments so let's take care of that too
992
+		delete_post_meta($post_id, '_wp_trash_meta_comments_status');
993
+	}
994
+
995
+
996
+
997
+	/**
998
+	 * This is a wrapper for the restore_cpt_revision route for cpt items so we can make sure that when a revision is
999
+	 * triggered that we restore related items.  In order to work cpt classes MUST have a restore_cpt_revision method
1000
+	 * in them. We also have our OWN action in here so addons can hook into the restore process easily.
1001
+	 *
1002
+	 * @param  int $post_id     ID of cpt item
1003
+	 * @param  int $revision_id ID of revision being restored
1004
+	 * @return void
1005
+	 */
1006
+	public function restore_revision($post_id, $revision_id)
1007
+	{
1008
+		$this->_restore_cpt_item($post_id, $revision_id);
1009
+		//global action
1010
+		do_action('AHEE_EE_Admin_Page_CPT__restore_revision', $post_id, $revision_id);
1011
+		//class specific action so you can limit hooking into a specific page.
1012
+		do_action('AHEE_EE_Admin_Page_CPT_' . get_class($this) . '__restore_revision', $post_id, $revision_id);
1013
+	}
1014
+
1015
+
1016
+
1017
+	/**
1018
+	 * @see restore_revision() for details
1019
+	 * @param  int $post_id     ID of cpt item
1020
+	 * @param  int $revision_id ID of revision for item
1021
+	 * @return void
1022
+	 */
1023
+	abstract protected function _restore_cpt_item($post_id, $revision_id);
1024
+
1025
+
1026
+
1027
+	/**
1028
+	 * Execution of this method is added to the end of the load_page_dependencies method in the parent
1029
+	 * so that we can fix a bug where default core metaboxes were not being called in the sidebar.
1030
+	 * To fix we have to reset the current_screen using the page_slug
1031
+	 * (which is identical - or should be - to our registered_post_type id.)
1032
+	 * Also, since the core WP file loads the admin_header.php for WP
1033
+	 * (and there are a bunch of other things edit-form-advanced.php loads that need to happen really early)
1034
+	 * we need to load it NOW, hence our _route_admin_request in here. (Otherwise screen options won't be set).
1035
+	 *
1036
+	 * @return void
1037
+	 */
1038
+	public function modify_current_screen()
1039
+	{
1040
+		//ONLY do this if the current page_route IS a cpt route
1041
+		if ( ! $this->_cpt_route) {
1042
+			return;
1043
+		}
1044
+		//routing things REALLY early b/c this is a cpt admin page
1045
+		set_current_screen($this->_cpt_routes[$this->_req_action]);
1046
+		$this->_current_screen       = get_current_screen();
1047
+		$this->_current_screen->base = 'event-espresso';
1048
+		$this->_add_help_tabs(); //we make sure we add any help tabs back in!
1049
+		/*try {
1050 1050
             $this->_route_admin_request();
1051 1051
         } catch ( EE_Error $e ) {
1052 1052
             $e->get_error();
1053 1053
         }/**/
1054
-    }
1055
-
1056
-
1057
-
1058
-    /**
1059
-     * This allows child classes to modify the default editor title that appears when people add a new or edit an
1060
-     * existing CPT item.     * This uses the _labels property set by the child class via _define_page_props. Just make
1061
-     * sure you have a key in _labels property that equals 'editor_title' and the value can be whatever you want the
1062
-     * default to be.
1063
-     *
1064
-     * @param string $title The new title (or existing if there is no editor_title defined)
1065
-     * @return string
1066
-     */
1067
-    public function add_custom_editor_default_title($title)
1068
-    {
1069
-        return isset($this->_labels['editor_title'][$this->_cpt_routes[$this->_req_action]])
1070
-            ? $this->_labels['editor_title'][$this->_cpt_routes[$this->_req_action]]
1071
-            : $title;
1072
-    }
1073
-
1074
-
1075
-
1076
-    /**
1077
-     * hooks into the wp_get_shortlink button and makes sure that the shortlink gets generated
1078
-     *
1079
-     * @param string $shortlink   The already generated shortlink
1080
-     * @param int    $id          Post ID for this item
1081
-     * @param string $context     The context for the link
1082
-     * @param bool   $allow_slugs Whether to allow post slugs in the shortlink.
1083
-     * @return string
1084
-     */
1085
-    public function add_shortlink_button_to_editor($shortlink, $id, $context, $allow_slugs)
1086
-    {
1087
-        if ( ! empty($id) && get_option('permalink_structure') !== '') {
1088
-            $post = get_post($id);
1089
-            if (isset($post->post_type) && $this->page_slug === $post->post_type) {
1090
-                $shortlink = home_url('?p=' . $post->ID);
1091
-            }
1092
-        }
1093
-        return $shortlink;
1094
-    }
1095
-
1096
-
1097
-
1098
-    /**
1099
-     * overriding the parent route_admin_request method so we DON'T run the route twice on cpt core page loads (it's
1100
-     * already run in modify_current_screen())
1101
-     *
1102
-     * @return void
1103
-     */
1104
-    public function route_admin_request()
1105
-    {
1106
-        if ($this->_cpt_route) {
1107
-            return;
1108
-        }
1109
-        try {
1110
-            $this->_route_admin_request();
1111
-        } catch (EE_Error $e) {
1112
-            $e->get_error();
1113
-        }
1114
-    }
1115
-
1116
-
1117
-
1118
-    /**
1119
-     * Add a hidden form input to cpt core pages so that we know to do redirects to our routes on saves
1120
-     *
1121
-     * @return void
1122
-     */
1123
-    public function cpt_post_form_hidden_input()
1124
-    {
1125
-        echo '<input type="hidden" name="ee_cpt_item_redirect_url" value="' . $this->_admin_base_url . '" />';
1126
-        //we're also going to add the route value and the current page so we can direct autosave parsing correctly
1127
-        echo '<div id="ee-cpt-hidden-inputs">';
1128
-        echo '<input type="hidden" id="current_route" name="current_route" value="' . $this->_current_view . '" />';
1129
-        echo '<input type="hidden" id="current_page" name="current_page" value="' . $this->page_slug . '" />';
1130
-        echo '</div>';
1131
-    }
1132
-
1133
-
1134
-
1135
-    /**
1136
-     * This allows us to redirect the location of revision restores when they happen so it goes to our CPT routes.
1137
-     *
1138
-     * @param  string $location Original location url
1139
-     * @param  int    $status   Status for http header
1140
-     * @return string           new (or original) url to redirect to.
1141
-     */
1142
-    public function revision_redirect($location, $status)
1143
-    {
1144
-        //get revision
1145
-        $rev_id = isset($this->_req_data['revision']) ? $this->_req_data['revision'] : null;
1146
-        //can't do anything without revision so let's get out if not present
1147
-        if (empty($rev_id)) {
1148
-            return $location;
1149
-        }
1150
-        //get rev_post_data
1151
-        $rev = get_post($rev_id);
1152
-        $admin_url = $this->_admin_base_url;
1153
-        $query_args = array(
1154
-            'action'   => 'edit',
1155
-            'post'     => $rev->post_parent,
1156
-            'revision' => $rev_id,
1157
-            'message'  => 5,
1158
-        );
1159
-        $this->_process_notices($query_args, true);
1160
-        return self::add_query_args_and_nonce($query_args, $admin_url);
1161
-    }
1162
-
1163
-
1164
-
1165
-    /**
1166
-     * Modify the edit post link generated by wp core function so that EE CPTs get setup differently.
1167
-     *
1168
-     * @param  string $link    the original generated link
1169
-     * @param  int    $id      post id
1170
-     * @param  string $context optional, defaults to display.  How to write the '&'
1171
-     * @return string          the link
1172
-     */
1173
-    public function modify_edit_post_link($link, $id, $context)
1174
-    {
1175
-        $post = get_post($id);
1176
-        if ( ! isset($this->_req_data['action'])
1177
-             || ! isset($this->_cpt_routes[$this->_req_data['action']])
1178
-             || $post->post_type !== $this->_cpt_routes[$this->_req_data['action']]
1179
-        ) {
1180
-            return $link;
1181
-        }
1182
-        $query_args = array(
1183
-            'action' => isset($this->_cpt_edit_routes[$post->post_type])
1184
-                ? $this->_cpt_edit_routes[$post->post_type]
1185
-                : 'edit',
1186
-            'post'   => $id,
1187
-        );
1188
-        return self::add_query_args_and_nonce($query_args, $this->_admin_base_url);
1189
-    }
1190
-
1191
-
1192
-    /**
1193
-     * Modify the trash link on our cpt edit pages so it has the required query var for triggering redirect properly on
1194
-     * our routes.
1195
-     *
1196
-     * @param  string $delete_link  original delete link
1197
-     * @param  int    $post_id      id of cpt object
1198
-     * @param  bool   $force_delete whether this is forcing a hard delete instead of trash
1199
-     * @return string new delete link
1200
-     * @throws EE_Error
1201
-     */
1202
-    public function modify_delete_post_link($delete_link, $post_id, $force_delete)
1203
-    {
1204
-        $post = get_post($post_id);
1205
-
1206
-        if (empty($this->_req_data['action'])
1207
-            || ! isset($this->_cpt_routes[$this->_req_data['action']])
1208
-            || ! $post instanceof WP_Post
1209
-            || $post->post_type !== $this->_cpt_routes[$this->_req_data['action']]
1210
-        ) {
1211
-            return $delete_link;
1212
-        }
1213
-        $this->_set_model_object($post->ID, true);
1214
-
1215
-        //returns something like `trash_event` or `trash_attendee` or `trash_venue`
1216
-        $action = 'trash_' . str_replace('ee_', '', strtolower(get_class($this->_cpt_model_obj)));
1217
-
1218
-        return EE_Admin_Page::add_query_args_and_nonce(
1219
-            array(
1220
-                'page' => $this->_req_data['page'],
1221
-                'action' => $action,
1222
-                $this->_cpt_model_obj->get_model()->get_primary_key_field()->get_name()
1223
-                    => $post->ID
1224
-            ),
1225
-            admin_url()
1226
-        );
1227
-    }
1228
-
1229
-
1230
-
1231
-    /**
1232
-     * This is the callback for the 'redirect_post_location' filter in wp-admin/post.php
1233
-     * so that we can hijack the default redirect locations for wp custom post types
1234
-     * that WE'RE using and send back to OUR routes.  This should only be hooked in on the right route.
1235
-     *
1236
-     * @param  string $location This is the incoming currently set redirect location
1237
-     * @param  string $post_id  This is the 'ID' value of the wp_posts table
1238
-     * @return string           the new location to redirect to
1239
-     */
1240
-    public function cpt_post_location_redirect($location, $post_id)
1241
-    {
1242
-        //we DO have a match so let's setup the url
1243
-        //we have to get the post to determine our route
1244
-        $post       = get_post($post_id);
1245
-        $edit_route = $this->_cpt_edit_routes[$post->post_type];
1246
-        //shared query_args
1247
-        $query_args = array('action' => $edit_route, 'post' => $post_id);
1248
-        $admin_url  = $this->_admin_base_url;
1249
-        if (isset($this->_req_data['save']) || isset($this->_req_data['publish'])) {
1250
-            $status = get_post_status($post_id);
1251
-            if (isset($this->_req_data['publish'])) {
1252
-                switch ($status) {
1253
-                    case 'pending':
1254
-                        $message = 8;
1255
-                        break;
1256
-                    case 'future':
1257
-                        $message = 9;
1258
-                        break;
1259
-                    default:
1260
-                        $message = 6;
1261
-                }
1262
-            } else {
1263
-                $message = 'draft' === $status ? 10 : 1;
1264
-            }
1265
-        } else if (isset($this->_req_data['addmeta']) && $this->_req_data['addmeta']) {
1266
-            $message = 2;
1267
-            //			$append = '#postcustom';
1268
-        } else if (isset($this->_req_data['deletemeta']) && $this->_req_data['deletemeta']) {
1269
-            $message = 3;
1270
-            //			$append = '#postcustom';
1271
-        } elseif ($this->_req_data['action'] === 'post-quickpress-save-cont') {
1272
-            $message = 7;
1273
-        } else {
1274
-            $message = 4;
1275
-        }
1276
-        //change the message if the post type is not viewable on the frontend
1277
-        $this->_cpt_object = get_post_type_object($post->post_type);
1278
-        $message           = $message === 1 && ! $this->_cpt_object->publicly_queryable ? 4 : $message;
1279
-        $query_args = array_merge(array('message' => $message), $query_args);
1280
-        $this->_process_notices($query_args, true);
1281
-        return self::add_query_args_and_nonce($query_args, $admin_url);
1282
-    }
1283
-
1284
-
1285
-
1286
-    /**
1287
-     * This method is called to inject nav tabs on core WP cpt pages
1288
-     *
1289
-     * @access public
1290
-     * @return void
1291
-     */
1292
-    public function inject_nav_tabs()
1293
-    {
1294
-        //can we hijack and insert the nav_tabs?
1295
-        $nav_tabs = $this->_get_main_nav_tabs();
1296
-        //first close off existing form tag
1297
-        $html = '>';
1298
-        $html .= $nav_tabs;
1299
-        //now let's handle the remaining tag ( missing ">" is CORRECT )
1300
-        $html .= '<span></span';
1301
-        echo $html;
1302
-    }
1303
-
1304
-
1305
-
1306
-    /**
1307
-     * This just sets up the post update messages when an update form is loaded
1308
-     *
1309
-     * @access public
1310
-     * @param  array $messages the original messages array
1311
-     * @return array           the new messages array
1312
-     */
1313
-    public function post_update_messages($messages)
1314
-    {
1315
-        global $post;
1316
-        $id = isset($this->_req_data['post']) ? $this->_req_data['post'] : null;
1317
-        $id = empty($id) && is_object($post) ? $post->ID : null;
1318
-        //		$post_type = $post ? $post->post_type : false;
1319
-        /*$current_route = isset($this->_req_data['current_route']) ? $this->_req_data['current_route'] : 'shouldneverwork';
1054
+	}
1055
+
1056
+
1057
+
1058
+	/**
1059
+	 * This allows child classes to modify the default editor title that appears when people add a new or edit an
1060
+	 * existing CPT item.     * This uses the _labels property set by the child class via _define_page_props. Just make
1061
+	 * sure you have a key in _labels property that equals 'editor_title' and the value can be whatever you want the
1062
+	 * default to be.
1063
+	 *
1064
+	 * @param string $title The new title (or existing if there is no editor_title defined)
1065
+	 * @return string
1066
+	 */
1067
+	public function add_custom_editor_default_title($title)
1068
+	{
1069
+		return isset($this->_labels['editor_title'][$this->_cpt_routes[$this->_req_action]])
1070
+			? $this->_labels['editor_title'][$this->_cpt_routes[$this->_req_action]]
1071
+			: $title;
1072
+	}
1073
+
1074
+
1075
+
1076
+	/**
1077
+	 * hooks into the wp_get_shortlink button and makes sure that the shortlink gets generated
1078
+	 *
1079
+	 * @param string $shortlink   The already generated shortlink
1080
+	 * @param int    $id          Post ID for this item
1081
+	 * @param string $context     The context for the link
1082
+	 * @param bool   $allow_slugs Whether to allow post slugs in the shortlink.
1083
+	 * @return string
1084
+	 */
1085
+	public function add_shortlink_button_to_editor($shortlink, $id, $context, $allow_slugs)
1086
+	{
1087
+		if ( ! empty($id) && get_option('permalink_structure') !== '') {
1088
+			$post = get_post($id);
1089
+			if (isset($post->post_type) && $this->page_slug === $post->post_type) {
1090
+				$shortlink = home_url('?p=' . $post->ID);
1091
+			}
1092
+		}
1093
+		return $shortlink;
1094
+	}
1095
+
1096
+
1097
+
1098
+	/**
1099
+	 * overriding the parent route_admin_request method so we DON'T run the route twice on cpt core page loads (it's
1100
+	 * already run in modify_current_screen())
1101
+	 *
1102
+	 * @return void
1103
+	 */
1104
+	public function route_admin_request()
1105
+	{
1106
+		if ($this->_cpt_route) {
1107
+			return;
1108
+		}
1109
+		try {
1110
+			$this->_route_admin_request();
1111
+		} catch (EE_Error $e) {
1112
+			$e->get_error();
1113
+		}
1114
+	}
1115
+
1116
+
1117
+
1118
+	/**
1119
+	 * Add a hidden form input to cpt core pages so that we know to do redirects to our routes on saves
1120
+	 *
1121
+	 * @return void
1122
+	 */
1123
+	public function cpt_post_form_hidden_input()
1124
+	{
1125
+		echo '<input type="hidden" name="ee_cpt_item_redirect_url" value="' . $this->_admin_base_url . '" />';
1126
+		//we're also going to add the route value and the current page so we can direct autosave parsing correctly
1127
+		echo '<div id="ee-cpt-hidden-inputs">';
1128
+		echo '<input type="hidden" id="current_route" name="current_route" value="' . $this->_current_view . '" />';
1129
+		echo '<input type="hidden" id="current_page" name="current_page" value="' . $this->page_slug . '" />';
1130
+		echo '</div>';
1131
+	}
1132
+
1133
+
1134
+
1135
+	/**
1136
+	 * This allows us to redirect the location of revision restores when they happen so it goes to our CPT routes.
1137
+	 *
1138
+	 * @param  string $location Original location url
1139
+	 * @param  int    $status   Status for http header
1140
+	 * @return string           new (or original) url to redirect to.
1141
+	 */
1142
+	public function revision_redirect($location, $status)
1143
+	{
1144
+		//get revision
1145
+		$rev_id = isset($this->_req_data['revision']) ? $this->_req_data['revision'] : null;
1146
+		//can't do anything without revision so let's get out if not present
1147
+		if (empty($rev_id)) {
1148
+			return $location;
1149
+		}
1150
+		//get rev_post_data
1151
+		$rev = get_post($rev_id);
1152
+		$admin_url = $this->_admin_base_url;
1153
+		$query_args = array(
1154
+			'action'   => 'edit',
1155
+			'post'     => $rev->post_parent,
1156
+			'revision' => $rev_id,
1157
+			'message'  => 5,
1158
+		);
1159
+		$this->_process_notices($query_args, true);
1160
+		return self::add_query_args_and_nonce($query_args, $admin_url);
1161
+	}
1162
+
1163
+
1164
+
1165
+	/**
1166
+	 * Modify the edit post link generated by wp core function so that EE CPTs get setup differently.
1167
+	 *
1168
+	 * @param  string $link    the original generated link
1169
+	 * @param  int    $id      post id
1170
+	 * @param  string $context optional, defaults to display.  How to write the '&'
1171
+	 * @return string          the link
1172
+	 */
1173
+	public function modify_edit_post_link($link, $id, $context)
1174
+	{
1175
+		$post = get_post($id);
1176
+		if ( ! isset($this->_req_data['action'])
1177
+			 || ! isset($this->_cpt_routes[$this->_req_data['action']])
1178
+			 || $post->post_type !== $this->_cpt_routes[$this->_req_data['action']]
1179
+		) {
1180
+			return $link;
1181
+		}
1182
+		$query_args = array(
1183
+			'action' => isset($this->_cpt_edit_routes[$post->post_type])
1184
+				? $this->_cpt_edit_routes[$post->post_type]
1185
+				: 'edit',
1186
+			'post'   => $id,
1187
+		);
1188
+		return self::add_query_args_and_nonce($query_args, $this->_admin_base_url);
1189
+	}
1190
+
1191
+
1192
+	/**
1193
+	 * Modify the trash link on our cpt edit pages so it has the required query var for triggering redirect properly on
1194
+	 * our routes.
1195
+	 *
1196
+	 * @param  string $delete_link  original delete link
1197
+	 * @param  int    $post_id      id of cpt object
1198
+	 * @param  bool   $force_delete whether this is forcing a hard delete instead of trash
1199
+	 * @return string new delete link
1200
+	 * @throws EE_Error
1201
+	 */
1202
+	public function modify_delete_post_link($delete_link, $post_id, $force_delete)
1203
+	{
1204
+		$post = get_post($post_id);
1205
+
1206
+		if (empty($this->_req_data['action'])
1207
+			|| ! isset($this->_cpt_routes[$this->_req_data['action']])
1208
+			|| ! $post instanceof WP_Post
1209
+			|| $post->post_type !== $this->_cpt_routes[$this->_req_data['action']]
1210
+		) {
1211
+			return $delete_link;
1212
+		}
1213
+		$this->_set_model_object($post->ID, true);
1214
+
1215
+		//returns something like `trash_event` or `trash_attendee` or `trash_venue`
1216
+		$action = 'trash_' . str_replace('ee_', '', strtolower(get_class($this->_cpt_model_obj)));
1217
+
1218
+		return EE_Admin_Page::add_query_args_and_nonce(
1219
+			array(
1220
+				'page' => $this->_req_data['page'],
1221
+				'action' => $action,
1222
+				$this->_cpt_model_obj->get_model()->get_primary_key_field()->get_name()
1223
+					=> $post->ID
1224
+			),
1225
+			admin_url()
1226
+		);
1227
+	}
1228
+
1229
+
1230
+
1231
+	/**
1232
+	 * This is the callback for the 'redirect_post_location' filter in wp-admin/post.php
1233
+	 * so that we can hijack the default redirect locations for wp custom post types
1234
+	 * that WE'RE using and send back to OUR routes.  This should only be hooked in on the right route.
1235
+	 *
1236
+	 * @param  string $location This is the incoming currently set redirect location
1237
+	 * @param  string $post_id  This is the 'ID' value of the wp_posts table
1238
+	 * @return string           the new location to redirect to
1239
+	 */
1240
+	public function cpt_post_location_redirect($location, $post_id)
1241
+	{
1242
+		//we DO have a match so let's setup the url
1243
+		//we have to get the post to determine our route
1244
+		$post       = get_post($post_id);
1245
+		$edit_route = $this->_cpt_edit_routes[$post->post_type];
1246
+		//shared query_args
1247
+		$query_args = array('action' => $edit_route, 'post' => $post_id);
1248
+		$admin_url  = $this->_admin_base_url;
1249
+		if (isset($this->_req_data['save']) || isset($this->_req_data['publish'])) {
1250
+			$status = get_post_status($post_id);
1251
+			if (isset($this->_req_data['publish'])) {
1252
+				switch ($status) {
1253
+					case 'pending':
1254
+						$message = 8;
1255
+						break;
1256
+					case 'future':
1257
+						$message = 9;
1258
+						break;
1259
+					default:
1260
+						$message = 6;
1261
+				}
1262
+			} else {
1263
+				$message = 'draft' === $status ? 10 : 1;
1264
+			}
1265
+		} else if (isset($this->_req_data['addmeta']) && $this->_req_data['addmeta']) {
1266
+			$message = 2;
1267
+			//			$append = '#postcustom';
1268
+		} else if (isset($this->_req_data['deletemeta']) && $this->_req_data['deletemeta']) {
1269
+			$message = 3;
1270
+			//			$append = '#postcustom';
1271
+		} elseif ($this->_req_data['action'] === 'post-quickpress-save-cont') {
1272
+			$message = 7;
1273
+		} else {
1274
+			$message = 4;
1275
+		}
1276
+		//change the message if the post type is not viewable on the frontend
1277
+		$this->_cpt_object = get_post_type_object($post->post_type);
1278
+		$message           = $message === 1 && ! $this->_cpt_object->publicly_queryable ? 4 : $message;
1279
+		$query_args = array_merge(array('message' => $message), $query_args);
1280
+		$this->_process_notices($query_args, true);
1281
+		return self::add_query_args_and_nonce($query_args, $admin_url);
1282
+	}
1283
+
1284
+
1285
+
1286
+	/**
1287
+	 * This method is called to inject nav tabs on core WP cpt pages
1288
+	 *
1289
+	 * @access public
1290
+	 * @return void
1291
+	 */
1292
+	public function inject_nav_tabs()
1293
+	{
1294
+		//can we hijack and insert the nav_tabs?
1295
+		$nav_tabs = $this->_get_main_nav_tabs();
1296
+		//first close off existing form tag
1297
+		$html = '>';
1298
+		$html .= $nav_tabs;
1299
+		//now let's handle the remaining tag ( missing ">" is CORRECT )
1300
+		$html .= '<span></span';
1301
+		echo $html;
1302
+	}
1303
+
1304
+
1305
+
1306
+	/**
1307
+	 * This just sets up the post update messages when an update form is loaded
1308
+	 *
1309
+	 * @access public
1310
+	 * @param  array $messages the original messages array
1311
+	 * @return array           the new messages array
1312
+	 */
1313
+	public function post_update_messages($messages)
1314
+	{
1315
+		global $post;
1316
+		$id = isset($this->_req_data['post']) ? $this->_req_data['post'] : null;
1317
+		$id = empty($id) && is_object($post) ? $post->ID : null;
1318
+		//		$post_type = $post ? $post->post_type : false;
1319
+		/*$current_route = isset($this->_req_data['current_route']) ? $this->_req_data['current_route'] : 'shouldneverwork';
1320 1320
 
1321 1321
         $route_to_check = $post_type && isset( $this->_cpt_routes[$current_route]) ? $this->_cpt_routes[$current_route] : '';/**/
1322
-        $messages[$post->post_type] = array(
1323
-            0 => '', //Unused. Messages start at index 1.
1324
-            1 => sprintf(
1325
-                __('%1$s updated. %2$sView %1$s%3$s', 'event_espresso'),
1326
-                $this->_cpt_object->labels->singular_name,
1327
-                '<a href="' . esc_url(get_permalink($id)) . '">',
1328
-                '</a>'
1329
-            ),
1330
-            2 => __('Custom field updated'),
1331
-            3 => __('Custom field deleted.'),
1332
-            4 => sprintf(__('%1$s updated.', 'event_espresso'), $this->_cpt_object->labels->singular_name),
1333
-            5 => isset($_GET['revision']) ? sprintf(__('%s restored to revision from %s', 'event_espresso'),
1334
-                $this->_cpt_object->labels->singular_name, wp_post_revision_title((int)$_GET['revision'], false))
1335
-                : false,
1336
-            6 => sprintf(
1337
-                __('%1$s published. %2$sView %1$s%3$s', 'event_espresso'),
1338
-                $this->_cpt_object->labels->singular_name,
1339
-                '<a href="' . esc_url(get_permalink($id)) . '">',
1340
-                '</a>'
1341
-            ),
1342
-            7 => sprintf(__('%1$s saved.', 'event_espresso'), $this->_cpt_object->labels->singular_name),
1343
-            8 => sprintf(
1344
-                __('%1$s submitted. %2$sPreview %1$s%3$s', 'event_espresso'),
1345
-                $this->_cpt_object->labels->singular_name,
1346
-                '<a target="_blank" href="' . esc_url(add_query_arg('preview', 'true', get_permalink($id))) . '">',
1347
-                '</a>'
1348
-            ),
1349
-            9 => sprintf(
1350
-                __('%1$s scheduled for: %2$s. %3$s">Preview %1$s%3$s', 'event_espresso'),
1351
-                $this->_cpt_object->labels->singular_name,
1352
-                '<strong>' . date_i18n(__('M j, Y @ G:i'), strtotime($post->post_date)) . '</strong>',
1353
-                '<a target="_blank" href="' . esc_url(get_permalink($id)),
1354
-                '</a>'
1355
-            ),
1356
-            10 => sprintf(
1357
-                __('%1$s draft updated. %2$s">Preview page%3$s', 'event_espresso'),
1358
-                $this->_cpt_object->labels->singular_name,
1359
-                '<a target="_blank" href="' . esc_url(add_query_arg('preview', 'true', get_permalink($id))),
1360
-                '</a>'
1361
-            ),
1362
-        );
1363
-        return $messages;
1364
-    }
1365
-
1366
-
1367
-
1368
-    /**
1369
-     * default method for the 'create_new' route for cpt admin pages.
1370
-     * For reference what to include in here, see wp-admin/post-new.php
1371
-     *
1372
-     * @access  protected
1373
-     * @return void
1374
-     */
1375
-    protected function _create_new_cpt_item()
1376
-    {
1377
-        // gather template vars for WP_ADMIN_PATH . 'edit-form-advanced.php'
1378
-        global $post, $title, $is_IE, $post_type, $post_type_object;
1379
-        $post_type        = $this->_cpt_routes[$this->_req_action];
1380
-        $post_type_object = $this->_cpt_object;
1381
-        $title            = $post_type_object->labels->add_new_item;
1382
-        $post    = $post = get_default_post_to_edit($this->_cpt_routes[$this->_req_action], true);
1383
-        add_action('admin_print_styles', array($this, 'add_new_admin_page_global'));
1384
-        //modify the default editor title field with default title.
1385
-        add_filter('enter_title_here', array($this, 'add_custom_editor_default_title'), 10);
1386
-        $this->loadEditorTemplate(true);
1387
-    }
1388
-
1389
-
1390
-    /**
1391
-     * Enqueues auto-save and loads the editor template
1392
-     *
1393
-     * @param bool $creating
1394
-     */
1395
-    private function loadEditorTemplate($creating = true) {
1396
-        global $post, $title, $is_IE, $post_type, $post_type_object;
1397
-        //these vars are used by the template
1398
-        $editing = true;
1399
-        $post_ID = $post->ID;
1400
-        if (apply_filters('FHEE__EE_Admin_Page_CPT___create_new_cpt_item__replace_editor', false, $post) === false) {
1401
-            //only enqueue autosave when creating event (necessary to get permalink/url generated)
1402
-            //otherwise EE doesn't support autosave fully, so to prevent user confusion we disable it in edit context.
1403
-            if ($creating) {
1404
-                wp_enqueue_script('autosave');
1405
-            } else {
1406
-                if (isset($this->_cpt_routes[$this->_req_data['action']])
1407
-                    && ! isset($this->_labels['hide_add_button_on_cpt_route'][$this->_req_data['action']])
1408
-                ) {
1409
-                    $create_new_action = apply_filters('FHEE__EE_Admin_Page_CPT___edit_cpt_item__create_new_action',
1410
-                        'create_new', $this);
1411
-                    $post_new_file = EE_Admin_Page::add_query_args_and_nonce(array(
1412
-                        'action' => $create_new_action,
1413
-                        'page'   => $this->page_slug,
1414
-                    ), 'admin.php');
1415
-                }
1416
-            }
1417
-            include_once WP_ADMIN_PATH . 'edit-form-advanced.php';
1418
-        }
1419
-    }
1420
-
1421
-
1422
-
1423
-    public function add_new_admin_page_global()
1424
-    {
1425
-        $admin_page = ! empty($this->_req_data['post']) ? 'post-php' : 'post-new-php';
1426
-        ?>
1322
+		$messages[$post->post_type] = array(
1323
+			0 => '', //Unused. Messages start at index 1.
1324
+			1 => sprintf(
1325
+				__('%1$s updated. %2$sView %1$s%3$s', 'event_espresso'),
1326
+				$this->_cpt_object->labels->singular_name,
1327
+				'<a href="' . esc_url(get_permalink($id)) . '">',
1328
+				'</a>'
1329
+			),
1330
+			2 => __('Custom field updated'),
1331
+			3 => __('Custom field deleted.'),
1332
+			4 => sprintf(__('%1$s updated.', 'event_espresso'), $this->_cpt_object->labels->singular_name),
1333
+			5 => isset($_GET['revision']) ? sprintf(__('%s restored to revision from %s', 'event_espresso'),
1334
+				$this->_cpt_object->labels->singular_name, wp_post_revision_title((int)$_GET['revision'], false))
1335
+				: false,
1336
+			6 => sprintf(
1337
+				__('%1$s published. %2$sView %1$s%3$s', 'event_espresso'),
1338
+				$this->_cpt_object->labels->singular_name,
1339
+				'<a href="' . esc_url(get_permalink($id)) . '">',
1340
+				'</a>'
1341
+			),
1342
+			7 => sprintf(__('%1$s saved.', 'event_espresso'), $this->_cpt_object->labels->singular_name),
1343
+			8 => sprintf(
1344
+				__('%1$s submitted. %2$sPreview %1$s%3$s', 'event_espresso'),
1345
+				$this->_cpt_object->labels->singular_name,
1346
+				'<a target="_blank" href="' . esc_url(add_query_arg('preview', 'true', get_permalink($id))) . '">',
1347
+				'</a>'
1348
+			),
1349
+			9 => sprintf(
1350
+				__('%1$s scheduled for: %2$s. %3$s">Preview %1$s%3$s', 'event_espresso'),
1351
+				$this->_cpt_object->labels->singular_name,
1352
+				'<strong>' . date_i18n(__('M j, Y @ G:i'), strtotime($post->post_date)) . '</strong>',
1353
+				'<a target="_blank" href="' . esc_url(get_permalink($id)),
1354
+				'</a>'
1355
+			),
1356
+			10 => sprintf(
1357
+				__('%1$s draft updated. %2$s">Preview page%3$s', 'event_espresso'),
1358
+				$this->_cpt_object->labels->singular_name,
1359
+				'<a target="_blank" href="' . esc_url(add_query_arg('preview', 'true', get_permalink($id))),
1360
+				'</a>'
1361
+			),
1362
+		);
1363
+		return $messages;
1364
+	}
1365
+
1366
+
1367
+
1368
+	/**
1369
+	 * default method for the 'create_new' route for cpt admin pages.
1370
+	 * For reference what to include in here, see wp-admin/post-new.php
1371
+	 *
1372
+	 * @access  protected
1373
+	 * @return void
1374
+	 */
1375
+	protected function _create_new_cpt_item()
1376
+	{
1377
+		// gather template vars for WP_ADMIN_PATH . 'edit-form-advanced.php'
1378
+		global $post, $title, $is_IE, $post_type, $post_type_object;
1379
+		$post_type        = $this->_cpt_routes[$this->_req_action];
1380
+		$post_type_object = $this->_cpt_object;
1381
+		$title            = $post_type_object->labels->add_new_item;
1382
+		$post    = $post = get_default_post_to_edit($this->_cpt_routes[$this->_req_action], true);
1383
+		add_action('admin_print_styles', array($this, 'add_new_admin_page_global'));
1384
+		//modify the default editor title field with default title.
1385
+		add_filter('enter_title_here', array($this, 'add_custom_editor_default_title'), 10);
1386
+		$this->loadEditorTemplate(true);
1387
+	}
1388
+
1389
+
1390
+	/**
1391
+	 * Enqueues auto-save and loads the editor template
1392
+	 *
1393
+	 * @param bool $creating
1394
+	 */
1395
+	private function loadEditorTemplate($creating = true) {
1396
+		global $post, $title, $is_IE, $post_type, $post_type_object;
1397
+		//these vars are used by the template
1398
+		$editing = true;
1399
+		$post_ID = $post->ID;
1400
+		if (apply_filters('FHEE__EE_Admin_Page_CPT___create_new_cpt_item__replace_editor', false, $post) === false) {
1401
+			//only enqueue autosave when creating event (necessary to get permalink/url generated)
1402
+			//otherwise EE doesn't support autosave fully, so to prevent user confusion we disable it in edit context.
1403
+			if ($creating) {
1404
+				wp_enqueue_script('autosave');
1405
+			} else {
1406
+				if (isset($this->_cpt_routes[$this->_req_data['action']])
1407
+					&& ! isset($this->_labels['hide_add_button_on_cpt_route'][$this->_req_data['action']])
1408
+				) {
1409
+					$create_new_action = apply_filters('FHEE__EE_Admin_Page_CPT___edit_cpt_item__create_new_action',
1410
+						'create_new', $this);
1411
+					$post_new_file = EE_Admin_Page::add_query_args_and_nonce(array(
1412
+						'action' => $create_new_action,
1413
+						'page'   => $this->page_slug,
1414
+					), 'admin.php');
1415
+				}
1416
+			}
1417
+			include_once WP_ADMIN_PATH . 'edit-form-advanced.php';
1418
+		}
1419
+	}
1420
+
1421
+
1422
+
1423
+	public function add_new_admin_page_global()
1424
+	{
1425
+		$admin_page = ! empty($this->_req_data['post']) ? 'post-php' : 'post-new-php';
1426
+		?>
1427 1427
         <script type="text/javascript">
1428 1428
             adminpage = '<?php echo $admin_page; ?>';
1429 1429
         </script>
1430 1430
         <?php
1431
-    }
1432
-
1433
-
1434
-
1435
-    /**
1436
-     * default method for the 'edit' route for cpt admin pages
1437
-     * For reference on what to put in here, refer to wp-admin/post.php
1438
-     *
1439
-     * @access protected
1440
-     * @return string   template for edit cpt form
1441
-     */
1442
-    protected function _edit_cpt_item()
1443
-    {
1444
-        global $post, $title, $is_IE, $post_type, $post_type_object;
1445
-        $post_id = isset($this->_req_data['post']) ? $this->_req_data['post'] : null;
1446
-        $post = ! empty($post_id) ? get_post($post_id, OBJECT, 'edit') : null;
1447
-        if (empty ($post)) {
1448
-            wp_die(__('You attempted to edit an item that doesn&#8217;t exist. Perhaps it was deleted?'));
1449
-        }
1450
-        if ( ! empty($_GET['get-post-lock'])) {
1451
-            wp_set_post_lock($post_id);
1452
-            wp_redirect(get_edit_post_link($post_id, 'url'));
1453
-            exit();
1454
-        }
1455
-
1456
-        // template vars for WP_ADMIN_PATH . 'edit-form-advanced.php'
1457
-        $post_type        = $this->_cpt_routes[$this->_req_action];
1458
-        $post_type_object = $this->_cpt_object;
1459
-
1460
-        if ( ! wp_check_post_lock($post->ID)) {
1461
-            wp_set_post_lock($post->ID);
1462
-        }
1463
-        add_action('admin_footer', '_admin_notice_post_locked');
1464
-        if (post_type_supports($this->_cpt_routes[$this->_req_action], 'comments')) {
1465
-            wp_enqueue_script('admin-comments');
1466
-            enqueue_comment_hotkeys_js();
1467
-        }
1468
-        add_action('admin_print_styles', array($this, 'add_new_admin_page_global'));
1469
-        //modify the default editor title field with default title.
1470
-        add_filter('enter_title_here', array($this, 'add_custom_editor_default_title'), 10);
1471
-        $this->loadEditorTemplate(false);
1472
-    }
1473
-
1474
-
1475
-
1476
-    /**
1477
-     * some getters
1478
-     */
1479
-    /**
1480
-     * This returns the protected _cpt_model_obj property
1481
-     *
1482
-     * @return EE_CPT_Base
1483
-     */
1484
-    public function get_cpt_model_obj()
1485
-    {
1486
-        return $this->_cpt_model_obj;
1487
-    }
1431
+	}
1432
+
1433
+
1434
+
1435
+	/**
1436
+	 * default method for the 'edit' route for cpt admin pages
1437
+	 * For reference on what to put in here, refer to wp-admin/post.php
1438
+	 *
1439
+	 * @access protected
1440
+	 * @return string   template for edit cpt form
1441
+	 */
1442
+	protected function _edit_cpt_item()
1443
+	{
1444
+		global $post, $title, $is_IE, $post_type, $post_type_object;
1445
+		$post_id = isset($this->_req_data['post']) ? $this->_req_data['post'] : null;
1446
+		$post = ! empty($post_id) ? get_post($post_id, OBJECT, 'edit') : null;
1447
+		if (empty ($post)) {
1448
+			wp_die(__('You attempted to edit an item that doesn&#8217;t exist. Perhaps it was deleted?'));
1449
+		}
1450
+		if ( ! empty($_GET['get-post-lock'])) {
1451
+			wp_set_post_lock($post_id);
1452
+			wp_redirect(get_edit_post_link($post_id, 'url'));
1453
+			exit();
1454
+		}
1455
+
1456
+		// template vars for WP_ADMIN_PATH . 'edit-form-advanced.php'
1457
+		$post_type        = $this->_cpt_routes[$this->_req_action];
1458
+		$post_type_object = $this->_cpt_object;
1459
+
1460
+		if ( ! wp_check_post_lock($post->ID)) {
1461
+			wp_set_post_lock($post->ID);
1462
+		}
1463
+		add_action('admin_footer', '_admin_notice_post_locked');
1464
+		if (post_type_supports($this->_cpt_routes[$this->_req_action], 'comments')) {
1465
+			wp_enqueue_script('admin-comments');
1466
+			enqueue_comment_hotkeys_js();
1467
+		}
1468
+		add_action('admin_print_styles', array($this, 'add_new_admin_page_global'));
1469
+		//modify the default editor title field with default title.
1470
+		add_filter('enter_title_here', array($this, 'add_custom_editor_default_title'), 10);
1471
+		$this->loadEditorTemplate(false);
1472
+	}
1473
+
1474
+
1475
+
1476
+	/**
1477
+	 * some getters
1478
+	 */
1479
+	/**
1480
+	 * This returns the protected _cpt_model_obj property
1481
+	 *
1482
+	 * @return EE_CPT_Base
1483
+	 */
1484
+	public function get_cpt_model_obj()
1485
+	{
1486
+		return $this->_cpt_model_obj;
1487
+	}
1488 1488
 
1489 1489
 }
Please login to merge, or discard this patch.
Spacing   +25 added lines, -25 removed lines patch added patch discarded remove patch
@@ -235,7 +235,7 @@  discard block
 block discarded – undo
235 235
      */
236 236
     protected function _register_autosave_containers($ids)
237 237
     {
238
-        $this->_autosave_containers = array_merge($this->_autosave_fields, (array)$ids);
238
+        $this->_autosave_containers = array_merge($this->_autosave_fields, (array) $ids);
239 239
     }
240 240
 
241 241
 
@@ -282,7 +282,7 @@  discard block
 block discarded – undo
282 282
         //filter _autosave_containers
283 283
         $containers = apply_filters('FHEE__EE_Admin_Page_CPT___load_autosave_scripts_styles__containers',
284 284
             $this->_autosave_containers, $this);
285
-        $containers = apply_filters('FHEE__EE_Admin_Page_CPT__' . get_class($this) . '___load_autosave_scripts_styles__containers',
285
+        $containers = apply_filters('FHEE__EE_Admin_Page_CPT__'.get_class($this).'___load_autosave_scripts_styles__containers',
286 286
             $containers, $this);
287 287
 
288 288
         wp_localize_script('event_editor_js', 'EE_AUTOSAVE_IDS',
@@ -394,7 +394,7 @@  discard block
 block discarded – undo
394 394
         // This is for any plugins that are doing things properly
395 395
         // and hooking into the load page hook for core wp cpt routes.
396 396
         global $pagenow;
397
-        do_action('load-' . $pagenow);
397
+        do_action('load-'.$pagenow);
398 398
         $this->modify_current_screen();
399 399
         add_action('admin_enqueue_scripts', array($this, 'setup_autosave_hooks'), 30);
400 400
         //we route REALLY early.
@@ -425,8 +425,8 @@  discard block
 block discarded – undo
425 425
                 'admin.php?page=espresso_registrations&action=contact_list',
426 426
             ),
427 427
             1 => array(
428
-                'edit.php?post_type=' . $this->_cpt_object->name,
429
-                'admin.php?page=' . $this->_cpt_object->name,
428
+                'edit.php?post_type='.$this->_cpt_object->name,
429
+                'admin.php?page='.$this->_cpt_object->name,
430 430
             ),
431 431
         );
432 432
         foreach ($routes_to_match as $route_matches) {
@@ -454,7 +454,7 @@  discard block
 block discarded – undo
454 454
         $cpt_has_support = ! empty($cpt_args['page_templates']);
455 455
 
456 456
         //if the installed version of WP is > 4.7 we do some additional checks.
457
-        if (EE_Recommended_Versions::check_wp_version('4.7','>=')) {
457
+        if (EE_Recommended_Versions::check_wp_version('4.7', '>=')) {
458 458
             $post_templates = wp_get_theme()->get_post_templates();
459 459
             //if there are $post_templates for this cpt, then we return false for this method because
460 460
             //that means we aren't going to load our page template manager and leave that up to the native
@@ -477,7 +477,7 @@  discard block
 block discarded – undo
477 477
         global $post;
478 478
         $template = '';
479 479
 
480
-        if (EE_Recommended_Versions::check_wp_version('4.7','>=')) {
480
+        if (EE_Recommended_Versions::check_wp_version('4.7', '>=')) {
481 481
             $page_template_count = count(get_page_templates());
482 482
         } else {
483 483
             $page_template_count = count(get_page_templates($post));
@@ -514,7 +514,7 @@  discard block
 block discarded – undo
514 514
         $post = get_post($id);
515 515
         if ('publish' !== get_post_status($post)) {
516 516
             //include shims for the `get_preview_post_link` function
517
-            require_once( EE_CORE . 'wordpress-shims.php' );
517
+            require_once(EE_CORE.'wordpress-shims.php');
518 518
             $return .= '<span_id="view-post-btn"><a target="_blank" href="'
519 519
                        . get_preview_post_link($id)
520 520
                        . '" class="button button-small">'
@@ -552,7 +552,7 @@  discard block
 block discarded – undo
552 552
             $template_args['statuses']         = $statuses;
553 553
         }
554 554
 
555
-        $template = EE_ADMIN_TEMPLATE . 'status_dropdown.template.php';
555
+        $template = EE_ADMIN_TEMPLATE.'status_dropdown.template.php';
556 556
         EEH_Template::display_template($template, $template_args);
557 557
     }
558 558
 
@@ -606,7 +606,7 @@  discard block
 block discarded – undo
606 606
             $this->_template_args['success'] = true;
607 607
         }
608 608
         do_action('AHEE__EE_Admin_Page_CPT__do_extra_autosave_stuff__global_after', $this);
609
-        do_action('AHEE__EE_Admin_Page_CPT__do_extra_autosave_stuff__after_' . get_class($this), $this);
609
+        do_action('AHEE__EE_Admin_Page_CPT__do_extra_autosave_stuff__after_'.get_class($this), $this);
610 610
         //now let's return json
611 611
         $this->_return_json();
612 612
     }
@@ -1009,7 +1009,7 @@  discard block
 block discarded – undo
1009 1009
         //global action
1010 1010
         do_action('AHEE_EE_Admin_Page_CPT__restore_revision', $post_id, $revision_id);
1011 1011
         //class specific action so you can limit hooking into a specific page.
1012
-        do_action('AHEE_EE_Admin_Page_CPT_' . get_class($this) . '__restore_revision', $post_id, $revision_id);
1012
+        do_action('AHEE_EE_Admin_Page_CPT_'.get_class($this).'__restore_revision', $post_id, $revision_id);
1013 1013
     }
1014 1014
 
1015 1015
 
@@ -1087,7 +1087,7 @@  discard block
 block discarded – undo
1087 1087
         if ( ! empty($id) && get_option('permalink_structure') !== '') {
1088 1088
             $post = get_post($id);
1089 1089
             if (isset($post->post_type) && $this->page_slug === $post->post_type) {
1090
-                $shortlink = home_url('?p=' . $post->ID);
1090
+                $shortlink = home_url('?p='.$post->ID);
1091 1091
             }
1092 1092
         }
1093 1093
         return $shortlink;
@@ -1122,11 +1122,11 @@  discard block
 block discarded – undo
1122 1122
      */
1123 1123
     public function cpt_post_form_hidden_input()
1124 1124
     {
1125
-        echo '<input type="hidden" name="ee_cpt_item_redirect_url" value="' . $this->_admin_base_url . '" />';
1125
+        echo '<input type="hidden" name="ee_cpt_item_redirect_url" value="'.$this->_admin_base_url.'" />';
1126 1126
         //we're also going to add the route value and the current page so we can direct autosave parsing correctly
1127 1127
         echo '<div id="ee-cpt-hidden-inputs">';
1128
-        echo '<input type="hidden" id="current_route" name="current_route" value="' . $this->_current_view . '" />';
1129
-        echo '<input type="hidden" id="current_page" name="current_page" value="' . $this->page_slug . '" />';
1128
+        echo '<input type="hidden" id="current_route" name="current_route" value="'.$this->_current_view.'" />';
1129
+        echo '<input type="hidden" id="current_page" name="current_page" value="'.$this->page_slug.'" />';
1130 1130
         echo '</div>';
1131 1131
     }
1132 1132
 
@@ -1213,7 +1213,7 @@  discard block
 block discarded – undo
1213 1213
         $this->_set_model_object($post->ID, true);
1214 1214
 
1215 1215
         //returns something like `trash_event` or `trash_attendee` or `trash_venue`
1216
-        $action = 'trash_' . str_replace('ee_', '', strtolower(get_class($this->_cpt_model_obj)));
1216
+        $action = 'trash_'.str_replace('ee_', '', strtolower(get_class($this->_cpt_model_obj)));
1217 1217
 
1218 1218
         return EE_Admin_Page::add_query_args_and_nonce(
1219 1219
             array(
@@ -1324,39 +1324,39 @@  discard block
 block discarded – undo
1324 1324
             1 => sprintf(
1325 1325
                 __('%1$s updated. %2$sView %1$s%3$s', 'event_espresso'),
1326 1326
                 $this->_cpt_object->labels->singular_name,
1327
-                '<a href="' . esc_url(get_permalink($id)) . '">',
1327
+                '<a href="'.esc_url(get_permalink($id)).'">',
1328 1328
                 '</a>'
1329 1329
             ),
1330 1330
             2 => __('Custom field updated'),
1331 1331
             3 => __('Custom field deleted.'),
1332 1332
             4 => sprintf(__('%1$s updated.', 'event_espresso'), $this->_cpt_object->labels->singular_name),
1333 1333
             5 => isset($_GET['revision']) ? sprintf(__('%s restored to revision from %s', 'event_espresso'),
1334
-                $this->_cpt_object->labels->singular_name, wp_post_revision_title((int)$_GET['revision'], false))
1334
+                $this->_cpt_object->labels->singular_name, wp_post_revision_title((int) $_GET['revision'], false))
1335 1335
                 : false,
1336 1336
             6 => sprintf(
1337 1337
                 __('%1$s published. %2$sView %1$s%3$s', 'event_espresso'),
1338 1338
                 $this->_cpt_object->labels->singular_name,
1339
-                '<a href="' . esc_url(get_permalink($id)) . '">',
1339
+                '<a href="'.esc_url(get_permalink($id)).'">',
1340 1340
                 '</a>'
1341 1341
             ),
1342 1342
             7 => sprintf(__('%1$s saved.', 'event_espresso'), $this->_cpt_object->labels->singular_name),
1343 1343
             8 => sprintf(
1344 1344
                 __('%1$s submitted. %2$sPreview %1$s%3$s', 'event_espresso'),
1345 1345
                 $this->_cpt_object->labels->singular_name,
1346
-                '<a target="_blank" href="' . esc_url(add_query_arg('preview', 'true', get_permalink($id))) . '">',
1346
+                '<a target="_blank" href="'.esc_url(add_query_arg('preview', 'true', get_permalink($id))).'">',
1347 1347
                 '</a>'
1348 1348
             ),
1349 1349
             9 => sprintf(
1350 1350
                 __('%1$s scheduled for: %2$s. %3$s">Preview %1$s%3$s', 'event_espresso'),
1351 1351
                 $this->_cpt_object->labels->singular_name,
1352
-                '<strong>' . date_i18n(__('M j, Y @ G:i'), strtotime($post->post_date)) . '</strong>',
1353
-                '<a target="_blank" href="' . esc_url(get_permalink($id)),
1352
+                '<strong>'.date_i18n(__('M j, Y @ G:i'), strtotime($post->post_date)).'</strong>',
1353
+                '<a target="_blank" href="'.esc_url(get_permalink($id)),
1354 1354
                 '</a>'
1355 1355
             ),
1356 1356
             10 => sprintf(
1357 1357
                 __('%1$s draft updated. %2$s">Preview page%3$s', 'event_espresso'),
1358 1358
                 $this->_cpt_object->labels->singular_name,
1359
-                '<a target="_blank" href="' . esc_url(add_query_arg('preview', 'true', get_permalink($id))),
1359
+                '<a target="_blank" href="'.esc_url(add_query_arg('preview', 'true', get_permalink($id))),
1360 1360
                 '</a>'
1361 1361
             ),
1362 1362
         );
@@ -1379,7 +1379,7 @@  discard block
 block discarded – undo
1379 1379
         $post_type        = $this->_cpt_routes[$this->_req_action];
1380 1380
         $post_type_object = $this->_cpt_object;
1381 1381
         $title            = $post_type_object->labels->add_new_item;
1382
-        $post    = $post = get_default_post_to_edit($this->_cpt_routes[$this->_req_action], true);
1382
+        $post = $post = get_default_post_to_edit($this->_cpt_routes[$this->_req_action], true);
1383 1383
         add_action('admin_print_styles', array($this, 'add_new_admin_page_global'));
1384 1384
         //modify the default editor title field with default title.
1385 1385
         add_filter('enter_title_here', array($this, 'add_custom_editor_default_title'), 10);
@@ -1414,7 +1414,7 @@  discard block
 block discarded – undo
1414 1414
                     ), 'admin.php');
1415 1415
                 }
1416 1416
             }
1417
-            include_once WP_ADMIN_PATH . 'edit-form-advanced.php';
1417
+            include_once WP_ADMIN_PATH.'edit-form-advanced.php';
1418 1418
         }
1419 1419
     }
1420 1420
 
Please login to merge, or discard this patch.
core/libraries/plugin_api/EE_Register_Data_Migration_Scripts.lib.php 2 patches
Indentation   +79 added lines, -79 removed lines patch added patch discarded remove patch
@@ -18,91 +18,91 @@
 block discarded – undo
18 18
 class EE_Register_Data_Migration_Scripts implements EEI_Plugin_API
19 19
 {
20 20
 
21
-    /**
22
-     * Holds values for registered DMSs
23
-     *
24
-     * @var array
25
-     */
26
-    protected static $_settings = array();
21
+	/**
22
+	 * Holds values for registered DMSs
23
+	 *
24
+	 * @var array
25
+	 */
26
+	protected static $_settings = array();
27 27
 
28 28
 
29
-    /**
30
-     * Method for registering new Data Migration Scripts
31
-     *
32
-     * @since 4.3.0
33
-     * @param string $dms_id a unique identifier for this set of data migration scripts
34
-     * @param array $setup_args {
35
-     *      @type string  $dms_paths an array of full server paths to folders that contain data migration scripts
36
-     *  }
37
-     * @throws EE_Error
38
-     * @return void
39
-     */
40
-    public static function register($dms_id = null, $setup_args = array())
41
-    {
42
-        //required fields MUST be present, so let's make sure they are.
43
-        if (empty($dms_id) || ! is_array($setup_args) || empty($setup_args['dms_paths'])) {
44
-            throw new EE_Error(
45
-                __(
46
-                    'In order to register Data Migration Scripts with EE_Register_Data_Migration_Scripts::register(), you must include a "dms_id" (a unique identifier for this set of data migration scripts), and  an array containing the following keys: "dms_paths" (an array of full server paths to folders that contain data migration scripts)',
47
-                    'event_espresso'
48
-                )
49
-            );
50
-        }
51
-        //make sure we don't register twice
52
-        if (isset(self::$_settings[ $dms_id ])) {
53
-            return;
54
-        }
55
-        //make sure this was called in the right place!
56
-        if (! did_action('AHEE__EE_System__load_espresso_addons')
57
-            || did_action('AHEE__EE_System___detect_if_activation_or_upgrade__begin')
58
-        ) {
59
-            EE_Error::doing_it_wrong(
60
-                __METHOD__,
61
-                __(
62
-                    'An attempt to register Data Migration Scripts has failed because it was not registered at the correct time.  Please use the "AHEE__EE_System__load_espresso_addons" hook to register Data Migration Scripts.',
63
-                    'event_espresso'
64
-                ),
65
-                '4.3.0'
66
-            );
67
-        }
68
-        //setup $_settings array from incoming values.
69
-        self::$_settings[ $dms_id ] = array(
70
-            'dms_paths' => isset($setup_args['dms_paths']) ? (array) $setup_args['dms_paths'] : array(),
71
-        );
72
-        // setup DMS
73
-        add_filter(
74
-            'FHEE__EE_Data_Migration_Manager__get_data_migration_script_folders',
75
-            array('EE_Register_Data_Migration_Scripts', 'add_data_migration_script_folders')
76
-        );
77
-    }
29
+	/**
30
+	 * Method for registering new Data Migration Scripts
31
+	 *
32
+	 * @since 4.3.0
33
+	 * @param string $dms_id a unique identifier for this set of data migration scripts
34
+	 * @param array $setup_args {
35
+	 *      @type string  $dms_paths an array of full server paths to folders that contain data migration scripts
36
+	 *  }
37
+	 * @throws EE_Error
38
+	 * @return void
39
+	 */
40
+	public static function register($dms_id = null, $setup_args = array())
41
+	{
42
+		//required fields MUST be present, so let's make sure they are.
43
+		if (empty($dms_id) || ! is_array($setup_args) || empty($setup_args['dms_paths'])) {
44
+			throw new EE_Error(
45
+				__(
46
+					'In order to register Data Migration Scripts with EE_Register_Data_Migration_Scripts::register(), you must include a "dms_id" (a unique identifier for this set of data migration scripts), and  an array containing the following keys: "dms_paths" (an array of full server paths to folders that contain data migration scripts)',
47
+					'event_espresso'
48
+				)
49
+			);
50
+		}
51
+		//make sure we don't register twice
52
+		if (isset(self::$_settings[ $dms_id ])) {
53
+			return;
54
+		}
55
+		//make sure this was called in the right place!
56
+		if (! did_action('AHEE__EE_System__load_espresso_addons')
57
+			|| did_action('AHEE__EE_System___detect_if_activation_or_upgrade__begin')
58
+		) {
59
+			EE_Error::doing_it_wrong(
60
+				__METHOD__,
61
+				__(
62
+					'An attempt to register Data Migration Scripts has failed because it was not registered at the correct time.  Please use the "AHEE__EE_System__load_espresso_addons" hook to register Data Migration Scripts.',
63
+					'event_espresso'
64
+				),
65
+				'4.3.0'
66
+			);
67
+		}
68
+		//setup $_settings array from incoming values.
69
+		self::$_settings[ $dms_id ] = array(
70
+			'dms_paths' => isset($setup_args['dms_paths']) ? (array) $setup_args['dms_paths'] : array(),
71
+		);
72
+		// setup DMS
73
+		add_filter(
74
+			'FHEE__EE_Data_Migration_Manager__get_data_migration_script_folders',
75
+			array('EE_Register_Data_Migration_Scripts', 'add_data_migration_script_folders')
76
+		);
77
+	}
78 78
 
79 79
 
80
-    /**
81
-     * @param array $dms_paths
82
-     * @return array
83
-     */
84
-    public static function add_data_migration_script_folders($dms_paths = array())
85
-    {
86
-        foreach (self::$_settings as $settings) {
87
-            $dms_paths = array_merge($dms_paths, $settings['dms_paths']);
88
-        }
89
-        return $dms_paths;
90
-    }
80
+	/**
81
+	 * @param array $dms_paths
82
+	 * @return array
83
+	 */
84
+	public static function add_data_migration_script_folders($dms_paths = array())
85
+	{
86
+		foreach (self::$_settings as $settings) {
87
+			$dms_paths = array_merge($dms_paths, $settings['dms_paths']);
88
+		}
89
+		return $dms_paths;
90
+	}
91 91
 
92 92
 
93
-    /**
94
-     * This deregisters a set of Data Migration Scripts that were previously registered with a specific dms_id
95
-     *
96
-     * @since 4.3.0
97
-     * @param mixed $dms_id unique identifier for the set of Data Migration Scripts that were previously registered
98
-     * @return void
99
-     */
100
-    public static function deregister($dms_id = null)
101
-    {
102
-        if (isset(self::$_settings[ $dms_id ])) {
103
-            unset(self::$_settings[ $dms_id ]);
104
-        }
105
-    }
93
+	/**
94
+	 * This deregisters a set of Data Migration Scripts that were previously registered with a specific dms_id
95
+	 *
96
+	 * @since 4.3.0
97
+	 * @param mixed $dms_id unique identifier for the set of Data Migration Scripts that were previously registered
98
+	 * @return void
99
+	 */
100
+	public static function deregister($dms_id = null)
101
+	{
102
+		if (isset(self::$_settings[ $dms_id ])) {
103
+			unset(self::$_settings[ $dms_id ]);
104
+		}
105
+	}
106 106
 }
107 107
 // End of file EE_Register_Data_Migration_Scripts.lib.php
108 108
 // Location: /core/libraries/plugin_api/EE_Register_Data_Migration_Scripts.lib.php
Please login to merge, or discard this patch.
Spacing   +5 added lines, -5 removed lines patch added patch discarded remove patch
@@ -49,11 +49,11 @@  discard block
 block discarded – undo
49 49
             );
50 50
         }
51 51
         //make sure we don't register twice
52
-        if (isset(self::$_settings[ $dms_id ])) {
52
+        if (isset(self::$_settings[$dms_id])) {
53 53
             return;
54 54
         }
55 55
         //make sure this was called in the right place!
56
-        if (! did_action('AHEE__EE_System__load_espresso_addons')
56
+        if ( ! did_action('AHEE__EE_System__load_espresso_addons')
57 57
             || did_action('AHEE__EE_System___detect_if_activation_or_upgrade__begin')
58 58
         ) {
59 59
             EE_Error::doing_it_wrong(
@@ -66,7 +66,7 @@  discard block
 block discarded – undo
66 66
             );
67 67
         }
68 68
         //setup $_settings array from incoming values.
69
-        self::$_settings[ $dms_id ] = array(
69
+        self::$_settings[$dms_id] = array(
70 70
             'dms_paths' => isset($setup_args['dms_paths']) ? (array) $setup_args['dms_paths'] : array(),
71 71
         );
72 72
         // setup DMS
@@ -99,8 +99,8 @@  discard block
 block discarded – undo
99 99
      */
100 100
     public static function deregister($dms_id = null)
101 101
     {
102
-        if (isset(self::$_settings[ $dms_id ])) {
103
-            unset(self::$_settings[ $dms_id ]);
102
+        if (isset(self::$_settings[$dms_id])) {
103
+            unset(self::$_settings[$dms_id]);
104 104
         }
105 105
     }
106 106
 }
Please login to merge, or discard this patch.
caffeinated/admin/new/pricing/espresso_events_Pricing_Hooks.class.php 3 patches
Doc Comments   +12 added lines, -12 removed lines patch added patch discarded remove patch
@@ -1163,8 +1163,8 @@  discard block
 block discarded – undo
1163 1163
      * @param array       $datetime_tickets
1164 1164
      * @param array       $all_tickets
1165 1165
      * @param bool        $default
1166
-     * @param array       $all_datetimes
1167
-     * @return mixed
1166
+     * @param EE_Datetime[]       $all_datetimes
1167
+     * @return string
1168 1168
      * @throws DomainException
1169 1169
      * @throws EE_Error
1170 1170
      */
@@ -1275,7 +1275,7 @@  discard block
 block discarded – undo
1275 1275
      * @param array       $datetime_tickets
1276 1276
      * @param array       $all_tickets
1277 1277
      * @param bool        $default
1278
-     * @return mixed
1278
+     * @return string
1279 1279
      * @throws DomainException
1280 1280
      * @throws EE_Error
1281 1281
      */
@@ -1343,7 +1343,7 @@  discard block
 block discarded – undo
1343 1343
      * @param EE_Ticket   $ticket
1344 1344
      * @param array       $datetime_tickets
1345 1345
      * @param bool        $default
1346
-     * @return mixed
1346
+     * @return string
1347 1347
      * @throws DomainException
1348 1348
      * @throws EE_Error
1349 1349
      */
@@ -1413,7 +1413,7 @@  discard block
 block discarded – undo
1413 1413
      * @param bool          $default          Whether default row being generated or not.
1414 1414
      * @param EE_Ticket[]   $all_tickets      This is an array of all tickets attached to the event
1415 1415
      *                                        (or empty in the case of defaults)
1416
-     * @return mixed
1416
+     * @return string
1417 1417
      * @throws InvalidArgumentException
1418 1418
      * @throws InvalidInterfaceException
1419 1419
      * @throws InvalidDataTypeException
@@ -1737,7 +1737,7 @@  discard block
 block discarded – undo
1737 1737
      * @param EE_Ticket|null $ticket
1738 1738
      * @param bool           $show_trash
1739 1739
      * @param bool           $show_create
1740
-     * @return mixed
1740
+     * @return string
1741 1741
      * @throws InvalidArgumentException
1742 1742
      * @throws InvalidInterfaceException
1743 1743
      * @throws InvalidDataTypeException
@@ -1840,7 +1840,7 @@  discard block
 block discarded – undo
1840 1840
      * @param EE_Price $price
1841 1841
      * @param bool     $default
1842 1842
      * @param bool     $disabled
1843
-     * @return mixed
1843
+     * @return string
1844 1844
      * @throws ReflectionException
1845 1845
      * @throws InvalidArgumentException
1846 1846
      * @throws InvalidInterfaceException
@@ -1873,7 +1873,7 @@  discard block
 block discarded – undo
1873 1873
      * @param int      $price_row
1874 1874
      * @param EE_Price $price
1875 1875
      * @param bool     $default
1876
-     * @return mixed
1876
+     * @return string
1877 1877
      * @throws DomainException
1878 1878
      * @throws EE_Error
1879 1879
      */
@@ -1910,7 +1910,7 @@  discard block
 block discarded – undo
1910 1910
      * @param EE_Price $price
1911 1911
      * @param bool     $default
1912 1912
      * @param bool     $disabled
1913
-     * @return mixed
1913
+     * @return string
1914 1914
      * @throws ReflectionException
1915 1915
      * @throws InvalidArgumentException
1916 1916
      * @throws InvalidInterfaceException
@@ -2012,7 +2012,7 @@  discard block
 block discarded – undo
2012 2012
      * @param EE_Ticket|null   $ticket
2013 2013
      * @param array            $ticket_datetimes
2014 2014
      * @param bool             $default
2015
-     * @return mixed
2015
+     * @return string
2016 2016
      * @throws DomainException
2017 2017
      * @throws EE_Error
2018 2018
      */
@@ -2065,9 +2065,9 @@  discard block
 block discarded – undo
2065 2065
 
2066 2066
 
2067 2067
     /**
2068
-     * @param array $all_datetimes
2068
+     * @param EE_Datetime[] $all_datetimes
2069 2069
      * @param array $all_tickets
2070
-     * @return mixed
2070
+     * @return string
2071 2071
      * @throws ReflectionException
2072 2072
      * @throws InvalidArgumentException
2073 2073
      * @throws InvalidInterfaceException
Please login to merge, or discard this patch.
Indentation   +2130 added lines, -2130 removed lines patch added patch discarded remove patch
@@ -19,2190 +19,2190 @@
 block discarded – undo
19 19
 class espresso_events_Pricing_Hooks extends EE_Admin_Hooks
20 20
 {
21 21
 
22
-    /**
23
-     * This property is just used to hold the status of whether an event is currently being
24
-     * created (true) or edited (false)
25
-     *
26
-     * @access protected
27
-     * @var bool
28
-     */
29
-    protected $_is_creating_event;
22
+	/**
23
+	 * This property is just used to hold the status of whether an event is currently being
24
+	 * created (true) or edited (false)
25
+	 *
26
+	 * @access protected
27
+	 * @var bool
28
+	 */
29
+	protected $_is_creating_event;
30 30
 
31
-    /**
32
-     * Used to contain the format strings for date and time that will be used for php date and
33
-     * time.
34
-     * Is set in the _set_hooks_properties() method.
35
-     *
36
-     * @var array
37
-     */
38
-    protected $_date_format_strings;
31
+	/**
32
+	 * Used to contain the format strings for date and time that will be used for php date and
33
+	 * time.
34
+	 * Is set in the _set_hooks_properties() method.
35
+	 *
36
+	 * @var array
37
+	 */
38
+	protected $_date_format_strings;
39 39
 
40
-    /**
41
-     * @var string $_date_time_format
42
-     */
43
-    protected $_date_time_format;
40
+	/**
41
+	 * @var string $_date_time_format
42
+	 */
43
+	protected $_date_time_format;
44 44
 
45 45
 
46
-    /**
47
-     * @throws InvalidArgumentException
48
-     * @throws InvalidInterfaceException
49
-     * @throws InvalidDataTypeException
50
-     */
51
-    protected function _set_hooks_properties()
52
-    {
53
-        $this->_name = 'pricing';
54
-        //capability check
55
-        if (! EE_Registry::instance()->CAP->current_user_can(
56
-            'ee_read_default_prices',
57
-            'advanced_ticket_datetime_metabox'
58
-        )) {
59
-            return;
60
-        }
61
-        $this->_setup_metaboxes();
62
-        $this->_set_date_time_formats();
63
-        $this->_validate_format_strings();
64
-        $this->_set_scripts_styles();
65
-        // commented out temporarily until logic is implemented in callback
66
-        // add_action(
67
-        //     'AHEE__EE_Admin_Page_CPT__do_extra_autosave_stuff__after_Extend_Events_Admin_Page',
68
-        //     array($this, 'autosave_handling')
69
-        // );
70
-        add_filter(
71
-            'FHEE__Events_Admin_Page___insert_update_cpt_item__event_update_callbacks',
72
-            array($this, 'caf_updates')
73
-        );
74
-    }
46
+	/**
47
+	 * @throws InvalidArgumentException
48
+	 * @throws InvalidInterfaceException
49
+	 * @throws InvalidDataTypeException
50
+	 */
51
+	protected function _set_hooks_properties()
52
+	{
53
+		$this->_name = 'pricing';
54
+		//capability check
55
+		if (! EE_Registry::instance()->CAP->current_user_can(
56
+			'ee_read_default_prices',
57
+			'advanced_ticket_datetime_metabox'
58
+		)) {
59
+			return;
60
+		}
61
+		$this->_setup_metaboxes();
62
+		$this->_set_date_time_formats();
63
+		$this->_validate_format_strings();
64
+		$this->_set_scripts_styles();
65
+		// commented out temporarily until logic is implemented in callback
66
+		// add_action(
67
+		//     'AHEE__EE_Admin_Page_CPT__do_extra_autosave_stuff__after_Extend_Events_Admin_Page',
68
+		//     array($this, 'autosave_handling')
69
+		// );
70
+		add_filter(
71
+			'FHEE__Events_Admin_Page___insert_update_cpt_item__event_update_callbacks',
72
+			array($this, 'caf_updates')
73
+		);
74
+	}
75 75
 
76 76
 
77
-    /**
78
-     * @return void
79
-     */
80
-    protected function _setup_metaboxes()
81
-    {
82
-        //if we were going to add our own metaboxes we'd use the below.
83
-        $this->_metaboxes        = array(
84
-            0 => array(
85
-                'page_route' => array('edit', 'create_new'),
86
-                'func'       => 'pricing_metabox',
87
-                'label'      => esc_html__('Event Tickets & Datetimes', 'event_espresso'),
88
-                'priority'   => 'high',
89
-                'context'    => 'normal',
90
-            ),
91
-        );
92
-        $this->_remove_metaboxes = array(
93
-            0 => array(
94
-                'page_route' => array('edit', 'create_new'),
95
-                'id'         => 'espresso_event_editor_tickets',
96
-                'context'    => 'normal',
97
-            ),
98
-        );
99
-    }
77
+	/**
78
+	 * @return void
79
+	 */
80
+	protected function _setup_metaboxes()
81
+	{
82
+		//if we were going to add our own metaboxes we'd use the below.
83
+		$this->_metaboxes        = array(
84
+			0 => array(
85
+				'page_route' => array('edit', 'create_new'),
86
+				'func'       => 'pricing_metabox',
87
+				'label'      => esc_html__('Event Tickets & Datetimes', 'event_espresso'),
88
+				'priority'   => 'high',
89
+				'context'    => 'normal',
90
+			),
91
+		);
92
+		$this->_remove_metaboxes = array(
93
+			0 => array(
94
+				'page_route' => array('edit', 'create_new'),
95
+				'id'         => 'espresso_event_editor_tickets',
96
+				'context'    => 'normal',
97
+			),
98
+		);
99
+	}
100 100
 
101 101
 
102
-    /**
103
-     * @return void
104
-     */
105
-    protected function _set_date_time_formats()
106
-    {
107
-        /**
108
-         * Format strings for date and time.  Defaults are existing behaviour from 4.1.
109
-         * Note, that if you return null as the value for 'date', and 'time' in the array, then
110
-         * EE will automatically use the set wp_options, 'date_format', and 'time_format'.
111
-         *
112
-         * @since 4.6.7
113
-         * @var array  Expected an array returned with 'date' and 'time' keys.
114
-         */
115
-        $this->_date_format_strings = apply_filters(
116
-            'FHEE__espresso_events_Pricing_Hooks___set_hooks_properties__date_format_strings',
117
-            array(
118
-                'date' => 'Y-m-d',
119
-                'time' => 'h:i a',
120
-            )
121
-        );
122
-        //validate
123
-        $this->_date_format_strings['date'] = isset($this->_date_format_strings['date'])
124
-            ? $this->_date_format_strings['date']
125
-            : null;
126
-        $this->_date_format_strings['time'] = isset($this->_date_format_strings['time'])
127
-            ? $this->_date_format_strings['time']
128
-            : null;
129
-        $this->_date_time_format            = $this->_date_format_strings['date']
130
-                                              . ' '
131
-                                              . $this->_date_format_strings['time'];
132
-    }
102
+	/**
103
+	 * @return void
104
+	 */
105
+	protected function _set_date_time_formats()
106
+	{
107
+		/**
108
+		 * Format strings for date and time.  Defaults are existing behaviour from 4.1.
109
+		 * Note, that if you return null as the value for 'date', and 'time' in the array, then
110
+		 * EE will automatically use the set wp_options, 'date_format', and 'time_format'.
111
+		 *
112
+		 * @since 4.6.7
113
+		 * @var array  Expected an array returned with 'date' and 'time' keys.
114
+		 */
115
+		$this->_date_format_strings = apply_filters(
116
+			'FHEE__espresso_events_Pricing_Hooks___set_hooks_properties__date_format_strings',
117
+			array(
118
+				'date' => 'Y-m-d',
119
+				'time' => 'h:i a',
120
+			)
121
+		);
122
+		//validate
123
+		$this->_date_format_strings['date'] = isset($this->_date_format_strings['date'])
124
+			? $this->_date_format_strings['date']
125
+			: null;
126
+		$this->_date_format_strings['time'] = isset($this->_date_format_strings['time'])
127
+			? $this->_date_format_strings['time']
128
+			: null;
129
+		$this->_date_time_format            = $this->_date_format_strings['date']
130
+											  . ' '
131
+											  . $this->_date_format_strings['time'];
132
+	}
133 133
 
134 134
 
135
-    /**
136
-     * @return void
137
-     */
138
-    protected function _validate_format_strings()
139
-    {
140
-        //validate format strings
141
-        $format_validation = EEH_DTT_Helper::validate_format_string(
142
-            $this->_date_time_format
143
-        );
144
-        if (is_array($format_validation)) {
145
-            $msg = '<p>';
146
-            $msg .= sprintf(
147
-                esc_html__(
148
-                    'The format "%s" was likely added via a filter and is invalid for the following reasons:',
149
-                    'event_espresso'
150
-                ),
151
-                $this->_date_time_format
152
-            );
153
-            $msg .= '</p><ul>';
154
-            foreach ($format_validation as $error) {
155
-                $msg .= '<li>' . $error . '</li>';
156
-            }
157
-            $msg .= '</ul><p>';
158
-            $msg .= sprintf(
159
-                esc_html__(
160
-                    '%sPlease note that your date and time formats have been reset to "Y-m-d" and "h:i a" respectively.%s',
161
-                    'event_espresso'
162
-                ),
163
-                '<span style="color:#D54E21;">',
164
-                '</span>'
165
-            );
166
-            $msg .= '</p>';
167
-            EE_Error::add_attention($msg, __FILE__, __FUNCTION__, __LINE__);
168
-            $this->_date_format_strings = array(
169
-                'date' => 'Y-m-d',
170
-                'time' => 'h:i a',
171
-            );
172
-        }
173
-    }
135
+	/**
136
+	 * @return void
137
+	 */
138
+	protected function _validate_format_strings()
139
+	{
140
+		//validate format strings
141
+		$format_validation = EEH_DTT_Helper::validate_format_string(
142
+			$this->_date_time_format
143
+		);
144
+		if (is_array($format_validation)) {
145
+			$msg = '<p>';
146
+			$msg .= sprintf(
147
+				esc_html__(
148
+					'The format "%s" was likely added via a filter and is invalid for the following reasons:',
149
+					'event_espresso'
150
+				),
151
+				$this->_date_time_format
152
+			);
153
+			$msg .= '</p><ul>';
154
+			foreach ($format_validation as $error) {
155
+				$msg .= '<li>' . $error . '</li>';
156
+			}
157
+			$msg .= '</ul><p>';
158
+			$msg .= sprintf(
159
+				esc_html__(
160
+					'%sPlease note that your date and time formats have been reset to "Y-m-d" and "h:i a" respectively.%s',
161
+					'event_espresso'
162
+				),
163
+				'<span style="color:#D54E21;">',
164
+				'</span>'
165
+			);
166
+			$msg .= '</p>';
167
+			EE_Error::add_attention($msg, __FILE__, __FUNCTION__, __LINE__);
168
+			$this->_date_format_strings = array(
169
+				'date' => 'Y-m-d',
170
+				'time' => 'h:i a',
171
+			);
172
+		}
173
+	}
174 174
 
175 175
 
176
-    /**
177
-     * @return void
178
-     */
179
-    protected function _set_scripts_styles()
180
-    {
181
-        $this->_scripts_styles = array(
182
-            'registers'   => array(
183
-                'ee-tickets-datetimes-css' => array(
184
-                    'url'  => PRICING_ASSETS_URL . 'event-tickets-datetimes.css',
185
-                    'type' => 'css',
186
-                ),
187
-                'ee-dtt-ticket-metabox'    => array(
188
-                    'url'     => PRICING_ASSETS_URL . 'ee-datetime-ticket-metabox.js',
189
-                    'depends' => array('ee-datepicker', 'ee-dialog', 'underscore'),
190
-                ),
191
-            ),
192
-            'deregisters' => array(
193
-                'event-editor-css'       => array('type' => 'css'),
194
-                'event-datetime-metabox' => array('type' => 'js'),
195
-            ),
196
-            'enqueues'    => array(
197
-                'ee-tickets-datetimes-css' => array('edit', 'create_new'),
198
-                'ee-dtt-ticket-metabox'    => array('edit', 'create_new'),
199
-            ),
200
-            'localize'    => array(
201
-                'ee-dtt-ticket-metabox' => array(
202
-                    'DTT_TRASH_BLOCK'       => array(
203
-                        'main_warning'            => esc_html__(
204
-                            'The Datetime you are attempting to trash is the only datetime selected for the following ticket(s):',
205
-                            'event_espresso'
206
-                        ),
207
-                        'after_warning'           => esc_html__(
208
-                            'In order to trash this datetime you must first make sure the above ticket(s) are assigned to other datetimes.',
209
-                            'event_espresso'
210
-                        ),
211
-                        'cancel_button'           => '<button class="button-secondary ee-modal-cancel">'
212
-                                                     . esc_html__('Cancel', 'event_espresso') . '</button>',
213
-                        'close_button'            => '<button class="button-secondary ee-modal-cancel">'
214
-                                                     . esc_html__('Close', 'event_espresso') . '</button>',
215
-                        'single_warning_from_tkt' => esc_html__(
216
-                            'The Datetime you are attempting to unassign from this ticket is the only remaining datetime for this ticket. Tickets must always have at least one datetime assigned to them.',
217
-                            'event_espresso'
218
-                        ),
219
-                        'single_warning_from_dtt' => esc_html__(
220
-                            'The ticket you are attempting to unassign from this datetime cannot be unassigned because the datetime is the only remaining datetime for the ticket.  Tickets must always have at least one datetime assigned to them.',
221
-                            'event_espresso'
222
-                        ),
223
-                        'dismiss_button'          => '<button class="button-secondary ee-modal-cancel">'
224
-                                                     . esc_html__('Dismiss', 'event_espresso') . '</button>',
225
-                    ),
226
-                    'DTT_ERROR_MSG'         => array(
227
-                        'no_ticket_name' => esc_html__('General Admission', 'event_espresso'),
228
-                        'dismiss_button' => '<div class="save-cancel-button-container">'
229
-                                            . '<button class="button-secondary ee-modal-cancel">'
230
-                                            . esc_html__('Dismiss', 'event_espresso')
231
-                                            . '</button></div>',
232
-                    ),
233
-                    'DTT_OVERSELL_WARNING'  => array(
234
-                        'datetime_ticket' => esc_html__(
235
-                            'You cannot add this ticket to this datetime because it has a sold amount that is greater than the amount of spots remaining for this datetime.',
236
-                            'event_espresso'
237
-                        ),
238
-                        'ticket_datetime' => esc_html__(
239
-                            'You cannot add this datetime to this ticket because the ticket has a sold amount that is greater than the amount of spots remaining on the datetime.',
240
-                            'event_espresso'
241
-                        ),
242
-                    ),
243
-                    'DTT_CONVERTED_FORMATS' => EEH_DTT_Helper::convert_php_to_js_and_moment_date_formats(
244
-                        $this->_date_format_strings['date'],
245
-                        $this->_date_format_strings['time']
246
-                    ),
247
-                    'DTT_START_OF_WEEK'     => array('dayValue' => (int) get_option('start_of_week')),
248
-                ),
249
-            ),
250
-        );
251
-    }
176
+	/**
177
+	 * @return void
178
+	 */
179
+	protected function _set_scripts_styles()
180
+	{
181
+		$this->_scripts_styles = array(
182
+			'registers'   => array(
183
+				'ee-tickets-datetimes-css' => array(
184
+					'url'  => PRICING_ASSETS_URL . 'event-tickets-datetimes.css',
185
+					'type' => 'css',
186
+				),
187
+				'ee-dtt-ticket-metabox'    => array(
188
+					'url'     => PRICING_ASSETS_URL . 'ee-datetime-ticket-metabox.js',
189
+					'depends' => array('ee-datepicker', 'ee-dialog', 'underscore'),
190
+				),
191
+			),
192
+			'deregisters' => array(
193
+				'event-editor-css'       => array('type' => 'css'),
194
+				'event-datetime-metabox' => array('type' => 'js'),
195
+			),
196
+			'enqueues'    => array(
197
+				'ee-tickets-datetimes-css' => array('edit', 'create_new'),
198
+				'ee-dtt-ticket-metabox'    => array('edit', 'create_new'),
199
+			),
200
+			'localize'    => array(
201
+				'ee-dtt-ticket-metabox' => array(
202
+					'DTT_TRASH_BLOCK'       => array(
203
+						'main_warning'            => esc_html__(
204
+							'The Datetime you are attempting to trash is the only datetime selected for the following ticket(s):',
205
+							'event_espresso'
206
+						),
207
+						'after_warning'           => esc_html__(
208
+							'In order to trash this datetime you must first make sure the above ticket(s) are assigned to other datetimes.',
209
+							'event_espresso'
210
+						),
211
+						'cancel_button'           => '<button class="button-secondary ee-modal-cancel">'
212
+													 . esc_html__('Cancel', 'event_espresso') . '</button>',
213
+						'close_button'            => '<button class="button-secondary ee-modal-cancel">'
214
+													 . esc_html__('Close', 'event_espresso') . '</button>',
215
+						'single_warning_from_tkt' => esc_html__(
216
+							'The Datetime you are attempting to unassign from this ticket is the only remaining datetime for this ticket. Tickets must always have at least one datetime assigned to them.',
217
+							'event_espresso'
218
+						),
219
+						'single_warning_from_dtt' => esc_html__(
220
+							'The ticket you are attempting to unassign from this datetime cannot be unassigned because the datetime is the only remaining datetime for the ticket.  Tickets must always have at least one datetime assigned to them.',
221
+							'event_espresso'
222
+						),
223
+						'dismiss_button'          => '<button class="button-secondary ee-modal-cancel">'
224
+													 . esc_html__('Dismiss', 'event_espresso') . '</button>',
225
+					),
226
+					'DTT_ERROR_MSG'         => array(
227
+						'no_ticket_name' => esc_html__('General Admission', 'event_espresso'),
228
+						'dismiss_button' => '<div class="save-cancel-button-container">'
229
+											. '<button class="button-secondary ee-modal-cancel">'
230
+											. esc_html__('Dismiss', 'event_espresso')
231
+											. '</button></div>',
232
+					),
233
+					'DTT_OVERSELL_WARNING'  => array(
234
+						'datetime_ticket' => esc_html__(
235
+							'You cannot add this ticket to this datetime because it has a sold amount that is greater than the amount of spots remaining for this datetime.',
236
+							'event_espresso'
237
+						),
238
+						'ticket_datetime' => esc_html__(
239
+							'You cannot add this datetime to this ticket because the ticket has a sold amount that is greater than the amount of spots remaining on the datetime.',
240
+							'event_espresso'
241
+						),
242
+					),
243
+					'DTT_CONVERTED_FORMATS' => EEH_DTT_Helper::convert_php_to_js_and_moment_date_formats(
244
+						$this->_date_format_strings['date'],
245
+						$this->_date_format_strings['time']
246
+					),
247
+					'DTT_START_OF_WEEK'     => array('dayValue' => (int) get_option('start_of_week')),
248
+				),
249
+			),
250
+		);
251
+	}
252 252
 
253 253
 
254
-    /**
255
-     * @param array $update_callbacks
256
-     * @return array
257
-     */
258
-    public function caf_updates(array $update_callbacks)
259
-    {
260
-        foreach ($update_callbacks as $key => $callback) {
261
-            if ($callback[1] === '_default_tickets_update') {
262
-                unset($update_callbacks[ $key ]);
263
-            }
264
-        }
265
-        $update_callbacks[] = array($this, 'datetime_and_tickets_caf_update');
266
-        return $update_callbacks;
267
-    }
254
+	/**
255
+	 * @param array $update_callbacks
256
+	 * @return array
257
+	 */
258
+	public function caf_updates(array $update_callbacks)
259
+	{
260
+		foreach ($update_callbacks as $key => $callback) {
261
+			if ($callback[1] === '_default_tickets_update') {
262
+				unset($update_callbacks[ $key ]);
263
+			}
264
+		}
265
+		$update_callbacks[] = array($this, 'datetime_and_tickets_caf_update');
266
+		return $update_callbacks;
267
+	}
268 268
 
269 269
 
270
-    /**
271
-     * Handles saving everything related to Tickets (datetimes, tickets, prices)
272
-     *
273
-     * @param  EE_Event $event The Event object we're attaching data to
274
-     * @param  array    $data  The request data from the form
275
-     * @throws ReflectionException
276
-     * @throws Exception
277
-     * @throws InvalidInterfaceException
278
-     * @throws InvalidDataTypeException
279
-     * @throws EE_Error
280
-     * @throws InvalidArgumentException
281
-     */
282
-    public function datetime_and_tickets_caf_update($event, $data)
283
-    {
284
-        //first we need to start with datetimes cause they are the "root" items attached to events.
285
-        $saved_datetimes = $this->_update_datetimes($event, $data);
286
-        //next tackle the tickets (and prices?)
287
-        $this->_update_tickets($event, $saved_datetimes, $data);
288
-    }
270
+	/**
271
+	 * Handles saving everything related to Tickets (datetimes, tickets, prices)
272
+	 *
273
+	 * @param  EE_Event $event The Event object we're attaching data to
274
+	 * @param  array    $data  The request data from the form
275
+	 * @throws ReflectionException
276
+	 * @throws Exception
277
+	 * @throws InvalidInterfaceException
278
+	 * @throws InvalidDataTypeException
279
+	 * @throws EE_Error
280
+	 * @throws InvalidArgumentException
281
+	 */
282
+	public function datetime_and_tickets_caf_update($event, $data)
283
+	{
284
+		//first we need to start with datetimes cause they are the "root" items attached to events.
285
+		$saved_datetimes = $this->_update_datetimes($event, $data);
286
+		//next tackle the tickets (and prices?)
287
+		$this->_update_tickets($event, $saved_datetimes, $data);
288
+	}
289 289
 
290 290
 
291
-    /**
292
-     * update event_datetimes
293
-     *
294
-     * @param  EE_Event $event Event being updated
295
-     * @param  array    $data  the request data from the form
296
-     * @return EE_Datetime[]
297
-     * @throws Exception
298
-     * @throws ReflectionException
299
-     * @throws InvalidInterfaceException
300
-     * @throws InvalidDataTypeException
301
-     * @throws InvalidArgumentException
302
-     * @throws EE_Error
303
-     */
304
-    protected function _update_datetimes($event, $data)
305
-    {
306
-        $timezone       = isset($data['timezone_string']) ? $data['timezone_string'] : null;
307
-        $saved_dtt_ids  = array();
308
-        $saved_dtt_objs = array();
309
-        if (empty($data['edit_event_datetimes']) || ! is_array($data['edit_event_datetimes'])) {
310
-            throw new InvalidArgumentException(
311
-                esc_html__(
312
-                    'The "edit_event_datetimes" array is invalid therefore the event can not be updated.',
313
-                    'event_espresso'
314
-                )
315
-            );
316
-        }
317
-        foreach ($data['edit_event_datetimes'] as $row => $datetime_data) {
318
-            //trim all values to ensure any excess whitespace is removed.
319
-            $datetime_data                = array_map(
320
-                function ($datetime_data)
321
-                {
322
-                    return is_array($datetime_data) ? $datetime_data : trim($datetime_data);
323
-                },
324
-                $datetime_data
325
-            );
326
-            $datetime_data['DTT_EVT_end'] = isset($datetime_data['DTT_EVT_end'])
327
-                                            && ! empty($datetime_data['DTT_EVT_end'])
328
-                ? $datetime_data['DTT_EVT_end']
329
-                : $datetime_data['DTT_EVT_start'];
330
-            $datetime_values              = array(
331
-                'DTT_ID'          => ! empty($datetime_data['DTT_ID'])
332
-                    ? $datetime_data['DTT_ID']
333
-                    : null,
334
-                'DTT_name'        => ! empty($datetime_data['DTT_name'])
335
-                    ? $datetime_data['DTT_name']
336
-                    : '',
337
-                'DTT_description' => ! empty($datetime_data['DTT_description'])
338
-                    ? $datetime_data['DTT_description']
339
-                    : '',
340
-                'DTT_EVT_start'   => $datetime_data['DTT_EVT_start'],
341
-                'DTT_EVT_end'     => $datetime_data['DTT_EVT_end'],
342
-                'DTT_reg_limit'   => empty($datetime_data['DTT_reg_limit'])
343
-                    ? EE_INF
344
-                    : $datetime_data['DTT_reg_limit'],
345
-                'DTT_order'       => ! isset($datetime_data['DTT_order'])
346
-                    ? $row
347
-                    : $datetime_data['DTT_order'],
348
-            );
349
-            // if we have an id then let's get existing object first and then set the new values.
350
-            // Otherwise we instantiate a new object for save.
351
-            if (! empty($datetime_data['DTT_ID'])) {
352
-                $datetime = EE_Registry::instance()
353
-                                       ->load_model('Datetime', array($timezone))
354
-                                       ->get_one_by_ID($datetime_data['DTT_ID']);
355
-                //set date and time format according to what is set in this class.
356
-                $datetime->set_date_format($this->_date_format_strings['date']);
357
-                $datetime->set_time_format($this->_date_format_strings['time']);
358
-                foreach ($datetime_values as $field => $value) {
359
-                    $datetime->set($field, $value);
360
-                }
361
-                // make sure the $dtt_id here is saved just in case
362
-                // after the add_relation_to() the autosave replaces it.
363
-                // We need to do this so we dont' TRASH the parent DTT.
364
-                // (save the ID for both key and value to avoid duplications)
365
-                $saved_dtt_ids[ $datetime->ID() ] = $datetime->ID();
366
-            } else {
367
-                $datetime = EE_Registry::instance()->load_class(
368
-                    'Datetime',
369
-                    array(
370
-                        $datetime_values,
371
-                        $timezone,
372
-                        array($this->_date_format_strings['date'], $this->_date_format_strings['time']),
373
-                    ),
374
-                    false,
375
-                    false
376
-                );
377
-                foreach ($datetime_values as $field => $value) {
378
-                    $datetime->set($field, $value);
379
-                }
380
-            }
381
-            $datetime->save();
382
-            $datetime = $event->_add_relation_to($datetime, 'Datetime');
383
-            // before going any further make sure our dates are setup correctly
384
-            // so that the end date is always equal or greater than the start date.
385
-            if ($datetime->get_raw('DTT_EVT_start') > $datetime->get_raw('DTT_EVT_end')) {
386
-                $datetime->set('DTT_EVT_end', $datetime->get('DTT_EVT_start'));
387
-                $datetime = EEH_DTT_Helper::date_time_add($datetime, 'DTT_EVT_end', 'days');
388
-                $datetime->save();
389
-            }
390
-            //	now we have to make sure we add the new DTT_ID to the $saved_dtt_ids array
391
-            // because it is possible there was a new one created for the autosave.
392
-            // (save the ID for both key and value to avoid duplications)
393
-            $DTT_ID                   = $datetime->ID();
394
-            $saved_dtt_ids[ $DTT_ID ] = $DTT_ID;
395
-            $saved_dtt_objs[ $row ]   = $datetime;
396
-            //todo if ANY of these updates fail then we want the appropriate global error message.
397
-        }
398
-        $event->save();
399
-        // now we need to REMOVE any datetimes that got deleted.
400
-        // Keep in mind that this process will only kick in for datetimes that don't have any DTT_sold on them.
401
-        // So its safe to permanently delete at this point.
402
-        $old_datetimes = explode(',', $data['datetime_IDs']);
403
-        $old_datetimes = $old_datetimes[0] === '' ? array() : $old_datetimes;
404
-        if (is_array($old_datetimes)) {
405
-            $datetimes_to_delete = array_diff($old_datetimes, $saved_dtt_ids);
406
-            foreach ($datetimes_to_delete as $id) {
407
-                $id = absint($id);
408
-                if (empty($id)) {
409
-                    continue;
410
-                }
411
-                $dtt_to_remove = EE_Registry::instance()->load_model('Datetime')->get_one_by_ID($id);
412
-                //remove tkt relationships.
413
-                $related_tickets = $dtt_to_remove->get_many_related('Ticket');
414
-                foreach ($related_tickets as $tkt) {
415
-                    $dtt_to_remove->_remove_relation_to($tkt, 'Ticket');
416
-                }
417
-                $event->_remove_relation_to($id, 'Datetime');
418
-                $dtt_to_remove->refresh_cache_of_related_objects();
419
-            }
420
-        }
421
-        return $saved_dtt_objs;
422
-    }
291
+	/**
292
+	 * update event_datetimes
293
+	 *
294
+	 * @param  EE_Event $event Event being updated
295
+	 * @param  array    $data  the request data from the form
296
+	 * @return EE_Datetime[]
297
+	 * @throws Exception
298
+	 * @throws ReflectionException
299
+	 * @throws InvalidInterfaceException
300
+	 * @throws InvalidDataTypeException
301
+	 * @throws InvalidArgumentException
302
+	 * @throws EE_Error
303
+	 */
304
+	protected function _update_datetimes($event, $data)
305
+	{
306
+		$timezone       = isset($data['timezone_string']) ? $data['timezone_string'] : null;
307
+		$saved_dtt_ids  = array();
308
+		$saved_dtt_objs = array();
309
+		if (empty($data['edit_event_datetimes']) || ! is_array($data['edit_event_datetimes'])) {
310
+			throw new InvalidArgumentException(
311
+				esc_html__(
312
+					'The "edit_event_datetimes" array is invalid therefore the event can not be updated.',
313
+					'event_espresso'
314
+				)
315
+			);
316
+		}
317
+		foreach ($data['edit_event_datetimes'] as $row => $datetime_data) {
318
+			//trim all values to ensure any excess whitespace is removed.
319
+			$datetime_data                = array_map(
320
+				function ($datetime_data)
321
+				{
322
+					return is_array($datetime_data) ? $datetime_data : trim($datetime_data);
323
+				},
324
+				$datetime_data
325
+			);
326
+			$datetime_data['DTT_EVT_end'] = isset($datetime_data['DTT_EVT_end'])
327
+											&& ! empty($datetime_data['DTT_EVT_end'])
328
+				? $datetime_data['DTT_EVT_end']
329
+				: $datetime_data['DTT_EVT_start'];
330
+			$datetime_values              = array(
331
+				'DTT_ID'          => ! empty($datetime_data['DTT_ID'])
332
+					? $datetime_data['DTT_ID']
333
+					: null,
334
+				'DTT_name'        => ! empty($datetime_data['DTT_name'])
335
+					? $datetime_data['DTT_name']
336
+					: '',
337
+				'DTT_description' => ! empty($datetime_data['DTT_description'])
338
+					? $datetime_data['DTT_description']
339
+					: '',
340
+				'DTT_EVT_start'   => $datetime_data['DTT_EVT_start'],
341
+				'DTT_EVT_end'     => $datetime_data['DTT_EVT_end'],
342
+				'DTT_reg_limit'   => empty($datetime_data['DTT_reg_limit'])
343
+					? EE_INF
344
+					: $datetime_data['DTT_reg_limit'],
345
+				'DTT_order'       => ! isset($datetime_data['DTT_order'])
346
+					? $row
347
+					: $datetime_data['DTT_order'],
348
+			);
349
+			// if we have an id then let's get existing object first and then set the new values.
350
+			// Otherwise we instantiate a new object for save.
351
+			if (! empty($datetime_data['DTT_ID'])) {
352
+				$datetime = EE_Registry::instance()
353
+									   ->load_model('Datetime', array($timezone))
354
+									   ->get_one_by_ID($datetime_data['DTT_ID']);
355
+				//set date and time format according to what is set in this class.
356
+				$datetime->set_date_format($this->_date_format_strings['date']);
357
+				$datetime->set_time_format($this->_date_format_strings['time']);
358
+				foreach ($datetime_values as $field => $value) {
359
+					$datetime->set($field, $value);
360
+				}
361
+				// make sure the $dtt_id here is saved just in case
362
+				// after the add_relation_to() the autosave replaces it.
363
+				// We need to do this so we dont' TRASH the parent DTT.
364
+				// (save the ID for both key and value to avoid duplications)
365
+				$saved_dtt_ids[ $datetime->ID() ] = $datetime->ID();
366
+			} else {
367
+				$datetime = EE_Registry::instance()->load_class(
368
+					'Datetime',
369
+					array(
370
+						$datetime_values,
371
+						$timezone,
372
+						array($this->_date_format_strings['date'], $this->_date_format_strings['time']),
373
+					),
374
+					false,
375
+					false
376
+				);
377
+				foreach ($datetime_values as $field => $value) {
378
+					$datetime->set($field, $value);
379
+				}
380
+			}
381
+			$datetime->save();
382
+			$datetime = $event->_add_relation_to($datetime, 'Datetime');
383
+			// before going any further make sure our dates are setup correctly
384
+			// so that the end date is always equal or greater than the start date.
385
+			if ($datetime->get_raw('DTT_EVT_start') > $datetime->get_raw('DTT_EVT_end')) {
386
+				$datetime->set('DTT_EVT_end', $datetime->get('DTT_EVT_start'));
387
+				$datetime = EEH_DTT_Helper::date_time_add($datetime, 'DTT_EVT_end', 'days');
388
+				$datetime->save();
389
+			}
390
+			//	now we have to make sure we add the new DTT_ID to the $saved_dtt_ids array
391
+			// because it is possible there was a new one created for the autosave.
392
+			// (save the ID for both key and value to avoid duplications)
393
+			$DTT_ID                   = $datetime->ID();
394
+			$saved_dtt_ids[ $DTT_ID ] = $DTT_ID;
395
+			$saved_dtt_objs[ $row ]   = $datetime;
396
+			//todo if ANY of these updates fail then we want the appropriate global error message.
397
+		}
398
+		$event->save();
399
+		// now we need to REMOVE any datetimes that got deleted.
400
+		// Keep in mind that this process will only kick in for datetimes that don't have any DTT_sold on them.
401
+		// So its safe to permanently delete at this point.
402
+		$old_datetimes = explode(',', $data['datetime_IDs']);
403
+		$old_datetimes = $old_datetimes[0] === '' ? array() : $old_datetimes;
404
+		if (is_array($old_datetimes)) {
405
+			$datetimes_to_delete = array_diff($old_datetimes, $saved_dtt_ids);
406
+			foreach ($datetimes_to_delete as $id) {
407
+				$id = absint($id);
408
+				if (empty($id)) {
409
+					continue;
410
+				}
411
+				$dtt_to_remove = EE_Registry::instance()->load_model('Datetime')->get_one_by_ID($id);
412
+				//remove tkt relationships.
413
+				$related_tickets = $dtt_to_remove->get_many_related('Ticket');
414
+				foreach ($related_tickets as $tkt) {
415
+					$dtt_to_remove->_remove_relation_to($tkt, 'Ticket');
416
+				}
417
+				$event->_remove_relation_to($id, 'Datetime');
418
+				$dtt_to_remove->refresh_cache_of_related_objects();
419
+			}
420
+		}
421
+		return $saved_dtt_objs;
422
+	}
423 423
 
424 424
 
425
-    /**
426
-     * update tickets
427
-     *
428
-     * @param  EE_Event      $event           Event object being updated
429
-     * @param  EE_Datetime[] $saved_datetimes an array of datetime ids being updated
430
-     * @param  array         $data            incoming request data
431
-     * @return EE_Ticket[]
432
-     * @throws Exception
433
-     * @throws ReflectionException
434
-     * @throws InvalidInterfaceException
435
-     * @throws InvalidDataTypeException
436
-     * @throws InvalidArgumentException
437
-     * @throws EE_Error
438
-     */
439
-    protected function _update_tickets($event, $saved_datetimes, $data)
440
-    {
441
-        $new_tkt     = null;
442
-        $new_default = null;
443
-        //stripslashes because WP filtered the $_POST ($data) array to add slashes
444
-        $data          = stripslashes_deep($data);
445
-        $timezone      = isset($data['timezone_string']) ? $data['timezone_string'] : null;
446
-        $saved_tickets = $datetimes_on_existing = array();
447
-        $old_tickets   = isset($data['ticket_IDs']) ? explode(',', $data['ticket_IDs']) : array();
448
-        if (empty($data['edit_tickets']) || ! is_array($data['edit_tickets'])) {
449
-            throw new InvalidArgumentException(
450
-                esc_html__(
451
-                    'The "edit_tickets" array is invalid therefore the event can not be updated.',
452
-                    'event_espresso'
453
-                )
454
-            );
455
-        }
456
-        foreach ($data['edit_tickets'] as $row => $tkt) {
457
-            $update_prices = $create_new_TKT = false;
458
-            // figure out what datetimes were added to the ticket
459
-            // and what datetimes were removed from the ticket in the session.
460
-            $starting_tkt_dtt_rows = explode(',', $data['starting_ticket_datetime_rows'][ $row ]);
461
-            $tkt_dtt_rows          = explode(',', $data['ticket_datetime_rows'][ $row ]);
462
-            $datetimes_added       = array_diff($tkt_dtt_rows, $starting_tkt_dtt_rows);
463
-            $datetimes_removed     = array_diff($starting_tkt_dtt_rows, $tkt_dtt_rows);
464
-            // trim inputs to ensure any excess whitespace is removed.
465
-            $tkt = array_map(
466
-                function ($ticket_data)
467
-                {
468
-                    return is_array($ticket_data) ? $ticket_data : trim($ticket_data);
469
-                },
470
-                $tkt
471
-            );
472
-            // note we are doing conversions to floats here instead of allowing EE_Money_Field to handle
473
-            // because we're doing calculations prior to using the models.
474
-            // note incoming ['TKT_price'] value is already in standard notation (via js).
475
-            $ticket_price = isset($tkt['TKT_price'])
476
-                ? round((float) $tkt['TKT_price'], 3)
477
-                : 0;
478
-            //note incoming base price needs converted from localized value.
479
-            $base_price = isset($tkt['TKT_base_price'])
480
-                ? EEH_Money::convert_to_float_from_localized_money($tkt['TKT_base_price'])
481
-                : 0;
482
-            //if ticket price == 0 and $base_price != 0 then ticket price == base_price
483
-            $ticket_price  = $ticket_price === 0 && $base_price !== 0
484
-                ? $base_price
485
-                : $ticket_price;
486
-            $base_price_id = isset($tkt['TKT_base_price_ID'])
487
-                ? $tkt['TKT_base_price_ID']
488
-                : 0;
489
-            $price_rows    = is_array($data['edit_prices']) && isset($data['edit_prices'][ $row ])
490
-                ? $data['edit_prices'][ $row ]
491
-                : array();
492
-            $now           = null;
493
-            if (empty($tkt['TKT_start_date'])) {
494
-                //lets' use now in the set timezone.
495
-                $now                   = new DateTime('now', new DateTimeZone($event->get_timezone()));
496
-                $tkt['TKT_start_date'] = $now->format($this->_date_time_format);
497
-            }
498
-            if (empty($tkt['TKT_end_date'])) {
499
-                /**
500
-                 * set the TKT_end_date to the first datetime attached to the ticket.
501
-                 */
502
-                $first_dtt           = $saved_datetimes[ reset($tkt_dtt_rows) ];
503
-                $tkt['TKT_end_date'] = $first_dtt->start_date_and_time($this->_date_time_format);
504
-            }
505
-            $TKT_values = array(
506
-                'TKT_ID'          => ! empty($tkt['TKT_ID']) ? $tkt['TKT_ID'] : null,
507
-                'TTM_ID'          => ! empty($tkt['TTM_ID']) ? $tkt['TTM_ID'] : 0,
508
-                'TKT_name'        => ! empty($tkt['TKT_name']) ? $tkt['TKT_name'] : '',
509
-                'TKT_description' => ! empty($tkt['TKT_description'])
510
-                                     && $tkt['TKT_description'] !== esc_html__(
511
-                    'You can modify this description',
512
-                    'event_espresso'
513
-                )
514
-                    ? $tkt['TKT_description']
515
-                    : '',
516
-                'TKT_start_date'  => $tkt['TKT_start_date'],
517
-                'TKT_end_date'    => $tkt['TKT_end_date'],
518
-                'TKT_qty'         => ! isset($tkt['TKT_qty']) || $tkt['TKT_qty'] === ''
519
-                    ? EE_INF
520
-                    : $tkt['TKT_qty'],
521
-                'TKT_uses'        => ! isset($tkt['TKT_uses']) || $tkt['TKT_uses'] === ''
522
-                    ? EE_INF
523
-                    : $tkt['TKT_uses'],
524
-                'TKT_min'         => empty($tkt['TKT_min']) ? 0 : $tkt['TKT_min'],
525
-                'TKT_max'         => empty($tkt['TKT_max']) ? EE_INF : $tkt['TKT_max'],
526
-                'TKT_row'         => $row,
527
-                'TKT_order'       => isset($tkt['TKT_order']) ? $tkt['TKT_order'] : 0,
528
-                'TKT_taxable'     => ! empty($tkt['TKT_taxable']) ? 1 : 0,
529
-                'TKT_required'    => ! empty($tkt['TKT_required']) ? 1 : 0,
530
-                'TKT_price'       => $ticket_price,
531
-            );
532
-            // if this is a default TKT, then we need to set the TKT_ID to 0 and update accordingly,
533
-            // which means in turn that the prices will become new prices as well.
534
-            if (isset($tkt['TKT_is_default']) && $tkt['TKT_is_default']) {
535
-                $TKT_values['TKT_ID']         = 0;
536
-                $TKT_values['TKT_is_default'] = 0;
537
-                $update_prices                = true;
538
-            }
539
-            // if we have a TKT_ID then we need to get that existing TKT_obj and update it
540
-            // we actually do our saves ahead of doing any add_relations to
541
-            // because its entirely possible that this ticket wasn't removed or added to any datetime in the session
542
-            // but DID have it's items modified.
543
-            // keep in mind that if the TKT has been sold (and we have changed pricing information),
544
-            // then we won't be updating the tkt but instead a new tkt will be created and the old one archived.
545
-            if (absint($TKT_values['TKT_ID'])) {
546
-                $ticket = EE_Registry::instance()
547
-                                     ->load_model('Ticket', array($timezone))
548
-                                     ->get_one_by_ID($tkt['TKT_ID']);
549
-                if ($ticket instanceof EE_Ticket) {
550
-                    $ticket = $this->_update_ticket_datetimes(
551
-                        $ticket,
552
-                        $saved_datetimes,
553
-                        $datetimes_added,
554
-                        $datetimes_removed
555
-                    );
556
-                    // are there any registrations using this ticket ?
557
-                    $tickets_sold = $ticket->count_related(
558
-                        'Registration',
559
-                        array(
560
-                            array(
561
-                                'STS_ID' => array('NOT IN', array(EEM_Registration::status_id_incomplete)),
562
-                            ),
563
-                        )
564
-                    );
565
-                    //set ticket formats
566
-                    $ticket->set_date_format($this->_date_format_strings['date']);
567
-                    $ticket->set_time_format($this->_date_format_strings['time']);
568
-                    // let's just check the total price for the existing ticket
569
-                    // and determine if it matches the new total price.
570
-                    // if they are different then we create a new ticket (if tickets sold)
571
-                    // if they aren't different then we go ahead and modify existing ticket.
572
-                    $create_new_TKT = $tickets_sold > 0 && $ticket_price !== $ticket->price() && ! $ticket->deleted();
573
-                    //set new values
574
-                    foreach ($TKT_values as $field => $value) {
575
-                        if ($field === 'TKT_qty') {
576
-                            $ticket->set_qty($value);
577
-                        } else {
578
-                            $ticket->set($field, $value);
579
-                        }
580
-                    }
581
-                    // if $create_new_TKT is false then we can safely update the existing ticket.
582
-                    // Otherwise we have to create a new ticket.
583
-                    if ($create_new_TKT) {
584
-                        $new_tkt = $this->_duplicate_ticket(
585
-                            $ticket,
586
-                            $price_rows,
587
-                            $ticket_price,
588
-                            $base_price,
589
-                            $base_price_id
590
-                        );
591
-                    }
592
-                }
593
-            } else {
594
-                // no TKT_id so a new TKT
595
-                $ticket = EE_Ticket::new_instance(
596
-                    $TKT_values,
597
-                    $timezone,
598
-                    array($this->_date_format_strings['date'], $this->_date_format_strings['time'])
599
-                );
600
-                if ($ticket instanceof EE_Ticket) {
601
-                    // make sure ticket has an ID of setting relations won't work
602
-                    $ticket->save();
603
-                    $ticket        = $this->_update_ticket_datetimes(
604
-                        $ticket,
605
-                        $saved_datetimes,
606
-                        $datetimes_added,
607
-                        $datetimes_removed
608
-                    );
609
-                    $update_prices = true;
610
-                }
611
-            }
612
-            //make sure any current values have been saved.
613
-            //$ticket->save();
614
-            // before going any further make sure our dates are setup correctly
615
-            // so that the end date is always equal or greater than the start date.
616
-            if ($ticket->get_raw('TKT_start_date') > $ticket->get_raw('TKT_end_date')) {
617
-                $ticket->set('TKT_end_date', $ticket->get('TKT_start_date'));
618
-                $ticket = EEH_DTT_Helper::date_time_add($ticket, 'TKT_end_date', 'days');
619
-            }
620
-            //let's make sure the base price is handled
621
-            $ticket = ! $create_new_TKT
622
-                ? $this->_add_prices_to_ticket(
623
-                    array(),
624
-                    $ticket,
625
-                    $update_prices,
626
-                    $base_price,
627
-                    $base_price_id
628
-                )
629
-                : $ticket;
630
-            //add/update price_modifiers
631
-            $ticket = ! $create_new_TKT
632
-                ? $this->_add_prices_to_ticket($price_rows, $ticket, $update_prices)
633
-                : $ticket;
634
-            //need to make sue that the TKT_price is accurate after saving the prices.
635
-            $ticket->ensure_TKT_Price_correct();
636
-            //handle CREATING a default tkt from the incoming tkt but ONLY if this isn't an autosave.
637
-            if (! defined('DOING_AUTOSAVE') && ! empty($tkt['TKT_is_default_selector'])) {
638
-                $update_prices = true;
639
-                $new_default   = clone $ticket;
640
-                $new_default->set('TKT_ID', 0);
641
-                $new_default->set('TKT_is_default', 1);
642
-                $new_default->set('TKT_row', 1);
643
-                $new_default->set('TKT_price', $ticket_price);
644
-                // remove any dtt relations cause we DON'T want dtt relations attached
645
-                // (note this is just removing the cached relations in the object)
646
-                $new_default->_remove_relations('Datetime');
647
-                //todo we need to add the current attached prices as new prices to the new default ticket.
648
-                $new_default = $this->_add_prices_to_ticket(
649
-                    $price_rows,
650
-                    $new_default,
651
-                    $update_prices
652
-                );
653
-                //don't forget the base price!
654
-                $new_default = $this->_add_prices_to_ticket(
655
-                    array(),
656
-                    $new_default,
657
-                    $update_prices,
658
-                    $base_price,
659
-                    $base_price_id
660
-                );
661
-                $new_default->save();
662
-                do_action(
663
-                    'AHEE__espresso_events_Pricing_Hooks___update_tkts_new_default_ticket',
664
-                    $new_default,
665
-                    $row,
666
-                    $ticket,
667
-                    $data
668
-                );
669
-            }
670
-            // DO ALL dtt relationships for both current tickets and any archived tickets
671
-            // for the given dtt that are related to the current ticket.
672
-            // TODO... not sure exactly how we're going to do this considering we don't know
673
-            // what current ticket the archived tickets are related to
674
-            // (and TKT_parent is used for autosaves so that's not a field we can reliably use).
675
-            //let's assign any tickets that have been setup to the saved_tickets tracker
676
-            //save existing TKT
677
-            $ticket->save();
678
-            if ($create_new_TKT && $new_tkt instanceof EE_Ticket) {
679
-                //save new TKT
680
-                $new_tkt->save();
681
-                //add new ticket to array
682
-                $saved_tickets[ $new_tkt->ID() ] = $new_tkt;
683
-                do_action(
684
-                    'AHEE__espresso_events_Pricing_Hooks___update_tkts_new_ticket',
685
-                    $new_tkt,
686
-                    $row,
687
-                    $tkt,
688
-                    $data
689
-                );
690
-            } else {
691
-                //add tkt to saved tkts
692
-                $saved_tickets[ $ticket->ID() ] = $ticket;
693
-                do_action(
694
-                    'AHEE__espresso_events_Pricing_Hooks___update_tkts_update_ticket',
695
-                    $ticket,
696
-                    $row,
697
-                    $tkt,
698
-                    $data
699
-                );
700
-            }
701
-        }
702
-        // now we need to handle tickets actually "deleted permanently".
703
-        // There are cases where we'd want this to happen
704
-        // (i.e. autosaves are happening and then in between autosaves the user trashes a ticket).
705
-        // Or a draft event was saved and in the process of editing a ticket is trashed.
706
-        // No sense in keeping all the related data in the db!
707
-        $old_tickets     = isset($old_tickets[0]) && $old_tickets[0] === '' ? array() : $old_tickets;
708
-        $tickets_removed = array_diff($old_tickets, array_keys($saved_tickets));
709
-        foreach ($tickets_removed as $id) {
710
-            $id = absint($id);
711
-            //get the ticket for this id
712
-            $tkt_to_remove = EE_Registry::instance()->load_model('Ticket')->get_one_by_ID($id);
713
-            //if this tkt is a default tkt we leave it alone cause it won't be attached to the datetime
714
-            if ($tkt_to_remove->get('TKT_is_default')) {
715
-                continue;
716
-            }
717
-            // if this tkt has any registrations attached so then we just ARCHIVE
718
-            // because we don't actually permanently delete these tickets.
719
-            if ($tkt_to_remove->count_related('Registration') > 0) {
720
-                $tkt_to_remove->delete();
721
-                continue;
722
-            }
723
-            // need to get all the related datetimes on this ticket and remove from every single one of them
724
-            // (remember this process can ONLY kick off if there are NO tkts_sold)
725
-            $datetimes = $tkt_to_remove->get_many_related('Datetime');
726
-            foreach ($datetimes as $datetime) {
727
-                $tkt_to_remove->_remove_relation_to($datetime, 'Datetime');
728
-            }
729
-            // need to do the same for prices (except these prices can also be deleted because again,
730
-            // tickets can only be trashed if they don't have any TKTs sold (otherwise they are just archived))
731
-            $tkt_to_remove->delete_related_permanently('Price');
732
-            do_action('AHEE__espresso_events_Pricing_Hooks___update_tkts_delete_ticket', $tkt_to_remove);
733
-            // finally let's delete this ticket
734
-            // (which should not be blocked at this point b/c we've removed all our relationships)
735
-            $tkt_to_remove->delete_permanently();
736
-        }
737
-        return $saved_tickets;
738
-    }
425
+	/**
426
+	 * update tickets
427
+	 *
428
+	 * @param  EE_Event      $event           Event object being updated
429
+	 * @param  EE_Datetime[] $saved_datetimes an array of datetime ids being updated
430
+	 * @param  array         $data            incoming request data
431
+	 * @return EE_Ticket[]
432
+	 * @throws Exception
433
+	 * @throws ReflectionException
434
+	 * @throws InvalidInterfaceException
435
+	 * @throws InvalidDataTypeException
436
+	 * @throws InvalidArgumentException
437
+	 * @throws EE_Error
438
+	 */
439
+	protected function _update_tickets($event, $saved_datetimes, $data)
440
+	{
441
+		$new_tkt     = null;
442
+		$new_default = null;
443
+		//stripslashes because WP filtered the $_POST ($data) array to add slashes
444
+		$data          = stripslashes_deep($data);
445
+		$timezone      = isset($data['timezone_string']) ? $data['timezone_string'] : null;
446
+		$saved_tickets = $datetimes_on_existing = array();
447
+		$old_tickets   = isset($data['ticket_IDs']) ? explode(',', $data['ticket_IDs']) : array();
448
+		if (empty($data['edit_tickets']) || ! is_array($data['edit_tickets'])) {
449
+			throw new InvalidArgumentException(
450
+				esc_html__(
451
+					'The "edit_tickets" array is invalid therefore the event can not be updated.',
452
+					'event_espresso'
453
+				)
454
+			);
455
+		}
456
+		foreach ($data['edit_tickets'] as $row => $tkt) {
457
+			$update_prices = $create_new_TKT = false;
458
+			// figure out what datetimes were added to the ticket
459
+			// and what datetimes were removed from the ticket in the session.
460
+			$starting_tkt_dtt_rows = explode(',', $data['starting_ticket_datetime_rows'][ $row ]);
461
+			$tkt_dtt_rows          = explode(',', $data['ticket_datetime_rows'][ $row ]);
462
+			$datetimes_added       = array_diff($tkt_dtt_rows, $starting_tkt_dtt_rows);
463
+			$datetimes_removed     = array_diff($starting_tkt_dtt_rows, $tkt_dtt_rows);
464
+			// trim inputs to ensure any excess whitespace is removed.
465
+			$tkt = array_map(
466
+				function ($ticket_data)
467
+				{
468
+					return is_array($ticket_data) ? $ticket_data : trim($ticket_data);
469
+				},
470
+				$tkt
471
+			);
472
+			// note we are doing conversions to floats here instead of allowing EE_Money_Field to handle
473
+			// because we're doing calculations prior to using the models.
474
+			// note incoming ['TKT_price'] value is already in standard notation (via js).
475
+			$ticket_price = isset($tkt['TKT_price'])
476
+				? round((float) $tkt['TKT_price'], 3)
477
+				: 0;
478
+			//note incoming base price needs converted from localized value.
479
+			$base_price = isset($tkt['TKT_base_price'])
480
+				? EEH_Money::convert_to_float_from_localized_money($tkt['TKT_base_price'])
481
+				: 0;
482
+			//if ticket price == 0 and $base_price != 0 then ticket price == base_price
483
+			$ticket_price  = $ticket_price === 0 && $base_price !== 0
484
+				? $base_price
485
+				: $ticket_price;
486
+			$base_price_id = isset($tkt['TKT_base_price_ID'])
487
+				? $tkt['TKT_base_price_ID']
488
+				: 0;
489
+			$price_rows    = is_array($data['edit_prices']) && isset($data['edit_prices'][ $row ])
490
+				? $data['edit_prices'][ $row ]
491
+				: array();
492
+			$now           = null;
493
+			if (empty($tkt['TKT_start_date'])) {
494
+				//lets' use now in the set timezone.
495
+				$now                   = new DateTime('now', new DateTimeZone($event->get_timezone()));
496
+				$tkt['TKT_start_date'] = $now->format($this->_date_time_format);
497
+			}
498
+			if (empty($tkt['TKT_end_date'])) {
499
+				/**
500
+				 * set the TKT_end_date to the first datetime attached to the ticket.
501
+				 */
502
+				$first_dtt           = $saved_datetimes[ reset($tkt_dtt_rows) ];
503
+				$tkt['TKT_end_date'] = $first_dtt->start_date_and_time($this->_date_time_format);
504
+			}
505
+			$TKT_values = array(
506
+				'TKT_ID'          => ! empty($tkt['TKT_ID']) ? $tkt['TKT_ID'] : null,
507
+				'TTM_ID'          => ! empty($tkt['TTM_ID']) ? $tkt['TTM_ID'] : 0,
508
+				'TKT_name'        => ! empty($tkt['TKT_name']) ? $tkt['TKT_name'] : '',
509
+				'TKT_description' => ! empty($tkt['TKT_description'])
510
+									 && $tkt['TKT_description'] !== esc_html__(
511
+					'You can modify this description',
512
+					'event_espresso'
513
+				)
514
+					? $tkt['TKT_description']
515
+					: '',
516
+				'TKT_start_date'  => $tkt['TKT_start_date'],
517
+				'TKT_end_date'    => $tkt['TKT_end_date'],
518
+				'TKT_qty'         => ! isset($tkt['TKT_qty']) || $tkt['TKT_qty'] === ''
519
+					? EE_INF
520
+					: $tkt['TKT_qty'],
521
+				'TKT_uses'        => ! isset($tkt['TKT_uses']) || $tkt['TKT_uses'] === ''
522
+					? EE_INF
523
+					: $tkt['TKT_uses'],
524
+				'TKT_min'         => empty($tkt['TKT_min']) ? 0 : $tkt['TKT_min'],
525
+				'TKT_max'         => empty($tkt['TKT_max']) ? EE_INF : $tkt['TKT_max'],
526
+				'TKT_row'         => $row,
527
+				'TKT_order'       => isset($tkt['TKT_order']) ? $tkt['TKT_order'] : 0,
528
+				'TKT_taxable'     => ! empty($tkt['TKT_taxable']) ? 1 : 0,
529
+				'TKT_required'    => ! empty($tkt['TKT_required']) ? 1 : 0,
530
+				'TKT_price'       => $ticket_price,
531
+			);
532
+			// if this is a default TKT, then we need to set the TKT_ID to 0 and update accordingly,
533
+			// which means in turn that the prices will become new prices as well.
534
+			if (isset($tkt['TKT_is_default']) && $tkt['TKT_is_default']) {
535
+				$TKT_values['TKT_ID']         = 0;
536
+				$TKT_values['TKT_is_default'] = 0;
537
+				$update_prices                = true;
538
+			}
539
+			// if we have a TKT_ID then we need to get that existing TKT_obj and update it
540
+			// we actually do our saves ahead of doing any add_relations to
541
+			// because its entirely possible that this ticket wasn't removed or added to any datetime in the session
542
+			// but DID have it's items modified.
543
+			// keep in mind that if the TKT has been sold (and we have changed pricing information),
544
+			// then we won't be updating the tkt but instead a new tkt will be created and the old one archived.
545
+			if (absint($TKT_values['TKT_ID'])) {
546
+				$ticket = EE_Registry::instance()
547
+									 ->load_model('Ticket', array($timezone))
548
+									 ->get_one_by_ID($tkt['TKT_ID']);
549
+				if ($ticket instanceof EE_Ticket) {
550
+					$ticket = $this->_update_ticket_datetimes(
551
+						$ticket,
552
+						$saved_datetimes,
553
+						$datetimes_added,
554
+						$datetimes_removed
555
+					);
556
+					// are there any registrations using this ticket ?
557
+					$tickets_sold = $ticket->count_related(
558
+						'Registration',
559
+						array(
560
+							array(
561
+								'STS_ID' => array('NOT IN', array(EEM_Registration::status_id_incomplete)),
562
+							),
563
+						)
564
+					);
565
+					//set ticket formats
566
+					$ticket->set_date_format($this->_date_format_strings['date']);
567
+					$ticket->set_time_format($this->_date_format_strings['time']);
568
+					// let's just check the total price for the existing ticket
569
+					// and determine if it matches the new total price.
570
+					// if they are different then we create a new ticket (if tickets sold)
571
+					// if they aren't different then we go ahead and modify existing ticket.
572
+					$create_new_TKT = $tickets_sold > 0 && $ticket_price !== $ticket->price() && ! $ticket->deleted();
573
+					//set new values
574
+					foreach ($TKT_values as $field => $value) {
575
+						if ($field === 'TKT_qty') {
576
+							$ticket->set_qty($value);
577
+						} else {
578
+							$ticket->set($field, $value);
579
+						}
580
+					}
581
+					// if $create_new_TKT is false then we can safely update the existing ticket.
582
+					// Otherwise we have to create a new ticket.
583
+					if ($create_new_TKT) {
584
+						$new_tkt = $this->_duplicate_ticket(
585
+							$ticket,
586
+							$price_rows,
587
+							$ticket_price,
588
+							$base_price,
589
+							$base_price_id
590
+						);
591
+					}
592
+				}
593
+			} else {
594
+				// no TKT_id so a new TKT
595
+				$ticket = EE_Ticket::new_instance(
596
+					$TKT_values,
597
+					$timezone,
598
+					array($this->_date_format_strings['date'], $this->_date_format_strings['time'])
599
+				);
600
+				if ($ticket instanceof EE_Ticket) {
601
+					// make sure ticket has an ID of setting relations won't work
602
+					$ticket->save();
603
+					$ticket        = $this->_update_ticket_datetimes(
604
+						$ticket,
605
+						$saved_datetimes,
606
+						$datetimes_added,
607
+						$datetimes_removed
608
+					);
609
+					$update_prices = true;
610
+				}
611
+			}
612
+			//make sure any current values have been saved.
613
+			//$ticket->save();
614
+			// before going any further make sure our dates are setup correctly
615
+			// so that the end date is always equal or greater than the start date.
616
+			if ($ticket->get_raw('TKT_start_date') > $ticket->get_raw('TKT_end_date')) {
617
+				$ticket->set('TKT_end_date', $ticket->get('TKT_start_date'));
618
+				$ticket = EEH_DTT_Helper::date_time_add($ticket, 'TKT_end_date', 'days');
619
+			}
620
+			//let's make sure the base price is handled
621
+			$ticket = ! $create_new_TKT
622
+				? $this->_add_prices_to_ticket(
623
+					array(),
624
+					$ticket,
625
+					$update_prices,
626
+					$base_price,
627
+					$base_price_id
628
+				)
629
+				: $ticket;
630
+			//add/update price_modifiers
631
+			$ticket = ! $create_new_TKT
632
+				? $this->_add_prices_to_ticket($price_rows, $ticket, $update_prices)
633
+				: $ticket;
634
+			//need to make sue that the TKT_price is accurate after saving the prices.
635
+			$ticket->ensure_TKT_Price_correct();
636
+			//handle CREATING a default tkt from the incoming tkt but ONLY if this isn't an autosave.
637
+			if (! defined('DOING_AUTOSAVE') && ! empty($tkt['TKT_is_default_selector'])) {
638
+				$update_prices = true;
639
+				$new_default   = clone $ticket;
640
+				$new_default->set('TKT_ID', 0);
641
+				$new_default->set('TKT_is_default', 1);
642
+				$new_default->set('TKT_row', 1);
643
+				$new_default->set('TKT_price', $ticket_price);
644
+				// remove any dtt relations cause we DON'T want dtt relations attached
645
+				// (note this is just removing the cached relations in the object)
646
+				$new_default->_remove_relations('Datetime');
647
+				//todo we need to add the current attached prices as new prices to the new default ticket.
648
+				$new_default = $this->_add_prices_to_ticket(
649
+					$price_rows,
650
+					$new_default,
651
+					$update_prices
652
+				);
653
+				//don't forget the base price!
654
+				$new_default = $this->_add_prices_to_ticket(
655
+					array(),
656
+					$new_default,
657
+					$update_prices,
658
+					$base_price,
659
+					$base_price_id
660
+				);
661
+				$new_default->save();
662
+				do_action(
663
+					'AHEE__espresso_events_Pricing_Hooks___update_tkts_new_default_ticket',
664
+					$new_default,
665
+					$row,
666
+					$ticket,
667
+					$data
668
+				);
669
+			}
670
+			// DO ALL dtt relationships for both current tickets and any archived tickets
671
+			// for the given dtt that are related to the current ticket.
672
+			// TODO... not sure exactly how we're going to do this considering we don't know
673
+			// what current ticket the archived tickets are related to
674
+			// (and TKT_parent is used for autosaves so that's not a field we can reliably use).
675
+			//let's assign any tickets that have been setup to the saved_tickets tracker
676
+			//save existing TKT
677
+			$ticket->save();
678
+			if ($create_new_TKT && $new_tkt instanceof EE_Ticket) {
679
+				//save new TKT
680
+				$new_tkt->save();
681
+				//add new ticket to array
682
+				$saved_tickets[ $new_tkt->ID() ] = $new_tkt;
683
+				do_action(
684
+					'AHEE__espresso_events_Pricing_Hooks___update_tkts_new_ticket',
685
+					$new_tkt,
686
+					$row,
687
+					$tkt,
688
+					$data
689
+				);
690
+			} else {
691
+				//add tkt to saved tkts
692
+				$saved_tickets[ $ticket->ID() ] = $ticket;
693
+				do_action(
694
+					'AHEE__espresso_events_Pricing_Hooks___update_tkts_update_ticket',
695
+					$ticket,
696
+					$row,
697
+					$tkt,
698
+					$data
699
+				);
700
+			}
701
+		}
702
+		// now we need to handle tickets actually "deleted permanently".
703
+		// There are cases where we'd want this to happen
704
+		// (i.e. autosaves are happening and then in between autosaves the user trashes a ticket).
705
+		// Or a draft event was saved and in the process of editing a ticket is trashed.
706
+		// No sense in keeping all the related data in the db!
707
+		$old_tickets     = isset($old_tickets[0]) && $old_tickets[0] === '' ? array() : $old_tickets;
708
+		$tickets_removed = array_diff($old_tickets, array_keys($saved_tickets));
709
+		foreach ($tickets_removed as $id) {
710
+			$id = absint($id);
711
+			//get the ticket for this id
712
+			$tkt_to_remove = EE_Registry::instance()->load_model('Ticket')->get_one_by_ID($id);
713
+			//if this tkt is a default tkt we leave it alone cause it won't be attached to the datetime
714
+			if ($tkt_to_remove->get('TKT_is_default')) {
715
+				continue;
716
+			}
717
+			// if this tkt has any registrations attached so then we just ARCHIVE
718
+			// because we don't actually permanently delete these tickets.
719
+			if ($tkt_to_remove->count_related('Registration') > 0) {
720
+				$tkt_to_remove->delete();
721
+				continue;
722
+			}
723
+			// need to get all the related datetimes on this ticket and remove from every single one of them
724
+			// (remember this process can ONLY kick off if there are NO tkts_sold)
725
+			$datetimes = $tkt_to_remove->get_many_related('Datetime');
726
+			foreach ($datetimes as $datetime) {
727
+				$tkt_to_remove->_remove_relation_to($datetime, 'Datetime');
728
+			}
729
+			// need to do the same for prices (except these prices can also be deleted because again,
730
+			// tickets can only be trashed if they don't have any TKTs sold (otherwise they are just archived))
731
+			$tkt_to_remove->delete_related_permanently('Price');
732
+			do_action('AHEE__espresso_events_Pricing_Hooks___update_tkts_delete_ticket', $tkt_to_remove);
733
+			// finally let's delete this ticket
734
+			// (which should not be blocked at this point b/c we've removed all our relationships)
735
+			$tkt_to_remove->delete_permanently();
736
+		}
737
+		return $saved_tickets;
738
+	}
739 739
 
740 740
 
741
-    /**
742
-     * @access  protected
743
-     * @param EE_Ticket      $ticket
744
-     * @param \EE_Datetime[] $saved_datetimes
745
-     * @param \EE_Datetime[] $added_datetimes
746
-     * @param \EE_Datetime[] $removed_datetimes
747
-     * @return EE_Ticket
748
-     * @throws EE_Error
749
-     */
750
-    protected function _update_ticket_datetimes(
751
-        EE_Ticket $ticket,
752
-        $saved_datetimes = array(),
753
-        $added_datetimes = array(),
754
-        $removed_datetimes = array()
755
-    ) {
756
-        // to start we have to add the ticket to all the datetimes its supposed to be with,
757
-        // and removing the ticket from datetimes it got removed from.
758
-        // first let's add datetimes
759
-        if (! empty($added_datetimes) && is_array($added_datetimes)) {
760
-            foreach ($added_datetimes as $row_id) {
761
-                $row_id = (int) $row_id;
762
-                if (isset($saved_datetimes[ $row_id ]) && $saved_datetimes[ $row_id ] instanceof EE_Datetime) {
763
-                    $ticket->_add_relation_to($saved_datetimes[ $row_id ], 'Datetime');
764
-                    // Is this an existing ticket (has an ID) and does it have any sold?
765
-                    // If so, then we need to add that to the DTT sold because this DTT is getting added.
766
-                    if ($ticket->ID() && $ticket->sold() > 0) {
767
-                        $saved_datetimes[ $row_id ]->increase_sold($ticket->sold());
768
-                        $saved_datetimes[ $row_id ]->save();
769
-                    }
770
-                }
771
-            }
772
-        }
773
-        // then remove datetimes
774
-        if (! empty($removed_datetimes) && is_array($removed_datetimes)) {
775
-            foreach ($removed_datetimes as $row_id) {
776
-                $row_id = (int) $row_id;
777
-                // its entirely possible that a datetime got deleted (instead of just removed from relationship.
778
-                // So make sure we skip over this if the dtt isn't in the $saved_datetimes array)
779
-                if (isset($saved_datetimes[ $row_id ]) && $saved_datetimes[ $row_id ] instanceof EE_Datetime) {
780
-                    $ticket->_remove_relation_to($saved_datetimes[ $row_id ], 'Datetime');
781
-                    // Is this an existing ticket (has an ID) and does it have any sold?
782
-                    // If so, then we need to remove it's sold from the DTT_sold.
783
-                    if ($ticket->ID() && $ticket->sold() > 0) {
784
-                        $saved_datetimes[ $row_id ]->decrease_sold($ticket->sold());
785
-                        $saved_datetimes[ $row_id ]->save();
786
-                    }
787
-                }
788
-            }
789
-        }
790
-        // cap ticket qty by datetime reg limits
791
-        $ticket->set_qty(min($ticket->qty(), $ticket->qty('reg_limit')));
792
-        return $ticket;
793
-    }
741
+	/**
742
+	 * @access  protected
743
+	 * @param EE_Ticket      $ticket
744
+	 * @param \EE_Datetime[] $saved_datetimes
745
+	 * @param \EE_Datetime[] $added_datetimes
746
+	 * @param \EE_Datetime[] $removed_datetimes
747
+	 * @return EE_Ticket
748
+	 * @throws EE_Error
749
+	 */
750
+	protected function _update_ticket_datetimes(
751
+		EE_Ticket $ticket,
752
+		$saved_datetimes = array(),
753
+		$added_datetimes = array(),
754
+		$removed_datetimes = array()
755
+	) {
756
+		// to start we have to add the ticket to all the datetimes its supposed to be with,
757
+		// and removing the ticket from datetimes it got removed from.
758
+		// first let's add datetimes
759
+		if (! empty($added_datetimes) && is_array($added_datetimes)) {
760
+			foreach ($added_datetimes as $row_id) {
761
+				$row_id = (int) $row_id;
762
+				if (isset($saved_datetimes[ $row_id ]) && $saved_datetimes[ $row_id ] instanceof EE_Datetime) {
763
+					$ticket->_add_relation_to($saved_datetimes[ $row_id ], 'Datetime');
764
+					// Is this an existing ticket (has an ID) and does it have any sold?
765
+					// If so, then we need to add that to the DTT sold because this DTT is getting added.
766
+					if ($ticket->ID() && $ticket->sold() > 0) {
767
+						$saved_datetimes[ $row_id ]->increase_sold($ticket->sold());
768
+						$saved_datetimes[ $row_id ]->save();
769
+					}
770
+				}
771
+			}
772
+		}
773
+		// then remove datetimes
774
+		if (! empty($removed_datetimes) && is_array($removed_datetimes)) {
775
+			foreach ($removed_datetimes as $row_id) {
776
+				$row_id = (int) $row_id;
777
+				// its entirely possible that a datetime got deleted (instead of just removed from relationship.
778
+				// So make sure we skip over this if the dtt isn't in the $saved_datetimes array)
779
+				if (isset($saved_datetimes[ $row_id ]) && $saved_datetimes[ $row_id ] instanceof EE_Datetime) {
780
+					$ticket->_remove_relation_to($saved_datetimes[ $row_id ], 'Datetime');
781
+					// Is this an existing ticket (has an ID) and does it have any sold?
782
+					// If so, then we need to remove it's sold from the DTT_sold.
783
+					if ($ticket->ID() && $ticket->sold() > 0) {
784
+						$saved_datetimes[ $row_id ]->decrease_sold($ticket->sold());
785
+						$saved_datetimes[ $row_id ]->save();
786
+					}
787
+				}
788
+			}
789
+		}
790
+		// cap ticket qty by datetime reg limits
791
+		$ticket->set_qty(min($ticket->qty(), $ticket->qty('reg_limit')));
792
+		return $ticket;
793
+	}
794 794
 
795 795
 
796
-    /**
797
-     * @access  protected
798
-     * @param EE_Ticket $ticket
799
-     * @param array     $price_rows
800
-     * @param int       $ticket_price
801
-     * @param int       $base_price
802
-     * @param int       $base_price_id
803
-     * @return EE_Ticket
804
-     * @throws ReflectionException
805
-     * @throws InvalidArgumentException
806
-     * @throws InvalidInterfaceException
807
-     * @throws InvalidDataTypeException
808
-     * @throws EE_Error
809
-     */
810
-    protected function _duplicate_ticket(
811
-        EE_Ticket $ticket,
812
-        $price_rows = array(),
813
-        $ticket_price = 0,
814
-        $base_price = 0,
815
-        $base_price_id = 0
816
-    ) {
817
-        // create new ticket that's a copy of the existing
818
-        // except a new id of course (and not archived)
819
-        // AND has the new TKT_price associated with it.
820
-        $new_ticket = clone $ticket;
821
-        $new_ticket->set('TKT_ID', 0);
822
-        $new_ticket->set_deleted(0);
823
-        $new_ticket->set_price($ticket_price);
824
-        $new_ticket->set_sold(0);
825
-        // let's get a new ID for this ticket
826
-        $new_ticket->save();
827
-        // we also need to make sure this new ticket gets the same datetime attachments as the archived ticket
828
-        $datetimes_on_existing = $ticket->datetimes();
829
-        $new_ticket            = $this->_update_ticket_datetimes(
830
-            $new_ticket,
831
-            $datetimes_on_existing,
832
-            array_keys($datetimes_on_existing)
833
-        );
834
-        // $ticket will get archived later b/c we are NOT adding it to the saved_tickets array.
835
-        // if existing $ticket has sold amount, then we need to adjust the qty for the new TKT to = the remaining
836
-        // available.
837
-        if ($ticket->sold() > 0) {
838
-            $new_qty = $ticket->qty() - $ticket->sold();
839
-            $new_ticket->set_qty($new_qty);
840
-        }
841
-        //now we update the prices just for this ticket
842
-        $new_ticket = $this->_add_prices_to_ticket($price_rows, $new_ticket, true);
843
-        //and we update the base price
844
-        $new_ticket = $this->_add_prices_to_ticket(
845
-            array(),
846
-            $new_ticket,
847
-            true,
848
-            $base_price,
849
-            $base_price_id
850
-        );
851
-        return $new_ticket;
852
-    }
796
+	/**
797
+	 * @access  protected
798
+	 * @param EE_Ticket $ticket
799
+	 * @param array     $price_rows
800
+	 * @param int       $ticket_price
801
+	 * @param int       $base_price
802
+	 * @param int       $base_price_id
803
+	 * @return EE_Ticket
804
+	 * @throws ReflectionException
805
+	 * @throws InvalidArgumentException
806
+	 * @throws InvalidInterfaceException
807
+	 * @throws InvalidDataTypeException
808
+	 * @throws EE_Error
809
+	 */
810
+	protected function _duplicate_ticket(
811
+		EE_Ticket $ticket,
812
+		$price_rows = array(),
813
+		$ticket_price = 0,
814
+		$base_price = 0,
815
+		$base_price_id = 0
816
+	) {
817
+		// create new ticket that's a copy of the existing
818
+		// except a new id of course (and not archived)
819
+		// AND has the new TKT_price associated with it.
820
+		$new_ticket = clone $ticket;
821
+		$new_ticket->set('TKT_ID', 0);
822
+		$new_ticket->set_deleted(0);
823
+		$new_ticket->set_price($ticket_price);
824
+		$new_ticket->set_sold(0);
825
+		// let's get a new ID for this ticket
826
+		$new_ticket->save();
827
+		// we also need to make sure this new ticket gets the same datetime attachments as the archived ticket
828
+		$datetimes_on_existing = $ticket->datetimes();
829
+		$new_ticket            = $this->_update_ticket_datetimes(
830
+			$new_ticket,
831
+			$datetimes_on_existing,
832
+			array_keys($datetimes_on_existing)
833
+		);
834
+		// $ticket will get archived later b/c we are NOT adding it to the saved_tickets array.
835
+		// if existing $ticket has sold amount, then we need to adjust the qty for the new TKT to = the remaining
836
+		// available.
837
+		if ($ticket->sold() > 0) {
838
+			$new_qty = $ticket->qty() - $ticket->sold();
839
+			$new_ticket->set_qty($new_qty);
840
+		}
841
+		//now we update the prices just for this ticket
842
+		$new_ticket = $this->_add_prices_to_ticket($price_rows, $new_ticket, true);
843
+		//and we update the base price
844
+		$new_ticket = $this->_add_prices_to_ticket(
845
+			array(),
846
+			$new_ticket,
847
+			true,
848
+			$base_price,
849
+			$base_price_id
850
+		);
851
+		return $new_ticket;
852
+	}
853 853
 
854 854
 
855
-    /**
856
-     * This attaches a list of given prices to a ticket.
857
-     * Note we dont' have to worry about ever removing relationships (or archiving prices) because if there is a change
858
-     * in price information on a ticket, a new ticket is created anyways so the archived ticket will retain the old
859
-     * price info and prices are automatically "archived" via the ticket.
860
-     *
861
-     * @access  private
862
-     * @param array     $prices        Array of prices from the form.
863
-     * @param EE_Ticket $ticket        EE_Ticket object that prices are being attached to.
864
-     * @param bool      $new_prices    Whether attach existing incoming prices or create new ones.
865
-     * @param int|bool  $base_price    if FALSE then NOT doing a base price add.
866
-     * @param int|bool  $base_price_id if present then this is the base_price_id being updated.
867
-     * @return EE_Ticket
868
-     * @throws ReflectionException
869
-     * @throws InvalidArgumentException
870
-     * @throws InvalidInterfaceException
871
-     * @throws InvalidDataTypeException
872
-     * @throws EE_Error
873
-     */
874
-    protected function _add_prices_to_ticket(
875
-        $prices = array(),
876
-        EE_Ticket $ticket,
877
-        $new_prices = false,
878
-        $base_price = false,
879
-        $base_price_id = false
880
-    ) {
881
-        // let's just get any current prices that may exist on the given ticket
882
-        // so we can remove any prices that got trashed in this session.
883
-        $current_prices_on_ticket = $base_price !== false
884
-            ? $ticket->base_price(true)
885
-            : $ticket->price_modifiers();
886
-        $updated_prices           = array();
887
-        // if $base_price ! FALSE then updating a base price.
888
-        if ($base_price !== false) {
889
-            $prices[1] = array(
890
-                'PRC_ID'     => $new_prices || $base_price_id === 1 ? null : $base_price_id,
891
-                'PRT_ID'     => 1,
892
-                'PRC_amount' => $base_price,
893
-                'PRC_name'   => $ticket->get('TKT_name'),
894
-                'PRC_desc'   => $ticket->get('TKT_description'),
895
-            );
896
-        }
897
-        //possibly need to save tkt
898
-        if (! $ticket->ID()) {
899
-            $ticket->save();
900
-        }
901
-        foreach ($prices as $row => $prc) {
902
-            $prt_id = ! empty($prc['PRT_ID']) ? $prc['PRT_ID'] : null;
903
-            if (empty($prt_id)) {
904
-                continue;
905
-            } //prices MUST have a price type id.
906
-            $PRC_values = array(
907
-                'PRC_ID'         => ! empty($prc['PRC_ID']) ? $prc['PRC_ID'] : null,
908
-                'PRT_ID'         => $prt_id,
909
-                'PRC_amount'     => ! empty($prc['PRC_amount']) ? $prc['PRC_amount'] : 0,
910
-                'PRC_name'       => ! empty($prc['PRC_name']) ? $prc['PRC_name'] : '',
911
-                'PRC_desc'       => ! empty($prc['PRC_desc']) ? $prc['PRC_desc'] : '',
912
-                'PRC_is_default' => false,
913
-                //make sure we set PRC_is_default to false for all ticket saves from event_editor
914
-                'PRC_order'      => $row,
915
-            );
916
-            if ($new_prices || empty($PRC_values['PRC_ID'])) {
917
-                $PRC_values['PRC_ID'] = 0;
918
-                $price                = EE_Registry::instance()->load_class(
919
-                    'Price',
920
-                    array($PRC_values),
921
-                    false,
922
-                    false
923
-                );
924
-            } else {
925
-                $price = EE_Registry::instance()->load_model('Price')->get_one_by_ID($prc['PRC_ID']);
926
-                //update this price with new values
927
-                foreach ($PRC_values as $field => $value) {
928
-                    $price->set($field, $value);
929
-                }
930
-            }
931
-            $price->save();
932
-            $updated_prices[ $price->ID() ] = $price;
933
-            $ticket->_add_relation_to($price, 'Price');
934
-        }
935
-        //now let's remove any prices that got removed from the ticket
936
-        if (! empty ($current_prices_on_ticket)) {
937
-            $current          = array_keys($current_prices_on_ticket);
938
-            $updated          = array_keys($updated_prices);
939
-            $prices_to_remove = array_diff($current, $updated);
940
-            if (! empty($prices_to_remove)) {
941
-                foreach ($prices_to_remove as $prc_id) {
942
-                    $p = $current_prices_on_ticket[ $prc_id ];
943
-                    $ticket->_remove_relation_to($p, 'Price');
944
-                    //delete permanently the price
945
-                    $p->delete_permanently();
946
-                }
947
-            }
948
-        }
949
-        return $ticket;
950
-    }
855
+	/**
856
+	 * This attaches a list of given prices to a ticket.
857
+	 * Note we dont' have to worry about ever removing relationships (or archiving prices) because if there is a change
858
+	 * in price information on a ticket, a new ticket is created anyways so the archived ticket will retain the old
859
+	 * price info and prices are automatically "archived" via the ticket.
860
+	 *
861
+	 * @access  private
862
+	 * @param array     $prices        Array of prices from the form.
863
+	 * @param EE_Ticket $ticket        EE_Ticket object that prices are being attached to.
864
+	 * @param bool      $new_prices    Whether attach existing incoming prices or create new ones.
865
+	 * @param int|bool  $base_price    if FALSE then NOT doing a base price add.
866
+	 * @param int|bool  $base_price_id if present then this is the base_price_id being updated.
867
+	 * @return EE_Ticket
868
+	 * @throws ReflectionException
869
+	 * @throws InvalidArgumentException
870
+	 * @throws InvalidInterfaceException
871
+	 * @throws InvalidDataTypeException
872
+	 * @throws EE_Error
873
+	 */
874
+	protected function _add_prices_to_ticket(
875
+		$prices = array(),
876
+		EE_Ticket $ticket,
877
+		$new_prices = false,
878
+		$base_price = false,
879
+		$base_price_id = false
880
+	) {
881
+		// let's just get any current prices that may exist on the given ticket
882
+		// so we can remove any prices that got trashed in this session.
883
+		$current_prices_on_ticket = $base_price !== false
884
+			? $ticket->base_price(true)
885
+			: $ticket->price_modifiers();
886
+		$updated_prices           = array();
887
+		// if $base_price ! FALSE then updating a base price.
888
+		if ($base_price !== false) {
889
+			$prices[1] = array(
890
+				'PRC_ID'     => $new_prices || $base_price_id === 1 ? null : $base_price_id,
891
+				'PRT_ID'     => 1,
892
+				'PRC_amount' => $base_price,
893
+				'PRC_name'   => $ticket->get('TKT_name'),
894
+				'PRC_desc'   => $ticket->get('TKT_description'),
895
+			);
896
+		}
897
+		//possibly need to save tkt
898
+		if (! $ticket->ID()) {
899
+			$ticket->save();
900
+		}
901
+		foreach ($prices as $row => $prc) {
902
+			$prt_id = ! empty($prc['PRT_ID']) ? $prc['PRT_ID'] : null;
903
+			if (empty($prt_id)) {
904
+				continue;
905
+			} //prices MUST have a price type id.
906
+			$PRC_values = array(
907
+				'PRC_ID'         => ! empty($prc['PRC_ID']) ? $prc['PRC_ID'] : null,
908
+				'PRT_ID'         => $prt_id,
909
+				'PRC_amount'     => ! empty($prc['PRC_amount']) ? $prc['PRC_amount'] : 0,
910
+				'PRC_name'       => ! empty($prc['PRC_name']) ? $prc['PRC_name'] : '',
911
+				'PRC_desc'       => ! empty($prc['PRC_desc']) ? $prc['PRC_desc'] : '',
912
+				'PRC_is_default' => false,
913
+				//make sure we set PRC_is_default to false for all ticket saves from event_editor
914
+				'PRC_order'      => $row,
915
+			);
916
+			if ($new_prices || empty($PRC_values['PRC_ID'])) {
917
+				$PRC_values['PRC_ID'] = 0;
918
+				$price                = EE_Registry::instance()->load_class(
919
+					'Price',
920
+					array($PRC_values),
921
+					false,
922
+					false
923
+				);
924
+			} else {
925
+				$price = EE_Registry::instance()->load_model('Price')->get_one_by_ID($prc['PRC_ID']);
926
+				//update this price with new values
927
+				foreach ($PRC_values as $field => $value) {
928
+					$price->set($field, $value);
929
+				}
930
+			}
931
+			$price->save();
932
+			$updated_prices[ $price->ID() ] = $price;
933
+			$ticket->_add_relation_to($price, 'Price');
934
+		}
935
+		//now let's remove any prices that got removed from the ticket
936
+		if (! empty ($current_prices_on_ticket)) {
937
+			$current          = array_keys($current_prices_on_ticket);
938
+			$updated          = array_keys($updated_prices);
939
+			$prices_to_remove = array_diff($current, $updated);
940
+			if (! empty($prices_to_remove)) {
941
+				foreach ($prices_to_remove as $prc_id) {
942
+					$p = $current_prices_on_ticket[ $prc_id ];
943
+					$ticket->_remove_relation_to($p, 'Price');
944
+					//delete permanently the price
945
+					$p->delete_permanently();
946
+				}
947
+			}
948
+		}
949
+		return $ticket;
950
+	}
951 951
 
952 952
 
953
-    /**
954
-     * @param Events_Admin_Page $event_admin_obj
955
-     * @return Events_Admin_Page
956
-     */
957
-    public function autosave_handling(Events_Admin_Page $event_admin_obj)
958
-    {
959
-        return $event_admin_obj;
960
-        //doing nothing for the moment.
961
-        // todo when I get to this remember that I need to set the template args on the $event_admin_obj
962
-        // (use the set_template_args() method)
963
-        /**
964
-         * need to remember to handle TICKET DEFAULT saves correctly:  I've got two input fields in the dom:
965
-         * 1. TKT_is_default_selector (visible)
966
-         * 2. TKT_is_default (hidden)
967
-         * I think we'll use the TKT_is_default for recording whether the ticket displayed IS a default ticket
968
-         * (on new event creations). Whereas the TKT_is_default_selector is for the user to indicate they want
969
-         * this ticket to be saved as a default.
970
-         * The tricky part is, on an initial display on create or edit (or after manually updating),
971
-         * the TKT_is_default_selector will always be unselected and the TKT_is_default will only be true
972
-         * if this is a create.  However, after an autosave, users will want some sort of indicator that
973
-         * the TKT HAS been saved as a default..
974
-         * in other words we don't want to remove the check on TKT_is_default_selector. So here's what I'm thinking.
975
-         * On Autosave:
976
-         * 1. If TKT_is_default is true: we create a new TKT, send back the new id and add id to related elements,
977
-         * then set the TKT_is_default to false.
978
-         * 2. If TKT_is_default_selector is true: we create/edit existing ticket (following conditions above as well).
979
-         *  We do NOT create a new default ticket.  The checkbox stays selected after autosave.
980
-         * 3. only on MANUAL update do we check for the selection and if selected create the new default ticket.
981
-         */
982
-    }
953
+	/**
954
+	 * @param Events_Admin_Page $event_admin_obj
955
+	 * @return Events_Admin_Page
956
+	 */
957
+	public function autosave_handling(Events_Admin_Page $event_admin_obj)
958
+	{
959
+		return $event_admin_obj;
960
+		//doing nothing for the moment.
961
+		// todo when I get to this remember that I need to set the template args on the $event_admin_obj
962
+		// (use the set_template_args() method)
963
+		/**
964
+		 * need to remember to handle TICKET DEFAULT saves correctly:  I've got two input fields in the dom:
965
+		 * 1. TKT_is_default_selector (visible)
966
+		 * 2. TKT_is_default (hidden)
967
+		 * I think we'll use the TKT_is_default for recording whether the ticket displayed IS a default ticket
968
+		 * (on new event creations). Whereas the TKT_is_default_selector is for the user to indicate they want
969
+		 * this ticket to be saved as a default.
970
+		 * The tricky part is, on an initial display on create or edit (or after manually updating),
971
+		 * the TKT_is_default_selector will always be unselected and the TKT_is_default will only be true
972
+		 * if this is a create.  However, after an autosave, users will want some sort of indicator that
973
+		 * the TKT HAS been saved as a default..
974
+		 * in other words we don't want to remove the check on TKT_is_default_selector. So here's what I'm thinking.
975
+		 * On Autosave:
976
+		 * 1. If TKT_is_default is true: we create a new TKT, send back the new id and add id to related elements,
977
+		 * then set the TKT_is_default to false.
978
+		 * 2. If TKT_is_default_selector is true: we create/edit existing ticket (following conditions above as well).
979
+		 *  We do NOT create a new default ticket.  The checkbox stays selected after autosave.
980
+		 * 3. only on MANUAL update do we check for the selection and if selected create the new default ticket.
981
+		 */
982
+	}
983 983
 
984 984
 
985
-    /**
986
-     * @throws ReflectionException
987
-     * @throws InvalidArgumentException
988
-     * @throws InvalidInterfaceException
989
-     * @throws InvalidDataTypeException
990
-     * @throws DomainException
991
-     * @throws EE_Error
992
-     */
993
-    public function pricing_metabox()
994
-    {
995
-        $existing_datetime_ids = $existing_ticket_ids = $datetime_tickets = $ticket_datetimes = array();
996
-        $event                 = $this->_adminpage_obj->get_cpt_model_obj();
997
-        //set is_creating_event property.
998
-        $EVT_ID                   = $event->ID();
999
-        $this->_is_creating_event = absint($EVT_ID) === 0;
1000
-        //default main template args
1001
-        $main_template_args = array(
1002
-            'event_datetime_help_link' => EEH_Template::get_help_tab_link(
1003
-                'event_editor_event_datetimes_help_tab',
1004
-                $this->_adminpage_obj->page_slug,
1005
-                $this->_adminpage_obj->get_req_action(),
1006
-                false,
1007
-                false
1008
-            ),
1009
-            // todo need to add a filter to the template for the help text
1010
-            // in the Events_Admin_Page core file so we can add further help
1011
-            'existing_datetime_ids'    => '',
1012
-            'total_dtt_rows'           => 1,
1013
-            'add_new_dtt_help_link'    => EEH_Template::get_help_tab_link(
1014
-                'add_new_dtt_info',
1015
-                $this->_adminpage_obj->page_slug,
1016
-                $this->_adminpage_obj->get_req_action(),
1017
-                false,
1018
-                false
1019
-            ),
1020
-            //todo need to add this help info id to the Events_Admin_Page core file so we can access it here.
1021
-            'datetime_rows'            => '',
1022
-            'show_tickets_container'   => '',
1023
-            //$this->_adminpage_obj->get_cpt_model_obj()->ID() > 1 ? ' style="display:none;"' : '',
1024
-            'ticket_rows'              => '',
1025
-            'existing_ticket_ids'      => '',
1026
-            'total_ticket_rows'        => 1,
1027
-            'ticket_js_structure'      => '',
1028
-            'ee_collapsible_status'    => ' ee-collapsible-open'
1029
-            //$this->_adminpage_obj->get_cpt_model_obj()->ID() > 0 ? ' ee-collapsible-closed' : ' ee-collapsible-open'
1030
-        );
1031
-        $timezone           = $event instanceof EE_Event ? $event->timezone_string() : null;
1032
-        do_action('AHEE_log', __FILE__, __FUNCTION__, '');
1033
-        /**
1034
-         * 1. Start with retrieving Datetimes
1035
-         * 2. For each datetime get related tickets
1036
-         * 3. For each ticket get related prices
1037
-         */
1038
-        /** @var EEM_Datetime $datetime_model */
1039
-        $datetime_model                       = EE_Registry::instance()->load_model('Datetime', array($timezone));
1040
-        $datetimes                            = $datetime_model->get_all_event_dates($EVT_ID);
1041
-        $main_template_args['total_dtt_rows'] = count($datetimes);
1042
-        /**
1043
-         * @see https://events.codebasehq.com/projects/event-espresso/tickets/9486
1044
-         * for why we are counting $datetime_row and then setting that on the Datetime object
1045
-         */
1046
-        $datetime_row = 1;
1047
-        foreach ($datetimes as $datetime) {
1048
-            $DTT_ID = $datetime->get('DTT_ID');
1049
-            $datetime->set('DTT_order', $datetime_row);
1050
-            $existing_datetime_ids[] = $DTT_ID;
1051
-            //tickets attached
1052
-            $related_tickets = $datetime->ID() > 0
1053
-                ? $datetime->get_many_related(
1054
-                    'Ticket',
1055
-                    array(
1056
-                        array(
1057
-                            'OR' => array('TKT_deleted' => 1, 'TKT_deleted*' => 0),
1058
-                        ),
1059
-                        'default_where_conditions' => 'none',
1060
-                        'order_by'                 => array('TKT_order' => 'ASC'),
1061
-                    )
1062
-                )
1063
-                : array();
1064
-            //if there are no related tickets this is likely a new event OR autodraft
1065
-            // event so we need to generate the default tickets because datetimes
1066
-            // ALWAYS have at least one related ticket!!.  EXCEPT, we dont' do this if there is already more than one
1067
-            // datetime on the event.
1068
-            if (empty ($related_tickets) && count($datetimes) < 2) {
1069
-                /** @var EEM_Ticket $ticket_model */
1070
-                $ticket_model    = EE_Registry::instance()->load_model('Ticket');
1071
-                $related_tickets = $ticket_model->get_all_default_tickets();
1072
-                // this should be ordered by TKT_ID, so let's grab the first default ticket
1073
-                // (which will be the main default) and ensure it has any default prices added to it (but do NOT save).
1074
-                $default_prices      = EEM_Price::instance()->get_all_default_prices();
1075
-                $main_default_ticket = reset($related_tickets);
1076
-                if ($main_default_ticket instanceof EE_Ticket) {
1077
-                    foreach ($default_prices as $default_price) {
1078
-                        if ($default_price instanceof EE_Price && $default_price->is_base_price()) {
1079
-                            continue;
1080
-                        }
1081
-                        $main_default_ticket->cache('Price', $default_price);
1082
-                    }
1083
-                }
1084
-            }
1085
-            // we can't actually setup rows in this loop yet cause we don't know all
1086
-            // the unique tickets for this event yet (tickets are linked through all datetimes).
1087
-            // So we're going to temporarily cache some of that information.
1088
-            //loop through and setup the ticket rows and make sure the order is set.
1089
-            foreach ($related_tickets as $ticket) {
1090
-                $TKT_ID     = $ticket->get('TKT_ID');
1091
-                $ticket_row = $ticket->get('TKT_row');
1092
-                //we only want unique tickets in our final display!!
1093
-                if (! in_array($TKT_ID, $existing_ticket_ids, true)) {
1094
-                    $existing_ticket_ids[] = $TKT_ID;
1095
-                    $all_tickets[]         = $ticket;
1096
-                }
1097
-                //temporary cache of this ticket info for this datetime for later processing of datetime rows.
1098
-                $datetime_tickets[ $DTT_ID ][] = $ticket_row;
1099
-                //temporary cache of this datetime info for this ticket for later processing of ticket rows.
1100
-                if (
1101
-                    ! isset($ticket_datetimes[ $TKT_ID ])
1102
-                    || ! in_array($datetime_row, $ticket_datetimes[ $TKT_ID ], true)
1103
-                ) {
1104
-                    $ticket_datetimes[ $TKT_ID ][] = $datetime_row;
1105
-                }
1106
-            }
1107
-            $datetime_row++;
1108
-        }
1109
-        $main_template_args['total_ticket_rows']     = count($existing_ticket_ids);
1110
-        $main_template_args['existing_ticket_ids']   = implode(',', $existing_ticket_ids);
1111
-        $main_template_args['existing_datetime_ids'] = implode(',', $existing_datetime_ids);
1112
-        //sort $all_tickets by order
1113
-        usort(
1114
-            $all_tickets,
1115
-            function (EE_Ticket $a, EE_Ticket $b)
1116
-            {
1117
-                $a_order = (int) $a->get('TKT_order');
1118
-                $b_order = (int) $b->get('TKT_order');
1119
-                if ($a_order === $b_order) {
1120
-                    return 0;
1121
-                }
1122
-                return ($a_order < $b_order) ? -1 : 1;
1123
-            }
1124
-        );
1125
-        // k NOW we have all the data we need for setting up the dtt rows
1126
-        // and ticket rows so we start our dtt loop again.
1127
-        $datetime_row = 1;
1128
-        foreach ($datetimes as $datetime) {
1129
-            $main_template_args['datetime_rows'] .= $this->_get_datetime_row(
1130
-                $datetime_row,
1131
-                $datetime,
1132
-                $datetime_tickets,
1133
-                $all_tickets,
1134
-                false,
1135
-                $datetimes
1136
-            );
1137
-            $datetime_row++;
1138
-        }
1139
-        //then loop through all tickets for the ticket rows.
1140
-        $ticket_row = 1;
1141
-        foreach ($all_tickets as $ticket) {
1142
-            $main_template_args['ticket_rows'] .= $this->_get_ticket_row(
1143
-                $ticket_row,
1144
-                $ticket,
1145
-                $ticket_datetimes,
1146
-                $datetimes,
1147
-                false,
1148
-                $all_tickets
1149
-            );
1150
-            $ticket_row++;
1151
-        }
1152
-        $main_template_args['ticket_js_structure'] = $this->_get_ticket_js_structure($datetimes, $all_tickets);
1153
-        EEH_Template::display_template(
1154
-            PRICING_TEMPLATE_PATH . 'event_tickets_metabox_main.template.php',
1155
-            $main_template_args
1156
-        );
1157
-    }
985
+	/**
986
+	 * @throws ReflectionException
987
+	 * @throws InvalidArgumentException
988
+	 * @throws InvalidInterfaceException
989
+	 * @throws InvalidDataTypeException
990
+	 * @throws DomainException
991
+	 * @throws EE_Error
992
+	 */
993
+	public function pricing_metabox()
994
+	{
995
+		$existing_datetime_ids = $existing_ticket_ids = $datetime_tickets = $ticket_datetimes = array();
996
+		$event                 = $this->_adminpage_obj->get_cpt_model_obj();
997
+		//set is_creating_event property.
998
+		$EVT_ID                   = $event->ID();
999
+		$this->_is_creating_event = absint($EVT_ID) === 0;
1000
+		//default main template args
1001
+		$main_template_args = array(
1002
+			'event_datetime_help_link' => EEH_Template::get_help_tab_link(
1003
+				'event_editor_event_datetimes_help_tab',
1004
+				$this->_adminpage_obj->page_slug,
1005
+				$this->_adminpage_obj->get_req_action(),
1006
+				false,
1007
+				false
1008
+			),
1009
+			// todo need to add a filter to the template for the help text
1010
+			// in the Events_Admin_Page core file so we can add further help
1011
+			'existing_datetime_ids'    => '',
1012
+			'total_dtt_rows'           => 1,
1013
+			'add_new_dtt_help_link'    => EEH_Template::get_help_tab_link(
1014
+				'add_new_dtt_info',
1015
+				$this->_adminpage_obj->page_slug,
1016
+				$this->_adminpage_obj->get_req_action(),
1017
+				false,
1018
+				false
1019
+			),
1020
+			//todo need to add this help info id to the Events_Admin_Page core file so we can access it here.
1021
+			'datetime_rows'            => '',
1022
+			'show_tickets_container'   => '',
1023
+			//$this->_adminpage_obj->get_cpt_model_obj()->ID() > 1 ? ' style="display:none;"' : '',
1024
+			'ticket_rows'              => '',
1025
+			'existing_ticket_ids'      => '',
1026
+			'total_ticket_rows'        => 1,
1027
+			'ticket_js_structure'      => '',
1028
+			'ee_collapsible_status'    => ' ee-collapsible-open'
1029
+			//$this->_adminpage_obj->get_cpt_model_obj()->ID() > 0 ? ' ee-collapsible-closed' : ' ee-collapsible-open'
1030
+		);
1031
+		$timezone           = $event instanceof EE_Event ? $event->timezone_string() : null;
1032
+		do_action('AHEE_log', __FILE__, __FUNCTION__, '');
1033
+		/**
1034
+		 * 1. Start with retrieving Datetimes
1035
+		 * 2. For each datetime get related tickets
1036
+		 * 3. For each ticket get related prices
1037
+		 */
1038
+		/** @var EEM_Datetime $datetime_model */
1039
+		$datetime_model                       = EE_Registry::instance()->load_model('Datetime', array($timezone));
1040
+		$datetimes                            = $datetime_model->get_all_event_dates($EVT_ID);
1041
+		$main_template_args['total_dtt_rows'] = count($datetimes);
1042
+		/**
1043
+		 * @see https://events.codebasehq.com/projects/event-espresso/tickets/9486
1044
+		 * for why we are counting $datetime_row and then setting that on the Datetime object
1045
+		 */
1046
+		$datetime_row = 1;
1047
+		foreach ($datetimes as $datetime) {
1048
+			$DTT_ID = $datetime->get('DTT_ID');
1049
+			$datetime->set('DTT_order', $datetime_row);
1050
+			$existing_datetime_ids[] = $DTT_ID;
1051
+			//tickets attached
1052
+			$related_tickets = $datetime->ID() > 0
1053
+				? $datetime->get_many_related(
1054
+					'Ticket',
1055
+					array(
1056
+						array(
1057
+							'OR' => array('TKT_deleted' => 1, 'TKT_deleted*' => 0),
1058
+						),
1059
+						'default_where_conditions' => 'none',
1060
+						'order_by'                 => array('TKT_order' => 'ASC'),
1061
+					)
1062
+				)
1063
+				: array();
1064
+			//if there are no related tickets this is likely a new event OR autodraft
1065
+			// event so we need to generate the default tickets because datetimes
1066
+			// ALWAYS have at least one related ticket!!.  EXCEPT, we dont' do this if there is already more than one
1067
+			// datetime on the event.
1068
+			if (empty ($related_tickets) && count($datetimes) < 2) {
1069
+				/** @var EEM_Ticket $ticket_model */
1070
+				$ticket_model    = EE_Registry::instance()->load_model('Ticket');
1071
+				$related_tickets = $ticket_model->get_all_default_tickets();
1072
+				// this should be ordered by TKT_ID, so let's grab the first default ticket
1073
+				// (which will be the main default) and ensure it has any default prices added to it (but do NOT save).
1074
+				$default_prices      = EEM_Price::instance()->get_all_default_prices();
1075
+				$main_default_ticket = reset($related_tickets);
1076
+				if ($main_default_ticket instanceof EE_Ticket) {
1077
+					foreach ($default_prices as $default_price) {
1078
+						if ($default_price instanceof EE_Price && $default_price->is_base_price()) {
1079
+							continue;
1080
+						}
1081
+						$main_default_ticket->cache('Price', $default_price);
1082
+					}
1083
+				}
1084
+			}
1085
+			// we can't actually setup rows in this loop yet cause we don't know all
1086
+			// the unique tickets for this event yet (tickets are linked through all datetimes).
1087
+			// So we're going to temporarily cache some of that information.
1088
+			//loop through and setup the ticket rows and make sure the order is set.
1089
+			foreach ($related_tickets as $ticket) {
1090
+				$TKT_ID     = $ticket->get('TKT_ID');
1091
+				$ticket_row = $ticket->get('TKT_row');
1092
+				//we only want unique tickets in our final display!!
1093
+				if (! in_array($TKT_ID, $existing_ticket_ids, true)) {
1094
+					$existing_ticket_ids[] = $TKT_ID;
1095
+					$all_tickets[]         = $ticket;
1096
+				}
1097
+				//temporary cache of this ticket info for this datetime for later processing of datetime rows.
1098
+				$datetime_tickets[ $DTT_ID ][] = $ticket_row;
1099
+				//temporary cache of this datetime info for this ticket for later processing of ticket rows.
1100
+				if (
1101
+					! isset($ticket_datetimes[ $TKT_ID ])
1102
+					|| ! in_array($datetime_row, $ticket_datetimes[ $TKT_ID ], true)
1103
+				) {
1104
+					$ticket_datetimes[ $TKT_ID ][] = $datetime_row;
1105
+				}
1106
+			}
1107
+			$datetime_row++;
1108
+		}
1109
+		$main_template_args['total_ticket_rows']     = count($existing_ticket_ids);
1110
+		$main_template_args['existing_ticket_ids']   = implode(',', $existing_ticket_ids);
1111
+		$main_template_args['existing_datetime_ids'] = implode(',', $existing_datetime_ids);
1112
+		//sort $all_tickets by order
1113
+		usort(
1114
+			$all_tickets,
1115
+			function (EE_Ticket $a, EE_Ticket $b)
1116
+			{
1117
+				$a_order = (int) $a->get('TKT_order');
1118
+				$b_order = (int) $b->get('TKT_order');
1119
+				if ($a_order === $b_order) {
1120
+					return 0;
1121
+				}
1122
+				return ($a_order < $b_order) ? -1 : 1;
1123
+			}
1124
+		);
1125
+		// k NOW we have all the data we need for setting up the dtt rows
1126
+		// and ticket rows so we start our dtt loop again.
1127
+		$datetime_row = 1;
1128
+		foreach ($datetimes as $datetime) {
1129
+			$main_template_args['datetime_rows'] .= $this->_get_datetime_row(
1130
+				$datetime_row,
1131
+				$datetime,
1132
+				$datetime_tickets,
1133
+				$all_tickets,
1134
+				false,
1135
+				$datetimes
1136
+			);
1137
+			$datetime_row++;
1138
+		}
1139
+		//then loop through all tickets for the ticket rows.
1140
+		$ticket_row = 1;
1141
+		foreach ($all_tickets as $ticket) {
1142
+			$main_template_args['ticket_rows'] .= $this->_get_ticket_row(
1143
+				$ticket_row,
1144
+				$ticket,
1145
+				$ticket_datetimes,
1146
+				$datetimes,
1147
+				false,
1148
+				$all_tickets
1149
+			);
1150
+			$ticket_row++;
1151
+		}
1152
+		$main_template_args['ticket_js_structure'] = $this->_get_ticket_js_structure($datetimes, $all_tickets);
1153
+		EEH_Template::display_template(
1154
+			PRICING_TEMPLATE_PATH . 'event_tickets_metabox_main.template.php',
1155
+			$main_template_args
1156
+		);
1157
+	}
1158 1158
 
1159 1159
 
1160
-    /**
1161
-     * @param int         $datetime_row
1162
-     * @param EE_Datetime $datetime
1163
-     * @param array       $datetime_tickets
1164
-     * @param array       $all_tickets
1165
-     * @param bool        $default
1166
-     * @param array       $all_datetimes
1167
-     * @return mixed
1168
-     * @throws DomainException
1169
-     * @throws EE_Error
1170
-     */
1171
-    protected function _get_datetime_row(
1172
-        $datetime_row,
1173
-        EE_Datetime $datetime,
1174
-        $datetime_tickets = array(),
1175
-        $all_tickets = array(),
1176
-        $default = false,
1177
-        $all_datetimes = array()
1178
-    ) {
1179
-        $dtt_display_template_args = array(
1180
-            'dtt_edit_row'             => $this->_get_dtt_edit_row(
1181
-                $datetime_row,
1182
-                $datetime,
1183
-                $default,
1184
-                $all_datetimes
1185
-            ),
1186
-            'dtt_attached_tickets_row' => $this->_get_dtt_attached_tickets_row(
1187
-                $datetime_row,
1188
-                $datetime,
1189
-                $datetime_tickets,
1190
-                $all_tickets,
1191
-                $default
1192
-            ),
1193
-            'dtt_row'                  => $default ? 'DTTNUM' : $datetime_row,
1194
-        );
1195
-        return EEH_Template::display_template(
1196
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_row_wrapper.template.php',
1197
-            $dtt_display_template_args,
1198
-            true
1199
-        );
1200
-    }
1160
+	/**
1161
+	 * @param int         $datetime_row
1162
+	 * @param EE_Datetime $datetime
1163
+	 * @param array       $datetime_tickets
1164
+	 * @param array       $all_tickets
1165
+	 * @param bool        $default
1166
+	 * @param array       $all_datetimes
1167
+	 * @return mixed
1168
+	 * @throws DomainException
1169
+	 * @throws EE_Error
1170
+	 */
1171
+	protected function _get_datetime_row(
1172
+		$datetime_row,
1173
+		EE_Datetime $datetime,
1174
+		$datetime_tickets = array(),
1175
+		$all_tickets = array(),
1176
+		$default = false,
1177
+		$all_datetimes = array()
1178
+	) {
1179
+		$dtt_display_template_args = array(
1180
+			'dtt_edit_row'             => $this->_get_dtt_edit_row(
1181
+				$datetime_row,
1182
+				$datetime,
1183
+				$default,
1184
+				$all_datetimes
1185
+			),
1186
+			'dtt_attached_tickets_row' => $this->_get_dtt_attached_tickets_row(
1187
+				$datetime_row,
1188
+				$datetime,
1189
+				$datetime_tickets,
1190
+				$all_tickets,
1191
+				$default
1192
+			),
1193
+			'dtt_row'                  => $default ? 'DTTNUM' : $datetime_row,
1194
+		);
1195
+		return EEH_Template::display_template(
1196
+			PRICING_TEMPLATE_PATH . 'event_tickets_datetime_row_wrapper.template.php',
1197
+			$dtt_display_template_args,
1198
+			true
1199
+		);
1200
+	}
1201 1201
 
1202 1202
 
1203
-    /**
1204
-     * This method is used to generate a dtt fields  edit row.
1205
-     * The same row is used to generate a row with valid DTT objects
1206
-     * and the default row that is used as the skeleton by the js.
1207
-     *
1208
-     * @param int           $datetime_row  The row number for the row being generated.
1209
-     * @param EE_Datetime   $datetime
1210
-     * @param bool          $default       Whether a default row is being generated or not.
1211
-     * @param EE_Datetime[] $all_datetimes This is the array of all datetimes used in the editor.
1212
-     * @return string
1213
-     * @throws DomainException
1214
-     * @throws EE_Error
1215
-     */
1216
-    protected function _get_dtt_edit_row($datetime_row, $datetime, $default, $all_datetimes)
1217
-    {
1218
-        // if the incoming $datetime object is NOT an instance of EE_Datetime then force default to true.
1219
-        $default                     = ! $datetime instanceof EE_Datetime ? true : $default;
1220
-        $template_args               = array(
1221
-            'dtt_row'              => $default ? 'DTTNUM' : $datetime_row,
1222
-            'event_datetimes_name' => $default ? 'DTTNAMEATTR' : 'edit_event_datetimes',
1223
-            'edit_dtt_expanded'    => '',
1224
-            'DTT_ID'               => $default ? '' : $datetime->ID(),
1225
-            'DTT_name'             => $default ? '' : $datetime->get_f('DTT_name'),
1226
-            'DTT_description'      => $default ? '' : $datetime->get_f('DTT_description'),
1227
-            'DTT_EVT_start'        => $default ? '' : $datetime->start_date($this->_date_time_format),
1228
-            'DTT_EVT_end'          => $default ? '' : $datetime->end_date($this->_date_time_format),
1229
-            'DTT_reg_limit'        => $default
1230
-                ? ''
1231
-                : $datetime->get_pretty(
1232
-                    'DTT_reg_limit',
1233
-                    'input'
1234
-                ),
1235
-            'DTT_order'            => $default ? 'DTTNUM' : $datetime_row,
1236
-            'dtt_sold'             => $default ? '0' : $datetime->get('DTT_sold'),
1237
-            'dtt_reserved'         => $default ? '0' : $datetime->reserved(),
1238
-            'clone_icon'           => ! empty($datetime) && $datetime->get('DTT_sold') > 0
1239
-                ? ''
1240
-                : 'clone-icon ee-icon ee-icon-clone clickable',
1241
-            'trash_icon'           => ! empty($datetime) && $datetime->get('DTT_sold') > 0
1242
-                ? 'ee-lock-icon'
1243
-                : 'trash-icon dashicons dashicons-post-trash clickable',
1244
-            'reg_list_url'         => $default || ! $datetime->event() instanceof \EE_Event
1245
-                ? ''
1246
-                : EE_Admin_Page::add_query_args_and_nonce(
1247
-                    array('event_id' => $datetime->event()->ID(), 'datetime_id' => $datetime->ID()),
1248
-                    REG_ADMIN_URL
1249
-                ),
1250
-        );
1251
-        $template_args['show_trash'] = count($all_datetimes) === 1 && $template_args['trash_icon'] !== 'ee-lock-icon'
1252
-            ? ' style="display:none"'
1253
-            : '';
1254
-        //allow filtering of template args at this point.
1255
-        $template_args = apply_filters(
1256
-            'FHEE__espresso_events_Pricing_Hooks___get_dtt_edit_row__template_args',
1257
-            $template_args,
1258
-            $datetime_row,
1259
-            $datetime,
1260
-            $default,
1261
-            $all_datetimes,
1262
-            $this->_is_creating_event
1263
-        );
1264
-        return EEH_Template::display_template(
1265
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_edit_row.template.php',
1266
-            $template_args,
1267
-            true
1268
-        );
1269
-    }
1203
+	/**
1204
+	 * This method is used to generate a dtt fields  edit row.
1205
+	 * The same row is used to generate a row with valid DTT objects
1206
+	 * and the default row that is used as the skeleton by the js.
1207
+	 *
1208
+	 * @param int           $datetime_row  The row number for the row being generated.
1209
+	 * @param EE_Datetime   $datetime
1210
+	 * @param bool          $default       Whether a default row is being generated or not.
1211
+	 * @param EE_Datetime[] $all_datetimes This is the array of all datetimes used in the editor.
1212
+	 * @return string
1213
+	 * @throws DomainException
1214
+	 * @throws EE_Error
1215
+	 */
1216
+	protected function _get_dtt_edit_row($datetime_row, $datetime, $default, $all_datetimes)
1217
+	{
1218
+		// if the incoming $datetime object is NOT an instance of EE_Datetime then force default to true.
1219
+		$default                     = ! $datetime instanceof EE_Datetime ? true : $default;
1220
+		$template_args               = array(
1221
+			'dtt_row'              => $default ? 'DTTNUM' : $datetime_row,
1222
+			'event_datetimes_name' => $default ? 'DTTNAMEATTR' : 'edit_event_datetimes',
1223
+			'edit_dtt_expanded'    => '',
1224
+			'DTT_ID'               => $default ? '' : $datetime->ID(),
1225
+			'DTT_name'             => $default ? '' : $datetime->get_f('DTT_name'),
1226
+			'DTT_description'      => $default ? '' : $datetime->get_f('DTT_description'),
1227
+			'DTT_EVT_start'        => $default ? '' : $datetime->start_date($this->_date_time_format),
1228
+			'DTT_EVT_end'          => $default ? '' : $datetime->end_date($this->_date_time_format),
1229
+			'DTT_reg_limit'        => $default
1230
+				? ''
1231
+				: $datetime->get_pretty(
1232
+					'DTT_reg_limit',
1233
+					'input'
1234
+				),
1235
+			'DTT_order'            => $default ? 'DTTNUM' : $datetime_row,
1236
+			'dtt_sold'             => $default ? '0' : $datetime->get('DTT_sold'),
1237
+			'dtt_reserved'         => $default ? '0' : $datetime->reserved(),
1238
+			'clone_icon'           => ! empty($datetime) && $datetime->get('DTT_sold') > 0
1239
+				? ''
1240
+				: 'clone-icon ee-icon ee-icon-clone clickable',
1241
+			'trash_icon'           => ! empty($datetime) && $datetime->get('DTT_sold') > 0
1242
+				? 'ee-lock-icon'
1243
+				: 'trash-icon dashicons dashicons-post-trash clickable',
1244
+			'reg_list_url'         => $default || ! $datetime->event() instanceof \EE_Event
1245
+				? ''
1246
+				: EE_Admin_Page::add_query_args_and_nonce(
1247
+					array('event_id' => $datetime->event()->ID(), 'datetime_id' => $datetime->ID()),
1248
+					REG_ADMIN_URL
1249
+				),
1250
+		);
1251
+		$template_args['show_trash'] = count($all_datetimes) === 1 && $template_args['trash_icon'] !== 'ee-lock-icon'
1252
+			? ' style="display:none"'
1253
+			: '';
1254
+		//allow filtering of template args at this point.
1255
+		$template_args = apply_filters(
1256
+			'FHEE__espresso_events_Pricing_Hooks___get_dtt_edit_row__template_args',
1257
+			$template_args,
1258
+			$datetime_row,
1259
+			$datetime,
1260
+			$default,
1261
+			$all_datetimes,
1262
+			$this->_is_creating_event
1263
+		);
1264
+		return EEH_Template::display_template(
1265
+			PRICING_TEMPLATE_PATH . 'event_tickets_datetime_edit_row.template.php',
1266
+			$template_args,
1267
+			true
1268
+		);
1269
+	}
1270 1270
 
1271 1271
 
1272
-    /**
1273
-     * @param int         $datetime_row
1274
-     * @param EE_Datetime $datetime
1275
-     * @param array       $datetime_tickets
1276
-     * @param array       $all_tickets
1277
-     * @param bool        $default
1278
-     * @return mixed
1279
-     * @throws DomainException
1280
-     * @throws EE_Error
1281
-     */
1282
-    protected function _get_dtt_attached_tickets_row(
1283
-        $datetime_row,
1284
-        $datetime,
1285
-        $datetime_tickets = array(),
1286
-        $all_tickets = array(),
1287
-        $default
1288
-    ) {
1289
-        $template_args = array(
1290
-            'dtt_row'                           => $default ? 'DTTNUM' : $datetime_row,
1291
-            'event_datetimes_name'              => $default ? 'DTTNAMEATTR' : 'edit_event_datetimes',
1292
-            'DTT_description'                   => $default ? '' : $datetime->get_f('DTT_description'),
1293
-            'datetime_tickets_list'             => $default ? '<li class="hidden"></li>' : '',
1294
-            'show_tickets_row'                  => ' style="display:none;"',
1295
-            'add_new_datetime_ticket_help_link' => EEH_Template::get_help_tab_link(
1296
-                'add_new_ticket_via_datetime',
1297
-                $this->_adminpage_obj->page_slug,
1298
-                $this->_adminpage_obj->get_req_action(),
1299
-                false,
1300
-                false
1301
-            ),
1302
-            //todo need to add this help info id to the Events_Admin_Page core file so we can access it here.
1303
-            'DTT_ID'                            => $default ? '' : $datetime->ID(),
1304
-        );
1305
-        //need to setup the list items (but only if this isn't a default skeleton setup)
1306
-        if (! $default) {
1307
-            $ticket_row = 1;
1308
-            foreach ($all_tickets as $ticket) {
1309
-                $template_args['datetime_tickets_list'] .= $this->_get_datetime_tickets_list_item(
1310
-                    $datetime_row,
1311
-                    $ticket_row,
1312
-                    $datetime,
1313
-                    $ticket,
1314
-                    $datetime_tickets,
1315
-                    $default
1316
-                );
1317
-                $ticket_row++;
1318
-            }
1319
-        }
1320
-        //filter template args at this point
1321
-        $template_args = apply_filters(
1322
-            'FHEE__espresso_events_Pricing_Hooks___get_dtt_attached_ticket_row__template_args',
1323
-            $template_args,
1324
-            $datetime_row,
1325
-            $datetime,
1326
-            $datetime_tickets,
1327
-            $all_tickets,
1328
-            $default,
1329
-            $this->_is_creating_event
1330
-        );
1331
-        return EEH_Template::display_template(
1332
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_attached_tickets_row.template.php',
1333
-            $template_args,
1334
-            true
1335
-        );
1336
-    }
1272
+	/**
1273
+	 * @param int         $datetime_row
1274
+	 * @param EE_Datetime $datetime
1275
+	 * @param array       $datetime_tickets
1276
+	 * @param array       $all_tickets
1277
+	 * @param bool        $default
1278
+	 * @return mixed
1279
+	 * @throws DomainException
1280
+	 * @throws EE_Error
1281
+	 */
1282
+	protected function _get_dtt_attached_tickets_row(
1283
+		$datetime_row,
1284
+		$datetime,
1285
+		$datetime_tickets = array(),
1286
+		$all_tickets = array(),
1287
+		$default
1288
+	) {
1289
+		$template_args = array(
1290
+			'dtt_row'                           => $default ? 'DTTNUM' : $datetime_row,
1291
+			'event_datetimes_name'              => $default ? 'DTTNAMEATTR' : 'edit_event_datetimes',
1292
+			'DTT_description'                   => $default ? '' : $datetime->get_f('DTT_description'),
1293
+			'datetime_tickets_list'             => $default ? '<li class="hidden"></li>' : '',
1294
+			'show_tickets_row'                  => ' style="display:none;"',
1295
+			'add_new_datetime_ticket_help_link' => EEH_Template::get_help_tab_link(
1296
+				'add_new_ticket_via_datetime',
1297
+				$this->_adminpage_obj->page_slug,
1298
+				$this->_adminpage_obj->get_req_action(),
1299
+				false,
1300
+				false
1301
+			),
1302
+			//todo need to add this help info id to the Events_Admin_Page core file so we can access it here.
1303
+			'DTT_ID'                            => $default ? '' : $datetime->ID(),
1304
+		);
1305
+		//need to setup the list items (but only if this isn't a default skeleton setup)
1306
+		if (! $default) {
1307
+			$ticket_row = 1;
1308
+			foreach ($all_tickets as $ticket) {
1309
+				$template_args['datetime_tickets_list'] .= $this->_get_datetime_tickets_list_item(
1310
+					$datetime_row,
1311
+					$ticket_row,
1312
+					$datetime,
1313
+					$ticket,
1314
+					$datetime_tickets,
1315
+					$default
1316
+				);
1317
+				$ticket_row++;
1318
+			}
1319
+		}
1320
+		//filter template args at this point
1321
+		$template_args = apply_filters(
1322
+			'FHEE__espresso_events_Pricing_Hooks___get_dtt_attached_ticket_row__template_args',
1323
+			$template_args,
1324
+			$datetime_row,
1325
+			$datetime,
1326
+			$datetime_tickets,
1327
+			$all_tickets,
1328
+			$default,
1329
+			$this->_is_creating_event
1330
+		);
1331
+		return EEH_Template::display_template(
1332
+			PRICING_TEMPLATE_PATH . 'event_tickets_datetime_attached_tickets_row.template.php',
1333
+			$template_args,
1334
+			true
1335
+		);
1336
+	}
1337 1337
 
1338 1338
 
1339
-    /**
1340
-     * @param int         $datetime_row
1341
-     * @param int         $ticket_row
1342
-     * @param EE_Datetime $datetime
1343
-     * @param EE_Ticket   $ticket
1344
-     * @param array       $datetime_tickets
1345
-     * @param bool        $default
1346
-     * @return mixed
1347
-     * @throws DomainException
1348
-     * @throws EE_Error
1349
-     */
1350
-    protected function _get_datetime_tickets_list_item(
1351
-        $datetime_row,
1352
-        $ticket_row,
1353
-        $datetime,
1354
-        $ticket,
1355
-        $datetime_tickets = array(),
1356
-        $default
1357
-    ) {
1358
-        $dtt_tkts      = $datetime instanceof EE_Datetime && isset($datetime_tickets[ $datetime->ID() ])
1359
-            ? $datetime_tickets[ $datetime->ID() ]
1360
-            : array();
1361
-        $display_row   = $ticket instanceof EE_Ticket ? $ticket->get('TKT_row') : 0;
1362
-        $no_ticket     = $default && empty($ticket);
1363
-        $template_args = array(
1364
-            'dtt_row'                 => $default
1365
-                ? 'DTTNUM'
1366
-                : $datetime_row,
1367
-            'tkt_row'                 => $no_ticket
1368
-                ? 'TICKETNUM'
1369
-                : $ticket_row,
1370
-            'datetime_ticket_checked' => in_array($display_row, $dtt_tkts, true)
1371
-                ? ' checked="checked"'
1372
-                : '',
1373
-            'ticket_selected'         => in_array($display_row, $dtt_tkts, true)
1374
-                ? ' ticket-selected'
1375
-                : '',
1376
-            'TKT_name'                => $no_ticket
1377
-                ? 'TKTNAME'
1378
-                : $ticket->get('TKT_name'),
1379
-            'tkt_status_class'        => $no_ticket || $this->_is_creating_event
1380
-                ? ' tkt-status-' . EE_Ticket::onsale
1381
-                : ' tkt-status-' . $ticket->ticket_status(),
1382
-        );
1383
-        //filter template args
1384
-        $template_args = apply_filters(
1385
-            'FHEE__espresso_events_Pricing_Hooks___get_datetime_tickets_list_item__template_args',
1386
-            $template_args,
1387
-            $datetime_row,
1388
-            $ticket_row,
1389
-            $datetime,
1390
-            $ticket,
1391
-            $datetime_tickets,
1392
-            $default,
1393
-            $this->_is_creating_event
1394
-        );
1395
-        return EEH_Template::display_template(
1396
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_dtt_tickets_list.template.php',
1397
-            $template_args,
1398
-            true
1399
-        );
1400
-    }
1339
+	/**
1340
+	 * @param int         $datetime_row
1341
+	 * @param int         $ticket_row
1342
+	 * @param EE_Datetime $datetime
1343
+	 * @param EE_Ticket   $ticket
1344
+	 * @param array       $datetime_tickets
1345
+	 * @param bool        $default
1346
+	 * @return mixed
1347
+	 * @throws DomainException
1348
+	 * @throws EE_Error
1349
+	 */
1350
+	protected function _get_datetime_tickets_list_item(
1351
+		$datetime_row,
1352
+		$ticket_row,
1353
+		$datetime,
1354
+		$ticket,
1355
+		$datetime_tickets = array(),
1356
+		$default
1357
+	) {
1358
+		$dtt_tkts      = $datetime instanceof EE_Datetime && isset($datetime_tickets[ $datetime->ID() ])
1359
+			? $datetime_tickets[ $datetime->ID() ]
1360
+			: array();
1361
+		$display_row   = $ticket instanceof EE_Ticket ? $ticket->get('TKT_row') : 0;
1362
+		$no_ticket     = $default && empty($ticket);
1363
+		$template_args = array(
1364
+			'dtt_row'                 => $default
1365
+				? 'DTTNUM'
1366
+				: $datetime_row,
1367
+			'tkt_row'                 => $no_ticket
1368
+				? 'TICKETNUM'
1369
+				: $ticket_row,
1370
+			'datetime_ticket_checked' => in_array($display_row, $dtt_tkts, true)
1371
+				? ' checked="checked"'
1372
+				: '',
1373
+			'ticket_selected'         => in_array($display_row, $dtt_tkts, true)
1374
+				? ' ticket-selected'
1375
+				: '',
1376
+			'TKT_name'                => $no_ticket
1377
+				? 'TKTNAME'
1378
+				: $ticket->get('TKT_name'),
1379
+			'tkt_status_class'        => $no_ticket || $this->_is_creating_event
1380
+				? ' tkt-status-' . EE_Ticket::onsale
1381
+				: ' tkt-status-' . $ticket->ticket_status(),
1382
+		);
1383
+		//filter template args
1384
+		$template_args = apply_filters(
1385
+			'FHEE__espresso_events_Pricing_Hooks___get_datetime_tickets_list_item__template_args',
1386
+			$template_args,
1387
+			$datetime_row,
1388
+			$ticket_row,
1389
+			$datetime,
1390
+			$ticket,
1391
+			$datetime_tickets,
1392
+			$default,
1393
+			$this->_is_creating_event
1394
+		);
1395
+		return EEH_Template::display_template(
1396
+			PRICING_TEMPLATE_PATH . 'event_tickets_datetime_dtt_tickets_list.template.php',
1397
+			$template_args,
1398
+			true
1399
+		);
1400
+	}
1401 1401
 
1402 1402
 
1403
-    /**
1404
-     * This generates the ticket row for tickets.
1405
-     * This same method is used to generate both the actual rows and the js skeleton row
1406
-     * (when default === true)
1407
-     *
1408
-     * @param int           $ticket_row       Represents the row number being generated.
1409
-     * @param               $ticket
1410
-     * @param EE_Datetime[] $ticket_datetimes Either an array of all datetimes on all tickets indexed by each ticket
1411
-     *                                        or empty for default
1412
-     * @param EE_Datetime[] $all_datetimes    All Datetimes on the event or empty for default.
1413
-     * @param bool          $default          Whether default row being generated or not.
1414
-     * @param EE_Ticket[]   $all_tickets      This is an array of all tickets attached to the event
1415
-     *                                        (or empty in the case of defaults)
1416
-     * @return mixed
1417
-     * @throws InvalidArgumentException
1418
-     * @throws InvalidInterfaceException
1419
-     * @throws InvalidDataTypeException
1420
-     * @throws DomainException
1421
-     * @throws EE_Error
1422
-     * @throws ReflectionException
1423
-     */
1424
-    protected function _get_ticket_row(
1425
-        $ticket_row,
1426
-        $ticket,
1427
-        $ticket_datetimes,
1428
-        $all_datetimes,
1429
-        $default = false,
1430
-        $all_tickets = array()
1431
-    ) {
1432
-        // if $ticket is not an instance of EE_Ticket then force default to true.
1433
-        $default = ! $ticket instanceof EE_Ticket ? true : $default;
1434
-        $prices  = ! empty($ticket) && ! $default ? $ticket->get_many_related('Price',
1435
-            array('default_where_conditions' => 'none', 'order_by' => array('PRC_order' => 'ASC'))) : array();
1436
-        // if there is only one price (which would be the base price)
1437
-        // or NO prices and this ticket is a default ticket,
1438
-        // let's just make sure there are no cached default prices on the object.
1439
-        // This is done by not including any query_params.
1440
-        if ($ticket instanceof EE_Ticket && $ticket->is_default() && (count($prices) === 1 || empty($prices))) {
1441
-            $prices = $ticket->prices();
1442
-        }
1443
-        // check if we're dealing with a default ticket in which case
1444
-        // we don't want any starting_ticket_datetime_row values set
1445
-        // (otherwise there won't be any new relationships created for tickets based off of the default ticket).
1446
-        // This will future proof in case there is ever any behaviour change between what the primary_key defaults to.
1447
-        $default_dtt      = $default || ($ticket instanceof EE_Ticket && $ticket->is_default());
1448
-        $tkt_datetimes    = $ticket instanceof EE_Ticket && isset($ticket_datetimes[ $ticket->ID() ])
1449
-            ? $ticket_datetimes[ $ticket->ID() ]
1450
-            : array();
1451
-        $ticket_subtotal  = $default ? 0 : $ticket->get_ticket_subtotal();
1452
-        $base_price       = $default ? null : $ticket->base_price();
1453
-        $count_price_mods = EEM_Price::instance()->get_all_default_prices(true);
1454
-        //breaking out complicated condition for ticket_status
1455
-        if ($default) {
1456
-            $ticket_status_class = ' tkt-status-' . EE_Ticket::onsale;
1457
-        } else {
1458
-            $ticket_status_class = $ticket->is_default()
1459
-                ? ' tkt-status-' . EE_Ticket::onsale
1460
-                : ' tkt-status-' . $ticket->ticket_status();
1461
-        }
1462
-        //breaking out complicated condition for TKT_taxable
1463
-        if ($default) {
1464
-            $TKT_taxable = '';
1465
-        } else {
1466
-            $TKT_taxable = $ticket->taxable()
1467
-                ? ' checked="checked"'
1468
-                : '';
1469
-        }
1470
-        if ($default) {
1471
-            $TKT_status = EEH_Template::pretty_status(EE_Ticket::onsale, false, 'sentence');
1472
-        } elseif ($ticket->is_default()) {
1473
-            $TKT_status = EEH_Template::pretty_status(EE_Ticket::onsale, false, 'sentence');
1474
-        } else {
1475
-            $TKT_status = $ticket->ticket_status(true);
1476
-        }
1477
-        if ($default) {
1478
-            $TKT_min = '';
1479
-        } else {
1480
-            $TKT_min = $ticket->min();
1481
-            if ($TKT_min === -1 || $TKT_min === 0) {
1482
-                $TKT_min = '';
1483
-            }
1484
-        }
1485
-        $template_args                 = array(
1486
-            'tkt_row'                       => $default ? 'TICKETNUM' : $ticket_row,
1487
-            'TKT_order'                     => $default ? 'TICKETNUM' : $ticket_row,
1488
-            //on initial page load this will always be the correct order.
1489
-            'tkt_status_class'              => $ticket_status_class,
1490
-            'display_edit_tkt_row'          => ' style="display:none;"',
1491
-            'edit_tkt_expanded'             => '',
1492
-            'edit_tickets_name'             => $default ? 'TICKETNAMEATTR' : 'edit_tickets',
1493
-            'TKT_name'                      => $default ? '' : $ticket->get_f('TKT_name'),
1494
-            'TKT_start_date'                => $default
1495
-                ? ''
1496
-                : $ticket->get_date('TKT_start_date', $this->_date_time_format),
1497
-            'TKT_end_date'                  => $default
1498
-                ? ''
1499
-                : $ticket->get_date('TKT_end_date', $this->_date_time_format),
1500
-            'TKT_status'                    => $TKT_status,
1501
-            'TKT_price'                     => $default
1502
-                ? ''
1503
-                : EEH_Template::format_currency(
1504
-                    $ticket->get_ticket_total_with_taxes(),
1505
-                    false,
1506
-                    false
1507
-                ),
1508
-            'TKT_price_code'                => EE_Registry::instance()->CFG->currency->code,
1509
-            'TKT_price_amount'              => $default ? 0 : $ticket_subtotal,
1510
-            'TKT_qty'                       => $default
1511
-                ? ''
1512
-                : $ticket->get_pretty('TKT_qty', 'symbol'),
1513
-            'TKT_qty_for_input'             => $default
1514
-                ? ''
1515
-                : $ticket->get_pretty('TKT_qty', 'input'),
1516
-            'TKT_uses'                      => $default
1517
-                ? ''
1518
-                : $ticket->get_pretty('TKT_uses', 'input'),
1519
-            'TKT_min'                       => $TKT_min,
1520
-            'TKT_max'                       => $default
1521
-                ? ''
1522
-                : $ticket->get_pretty('TKT_max', 'input'),
1523
-            'TKT_sold'                      => $default ? 0 : $ticket->tickets_sold('ticket'),
1524
-            'TKT_reserved'                  => $default ? 0 : $ticket->reserved(),
1525
-            'TKT_registrations'             => $default
1526
-                ? 0
1527
-                : $ticket->count_registrations(
1528
-                    array(
1529
-                        array(
1530
-                            'STS_ID' => array(
1531
-                                '!=',
1532
-                                EEM_Registration::status_id_incomplete,
1533
-                            ),
1534
-                        ),
1535
-                    )
1536
-                ),
1537
-            'TKT_ID'                        => $default ? 0 : $ticket->ID(),
1538
-            'TKT_description'               => $default ? '' : $ticket->get_f('TKT_description'),
1539
-            'TKT_is_default'                => $default ? 0 : $ticket->is_default(),
1540
-            'TKT_required'                  => $default ? 0 : $ticket->required(),
1541
-            'TKT_is_default_selector'       => '',
1542
-            'ticket_price_rows'             => '',
1543
-            'TKT_base_price'                => $default || ! $base_price instanceof EE_Price
1544
-                ? ''
1545
-                : $base_price->get_pretty('PRC_amount', 'localized_float'),
1546
-            'TKT_base_price_ID'             => $default || ! $base_price instanceof EE_Price ? 0 : $base_price->ID(),
1547
-            'show_price_modifier'           => count($prices) > 1 || ($default && $count_price_mods > 0)
1548
-                ? ''
1549
-                : ' style="display:none;"',
1550
-            'show_price_mod_button'         => count($prices) > 1
1551
-                                               || ($default && $count_price_mods > 0)
1552
-                                               || (! $default && $ticket->deleted())
1553
-                ? ' style="display:none;"'
1554
-                : '',
1555
-            'total_price_rows'              => count($prices) > 1 ? count($prices) : 1,
1556
-            'ticket_datetimes_list'         => $default ? '<li class="hidden"></li>' : '',
1557
-            'starting_ticket_datetime_rows' => $default || $default_dtt ? '' : implode(',', $tkt_datetimes),
1558
-            'ticket_datetime_rows'          => $default ? '' : implode(',', $tkt_datetimes),
1559
-            'existing_ticket_price_ids'     => $default ? '' : implode(',', array_keys($prices)),
1560
-            'ticket_template_id'            => $default ? 0 : $ticket->get('TTM_ID'),
1561
-            'TKT_taxable'                   => $TKT_taxable,
1562
-            'display_subtotal'              => $ticket instanceof EE_Ticket && $ticket->taxable()
1563
-                ? ''
1564
-                : ' style="display:none"',
1565
-            'price_currency_symbol'         => EE_Registry::instance()->CFG->currency->sign,
1566
-            'TKT_subtotal_amount_display'   => EEH_Template::format_currency(
1567
-                $ticket_subtotal,
1568
-                false,
1569
-                false
1570
-            ),
1571
-            'TKT_subtotal_amount'           => $ticket_subtotal,
1572
-            'tax_rows'                      => $this->_get_tax_rows($ticket_row, $ticket),
1573
-            'disabled'                      => $ticket instanceof EE_Ticket && $ticket->deleted(),
1574
-            'ticket_archive_class'          => $ticket instanceof EE_Ticket && $ticket->deleted()
1575
-                ? ' ticket-archived'
1576
-                : '',
1577
-            'trash_icon'                    => $ticket instanceof EE_Ticket
1578
-                                               && $ticket->deleted()
1579
-                                               && ! $ticket->is_permanently_deleteable()
1580
-                ? 'ee-lock-icon '
1581
-                : 'trash-icon dashicons dashicons-post-trash clickable',
1582
-            'clone_icon'                    => $ticket instanceof EE_Ticket && $ticket->deleted()
1583
-                ? ''
1584
-                : 'clone-icon ee-icon ee-icon-clone clickable',
1585
-        );
1586
-        $template_args['trash_hidden'] = count($all_tickets) === 1 && $template_args['trash_icon'] !== 'ee-lock-icon'
1587
-            ? ' style="display:none"'
1588
-            : '';
1589
-        //handle rows that should NOT be empty
1590
-        if (empty($template_args['TKT_start_date'])) {
1591
-            //if empty then the start date will be now.
1592
-            $template_args['TKT_start_date']   = date($this->_date_time_format,
1593
-                current_time('timestamp'));
1594
-            $template_args['tkt_status_class'] = ' tkt-status-' . EE_Ticket::onsale;
1595
-        }
1596
-        if (empty($template_args['TKT_end_date'])) {
1597
-            //get the earliest datetime (if present);
1598
-            $earliest_dtt = $this->_adminpage_obj->get_cpt_model_obj()->ID() > 0
1599
-                ? $this->_adminpage_obj->get_cpt_model_obj()->get_first_related(
1600
-                    'Datetime',
1601
-                    array('order_by' => array('DTT_EVT_start' => 'ASC'))
1602
-                )
1603
-                : null;
1604
-            if (! empty($earliest_dtt)) {
1605
-                $template_args['TKT_end_date'] = $earliest_dtt->get_datetime(
1606
-                    'DTT_EVT_start',
1607
-                    $this->_date_time_format
1608
-                );
1609
-            } else {
1610
-                //default so let's just use what's been set for the default date-time which is 30 days from now.
1611
-                $template_args['TKT_end_date'] = date(
1612
-                    $this->_date_time_format,
1613
-                    mktime(
1614
-                        24, 0, 0, date('m'), date('d') + 29, date('Y')
1615
-                    )
1616
-                );
1617
-            }
1618
-            $template_args['tkt_status_class'] = ' tkt-status-' . EE_Ticket::onsale;
1619
-        }
1620
-        //generate ticket_datetime items
1621
-        if (! $default) {
1622
-            $datetime_row = 1;
1623
-            foreach ($all_datetimes as $datetime) {
1624
-                $template_args['ticket_datetimes_list'] .= $this->_get_ticket_datetime_list_item(
1625
-                    $datetime_row,
1626
-                    $ticket_row,
1627
-                    $datetime,
1628
-                    $ticket,
1629
-                    $ticket_datetimes,
1630
-                    $default
1631
-                );
1632
-                $datetime_row++;
1633
-            }
1634
-        }
1635
-        $price_row = 1;
1636
-        foreach ($prices as $price) {
1637
-            if (! $price instanceof EE_Price) {
1638
-                continue;
1639
-            }
1640
-            if ($price->is_base_price()) {
1641
-                $price_row++;
1642
-                continue;
1643
-            }
1644
-            $show_trash                         = ! ((count($prices) > 1 && $price_row === 1) || count($prices) === 1);
1645
-            $show_create                        = ! (count($prices) > 1 && count($prices) !== $price_row);
1646
-            $template_args['ticket_price_rows'] .= $this->_get_ticket_price_row(
1647
-                $ticket_row,
1648
-                $price_row,
1649
-                $price,
1650
-                $default,
1651
-                $ticket,
1652
-                $show_trash,
1653
-                $show_create
1654
-            );
1655
-            $price_row++;
1656
-        }
1657
-        //filter $template_args
1658
-        $template_args = apply_filters(
1659
-            'FHEE__espresso_events_Pricing_Hooks___get_ticket_row__template_args',
1660
-            $template_args,
1661
-            $ticket_row,
1662
-            $ticket,
1663
-            $ticket_datetimes,
1664
-            $all_datetimes,
1665
-            $default,
1666
-            $all_tickets,
1667
-            $this->_is_creating_event
1668
-        );
1669
-        return EEH_Template::display_template(
1670
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_row.template.php',
1671
-            $template_args,
1672
-            true
1673
-        );
1674
-    }
1403
+	/**
1404
+	 * This generates the ticket row for tickets.
1405
+	 * This same method is used to generate both the actual rows and the js skeleton row
1406
+	 * (when default === true)
1407
+	 *
1408
+	 * @param int           $ticket_row       Represents the row number being generated.
1409
+	 * @param               $ticket
1410
+	 * @param EE_Datetime[] $ticket_datetimes Either an array of all datetimes on all tickets indexed by each ticket
1411
+	 *                                        or empty for default
1412
+	 * @param EE_Datetime[] $all_datetimes    All Datetimes on the event or empty for default.
1413
+	 * @param bool          $default          Whether default row being generated or not.
1414
+	 * @param EE_Ticket[]   $all_tickets      This is an array of all tickets attached to the event
1415
+	 *                                        (or empty in the case of defaults)
1416
+	 * @return mixed
1417
+	 * @throws InvalidArgumentException
1418
+	 * @throws InvalidInterfaceException
1419
+	 * @throws InvalidDataTypeException
1420
+	 * @throws DomainException
1421
+	 * @throws EE_Error
1422
+	 * @throws ReflectionException
1423
+	 */
1424
+	protected function _get_ticket_row(
1425
+		$ticket_row,
1426
+		$ticket,
1427
+		$ticket_datetimes,
1428
+		$all_datetimes,
1429
+		$default = false,
1430
+		$all_tickets = array()
1431
+	) {
1432
+		// if $ticket is not an instance of EE_Ticket then force default to true.
1433
+		$default = ! $ticket instanceof EE_Ticket ? true : $default;
1434
+		$prices  = ! empty($ticket) && ! $default ? $ticket->get_many_related('Price',
1435
+			array('default_where_conditions' => 'none', 'order_by' => array('PRC_order' => 'ASC'))) : array();
1436
+		// if there is only one price (which would be the base price)
1437
+		// or NO prices and this ticket is a default ticket,
1438
+		// let's just make sure there are no cached default prices on the object.
1439
+		// This is done by not including any query_params.
1440
+		if ($ticket instanceof EE_Ticket && $ticket->is_default() && (count($prices) === 1 || empty($prices))) {
1441
+			$prices = $ticket->prices();
1442
+		}
1443
+		// check if we're dealing with a default ticket in which case
1444
+		// we don't want any starting_ticket_datetime_row values set
1445
+		// (otherwise there won't be any new relationships created for tickets based off of the default ticket).
1446
+		// This will future proof in case there is ever any behaviour change between what the primary_key defaults to.
1447
+		$default_dtt      = $default || ($ticket instanceof EE_Ticket && $ticket->is_default());
1448
+		$tkt_datetimes    = $ticket instanceof EE_Ticket && isset($ticket_datetimes[ $ticket->ID() ])
1449
+			? $ticket_datetimes[ $ticket->ID() ]
1450
+			: array();
1451
+		$ticket_subtotal  = $default ? 0 : $ticket->get_ticket_subtotal();
1452
+		$base_price       = $default ? null : $ticket->base_price();
1453
+		$count_price_mods = EEM_Price::instance()->get_all_default_prices(true);
1454
+		//breaking out complicated condition for ticket_status
1455
+		if ($default) {
1456
+			$ticket_status_class = ' tkt-status-' . EE_Ticket::onsale;
1457
+		} else {
1458
+			$ticket_status_class = $ticket->is_default()
1459
+				? ' tkt-status-' . EE_Ticket::onsale
1460
+				: ' tkt-status-' . $ticket->ticket_status();
1461
+		}
1462
+		//breaking out complicated condition for TKT_taxable
1463
+		if ($default) {
1464
+			$TKT_taxable = '';
1465
+		} else {
1466
+			$TKT_taxable = $ticket->taxable()
1467
+				? ' checked="checked"'
1468
+				: '';
1469
+		}
1470
+		if ($default) {
1471
+			$TKT_status = EEH_Template::pretty_status(EE_Ticket::onsale, false, 'sentence');
1472
+		} elseif ($ticket->is_default()) {
1473
+			$TKT_status = EEH_Template::pretty_status(EE_Ticket::onsale, false, 'sentence');
1474
+		} else {
1475
+			$TKT_status = $ticket->ticket_status(true);
1476
+		}
1477
+		if ($default) {
1478
+			$TKT_min = '';
1479
+		} else {
1480
+			$TKT_min = $ticket->min();
1481
+			if ($TKT_min === -1 || $TKT_min === 0) {
1482
+				$TKT_min = '';
1483
+			}
1484
+		}
1485
+		$template_args                 = array(
1486
+			'tkt_row'                       => $default ? 'TICKETNUM' : $ticket_row,
1487
+			'TKT_order'                     => $default ? 'TICKETNUM' : $ticket_row,
1488
+			//on initial page load this will always be the correct order.
1489
+			'tkt_status_class'              => $ticket_status_class,
1490
+			'display_edit_tkt_row'          => ' style="display:none;"',
1491
+			'edit_tkt_expanded'             => '',
1492
+			'edit_tickets_name'             => $default ? 'TICKETNAMEATTR' : 'edit_tickets',
1493
+			'TKT_name'                      => $default ? '' : $ticket->get_f('TKT_name'),
1494
+			'TKT_start_date'                => $default
1495
+				? ''
1496
+				: $ticket->get_date('TKT_start_date', $this->_date_time_format),
1497
+			'TKT_end_date'                  => $default
1498
+				? ''
1499
+				: $ticket->get_date('TKT_end_date', $this->_date_time_format),
1500
+			'TKT_status'                    => $TKT_status,
1501
+			'TKT_price'                     => $default
1502
+				? ''
1503
+				: EEH_Template::format_currency(
1504
+					$ticket->get_ticket_total_with_taxes(),
1505
+					false,
1506
+					false
1507
+				),
1508
+			'TKT_price_code'                => EE_Registry::instance()->CFG->currency->code,
1509
+			'TKT_price_amount'              => $default ? 0 : $ticket_subtotal,
1510
+			'TKT_qty'                       => $default
1511
+				? ''
1512
+				: $ticket->get_pretty('TKT_qty', 'symbol'),
1513
+			'TKT_qty_for_input'             => $default
1514
+				? ''
1515
+				: $ticket->get_pretty('TKT_qty', 'input'),
1516
+			'TKT_uses'                      => $default
1517
+				? ''
1518
+				: $ticket->get_pretty('TKT_uses', 'input'),
1519
+			'TKT_min'                       => $TKT_min,
1520
+			'TKT_max'                       => $default
1521
+				? ''
1522
+				: $ticket->get_pretty('TKT_max', 'input'),
1523
+			'TKT_sold'                      => $default ? 0 : $ticket->tickets_sold('ticket'),
1524
+			'TKT_reserved'                  => $default ? 0 : $ticket->reserved(),
1525
+			'TKT_registrations'             => $default
1526
+				? 0
1527
+				: $ticket->count_registrations(
1528
+					array(
1529
+						array(
1530
+							'STS_ID' => array(
1531
+								'!=',
1532
+								EEM_Registration::status_id_incomplete,
1533
+							),
1534
+						),
1535
+					)
1536
+				),
1537
+			'TKT_ID'                        => $default ? 0 : $ticket->ID(),
1538
+			'TKT_description'               => $default ? '' : $ticket->get_f('TKT_description'),
1539
+			'TKT_is_default'                => $default ? 0 : $ticket->is_default(),
1540
+			'TKT_required'                  => $default ? 0 : $ticket->required(),
1541
+			'TKT_is_default_selector'       => '',
1542
+			'ticket_price_rows'             => '',
1543
+			'TKT_base_price'                => $default || ! $base_price instanceof EE_Price
1544
+				? ''
1545
+				: $base_price->get_pretty('PRC_amount', 'localized_float'),
1546
+			'TKT_base_price_ID'             => $default || ! $base_price instanceof EE_Price ? 0 : $base_price->ID(),
1547
+			'show_price_modifier'           => count($prices) > 1 || ($default && $count_price_mods > 0)
1548
+				? ''
1549
+				: ' style="display:none;"',
1550
+			'show_price_mod_button'         => count($prices) > 1
1551
+											   || ($default && $count_price_mods > 0)
1552
+											   || (! $default && $ticket->deleted())
1553
+				? ' style="display:none;"'
1554
+				: '',
1555
+			'total_price_rows'              => count($prices) > 1 ? count($prices) : 1,
1556
+			'ticket_datetimes_list'         => $default ? '<li class="hidden"></li>' : '',
1557
+			'starting_ticket_datetime_rows' => $default || $default_dtt ? '' : implode(',', $tkt_datetimes),
1558
+			'ticket_datetime_rows'          => $default ? '' : implode(',', $tkt_datetimes),
1559
+			'existing_ticket_price_ids'     => $default ? '' : implode(',', array_keys($prices)),
1560
+			'ticket_template_id'            => $default ? 0 : $ticket->get('TTM_ID'),
1561
+			'TKT_taxable'                   => $TKT_taxable,
1562
+			'display_subtotal'              => $ticket instanceof EE_Ticket && $ticket->taxable()
1563
+				? ''
1564
+				: ' style="display:none"',
1565
+			'price_currency_symbol'         => EE_Registry::instance()->CFG->currency->sign,
1566
+			'TKT_subtotal_amount_display'   => EEH_Template::format_currency(
1567
+				$ticket_subtotal,
1568
+				false,
1569
+				false
1570
+			),
1571
+			'TKT_subtotal_amount'           => $ticket_subtotal,
1572
+			'tax_rows'                      => $this->_get_tax_rows($ticket_row, $ticket),
1573
+			'disabled'                      => $ticket instanceof EE_Ticket && $ticket->deleted(),
1574
+			'ticket_archive_class'          => $ticket instanceof EE_Ticket && $ticket->deleted()
1575
+				? ' ticket-archived'
1576
+				: '',
1577
+			'trash_icon'                    => $ticket instanceof EE_Ticket
1578
+											   && $ticket->deleted()
1579
+											   && ! $ticket->is_permanently_deleteable()
1580
+				? 'ee-lock-icon '
1581
+				: 'trash-icon dashicons dashicons-post-trash clickable',
1582
+			'clone_icon'                    => $ticket instanceof EE_Ticket && $ticket->deleted()
1583
+				? ''
1584
+				: 'clone-icon ee-icon ee-icon-clone clickable',
1585
+		);
1586
+		$template_args['trash_hidden'] = count($all_tickets) === 1 && $template_args['trash_icon'] !== 'ee-lock-icon'
1587
+			? ' style="display:none"'
1588
+			: '';
1589
+		//handle rows that should NOT be empty
1590
+		if (empty($template_args['TKT_start_date'])) {
1591
+			//if empty then the start date will be now.
1592
+			$template_args['TKT_start_date']   = date($this->_date_time_format,
1593
+				current_time('timestamp'));
1594
+			$template_args['tkt_status_class'] = ' tkt-status-' . EE_Ticket::onsale;
1595
+		}
1596
+		if (empty($template_args['TKT_end_date'])) {
1597
+			//get the earliest datetime (if present);
1598
+			$earliest_dtt = $this->_adminpage_obj->get_cpt_model_obj()->ID() > 0
1599
+				? $this->_adminpage_obj->get_cpt_model_obj()->get_first_related(
1600
+					'Datetime',
1601
+					array('order_by' => array('DTT_EVT_start' => 'ASC'))
1602
+				)
1603
+				: null;
1604
+			if (! empty($earliest_dtt)) {
1605
+				$template_args['TKT_end_date'] = $earliest_dtt->get_datetime(
1606
+					'DTT_EVT_start',
1607
+					$this->_date_time_format
1608
+				);
1609
+			} else {
1610
+				//default so let's just use what's been set for the default date-time which is 30 days from now.
1611
+				$template_args['TKT_end_date'] = date(
1612
+					$this->_date_time_format,
1613
+					mktime(
1614
+						24, 0, 0, date('m'), date('d') + 29, date('Y')
1615
+					)
1616
+				);
1617
+			}
1618
+			$template_args['tkt_status_class'] = ' tkt-status-' . EE_Ticket::onsale;
1619
+		}
1620
+		//generate ticket_datetime items
1621
+		if (! $default) {
1622
+			$datetime_row = 1;
1623
+			foreach ($all_datetimes as $datetime) {
1624
+				$template_args['ticket_datetimes_list'] .= $this->_get_ticket_datetime_list_item(
1625
+					$datetime_row,
1626
+					$ticket_row,
1627
+					$datetime,
1628
+					$ticket,
1629
+					$ticket_datetimes,
1630
+					$default
1631
+				);
1632
+				$datetime_row++;
1633
+			}
1634
+		}
1635
+		$price_row = 1;
1636
+		foreach ($prices as $price) {
1637
+			if (! $price instanceof EE_Price) {
1638
+				continue;
1639
+			}
1640
+			if ($price->is_base_price()) {
1641
+				$price_row++;
1642
+				continue;
1643
+			}
1644
+			$show_trash                         = ! ((count($prices) > 1 && $price_row === 1) || count($prices) === 1);
1645
+			$show_create                        = ! (count($prices) > 1 && count($prices) !== $price_row);
1646
+			$template_args['ticket_price_rows'] .= $this->_get_ticket_price_row(
1647
+				$ticket_row,
1648
+				$price_row,
1649
+				$price,
1650
+				$default,
1651
+				$ticket,
1652
+				$show_trash,
1653
+				$show_create
1654
+			);
1655
+			$price_row++;
1656
+		}
1657
+		//filter $template_args
1658
+		$template_args = apply_filters(
1659
+			'FHEE__espresso_events_Pricing_Hooks___get_ticket_row__template_args',
1660
+			$template_args,
1661
+			$ticket_row,
1662
+			$ticket,
1663
+			$ticket_datetimes,
1664
+			$all_datetimes,
1665
+			$default,
1666
+			$all_tickets,
1667
+			$this->_is_creating_event
1668
+		);
1669
+		return EEH_Template::display_template(
1670
+			PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_row.template.php',
1671
+			$template_args,
1672
+			true
1673
+		);
1674
+	}
1675 1675
 
1676 1676
 
1677
-    /**
1678
-     * @param int            $ticket_row
1679
-     * @param EE_Ticket|null $ticket
1680
-     * @return string
1681
-     * @throws DomainException
1682
-     * @throws EE_Error
1683
-     */
1684
-    protected function _get_tax_rows($ticket_row, $ticket)
1685
-    {
1686
-        $tax_rows = '';
1687
-        /** @var EE_Price[] $taxes */
1688
-        $taxes = empty($ticket) ? EE_Taxes::get_taxes_for_admin() : $ticket->get_ticket_taxes_for_admin();
1689
-        foreach ($taxes as $tax) {
1690
-            $tax_added     = $this->_get_tax_added($tax, $ticket);
1691
-            $template_args = array(
1692
-                'display_tax'       => ! empty($ticket) && $ticket->get('TKT_taxable')
1693
-                    ? ''
1694
-                    : ' style="display:none;"',
1695
-                'tax_id'            => $tax->ID(),
1696
-                'tkt_row'           => $ticket_row,
1697
-                'tax_label'         => $tax->get('PRC_name'),
1698
-                'tax_added'         => $tax_added,
1699
-                'tax_added_display' => EEH_Template::format_currency($tax_added, false, false),
1700
-                'tax_amount'        => $tax->get('PRC_amount'),
1701
-            );
1702
-            $template_args = apply_filters(
1703
-                'FHEE__espresso_events_Pricing_Hooks___get_tax_rows__template_args',
1704
-                $template_args,
1705
-                $ticket_row,
1706
-                $ticket,
1707
-                $this->_is_creating_event
1708
-            );
1709
-            $tax_rows      .= EEH_Template::display_template(
1710
-                PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_tax_row.template.php',
1711
-                $template_args,
1712
-                true
1713
-            );
1714
-        }
1715
-        return $tax_rows;
1716
-    }
1677
+	/**
1678
+	 * @param int            $ticket_row
1679
+	 * @param EE_Ticket|null $ticket
1680
+	 * @return string
1681
+	 * @throws DomainException
1682
+	 * @throws EE_Error
1683
+	 */
1684
+	protected function _get_tax_rows($ticket_row, $ticket)
1685
+	{
1686
+		$tax_rows = '';
1687
+		/** @var EE_Price[] $taxes */
1688
+		$taxes = empty($ticket) ? EE_Taxes::get_taxes_for_admin() : $ticket->get_ticket_taxes_for_admin();
1689
+		foreach ($taxes as $tax) {
1690
+			$tax_added     = $this->_get_tax_added($tax, $ticket);
1691
+			$template_args = array(
1692
+				'display_tax'       => ! empty($ticket) && $ticket->get('TKT_taxable')
1693
+					? ''
1694
+					: ' style="display:none;"',
1695
+				'tax_id'            => $tax->ID(),
1696
+				'tkt_row'           => $ticket_row,
1697
+				'tax_label'         => $tax->get('PRC_name'),
1698
+				'tax_added'         => $tax_added,
1699
+				'tax_added_display' => EEH_Template::format_currency($tax_added, false, false),
1700
+				'tax_amount'        => $tax->get('PRC_amount'),
1701
+			);
1702
+			$template_args = apply_filters(
1703
+				'FHEE__espresso_events_Pricing_Hooks___get_tax_rows__template_args',
1704
+				$template_args,
1705
+				$ticket_row,
1706
+				$ticket,
1707
+				$this->_is_creating_event
1708
+			);
1709
+			$tax_rows      .= EEH_Template::display_template(
1710
+				PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_tax_row.template.php',
1711
+				$template_args,
1712
+				true
1713
+			);
1714
+		}
1715
+		return $tax_rows;
1716
+	}
1717 1717
 
1718 1718
 
1719
-    /**
1720
-     * @param EE_Price       $tax
1721
-     * @param EE_Ticket|null $ticket
1722
-     * @return float|int
1723
-     * @throws EE_Error
1724
-     */
1725
-    protected function _get_tax_added(EE_Price $tax, $ticket)
1726
-    {
1727
-        $subtotal = empty($ticket) ? 0 : $ticket->get_ticket_subtotal();
1728
-        return $subtotal * $tax->get('PRC_amount') / 100;
1729
-    }
1719
+	/**
1720
+	 * @param EE_Price       $tax
1721
+	 * @param EE_Ticket|null $ticket
1722
+	 * @return float|int
1723
+	 * @throws EE_Error
1724
+	 */
1725
+	protected function _get_tax_added(EE_Price $tax, $ticket)
1726
+	{
1727
+		$subtotal = empty($ticket) ? 0 : $ticket->get_ticket_subtotal();
1728
+		return $subtotal * $tax->get('PRC_amount') / 100;
1729
+	}
1730 1730
 
1731 1731
 
1732
-    /**
1733
-     * @param int            $ticket_row
1734
-     * @param int            $price_row
1735
-     * @param EE_Price|null  $price
1736
-     * @param bool           $default
1737
-     * @param EE_Ticket|null $ticket
1738
-     * @param bool           $show_trash
1739
-     * @param bool           $show_create
1740
-     * @return mixed
1741
-     * @throws InvalidArgumentException
1742
-     * @throws InvalidInterfaceException
1743
-     * @throws InvalidDataTypeException
1744
-     * @throws DomainException
1745
-     * @throws EE_Error
1746
-     * @throws ReflectionException
1747
-     */
1748
-    protected function _get_ticket_price_row(
1749
-        $ticket_row,
1750
-        $price_row,
1751
-        $price,
1752
-        $default,
1753
-        $ticket,
1754
-        $show_trash = true,
1755
-        $show_create = true
1756
-    ) {
1757
-        $send_disabled = ! empty($ticket) && $ticket->get('TKT_deleted');
1758
-        $template_args = array(
1759
-            'tkt_row'               => $default && empty($ticket)
1760
-                ? 'TICKETNUM'
1761
-                : $ticket_row,
1762
-            'PRC_order'             => $default && empty($price)
1763
-                ? 'PRICENUM'
1764
-                : $price_row,
1765
-            'edit_prices_name'      => $default && empty($price)
1766
-                ? 'PRICENAMEATTR'
1767
-                : 'edit_prices',
1768
-            'price_type_selector'   => $default && empty($price)
1769
-                ? $this->_get_base_price_template($ticket_row, $price_row, $price, $default)
1770
-                : $this->_get_price_type_selector(
1771
-                    $ticket_row,
1772
-                    $price_row,
1773
-                    $price,
1774
-                    $default,
1775
-                    $send_disabled
1776
-                ),
1777
-            'PRC_ID'                => $default && empty($price)
1778
-                ? 0
1779
-                : $price->ID(),
1780
-            'PRC_is_default'        => $default && empty($price)
1781
-                ? 0
1782
-                : $price->get('PRC_is_default'),
1783
-            'PRC_name'              => $default && empty($price)
1784
-                ? ''
1785
-                : $price->get('PRC_name'),
1786
-            'price_currency_symbol' => EE_Registry::instance()->CFG->currency->sign,
1787
-            'show_plus_or_minus'    => $default && empty($price)
1788
-                ? ''
1789
-                : ' style="display:none;"',
1790
-            'show_plus'             => ($default && empty($price)) || ($price->is_discount() || $price->is_base_price())
1791
-                ? ' style="display:none;"'
1792
-                : '',
1793
-            'show_minus'            => ($default && empty($price)) || ! $price->is_discount()
1794
-                ? ' style="display:none;"'
1795
-                : '',
1796
-            'show_currency_symbol'  => ($default && empty($price)) || $price->is_percent()
1797
-                ? ' style="display:none"'
1798
-                : '',
1799
-            'PRC_amount'            => $default && empty($price)
1800
-                ? 0
1801
-                : $price->get_pretty('PRC_amount',
1802
-                    'localized_float'),
1803
-            'show_percentage'       => ($default && empty($price)) || ! $price->is_percent()
1804
-                ? ' style="display:none;"'
1805
-                : '',
1806
-            'show_trash_icon'       => $show_trash
1807
-                ? ''
1808
-                : ' style="display:none;"',
1809
-            'show_create_button'    => $show_create
1810
-                ? ''
1811
-                : ' style="display:none;"',
1812
-            'PRC_desc'              => $default && empty($price)
1813
-                ? ''
1814
-                : $price->get('PRC_desc'),
1815
-            'disabled'              => ! empty($ticket) && $ticket->get('TKT_deleted'),
1816
-        );
1817
-        $template_args = apply_filters(
1818
-            'FHEE__espresso_events_Pricing_Hooks___get_ticket_price_row__template_args',
1819
-            $template_args,
1820
-            $ticket_row,
1821
-            $price_row,
1822
-            $price,
1823
-            $default,
1824
-            $ticket,
1825
-            $show_trash,
1826
-            $show_create,
1827
-            $this->_is_creating_event
1828
-        );
1829
-        return EEH_Template::display_template(
1830
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_price_row.template.php',
1831
-            $template_args,
1832
-            true
1833
-        );
1834
-    }
1732
+	/**
1733
+	 * @param int            $ticket_row
1734
+	 * @param int            $price_row
1735
+	 * @param EE_Price|null  $price
1736
+	 * @param bool           $default
1737
+	 * @param EE_Ticket|null $ticket
1738
+	 * @param bool           $show_trash
1739
+	 * @param bool           $show_create
1740
+	 * @return mixed
1741
+	 * @throws InvalidArgumentException
1742
+	 * @throws InvalidInterfaceException
1743
+	 * @throws InvalidDataTypeException
1744
+	 * @throws DomainException
1745
+	 * @throws EE_Error
1746
+	 * @throws ReflectionException
1747
+	 */
1748
+	protected function _get_ticket_price_row(
1749
+		$ticket_row,
1750
+		$price_row,
1751
+		$price,
1752
+		$default,
1753
+		$ticket,
1754
+		$show_trash = true,
1755
+		$show_create = true
1756
+	) {
1757
+		$send_disabled = ! empty($ticket) && $ticket->get('TKT_deleted');
1758
+		$template_args = array(
1759
+			'tkt_row'               => $default && empty($ticket)
1760
+				? 'TICKETNUM'
1761
+				: $ticket_row,
1762
+			'PRC_order'             => $default && empty($price)
1763
+				? 'PRICENUM'
1764
+				: $price_row,
1765
+			'edit_prices_name'      => $default && empty($price)
1766
+				? 'PRICENAMEATTR'
1767
+				: 'edit_prices',
1768
+			'price_type_selector'   => $default && empty($price)
1769
+				? $this->_get_base_price_template($ticket_row, $price_row, $price, $default)
1770
+				: $this->_get_price_type_selector(
1771
+					$ticket_row,
1772
+					$price_row,
1773
+					$price,
1774
+					$default,
1775
+					$send_disabled
1776
+				),
1777
+			'PRC_ID'                => $default && empty($price)
1778
+				? 0
1779
+				: $price->ID(),
1780
+			'PRC_is_default'        => $default && empty($price)
1781
+				? 0
1782
+				: $price->get('PRC_is_default'),
1783
+			'PRC_name'              => $default && empty($price)
1784
+				? ''
1785
+				: $price->get('PRC_name'),
1786
+			'price_currency_symbol' => EE_Registry::instance()->CFG->currency->sign,
1787
+			'show_plus_or_minus'    => $default && empty($price)
1788
+				? ''
1789
+				: ' style="display:none;"',
1790
+			'show_plus'             => ($default && empty($price)) || ($price->is_discount() || $price->is_base_price())
1791
+				? ' style="display:none;"'
1792
+				: '',
1793
+			'show_minus'            => ($default && empty($price)) || ! $price->is_discount()
1794
+				? ' style="display:none;"'
1795
+				: '',
1796
+			'show_currency_symbol'  => ($default && empty($price)) || $price->is_percent()
1797
+				? ' style="display:none"'
1798
+				: '',
1799
+			'PRC_amount'            => $default && empty($price)
1800
+				? 0
1801
+				: $price->get_pretty('PRC_amount',
1802
+					'localized_float'),
1803
+			'show_percentage'       => ($default && empty($price)) || ! $price->is_percent()
1804
+				? ' style="display:none;"'
1805
+				: '',
1806
+			'show_trash_icon'       => $show_trash
1807
+				? ''
1808
+				: ' style="display:none;"',
1809
+			'show_create_button'    => $show_create
1810
+				? ''
1811
+				: ' style="display:none;"',
1812
+			'PRC_desc'              => $default && empty($price)
1813
+				? ''
1814
+				: $price->get('PRC_desc'),
1815
+			'disabled'              => ! empty($ticket) && $ticket->get('TKT_deleted'),
1816
+		);
1817
+		$template_args = apply_filters(
1818
+			'FHEE__espresso_events_Pricing_Hooks___get_ticket_price_row__template_args',
1819
+			$template_args,
1820
+			$ticket_row,
1821
+			$price_row,
1822
+			$price,
1823
+			$default,
1824
+			$ticket,
1825
+			$show_trash,
1826
+			$show_create,
1827
+			$this->_is_creating_event
1828
+		);
1829
+		return EEH_Template::display_template(
1830
+			PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_price_row.template.php',
1831
+			$template_args,
1832
+			true
1833
+		);
1834
+	}
1835 1835
 
1836 1836
 
1837
-    /**
1838
-     * @param int      $ticket_row
1839
-     * @param int      $price_row
1840
-     * @param EE_Price $price
1841
-     * @param bool     $default
1842
-     * @param bool     $disabled
1843
-     * @return mixed
1844
-     * @throws ReflectionException
1845
-     * @throws InvalidArgumentException
1846
-     * @throws InvalidInterfaceException
1847
-     * @throws InvalidDataTypeException
1848
-     * @throws DomainException
1849
-     * @throws EE_Error
1850
-     */
1851
-    protected function _get_price_type_selector($ticket_row, $price_row, $price, $default, $disabled = false)
1852
-    {
1853
-        if ($price->is_base_price()) {
1854
-            return $this->_get_base_price_template(
1855
-                $ticket_row,
1856
-                $price_row,
1857
-                $price,
1858
-                $default
1859
-            );
1860
-        }
1861
-        return $this->_get_price_modifier_template(
1862
-            $ticket_row,
1863
-            $price_row,
1864
-            $price,
1865
-            $default,
1866
-            $disabled
1867
-        );
1868
-    }
1837
+	/**
1838
+	 * @param int      $ticket_row
1839
+	 * @param int      $price_row
1840
+	 * @param EE_Price $price
1841
+	 * @param bool     $default
1842
+	 * @param bool     $disabled
1843
+	 * @return mixed
1844
+	 * @throws ReflectionException
1845
+	 * @throws InvalidArgumentException
1846
+	 * @throws InvalidInterfaceException
1847
+	 * @throws InvalidDataTypeException
1848
+	 * @throws DomainException
1849
+	 * @throws EE_Error
1850
+	 */
1851
+	protected function _get_price_type_selector($ticket_row, $price_row, $price, $default, $disabled = false)
1852
+	{
1853
+		if ($price->is_base_price()) {
1854
+			return $this->_get_base_price_template(
1855
+				$ticket_row,
1856
+				$price_row,
1857
+				$price,
1858
+				$default
1859
+			);
1860
+		}
1861
+		return $this->_get_price_modifier_template(
1862
+			$ticket_row,
1863
+			$price_row,
1864
+			$price,
1865
+			$default,
1866
+			$disabled
1867
+		);
1868
+	}
1869 1869
 
1870 1870
 
1871
-    /**
1872
-     * @param int      $ticket_row
1873
-     * @param int      $price_row
1874
-     * @param EE_Price $price
1875
-     * @param bool     $default
1876
-     * @return mixed
1877
-     * @throws DomainException
1878
-     * @throws EE_Error
1879
-     */
1880
-    protected function _get_base_price_template($ticket_row, $price_row, $price, $default)
1881
-    {
1882
-        $template_args = array(
1883
-            'tkt_row'                   => $default ? 'TICKETNUM' : $ticket_row,
1884
-            'PRC_order'                 => $default && empty($price) ? 'PRICENUM' : $price_row,
1885
-            'PRT_ID'                    => $default && empty($price) ? 1 : $price->get('PRT_ID'),
1886
-            'PRT_name'                  => esc_html__('Price', 'event_espresso'),
1887
-            'price_selected_operator'   => '+',
1888
-            'price_selected_is_percent' => 0,
1889
-        );
1890
-        $template_args = apply_filters(
1891
-            'FHEE__espresso_events_Pricing_Hooks___get_base_price_template__template_args',
1892
-            $template_args,
1893
-            $ticket_row,
1894
-            $price_row,
1895
-            $price,
1896
-            $default,
1897
-            $this->_is_creating_event
1898
-        );
1899
-        return EEH_Template::display_template(
1900
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_price_type_base.template.php',
1901
-            $template_args,
1902
-            true
1903
-        );
1904
-    }
1871
+	/**
1872
+	 * @param int      $ticket_row
1873
+	 * @param int      $price_row
1874
+	 * @param EE_Price $price
1875
+	 * @param bool     $default
1876
+	 * @return mixed
1877
+	 * @throws DomainException
1878
+	 * @throws EE_Error
1879
+	 */
1880
+	protected function _get_base_price_template($ticket_row, $price_row, $price, $default)
1881
+	{
1882
+		$template_args = array(
1883
+			'tkt_row'                   => $default ? 'TICKETNUM' : $ticket_row,
1884
+			'PRC_order'                 => $default && empty($price) ? 'PRICENUM' : $price_row,
1885
+			'PRT_ID'                    => $default && empty($price) ? 1 : $price->get('PRT_ID'),
1886
+			'PRT_name'                  => esc_html__('Price', 'event_espresso'),
1887
+			'price_selected_operator'   => '+',
1888
+			'price_selected_is_percent' => 0,
1889
+		);
1890
+		$template_args = apply_filters(
1891
+			'FHEE__espresso_events_Pricing_Hooks___get_base_price_template__template_args',
1892
+			$template_args,
1893
+			$ticket_row,
1894
+			$price_row,
1895
+			$price,
1896
+			$default,
1897
+			$this->_is_creating_event
1898
+		);
1899
+		return EEH_Template::display_template(
1900
+			PRICING_TEMPLATE_PATH . 'event_tickets_datetime_price_type_base.template.php',
1901
+			$template_args,
1902
+			true
1903
+		);
1904
+	}
1905 1905
 
1906 1906
 
1907
-    /**
1908
-     * @param int      $ticket_row
1909
-     * @param int      $price_row
1910
-     * @param EE_Price $price
1911
-     * @param bool     $default
1912
-     * @param bool     $disabled
1913
-     * @return mixed
1914
-     * @throws ReflectionException
1915
-     * @throws InvalidArgumentException
1916
-     * @throws InvalidInterfaceException
1917
-     * @throws InvalidDataTypeException
1918
-     * @throws DomainException
1919
-     * @throws EE_Error
1920
-     */
1921
-    protected function _get_price_modifier_template(
1922
-        $ticket_row,
1923
-        $price_row,
1924
-        $price,
1925
-        $default,
1926
-        $disabled = false
1927
-    ) {
1928
-        $select_name = $default && ! $price instanceof EE_Price
1929
-            ? 'edit_prices[TICKETNUM][PRICENUM][PRT_ID]'
1930
-            : 'edit_prices[' . $ticket_row . '][' . $price_row . '][PRT_ID]';
1931
-        /** @var EEM_Price_Type $price_type_model */
1932
-        $price_type_model       = EE_Registry::instance()->load_model('Price_Type');
1933
-        $price_types            = $price_type_model->get_all(array(
1934
-            array(
1935
-                'OR' => array(
1936
-                    'PBT_ID'  => '2',
1937
-                    'PBT_ID*' => '3',
1938
-                ),
1939
-            ),
1940
-        ));
1941
-        $all_price_types        = $default && ! $price instanceof EE_Price
1942
-            ? array(esc_html__('Select Modifier', 'event_espresso'))
1943
-            : array();
1944
-        $selected_price_type_id = $default && ! $price instanceof EE_Price ? 0 : $price->type();
1945
-        $price_option_spans     = '';
1946
-        //setup price types for selector
1947
-        foreach ($price_types as $price_type) {
1948
-            if (! $price_type instanceof EE_Price_Type) {
1949
-                continue;
1950
-            }
1951
-            $all_price_types[ $price_type->ID() ] = $price_type->get('PRT_name');
1952
-            //while we're in the loop let's setup the option spans used by js
1953
-            $span_args          = array(
1954
-                'PRT_ID'         => $price_type->ID(),
1955
-                'PRT_operator'   => $price_type->is_discount() ? '-' : '+',
1956
-                'PRT_is_percent' => $price_type->get('PRT_is_percent') ? 1 : 0,
1957
-            );
1958
-            $price_option_spans .= EEH_Template::display_template(
1959
-                PRICING_TEMPLATE_PATH . 'event_tickets_datetime_price_option_span.template.php',
1960
-                $span_args,
1961
-                true
1962
-            );
1963
-        }
1964
-        $select_name               = $disabled ? 'archive_price[' . $ticket_row . '][' . $price_row . '][PRT_ID]'
1965
-            : $select_name;
1966
-        $select_input              = new EE_Select_Input(
1967
-            $all_price_types,
1968
-            array(
1969
-                'default'               => $selected_price_type_id,
1970
-                'html_name'             => $select_name,
1971
-                'html_class'            => 'edit-price-PRT_ID',
1972
-                'html_other_attributes' => $disabled ? 'style="width:auto;" disabled' : 'style="width:auto;"',
1973
-            )
1974
-        );
1975
-        $price_selected_operator   = $price instanceof EE_Price && $price->is_discount() ? '-' : '+';
1976
-        $price_selected_operator   = $default && ! $price instanceof EE_Price ? '' : $price_selected_operator;
1977
-        $price_selected_is_percent = $price instanceof EE_Price && $price->is_percent() ? 1 : 0;
1978
-        $price_selected_is_percent = $default && ! $price instanceof EE_Price ? '' : $price_selected_is_percent;
1979
-        $template_args             = array(
1980
-            'tkt_row'                   => $default ? 'TICKETNUM' : $ticket_row,
1981
-            'PRC_order'                 => $default && ! $price instanceof EE_Price ? 'PRICENUM' : $price_row,
1982
-            'price_modifier_selector'   => $select_input->get_html_for_input(),
1983
-            'main_name'                 => $select_name,
1984
-            'selected_price_type_id'    => $selected_price_type_id,
1985
-            'price_option_spans'        => $price_option_spans,
1986
-            'price_selected_operator'   => $price_selected_operator,
1987
-            'price_selected_is_percent' => $price_selected_is_percent,
1988
-            'disabled'                  => $disabled,
1989
-        );
1990
-        $template_args             = apply_filters(
1991
-            'FHEE__espresso_events_Pricing_Hooks___get_price_modifier_template__template_args',
1992
-            $template_args,
1993
-            $ticket_row,
1994
-            $price_row,
1995
-            $price,
1996
-            $default,
1997
-            $disabled,
1998
-            $this->_is_creating_event
1999
-        );
2000
-        return EEH_Template::display_template(
2001
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_price_modifier_selector.template.php',
2002
-            $template_args,
2003
-            true
2004
-        );
2005
-    }
1907
+	/**
1908
+	 * @param int      $ticket_row
1909
+	 * @param int      $price_row
1910
+	 * @param EE_Price $price
1911
+	 * @param bool     $default
1912
+	 * @param bool     $disabled
1913
+	 * @return mixed
1914
+	 * @throws ReflectionException
1915
+	 * @throws InvalidArgumentException
1916
+	 * @throws InvalidInterfaceException
1917
+	 * @throws InvalidDataTypeException
1918
+	 * @throws DomainException
1919
+	 * @throws EE_Error
1920
+	 */
1921
+	protected function _get_price_modifier_template(
1922
+		$ticket_row,
1923
+		$price_row,
1924
+		$price,
1925
+		$default,
1926
+		$disabled = false
1927
+	) {
1928
+		$select_name = $default && ! $price instanceof EE_Price
1929
+			? 'edit_prices[TICKETNUM][PRICENUM][PRT_ID]'
1930
+			: 'edit_prices[' . $ticket_row . '][' . $price_row . '][PRT_ID]';
1931
+		/** @var EEM_Price_Type $price_type_model */
1932
+		$price_type_model       = EE_Registry::instance()->load_model('Price_Type');
1933
+		$price_types            = $price_type_model->get_all(array(
1934
+			array(
1935
+				'OR' => array(
1936
+					'PBT_ID'  => '2',
1937
+					'PBT_ID*' => '3',
1938
+				),
1939
+			),
1940
+		));
1941
+		$all_price_types        = $default && ! $price instanceof EE_Price
1942
+			? array(esc_html__('Select Modifier', 'event_espresso'))
1943
+			: array();
1944
+		$selected_price_type_id = $default && ! $price instanceof EE_Price ? 0 : $price->type();
1945
+		$price_option_spans     = '';
1946
+		//setup price types for selector
1947
+		foreach ($price_types as $price_type) {
1948
+			if (! $price_type instanceof EE_Price_Type) {
1949
+				continue;
1950
+			}
1951
+			$all_price_types[ $price_type->ID() ] = $price_type->get('PRT_name');
1952
+			//while we're in the loop let's setup the option spans used by js
1953
+			$span_args          = array(
1954
+				'PRT_ID'         => $price_type->ID(),
1955
+				'PRT_operator'   => $price_type->is_discount() ? '-' : '+',
1956
+				'PRT_is_percent' => $price_type->get('PRT_is_percent') ? 1 : 0,
1957
+			);
1958
+			$price_option_spans .= EEH_Template::display_template(
1959
+				PRICING_TEMPLATE_PATH . 'event_tickets_datetime_price_option_span.template.php',
1960
+				$span_args,
1961
+				true
1962
+			);
1963
+		}
1964
+		$select_name               = $disabled ? 'archive_price[' . $ticket_row . '][' . $price_row . '][PRT_ID]'
1965
+			: $select_name;
1966
+		$select_input              = new EE_Select_Input(
1967
+			$all_price_types,
1968
+			array(
1969
+				'default'               => $selected_price_type_id,
1970
+				'html_name'             => $select_name,
1971
+				'html_class'            => 'edit-price-PRT_ID',
1972
+				'html_other_attributes' => $disabled ? 'style="width:auto;" disabled' : 'style="width:auto;"',
1973
+			)
1974
+		);
1975
+		$price_selected_operator   = $price instanceof EE_Price && $price->is_discount() ? '-' : '+';
1976
+		$price_selected_operator   = $default && ! $price instanceof EE_Price ? '' : $price_selected_operator;
1977
+		$price_selected_is_percent = $price instanceof EE_Price && $price->is_percent() ? 1 : 0;
1978
+		$price_selected_is_percent = $default && ! $price instanceof EE_Price ? '' : $price_selected_is_percent;
1979
+		$template_args             = array(
1980
+			'tkt_row'                   => $default ? 'TICKETNUM' : $ticket_row,
1981
+			'PRC_order'                 => $default && ! $price instanceof EE_Price ? 'PRICENUM' : $price_row,
1982
+			'price_modifier_selector'   => $select_input->get_html_for_input(),
1983
+			'main_name'                 => $select_name,
1984
+			'selected_price_type_id'    => $selected_price_type_id,
1985
+			'price_option_spans'        => $price_option_spans,
1986
+			'price_selected_operator'   => $price_selected_operator,
1987
+			'price_selected_is_percent' => $price_selected_is_percent,
1988
+			'disabled'                  => $disabled,
1989
+		);
1990
+		$template_args             = apply_filters(
1991
+			'FHEE__espresso_events_Pricing_Hooks___get_price_modifier_template__template_args',
1992
+			$template_args,
1993
+			$ticket_row,
1994
+			$price_row,
1995
+			$price,
1996
+			$default,
1997
+			$disabled,
1998
+			$this->_is_creating_event
1999
+		);
2000
+		return EEH_Template::display_template(
2001
+			PRICING_TEMPLATE_PATH . 'event_tickets_datetime_price_modifier_selector.template.php',
2002
+			$template_args,
2003
+			true
2004
+		);
2005
+	}
2006 2006
 
2007 2007
 
2008
-    /**
2009
-     * @param int              $datetime_row
2010
-     * @param int              $ticket_row
2011
-     * @param EE_Datetime|null $datetime
2012
-     * @param EE_Ticket|null   $ticket
2013
-     * @param array            $ticket_datetimes
2014
-     * @param bool             $default
2015
-     * @return mixed
2016
-     * @throws DomainException
2017
-     * @throws EE_Error
2018
-     */
2019
-    protected function _get_ticket_datetime_list_item(
2020
-        $datetime_row,
2021
-        $ticket_row,
2022
-        $datetime,
2023
-        $ticket,
2024
-        $ticket_datetimes = array(),
2025
-        $default
2026
-    ) {
2027
-        $tkt_datetimes = $ticket instanceof EE_Ticket && isset($ticket_datetimes[ $ticket->ID() ])
2028
-            ? $ticket_datetimes[ $ticket->ID() ]
2029
-            : array();
2030
-        $template_args = array(
2031
-            'dtt_row'                  => $default && ! $datetime instanceof EE_Datetime
2032
-                ? 'DTTNUM'
2033
-                : $datetime_row,
2034
-            'tkt_row'                  => $default
2035
-                ? 'TICKETNUM'
2036
-                : $ticket_row,
2037
-            'ticket_datetime_selected' => in_array($datetime_row, $tkt_datetimes, true)
2038
-                ? ' ticket-selected'
2039
-                : '',
2040
-            'ticket_datetime_checked'  => in_array($datetime_row, $tkt_datetimes, true)
2041
-                ? ' checked="checked"'
2042
-                : '',
2043
-            'DTT_name'                 => $default && empty($datetime)
2044
-                ? 'DTTNAME'
2045
-                : $datetime->get_dtt_display_name(true),
2046
-            'tkt_status_class'         => '',
2047
-        );
2048
-        $template_args = apply_filters(
2049
-            'FHEE__espresso_events_Pricing_Hooks___get_ticket_datetime_list_item__template_args',
2050
-            $template_args,
2051
-            $datetime_row,
2052
-            $ticket_row,
2053
-            $datetime,
2054
-            $ticket,
2055
-            $ticket_datetimes,
2056
-            $default,
2057
-            $this->_is_creating_event
2058
-        );
2059
-        return EEH_Template::display_template(
2060
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_datetimes_list_item.template.php',
2061
-            $template_args,
2062
-            true
2063
-        );
2064
-    }
2008
+	/**
2009
+	 * @param int              $datetime_row
2010
+	 * @param int              $ticket_row
2011
+	 * @param EE_Datetime|null $datetime
2012
+	 * @param EE_Ticket|null   $ticket
2013
+	 * @param array            $ticket_datetimes
2014
+	 * @param bool             $default
2015
+	 * @return mixed
2016
+	 * @throws DomainException
2017
+	 * @throws EE_Error
2018
+	 */
2019
+	protected function _get_ticket_datetime_list_item(
2020
+		$datetime_row,
2021
+		$ticket_row,
2022
+		$datetime,
2023
+		$ticket,
2024
+		$ticket_datetimes = array(),
2025
+		$default
2026
+	) {
2027
+		$tkt_datetimes = $ticket instanceof EE_Ticket && isset($ticket_datetimes[ $ticket->ID() ])
2028
+			? $ticket_datetimes[ $ticket->ID() ]
2029
+			: array();
2030
+		$template_args = array(
2031
+			'dtt_row'                  => $default && ! $datetime instanceof EE_Datetime
2032
+				? 'DTTNUM'
2033
+				: $datetime_row,
2034
+			'tkt_row'                  => $default
2035
+				? 'TICKETNUM'
2036
+				: $ticket_row,
2037
+			'ticket_datetime_selected' => in_array($datetime_row, $tkt_datetimes, true)
2038
+				? ' ticket-selected'
2039
+				: '',
2040
+			'ticket_datetime_checked'  => in_array($datetime_row, $tkt_datetimes, true)
2041
+				? ' checked="checked"'
2042
+				: '',
2043
+			'DTT_name'                 => $default && empty($datetime)
2044
+				? 'DTTNAME'
2045
+				: $datetime->get_dtt_display_name(true),
2046
+			'tkt_status_class'         => '',
2047
+		);
2048
+		$template_args = apply_filters(
2049
+			'FHEE__espresso_events_Pricing_Hooks___get_ticket_datetime_list_item__template_args',
2050
+			$template_args,
2051
+			$datetime_row,
2052
+			$ticket_row,
2053
+			$datetime,
2054
+			$ticket,
2055
+			$ticket_datetimes,
2056
+			$default,
2057
+			$this->_is_creating_event
2058
+		);
2059
+		return EEH_Template::display_template(
2060
+			PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_datetimes_list_item.template.php',
2061
+			$template_args,
2062
+			true
2063
+		);
2064
+	}
2065 2065
 
2066 2066
 
2067
-    /**
2068
-     * @param array $all_datetimes
2069
-     * @param array $all_tickets
2070
-     * @return mixed
2071
-     * @throws ReflectionException
2072
-     * @throws InvalidArgumentException
2073
-     * @throws InvalidInterfaceException
2074
-     * @throws InvalidDataTypeException
2075
-     * @throws DomainException
2076
-     * @throws EE_Error
2077
-     */
2078
-    protected function _get_ticket_js_structure($all_datetimes = array(), $all_tickets = array())
2079
-    {
2080
-        $template_args = array(
2081
-            'default_datetime_edit_row'                => $this->_get_dtt_edit_row(
2082
-                'DTTNUM',
2083
-                null,
2084
-                true,
2085
-                $all_datetimes
2086
-            ),
2087
-            'default_ticket_row'                       => $this->_get_ticket_row(
2088
-                'TICKETNUM',
2089
-                null,
2090
-                array(),
2091
-                array(),
2092
-                true
2093
-            ),
2094
-            'default_price_row'                        => $this->_get_ticket_price_row(
2095
-                'TICKETNUM',
2096
-                'PRICENUM',
2097
-                null,
2098
-                true,
2099
-                null
2100
-            ),
2101
-            'default_price_rows'                       => '',
2102
-            'default_base_price_amount'                => 0,
2103
-            'default_base_price_name'                  => '',
2104
-            'default_base_price_description'           => '',
2105
-            'default_price_modifier_selector_row'      => $this->_get_price_modifier_template(
2106
-                'TICKETNUM',
2107
-                'PRICENUM',
2108
-                null,
2109
-                true
2110
-            ),
2111
-            'default_available_tickets_for_datetime'   => $this->_get_dtt_attached_tickets_row(
2112
-                'DTTNUM',
2113
-                null,
2114
-                array(),
2115
-                array(),
2116
-                true
2117
-            ),
2118
-            'existing_available_datetime_tickets_list' => '',
2119
-            'existing_available_ticket_datetimes_list' => '',
2120
-            'new_available_datetime_ticket_list_item'  => $this->_get_datetime_tickets_list_item(
2121
-                'DTTNUM',
2122
-                'TICKETNUM',
2123
-                null,
2124
-                null,
2125
-                array(),
2126
-                true
2127
-            ),
2128
-            'new_available_ticket_datetime_list_item'  => $this->_get_ticket_datetime_list_item(
2129
-                'DTTNUM',
2130
-                'TICKETNUM',
2131
-                null,
2132
-                null,
2133
-                array(),
2134
-                true
2135
-            ),
2136
-        );
2137
-        $ticket_row    = 1;
2138
-        foreach ($all_tickets as $ticket) {
2139
-            $template_args['existing_available_datetime_tickets_list'] .= $this->_get_datetime_tickets_list_item(
2140
-                'DTTNUM',
2141
-                $ticket_row,
2142
-                null,
2143
-                $ticket,
2144
-                array(),
2145
-                true
2146
-            );
2147
-            $ticket_row++;
2148
-        }
2149
-        $datetime_row = 1;
2150
-        foreach ($all_datetimes as $datetime) {
2151
-            $template_args['existing_available_ticket_datetimes_list'] .= $this->_get_ticket_datetime_list_item(
2152
-                $datetime_row,
2153
-                'TICKETNUM',
2154
-                $datetime,
2155
-                null,
2156
-                array(),
2157
-                true
2158
-            );
2159
-            $datetime_row++;
2160
-        }
2161
-        /** @var EEM_Price $price_model */
2162
-        $price_model    = EE_Registry::instance()->load_model('Price');
2163
-        $default_prices = $price_model->get_all_default_prices();
2164
-        $price_row      = 1;
2165
-        foreach ($default_prices as $price) {
2166
-            if (! $price instanceof EE_Price) {
2167
-                continue;
2168
-            }
2169
-            if ($price->is_base_price()) {
2170
-                $template_args['default_base_price_amount']      = $price->get_pretty(
2171
-                    'PRC_amount',
2172
-                    'localized_float'
2173
-                );
2174
-                $template_args['default_base_price_name']        = $price->get('PRC_name');
2175
-                $template_args['default_base_price_description'] = $price->get('PRC_desc');
2176
-                $price_row++;
2177
-                continue;
2178
-            }
2179
-            $show_trash                          = ! ((count($default_prices) > 1 && $price_row === 1)
2180
-                                                      || count($default_prices) === 1);
2181
-            $show_create                         = ! (count($default_prices) > 1
2182
-                                                      && count($default_prices)
2183
-                                                         !== $price_row);
2184
-            $template_args['default_price_rows'] .= $this->_get_ticket_price_row(
2185
-                'TICKETNUM',
2186
-                $price_row,
2187
-                $price,
2188
-                true,
2189
-                null,
2190
-                $show_trash,
2191
-                $show_create
2192
-            );
2193
-            $price_row++;
2194
-        }
2195
-        $template_args = apply_filters(
2196
-            'FHEE__espresso_events_Pricing_Hooks___get_ticket_js_structure__template_args',
2197
-            $template_args,
2198
-            $all_datetimes,
2199
-            $all_tickets,
2200
-            $this->_is_creating_event
2201
-        );
2202
-        return EEH_Template::display_template(
2203
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_js_structure.template.php',
2204
-            $template_args,
2205
-            true
2206
-        );
2207
-    }
2067
+	/**
2068
+	 * @param array $all_datetimes
2069
+	 * @param array $all_tickets
2070
+	 * @return mixed
2071
+	 * @throws ReflectionException
2072
+	 * @throws InvalidArgumentException
2073
+	 * @throws InvalidInterfaceException
2074
+	 * @throws InvalidDataTypeException
2075
+	 * @throws DomainException
2076
+	 * @throws EE_Error
2077
+	 */
2078
+	protected function _get_ticket_js_structure($all_datetimes = array(), $all_tickets = array())
2079
+	{
2080
+		$template_args = array(
2081
+			'default_datetime_edit_row'                => $this->_get_dtt_edit_row(
2082
+				'DTTNUM',
2083
+				null,
2084
+				true,
2085
+				$all_datetimes
2086
+			),
2087
+			'default_ticket_row'                       => $this->_get_ticket_row(
2088
+				'TICKETNUM',
2089
+				null,
2090
+				array(),
2091
+				array(),
2092
+				true
2093
+			),
2094
+			'default_price_row'                        => $this->_get_ticket_price_row(
2095
+				'TICKETNUM',
2096
+				'PRICENUM',
2097
+				null,
2098
+				true,
2099
+				null
2100
+			),
2101
+			'default_price_rows'                       => '',
2102
+			'default_base_price_amount'                => 0,
2103
+			'default_base_price_name'                  => '',
2104
+			'default_base_price_description'           => '',
2105
+			'default_price_modifier_selector_row'      => $this->_get_price_modifier_template(
2106
+				'TICKETNUM',
2107
+				'PRICENUM',
2108
+				null,
2109
+				true
2110
+			),
2111
+			'default_available_tickets_for_datetime'   => $this->_get_dtt_attached_tickets_row(
2112
+				'DTTNUM',
2113
+				null,
2114
+				array(),
2115
+				array(),
2116
+				true
2117
+			),
2118
+			'existing_available_datetime_tickets_list' => '',
2119
+			'existing_available_ticket_datetimes_list' => '',
2120
+			'new_available_datetime_ticket_list_item'  => $this->_get_datetime_tickets_list_item(
2121
+				'DTTNUM',
2122
+				'TICKETNUM',
2123
+				null,
2124
+				null,
2125
+				array(),
2126
+				true
2127
+			),
2128
+			'new_available_ticket_datetime_list_item'  => $this->_get_ticket_datetime_list_item(
2129
+				'DTTNUM',
2130
+				'TICKETNUM',
2131
+				null,
2132
+				null,
2133
+				array(),
2134
+				true
2135
+			),
2136
+		);
2137
+		$ticket_row    = 1;
2138
+		foreach ($all_tickets as $ticket) {
2139
+			$template_args['existing_available_datetime_tickets_list'] .= $this->_get_datetime_tickets_list_item(
2140
+				'DTTNUM',
2141
+				$ticket_row,
2142
+				null,
2143
+				$ticket,
2144
+				array(),
2145
+				true
2146
+			);
2147
+			$ticket_row++;
2148
+		}
2149
+		$datetime_row = 1;
2150
+		foreach ($all_datetimes as $datetime) {
2151
+			$template_args['existing_available_ticket_datetimes_list'] .= $this->_get_ticket_datetime_list_item(
2152
+				$datetime_row,
2153
+				'TICKETNUM',
2154
+				$datetime,
2155
+				null,
2156
+				array(),
2157
+				true
2158
+			);
2159
+			$datetime_row++;
2160
+		}
2161
+		/** @var EEM_Price $price_model */
2162
+		$price_model    = EE_Registry::instance()->load_model('Price');
2163
+		$default_prices = $price_model->get_all_default_prices();
2164
+		$price_row      = 1;
2165
+		foreach ($default_prices as $price) {
2166
+			if (! $price instanceof EE_Price) {
2167
+				continue;
2168
+			}
2169
+			if ($price->is_base_price()) {
2170
+				$template_args['default_base_price_amount']      = $price->get_pretty(
2171
+					'PRC_amount',
2172
+					'localized_float'
2173
+				);
2174
+				$template_args['default_base_price_name']        = $price->get('PRC_name');
2175
+				$template_args['default_base_price_description'] = $price->get('PRC_desc');
2176
+				$price_row++;
2177
+				continue;
2178
+			}
2179
+			$show_trash                          = ! ((count($default_prices) > 1 && $price_row === 1)
2180
+													  || count($default_prices) === 1);
2181
+			$show_create                         = ! (count($default_prices) > 1
2182
+													  && count($default_prices)
2183
+														 !== $price_row);
2184
+			$template_args['default_price_rows'] .= $this->_get_ticket_price_row(
2185
+				'TICKETNUM',
2186
+				$price_row,
2187
+				$price,
2188
+				true,
2189
+				null,
2190
+				$show_trash,
2191
+				$show_create
2192
+			);
2193
+			$price_row++;
2194
+		}
2195
+		$template_args = apply_filters(
2196
+			'FHEE__espresso_events_Pricing_Hooks___get_ticket_js_structure__template_args',
2197
+			$template_args,
2198
+			$all_datetimes,
2199
+			$all_tickets,
2200
+			$this->_is_creating_event
2201
+		);
2202
+		return EEH_Template::display_template(
2203
+			PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_js_structure.template.php',
2204
+			$template_args,
2205
+			true
2206
+		);
2207
+	}
2208 2208
 } //end class espresso_events_Pricing_Hooks
Please login to merge, or discard this patch.
Spacing   +90 added lines, -90 removed lines patch added patch discarded remove patch
@@ -52,7 +52,7 @@  discard block
 block discarded – undo
52 52
     {
53 53
         $this->_name = 'pricing';
54 54
         //capability check
55
-        if (! EE_Registry::instance()->CAP->current_user_can(
55
+        if ( ! EE_Registry::instance()->CAP->current_user_can(
56 56
             'ee_read_default_prices',
57 57
             'advanced_ticket_datetime_metabox'
58 58
         )) {
@@ -80,7 +80,7 @@  discard block
 block discarded – undo
80 80
     protected function _setup_metaboxes()
81 81
     {
82 82
         //if we were going to add our own metaboxes we'd use the below.
83
-        $this->_metaboxes        = array(
83
+        $this->_metaboxes = array(
84 84
             0 => array(
85 85
                 'page_route' => array('edit', 'create_new'),
86 86
                 'func'       => 'pricing_metabox',
@@ -152,7 +152,7 @@  discard block
 block discarded – undo
152 152
             );
153 153
             $msg .= '</p><ul>';
154 154
             foreach ($format_validation as $error) {
155
-                $msg .= '<li>' . $error . '</li>';
155
+                $msg .= '<li>'.$error.'</li>';
156 156
             }
157 157
             $msg .= '</ul><p>';
158 158
             $msg .= sprintf(
@@ -181,11 +181,11 @@  discard block
 block discarded – undo
181 181
         $this->_scripts_styles = array(
182 182
             'registers'   => array(
183 183
                 'ee-tickets-datetimes-css' => array(
184
-                    'url'  => PRICING_ASSETS_URL . 'event-tickets-datetimes.css',
184
+                    'url'  => PRICING_ASSETS_URL.'event-tickets-datetimes.css',
185 185
                     'type' => 'css',
186 186
                 ),
187 187
                 'ee-dtt-ticket-metabox'    => array(
188
-                    'url'     => PRICING_ASSETS_URL . 'ee-datetime-ticket-metabox.js',
188
+                    'url'     => PRICING_ASSETS_URL.'ee-datetime-ticket-metabox.js',
189 189
                     'depends' => array('ee-datepicker', 'ee-dialog', 'underscore'),
190 190
                 ),
191 191
             ),
@@ -209,9 +209,9 @@  discard block
 block discarded – undo
209 209
                             'event_espresso'
210 210
                         ),
211 211
                         'cancel_button'           => '<button class="button-secondary ee-modal-cancel">'
212
-                                                     . esc_html__('Cancel', 'event_espresso') . '</button>',
212
+                                                     . esc_html__('Cancel', 'event_espresso').'</button>',
213 213
                         'close_button'            => '<button class="button-secondary ee-modal-cancel">'
214
-                                                     . esc_html__('Close', 'event_espresso') . '</button>',
214
+                                                     . esc_html__('Close', 'event_espresso').'</button>',
215 215
                         'single_warning_from_tkt' => esc_html__(
216 216
                             'The Datetime you are attempting to unassign from this ticket is the only remaining datetime for this ticket. Tickets must always have at least one datetime assigned to them.',
217 217
                             'event_espresso'
@@ -221,7 +221,7 @@  discard block
 block discarded – undo
221 221
                             'event_espresso'
222 222
                         ),
223 223
                         'dismiss_button'          => '<button class="button-secondary ee-modal-cancel">'
224
-                                                     . esc_html__('Dismiss', 'event_espresso') . '</button>',
224
+                                                     . esc_html__('Dismiss', 'event_espresso').'</button>',
225 225
                     ),
226 226
                     'DTT_ERROR_MSG'         => array(
227 227
                         'no_ticket_name' => esc_html__('General Admission', 'event_espresso'),
@@ -259,7 +259,7 @@  discard block
 block discarded – undo
259 259
     {
260 260
         foreach ($update_callbacks as $key => $callback) {
261 261
             if ($callback[1] === '_default_tickets_update') {
262
-                unset($update_callbacks[ $key ]);
262
+                unset($update_callbacks[$key]);
263 263
             }
264 264
         }
265 265
         $update_callbacks[] = array($this, 'datetime_and_tickets_caf_update');
@@ -316,8 +316,8 @@  discard block
 block discarded – undo
316 316
         }
317 317
         foreach ($data['edit_event_datetimes'] as $row => $datetime_data) {
318 318
             //trim all values to ensure any excess whitespace is removed.
319
-            $datetime_data                = array_map(
320
-                function ($datetime_data)
319
+            $datetime_data = array_map(
320
+                function($datetime_data)
321 321
                 {
322 322
                     return is_array($datetime_data) ? $datetime_data : trim($datetime_data);
323 323
                 },
@@ -327,7 +327,7 @@  discard block
 block discarded – undo
327 327
                                             && ! empty($datetime_data['DTT_EVT_end'])
328 328
                 ? $datetime_data['DTT_EVT_end']
329 329
                 : $datetime_data['DTT_EVT_start'];
330
-            $datetime_values              = array(
330
+            $datetime_values = array(
331 331
                 'DTT_ID'          => ! empty($datetime_data['DTT_ID'])
332 332
                     ? $datetime_data['DTT_ID']
333 333
                     : null,
@@ -348,7 +348,7 @@  discard block
 block discarded – undo
348 348
             );
349 349
             // if we have an id then let's get existing object first and then set the new values.
350 350
             // Otherwise we instantiate a new object for save.
351
-            if (! empty($datetime_data['DTT_ID'])) {
351
+            if ( ! empty($datetime_data['DTT_ID'])) {
352 352
                 $datetime = EE_Registry::instance()
353 353
                                        ->load_model('Datetime', array($timezone))
354 354
                                        ->get_one_by_ID($datetime_data['DTT_ID']);
@@ -362,7 +362,7 @@  discard block
 block discarded – undo
362 362
                 // after the add_relation_to() the autosave replaces it.
363 363
                 // We need to do this so we dont' TRASH the parent DTT.
364 364
                 // (save the ID for both key and value to avoid duplications)
365
-                $saved_dtt_ids[ $datetime->ID() ] = $datetime->ID();
365
+                $saved_dtt_ids[$datetime->ID()] = $datetime->ID();
366 366
             } else {
367 367
                 $datetime = EE_Registry::instance()->load_class(
368 368
                     'Datetime',
@@ -391,8 +391,8 @@  discard block
 block discarded – undo
391 391
             // because it is possible there was a new one created for the autosave.
392 392
             // (save the ID for both key and value to avoid duplications)
393 393
             $DTT_ID                   = $datetime->ID();
394
-            $saved_dtt_ids[ $DTT_ID ] = $DTT_ID;
395
-            $saved_dtt_objs[ $row ]   = $datetime;
394
+            $saved_dtt_ids[$DTT_ID] = $DTT_ID;
395
+            $saved_dtt_objs[$row]   = $datetime;
396 396
             //todo if ANY of these updates fail then we want the appropriate global error message.
397 397
         }
398 398
         $event->save();
@@ -457,13 +457,13 @@  discard block
 block discarded – undo
457 457
             $update_prices = $create_new_TKT = false;
458 458
             // figure out what datetimes were added to the ticket
459 459
             // and what datetimes were removed from the ticket in the session.
460
-            $starting_tkt_dtt_rows = explode(',', $data['starting_ticket_datetime_rows'][ $row ]);
461
-            $tkt_dtt_rows          = explode(',', $data['ticket_datetime_rows'][ $row ]);
460
+            $starting_tkt_dtt_rows = explode(',', $data['starting_ticket_datetime_rows'][$row]);
461
+            $tkt_dtt_rows          = explode(',', $data['ticket_datetime_rows'][$row]);
462 462
             $datetimes_added       = array_diff($tkt_dtt_rows, $starting_tkt_dtt_rows);
463 463
             $datetimes_removed     = array_diff($starting_tkt_dtt_rows, $tkt_dtt_rows);
464 464
             // trim inputs to ensure any excess whitespace is removed.
465 465
             $tkt = array_map(
466
-                function ($ticket_data)
466
+                function($ticket_data)
467 467
                 {
468 468
                     return is_array($ticket_data) ? $ticket_data : trim($ticket_data);
469 469
                 },
@@ -486,8 +486,8 @@  discard block
 block discarded – undo
486 486
             $base_price_id = isset($tkt['TKT_base_price_ID'])
487 487
                 ? $tkt['TKT_base_price_ID']
488 488
                 : 0;
489
-            $price_rows    = is_array($data['edit_prices']) && isset($data['edit_prices'][ $row ])
490
-                ? $data['edit_prices'][ $row ]
489
+            $price_rows    = is_array($data['edit_prices']) && isset($data['edit_prices'][$row])
490
+                ? $data['edit_prices'][$row]
491 491
                 : array();
492 492
             $now           = null;
493 493
             if (empty($tkt['TKT_start_date'])) {
@@ -499,7 +499,7 @@  discard block
 block discarded – undo
499 499
                 /**
500 500
                  * set the TKT_end_date to the first datetime attached to the ticket.
501 501
                  */
502
-                $first_dtt           = $saved_datetimes[ reset($tkt_dtt_rows) ];
502
+                $first_dtt           = $saved_datetimes[reset($tkt_dtt_rows)];
503 503
                 $tkt['TKT_end_date'] = $first_dtt->start_date_and_time($this->_date_time_format);
504 504
             }
505 505
             $TKT_values = array(
@@ -600,7 +600,7 @@  discard block
 block discarded – undo
600 600
                 if ($ticket instanceof EE_Ticket) {
601 601
                     // make sure ticket has an ID of setting relations won't work
602 602
                     $ticket->save();
603
-                    $ticket        = $this->_update_ticket_datetimes(
603
+                    $ticket = $this->_update_ticket_datetimes(
604 604
                         $ticket,
605 605
                         $saved_datetimes,
606 606
                         $datetimes_added,
@@ -634,7 +634,7 @@  discard block
 block discarded – undo
634 634
             //need to make sue that the TKT_price is accurate after saving the prices.
635 635
             $ticket->ensure_TKT_Price_correct();
636 636
             //handle CREATING a default tkt from the incoming tkt but ONLY if this isn't an autosave.
637
-            if (! defined('DOING_AUTOSAVE') && ! empty($tkt['TKT_is_default_selector'])) {
637
+            if ( ! defined('DOING_AUTOSAVE') && ! empty($tkt['TKT_is_default_selector'])) {
638 638
                 $update_prices = true;
639 639
                 $new_default   = clone $ticket;
640 640
                 $new_default->set('TKT_ID', 0);
@@ -679,7 +679,7 @@  discard block
 block discarded – undo
679 679
                 //save new TKT
680 680
                 $new_tkt->save();
681 681
                 //add new ticket to array
682
-                $saved_tickets[ $new_tkt->ID() ] = $new_tkt;
682
+                $saved_tickets[$new_tkt->ID()] = $new_tkt;
683 683
                 do_action(
684 684
                     'AHEE__espresso_events_Pricing_Hooks___update_tkts_new_ticket',
685 685
                     $new_tkt,
@@ -689,7 +689,7 @@  discard block
 block discarded – undo
689 689
                 );
690 690
             } else {
691 691
                 //add tkt to saved tkts
692
-                $saved_tickets[ $ticket->ID() ] = $ticket;
692
+                $saved_tickets[$ticket->ID()] = $ticket;
693 693
                 do_action(
694 694
                     'AHEE__espresso_events_Pricing_Hooks___update_tkts_update_ticket',
695 695
                     $ticket,
@@ -756,33 +756,33 @@  discard block
 block discarded – undo
756 756
         // to start we have to add the ticket to all the datetimes its supposed to be with,
757 757
         // and removing the ticket from datetimes it got removed from.
758 758
         // first let's add datetimes
759
-        if (! empty($added_datetimes) && is_array($added_datetimes)) {
759
+        if ( ! empty($added_datetimes) && is_array($added_datetimes)) {
760 760
             foreach ($added_datetimes as $row_id) {
761 761
                 $row_id = (int) $row_id;
762
-                if (isset($saved_datetimes[ $row_id ]) && $saved_datetimes[ $row_id ] instanceof EE_Datetime) {
763
-                    $ticket->_add_relation_to($saved_datetimes[ $row_id ], 'Datetime');
762
+                if (isset($saved_datetimes[$row_id]) && $saved_datetimes[$row_id] instanceof EE_Datetime) {
763
+                    $ticket->_add_relation_to($saved_datetimes[$row_id], 'Datetime');
764 764
                     // Is this an existing ticket (has an ID) and does it have any sold?
765 765
                     // If so, then we need to add that to the DTT sold because this DTT is getting added.
766 766
                     if ($ticket->ID() && $ticket->sold() > 0) {
767
-                        $saved_datetimes[ $row_id ]->increase_sold($ticket->sold());
768
-                        $saved_datetimes[ $row_id ]->save();
767
+                        $saved_datetimes[$row_id]->increase_sold($ticket->sold());
768
+                        $saved_datetimes[$row_id]->save();
769 769
                     }
770 770
                 }
771 771
             }
772 772
         }
773 773
         // then remove datetimes
774
-        if (! empty($removed_datetimes) && is_array($removed_datetimes)) {
774
+        if ( ! empty($removed_datetimes) && is_array($removed_datetimes)) {
775 775
             foreach ($removed_datetimes as $row_id) {
776 776
                 $row_id = (int) $row_id;
777 777
                 // its entirely possible that a datetime got deleted (instead of just removed from relationship.
778 778
                 // So make sure we skip over this if the dtt isn't in the $saved_datetimes array)
779
-                if (isset($saved_datetimes[ $row_id ]) && $saved_datetimes[ $row_id ] instanceof EE_Datetime) {
780
-                    $ticket->_remove_relation_to($saved_datetimes[ $row_id ], 'Datetime');
779
+                if (isset($saved_datetimes[$row_id]) && $saved_datetimes[$row_id] instanceof EE_Datetime) {
780
+                    $ticket->_remove_relation_to($saved_datetimes[$row_id], 'Datetime');
781 781
                     // Is this an existing ticket (has an ID) and does it have any sold?
782 782
                     // If so, then we need to remove it's sold from the DTT_sold.
783 783
                     if ($ticket->ID() && $ticket->sold() > 0) {
784
-                        $saved_datetimes[ $row_id ]->decrease_sold($ticket->sold());
785
-                        $saved_datetimes[ $row_id ]->save();
784
+                        $saved_datetimes[$row_id]->decrease_sold($ticket->sold());
785
+                        $saved_datetimes[$row_id]->save();
786 786
                     }
787 787
                 }
788 788
             }
@@ -895,7 +895,7 @@  discard block
 block discarded – undo
895 895
             );
896 896
         }
897 897
         //possibly need to save tkt
898
-        if (! $ticket->ID()) {
898
+        if ( ! $ticket->ID()) {
899 899
             $ticket->save();
900 900
         }
901 901
         foreach ($prices as $row => $prc) {
@@ -929,17 +929,17 @@  discard block
 block discarded – undo
929 929
                 }
930 930
             }
931 931
             $price->save();
932
-            $updated_prices[ $price->ID() ] = $price;
932
+            $updated_prices[$price->ID()] = $price;
933 933
             $ticket->_add_relation_to($price, 'Price');
934 934
         }
935 935
         //now let's remove any prices that got removed from the ticket
936
-        if (! empty ($current_prices_on_ticket)) {
936
+        if ( ! empty ($current_prices_on_ticket)) {
937 937
             $current          = array_keys($current_prices_on_ticket);
938 938
             $updated          = array_keys($updated_prices);
939 939
             $prices_to_remove = array_diff($current, $updated);
940
-            if (! empty($prices_to_remove)) {
940
+            if ( ! empty($prices_to_remove)) {
941 941
                 foreach ($prices_to_remove as $prc_id) {
942
-                    $p = $current_prices_on_ticket[ $prc_id ];
942
+                    $p = $current_prices_on_ticket[$prc_id];
943 943
                     $ticket->_remove_relation_to($p, 'Price');
944 944
                     //delete permanently the price
945 945
                     $p->delete_permanently();
@@ -1028,7 +1028,7 @@  discard block
 block discarded – undo
1028 1028
             'ee_collapsible_status'    => ' ee-collapsible-open'
1029 1029
             //$this->_adminpage_obj->get_cpt_model_obj()->ID() > 0 ? ' ee-collapsible-closed' : ' ee-collapsible-open'
1030 1030
         );
1031
-        $timezone           = $event instanceof EE_Event ? $event->timezone_string() : null;
1031
+        $timezone = $event instanceof EE_Event ? $event->timezone_string() : null;
1032 1032
         do_action('AHEE_log', __FILE__, __FUNCTION__, '');
1033 1033
         /**
1034 1034
          * 1. Start with retrieving Datetimes
@@ -1090,18 +1090,18 @@  discard block
 block discarded – undo
1090 1090
                 $TKT_ID     = $ticket->get('TKT_ID');
1091 1091
                 $ticket_row = $ticket->get('TKT_row');
1092 1092
                 //we only want unique tickets in our final display!!
1093
-                if (! in_array($TKT_ID, $existing_ticket_ids, true)) {
1093
+                if ( ! in_array($TKT_ID, $existing_ticket_ids, true)) {
1094 1094
                     $existing_ticket_ids[] = $TKT_ID;
1095 1095
                     $all_tickets[]         = $ticket;
1096 1096
                 }
1097 1097
                 //temporary cache of this ticket info for this datetime for later processing of datetime rows.
1098
-                $datetime_tickets[ $DTT_ID ][] = $ticket_row;
1098
+                $datetime_tickets[$DTT_ID][] = $ticket_row;
1099 1099
                 //temporary cache of this datetime info for this ticket for later processing of ticket rows.
1100 1100
                 if (
1101
-                    ! isset($ticket_datetimes[ $TKT_ID ])
1102
-                    || ! in_array($datetime_row, $ticket_datetimes[ $TKT_ID ], true)
1101
+                    ! isset($ticket_datetimes[$TKT_ID])
1102
+                    || ! in_array($datetime_row, $ticket_datetimes[$TKT_ID], true)
1103 1103
                 ) {
1104
-                    $ticket_datetimes[ $TKT_ID ][] = $datetime_row;
1104
+                    $ticket_datetimes[$TKT_ID][] = $datetime_row;
1105 1105
                 }
1106 1106
             }
1107 1107
             $datetime_row++;
@@ -1112,7 +1112,7 @@  discard block
 block discarded – undo
1112 1112
         //sort $all_tickets by order
1113 1113
         usort(
1114 1114
             $all_tickets,
1115
-            function (EE_Ticket $a, EE_Ticket $b)
1115
+            function(EE_Ticket $a, EE_Ticket $b)
1116 1116
             {
1117 1117
                 $a_order = (int) $a->get('TKT_order');
1118 1118
                 $b_order = (int) $b->get('TKT_order');
@@ -1151,7 +1151,7 @@  discard block
 block discarded – undo
1151 1151
         }
1152 1152
         $main_template_args['ticket_js_structure'] = $this->_get_ticket_js_structure($datetimes, $all_tickets);
1153 1153
         EEH_Template::display_template(
1154
-            PRICING_TEMPLATE_PATH . 'event_tickets_metabox_main.template.php',
1154
+            PRICING_TEMPLATE_PATH.'event_tickets_metabox_main.template.php',
1155 1155
             $main_template_args
1156 1156
         );
1157 1157
     }
@@ -1193,7 +1193,7 @@  discard block
 block discarded – undo
1193 1193
             'dtt_row'                  => $default ? 'DTTNUM' : $datetime_row,
1194 1194
         );
1195 1195
         return EEH_Template::display_template(
1196
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_row_wrapper.template.php',
1196
+            PRICING_TEMPLATE_PATH.'event_tickets_datetime_row_wrapper.template.php',
1197 1197
             $dtt_display_template_args,
1198 1198
             true
1199 1199
         );
@@ -1262,7 +1262,7 @@  discard block
 block discarded – undo
1262 1262
             $this->_is_creating_event
1263 1263
         );
1264 1264
         return EEH_Template::display_template(
1265
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_edit_row.template.php',
1265
+            PRICING_TEMPLATE_PATH.'event_tickets_datetime_edit_row.template.php',
1266 1266
             $template_args,
1267 1267
             true
1268 1268
         );
@@ -1303,7 +1303,7 @@  discard block
 block discarded – undo
1303 1303
             'DTT_ID'                            => $default ? '' : $datetime->ID(),
1304 1304
         );
1305 1305
         //need to setup the list items (but only if this isn't a default skeleton setup)
1306
-        if (! $default) {
1306
+        if ( ! $default) {
1307 1307
             $ticket_row = 1;
1308 1308
             foreach ($all_tickets as $ticket) {
1309 1309
                 $template_args['datetime_tickets_list'] .= $this->_get_datetime_tickets_list_item(
@@ -1329,7 +1329,7 @@  discard block
 block discarded – undo
1329 1329
             $this->_is_creating_event
1330 1330
         );
1331 1331
         return EEH_Template::display_template(
1332
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_attached_tickets_row.template.php',
1332
+            PRICING_TEMPLATE_PATH.'event_tickets_datetime_attached_tickets_row.template.php',
1333 1333
             $template_args,
1334 1334
             true
1335 1335
         );
@@ -1355,8 +1355,8 @@  discard block
 block discarded – undo
1355 1355
         $datetime_tickets = array(),
1356 1356
         $default
1357 1357
     ) {
1358
-        $dtt_tkts      = $datetime instanceof EE_Datetime && isset($datetime_tickets[ $datetime->ID() ])
1359
-            ? $datetime_tickets[ $datetime->ID() ]
1358
+        $dtt_tkts      = $datetime instanceof EE_Datetime && isset($datetime_tickets[$datetime->ID()])
1359
+            ? $datetime_tickets[$datetime->ID()]
1360 1360
             : array();
1361 1361
         $display_row   = $ticket instanceof EE_Ticket ? $ticket->get('TKT_row') : 0;
1362 1362
         $no_ticket     = $default && empty($ticket);
@@ -1377,8 +1377,8 @@  discard block
 block discarded – undo
1377 1377
                 ? 'TKTNAME'
1378 1378
                 : $ticket->get('TKT_name'),
1379 1379
             'tkt_status_class'        => $no_ticket || $this->_is_creating_event
1380
-                ? ' tkt-status-' . EE_Ticket::onsale
1381
-                : ' tkt-status-' . $ticket->ticket_status(),
1380
+                ? ' tkt-status-'.EE_Ticket::onsale
1381
+                : ' tkt-status-'.$ticket->ticket_status(),
1382 1382
         );
1383 1383
         //filter template args
1384 1384
         $template_args = apply_filters(
@@ -1393,7 +1393,7 @@  discard block
 block discarded – undo
1393 1393
             $this->_is_creating_event
1394 1394
         );
1395 1395
         return EEH_Template::display_template(
1396
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_dtt_tickets_list.template.php',
1396
+            PRICING_TEMPLATE_PATH.'event_tickets_datetime_dtt_tickets_list.template.php',
1397 1397
             $template_args,
1398 1398
             true
1399 1399
         );
@@ -1445,19 +1445,19 @@  discard block
 block discarded – undo
1445 1445
         // (otherwise there won't be any new relationships created for tickets based off of the default ticket).
1446 1446
         // This will future proof in case there is ever any behaviour change between what the primary_key defaults to.
1447 1447
         $default_dtt      = $default || ($ticket instanceof EE_Ticket && $ticket->is_default());
1448
-        $tkt_datetimes    = $ticket instanceof EE_Ticket && isset($ticket_datetimes[ $ticket->ID() ])
1449
-            ? $ticket_datetimes[ $ticket->ID() ]
1448
+        $tkt_datetimes    = $ticket instanceof EE_Ticket && isset($ticket_datetimes[$ticket->ID()])
1449
+            ? $ticket_datetimes[$ticket->ID()]
1450 1450
             : array();
1451 1451
         $ticket_subtotal  = $default ? 0 : $ticket->get_ticket_subtotal();
1452 1452
         $base_price       = $default ? null : $ticket->base_price();
1453 1453
         $count_price_mods = EEM_Price::instance()->get_all_default_prices(true);
1454 1454
         //breaking out complicated condition for ticket_status
1455 1455
         if ($default) {
1456
-            $ticket_status_class = ' tkt-status-' . EE_Ticket::onsale;
1456
+            $ticket_status_class = ' tkt-status-'.EE_Ticket::onsale;
1457 1457
         } else {
1458 1458
             $ticket_status_class = $ticket->is_default()
1459
-                ? ' tkt-status-' . EE_Ticket::onsale
1460
-                : ' tkt-status-' . $ticket->ticket_status();
1459
+                ? ' tkt-status-'.EE_Ticket::onsale
1460
+                : ' tkt-status-'.$ticket->ticket_status();
1461 1461
         }
1462 1462
         //breaking out complicated condition for TKT_taxable
1463 1463
         if ($default) {
@@ -1482,7 +1482,7 @@  discard block
 block discarded – undo
1482 1482
                 $TKT_min = '';
1483 1483
             }
1484 1484
         }
1485
-        $template_args                 = array(
1485
+        $template_args = array(
1486 1486
             'tkt_row'                       => $default ? 'TICKETNUM' : $ticket_row,
1487 1487
             'TKT_order'                     => $default ? 'TICKETNUM' : $ticket_row,
1488 1488
             //on initial page load this will always be the correct order.
@@ -1549,7 +1549,7 @@  discard block
 block discarded – undo
1549 1549
                 : ' style="display:none;"',
1550 1550
             'show_price_mod_button'         => count($prices) > 1
1551 1551
                                                || ($default && $count_price_mods > 0)
1552
-                                               || (! $default && $ticket->deleted())
1552
+                                               || ( ! $default && $ticket->deleted())
1553 1553
                 ? ' style="display:none;"'
1554 1554
                 : '',
1555 1555
             'total_price_rows'              => count($prices) > 1 ? count($prices) : 1,
@@ -1591,7 +1591,7 @@  discard block
 block discarded – undo
1591 1591
             //if empty then the start date will be now.
1592 1592
             $template_args['TKT_start_date']   = date($this->_date_time_format,
1593 1593
                 current_time('timestamp'));
1594
-            $template_args['tkt_status_class'] = ' tkt-status-' . EE_Ticket::onsale;
1594
+            $template_args['tkt_status_class'] = ' tkt-status-'.EE_Ticket::onsale;
1595 1595
         }
1596 1596
         if (empty($template_args['TKT_end_date'])) {
1597 1597
             //get the earliest datetime (if present);
@@ -1601,7 +1601,7 @@  discard block
 block discarded – undo
1601 1601
                     array('order_by' => array('DTT_EVT_start' => 'ASC'))
1602 1602
                 )
1603 1603
                 : null;
1604
-            if (! empty($earliest_dtt)) {
1604
+            if ( ! empty($earliest_dtt)) {
1605 1605
                 $template_args['TKT_end_date'] = $earliest_dtt->get_datetime(
1606 1606
                     'DTT_EVT_start',
1607 1607
                     $this->_date_time_format
@@ -1615,10 +1615,10 @@  discard block
 block discarded – undo
1615 1615
                     )
1616 1616
                 );
1617 1617
             }
1618
-            $template_args['tkt_status_class'] = ' tkt-status-' . EE_Ticket::onsale;
1618
+            $template_args['tkt_status_class'] = ' tkt-status-'.EE_Ticket::onsale;
1619 1619
         }
1620 1620
         //generate ticket_datetime items
1621
-        if (! $default) {
1621
+        if ( ! $default) {
1622 1622
             $datetime_row = 1;
1623 1623
             foreach ($all_datetimes as $datetime) {
1624 1624
                 $template_args['ticket_datetimes_list'] .= $this->_get_ticket_datetime_list_item(
@@ -1634,7 +1634,7 @@  discard block
 block discarded – undo
1634 1634
         }
1635 1635
         $price_row = 1;
1636 1636
         foreach ($prices as $price) {
1637
-            if (! $price instanceof EE_Price) {
1637
+            if ( ! $price instanceof EE_Price) {
1638 1638
                 continue;
1639 1639
             }
1640 1640
             if ($price->is_base_price()) {
@@ -1667,7 +1667,7 @@  discard block
 block discarded – undo
1667 1667
             $this->_is_creating_event
1668 1668
         );
1669 1669
         return EEH_Template::display_template(
1670
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_row.template.php',
1670
+            PRICING_TEMPLATE_PATH.'event_tickets_datetime_ticket_row.template.php',
1671 1671
             $template_args,
1672 1672
             true
1673 1673
         );
@@ -1706,8 +1706,8 @@  discard block
 block discarded – undo
1706 1706
                 $ticket,
1707 1707
                 $this->_is_creating_event
1708 1708
             );
1709
-            $tax_rows      .= EEH_Template::display_template(
1710
-                PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_tax_row.template.php',
1709
+            $tax_rows .= EEH_Template::display_template(
1710
+                PRICING_TEMPLATE_PATH.'event_tickets_datetime_ticket_tax_row.template.php',
1711 1711
                 $template_args,
1712 1712
                 true
1713 1713
             );
@@ -1827,7 +1827,7 @@  discard block
 block discarded – undo
1827 1827
             $this->_is_creating_event
1828 1828
         );
1829 1829
         return EEH_Template::display_template(
1830
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_price_row.template.php',
1830
+            PRICING_TEMPLATE_PATH.'event_tickets_datetime_ticket_price_row.template.php',
1831 1831
             $template_args,
1832 1832
             true
1833 1833
         );
@@ -1897,7 +1897,7 @@  discard block
 block discarded – undo
1897 1897
             $this->_is_creating_event
1898 1898
         );
1899 1899
         return EEH_Template::display_template(
1900
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_price_type_base.template.php',
1900
+            PRICING_TEMPLATE_PATH.'event_tickets_datetime_price_type_base.template.php',
1901 1901
             $template_args,
1902 1902
             true
1903 1903
         );
@@ -1927,7 +1927,7 @@  discard block
 block discarded – undo
1927 1927
     ) {
1928 1928
         $select_name = $default && ! $price instanceof EE_Price
1929 1929
             ? 'edit_prices[TICKETNUM][PRICENUM][PRT_ID]'
1930
-            : 'edit_prices[' . $ticket_row . '][' . $price_row . '][PRT_ID]';
1930
+            : 'edit_prices['.$ticket_row.']['.$price_row.'][PRT_ID]';
1931 1931
         /** @var EEM_Price_Type $price_type_model */
1932 1932
         $price_type_model       = EE_Registry::instance()->load_model('Price_Type');
1933 1933
         $price_types            = $price_type_model->get_all(array(
@@ -1945,23 +1945,23 @@  discard block
 block discarded – undo
1945 1945
         $price_option_spans     = '';
1946 1946
         //setup price types for selector
1947 1947
         foreach ($price_types as $price_type) {
1948
-            if (! $price_type instanceof EE_Price_Type) {
1948
+            if ( ! $price_type instanceof EE_Price_Type) {
1949 1949
                 continue;
1950 1950
             }
1951
-            $all_price_types[ $price_type->ID() ] = $price_type->get('PRT_name');
1951
+            $all_price_types[$price_type->ID()] = $price_type->get('PRT_name');
1952 1952
             //while we're in the loop let's setup the option spans used by js
1953
-            $span_args          = array(
1953
+            $span_args = array(
1954 1954
                 'PRT_ID'         => $price_type->ID(),
1955 1955
                 'PRT_operator'   => $price_type->is_discount() ? '-' : '+',
1956 1956
                 'PRT_is_percent' => $price_type->get('PRT_is_percent') ? 1 : 0,
1957 1957
             );
1958 1958
             $price_option_spans .= EEH_Template::display_template(
1959
-                PRICING_TEMPLATE_PATH . 'event_tickets_datetime_price_option_span.template.php',
1959
+                PRICING_TEMPLATE_PATH.'event_tickets_datetime_price_option_span.template.php',
1960 1960
                 $span_args,
1961 1961
                 true
1962 1962
             );
1963 1963
         }
1964
-        $select_name               = $disabled ? 'archive_price[' . $ticket_row . '][' . $price_row . '][PRT_ID]'
1964
+        $select_name               = $disabled ? 'archive_price['.$ticket_row.']['.$price_row.'][PRT_ID]'
1965 1965
             : $select_name;
1966 1966
         $select_input              = new EE_Select_Input(
1967 1967
             $all_price_types,
@@ -1987,7 +1987,7 @@  discard block
 block discarded – undo
1987 1987
             'price_selected_is_percent' => $price_selected_is_percent,
1988 1988
             'disabled'                  => $disabled,
1989 1989
         );
1990
-        $template_args             = apply_filters(
1990
+        $template_args = apply_filters(
1991 1991
             'FHEE__espresso_events_Pricing_Hooks___get_price_modifier_template__template_args',
1992 1992
             $template_args,
1993 1993
             $ticket_row,
@@ -1998,7 +1998,7 @@  discard block
 block discarded – undo
1998 1998
             $this->_is_creating_event
1999 1999
         );
2000 2000
         return EEH_Template::display_template(
2001
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_price_modifier_selector.template.php',
2001
+            PRICING_TEMPLATE_PATH.'event_tickets_datetime_price_modifier_selector.template.php',
2002 2002
             $template_args,
2003 2003
             true
2004 2004
         );
@@ -2024,8 +2024,8 @@  discard block
 block discarded – undo
2024 2024
         $ticket_datetimes = array(),
2025 2025
         $default
2026 2026
     ) {
2027
-        $tkt_datetimes = $ticket instanceof EE_Ticket && isset($ticket_datetimes[ $ticket->ID() ])
2028
-            ? $ticket_datetimes[ $ticket->ID() ]
2027
+        $tkt_datetimes = $ticket instanceof EE_Ticket && isset($ticket_datetimes[$ticket->ID()])
2028
+            ? $ticket_datetimes[$ticket->ID()]
2029 2029
             : array();
2030 2030
         $template_args = array(
2031 2031
             'dtt_row'                  => $default && ! $datetime instanceof EE_Datetime
@@ -2057,7 +2057,7 @@  discard block
 block discarded – undo
2057 2057
             $this->_is_creating_event
2058 2058
         );
2059 2059
         return EEH_Template::display_template(
2060
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_datetimes_list_item.template.php',
2060
+            PRICING_TEMPLATE_PATH.'event_tickets_datetime_ticket_datetimes_list_item.template.php',
2061 2061
             $template_args,
2062 2062
             true
2063 2063
         );
@@ -2134,7 +2134,7 @@  discard block
 block discarded – undo
2134 2134
                 true
2135 2135
             ),
2136 2136
         );
2137
-        $ticket_row    = 1;
2137
+        $ticket_row = 1;
2138 2138
         foreach ($all_tickets as $ticket) {
2139 2139
             $template_args['existing_available_datetime_tickets_list'] .= $this->_get_datetime_tickets_list_item(
2140 2140
                 'DTTNUM',
@@ -2163,11 +2163,11 @@  discard block
 block discarded – undo
2163 2163
         $default_prices = $price_model->get_all_default_prices();
2164 2164
         $price_row      = 1;
2165 2165
         foreach ($default_prices as $price) {
2166
-            if (! $price instanceof EE_Price) {
2166
+            if ( ! $price instanceof EE_Price) {
2167 2167
                 continue;
2168 2168
             }
2169 2169
             if ($price->is_base_price()) {
2170
-                $template_args['default_base_price_amount']      = $price->get_pretty(
2170
+                $template_args['default_base_price_amount'] = $price->get_pretty(
2171 2171
                     'PRC_amount',
2172 2172
                     'localized_float'
2173 2173
                 );
@@ -2200,7 +2200,7 @@  discard block
 block discarded – undo
2200 2200
             $this->_is_creating_event
2201 2201
         );
2202 2202
         return EEH_Template::display_template(
2203
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_js_structure.template.php',
2203
+            PRICING_TEMPLATE_PATH.'event_tickets_datetime_ticket_js_structure.template.php',
2204 2204
             $template_args,
2205 2205
             true
2206 2206
         );
Please login to merge, or discard this patch.
core/db_classes/EE_Datetime.class.php 3 patches
Doc Comments   +2 added lines, -2 removed lines patch added patch discarded remove patch
@@ -578,7 +578,7 @@  discard block
 block discarded – undo
578 578
      * @param string $dt_frmt     string representation of date format defaults to WP settings
579 579
      * @param string $conjunction conjunction junction what's your function ?
580 580
      *                            this string joins the start date with the end date ie: Jan 01 "to" Dec 31
581
-     * @return mixed              string on success, FALSE on fail
581
+     * @return string              string on success, FALSE on fail
582 582
      * @throws ReflectionException
583 583
      * @throws InvalidArgumentException
584 584
      * @throws InvalidInterfaceException
@@ -686,7 +686,7 @@  discard block
 block discarded – undo
686 686
      * @param string $tm_format   string representation of time format defaults to 'g:i a'
687 687
      * @param string $conjunction conjunction junction what's your function ?
688 688
      *                            this string joins the start date with the end date ie: Jan 01 "to" Dec 31
689
-     * @return mixed              string on success, FALSE on fail
689
+     * @return string              string on success, FALSE on fail
690 690
      * @throws ReflectionException
691 691
      * @throws InvalidArgumentException
692 692
      * @throws InvalidInterfaceException
Please login to merge, or discard this patch.
Indentation   +1270 added lines, -1270 removed lines patch added patch discarded remove patch
@@ -14,1276 +14,1276 @@
 block discarded – undo
14 14
 class EE_Datetime extends EE_Soft_Delete_Base_Class
15 15
 {
16 16
 
17
-    /**
18
-     * constant used by get_active_status, indicates datetime has no more available spaces
19
-     */
20
-    const sold_out = 'DTS';
21
-
22
-    /**
23
-     * constant used by get_active_status, indicating datetime is still active (even is not over, can be registered-for)
24
-     */
25
-    const active = 'DTA';
26
-
27
-    /**
28
-     * constant used by get_active_status, indicating the datetime cannot be used for registrations yet, but has not
29
-     * expired
30
-     */
31
-    const upcoming = 'DTU';
32
-
33
-    /**
34
-     * Datetime is postponed
35
-     */
36
-    const postponed = 'DTP';
37
-
38
-    /**
39
-     * Datetime is cancelled
40
-     */
41
-    const cancelled = 'DTC';
42
-
43
-    /**
44
-     * constant used by get_active_status, indicates datetime has expired (event is over)
45
-     */
46
-    const expired = 'DTE';
47
-
48
-    /**
49
-     * constant used in various places indicating that an event is INACTIVE (not yet ready to be published)
50
-     */
51
-    const inactive = 'DTI';
52
-
53
-
54
-    /**
55
-     * @param array  $props_n_values    incoming values
56
-     * @param string $timezone          incoming timezone (if not set the timezone set for the website will be used.)
57
-     * @param array $date_formats       incoming date_formats in an array where the first value is the date_format
58
-     *                                  and the second value is the time format
59
-     * @return EE_Datetime
60
-     * @throws ReflectionException
61
-     * @throws InvalidArgumentException
62
-     * @throws InvalidInterfaceException
63
-     * @throws InvalidDataTypeException
64
-     * @throws EE_Error
65
-     */
66
-    public static function new_instance($props_n_values = array(), $timezone = null, $date_formats = array())
67
-    {
68
-        $has_object = parent::_check_for_object(
69
-            $props_n_values,
70
-            __CLASS__,
71
-            $timezone,
72
-            $date_formats
73
-        );
74
-        return $has_object
75
-            ? $has_object
76
-            : new self($props_n_values, false, $timezone, $date_formats);
77
-    }
78
-
79
-
80
-    /**
81
-     * @param array  $props_n_values  incoming values from the database
82
-     * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
83
-     *                                the website will be used.
84
-     * @return EE_Datetime
85
-     * @throws ReflectionException
86
-     * @throws InvalidArgumentException
87
-     * @throws InvalidInterfaceException
88
-     * @throws InvalidDataTypeException
89
-     * @throws EE_Error
90
-     */
91
-    public static function new_instance_from_db($props_n_values = array(), $timezone = null)
92
-    {
93
-        return new self($props_n_values, true, $timezone);
94
-    }
95
-
96
-
97
-    /**
98
-     * @param $name
99
-     * @throws ReflectionException
100
-     * @throws InvalidArgumentException
101
-     * @throws InvalidInterfaceException
102
-     * @throws InvalidDataTypeException
103
-     * @throws EE_Error
104
-     */
105
-    public function set_name($name)
106
-    {
107
-        $this->set('DTT_name', $name);
108
-    }
109
-
110
-
111
-    /**
112
-     * @param $description
113
-     * @throws ReflectionException
114
-     * @throws InvalidArgumentException
115
-     * @throws InvalidInterfaceException
116
-     * @throws InvalidDataTypeException
117
-     * @throws EE_Error
118
-     */
119
-    public function set_description($description)
120
-    {
121
-        $this->set('DTT_description', $description);
122
-    }
123
-
124
-
125
-    /**
126
-     * Set event start date
127
-     * set the start date for an event
128
-     *
129
-     * @param string $date a string representation of the event's date ex:  Dec. 25, 2025 or 12-25-2025
130
-     * @throws ReflectionException
131
-     * @throws InvalidArgumentException
132
-     * @throws InvalidInterfaceException
133
-     * @throws InvalidDataTypeException
134
-     * @throws EE_Error
135
-     */
136
-    public function set_start_date($date)
137
-    {
138
-        $this->_set_date_for($date, 'DTT_EVT_start');
139
-    }
140
-
141
-
142
-    /**
143
-     * Set event start time
144
-     * set the start time for an event
145
-     *
146
-     * @param string $time a string representation of the event time ex:  9am  or  7:30 PM
147
-     * @throws ReflectionException
148
-     * @throws InvalidArgumentException
149
-     * @throws InvalidInterfaceException
150
-     * @throws InvalidDataTypeException
151
-     * @throws EE_Error
152
-     */
153
-    public function set_start_time($time)
154
-    {
155
-        $this->_set_time_for($time, 'DTT_EVT_start');
156
-    }
157
-
158
-
159
-    /**
160
-     * Set event end date
161
-     * set the end date for an event
162
-     *
163
-     * @param string $date a string representation of the event's date ex:  Dec. 25, 2025 or 12-25-2025
164
-     * @throws ReflectionException
165
-     * @throws InvalidArgumentException
166
-     * @throws InvalidInterfaceException
167
-     * @throws InvalidDataTypeException
168
-     * @throws EE_Error
169
-     */
170
-    public function set_end_date($date)
171
-    {
172
-        $this->_set_date_for($date, 'DTT_EVT_end');
173
-    }
174
-
175
-
176
-    /**
177
-     * Set event end time
178
-     * set the end time for an event
179
-     *
180
-     * @param string $time a string representation of the event time ex:  9am  or  7:30 PM
181
-     * @throws ReflectionException
182
-     * @throws InvalidArgumentException
183
-     * @throws InvalidInterfaceException
184
-     * @throws InvalidDataTypeException
185
-     * @throws EE_Error
186
-     */
187
-    public function set_end_time($time)
188
-    {
189
-        $this->_set_time_for($time, 'DTT_EVT_end');
190
-    }
191
-
192
-
193
-    /**
194
-     * Set registration limit
195
-     * set the maximum number of attendees that can be registered for this datetime slot
196
-     *
197
-     * @param int $reg_limit
198
-     * @throws ReflectionException
199
-     * @throws InvalidArgumentException
200
-     * @throws InvalidInterfaceException
201
-     * @throws InvalidDataTypeException
202
-     * @throws EE_Error
203
-     */
204
-    public function set_reg_limit($reg_limit)
205
-    {
206
-        $this->set('DTT_reg_limit', $reg_limit);
207
-    }
208
-
209
-
210
-    /**
211
-     * get the number of tickets sold for this datetime slot
212
-     *
213
-     * @return mixed int on success, FALSE on fail
214
-     * @throws ReflectionException
215
-     * @throws InvalidArgumentException
216
-     * @throws InvalidInterfaceException
217
-     * @throws InvalidDataTypeException
218
-     * @throws EE_Error
219
-     */
220
-    public function sold()
221
-    {
222
-        return $this->get_raw('DTT_sold');
223
-    }
224
-
225
-
226
-    /**
227
-     * @param int $sold
228
-     * @throws ReflectionException
229
-     * @throws InvalidArgumentException
230
-     * @throws InvalidInterfaceException
231
-     * @throws InvalidDataTypeException
232
-     * @throws EE_Error
233
-     */
234
-    public function set_sold($sold)
235
-    {
236
-        // sold can not go below zero
237
-        $sold = max(0, $sold);
238
-        $this->set('DTT_sold', $sold);
239
-    }
240
-
241
-
242
-    /**
243
-     * increments sold by amount passed by $qty
244
-     *
245
-     * @param int $qty
246
-     * @throws ReflectionException
247
-     * @throws InvalidArgumentException
248
-     * @throws InvalidInterfaceException
249
-     * @throws InvalidDataTypeException
250
-     * @throws EE_Error
251
-     */
252
-    public function increase_sold($qty = 1)
253
-    {
254
-        $sold = $this->sold() + $qty;
255
-        // remove ticket reservation
256
-        $this->decrease_reserved($qty);
257
-        $this->set_sold($sold);
258
-        do_action(
259
-            'AHEE__EE_Datetime__increase_sold',
260
-            $this,
261
-            $qty,
262
-            $sold
263
-        );
264
-    }
265
-
266
-
267
-    /**
268
-     * decrements (subtracts) sold amount passed by $qty
269
-     *
270
-     * @param int $qty
271
-     * @throws ReflectionException
272
-     * @throws InvalidArgumentException
273
-     * @throws InvalidInterfaceException
274
-     * @throws InvalidDataTypeException
275
-     * @throws EE_Error
276
-     */
277
-    public function decrease_sold($qty = 1)
278
-    {
279
-        $sold = $this->sold() - $qty;
280
-        $this->set_sold($sold);
281
-        do_action(
282
-            'AHEE__EE_Datetime__decrease_sold',
283
-            $this,
284
-            $qty,
285
-            $sold
286
-        );
287
-    }
288
-
289
-
290
-    /**
291
-     * Gets qty of reserved tickets for this datetime
292
-     *
293
-     * @return int
294
-     * @throws ReflectionException
295
-     * @throws InvalidArgumentException
296
-     * @throws InvalidInterfaceException
297
-     * @throws InvalidDataTypeException
298
-     * @throws EE_Error
299
-     */
300
-    public function reserved()
301
-    {
302
-        return $this->get_raw('DTT_reserved');
303
-    }
304
-
305
-
306
-    /**
307
-     * Sets qty of reserved tickets for this datetime
308
-     *
309
-     * @param int $reserved
310
-     * @throws ReflectionException
311
-     * @throws InvalidArgumentException
312
-     * @throws InvalidInterfaceException
313
-     * @throws InvalidDataTypeException
314
-     * @throws EE_Error
315
-     */
316
-    public function set_reserved($reserved)
317
-    {
318
-        // reserved can not go below zero
319
-        $reserved = max(0, (int) $reserved);
320
-        $this->set('DTT_reserved', $reserved);
321
-    }
322
-
323
-
324
-    /**
325
-     * increments reserved by amount passed by $qty
326
-     *
327
-     * @param int $qty
328
-     * @return void
329
-     * @throws ReflectionException
330
-     * @throws InvalidArgumentException
331
-     * @throws InvalidInterfaceException
332
-     * @throws InvalidDataTypeException
333
-     * @throws EE_Error
334
-     */
335
-    public function increase_reserved($qty = 1)
336
-    {
337
-        $reserved = $this->reserved() + absint($qty);
338
-        do_action(
339
-            'AHEE__EE_Datetime__increase_reserved',
340
-            $this,
341
-            $qty,
342
-            $reserved
343
-        );
344
-        $this->set_reserved($reserved);
345
-    }
346
-
347
-
348
-    /**
349
-     * decrements (subtracts) reserved by amount passed by $qty
350
-     *
351
-     * @param int $qty
352
-     * @return void
353
-     * @throws ReflectionException
354
-     * @throws InvalidArgumentException
355
-     * @throws InvalidInterfaceException
356
-     * @throws InvalidDataTypeException
357
-     * @throws EE_Error
358
-     */
359
-    public function decrease_reserved($qty = 1)
360
-    {
361
-        $reserved = $this->reserved() - absint($qty);
362
-        do_action(
363
-            'AHEE__EE_Datetime__decrease_reserved',
364
-            $this,
365
-            $qty,
366
-            $reserved
367
-        );
368
-        $this->set_reserved($reserved);
369
-    }
370
-
371
-
372
-    /**
373
-     * total sold and reserved tickets
374
-     *
375
-     * @return int
376
-     * @throws ReflectionException
377
-     * @throws InvalidArgumentException
378
-     * @throws InvalidInterfaceException
379
-     * @throws InvalidDataTypeException
380
-     * @throws EE_Error
381
-     */
382
-    public function sold_and_reserved()
383
-    {
384
-        return $this->sold() + $this->reserved();
385
-    }
386
-
387
-
388
-    /**
389
-     * returns the datetime name
390
-     *
391
-     * @return string
392
-     * @throws ReflectionException
393
-     * @throws InvalidArgumentException
394
-     * @throws InvalidInterfaceException
395
-     * @throws InvalidDataTypeException
396
-     * @throws EE_Error
397
-     */
398
-    public function name()
399
-    {
400
-        return $this->get('DTT_name');
401
-    }
402
-
403
-
404
-    /**
405
-     * returns the datetime description
406
-     *
407
-     * @return string
408
-     * @throws ReflectionException
409
-     * @throws InvalidArgumentException
410
-     * @throws InvalidInterfaceException
411
-     * @throws InvalidDataTypeException
412
-     * @throws EE_Error
413
-     */
414
-    public function description()
415
-    {
416
-        return $this->get('DTT_description');
417
-    }
418
-
419
-
420
-    /**
421
-     * This helper simply returns whether the event_datetime for the current datetime is a primary datetime
422
-     *
423
-     * @return boolean  TRUE if is primary, FALSE if not.
424
-     * @throws ReflectionException
425
-     * @throws InvalidArgumentException
426
-     * @throws InvalidInterfaceException
427
-     * @throws InvalidDataTypeException
428
-     * @throws EE_Error
429
-     */
430
-    public function is_primary()
431
-    {
432
-        return $this->get('DTT_is_primary');
433
-    }
434
-
435
-
436
-    /**
437
-     * This helper simply returns the order for the datetime
438
-     *
439
-     * @return int  The order of the datetime for this event.
440
-     * @throws ReflectionException
441
-     * @throws InvalidArgumentException
442
-     * @throws InvalidInterfaceException
443
-     * @throws InvalidDataTypeException
444
-     * @throws EE_Error
445
-     */
446
-    public function order()
447
-    {
448
-        return $this->get('DTT_order');
449
-    }
450
-
451
-
452
-    /**
453
-     * This helper simply returns the parent id for the datetime
454
-     *
455
-     * @return int
456
-     * @throws ReflectionException
457
-     * @throws InvalidArgumentException
458
-     * @throws InvalidInterfaceException
459
-     * @throws InvalidDataTypeException
460
-     * @throws EE_Error
461
-     */
462
-    public function parent()
463
-    {
464
-        return $this->get('DTT_parent');
465
-    }
466
-
467
-
468
-    /**
469
-     * show date and/or time
470
-     *
471
-     * @param string $date_or_time    whether to display a date or time or both
472
-     * @param string $start_or_end    whether to display start or end datetimes
473
-     * @param string $dt_frmt
474
-     * @param string $tm_frmt
475
-     * @param bool   $echo            whether we echo or return (note echoing uses "pretty" formats,
476
-     *                                otherwise we use the standard formats)
477
-     * @return string|bool  string on success, FALSE on fail
478
-     * @throws ReflectionException
479
-     * @throws InvalidArgumentException
480
-     * @throws InvalidInterfaceException
481
-     * @throws InvalidDataTypeException
482
-     * @throws EE_Error
483
-     */
484
-    private function _show_datetime(
485
-        $date_or_time = null,
486
-        $start_or_end = 'start',
487
-        $dt_frmt = '',
488
-        $tm_frmt = '',
489
-        $echo = false
490
-    ) {
491
-        $field_name = "DTT_EVT_{$start_or_end}";
492
-        $dtt        = $this->_get_datetime(
493
-            $field_name,
494
-            $dt_frmt,
495
-            $tm_frmt,
496
-            $date_or_time,
497
-            $echo
498
-        );
499
-        if (! $echo) {
500
-            return $dtt;
501
-        }
502
-        return '';
503
-    }
504
-
505
-
506
-    /**
507
-     * get event start date.  Provide either the date format, or NULL to re-use the
508
-     * last-used format, or '' to use the default date format
509
-     *
510
-     * @param string $dt_frmt   string representation of date format defaults to 'F j, Y'
511
-     * @return mixed            string on success, FALSE on fail
512
-     * @throws ReflectionException
513
-     * @throws InvalidArgumentException
514
-     * @throws InvalidInterfaceException
515
-     * @throws InvalidDataTypeException
516
-     * @throws EE_Error
517
-     */
518
-    public function start_date($dt_frmt = '')
519
-    {
520
-        return $this->_show_datetime('D', 'start', $dt_frmt);
521
-    }
522
-
523
-
524
-    /**
525
-     * Echoes start_date()
526
-     *
527
-     * @param string $dt_frmt
528
-     * @throws ReflectionException
529
-     * @throws InvalidArgumentException
530
-     * @throws InvalidInterfaceException
531
-     * @throws InvalidDataTypeException
532
-     * @throws EE_Error
533
-     */
534
-    public function e_start_date($dt_frmt = '')
535
-    {
536
-        $this->_show_datetime('D', 'start', $dt_frmt, null, true);
537
-    }
538
-
539
-
540
-    /**
541
-     * get end date. Provide either the date format, or NULL to re-use the
542
-     * last-used format, or '' to use the default date format
543
-     *
544
-     * @param string $dt_frmt   string representation of date format defaults to 'F j, Y'
545
-     * @return mixed            string on success, FALSE on fail
546
-     * @throws ReflectionException
547
-     * @throws InvalidArgumentException
548
-     * @throws InvalidInterfaceException
549
-     * @throws InvalidDataTypeException
550
-     * @throws EE_Error
551
-     */
552
-    public function end_date($dt_frmt = '')
553
-    {
554
-        return $this->_show_datetime('D', 'end', $dt_frmt);
555
-    }
556
-
557
-
558
-    /**
559
-     * Echoes the end date. See end_date()
560
-     *
561
-     * @param string $dt_frmt
562
-     * @throws ReflectionException
563
-     * @throws InvalidArgumentException
564
-     * @throws InvalidInterfaceException
565
-     * @throws InvalidDataTypeException
566
-     * @throws EE_Error
567
-     */
568
-    public function e_end_date($dt_frmt = '')
569
-    {
570
-        $this->_show_datetime('D', 'end', $dt_frmt, null, true);
571
-    }
572
-
573
-
574
-    /**
575
-     * get date_range - meaning the start AND end date
576
-     *
577
-     * @access public
578
-     * @param string $dt_frmt     string representation of date format defaults to WP settings
579
-     * @param string $conjunction conjunction junction what's your function ?
580
-     *                            this string joins the start date with the end date ie: Jan 01 "to" Dec 31
581
-     * @return mixed              string on success, FALSE on fail
582
-     * @throws ReflectionException
583
-     * @throws InvalidArgumentException
584
-     * @throws InvalidInterfaceException
585
-     * @throws InvalidDataTypeException
586
-     * @throws EE_Error
587
-     */
588
-    public function date_range($dt_frmt = '', $conjunction = ' - ')
589
-    {
590
-        $dt_frmt = ! empty($dt_frmt) ? $dt_frmt : $this->_dt_frmt;
591
-        $start   = str_replace(
592
-            ' ',
593
-            '&nbsp;',
594
-            $this->get_i18n_datetime('DTT_EVT_start', $dt_frmt)
595
-        );
596
-        $end     = str_replace(
597
-            ' ',
598
-            '&nbsp;',
599
-            $this->get_i18n_datetime('DTT_EVT_end', $dt_frmt)
600
-        );
601
-        return $start !== $end ? $start . $conjunction . $end : $start;
602
-    }
603
-
604
-
605
-    /**
606
-     * @param string $dt_frmt
607
-     * @param string $conjunction
608
-     * @throws ReflectionException
609
-     * @throws InvalidArgumentException
610
-     * @throws InvalidInterfaceException
611
-     * @throws InvalidDataTypeException
612
-     * @throws EE_Error
613
-     */
614
-    public function e_date_range($dt_frmt = '', $conjunction = ' - ')
615
-    {
616
-        echo $this->date_range($dt_frmt, $conjunction);
617
-    }
618
-
619
-
620
-    /**
621
-     * get start time
622
-     *
623
-     * @param string $tm_format - string representation of time format defaults to 'g:i a'
624
-     * @return mixed        string on success, FALSE on fail
625
-     * @throws ReflectionException
626
-     * @throws InvalidArgumentException
627
-     * @throws InvalidInterfaceException
628
-     * @throws InvalidDataTypeException
629
-     * @throws EE_Error
630
-     */
631
-    public function start_time($tm_format = '')
632
-    {
633
-        return $this->_show_datetime('T', 'start', null, $tm_format);
634
-    }
635
-
636
-
637
-    /**
638
-     * @param string $tm_format
639
-     * @throws ReflectionException
640
-     * @throws InvalidArgumentException
641
-     * @throws InvalidInterfaceException
642
-     * @throws InvalidDataTypeException
643
-     * @throws EE_Error
644
-     */
645
-    public function e_start_time($tm_format = '')
646
-    {
647
-        $this->_show_datetime('T', 'start', null, $tm_format, true);
648
-    }
649
-
650
-
651
-    /**
652
-     * get end time
653
-     *
654
-     * @param string $tm_format     string representation of time format defaults to 'g:i a'
655
-     * @return mixed                string on success, FALSE on fail
656
-     * @throws ReflectionException
657
-     * @throws InvalidArgumentException
658
-     * @throws InvalidInterfaceException
659
-     * @throws InvalidDataTypeException
660
-     * @throws EE_Error
661
-     */
662
-    public function end_time($tm_format = '')
663
-    {
664
-        return $this->_show_datetime('T', 'end', null, $tm_format);
665
-    }
666
-
667
-
668
-    /**
669
-     * @param string $tm_format
670
-     * @throws ReflectionException
671
-     * @throws InvalidArgumentException
672
-     * @throws InvalidInterfaceException
673
-     * @throws InvalidDataTypeException
674
-     * @throws EE_Error
675
-     */
676
-    public function e_end_time($tm_format = '')
677
-    {
678
-        $this->_show_datetime('T', 'end', null, $tm_format, true);
679
-    }
680
-
681
-
682
-    /**
683
-     * get time_range
684
-     *
685
-     * @access public
686
-     * @param string $tm_format   string representation of time format defaults to 'g:i a'
687
-     * @param string $conjunction conjunction junction what's your function ?
688
-     *                            this string joins the start date with the end date ie: Jan 01 "to" Dec 31
689
-     * @return mixed              string on success, FALSE on fail
690
-     * @throws ReflectionException
691
-     * @throws InvalidArgumentException
692
-     * @throws InvalidInterfaceException
693
-     * @throws InvalidDataTypeException
694
-     * @throws EE_Error
695
-     */
696
-    public function time_range($tm_format = '', $conjunction = ' - ')
697
-    {
698
-        $tm_format = ! empty($tm_format) ? $tm_format : $this->_tm_frmt;
699
-        $start     = str_replace(
700
-            ' ',
701
-            '&nbsp;',
702
-            $this->get_i18n_datetime('DTT_EVT_start', $tm_format)
703
-        );
704
-        $end       = str_replace(
705
-            ' ',
706
-            '&nbsp;',
707
-            $this->get_i18n_datetime('DTT_EVT_end', $tm_format)
708
-        );
709
-        return $start !== $end ? $start . $conjunction . $end : $start;
710
-    }
711
-
712
-
713
-    /**
714
-     * @param string $tm_format
715
-     * @param string $conjunction
716
-     * @throws ReflectionException
717
-     * @throws InvalidArgumentException
718
-     * @throws InvalidInterfaceException
719
-     * @throws InvalidDataTypeException
720
-     * @throws EE_Error
721
-     */
722
-    public function e_time_range($tm_format = '', $conjunction = ' - ')
723
-    {
724
-        echo $this->time_range($tm_format, $conjunction);
725
-    }
726
-
727
-
728
-    /**
729
-     * This returns a range representation of the date and times.
730
-     * Output is dependent on the difference (or similarity) between DTT_EVT_start and DTT_EVT_end.
731
-     * Also, the return value is localized.
732
-     *
733
-     * @param string $dt_format
734
-     * @param string $tm_format
735
-     * @param string $conjunction used between two different dates or times.
736
-     *                            ex: Dec 1{$conjunction}}Dec 6, or 2pm{$conjunction}3pm
737
-     * @param string $separator   used between the date and time formats.
738
-     *                            ex: Dec 1, 2016{$separator}2pm
739
-     * @return string
740
-     * @throws ReflectionException
741
-     * @throws InvalidArgumentException
742
-     * @throws InvalidInterfaceException
743
-     * @throws InvalidDataTypeException
744
-     * @throws EE_Error
745
-     */
746
-    public function date_and_time_range(
747
-        $dt_format = '',
748
-        $tm_format = '',
749
-        $conjunction = ' - ',
750
-        $separator = ' '
751
-    ) {
752
-        $dt_format   = ! empty($dt_format) ? $dt_format : $this->_dt_frmt;
753
-        $tm_format   = ! empty($tm_format) ? $tm_format : $this->_tm_frmt;
754
-        $full_format = $dt_format . $separator . $tm_format;
755
-        //the range output depends on various conditions
756
-        switch (true) {
757
-            //start date timestamp and end date timestamp are the same.
758
-            case ($this->get_raw('DTT_EVT_start') === $this->get_raw('DTT_EVT_end')) :
759
-                $output = $this->get_i18n_datetime('DTT_EVT_start', $full_format);
760
-                break;
761
-            //start and end date are the same but times are different
762
-            case ($this->start_date() === $this->end_date()) :
763
-                $output = $this->get_i18n_datetime('DTT_EVT_start', $full_format)
764
-                          . $conjunction
765
-                          . $this->get_i18n_datetime('DTT_EVT_end', $tm_format);
766
-                break;
767
-            //all other conditions
768
-            default :
769
-                $output = $this->get_i18n_datetime('DTT_EVT_start', $full_format)
770
-                          . $conjunction
771
-                          . $this->get_i18n_datetime('DTT_EVT_end', $full_format);
772
-                break;
773
-        }
774
-        return $output;
775
-    }
776
-
777
-
778
-    /**
779
-     * This echos the results of date and time range.
780
-     *
781
-     * @see date_and_time_range() for more details on purpose.
782
-     * @param string $dt_format
783
-     * @param string $tm_format
784
-     * @param string $conjunction
785
-     * @return void
786
-     * @throws ReflectionException
787
-     * @throws InvalidArgumentException
788
-     * @throws InvalidInterfaceException
789
-     * @throws InvalidDataTypeException
790
-     * @throws EE_Error
791
-     */
792
-    public function e_date_and_time_range($dt_format = '', $tm_format = '', $conjunction = ' - ')
793
-    {
794
-        echo $this->date_and_time_range($dt_format, $tm_format, $conjunction);
795
-    }
796
-
797
-
798
-    /**
799
-     * get start date and start time
800
-     *
801
-     * @param    string $dt_format - string representation of date format defaults to 'F j, Y'
802
-     * @param    string $tm_format - string representation of time format defaults to 'g:i a'
803
-     * @return    mixed    string on success, FALSE on fail
804
-     * @throws ReflectionException
805
-     * @throws InvalidArgumentException
806
-     * @throws InvalidInterfaceException
807
-     * @throws InvalidDataTypeException
808
-     * @throws EE_Error
809
-     */
810
-    public function start_date_and_time($dt_format = '', $tm_format = '')
811
-    {
812
-        return $this->_show_datetime('', 'start', $dt_format, $tm_format);
813
-    }
814
-
815
-
816
-    /**
817
-     * @param string $dt_frmt
818
-     * @param string $tm_format
819
-     * @throws ReflectionException
820
-     * @throws InvalidArgumentException
821
-     * @throws InvalidInterfaceException
822
-     * @throws InvalidDataTypeException
823
-     * @throws EE_Error
824
-     */
825
-    public function e_start_date_and_time($dt_frmt = '', $tm_format = '')
826
-    {
827
-        $this->_show_datetime('', 'start', $dt_frmt, $tm_format, true);
828
-    }
829
-
830
-
831
-    /**
832
-     * Shows the length of the event (start to end time).
833
-     * Can be shown in 'seconds','minutes','hours', or 'days'.
834
-     * By default, rounds up. (So if you use 'days', and then event
835
-     * only occurs for 1 hour, it will return 1 day).
836
-     *
837
-     * @param string $units 'seconds','minutes','hours','days'
838
-     * @param bool   $round_up
839
-     * @return float|int|mixed
840
-     * @throws ReflectionException
841
-     * @throws InvalidArgumentException
842
-     * @throws InvalidInterfaceException
843
-     * @throws InvalidDataTypeException
844
-     * @throws EE_Error
845
-     */
846
-    public function length($units = 'seconds', $round_up = false)
847
-    {
848
-        $start           = $this->get_raw('DTT_EVT_start');
849
-        $end             = $this->get_raw('DTT_EVT_end');
850
-        $length_in_units = $end - $start;
851
-        switch ($units) {
852
-            //NOTE: We purposefully don't use "break;" in order to chain the divisions
853
-            /** @noinspection PhpMissingBreakStatementInspection */
854
-            case 'days':
855
-                $length_in_units /= 24;
856
-            /** @noinspection PhpMissingBreakStatementInspection */
857
-            case 'hours':
858
-                $length_in_units /= 60;
859
-            /** @noinspection PhpMissingBreakStatementInspection */
860
-            case 'minutes':
861
-                $length_in_units /= 60;
862
-            case 'seconds':
863
-            default:
864
-                $length_in_units = ceil($length_in_units);
865
-        }
866
-        if ($round_up) {
867
-            $length_in_units = max($length_in_units, 1);
868
-        }
869
-        return $length_in_units;
870
-    }
871
-
872
-
873
-    /**
874
-     *        get end date and time
875
-     *
876
-     * @param string $dt_frmt   - string representation of date format defaults to 'F j, Y'
877
-     * @param string $tm_format - string representation of time format defaults to 'g:i a'
878
-     * @return    mixed                string on success, FALSE on fail
879
-     * @throws ReflectionException
880
-     * @throws InvalidArgumentException
881
-     * @throws InvalidInterfaceException
882
-     * @throws InvalidDataTypeException
883
-     * @throws EE_Error
884
-     */
885
-    public function end_date_and_time($dt_frmt = '', $tm_format = '')
886
-    {
887
-        return $this->_show_datetime('', 'end', $dt_frmt, $tm_format);
888
-    }
889
-
890
-
891
-    /**
892
-     * @param string $dt_frmt
893
-     * @param string $tm_format
894
-     * @throws ReflectionException
895
-     * @throws InvalidArgumentException
896
-     * @throws InvalidInterfaceException
897
-     * @throws InvalidDataTypeException
898
-     * @throws EE_Error
899
-     */
900
-    public function e_end_date_and_time($dt_frmt = '', $tm_format = '')
901
-    {
902
-        $this->_show_datetime('', 'end', $dt_frmt, $tm_format, true);
903
-    }
904
-
905
-
906
-    /**
907
-     *        get start timestamp
908
-     *
909
-     * @return        int
910
-     * @throws ReflectionException
911
-     * @throws InvalidArgumentException
912
-     * @throws InvalidInterfaceException
913
-     * @throws InvalidDataTypeException
914
-     * @throws EE_Error
915
-     */
916
-    public function start()
917
-    {
918
-        return $this->get_raw('DTT_EVT_start');
919
-    }
920
-
921
-
922
-    /**
923
-     *        get end timestamp
924
-     *
925
-     * @return        int
926
-     * @throws ReflectionException
927
-     * @throws InvalidArgumentException
928
-     * @throws InvalidInterfaceException
929
-     * @throws InvalidDataTypeException
930
-     * @throws EE_Error
931
-     */
932
-    public function end()
933
-    {
934
-        return $this->get_raw('DTT_EVT_end');
935
-    }
936
-
937
-
938
-    /**
939
-     *    get the registration limit for this datetime slot
940
-     *
941
-     * @return        mixed        int on success, FALSE on fail
942
-     * @throws ReflectionException
943
-     * @throws InvalidArgumentException
944
-     * @throws InvalidInterfaceException
945
-     * @throws InvalidDataTypeException
946
-     * @throws EE_Error
947
-     */
948
-    public function reg_limit()
949
-    {
950
-        return $this->get_raw('DTT_reg_limit');
951
-    }
952
-
953
-
954
-    /**
955
-     *    have the tickets sold for this datetime, met or exceed the registration limit ?
956
-     *
957
-     * @return        boolean
958
-     * @throws ReflectionException
959
-     * @throws InvalidArgumentException
960
-     * @throws InvalidInterfaceException
961
-     * @throws InvalidDataTypeException
962
-     * @throws EE_Error
963
-     */
964
-    public function sold_out()
965
-    {
966
-        return $this->reg_limit() > 0 && $this->sold() >= $this->reg_limit();
967
-    }
968
-
969
-
970
-    /**
971
-     * return the total number of spaces remaining at this venue.
972
-     * This only takes the venue's capacity into account, NOT the tickets available for sale
973
-     *
974
-     * @param bool $consider_tickets Whether to consider tickets remaining when determining if there are any spaces left
975
-     *                               Because if all tickets attached to this datetime have no spaces left,
976
-     *                               then this datetime IS effectively sold out.
977
-     *                               However, there are cases where we just want to know the spaces
978
-     *                               remaining for this particular datetime, hence the flag.
979
-     * @return int
980
-     * @throws ReflectionException
981
-     * @throws InvalidArgumentException
982
-     * @throws InvalidInterfaceException
983
-     * @throws InvalidDataTypeException
984
-     * @throws EE_Error
985
-     */
986
-    public function spaces_remaining($consider_tickets = false)
987
-    {
988
-        // tickets remaining available for purchase
989
-        //no need for special checks for infinite, because if DTT_reg_limit == EE_INF, then EE_INF - x = EE_INF
990
-        $dtt_remaining = $this->reg_limit() - $this->sold_and_reserved();
991
-        if (! $consider_tickets) {
992
-            return $dtt_remaining;
993
-        }
994
-        $tickets_remaining = $this->tickets_remaining();
995
-        return min($dtt_remaining, $tickets_remaining);
996
-    }
997
-
998
-
999
-    /**
1000
-     * Counts the total tickets available
1001
-     * (from all the different types of tickets which are available for this datetime).
1002
-     *
1003
-     * @param array $query_params like EEM_Base::get_all's
1004
-     * @return int
1005
-     * @throws ReflectionException
1006
-     * @throws InvalidArgumentException
1007
-     * @throws InvalidInterfaceException
1008
-     * @throws InvalidDataTypeException
1009
-     * @throws EE_Error
1010
-     */
1011
-    public function tickets_remaining($query_params = array())
1012
-    {
1013
-        $sum     = 0;
1014
-        $tickets = $this->tickets($query_params);
1015
-        if (! empty($tickets)) {
1016
-            foreach ($tickets as $ticket) {
1017
-                if ($ticket instanceof EE_Ticket) {
1018
-                    // get the actual amount of tickets that can be sold
1019
-                    $qty = $ticket->qty('saleable');
1020
-                    if ($qty === EE_INF) {
1021
-                        return EE_INF;
1022
-                    }
1023
-                    // no negative ticket quantities plz
1024
-                    if ($qty > 0) {
1025
-                        $sum += $qty;
1026
-                    }
1027
-                }
1028
-            }
1029
-        }
1030
-        return $sum;
1031
-    }
1032
-
1033
-
1034
-    /**
1035
-     * Gets the count of all the tickets available at this datetime (not ticket types)
1036
-     * before any were sold
1037
-     *
1038
-     * @param array $query_params like EEM_Base::get_all's
1039
-     * @return int
1040
-     * @throws ReflectionException
1041
-     * @throws InvalidArgumentException
1042
-     * @throws InvalidInterfaceException
1043
-     * @throws InvalidDataTypeException
1044
-     * @throws EE_Error
1045
-     */
1046
-    public function sum_tickets_initially_available($query_params = array())
1047
-    {
1048
-        return $this->sum_related('Ticket', $query_params, 'TKT_qty');
1049
-    }
1050
-
1051
-
1052
-    /**
1053
-     * Returns the lesser-of-the two: spaces remaining at this datetime, or
1054
-     * the total tickets remaining (a sum of the tickets remaining for each ticket type
1055
-     * that is available for this datetime).
1056
-     *
1057
-     * @return int
1058
-     * @throws ReflectionException
1059
-     * @throws InvalidArgumentException
1060
-     * @throws InvalidInterfaceException
1061
-     * @throws InvalidDataTypeException
1062
-     * @throws EE_Error
1063
-     */
1064
-    public function total_tickets_available_at_this_datetime()
1065
-    {
1066
-        return $this->spaces_remaining(true);
1067
-    }
1068
-
1069
-
1070
-    /**
1071
-     * This simply compares the internal dtt for the given string with NOW
1072
-     * and determines if the date is upcoming or not.
1073
-     *
1074
-     * @access public
1075
-     * @return boolean
1076
-     * @throws ReflectionException
1077
-     * @throws InvalidArgumentException
1078
-     * @throws InvalidInterfaceException
1079
-     * @throws InvalidDataTypeException
1080
-     * @throws EE_Error
1081
-     */
1082
-    public function is_upcoming()
1083
-    {
1084
-        return ($this->get_raw('DTT_EVT_start') > time());
1085
-    }
1086
-
1087
-
1088
-    /**
1089
-     * This simply compares the internal datetime for the given string with NOW
1090
-     * and returns if the date is active (i.e. start and end time)
1091
-     *
1092
-     * @return boolean
1093
-     * @throws ReflectionException
1094
-     * @throws InvalidArgumentException
1095
-     * @throws InvalidInterfaceException
1096
-     * @throws InvalidDataTypeException
1097
-     * @throws EE_Error
1098
-     */
1099
-    public function is_active()
1100
-    {
1101
-        return ($this->get_raw('DTT_EVT_start') < time() && $this->get_raw('DTT_EVT_end') > time());
1102
-    }
1103
-
1104
-
1105
-    /**
1106
-     * This simply compares the internal dtt for the given string with NOW
1107
-     * and determines if the date is expired or not.
1108
-     *
1109
-     * @return boolean
1110
-     * @throws ReflectionException
1111
-     * @throws InvalidArgumentException
1112
-     * @throws InvalidInterfaceException
1113
-     * @throws InvalidDataTypeException
1114
-     * @throws EE_Error
1115
-     */
1116
-    public function is_expired()
1117
-    {
1118
-        return ($this->get_raw('DTT_EVT_end') < time());
1119
-    }
1120
-
1121
-
1122
-    /**
1123
-     * This returns the active status for whether an event is active, upcoming, or expired
1124
-     *
1125
-     * @return int return value will be one of the EE_Datetime status constants.
1126
-     * @throws ReflectionException
1127
-     * @throws InvalidArgumentException
1128
-     * @throws InvalidInterfaceException
1129
-     * @throws InvalidDataTypeException
1130
-     * @throws EE_Error
1131
-     */
1132
-    public function get_active_status()
1133
-    {
1134
-        $total_tickets_for_this_dtt = $this->total_tickets_available_at_this_datetime();
1135
-        if ($total_tickets_for_this_dtt !== false && $total_tickets_for_this_dtt < 1) {
1136
-            return EE_Datetime::sold_out;
1137
-        }
1138
-        if ($this->is_expired()) {
1139
-            return EE_Datetime::expired;
1140
-        }
1141
-        if ($this->is_upcoming()) {
1142
-            return EE_Datetime::upcoming;
1143
-        }
1144
-        if ($this->is_active()) {
1145
-            return EE_Datetime::active;
1146
-        }
1147
-        return null;
1148
-    }
1149
-
1150
-
1151
-    /**
1152
-     * This returns a nice display name for the datetime that is contingent on the span between the dates and times.
1153
-     *
1154
-     * @param  boolean $use_dtt_name if TRUE then we'll use DTT->name() if its not empty.
1155
-     * @return string
1156
-     * @throws ReflectionException
1157
-     * @throws InvalidArgumentException
1158
-     * @throws InvalidInterfaceException
1159
-     * @throws InvalidDataTypeException
1160
-     * @throws EE_Error
1161
-     */
1162
-    public function get_dtt_display_name($use_dtt_name = false)
1163
-    {
1164
-        if ($use_dtt_name) {
1165
-            $dtt_name = $this->name();
1166
-            if (! empty($dtt_name)) {
1167
-                return $dtt_name;
1168
-            }
1169
-        }
1170
-        //first condition is to see if the months are different
1171
-        if (
1172
-            date('m', $this->get_raw('DTT_EVT_start')) !== date('m', $this->get_raw('DTT_EVT_end'))
1173
-        ) {
1174
-            $display_date = $this->start_date('M j\, Y g:i a') . ' - ' . $this->end_date('M j\, Y g:i a');
1175
-            //next condition is if its the same month but different day
1176
-        } else {
1177
-            if (
1178
-                date('m', $this->get_raw('DTT_EVT_start')) === date('m', $this->get_raw('DTT_EVT_end'))
1179
-                && date('d', $this->get_raw('DTT_EVT_start')) !== date('d', $this->get_raw('DTT_EVT_end'))
1180
-            ) {
1181
-                $display_date = $this->start_date('M j\, g:i a') . ' - ' . $this->end_date('M j\, g:i a Y');
1182
-            } else {
1183
-                $display_date = $this->start_date('F j\, Y')
1184
-                                . ' @ '
1185
-                                . $this->start_date('g:i a')
1186
-                                . ' - '
1187
-                                . $this->end_date('g:i a');
1188
-            }
1189
-        }
1190
-        return $display_date;
1191
-    }
1192
-
1193
-
1194
-    /**
1195
-     * Gets all the tickets for this datetime
1196
-     *
1197
-     * @param array $query_params see EEM_Base::get_all()
1198
-     * @return EE_Base_Class[]|EE_Ticket[]
1199
-     * @throws ReflectionException
1200
-     * @throws InvalidArgumentException
1201
-     * @throws InvalidInterfaceException
1202
-     * @throws InvalidDataTypeException
1203
-     * @throws EE_Error
1204
-     */
1205
-    public function tickets($query_params = array())
1206
-    {
1207
-        return $this->get_many_related('Ticket', $query_params);
1208
-    }
1209
-
1210
-
1211
-    /**
1212
-     * Gets all the ticket types currently available for purchase
1213
-     *
1214
-     * @param array $query_params like EEM_Base::get_all's
1215
-     * @return EE_Ticket[]
1216
-     * @throws ReflectionException
1217
-     * @throws InvalidArgumentException
1218
-     * @throws InvalidInterfaceException
1219
-     * @throws InvalidDataTypeException
1220
-     * @throws EE_Error
1221
-     */
1222
-    public function ticket_types_available_for_purchase($query_params = array())
1223
-    {
1224
-        // first check if datetime is valid
1225
-        if ($this->sold_out() || ! ($this->is_upcoming() || $this->is_active())) {
1226
-            return array();
1227
-        }
1228
-        if (empty($query_params)) {
1229
-            $query_params = array(
1230
-                array(
1231
-                    'TKT_start_date' => array('<=', EEM_Ticket::instance()->current_time_for_query('TKT_start_date')),
1232
-                    'TKT_end_date'   => array('>=', EEM_Ticket::instance()->current_time_for_query('TKT_end_date')),
1233
-                    'TKT_deleted'    => false,
1234
-                ),
1235
-            );
1236
-        }
1237
-        return $this->tickets($query_params);
1238
-    }
1239
-
1240
-
1241
-    /**
1242
-     * @return EE_Base_Class|EE_Event
1243
-     * @throws ReflectionException
1244
-     * @throws InvalidArgumentException
1245
-     * @throws InvalidInterfaceException
1246
-     * @throws InvalidDataTypeException
1247
-     * @throws EE_Error
1248
-     */
1249
-    public function event()
1250
-    {
1251
-        return $this->get_first_related('Event');
1252
-    }
1253
-
1254
-
1255
-    /**
1256
-     * Updates the DTT_sold attribute (and saves) based on the number of registrations for this datetime
1257
-     * (via the tickets). into account
1258
-     *
1259
-     * @return int
1260
-     * @throws ReflectionException
1261
-     * @throws InvalidArgumentException
1262
-     * @throws InvalidInterfaceException
1263
-     * @throws InvalidDataTypeException
1264
-     * @throws EE_Error
1265
-     */
1266
-    public function update_sold()
1267
-    {
1268
-        $count_regs_for_this_datetime = EEM_Registration::instance()->count(
1269
-            array(
1270
-                array(
1271
-                    'STS_ID'                 => EEM_Registration::status_id_approved,
1272
-                    'REG_deleted'            => 0,
1273
-                    'Ticket.Datetime.DTT_ID' => $this->ID(),
1274
-                ),
1275
-            )
1276
-        );
1277
-        $sold                         = $this->sold();
1278
-        if ($count_regs_for_this_datetime > $sold) {
1279
-            $this->increase_sold($count_regs_for_this_datetime - $sold);
1280
-            $this->save();
1281
-        } elseif ($count_regs_for_this_datetime < $sold) {
1282
-            $this->decrease_sold($count_regs_for_this_datetime - $sold);
1283
-            $this->save();
1284
-        }
1285
-        return $count_regs_for_this_datetime;
1286
-    }
17
+	/**
18
+	 * constant used by get_active_status, indicates datetime has no more available spaces
19
+	 */
20
+	const sold_out = 'DTS';
21
+
22
+	/**
23
+	 * constant used by get_active_status, indicating datetime is still active (even is not over, can be registered-for)
24
+	 */
25
+	const active = 'DTA';
26
+
27
+	/**
28
+	 * constant used by get_active_status, indicating the datetime cannot be used for registrations yet, but has not
29
+	 * expired
30
+	 */
31
+	const upcoming = 'DTU';
32
+
33
+	/**
34
+	 * Datetime is postponed
35
+	 */
36
+	const postponed = 'DTP';
37
+
38
+	/**
39
+	 * Datetime is cancelled
40
+	 */
41
+	const cancelled = 'DTC';
42
+
43
+	/**
44
+	 * constant used by get_active_status, indicates datetime has expired (event is over)
45
+	 */
46
+	const expired = 'DTE';
47
+
48
+	/**
49
+	 * constant used in various places indicating that an event is INACTIVE (not yet ready to be published)
50
+	 */
51
+	const inactive = 'DTI';
52
+
53
+
54
+	/**
55
+	 * @param array  $props_n_values    incoming values
56
+	 * @param string $timezone          incoming timezone (if not set the timezone set for the website will be used.)
57
+	 * @param array $date_formats       incoming date_formats in an array where the first value is the date_format
58
+	 *                                  and the second value is the time format
59
+	 * @return EE_Datetime
60
+	 * @throws ReflectionException
61
+	 * @throws InvalidArgumentException
62
+	 * @throws InvalidInterfaceException
63
+	 * @throws InvalidDataTypeException
64
+	 * @throws EE_Error
65
+	 */
66
+	public static function new_instance($props_n_values = array(), $timezone = null, $date_formats = array())
67
+	{
68
+		$has_object = parent::_check_for_object(
69
+			$props_n_values,
70
+			__CLASS__,
71
+			$timezone,
72
+			$date_formats
73
+		);
74
+		return $has_object
75
+			? $has_object
76
+			: new self($props_n_values, false, $timezone, $date_formats);
77
+	}
78
+
79
+
80
+	/**
81
+	 * @param array  $props_n_values  incoming values from the database
82
+	 * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
83
+	 *                                the website will be used.
84
+	 * @return EE_Datetime
85
+	 * @throws ReflectionException
86
+	 * @throws InvalidArgumentException
87
+	 * @throws InvalidInterfaceException
88
+	 * @throws InvalidDataTypeException
89
+	 * @throws EE_Error
90
+	 */
91
+	public static function new_instance_from_db($props_n_values = array(), $timezone = null)
92
+	{
93
+		return new self($props_n_values, true, $timezone);
94
+	}
95
+
96
+
97
+	/**
98
+	 * @param $name
99
+	 * @throws ReflectionException
100
+	 * @throws InvalidArgumentException
101
+	 * @throws InvalidInterfaceException
102
+	 * @throws InvalidDataTypeException
103
+	 * @throws EE_Error
104
+	 */
105
+	public function set_name($name)
106
+	{
107
+		$this->set('DTT_name', $name);
108
+	}
109
+
110
+
111
+	/**
112
+	 * @param $description
113
+	 * @throws ReflectionException
114
+	 * @throws InvalidArgumentException
115
+	 * @throws InvalidInterfaceException
116
+	 * @throws InvalidDataTypeException
117
+	 * @throws EE_Error
118
+	 */
119
+	public function set_description($description)
120
+	{
121
+		$this->set('DTT_description', $description);
122
+	}
123
+
124
+
125
+	/**
126
+	 * Set event start date
127
+	 * set the start date for an event
128
+	 *
129
+	 * @param string $date a string representation of the event's date ex:  Dec. 25, 2025 or 12-25-2025
130
+	 * @throws ReflectionException
131
+	 * @throws InvalidArgumentException
132
+	 * @throws InvalidInterfaceException
133
+	 * @throws InvalidDataTypeException
134
+	 * @throws EE_Error
135
+	 */
136
+	public function set_start_date($date)
137
+	{
138
+		$this->_set_date_for($date, 'DTT_EVT_start');
139
+	}
140
+
141
+
142
+	/**
143
+	 * Set event start time
144
+	 * set the start time for an event
145
+	 *
146
+	 * @param string $time a string representation of the event time ex:  9am  or  7:30 PM
147
+	 * @throws ReflectionException
148
+	 * @throws InvalidArgumentException
149
+	 * @throws InvalidInterfaceException
150
+	 * @throws InvalidDataTypeException
151
+	 * @throws EE_Error
152
+	 */
153
+	public function set_start_time($time)
154
+	{
155
+		$this->_set_time_for($time, 'DTT_EVT_start');
156
+	}
157
+
158
+
159
+	/**
160
+	 * Set event end date
161
+	 * set the end date for an event
162
+	 *
163
+	 * @param string $date a string representation of the event's date ex:  Dec. 25, 2025 or 12-25-2025
164
+	 * @throws ReflectionException
165
+	 * @throws InvalidArgumentException
166
+	 * @throws InvalidInterfaceException
167
+	 * @throws InvalidDataTypeException
168
+	 * @throws EE_Error
169
+	 */
170
+	public function set_end_date($date)
171
+	{
172
+		$this->_set_date_for($date, 'DTT_EVT_end');
173
+	}
174
+
175
+
176
+	/**
177
+	 * Set event end time
178
+	 * set the end time for an event
179
+	 *
180
+	 * @param string $time a string representation of the event time ex:  9am  or  7:30 PM
181
+	 * @throws ReflectionException
182
+	 * @throws InvalidArgumentException
183
+	 * @throws InvalidInterfaceException
184
+	 * @throws InvalidDataTypeException
185
+	 * @throws EE_Error
186
+	 */
187
+	public function set_end_time($time)
188
+	{
189
+		$this->_set_time_for($time, 'DTT_EVT_end');
190
+	}
191
+
192
+
193
+	/**
194
+	 * Set registration limit
195
+	 * set the maximum number of attendees that can be registered for this datetime slot
196
+	 *
197
+	 * @param int $reg_limit
198
+	 * @throws ReflectionException
199
+	 * @throws InvalidArgumentException
200
+	 * @throws InvalidInterfaceException
201
+	 * @throws InvalidDataTypeException
202
+	 * @throws EE_Error
203
+	 */
204
+	public function set_reg_limit($reg_limit)
205
+	{
206
+		$this->set('DTT_reg_limit', $reg_limit);
207
+	}
208
+
209
+
210
+	/**
211
+	 * get the number of tickets sold for this datetime slot
212
+	 *
213
+	 * @return mixed int on success, FALSE on fail
214
+	 * @throws ReflectionException
215
+	 * @throws InvalidArgumentException
216
+	 * @throws InvalidInterfaceException
217
+	 * @throws InvalidDataTypeException
218
+	 * @throws EE_Error
219
+	 */
220
+	public function sold()
221
+	{
222
+		return $this->get_raw('DTT_sold');
223
+	}
224
+
225
+
226
+	/**
227
+	 * @param int $sold
228
+	 * @throws ReflectionException
229
+	 * @throws InvalidArgumentException
230
+	 * @throws InvalidInterfaceException
231
+	 * @throws InvalidDataTypeException
232
+	 * @throws EE_Error
233
+	 */
234
+	public function set_sold($sold)
235
+	{
236
+		// sold can not go below zero
237
+		$sold = max(0, $sold);
238
+		$this->set('DTT_sold', $sold);
239
+	}
240
+
241
+
242
+	/**
243
+	 * increments sold by amount passed by $qty
244
+	 *
245
+	 * @param int $qty
246
+	 * @throws ReflectionException
247
+	 * @throws InvalidArgumentException
248
+	 * @throws InvalidInterfaceException
249
+	 * @throws InvalidDataTypeException
250
+	 * @throws EE_Error
251
+	 */
252
+	public function increase_sold($qty = 1)
253
+	{
254
+		$sold = $this->sold() + $qty;
255
+		// remove ticket reservation
256
+		$this->decrease_reserved($qty);
257
+		$this->set_sold($sold);
258
+		do_action(
259
+			'AHEE__EE_Datetime__increase_sold',
260
+			$this,
261
+			$qty,
262
+			$sold
263
+		);
264
+	}
265
+
266
+
267
+	/**
268
+	 * decrements (subtracts) sold amount passed by $qty
269
+	 *
270
+	 * @param int $qty
271
+	 * @throws ReflectionException
272
+	 * @throws InvalidArgumentException
273
+	 * @throws InvalidInterfaceException
274
+	 * @throws InvalidDataTypeException
275
+	 * @throws EE_Error
276
+	 */
277
+	public function decrease_sold($qty = 1)
278
+	{
279
+		$sold = $this->sold() - $qty;
280
+		$this->set_sold($sold);
281
+		do_action(
282
+			'AHEE__EE_Datetime__decrease_sold',
283
+			$this,
284
+			$qty,
285
+			$sold
286
+		);
287
+	}
288
+
289
+
290
+	/**
291
+	 * Gets qty of reserved tickets for this datetime
292
+	 *
293
+	 * @return int
294
+	 * @throws ReflectionException
295
+	 * @throws InvalidArgumentException
296
+	 * @throws InvalidInterfaceException
297
+	 * @throws InvalidDataTypeException
298
+	 * @throws EE_Error
299
+	 */
300
+	public function reserved()
301
+	{
302
+		return $this->get_raw('DTT_reserved');
303
+	}
304
+
305
+
306
+	/**
307
+	 * Sets qty of reserved tickets for this datetime
308
+	 *
309
+	 * @param int $reserved
310
+	 * @throws ReflectionException
311
+	 * @throws InvalidArgumentException
312
+	 * @throws InvalidInterfaceException
313
+	 * @throws InvalidDataTypeException
314
+	 * @throws EE_Error
315
+	 */
316
+	public function set_reserved($reserved)
317
+	{
318
+		// reserved can not go below zero
319
+		$reserved = max(0, (int) $reserved);
320
+		$this->set('DTT_reserved', $reserved);
321
+	}
322
+
323
+
324
+	/**
325
+	 * increments reserved by amount passed by $qty
326
+	 *
327
+	 * @param int $qty
328
+	 * @return void
329
+	 * @throws ReflectionException
330
+	 * @throws InvalidArgumentException
331
+	 * @throws InvalidInterfaceException
332
+	 * @throws InvalidDataTypeException
333
+	 * @throws EE_Error
334
+	 */
335
+	public function increase_reserved($qty = 1)
336
+	{
337
+		$reserved = $this->reserved() + absint($qty);
338
+		do_action(
339
+			'AHEE__EE_Datetime__increase_reserved',
340
+			$this,
341
+			$qty,
342
+			$reserved
343
+		);
344
+		$this->set_reserved($reserved);
345
+	}
346
+
347
+
348
+	/**
349
+	 * decrements (subtracts) reserved by amount passed by $qty
350
+	 *
351
+	 * @param int $qty
352
+	 * @return void
353
+	 * @throws ReflectionException
354
+	 * @throws InvalidArgumentException
355
+	 * @throws InvalidInterfaceException
356
+	 * @throws InvalidDataTypeException
357
+	 * @throws EE_Error
358
+	 */
359
+	public function decrease_reserved($qty = 1)
360
+	{
361
+		$reserved = $this->reserved() - absint($qty);
362
+		do_action(
363
+			'AHEE__EE_Datetime__decrease_reserved',
364
+			$this,
365
+			$qty,
366
+			$reserved
367
+		);
368
+		$this->set_reserved($reserved);
369
+	}
370
+
371
+
372
+	/**
373
+	 * total sold and reserved tickets
374
+	 *
375
+	 * @return int
376
+	 * @throws ReflectionException
377
+	 * @throws InvalidArgumentException
378
+	 * @throws InvalidInterfaceException
379
+	 * @throws InvalidDataTypeException
380
+	 * @throws EE_Error
381
+	 */
382
+	public function sold_and_reserved()
383
+	{
384
+		return $this->sold() + $this->reserved();
385
+	}
386
+
387
+
388
+	/**
389
+	 * returns the datetime name
390
+	 *
391
+	 * @return string
392
+	 * @throws ReflectionException
393
+	 * @throws InvalidArgumentException
394
+	 * @throws InvalidInterfaceException
395
+	 * @throws InvalidDataTypeException
396
+	 * @throws EE_Error
397
+	 */
398
+	public function name()
399
+	{
400
+		return $this->get('DTT_name');
401
+	}
402
+
403
+
404
+	/**
405
+	 * returns the datetime description
406
+	 *
407
+	 * @return string
408
+	 * @throws ReflectionException
409
+	 * @throws InvalidArgumentException
410
+	 * @throws InvalidInterfaceException
411
+	 * @throws InvalidDataTypeException
412
+	 * @throws EE_Error
413
+	 */
414
+	public function description()
415
+	{
416
+		return $this->get('DTT_description');
417
+	}
418
+
419
+
420
+	/**
421
+	 * This helper simply returns whether the event_datetime for the current datetime is a primary datetime
422
+	 *
423
+	 * @return boolean  TRUE if is primary, FALSE if not.
424
+	 * @throws ReflectionException
425
+	 * @throws InvalidArgumentException
426
+	 * @throws InvalidInterfaceException
427
+	 * @throws InvalidDataTypeException
428
+	 * @throws EE_Error
429
+	 */
430
+	public function is_primary()
431
+	{
432
+		return $this->get('DTT_is_primary');
433
+	}
434
+
435
+
436
+	/**
437
+	 * This helper simply returns the order for the datetime
438
+	 *
439
+	 * @return int  The order of the datetime for this event.
440
+	 * @throws ReflectionException
441
+	 * @throws InvalidArgumentException
442
+	 * @throws InvalidInterfaceException
443
+	 * @throws InvalidDataTypeException
444
+	 * @throws EE_Error
445
+	 */
446
+	public function order()
447
+	{
448
+		return $this->get('DTT_order');
449
+	}
450
+
451
+
452
+	/**
453
+	 * This helper simply returns the parent id for the datetime
454
+	 *
455
+	 * @return int
456
+	 * @throws ReflectionException
457
+	 * @throws InvalidArgumentException
458
+	 * @throws InvalidInterfaceException
459
+	 * @throws InvalidDataTypeException
460
+	 * @throws EE_Error
461
+	 */
462
+	public function parent()
463
+	{
464
+		return $this->get('DTT_parent');
465
+	}
466
+
467
+
468
+	/**
469
+	 * show date and/or time
470
+	 *
471
+	 * @param string $date_or_time    whether to display a date or time or both
472
+	 * @param string $start_or_end    whether to display start or end datetimes
473
+	 * @param string $dt_frmt
474
+	 * @param string $tm_frmt
475
+	 * @param bool   $echo            whether we echo or return (note echoing uses "pretty" formats,
476
+	 *                                otherwise we use the standard formats)
477
+	 * @return string|bool  string on success, FALSE on fail
478
+	 * @throws ReflectionException
479
+	 * @throws InvalidArgumentException
480
+	 * @throws InvalidInterfaceException
481
+	 * @throws InvalidDataTypeException
482
+	 * @throws EE_Error
483
+	 */
484
+	private function _show_datetime(
485
+		$date_or_time = null,
486
+		$start_or_end = 'start',
487
+		$dt_frmt = '',
488
+		$tm_frmt = '',
489
+		$echo = false
490
+	) {
491
+		$field_name = "DTT_EVT_{$start_or_end}";
492
+		$dtt        = $this->_get_datetime(
493
+			$field_name,
494
+			$dt_frmt,
495
+			$tm_frmt,
496
+			$date_or_time,
497
+			$echo
498
+		);
499
+		if (! $echo) {
500
+			return $dtt;
501
+		}
502
+		return '';
503
+	}
504
+
505
+
506
+	/**
507
+	 * get event start date.  Provide either the date format, or NULL to re-use the
508
+	 * last-used format, or '' to use the default date format
509
+	 *
510
+	 * @param string $dt_frmt   string representation of date format defaults to 'F j, Y'
511
+	 * @return mixed            string on success, FALSE on fail
512
+	 * @throws ReflectionException
513
+	 * @throws InvalidArgumentException
514
+	 * @throws InvalidInterfaceException
515
+	 * @throws InvalidDataTypeException
516
+	 * @throws EE_Error
517
+	 */
518
+	public function start_date($dt_frmt = '')
519
+	{
520
+		return $this->_show_datetime('D', 'start', $dt_frmt);
521
+	}
522
+
523
+
524
+	/**
525
+	 * Echoes start_date()
526
+	 *
527
+	 * @param string $dt_frmt
528
+	 * @throws ReflectionException
529
+	 * @throws InvalidArgumentException
530
+	 * @throws InvalidInterfaceException
531
+	 * @throws InvalidDataTypeException
532
+	 * @throws EE_Error
533
+	 */
534
+	public function e_start_date($dt_frmt = '')
535
+	{
536
+		$this->_show_datetime('D', 'start', $dt_frmt, null, true);
537
+	}
538
+
539
+
540
+	/**
541
+	 * get end date. Provide either the date format, or NULL to re-use the
542
+	 * last-used format, or '' to use the default date format
543
+	 *
544
+	 * @param string $dt_frmt   string representation of date format defaults to 'F j, Y'
545
+	 * @return mixed            string on success, FALSE on fail
546
+	 * @throws ReflectionException
547
+	 * @throws InvalidArgumentException
548
+	 * @throws InvalidInterfaceException
549
+	 * @throws InvalidDataTypeException
550
+	 * @throws EE_Error
551
+	 */
552
+	public function end_date($dt_frmt = '')
553
+	{
554
+		return $this->_show_datetime('D', 'end', $dt_frmt);
555
+	}
556
+
557
+
558
+	/**
559
+	 * Echoes the end date. See end_date()
560
+	 *
561
+	 * @param string $dt_frmt
562
+	 * @throws ReflectionException
563
+	 * @throws InvalidArgumentException
564
+	 * @throws InvalidInterfaceException
565
+	 * @throws InvalidDataTypeException
566
+	 * @throws EE_Error
567
+	 */
568
+	public function e_end_date($dt_frmt = '')
569
+	{
570
+		$this->_show_datetime('D', 'end', $dt_frmt, null, true);
571
+	}
572
+
573
+
574
+	/**
575
+	 * get date_range - meaning the start AND end date
576
+	 *
577
+	 * @access public
578
+	 * @param string $dt_frmt     string representation of date format defaults to WP settings
579
+	 * @param string $conjunction conjunction junction what's your function ?
580
+	 *                            this string joins the start date with the end date ie: Jan 01 "to" Dec 31
581
+	 * @return mixed              string on success, FALSE on fail
582
+	 * @throws ReflectionException
583
+	 * @throws InvalidArgumentException
584
+	 * @throws InvalidInterfaceException
585
+	 * @throws InvalidDataTypeException
586
+	 * @throws EE_Error
587
+	 */
588
+	public function date_range($dt_frmt = '', $conjunction = ' - ')
589
+	{
590
+		$dt_frmt = ! empty($dt_frmt) ? $dt_frmt : $this->_dt_frmt;
591
+		$start   = str_replace(
592
+			' ',
593
+			'&nbsp;',
594
+			$this->get_i18n_datetime('DTT_EVT_start', $dt_frmt)
595
+		);
596
+		$end     = str_replace(
597
+			' ',
598
+			'&nbsp;',
599
+			$this->get_i18n_datetime('DTT_EVT_end', $dt_frmt)
600
+		);
601
+		return $start !== $end ? $start . $conjunction . $end : $start;
602
+	}
603
+
604
+
605
+	/**
606
+	 * @param string $dt_frmt
607
+	 * @param string $conjunction
608
+	 * @throws ReflectionException
609
+	 * @throws InvalidArgumentException
610
+	 * @throws InvalidInterfaceException
611
+	 * @throws InvalidDataTypeException
612
+	 * @throws EE_Error
613
+	 */
614
+	public function e_date_range($dt_frmt = '', $conjunction = ' - ')
615
+	{
616
+		echo $this->date_range($dt_frmt, $conjunction);
617
+	}
618
+
619
+
620
+	/**
621
+	 * get start time
622
+	 *
623
+	 * @param string $tm_format - string representation of time format defaults to 'g:i a'
624
+	 * @return mixed        string on success, FALSE on fail
625
+	 * @throws ReflectionException
626
+	 * @throws InvalidArgumentException
627
+	 * @throws InvalidInterfaceException
628
+	 * @throws InvalidDataTypeException
629
+	 * @throws EE_Error
630
+	 */
631
+	public function start_time($tm_format = '')
632
+	{
633
+		return $this->_show_datetime('T', 'start', null, $tm_format);
634
+	}
635
+
636
+
637
+	/**
638
+	 * @param string $tm_format
639
+	 * @throws ReflectionException
640
+	 * @throws InvalidArgumentException
641
+	 * @throws InvalidInterfaceException
642
+	 * @throws InvalidDataTypeException
643
+	 * @throws EE_Error
644
+	 */
645
+	public function e_start_time($tm_format = '')
646
+	{
647
+		$this->_show_datetime('T', 'start', null, $tm_format, true);
648
+	}
649
+
650
+
651
+	/**
652
+	 * get end time
653
+	 *
654
+	 * @param string $tm_format     string representation of time format defaults to 'g:i a'
655
+	 * @return mixed                string on success, FALSE on fail
656
+	 * @throws ReflectionException
657
+	 * @throws InvalidArgumentException
658
+	 * @throws InvalidInterfaceException
659
+	 * @throws InvalidDataTypeException
660
+	 * @throws EE_Error
661
+	 */
662
+	public function end_time($tm_format = '')
663
+	{
664
+		return $this->_show_datetime('T', 'end', null, $tm_format);
665
+	}
666
+
667
+
668
+	/**
669
+	 * @param string $tm_format
670
+	 * @throws ReflectionException
671
+	 * @throws InvalidArgumentException
672
+	 * @throws InvalidInterfaceException
673
+	 * @throws InvalidDataTypeException
674
+	 * @throws EE_Error
675
+	 */
676
+	public function e_end_time($tm_format = '')
677
+	{
678
+		$this->_show_datetime('T', 'end', null, $tm_format, true);
679
+	}
680
+
681
+
682
+	/**
683
+	 * get time_range
684
+	 *
685
+	 * @access public
686
+	 * @param string $tm_format   string representation of time format defaults to 'g:i a'
687
+	 * @param string $conjunction conjunction junction what's your function ?
688
+	 *                            this string joins the start date with the end date ie: Jan 01 "to" Dec 31
689
+	 * @return mixed              string on success, FALSE on fail
690
+	 * @throws ReflectionException
691
+	 * @throws InvalidArgumentException
692
+	 * @throws InvalidInterfaceException
693
+	 * @throws InvalidDataTypeException
694
+	 * @throws EE_Error
695
+	 */
696
+	public function time_range($tm_format = '', $conjunction = ' - ')
697
+	{
698
+		$tm_format = ! empty($tm_format) ? $tm_format : $this->_tm_frmt;
699
+		$start     = str_replace(
700
+			' ',
701
+			'&nbsp;',
702
+			$this->get_i18n_datetime('DTT_EVT_start', $tm_format)
703
+		);
704
+		$end       = str_replace(
705
+			' ',
706
+			'&nbsp;',
707
+			$this->get_i18n_datetime('DTT_EVT_end', $tm_format)
708
+		);
709
+		return $start !== $end ? $start . $conjunction . $end : $start;
710
+	}
711
+
712
+
713
+	/**
714
+	 * @param string $tm_format
715
+	 * @param string $conjunction
716
+	 * @throws ReflectionException
717
+	 * @throws InvalidArgumentException
718
+	 * @throws InvalidInterfaceException
719
+	 * @throws InvalidDataTypeException
720
+	 * @throws EE_Error
721
+	 */
722
+	public function e_time_range($tm_format = '', $conjunction = ' - ')
723
+	{
724
+		echo $this->time_range($tm_format, $conjunction);
725
+	}
726
+
727
+
728
+	/**
729
+	 * This returns a range representation of the date and times.
730
+	 * Output is dependent on the difference (or similarity) between DTT_EVT_start and DTT_EVT_end.
731
+	 * Also, the return value is localized.
732
+	 *
733
+	 * @param string $dt_format
734
+	 * @param string $tm_format
735
+	 * @param string $conjunction used between two different dates or times.
736
+	 *                            ex: Dec 1{$conjunction}}Dec 6, or 2pm{$conjunction}3pm
737
+	 * @param string $separator   used between the date and time formats.
738
+	 *                            ex: Dec 1, 2016{$separator}2pm
739
+	 * @return string
740
+	 * @throws ReflectionException
741
+	 * @throws InvalidArgumentException
742
+	 * @throws InvalidInterfaceException
743
+	 * @throws InvalidDataTypeException
744
+	 * @throws EE_Error
745
+	 */
746
+	public function date_and_time_range(
747
+		$dt_format = '',
748
+		$tm_format = '',
749
+		$conjunction = ' - ',
750
+		$separator = ' '
751
+	) {
752
+		$dt_format   = ! empty($dt_format) ? $dt_format : $this->_dt_frmt;
753
+		$tm_format   = ! empty($tm_format) ? $tm_format : $this->_tm_frmt;
754
+		$full_format = $dt_format . $separator . $tm_format;
755
+		//the range output depends on various conditions
756
+		switch (true) {
757
+			//start date timestamp and end date timestamp are the same.
758
+			case ($this->get_raw('DTT_EVT_start') === $this->get_raw('DTT_EVT_end')) :
759
+				$output = $this->get_i18n_datetime('DTT_EVT_start', $full_format);
760
+				break;
761
+			//start and end date are the same but times are different
762
+			case ($this->start_date() === $this->end_date()) :
763
+				$output = $this->get_i18n_datetime('DTT_EVT_start', $full_format)
764
+						  . $conjunction
765
+						  . $this->get_i18n_datetime('DTT_EVT_end', $tm_format);
766
+				break;
767
+			//all other conditions
768
+			default :
769
+				$output = $this->get_i18n_datetime('DTT_EVT_start', $full_format)
770
+						  . $conjunction
771
+						  . $this->get_i18n_datetime('DTT_EVT_end', $full_format);
772
+				break;
773
+		}
774
+		return $output;
775
+	}
776
+
777
+
778
+	/**
779
+	 * This echos the results of date and time range.
780
+	 *
781
+	 * @see date_and_time_range() for more details on purpose.
782
+	 * @param string $dt_format
783
+	 * @param string $tm_format
784
+	 * @param string $conjunction
785
+	 * @return void
786
+	 * @throws ReflectionException
787
+	 * @throws InvalidArgumentException
788
+	 * @throws InvalidInterfaceException
789
+	 * @throws InvalidDataTypeException
790
+	 * @throws EE_Error
791
+	 */
792
+	public function e_date_and_time_range($dt_format = '', $tm_format = '', $conjunction = ' - ')
793
+	{
794
+		echo $this->date_and_time_range($dt_format, $tm_format, $conjunction);
795
+	}
796
+
797
+
798
+	/**
799
+	 * get start date and start time
800
+	 *
801
+	 * @param    string $dt_format - string representation of date format defaults to 'F j, Y'
802
+	 * @param    string $tm_format - string representation of time format defaults to 'g:i a'
803
+	 * @return    mixed    string on success, FALSE on fail
804
+	 * @throws ReflectionException
805
+	 * @throws InvalidArgumentException
806
+	 * @throws InvalidInterfaceException
807
+	 * @throws InvalidDataTypeException
808
+	 * @throws EE_Error
809
+	 */
810
+	public function start_date_and_time($dt_format = '', $tm_format = '')
811
+	{
812
+		return $this->_show_datetime('', 'start', $dt_format, $tm_format);
813
+	}
814
+
815
+
816
+	/**
817
+	 * @param string $dt_frmt
818
+	 * @param string $tm_format
819
+	 * @throws ReflectionException
820
+	 * @throws InvalidArgumentException
821
+	 * @throws InvalidInterfaceException
822
+	 * @throws InvalidDataTypeException
823
+	 * @throws EE_Error
824
+	 */
825
+	public function e_start_date_and_time($dt_frmt = '', $tm_format = '')
826
+	{
827
+		$this->_show_datetime('', 'start', $dt_frmt, $tm_format, true);
828
+	}
829
+
830
+
831
+	/**
832
+	 * Shows the length of the event (start to end time).
833
+	 * Can be shown in 'seconds','minutes','hours', or 'days'.
834
+	 * By default, rounds up. (So if you use 'days', and then event
835
+	 * only occurs for 1 hour, it will return 1 day).
836
+	 *
837
+	 * @param string $units 'seconds','minutes','hours','days'
838
+	 * @param bool   $round_up
839
+	 * @return float|int|mixed
840
+	 * @throws ReflectionException
841
+	 * @throws InvalidArgumentException
842
+	 * @throws InvalidInterfaceException
843
+	 * @throws InvalidDataTypeException
844
+	 * @throws EE_Error
845
+	 */
846
+	public function length($units = 'seconds', $round_up = false)
847
+	{
848
+		$start           = $this->get_raw('DTT_EVT_start');
849
+		$end             = $this->get_raw('DTT_EVT_end');
850
+		$length_in_units = $end - $start;
851
+		switch ($units) {
852
+			//NOTE: We purposefully don't use "break;" in order to chain the divisions
853
+			/** @noinspection PhpMissingBreakStatementInspection */
854
+			case 'days':
855
+				$length_in_units /= 24;
856
+			/** @noinspection PhpMissingBreakStatementInspection */
857
+			case 'hours':
858
+				$length_in_units /= 60;
859
+			/** @noinspection PhpMissingBreakStatementInspection */
860
+			case 'minutes':
861
+				$length_in_units /= 60;
862
+			case 'seconds':
863
+			default:
864
+				$length_in_units = ceil($length_in_units);
865
+		}
866
+		if ($round_up) {
867
+			$length_in_units = max($length_in_units, 1);
868
+		}
869
+		return $length_in_units;
870
+	}
871
+
872
+
873
+	/**
874
+	 *        get end date and time
875
+	 *
876
+	 * @param string $dt_frmt   - string representation of date format defaults to 'F j, Y'
877
+	 * @param string $tm_format - string representation of time format defaults to 'g:i a'
878
+	 * @return    mixed                string on success, FALSE on fail
879
+	 * @throws ReflectionException
880
+	 * @throws InvalidArgumentException
881
+	 * @throws InvalidInterfaceException
882
+	 * @throws InvalidDataTypeException
883
+	 * @throws EE_Error
884
+	 */
885
+	public function end_date_and_time($dt_frmt = '', $tm_format = '')
886
+	{
887
+		return $this->_show_datetime('', 'end', $dt_frmt, $tm_format);
888
+	}
889
+
890
+
891
+	/**
892
+	 * @param string $dt_frmt
893
+	 * @param string $tm_format
894
+	 * @throws ReflectionException
895
+	 * @throws InvalidArgumentException
896
+	 * @throws InvalidInterfaceException
897
+	 * @throws InvalidDataTypeException
898
+	 * @throws EE_Error
899
+	 */
900
+	public function e_end_date_and_time($dt_frmt = '', $tm_format = '')
901
+	{
902
+		$this->_show_datetime('', 'end', $dt_frmt, $tm_format, true);
903
+	}
904
+
905
+
906
+	/**
907
+	 *        get start timestamp
908
+	 *
909
+	 * @return        int
910
+	 * @throws ReflectionException
911
+	 * @throws InvalidArgumentException
912
+	 * @throws InvalidInterfaceException
913
+	 * @throws InvalidDataTypeException
914
+	 * @throws EE_Error
915
+	 */
916
+	public function start()
917
+	{
918
+		return $this->get_raw('DTT_EVT_start');
919
+	}
920
+
921
+
922
+	/**
923
+	 *        get end timestamp
924
+	 *
925
+	 * @return        int
926
+	 * @throws ReflectionException
927
+	 * @throws InvalidArgumentException
928
+	 * @throws InvalidInterfaceException
929
+	 * @throws InvalidDataTypeException
930
+	 * @throws EE_Error
931
+	 */
932
+	public function end()
933
+	{
934
+		return $this->get_raw('DTT_EVT_end');
935
+	}
936
+
937
+
938
+	/**
939
+	 *    get the registration limit for this datetime slot
940
+	 *
941
+	 * @return        mixed        int on success, FALSE on fail
942
+	 * @throws ReflectionException
943
+	 * @throws InvalidArgumentException
944
+	 * @throws InvalidInterfaceException
945
+	 * @throws InvalidDataTypeException
946
+	 * @throws EE_Error
947
+	 */
948
+	public function reg_limit()
949
+	{
950
+		return $this->get_raw('DTT_reg_limit');
951
+	}
952
+
953
+
954
+	/**
955
+	 *    have the tickets sold for this datetime, met or exceed the registration limit ?
956
+	 *
957
+	 * @return        boolean
958
+	 * @throws ReflectionException
959
+	 * @throws InvalidArgumentException
960
+	 * @throws InvalidInterfaceException
961
+	 * @throws InvalidDataTypeException
962
+	 * @throws EE_Error
963
+	 */
964
+	public function sold_out()
965
+	{
966
+		return $this->reg_limit() > 0 && $this->sold() >= $this->reg_limit();
967
+	}
968
+
969
+
970
+	/**
971
+	 * return the total number of spaces remaining at this venue.
972
+	 * This only takes the venue's capacity into account, NOT the tickets available for sale
973
+	 *
974
+	 * @param bool $consider_tickets Whether to consider tickets remaining when determining if there are any spaces left
975
+	 *                               Because if all tickets attached to this datetime have no spaces left,
976
+	 *                               then this datetime IS effectively sold out.
977
+	 *                               However, there are cases where we just want to know the spaces
978
+	 *                               remaining for this particular datetime, hence the flag.
979
+	 * @return int
980
+	 * @throws ReflectionException
981
+	 * @throws InvalidArgumentException
982
+	 * @throws InvalidInterfaceException
983
+	 * @throws InvalidDataTypeException
984
+	 * @throws EE_Error
985
+	 */
986
+	public function spaces_remaining($consider_tickets = false)
987
+	{
988
+		// tickets remaining available for purchase
989
+		//no need for special checks for infinite, because if DTT_reg_limit == EE_INF, then EE_INF - x = EE_INF
990
+		$dtt_remaining = $this->reg_limit() - $this->sold_and_reserved();
991
+		if (! $consider_tickets) {
992
+			return $dtt_remaining;
993
+		}
994
+		$tickets_remaining = $this->tickets_remaining();
995
+		return min($dtt_remaining, $tickets_remaining);
996
+	}
997
+
998
+
999
+	/**
1000
+	 * Counts the total tickets available
1001
+	 * (from all the different types of tickets which are available for this datetime).
1002
+	 *
1003
+	 * @param array $query_params like EEM_Base::get_all's
1004
+	 * @return int
1005
+	 * @throws ReflectionException
1006
+	 * @throws InvalidArgumentException
1007
+	 * @throws InvalidInterfaceException
1008
+	 * @throws InvalidDataTypeException
1009
+	 * @throws EE_Error
1010
+	 */
1011
+	public function tickets_remaining($query_params = array())
1012
+	{
1013
+		$sum     = 0;
1014
+		$tickets = $this->tickets($query_params);
1015
+		if (! empty($tickets)) {
1016
+			foreach ($tickets as $ticket) {
1017
+				if ($ticket instanceof EE_Ticket) {
1018
+					// get the actual amount of tickets that can be sold
1019
+					$qty = $ticket->qty('saleable');
1020
+					if ($qty === EE_INF) {
1021
+						return EE_INF;
1022
+					}
1023
+					// no negative ticket quantities plz
1024
+					if ($qty > 0) {
1025
+						$sum += $qty;
1026
+					}
1027
+				}
1028
+			}
1029
+		}
1030
+		return $sum;
1031
+	}
1032
+
1033
+
1034
+	/**
1035
+	 * Gets the count of all the tickets available at this datetime (not ticket types)
1036
+	 * before any were sold
1037
+	 *
1038
+	 * @param array $query_params like EEM_Base::get_all's
1039
+	 * @return int
1040
+	 * @throws ReflectionException
1041
+	 * @throws InvalidArgumentException
1042
+	 * @throws InvalidInterfaceException
1043
+	 * @throws InvalidDataTypeException
1044
+	 * @throws EE_Error
1045
+	 */
1046
+	public function sum_tickets_initially_available($query_params = array())
1047
+	{
1048
+		return $this->sum_related('Ticket', $query_params, 'TKT_qty');
1049
+	}
1050
+
1051
+
1052
+	/**
1053
+	 * Returns the lesser-of-the two: spaces remaining at this datetime, or
1054
+	 * the total tickets remaining (a sum of the tickets remaining for each ticket type
1055
+	 * that is available for this datetime).
1056
+	 *
1057
+	 * @return int
1058
+	 * @throws ReflectionException
1059
+	 * @throws InvalidArgumentException
1060
+	 * @throws InvalidInterfaceException
1061
+	 * @throws InvalidDataTypeException
1062
+	 * @throws EE_Error
1063
+	 */
1064
+	public function total_tickets_available_at_this_datetime()
1065
+	{
1066
+		return $this->spaces_remaining(true);
1067
+	}
1068
+
1069
+
1070
+	/**
1071
+	 * This simply compares the internal dtt for the given string with NOW
1072
+	 * and determines if the date is upcoming or not.
1073
+	 *
1074
+	 * @access public
1075
+	 * @return boolean
1076
+	 * @throws ReflectionException
1077
+	 * @throws InvalidArgumentException
1078
+	 * @throws InvalidInterfaceException
1079
+	 * @throws InvalidDataTypeException
1080
+	 * @throws EE_Error
1081
+	 */
1082
+	public function is_upcoming()
1083
+	{
1084
+		return ($this->get_raw('DTT_EVT_start') > time());
1085
+	}
1086
+
1087
+
1088
+	/**
1089
+	 * This simply compares the internal datetime for the given string with NOW
1090
+	 * and returns if the date is active (i.e. start and end time)
1091
+	 *
1092
+	 * @return boolean
1093
+	 * @throws ReflectionException
1094
+	 * @throws InvalidArgumentException
1095
+	 * @throws InvalidInterfaceException
1096
+	 * @throws InvalidDataTypeException
1097
+	 * @throws EE_Error
1098
+	 */
1099
+	public function is_active()
1100
+	{
1101
+		return ($this->get_raw('DTT_EVT_start') < time() && $this->get_raw('DTT_EVT_end') > time());
1102
+	}
1103
+
1104
+
1105
+	/**
1106
+	 * This simply compares the internal dtt for the given string with NOW
1107
+	 * and determines if the date is expired or not.
1108
+	 *
1109
+	 * @return boolean
1110
+	 * @throws ReflectionException
1111
+	 * @throws InvalidArgumentException
1112
+	 * @throws InvalidInterfaceException
1113
+	 * @throws InvalidDataTypeException
1114
+	 * @throws EE_Error
1115
+	 */
1116
+	public function is_expired()
1117
+	{
1118
+		return ($this->get_raw('DTT_EVT_end') < time());
1119
+	}
1120
+
1121
+
1122
+	/**
1123
+	 * This returns the active status for whether an event is active, upcoming, or expired
1124
+	 *
1125
+	 * @return int return value will be one of the EE_Datetime status constants.
1126
+	 * @throws ReflectionException
1127
+	 * @throws InvalidArgumentException
1128
+	 * @throws InvalidInterfaceException
1129
+	 * @throws InvalidDataTypeException
1130
+	 * @throws EE_Error
1131
+	 */
1132
+	public function get_active_status()
1133
+	{
1134
+		$total_tickets_for_this_dtt = $this->total_tickets_available_at_this_datetime();
1135
+		if ($total_tickets_for_this_dtt !== false && $total_tickets_for_this_dtt < 1) {
1136
+			return EE_Datetime::sold_out;
1137
+		}
1138
+		if ($this->is_expired()) {
1139
+			return EE_Datetime::expired;
1140
+		}
1141
+		if ($this->is_upcoming()) {
1142
+			return EE_Datetime::upcoming;
1143
+		}
1144
+		if ($this->is_active()) {
1145
+			return EE_Datetime::active;
1146
+		}
1147
+		return null;
1148
+	}
1149
+
1150
+
1151
+	/**
1152
+	 * This returns a nice display name for the datetime that is contingent on the span between the dates and times.
1153
+	 *
1154
+	 * @param  boolean $use_dtt_name if TRUE then we'll use DTT->name() if its not empty.
1155
+	 * @return string
1156
+	 * @throws ReflectionException
1157
+	 * @throws InvalidArgumentException
1158
+	 * @throws InvalidInterfaceException
1159
+	 * @throws InvalidDataTypeException
1160
+	 * @throws EE_Error
1161
+	 */
1162
+	public function get_dtt_display_name($use_dtt_name = false)
1163
+	{
1164
+		if ($use_dtt_name) {
1165
+			$dtt_name = $this->name();
1166
+			if (! empty($dtt_name)) {
1167
+				return $dtt_name;
1168
+			}
1169
+		}
1170
+		//first condition is to see if the months are different
1171
+		if (
1172
+			date('m', $this->get_raw('DTT_EVT_start')) !== date('m', $this->get_raw('DTT_EVT_end'))
1173
+		) {
1174
+			$display_date = $this->start_date('M j\, Y g:i a') . ' - ' . $this->end_date('M j\, Y g:i a');
1175
+			//next condition is if its the same month but different day
1176
+		} else {
1177
+			if (
1178
+				date('m', $this->get_raw('DTT_EVT_start')) === date('m', $this->get_raw('DTT_EVT_end'))
1179
+				&& date('d', $this->get_raw('DTT_EVT_start')) !== date('d', $this->get_raw('DTT_EVT_end'))
1180
+			) {
1181
+				$display_date = $this->start_date('M j\, g:i a') . ' - ' . $this->end_date('M j\, g:i a Y');
1182
+			} else {
1183
+				$display_date = $this->start_date('F j\, Y')
1184
+								. ' @ '
1185
+								. $this->start_date('g:i a')
1186
+								. ' - '
1187
+								. $this->end_date('g:i a');
1188
+			}
1189
+		}
1190
+		return $display_date;
1191
+	}
1192
+
1193
+
1194
+	/**
1195
+	 * Gets all the tickets for this datetime
1196
+	 *
1197
+	 * @param array $query_params see EEM_Base::get_all()
1198
+	 * @return EE_Base_Class[]|EE_Ticket[]
1199
+	 * @throws ReflectionException
1200
+	 * @throws InvalidArgumentException
1201
+	 * @throws InvalidInterfaceException
1202
+	 * @throws InvalidDataTypeException
1203
+	 * @throws EE_Error
1204
+	 */
1205
+	public function tickets($query_params = array())
1206
+	{
1207
+		return $this->get_many_related('Ticket', $query_params);
1208
+	}
1209
+
1210
+
1211
+	/**
1212
+	 * Gets all the ticket types currently available for purchase
1213
+	 *
1214
+	 * @param array $query_params like EEM_Base::get_all's
1215
+	 * @return EE_Ticket[]
1216
+	 * @throws ReflectionException
1217
+	 * @throws InvalidArgumentException
1218
+	 * @throws InvalidInterfaceException
1219
+	 * @throws InvalidDataTypeException
1220
+	 * @throws EE_Error
1221
+	 */
1222
+	public function ticket_types_available_for_purchase($query_params = array())
1223
+	{
1224
+		// first check if datetime is valid
1225
+		if ($this->sold_out() || ! ($this->is_upcoming() || $this->is_active())) {
1226
+			return array();
1227
+		}
1228
+		if (empty($query_params)) {
1229
+			$query_params = array(
1230
+				array(
1231
+					'TKT_start_date' => array('<=', EEM_Ticket::instance()->current_time_for_query('TKT_start_date')),
1232
+					'TKT_end_date'   => array('>=', EEM_Ticket::instance()->current_time_for_query('TKT_end_date')),
1233
+					'TKT_deleted'    => false,
1234
+				),
1235
+			);
1236
+		}
1237
+		return $this->tickets($query_params);
1238
+	}
1239
+
1240
+
1241
+	/**
1242
+	 * @return EE_Base_Class|EE_Event
1243
+	 * @throws ReflectionException
1244
+	 * @throws InvalidArgumentException
1245
+	 * @throws InvalidInterfaceException
1246
+	 * @throws InvalidDataTypeException
1247
+	 * @throws EE_Error
1248
+	 */
1249
+	public function event()
1250
+	{
1251
+		return $this->get_first_related('Event');
1252
+	}
1253
+
1254
+
1255
+	/**
1256
+	 * Updates the DTT_sold attribute (and saves) based on the number of registrations for this datetime
1257
+	 * (via the tickets). into account
1258
+	 *
1259
+	 * @return int
1260
+	 * @throws ReflectionException
1261
+	 * @throws InvalidArgumentException
1262
+	 * @throws InvalidInterfaceException
1263
+	 * @throws InvalidDataTypeException
1264
+	 * @throws EE_Error
1265
+	 */
1266
+	public function update_sold()
1267
+	{
1268
+		$count_regs_for_this_datetime = EEM_Registration::instance()->count(
1269
+			array(
1270
+				array(
1271
+					'STS_ID'                 => EEM_Registration::status_id_approved,
1272
+					'REG_deleted'            => 0,
1273
+					'Ticket.Datetime.DTT_ID' => $this->ID(),
1274
+				),
1275
+			)
1276
+		);
1277
+		$sold                         = $this->sold();
1278
+		if ($count_regs_for_this_datetime > $sold) {
1279
+			$this->increase_sold($count_regs_for_this_datetime - $sold);
1280
+			$this->save();
1281
+		} elseif ($count_regs_for_this_datetime < $sold) {
1282
+			$this->decrease_sold($count_regs_for_this_datetime - $sold);
1283
+			$this->save();
1284
+		}
1285
+		return $count_regs_for_this_datetime;
1286
+	}
1287 1287
 }
1288 1288
 
1289 1289
 /* End of file EE_Datetime.class.php */
Please login to merge, or discard this patch.
Spacing   +12 added lines, -12 removed lines patch added patch discarded remove patch
@@ -496,7 +496,7 @@  discard block
 block discarded – undo
496 496
             $date_or_time,
497 497
             $echo
498 498
         );
499
-        if (! $echo) {
499
+        if ( ! $echo) {
500 500
             return $dtt;
501 501
         }
502 502
         return '';
@@ -593,12 +593,12 @@  discard block
 block discarded – undo
593 593
             '&nbsp;',
594 594
             $this->get_i18n_datetime('DTT_EVT_start', $dt_frmt)
595 595
         );
596
-        $end     = str_replace(
596
+        $end = str_replace(
597 597
             ' ',
598 598
             '&nbsp;',
599 599
             $this->get_i18n_datetime('DTT_EVT_end', $dt_frmt)
600 600
         );
601
-        return $start !== $end ? $start . $conjunction . $end : $start;
601
+        return $start !== $end ? $start.$conjunction.$end : $start;
602 602
     }
603 603
 
604 604
 
@@ -701,12 +701,12 @@  discard block
 block discarded – undo
701 701
             '&nbsp;',
702 702
             $this->get_i18n_datetime('DTT_EVT_start', $tm_format)
703 703
         );
704
-        $end       = str_replace(
704
+        $end = str_replace(
705 705
             ' ',
706 706
             '&nbsp;',
707 707
             $this->get_i18n_datetime('DTT_EVT_end', $tm_format)
708 708
         );
709
-        return $start !== $end ? $start . $conjunction . $end : $start;
709
+        return $start !== $end ? $start.$conjunction.$end : $start;
710 710
     }
711 711
 
712 712
 
@@ -751,7 +751,7 @@  discard block
 block discarded – undo
751 751
     ) {
752 752
         $dt_format   = ! empty($dt_format) ? $dt_format : $this->_dt_frmt;
753 753
         $tm_format   = ! empty($tm_format) ? $tm_format : $this->_tm_frmt;
754
-        $full_format = $dt_format . $separator . $tm_format;
754
+        $full_format = $dt_format.$separator.$tm_format;
755 755
         //the range output depends on various conditions
756 756
         switch (true) {
757 757
             //start date timestamp and end date timestamp are the same.
@@ -988,7 +988,7 @@  discard block
 block discarded – undo
988 988
         // tickets remaining available for purchase
989 989
         //no need for special checks for infinite, because if DTT_reg_limit == EE_INF, then EE_INF - x = EE_INF
990 990
         $dtt_remaining = $this->reg_limit() - $this->sold_and_reserved();
991
-        if (! $consider_tickets) {
991
+        if ( ! $consider_tickets) {
992 992
             return $dtt_remaining;
993 993
         }
994 994
         $tickets_remaining = $this->tickets_remaining();
@@ -1012,7 +1012,7 @@  discard block
 block discarded – undo
1012 1012
     {
1013 1013
         $sum     = 0;
1014 1014
         $tickets = $this->tickets($query_params);
1015
-        if (! empty($tickets)) {
1015
+        if ( ! empty($tickets)) {
1016 1016
             foreach ($tickets as $ticket) {
1017 1017
                 if ($ticket instanceof EE_Ticket) {
1018 1018
                     // get the actual amount of tickets that can be sold
@@ -1163,7 +1163,7 @@  discard block
 block discarded – undo
1163 1163
     {
1164 1164
         if ($use_dtt_name) {
1165 1165
             $dtt_name = $this->name();
1166
-            if (! empty($dtt_name)) {
1166
+            if ( ! empty($dtt_name)) {
1167 1167
                 return $dtt_name;
1168 1168
             }
1169 1169
         }
@@ -1171,14 +1171,14 @@  discard block
 block discarded – undo
1171 1171
         if (
1172 1172
             date('m', $this->get_raw('DTT_EVT_start')) !== date('m', $this->get_raw('DTT_EVT_end'))
1173 1173
         ) {
1174
-            $display_date = $this->start_date('M j\, Y g:i a') . ' - ' . $this->end_date('M j\, Y g:i a');
1174
+            $display_date = $this->start_date('M j\, Y g:i a').' - '.$this->end_date('M j\, Y g:i a');
1175 1175
             //next condition is if its the same month but different day
1176 1176
         } else {
1177 1177
             if (
1178 1178
                 date('m', $this->get_raw('DTT_EVT_start')) === date('m', $this->get_raw('DTT_EVT_end'))
1179 1179
                 && date('d', $this->get_raw('DTT_EVT_start')) !== date('d', $this->get_raw('DTT_EVT_end'))
1180 1180
             ) {
1181
-                $display_date = $this->start_date('M j\, g:i a') . ' - ' . $this->end_date('M j\, g:i a Y');
1181
+                $display_date = $this->start_date('M j\, g:i a').' - '.$this->end_date('M j\, g:i a Y');
1182 1182
             } else {
1183 1183
                 $display_date = $this->start_date('F j\, Y')
1184 1184
                                 . ' @ '
@@ -1274,7 +1274,7 @@  discard block
 block discarded – undo
1274 1274
                 ),
1275 1275
             )
1276 1276
         );
1277
-        $sold                         = $this->sold();
1277
+        $sold = $this->sold();
1278 1278
         if ($count_regs_for_this_datetime > $sold) {
1279 1279
             $this->increase_sold($count_regs_for_this_datetime - $sold);
1280 1280
             $this->save();
Please login to merge, or discard this patch.
core/db_models/EEM_Datetime.model.php 2 patches
Indentation   +652 added lines, -652 removed lines patch added patch discarded remove patch
@@ -1,5 +1,5 @@  discard block
 block discarded – undo
1 1
 <?php if (! defined('EVENT_ESPRESSO_VERSION')) {
2
-    exit('No direct script access allowed');
2
+	exit('No direct script access allowed');
3 3
 }
4 4
 
5 5
 
@@ -14,657 +14,657 @@  discard block
 block discarded – undo
14 14
 class EEM_Datetime extends EEM_Soft_Delete_Base
15 15
 {
16 16
 
17
-    /**
18
-     * @var EEM_Datetime $_instance
19
-     */
20
-    protected static $_instance;
21
-
22
-
23
-    /**
24
-     * private constructor to prevent direct creation
25
-     *
26
-     * @param string $timezone A string representing the timezone we want to set for returned Date Time Strings
27
-     *                         (and any incoming timezone data that gets saved).
28
-     *                         Note this just sends the timezone info to the date time model field objects.
29
-     *                         Default is NULL
30
-     *                         (and will be assumed using the set timezone in the 'timezone_string' wp option)
31
-     * @throws EE_Error
32
-     * @throws InvalidArgumentException
33
-     * @throws InvalidArgumentException
34
-     */
35
-    protected function __construct($timezone)
36
-    {
37
-        $this->singular_item           = esc_html__('Datetime', 'event_espresso');
38
-        $this->plural_item             = esc_html__('Datetimes', 'event_espresso');
39
-        $this->_tables                 = array(
40
-            'Datetime' => new EE_Primary_Table('esp_datetime', 'DTT_ID'),
41
-        );
42
-        $this->_fields                 = array(
43
-            'Datetime' => array(
44
-                'DTT_ID'          => new EE_Primary_Key_Int_Field(
45
-                    'DTT_ID',
46
-                    esc_html__('Datetime ID', 'event_espresso')
47
-                ),
48
-                'EVT_ID'          => new EE_Foreign_Key_Int_Field(
49
-                    'EVT_ID',
50
-                    esc_html__('Event ID', 'event_espresso'),
51
-                    false,
52
-                    0,
53
-                    'Event'
54
-                ),
55
-                'DTT_name'        => new EE_Plain_Text_Field(
56
-                    'DTT_name',
57
-                    esc_html__('Datetime Name', 'event_espresso'),
58
-                    false,
59
-                    ''
60
-                ),
61
-                'DTT_description' => new EE_Post_Content_Field(
62
-                    'DTT_description',
63
-                    esc_html__('Description for Datetime', 'event_espresso'),
64
-                    false,
65
-                    ''
66
-                ),
67
-                'DTT_EVT_start'   => new EE_Datetime_Field(
68
-                    'DTT_EVT_start',
69
-                    esc_html__('Start time/date of Event', 'event_espresso'),
70
-                    false,
71
-                    EE_Datetime_Field::now,
72
-                    $timezone
73
-                ),
74
-                'DTT_EVT_end'     => new EE_Datetime_Field(
75
-                    'DTT_EVT_end',
76
-                    esc_html__('End time/date of Event', 'event_espresso'),
77
-                    false,
78
-                    EE_Datetime_Field::now,
79
-                    $timezone
80
-                ),
81
-                'DTT_reg_limit'   => new EE_Infinite_Integer_Field(
82
-                    'DTT_reg_limit',
83
-                    esc_html__('Registration Limit for this time', 'event_espresso'),
84
-                    true,
85
-                    EE_INF
86
-                ),
87
-                'DTT_sold'        => new EE_Integer_Field(
88
-                    'DTT_sold',
89
-                    esc_html__('How many sales for this Datetime that have occurred', 'event_espresso'),
90
-                    true,
91
-                    0
92
-                ),
93
-                'DTT_reserved'    => new EE_Integer_Field(
94
-                    'DTT_reserved',
95
-                    esc_html__('Quantity of tickets reserved, but not yet fully purchased', 'event_espresso'),
96
-                    false,
97
-                    0
98
-                ),
99
-                'DTT_is_primary'  => new EE_Boolean_Field(
100
-                    'DTT_is_primary',
101
-                    esc_html__('Flag indicating datetime is primary one for event', 'event_espresso'),
102
-                    false,
103
-                    false
104
-                ),
105
-                'DTT_order'       => new EE_Integer_Field(
106
-                    'DTT_order',
107
-                    esc_html__('The order in which the Datetime is displayed', 'event_espresso'),
108
-                    false,
109
-                    0
110
-                ),
111
-                'DTT_parent'      => new EE_Integer_Field(
112
-                    'DTT_parent',
113
-                    esc_html__('Indicates what DTT_ID is the parent of this DTT_ID'),
114
-                    true,
115
-                    0
116
-                ),
117
-                'DTT_deleted'     => new EE_Trashed_Flag_Field(
118
-                    'DTT_deleted',
119
-                    esc_html__('Flag indicating datetime is archived', 'event_espresso'),
120
-                    false,
121
-                    false
122
-                ),
123
-            ),
124
-        );
125
-        $this->_model_relations        = array(
126
-            'Ticket'  => new EE_HABTM_Relation('Datetime_Ticket'),
127
-            'Event'   => new EE_Belongs_To_Relation(),
128
-            'Checkin' => new EE_Has_Many_Relation(),
129
-        );
130
-        $this->_model_chain_to_wp_user = 'Event';
131
-        //this model is generally available for reading
132
-        $this->_cap_restriction_generators[ EEM_Base::caps_read ]       = new EE_Restriction_Generator_Event_Related_Public(
133
-            'Event'
134
-        );
135
-        $this->_cap_restriction_generators[ EEM_Base::caps_read_admin ] = new EE_Restriction_Generator_Event_Related_Protected(
136
-            'Event'
137
-        );
138
-        $this->_cap_restriction_generators[ EEM_Base::caps_edit ]       = new EE_Restriction_Generator_Event_Related_Protected(
139
-            'Event'
140
-        );
141
-        $this->_cap_restriction_generators[ EEM_Base::caps_delete ]     = new EE_Restriction_Generator_Event_Related_Protected(
142
-            'Event',
143
-            EEM_Base::caps_edit
144
-        );
145
-        parent::__construct($timezone);
146
-    }
147
-
148
-
149
-    /**
150
-     * create new blank datetime
151
-     *
152
-     * @access public
153
-     * @return EE_Datetime[] array on success, FALSE on fail
154
-     * @throws EE_Error
155
-     */
156
-    public function create_new_blank_datetime()
157
-    {
158
-        //makes sure timezone is always set.
159
-        $timezone_string = $this->get_timezone();
160
-        $blank_datetime  = EE_Datetime::new_instance(
161
-            array(
162
-                'DTT_EVT_start' => $this->current_time_for_query('DTT_EVT_start', true) + MONTH_IN_SECONDS,
163
-                'DTT_EVT_end'   => $this->current_time_for_query('DTT_EVT_end', true) + MONTH_IN_SECONDS,
164
-                'DTT_order'     => 1,
165
-                'DTT_reg_limit' => EE_INF,
166
-            ),
167
-            $timezone_string
168
-        );
169
-        $blank_datetime->set_start_time(
170
-            $this->convert_datetime_for_query(
171
-                'DTT_EVT_start',
172
-                '8am',
173
-                'ga',
174
-                $timezone_string
175
-            )
176
-        );
177
-        $blank_datetime->set_end_time(
178
-            $this->convert_datetime_for_query(
179
-                'DTT_EVT_end',
180
-                '5pm',
181
-                'ga',
182
-                $timezone_string
183
-            )
184
-        );
185
-        return array($blank_datetime);
186
-    }
187
-
188
-
189
-    /**
190
-     * get event start date from db
191
-     *
192
-     * @access public
193
-     * @param  int $EVT_ID
194
-     * @return EE_Datetime[] array on success, FALSE on fail
195
-     * @throws EE_Error
196
-     */
197
-    public function get_all_event_dates($EVT_ID = 0)
198
-    {
199
-        if (! $EVT_ID) { // on add_new_event event_id gets set to 0
200
-            return $this->create_new_blank_datetime();
201
-        }
202
-        $results = $this->get_datetimes_for_event_ordered_by_DTT_order($EVT_ID);
203
-        if (empty($results)) {
204
-            return $this->create_new_blank_datetime();
205
-        }
206
-        return $results;
207
-    }
208
-
209
-
210
-    /**
211
-     * get all datetimes attached to an event ordered by the DTT_order field
212
-     *
213
-     * @public
214
-     * @param  int    $EVT_ID     event id
215
-     * @param boolean $include_expired
216
-     * @param boolean $include_deleted
217
-     * @param  int    $limit      If included then limit the count of results by
218
-     *                            the given number
219
-     * @return EE_Datetime[]
220
-     * @throws EE_Error
221
-     */
222
-    public function get_datetimes_for_event_ordered_by_DTT_order(
223
-        $EVT_ID,
224
-        $include_expired = true,
225
-        $include_deleted = true,
226
-        $limit = null
227
-    ) {
228
-        //sanitize EVT_ID
229
-        $EVT_ID         = absint($EVT_ID);
230
-        $old_assumption = $this->get_assumption_concerning_values_already_prepared_by_model_object();
231
-        $this->assume_values_already_prepared_by_model_object(EEM_Base::prepared_for_use_in_db);
232
-        $where_params = array('Event.EVT_ID' => $EVT_ID);
233
-        $query_params = ! empty($limit)
234
-            ? array(
235
-                $where_params,
236
-                'limit'                    => $limit,
237
-                'order_by'                 => array('DTT_order' => 'ASC'),
238
-                'default_where_conditions' => 'none',
239
-            )
240
-            : array(
241
-                $where_params,
242
-                'order_by'                 => array('DTT_order' => 'ASC'),
243
-                'default_where_conditions' => 'none',
244
-            );
245
-        if (! $include_expired) {
246
-            $query_params[0]['DTT_EVT_end'] = array('>=', current_time('mysql', true));
247
-        }
248
-        if ($include_deleted) {
249
-            $query_params[0]['DTT_deleted'] = array('IN', array(true, false));
250
-        }
251
-        /** @var EE_Datetime[] $result */
252
-        $result = $this->get_all($query_params);
253
-        $this->assume_values_already_prepared_by_model_object($old_assumption);
254
-        return $result;
255
-    }
256
-
257
-
258
-    /**
259
-     * Gets the datetimes for the event (with the given limit), and orders them by "importance".
260
-     * By importance, we mean that the primary datetimes are most important (DEPRECATED FOR NOW),
261
-     * and then the earlier datetimes are the most important.
262
-     * Maybe we'll want this to take into account datetimes that haven't already passed, but we don't yet.
263
-     *
264
-     * @param int $EVT_ID
265
-     * @param int $limit
266
-     * @return EE_Datetime[]|EE_Base_Class[]
267
-     * @throws EE_Error
268
-     */
269
-    public function get_datetimes_for_event_ordered_by_importance($EVT_ID = 0, $limit = null)
270
-    {
271
-        return $this->get_all(
272
-            array(
273
-                array('Event.EVT_ID' => $EVT_ID),
274
-                'limit'                    => $limit,
275
-                'order_by'                 => array('DTT_EVT_start' => 'ASC'),
276
-                'default_where_conditions' => 'none',
277
-            )
278
-        );
279
-    }
280
-
281
-
282
-    /**
283
-     * @param int     $EVT_ID
284
-     * @param boolean $include_expired
285
-     * @param boolean $include_deleted
286
-     * @return EE_Datetime
287
-     * @throws EE_Error
288
-     */
289
-    public function get_oldest_datetime_for_event($EVT_ID, $include_expired = false, $include_deleted = false)
290
-    {
291
-        $results = $this->get_datetimes_for_event_ordered_by_start_time(
292
-            $EVT_ID,
293
-            $include_expired,
294
-            $include_deleted,
295
-            1
296
-        );
297
-        if ($results) {
298
-            return array_shift($results);
299
-        }
300
-        return null;
301
-    }
302
-
303
-
304
-    /**
305
-     * Gets the 'primary' datetime for an event.
306
-     *
307
-     * @param int  $EVT_ID
308
-     * @param bool $try_to_exclude_expired
309
-     * @param bool $try_to_exclude_deleted
310
-     * @return \EE_Datetime
311
-     * @throws EE_Error
312
-     */
313
-    public function get_primary_datetime_for_event(
314
-        $EVT_ID,
315
-        $try_to_exclude_expired = true,
316
-        $try_to_exclude_deleted = true
317
-    ) {
318
-        if ($try_to_exclude_expired) {
319
-            $non_expired = $this->get_oldest_datetime_for_event($EVT_ID, false, false);
320
-            if ($non_expired) {
321
-                return $non_expired;
322
-            }
323
-        }
324
-        if ($try_to_exclude_deleted) {
325
-            $expired_even = $this->get_oldest_datetime_for_event($EVT_ID, true);
326
-            if ($expired_even) {
327
-                return $expired_even;
328
-            }
329
-        }
330
-        return $this->get_oldest_datetime_for_event($EVT_ID, true, true);
331
-    }
332
-
333
-
334
-    /**
335
-     * Gets ALL the datetimes for an event (including trashed ones, for now), ordered
336
-     * only by start date
337
-     *
338
-     * @param int     $EVT_ID
339
-     * @param boolean $include_expired
340
-     * @param boolean $include_deleted
341
-     * @param int     $limit
342
-     * @return EE_Datetime[]
343
-     * @throws EE_Error
344
-     */
345
-    public function get_datetimes_for_event_ordered_by_start_time(
346
-        $EVT_ID,
347
-        $include_expired = true,
348
-        $include_deleted = true,
349
-        $limit = null
350
-    ) {
351
-        //sanitize EVT_ID
352
-        $EVT_ID         = absint($EVT_ID);
353
-        $old_assumption = $this->get_assumption_concerning_values_already_prepared_by_model_object();
354
-        $this->assume_values_already_prepared_by_model_object(EEM_Base::prepared_for_use_in_db);
355
-        $query_params = array(array('Event.EVT_ID' => $EVT_ID), 'order_by' => array('DTT_EVT_start' => 'asc'));
356
-        if (! $include_expired) {
357
-            $query_params[0]['DTT_EVT_end'] = array('>=', current_time('mysql', true));
358
-        }
359
-        if ($include_deleted) {
360
-            $query_params[0]['DTT_deleted'] = array('IN', array(true, false));
361
-        }
362
-        if ($limit) {
363
-            $query_params['limit'] = $limit;
364
-        }
365
-        /** @var EE_Datetime[] $result */
366
-        $result = $this->get_all($query_params);
367
-        $this->assume_values_already_prepared_by_model_object($old_assumption);
368
-        return $result;
369
-    }
370
-
371
-
372
-    /**
373
-     * Gets ALL the datetimes for an ticket (including trashed ones, for now), ordered
374
-     * only by start date
375
-     *
376
-     * @param int     $TKT_ID
377
-     * @param boolean $include_expired
378
-     * @param boolean $include_deleted
379
-     * @param int     $limit
380
-     * @return EE_Datetime[]
381
-     * @throws EE_Error
382
-     */
383
-    public function get_datetimes_for_ticket_ordered_by_start_time(
384
-        $TKT_ID,
385
-        $include_expired = true,
386
-        $include_deleted = true,
387
-        $limit = null
388
-    ) {
389
-        //sanitize TKT_ID
390
-        $TKT_ID         = absint($TKT_ID);
391
-        $old_assumption = $this->get_assumption_concerning_values_already_prepared_by_model_object();
392
-        $this->assume_values_already_prepared_by_model_object(EEM_Base::prepared_for_use_in_db);
393
-        $query_params = array(array('Ticket.TKT_ID' => $TKT_ID), 'order_by' => array('DTT_EVT_start' => 'asc'));
394
-        if (! $include_expired) {
395
-            $query_params[0]['DTT_EVT_end'] = array('>=', current_time('mysql', true));
396
-        }
397
-        if ($include_deleted) {
398
-            $query_params[0]['DTT_deleted'] = array('IN', array(true, false));
399
-        }
400
-        if ($limit) {
401
-            $query_params['limit'] = $limit;
402
-        }
403
-        /** @var EE_Datetime[] $result */
404
-        $result = $this->get_all($query_params);
405
-        $this->assume_values_already_prepared_by_model_object($old_assumption);
406
-        return $result;
407
-    }
408
-
409
-
410
-    /**
411
-     * Gets all the datetimes for a ticket (including trashed ones, for now), ordered by the DTT_order for the
412
-     * datetimes.
413
-     *
414
-     * @param  int      $TKT_ID          ID of ticket to retrieve the datetimes for
415
-     * @param  boolean  $include_expired whether to include expired datetimes or not
416
-     * @param  boolean  $include_deleted whether to include trashed datetimes or not.
417
-     * @param  int|null $limit           if null, no limit, if int then limit results by
418
-     *                                   that number
419
-     * @return EE_Datetime[]
420
-     * @throws EE_Error
421
-     */
422
-    public function get_datetimes_for_ticket_ordered_by_DTT_order(
423
-        $TKT_ID,
424
-        $include_expired = true,
425
-        $include_deleted = true,
426
-        $limit = null
427
-    ) {
428
-        //sanitize id.
429
-        $TKT_ID         = absint($TKT_ID);
430
-        $old_assumption = $this->get_assumption_concerning_values_already_prepared_by_model_object();
431
-        $this->assume_values_already_prepared_by_model_object(EEM_Base::prepared_for_use_in_db);
432
-        $where_params = array('Ticket.TKT_ID' => $TKT_ID);
433
-        $query_params = array($where_params, 'order_by' => array('DTT_order' => 'ASC'));
434
-        if (! $include_expired) {
435
-            $query_params[0]['DTT_EVT_end'] = array('>=', current_time('mysql', true));
436
-        }
437
-        if ($include_deleted) {
438
-            $query_params[0]['DTT_deleted'] = array('IN', array(true, false));
439
-        }
440
-        if ($limit) {
441
-            $query_params['limit'] = $limit;
442
-        }
443
-        /** @var EE_Datetime[] $result */
444
-        $result = $this->get_all($query_params);
445
-        $this->assume_values_already_prepared_by_model_object($old_assumption);
446
-        return $result;
447
-    }
448
-
449
-
450
-    /**
451
-     * Gets the most important datetime for a particular event (ie, the primary event usually. But if for some WACK
452
-     * reason it doesn't exist, we consider the earliest event the most important)
453
-     *
454
-     * @param int $EVT_ID
455
-     * @return EE_Datetime
456
-     * @throws EE_Error
457
-     */
458
-    public function get_most_important_datetime_for_event($EVT_ID)
459
-    {
460
-        $results = $this->get_datetimes_for_event_ordered_by_importance($EVT_ID, 1);
461
-        if ($results) {
462
-            return array_shift($results);
463
-        }
464
-        return null;
465
-    }
466
-
467
-
468
-    /**
469
-     * This returns a wpdb->results        Array of all DTT month and years matching the incoming query params and
470
-     * grouped by month and year.
471
-     *
472
-     * @param  array  $where_params      Array of query_params as described in the comments for EEM_Base::get_all()
473
-     * @param  string $evt_active_status A string representing the evt active status to filter the months by.
474
-     *                                   Can be:
475
-     *                                   - '' = no filter
476
-     *                                   - upcoming = Published events with at least one upcoming datetime.
477
-     *                                   - expired = Events with all datetimes expired.
478
-     *                                   - active = Events that are published and have at least one datetime that
479
-     *                                   starts before now and ends after now.
480
-     *                                   - inactive = Events that are either not published.
481
-     * @return EE_Base_Class[]
482
-     * @throws EE_Error
483
-     * @throws InvalidArgumentException
484
-     * @throws InvalidArgumentException
485
-     */
486
-    public function get_dtt_months_and_years($where_params, $evt_active_status = '')
487
-    {
488
-        $current_time_for_DTT_EVT_start = $this->current_time_for_query('DTT_EVT_start');
489
-        $current_time_for_DTT_EVT_end   = $this->current_time_for_query('DTT_EVT_end');
490
-        switch ($evt_active_status) {
491
-            case 'upcoming' :
492
-                $where_params['Event.status'] = 'publish';
493
-                //if there are already query_params matching DTT_EVT_start then we need to modify that to add them.
494
-                if (isset($where_params['DTT_EVT_start'])) {
495
-                    $where_params['DTT_EVT_start*****'] = $where_params['DTT_EVT_start'];
496
-                }
497
-                $where_params['DTT_EVT_start'] = array('>', $current_time_for_DTT_EVT_start);
498
-                break;
499
-            case 'expired' :
500
-                if (isset($where_params['Event.status'])) {
501
-                    unset($where_params['Event.status']);
502
-                }
503
-                //get events to exclude
504
-                $exclude_query[0] = array_merge($where_params,
505
-                    array('DTT_EVT_end' => array('>', $current_time_for_DTT_EVT_end)));
506
-                //first get all events that have datetimes where its not expired.
507
-                $event_ids = $this->_get_all_wpdb_results(
508
-                    $exclude_query,
509
-                    OBJECT_K,
510
-                    'Datetime.EVT_ID'
511
-                );
512
-                $event_ids = array_keys($event_ids);
513
-                if (isset($where_params['DTT_EVT_end'])) {
514
-                    $where_params['DTT_EVT_end****'] = $where_params['DTT_EVT_end'];
515
-                }
516
-                $where_params['DTT_EVT_end']  = array('<', $current_time_for_DTT_EVT_end);
517
-                $where_params['Event.EVT_ID'] = array('NOT IN', $event_ids);
518
-                break;
519
-            case 'active' :
520
-                $where_params['Event.status'] = 'publish';
521
-                if (isset($where_params['DTT_EVT_start'])) {
522
-                    $where_params['Datetime.DTT_EVT_start******'] = $where_params['DTT_EVT_start'];
523
-                }
524
-                if (isset($where_params['Datetime.DTT_EVT_end'])) {
525
-                    $where_params['Datetime.DTT_EVT_end*****'] = $where_params['DTT_EVT_end'];
526
-                }
527
-                $where_params['DTT_EVT_start'] = array('<', $current_time_for_DTT_EVT_start);
528
-                $where_params['DTT_EVT_end']   = array('>', $current_time_for_DTT_EVT_end);
529
-                break;
530
-            case 'inactive' :
531
-                if (isset($where_params['Event.status'])) {
532
-                    unset($where_params['Event.status']);
533
-                }
534
-                if (isset($where_params['OR'])) {
535
-                    $where_params['AND']['OR'] = $where_params['OR'];
536
-                }
537
-                if (isset($where_params['DTT_EVT_end'])) {
538
-                    $where_params['AND']['DTT_EVT_end****'] = $where_params['DTT_EVT_end'];
539
-                    unset($where_params['DTT_EVT_end']);
540
-                }
541
-                if (isset($where_params['DTT_EVT_start'])) {
542
-                    $where_params['AND']['DTT_EVT_start'] = $where_params['DTT_EVT_start'];
543
-                    unset($where_params['DTT_EVT_start']);
544
-                }
545
-                $where_params['AND']['Event.status'] = array('!=', 'publish');
546
-                break;
547
-        }
548
-        $query_params[0]          = $where_params;
549
-        $query_params['group_by'] = array('dtt_year', 'dtt_month');
550
-        $query_params['order_by'] = array('DTT_EVT_start' => 'DESC');
551
-        $query_interval           = EEH_DTT_Helper::get_sql_query_interval_for_offset($this->get_timezone(),
552
-            'DTT_EVT_start');
553
-        $columns_to_select        = array(
554
-            'dtt_year'      => array('YEAR(' . $query_interval . ')', '%s'),
555
-            'dtt_month'     => array('MONTHNAME(' . $query_interval . ')', '%s'),
556
-            'dtt_month_num' => array('MONTH(' . $query_interval . ')', '%s'),
557
-        );
558
-        return $this->_get_all_wpdb_results($query_params, OBJECT, $columns_to_select);
559
-    }
560
-
561
-
562
-    /**
563
-     * Updates the DTT_sold attribute on each datetime (based on the registrations
564
-     * for the tickets for each datetime)
565
-     *
566
-     * @param EE_Base_Class[]|EE_Datetime[] $datetimes
567
-     * @throws EE_Error
568
-     */
569
-    public function update_sold($datetimes)
570
-    {
571
-        EE_Error::doing_it_wrong(
572
-            __FUNCTION__,
573
-            esc_html__(
574
-                'Please use \EEM_Ticket::update_tickets_sold() instead which will in turn correctly update both the Ticket AND Datetime counts.',
575
-                'event_espresso'
576
-            ),
577
-            '4.9.32.rc.005'
578
-        );
579
-        foreach ($datetimes as $datetime) {
580
-            $datetime->update_sold();
581
-        }
582
-    }
583
-
584
-
585
-    /**
586
-     *    Gets the total number of tickets available at a particular datetime
587
-     *    (does NOT take into account the datetime's spaces available)
588
-     *
589
-     * @param int   $DTT_ID
590
-     * @param array $query_params
591
-     * @return int of tickets available. If sold out, return less than 1. If infinite, returns EE_INF,  IF there are NO
592
-     *             tickets attached to datetime then FALSE is returned.
593
-     */
594
-    public function sum_tickets_currently_available_at_datetime($DTT_ID, array $query_params = array())
595
-    {
596
-        $datetime = $this->get_one_by_ID($DTT_ID);
597
-        if ($datetime instanceof EE_Datetime) {
598
-            return $datetime->tickets_remaining($query_params);
599
-        }
600
-        return 0;
601
-    }
602
-
603
-
604
-    /**
605
-     * This returns an array of counts of datetimes in the database for each Datetime status that can be queried.
606
-     *
607
-     * @param  array $stati_to_include If included you can restrict the statuses we return counts for by including the
608
-     *                                 stati you want counts for as values in the array.  An empty array returns counts
609
-     *                                 for all valid stati.
610
-     * @param  array $query_params     If included can be used to refine the conditions for returning the count (i.e.
611
-     *                                 only for Datetimes connected to a specific event, or specific ticket.
612
-     * @return array  The value returned is an array indexed by Datetime Status and the values are the counts.  The
613
-     * @throws EE_Error
614
-     *                                 stati used as index keys are: EE_Datetime::active EE_Datetime::upcoming
615
-     *                                 EE_Datetime::expired
616
-     */
617
-    public function get_datetime_counts_by_status(array $stati_to_include = array(), array $query_params = array())
618
-    {
619
-        //only accept where conditions for this query.
620
-        $_where            = isset($query_params[0]) ? $query_params[0] : array();
621
-        $status_query_args = array(
622
-            EE_Datetime::active   => array_merge(
623
-                $_where,
624
-                array('DTT_EVT_start' => array('<', time()), 'DTT_EVT_end' => array('>', time()))
625
-            ),
626
-            EE_Datetime::upcoming => array_merge(
627
-                $_where,
628
-                array('DTT_EVT_start' => array('>', time()))
629
-            ),
630
-            EE_Datetime::expired  => array_merge(
631
-                $_where,
632
-                array('DTT_EVT_end' => array('<', time()))
633
-            ),
634
-        );
635
-        if (! empty($stati_to_include)) {
636
-            foreach (array_keys($status_query_args) as $status) {
637
-                if (! in_array($status, $stati_to_include, true)) {
638
-                    unset($status_query_args[ $status ]);
639
-                }
640
-            }
641
-        }
642
-        //loop through and query counts for each stati.
643
-        $status_query_results = array();
644
-        foreach ($status_query_args as $status => $status_where_conditions) {
645
-            $status_query_results[ $status ] = EEM_Datetime::count(
646
-                array($status_where_conditions),
647
-                'DTT_ID',
648
-                true
649
-            );
650
-        }
651
-        return $status_query_results;
652
-    }
653
-
654
-
655
-    /**
656
-     * Returns the specific count for a given Datetime status matching any given query_params.
657
-     *
658
-     * @param string $status Valid string representation for Datetime status requested. (Defaults to Active).
659
-     * @param array  $query_params
660
-     * @return int
661
-     * @throws EE_Error
662
-     */
663
-    public function get_datetime_count_for_status($status = EE_Datetime::active, array $query_params = array())
664
-    {
665
-        $count = $this->get_datetime_counts_by_status(array($status), $query_params);
666
-        return ! empty($count[ $status ]) ? $count[ $status ] : 0;
667
-    }
17
+	/**
18
+	 * @var EEM_Datetime $_instance
19
+	 */
20
+	protected static $_instance;
21
+
22
+
23
+	/**
24
+	 * private constructor to prevent direct creation
25
+	 *
26
+	 * @param string $timezone A string representing the timezone we want to set for returned Date Time Strings
27
+	 *                         (and any incoming timezone data that gets saved).
28
+	 *                         Note this just sends the timezone info to the date time model field objects.
29
+	 *                         Default is NULL
30
+	 *                         (and will be assumed using the set timezone in the 'timezone_string' wp option)
31
+	 * @throws EE_Error
32
+	 * @throws InvalidArgumentException
33
+	 * @throws InvalidArgumentException
34
+	 */
35
+	protected function __construct($timezone)
36
+	{
37
+		$this->singular_item           = esc_html__('Datetime', 'event_espresso');
38
+		$this->plural_item             = esc_html__('Datetimes', 'event_espresso');
39
+		$this->_tables                 = array(
40
+			'Datetime' => new EE_Primary_Table('esp_datetime', 'DTT_ID'),
41
+		);
42
+		$this->_fields                 = array(
43
+			'Datetime' => array(
44
+				'DTT_ID'          => new EE_Primary_Key_Int_Field(
45
+					'DTT_ID',
46
+					esc_html__('Datetime ID', 'event_espresso')
47
+				),
48
+				'EVT_ID'          => new EE_Foreign_Key_Int_Field(
49
+					'EVT_ID',
50
+					esc_html__('Event ID', 'event_espresso'),
51
+					false,
52
+					0,
53
+					'Event'
54
+				),
55
+				'DTT_name'        => new EE_Plain_Text_Field(
56
+					'DTT_name',
57
+					esc_html__('Datetime Name', 'event_espresso'),
58
+					false,
59
+					''
60
+				),
61
+				'DTT_description' => new EE_Post_Content_Field(
62
+					'DTT_description',
63
+					esc_html__('Description for Datetime', 'event_espresso'),
64
+					false,
65
+					''
66
+				),
67
+				'DTT_EVT_start'   => new EE_Datetime_Field(
68
+					'DTT_EVT_start',
69
+					esc_html__('Start time/date of Event', 'event_espresso'),
70
+					false,
71
+					EE_Datetime_Field::now,
72
+					$timezone
73
+				),
74
+				'DTT_EVT_end'     => new EE_Datetime_Field(
75
+					'DTT_EVT_end',
76
+					esc_html__('End time/date of Event', 'event_espresso'),
77
+					false,
78
+					EE_Datetime_Field::now,
79
+					$timezone
80
+				),
81
+				'DTT_reg_limit'   => new EE_Infinite_Integer_Field(
82
+					'DTT_reg_limit',
83
+					esc_html__('Registration Limit for this time', 'event_espresso'),
84
+					true,
85
+					EE_INF
86
+				),
87
+				'DTT_sold'        => new EE_Integer_Field(
88
+					'DTT_sold',
89
+					esc_html__('How many sales for this Datetime that have occurred', 'event_espresso'),
90
+					true,
91
+					0
92
+				),
93
+				'DTT_reserved'    => new EE_Integer_Field(
94
+					'DTT_reserved',
95
+					esc_html__('Quantity of tickets reserved, but not yet fully purchased', 'event_espresso'),
96
+					false,
97
+					0
98
+				),
99
+				'DTT_is_primary'  => new EE_Boolean_Field(
100
+					'DTT_is_primary',
101
+					esc_html__('Flag indicating datetime is primary one for event', 'event_espresso'),
102
+					false,
103
+					false
104
+				),
105
+				'DTT_order'       => new EE_Integer_Field(
106
+					'DTT_order',
107
+					esc_html__('The order in which the Datetime is displayed', 'event_espresso'),
108
+					false,
109
+					0
110
+				),
111
+				'DTT_parent'      => new EE_Integer_Field(
112
+					'DTT_parent',
113
+					esc_html__('Indicates what DTT_ID is the parent of this DTT_ID'),
114
+					true,
115
+					0
116
+				),
117
+				'DTT_deleted'     => new EE_Trashed_Flag_Field(
118
+					'DTT_deleted',
119
+					esc_html__('Flag indicating datetime is archived', 'event_espresso'),
120
+					false,
121
+					false
122
+				),
123
+			),
124
+		);
125
+		$this->_model_relations        = array(
126
+			'Ticket'  => new EE_HABTM_Relation('Datetime_Ticket'),
127
+			'Event'   => new EE_Belongs_To_Relation(),
128
+			'Checkin' => new EE_Has_Many_Relation(),
129
+		);
130
+		$this->_model_chain_to_wp_user = 'Event';
131
+		//this model is generally available for reading
132
+		$this->_cap_restriction_generators[ EEM_Base::caps_read ]       = new EE_Restriction_Generator_Event_Related_Public(
133
+			'Event'
134
+		);
135
+		$this->_cap_restriction_generators[ EEM_Base::caps_read_admin ] = new EE_Restriction_Generator_Event_Related_Protected(
136
+			'Event'
137
+		);
138
+		$this->_cap_restriction_generators[ EEM_Base::caps_edit ]       = new EE_Restriction_Generator_Event_Related_Protected(
139
+			'Event'
140
+		);
141
+		$this->_cap_restriction_generators[ EEM_Base::caps_delete ]     = new EE_Restriction_Generator_Event_Related_Protected(
142
+			'Event',
143
+			EEM_Base::caps_edit
144
+		);
145
+		parent::__construct($timezone);
146
+	}
147
+
148
+
149
+	/**
150
+	 * create new blank datetime
151
+	 *
152
+	 * @access public
153
+	 * @return EE_Datetime[] array on success, FALSE on fail
154
+	 * @throws EE_Error
155
+	 */
156
+	public function create_new_blank_datetime()
157
+	{
158
+		//makes sure timezone is always set.
159
+		$timezone_string = $this->get_timezone();
160
+		$blank_datetime  = EE_Datetime::new_instance(
161
+			array(
162
+				'DTT_EVT_start' => $this->current_time_for_query('DTT_EVT_start', true) + MONTH_IN_SECONDS,
163
+				'DTT_EVT_end'   => $this->current_time_for_query('DTT_EVT_end', true) + MONTH_IN_SECONDS,
164
+				'DTT_order'     => 1,
165
+				'DTT_reg_limit' => EE_INF,
166
+			),
167
+			$timezone_string
168
+		);
169
+		$blank_datetime->set_start_time(
170
+			$this->convert_datetime_for_query(
171
+				'DTT_EVT_start',
172
+				'8am',
173
+				'ga',
174
+				$timezone_string
175
+			)
176
+		);
177
+		$blank_datetime->set_end_time(
178
+			$this->convert_datetime_for_query(
179
+				'DTT_EVT_end',
180
+				'5pm',
181
+				'ga',
182
+				$timezone_string
183
+			)
184
+		);
185
+		return array($blank_datetime);
186
+	}
187
+
188
+
189
+	/**
190
+	 * get event start date from db
191
+	 *
192
+	 * @access public
193
+	 * @param  int $EVT_ID
194
+	 * @return EE_Datetime[] array on success, FALSE on fail
195
+	 * @throws EE_Error
196
+	 */
197
+	public function get_all_event_dates($EVT_ID = 0)
198
+	{
199
+		if (! $EVT_ID) { // on add_new_event event_id gets set to 0
200
+			return $this->create_new_blank_datetime();
201
+		}
202
+		$results = $this->get_datetimes_for_event_ordered_by_DTT_order($EVT_ID);
203
+		if (empty($results)) {
204
+			return $this->create_new_blank_datetime();
205
+		}
206
+		return $results;
207
+	}
208
+
209
+
210
+	/**
211
+	 * get all datetimes attached to an event ordered by the DTT_order field
212
+	 *
213
+	 * @public
214
+	 * @param  int    $EVT_ID     event id
215
+	 * @param boolean $include_expired
216
+	 * @param boolean $include_deleted
217
+	 * @param  int    $limit      If included then limit the count of results by
218
+	 *                            the given number
219
+	 * @return EE_Datetime[]
220
+	 * @throws EE_Error
221
+	 */
222
+	public function get_datetimes_for_event_ordered_by_DTT_order(
223
+		$EVT_ID,
224
+		$include_expired = true,
225
+		$include_deleted = true,
226
+		$limit = null
227
+	) {
228
+		//sanitize EVT_ID
229
+		$EVT_ID         = absint($EVT_ID);
230
+		$old_assumption = $this->get_assumption_concerning_values_already_prepared_by_model_object();
231
+		$this->assume_values_already_prepared_by_model_object(EEM_Base::prepared_for_use_in_db);
232
+		$where_params = array('Event.EVT_ID' => $EVT_ID);
233
+		$query_params = ! empty($limit)
234
+			? array(
235
+				$where_params,
236
+				'limit'                    => $limit,
237
+				'order_by'                 => array('DTT_order' => 'ASC'),
238
+				'default_where_conditions' => 'none',
239
+			)
240
+			: array(
241
+				$where_params,
242
+				'order_by'                 => array('DTT_order' => 'ASC'),
243
+				'default_where_conditions' => 'none',
244
+			);
245
+		if (! $include_expired) {
246
+			$query_params[0]['DTT_EVT_end'] = array('>=', current_time('mysql', true));
247
+		}
248
+		if ($include_deleted) {
249
+			$query_params[0]['DTT_deleted'] = array('IN', array(true, false));
250
+		}
251
+		/** @var EE_Datetime[] $result */
252
+		$result = $this->get_all($query_params);
253
+		$this->assume_values_already_prepared_by_model_object($old_assumption);
254
+		return $result;
255
+	}
256
+
257
+
258
+	/**
259
+	 * Gets the datetimes for the event (with the given limit), and orders them by "importance".
260
+	 * By importance, we mean that the primary datetimes are most important (DEPRECATED FOR NOW),
261
+	 * and then the earlier datetimes are the most important.
262
+	 * Maybe we'll want this to take into account datetimes that haven't already passed, but we don't yet.
263
+	 *
264
+	 * @param int $EVT_ID
265
+	 * @param int $limit
266
+	 * @return EE_Datetime[]|EE_Base_Class[]
267
+	 * @throws EE_Error
268
+	 */
269
+	public function get_datetimes_for_event_ordered_by_importance($EVT_ID = 0, $limit = null)
270
+	{
271
+		return $this->get_all(
272
+			array(
273
+				array('Event.EVT_ID' => $EVT_ID),
274
+				'limit'                    => $limit,
275
+				'order_by'                 => array('DTT_EVT_start' => 'ASC'),
276
+				'default_where_conditions' => 'none',
277
+			)
278
+		);
279
+	}
280
+
281
+
282
+	/**
283
+	 * @param int     $EVT_ID
284
+	 * @param boolean $include_expired
285
+	 * @param boolean $include_deleted
286
+	 * @return EE_Datetime
287
+	 * @throws EE_Error
288
+	 */
289
+	public function get_oldest_datetime_for_event($EVT_ID, $include_expired = false, $include_deleted = false)
290
+	{
291
+		$results = $this->get_datetimes_for_event_ordered_by_start_time(
292
+			$EVT_ID,
293
+			$include_expired,
294
+			$include_deleted,
295
+			1
296
+		);
297
+		if ($results) {
298
+			return array_shift($results);
299
+		}
300
+		return null;
301
+	}
302
+
303
+
304
+	/**
305
+	 * Gets the 'primary' datetime for an event.
306
+	 *
307
+	 * @param int  $EVT_ID
308
+	 * @param bool $try_to_exclude_expired
309
+	 * @param bool $try_to_exclude_deleted
310
+	 * @return \EE_Datetime
311
+	 * @throws EE_Error
312
+	 */
313
+	public function get_primary_datetime_for_event(
314
+		$EVT_ID,
315
+		$try_to_exclude_expired = true,
316
+		$try_to_exclude_deleted = true
317
+	) {
318
+		if ($try_to_exclude_expired) {
319
+			$non_expired = $this->get_oldest_datetime_for_event($EVT_ID, false, false);
320
+			if ($non_expired) {
321
+				return $non_expired;
322
+			}
323
+		}
324
+		if ($try_to_exclude_deleted) {
325
+			$expired_even = $this->get_oldest_datetime_for_event($EVT_ID, true);
326
+			if ($expired_even) {
327
+				return $expired_even;
328
+			}
329
+		}
330
+		return $this->get_oldest_datetime_for_event($EVT_ID, true, true);
331
+	}
332
+
333
+
334
+	/**
335
+	 * Gets ALL the datetimes for an event (including trashed ones, for now), ordered
336
+	 * only by start date
337
+	 *
338
+	 * @param int     $EVT_ID
339
+	 * @param boolean $include_expired
340
+	 * @param boolean $include_deleted
341
+	 * @param int     $limit
342
+	 * @return EE_Datetime[]
343
+	 * @throws EE_Error
344
+	 */
345
+	public function get_datetimes_for_event_ordered_by_start_time(
346
+		$EVT_ID,
347
+		$include_expired = true,
348
+		$include_deleted = true,
349
+		$limit = null
350
+	) {
351
+		//sanitize EVT_ID
352
+		$EVT_ID         = absint($EVT_ID);
353
+		$old_assumption = $this->get_assumption_concerning_values_already_prepared_by_model_object();
354
+		$this->assume_values_already_prepared_by_model_object(EEM_Base::prepared_for_use_in_db);
355
+		$query_params = array(array('Event.EVT_ID' => $EVT_ID), 'order_by' => array('DTT_EVT_start' => 'asc'));
356
+		if (! $include_expired) {
357
+			$query_params[0]['DTT_EVT_end'] = array('>=', current_time('mysql', true));
358
+		}
359
+		if ($include_deleted) {
360
+			$query_params[0]['DTT_deleted'] = array('IN', array(true, false));
361
+		}
362
+		if ($limit) {
363
+			$query_params['limit'] = $limit;
364
+		}
365
+		/** @var EE_Datetime[] $result */
366
+		$result = $this->get_all($query_params);
367
+		$this->assume_values_already_prepared_by_model_object($old_assumption);
368
+		return $result;
369
+	}
370
+
371
+
372
+	/**
373
+	 * Gets ALL the datetimes for an ticket (including trashed ones, for now), ordered
374
+	 * only by start date
375
+	 *
376
+	 * @param int     $TKT_ID
377
+	 * @param boolean $include_expired
378
+	 * @param boolean $include_deleted
379
+	 * @param int     $limit
380
+	 * @return EE_Datetime[]
381
+	 * @throws EE_Error
382
+	 */
383
+	public function get_datetimes_for_ticket_ordered_by_start_time(
384
+		$TKT_ID,
385
+		$include_expired = true,
386
+		$include_deleted = true,
387
+		$limit = null
388
+	) {
389
+		//sanitize TKT_ID
390
+		$TKT_ID         = absint($TKT_ID);
391
+		$old_assumption = $this->get_assumption_concerning_values_already_prepared_by_model_object();
392
+		$this->assume_values_already_prepared_by_model_object(EEM_Base::prepared_for_use_in_db);
393
+		$query_params = array(array('Ticket.TKT_ID' => $TKT_ID), 'order_by' => array('DTT_EVT_start' => 'asc'));
394
+		if (! $include_expired) {
395
+			$query_params[0]['DTT_EVT_end'] = array('>=', current_time('mysql', true));
396
+		}
397
+		if ($include_deleted) {
398
+			$query_params[0]['DTT_deleted'] = array('IN', array(true, false));
399
+		}
400
+		if ($limit) {
401
+			$query_params['limit'] = $limit;
402
+		}
403
+		/** @var EE_Datetime[] $result */
404
+		$result = $this->get_all($query_params);
405
+		$this->assume_values_already_prepared_by_model_object($old_assumption);
406
+		return $result;
407
+	}
408
+
409
+
410
+	/**
411
+	 * Gets all the datetimes for a ticket (including trashed ones, for now), ordered by the DTT_order for the
412
+	 * datetimes.
413
+	 *
414
+	 * @param  int      $TKT_ID          ID of ticket to retrieve the datetimes for
415
+	 * @param  boolean  $include_expired whether to include expired datetimes or not
416
+	 * @param  boolean  $include_deleted whether to include trashed datetimes or not.
417
+	 * @param  int|null $limit           if null, no limit, if int then limit results by
418
+	 *                                   that number
419
+	 * @return EE_Datetime[]
420
+	 * @throws EE_Error
421
+	 */
422
+	public function get_datetimes_for_ticket_ordered_by_DTT_order(
423
+		$TKT_ID,
424
+		$include_expired = true,
425
+		$include_deleted = true,
426
+		$limit = null
427
+	) {
428
+		//sanitize id.
429
+		$TKT_ID         = absint($TKT_ID);
430
+		$old_assumption = $this->get_assumption_concerning_values_already_prepared_by_model_object();
431
+		$this->assume_values_already_prepared_by_model_object(EEM_Base::prepared_for_use_in_db);
432
+		$where_params = array('Ticket.TKT_ID' => $TKT_ID);
433
+		$query_params = array($where_params, 'order_by' => array('DTT_order' => 'ASC'));
434
+		if (! $include_expired) {
435
+			$query_params[0]['DTT_EVT_end'] = array('>=', current_time('mysql', true));
436
+		}
437
+		if ($include_deleted) {
438
+			$query_params[0]['DTT_deleted'] = array('IN', array(true, false));
439
+		}
440
+		if ($limit) {
441
+			$query_params['limit'] = $limit;
442
+		}
443
+		/** @var EE_Datetime[] $result */
444
+		$result = $this->get_all($query_params);
445
+		$this->assume_values_already_prepared_by_model_object($old_assumption);
446
+		return $result;
447
+	}
448
+
449
+
450
+	/**
451
+	 * Gets the most important datetime for a particular event (ie, the primary event usually. But if for some WACK
452
+	 * reason it doesn't exist, we consider the earliest event the most important)
453
+	 *
454
+	 * @param int $EVT_ID
455
+	 * @return EE_Datetime
456
+	 * @throws EE_Error
457
+	 */
458
+	public function get_most_important_datetime_for_event($EVT_ID)
459
+	{
460
+		$results = $this->get_datetimes_for_event_ordered_by_importance($EVT_ID, 1);
461
+		if ($results) {
462
+			return array_shift($results);
463
+		}
464
+		return null;
465
+	}
466
+
467
+
468
+	/**
469
+	 * This returns a wpdb->results        Array of all DTT month and years matching the incoming query params and
470
+	 * grouped by month and year.
471
+	 *
472
+	 * @param  array  $where_params      Array of query_params as described in the comments for EEM_Base::get_all()
473
+	 * @param  string $evt_active_status A string representing the evt active status to filter the months by.
474
+	 *                                   Can be:
475
+	 *                                   - '' = no filter
476
+	 *                                   - upcoming = Published events with at least one upcoming datetime.
477
+	 *                                   - expired = Events with all datetimes expired.
478
+	 *                                   - active = Events that are published and have at least one datetime that
479
+	 *                                   starts before now and ends after now.
480
+	 *                                   - inactive = Events that are either not published.
481
+	 * @return EE_Base_Class[]
482
+	 * @throws EE_Error
483
+	 * @throws InvalidArgumentException
484
+	 * @throws InvalidArgumentException
485
+	 */
486
+	public function get_dtt_months_and_years($where_params, $evt_active_status = '')
487
+	{
488
+		$current_time_for_DTT_EVT_start = $this->current_time_for_query('DTT_EVT_start');
489
+		$current_time_for_DTT_EVT_end   = $this->current_time_for_query('DTT_EVT_end');
490
+		switch ($evt_active_status) {
491
+			case 'upcoming' :
492
+				$where_params['Event.status'] = 'publish';
493
+				//if there are already query_params matching DTT_EVT_start then we need to modify that to add them.
494
+				if (isset($where_params['DTT_EVT_start'])) {
495
+					$where_params['DTT_EVT_start*****'] = $where_params['DTT_EVT_start'];
496
+				}
497
+				$where_params['DTT_EVT_start'] = array('>', $current_time_for_DTT_EVT_start);
498
+				break;
499
+			case 'expired' :
500
+				if (isset($where_params['Event.status'])) {
501
+					unset($where_params['Event.status']);
502
+				}
503
+				//get events to exclude
504
+				$exclude_query[0] = array_merge($where_params,
505
+					array('DTT_EVT_end' => array('>', $current_time_for_DTT_EVT_end)));
506
+				//first get all events that have datetimes where its not expired.
507
+				$event_ids = $this->_get_all_wpdb_results(
508
+					$exclude_query,
509
+					OBJECT_K,
510
+					'Datetime.EVT_ID'
511
+				);
512
+				$event_ids = array_keys($event_ids);
513
+				if (isset($where_params['DTT_EVT_end'])) {
514
+					$where_params['DTT_EVT_end****'] = $where_params['DTT_EVT_end'];
515
+				}
516
+				$where_params['DTT_EVT_end']  = array('<', $current_time_for_DTT_EVT_end);
517
+				$where_params['Event.EVT_ID'] = array('NOT IN', $event_ids);
518
+				break;
519
+			case 'active' :
520
+				$where_params['Event.status'] = 'publish';
521
+				if (isset($where_params['DTT_EVT_start'])) {
522
+					$where_params['Datetime.DTT_EVT_start******'] = $where_params['DTT_EVT_start'];
523
+				}
524
+				if (isset($where_params['Datetime.DTT_EVT_end'])) {
525
+					$where_params['Datetime.DTT_EVT_end*****'] = $where_params['DTT_EVT_end'];
526
+				}
527
+				$where_params['DTT_EVT_start'] = array('<', $current_time_for_DTT_EVT_start);
528
+				$where_params['DTT_EVT_end']   = array('>', $current_time_for_DTT_EVT_end);
529
+				break;
530
+			case 'inactive' :
531
+				if (isset($where_params['Event.status'])) {
532
+					unset($where_params['Event.status']);
533
+				}
534
+				if (isset($where_params['OR'])) {
535
+					$where_params['AND']['OR'] = $where_params['OR'];
536
+				}
537
+				if (isset($where_params['DTT_EVT_end'])) {
538
+					$where_params['AND']['DTT_EVT_end****'] = $where_params['DTT_EVT_end'];
539
+					unset($where_params['DTT_EVT_end']);
540
+				}
541
+				if (isset($where_params['DTT_EVT_start'])) {
542
+					$where_params['AND']['DTT_EVT_start'] = $where_params['DTT_EVT_start'];
543
+					unset($where_params['DTT_EVT_start']);
544
+				}
545
+				$where_params['AND']['Event.status'] = array('!=', 'publish');
546
+				break;
547
+		}
548
+		$query_params[0]          = $where_params;
549
+		$query_params['group_by'] = array('dtt_year', 'dtt_month');
550
+		$query_params['order_by'] = array('DTT_EVT_start' => 'DESC');
551
+		$query_interval           = EEH_DTT_Helper::get_sql_query_interval_for_offset($this->get_timezone(),
552
+			'DTT_EVT_start');
553
+		$columns_to_select        = array(
554
+			'dtt_year'      => array('YEAR(' . $query_interval . ')', '%s'),
555
+			'dtt_month'     => array('MONTHNAME(' . $query_interval . ')', '%s'),
556
+			'dtt_month_num' => array('MONTH(' . $query_interval . ')', '%s'),
557
+		);
558
+		return $this->_get_all_wpdb_results($query_params, OBJECT, $columns_to_select);
559
+	}
560
+
561
+
562
+	/**
563
+	 * Updates the DTT_sold attribute on each datetime (based on the registrations
564
+	 * for the tickets for each datetime)
565
+	 *
566
+	 * @param EE_Base_Class[]|EE_Datetime[] $datetimes
567
+	 * @throws EE_Error
568
+	 */
569
+	public function update_sold($datetimes)
570
+	{
571
+		EE_Error::doing_it_wrong(
572
+			__FUNCTION__,
573
+			esc_html__(
574
+				'Please use \EEM_Ticket::update_tickets_sold() instead which will in turn correctly update both the Ticket AND Datetime counts.',
575
+				'event_espresso'
576
+			),
577
+			'4.9.32.rc.005'
578
+		);
579
+		foreach ($datetimes as $datetime) {
580
+			$datetime->update_sold();
581
+		}
582
+	}
583
+
584
+
585
+	/**
586
+	 *    Gets the total number of tickets available at a particular datetime
587
+	 *    (does NOT take into account the datetime's spaces available)
588
+	 *
589
+	 * @param int   $DTT_ID
590
+	 * @param array $query_params
591
+	 * @return int of tickets available. If sold out, return less than 1. If infinite, returns EE_INF,  IF there are NO
592
+	 *             tickets attached to datetime then FALSE is returned.
593
+	 */
594
+	public function sum_tickets_currently_available_at_datetime($DTT_ID, array $query_params = array())
595
+	{
596
+		$datetime = $this->get_one_by_ID($DTT_ID);
597
+		if ($datetime instanceof EE_Datetime) {
598
+			return $datetime->tickets_remaining($query_params);
599
+		}
600
+		return 0;
601
+	}
602
+
603
+
604
+	/**
605
+	 * This returns an array of counts of datetimes in the database for each Datetime status that can be queried.
606
+	 *
607
+	 * @param  array $stati_to_include If included you can restrict the statuses we return counts for by including the
608
+	 *                                 stati you want counts for as values in the array.  An empty array returns counts
609
+	 *                                 for all valid stati.
610
+	 * @param  array $query_params     If included can be used to refine the conditions for returning the count (i.e.
611
+	 *                                 only for Datetimes connected to a specific event, or specific ticket.
612
+	 * @return array  The value returned is an array indexed by Datetime Status and the values are the counts.  The
613
+	 * @throws EE_Error
614
+	 *                                 stati used as index keys are: EE_Datetime::active EE_Datetime::upcoming
615
+	 *                                 EE_Datetime::expired
616
+	 */
617
+	public function get_datetime_counts_by_status(array $stati_to_include = array(), array $query_params = array())
618
+	{
619
+		//only accept where conditions for this query.
620
+		$_where            = isset($query_params[0]) ? $query_params[0] : array();
621
+		$status_query_args = array(
622
+			EE_Datetime::active   => array_merge(
623
+				$_where,
624
+				array('DTT_EVT_start' => array('<', time()), 'DTT_EVT_end' => array('>', time()))
625
+			),
626
+			EE_Datetime::upcoming => array_merge(
627
+				$_where,
628
+				array('DTT_EVT_start' => array('>', time()))
629
+			),
630
+			EE_Datetime::expired  => array_merge(
631
+				$_where,
632
+				array('DTT_EVT_end' => array('<', time()))
633
+			),
634
+		);
635
+		if (! empty($stati_to_include)) {
636
+			foreach (array_keys($status_query_args) as $status) {
637
+				if (! in_array($status, $stati_to_include, true)) {
638
+					unset($status_query_args[ $status ]);
639
+				}
640
+			}
641
+		}
642
+		//loop through and query counts for each stati.
643
+		$status_query_results = array();
644
+		foreach ($status_query_args as $status => $status_where_conditions) {
645
+			$status_query_results[ $status ] = EEM_Datetime::count(
646
+				array($status_where_conditions),
647
+				'DTT_ID',
648
+				true
649
+			);
650
+		}
651
+		return $status_query_results;
652
+	}
653
+
654
+
655
+	/**
656
+	 * Returns the specific count for a given Datetime status matching any given query_params.
657
+	 *
658
+	 * @param string $status Valid string representation for Datetime status requested. (Defaults to Active).
659
+	 * @param array  $query_params
660
+	 * @return int
661
+	 * @throws EE_Error
662
+	 */
663
+	public function get_datetime_count_for_status($status = EE_Datetime::active, array $query_params = array())
664
+	{
665
+		$count = $this->get_datetime_counts_by_status(array($status), $query_params);
666
+		return ! empty($count[ $status ]) ? $count[ $status ] : 0;
667
+	}
668 668
 }
669 669
 // End of file EEM_Datetime.model.php
670 670
 // Location: /includes/models/EEM_Datetime.model.php
Please login to merge, or discard this patch.
Spacing   +19 added lines, -19 removed lines patch added patch discarded remove patch
@@ -1,4 +1,4 @@  discard block
 block discarded – undo
1
-<?php if (! defined('EVENT_ESPRESSO_VERSION')) {
1
+<?php if ( ! defined('EVENT_ESPRESSO_VERSION')) {
2 2
     exit('No direct script access allowed');
3 3
 }
4 4
 
@@ -122,23 +122,23 @@  discard block
 block discarded – undo
122 122
                 ),
123 123
             ),
124 124
         );
125
-        $this->_model_relations        = array(
125
+        $this->_model_relations = array(
126 126
             'Ticket'  => new EE_HABTM_Relation('Datetime_Ticket'),
127 127
             'Event'   => new EE_Belongs_To_Relation(),
128 128
             'Checkin' => new EE_Has_Many_Relation(),
129 129
         );
130 130
         $this->_model_chain_to_wp_user = 'Event';
131 131
         //this model is generally available for reading
132
-        $this->_cap_restriction_generators[ EEM_Base::caps_read ]       = new EE_Restriction_Generator_Event_Related_Public(
132
+        $this->_cap_restriction_generators[EEM_Base::caps_read]       = new EE_Restriction_Generator_Event_Related_Public(
133 133
             'Event'
134 134
         );
135
-        $this->_cap_restriction_generators[ EEM_Base::caps_read_admin ] = new EE_Restriction_Generator_Event_Related_Protected(
135
+        $this->_cap_restriction_generators[EEM_Base::caps_read_admin] = new EE_Restriction_Generator_Event_Related_Protected(
136 136
             'Event'
137 137
         );
138
-        $this->_cap_restriction_generators[ EEM_Base::caps_edit ]       = new EE_Restriction_Generator_Event_Related_Protected(
138
+        $this->_cap_restriction_generators[EEM_Base::caps_edit]       = new EE_Restriction_Generator_Event_Related_Protected(
139 139
             'Event'
140 140
         );
141
-        $this->_cap_restriction_generators[ EEM_Base::caps_delete ]     = new EE_Restriction_Generator_Event_Related_Protected(
141
+        $this->_cap_restriction_generators[EEM_Base::caps_delete]     = new EE_Restriction_Generator_Event_Related_Protected(
142 142
             'Event',
143 143
             EEM_Base::caps_edit
144 144
         );
@@ -196,7 +196,7 @@  discard block
 block discarded – undo
196 196
      */
197 197
     public function get_all_event_dates($EVT_ID = 0)
198 198
     {
199
-        if (! $EVT_ID) { // on add_new_event event_id gets set to 0
199
+        if ( ! $EVT_ID) { // on add_new_event event_id gets set to 0
200 200
             return $this->create_new_blank_datetime();
201 201
         }
202 202
         $results = $this->get_datetimes_for_event_ordered_by_DTT_order($EVT_ID);
@@ -242,7 +242,7 @@  discard block
 block discarded – undo
242 242
                 'order_by'                 => array('DTT_order' => 'ASC'),
243 243
                 'default_where_conditions' => 'none',
244 244
             );
245
-        if (! $include_expired) {
245
+        if ( ! $include_expired) {
246 246
             $query_params[0]['DTT_EVT_end'] = array('>=', current_time('mysql', true));
247 247
         }
248 248
         if ($include_deleted) {
@@ -353,7 +353,7 @@  discard block
 block discarded – undo
353 353
         $old_assumption = $this->get_assumption_concerning_values_already_prepared_by_model_object();
354 354
         $this->assume_values_already_prepared_by_model_object(EEM_Base::prepared_for_use_in_db);
355 355
         $query_params = array(array('Event.EVT_ID' => $EVT_ID), 'order_by' => array('DTT_EVT_start' => 'asc'));
356
-        if (! $include_expired) {
356
+        if ( ! $include_expired) {
357 357
             $query_params[0]['DTT_EVT_end'] = array('>=', current_time('mysql', true));
358 358
         }
359 359
         if ($include_deleted) {
@@ -391,7 +391,7 @@  discard block
 block discarded – undo
391 391
         $old_assumption = $this->get_assumption_concerning_values_already_prepared_by_model_object();
392 392
         $this->assume_values_already_prepared_by_model_object(EEM_Base::prepared_for_use_in_db);
393 393
         $query_params = array(array('Ticket.TKT_ID' => $TKT_ID), 'order_by' => array('DTT_EVT_start' => 'asc'));
394
-        if (! $include_expired) {
394
+        if ( ! $include_expired) {
395 395
             $query_params[0]['DTT_EVT_end'] = array('>=', current_time('mysql', true));
396 396
         }
397 397
         if ($include_deleted) {
@@ -431,7 +431,7 @@  discard block
 block discarded – undo
431 431
         $this->assume_values_already_prepared_by_model_object(EEM_Base::prepared_for_use_in_db);
432 432
         $where_params = array('Ticket.TKT_ID' => $TKT_ID);
433 433
         $query_params = array($where_params, 'order_by' => array('DTT_order' => 'ASC'));
434
-        if (! $include_expired) {
434
+        if ( ! $include_expired) {
435 435
             $query_params[0]['DTT_EVT_end'] = array('>=', current_time('mysql', true));
436 436
         }
437 437
         if ($include_deleted) {
@@ -551,9 +551,9 @@  discard block
 block discarded – undo
551 551
         $query_interval           = EEH_DTT_Helper::get_sql_query_interval_for_offset($this->get_timezone(),
552 552
             'DTT_EVT_start');
553 553
         $columns_to_select        = array(
554
-            'dtt_year'      => array('YEAR(' . $query_interval . ')', '%s'),
555
-            'dtt_month'     => array('MONTHNAME(' . $query_interval . ')', '%s'),
556
-            'dtt_month_num' => array('MONTH(' . $query_interval . ')', '%s'),
554
+            'dtt_year'      => array('YEAR('.$query_interval.')', '%s'),
555
+            'dtt_month'     => array('MONTHNAME('.$query_interval.')', '%s'),
556
+            'dtt_month_num' => array('MONTH('.$query_interval.')', '%s'),
557 557
         );
558 558
         return $this->_get_all_wpdb_results($query_params, OBJECT, $columns_to_select);
559 559
     }
@@ -632,17 +632,17 @@  discard block
 block discarded – undo
632 632
                 array('DTT_EVT_end' => array('<', time()))
633 633
             ),
634 634
         );
635
-        if (! empty($stati_to_include)) {
635
+        if ( ! empty($stati_to_include)) {
636 636
             foreach (array_keys($status_query_args) as $status) {
637
-                if (! in_array($status, $stati_to_include, true)) {
638
-                    unset($status_query_args[ $status ]);
637
+                if ( ! in_array($status, $stati_to_include, true)) {
638
+                    unset($status_query_args[$status]);
639 639
                 }
640 640
             }
641 641
         }
642 642
         //loop through and query counts for each stati.
643 643
         $status_query_results = array();
644 644
         foreach ($status_query_args as $status => $status_where_conditions) {
645
-            $status_query_results[ $status ] = EEM_Datetime::count(
645
+            $status_query_results[$status] = EEM_Datetime::count(
646 646
                 array($status_where_conditions),
647 647
                 'DTT_ID',
648 648
                 true
@@ -663,7 +663,7 @@  discard block
 block discarded – undo
663 663
     public function get_datetime_count_for_status($status = EE_Datetime::active, array $query_params = array())
664 664
     {
665 665
         $count = $this->get_datetime_counts_by_status(array($status), $query_params);
666
-        return ! empty($count[ $status ]) ? $count[ $status ] : 0;
666
+        return ! empty($count[$status]) ? $count[$status] : 0;
667 667
     }
668 668
 }
669 669
 // End of file EEM_Datetime.model.php
Please login to merge, or discard this patch.
core/db_classes/EE_Base_Class.class.php 3 patches
Indentation   +3125 added lines, -3125 removed lines patch added patch discarded remove patch
@@ -15,3131 +15,3131 @@
 block discarded – undo
15 15
 abstract class EE_Base_Class
16 16
 {
17 17
 
18
-    /**
19
-     * This is an array of the original properties and values provided during construction
20
-     * of this model object. (keys are model field names, values are their values).
21
-     * This list is important to remember so that when we are merging data from the db, we know
22
-     * which values to override and which to not override.
23
-     *
24
-     * @var array
25
-     */
26
-    protected $_props_n_values_provided_in_constructor;
27
-
28
-    /**
29
-     * Timezone
30
-     * This gets set by the "set_timezone()" method so that we know what timezone incoming strings|timestamps are in.
31
-     * This can also be used before a get to set what timezone you want strings coming out of the object to be in.  NOT
32
-     * all EE_Base_Class child classes use this property but any that use a EE_Datetime_Field data type will have
33
-     * access to it.
34
-     *
35
-     * @var string
36
-     */
37
-    protected $_timezone;
38
-
39
-    /**
40
-     * date format
41
-     * pattern or format for displaying dates
42
-     *
43
-     * @var string $_dt_frmt
44
-     */
45
-    protected $_dt_frmt;
46
-
47
-    /**
48
-     * time format
49
-     * pattern or format for displaying time
50
-     *
51
-     * @var string $_tm_frmt
52
-     */
53
-    protected $_tm_frmt;
54
-
55
-    /**
56
-     * This property is for holding a cached array of object properties indexed by property name as the key.
57
-     * The purpose of this is for setting a cache on properties that may have calculated values after a
58
-     * prepare_for_get.  That way the cache can be checked first and the calculated property returned instead of having
59
-     * to recalculate. Used by _set_cached_property() and _get_cached_property() methods.
60
-     *
61
-     * @var array
62
-     */
63
-    protected $_cached_properties = array();
64
-
65
-    /**
66
-     * An array containing keys of the related model, and values are either an array of related mode objects or a
67
-     * single
68
-     * related model object. see the model's _model_relations. The keys should match those specified. And if the
69
-     * relation is of type EE_Belongs_To (or one of its children), then there should only be ONE related model object,
70
-     * all others have an array)
71
-     *
72
-     * @var array
73
-     */
74
-    protected $_model_relations = array();
75
-
76
-    /**
77
-     * Array where keys are field names (see the model's _fields property) and values are their values. To see what
78
-     * their types should be, look at what that field object returns on its prepare_for_get and prepare_for_set methods)
79
-     *
80
-     * @var array
81
-     */
82
-    protected $_fields = array();
83
-
84
-    /**
85
-     * @var boolean indicating whether or not this model object is intended to ever be saved
86
-     * For example, we might create model objects intended to only be used for the duration
87
-     * of this request and to be thrown away, and if they were accidentally saved
88
-     * it would be a bug.
89
-     */
90
-    protected $_allow_persist = true;
91
-
92
-    /**
93
-     * @var boolean indicating whether or not this model object's properties have changed since construction
94
-     */
95
-    protected $_has_changes = false;
96
-
97
-    /**
98
-     * @var EEM_Base
99
-     */
100
-    protected $_model;
101
-
102
-    /**
103
-     * This is a cache of results from custom selections done on a query that constructs this entity. The only purpose
104
-     * for these values is for retrieval of the results, they are not further queryable and they are not persisted to
105
-     * the db.  They also do not automatically update if there are any changes to the data that produced their results.
106
-     * The format is a simple array of field_alias => field_value.  So for instance if a custom select was something
107
-     * like,  "Select COUNT(Registration.REG_ID) as Registration_Count ...", then the resulting value will be in this
108
-     * array as:
109
-     * array(
110
-     *  'Registration_Count' => 24
111
-     * );
112
-     * Note: if the custom select configuration for the query included a data type, the value will be in the data type
113
-     * provided for the query (@see EventEspresso\core\domain\values\model\CustomSelects::__construct phpdocs for more
114
-     * info)
115
-     *
116
-     * @var array
117
-     */
118
-    protected $custom_selection_results = array();
119
-
120
-
121
-    /**
122
-     * basic constructor for Event Espresso classes, performs any necessary initialization, and verifies it's children
123
-     * play nice
124
-     *
125
-     * @param array   $fieldValues                             where each key is a field (ie, array key in the 2nd
126
-     *                                                         layer of the model's _fields array, (eg, EVT_ID,
127
-     *                                                         TXN_amount, QST_name, etc) and values are their values
128
-     * @param boolean $bydb                                    a flag for setting if the class is instantiated by the
129
-     *                                                         corresponding db model or not.
130
-     * @param string  $timezone                                indicate what timezone you want any datetime fields to
131
-     *                                                         be in when instantiating a EE_Base_Class object.
132
-     * @param array   $date_formats                            An array of date formats to set on construct where first
133
-     *                                                         value is the date_format and second value is the time
134
-     *                                                         format.
135
-     * @throws InvalidArgumentException
136
-     * @throws InvalidInterfaceException
137
-     * @throws InvalidDataTypeException
138
-     * @throws EE_Error
139
-     * @throws ReflectionException
140
-     */
141
-    protected function __construct($fieldValues = array(), $bydb = false, $timezone = '', $date_formats = array())
142
-    {
143
-        $className = get_class($this);
144
-        do_action("AHEE__{$className}__construct", $this, $fieldValues);
145
-        $model        = $this->get_model();
146
-        $model_fields = $model->field_settings(false);
147
-        // ensure $fieldValues is an array
148
-        $fieldValues = is_array($fieldValues) ? $fieldValues : array($fieldValues);
149
-        // verify client code has not passed any invalid field names
150
-        foreach ($fieldValues as $field_name => $field_value) {
151
-            if (! isset($model_fields[ $field_name ])) {
152
-                throw new EE_Error(
153
-                    sprintf(
154
-                        esc_html__(
155
-                            'Invalid field (%s) passed to constructor of %s. Allowed fields are :%s',
156
-                            'event_espresso'
157
-                        ),
158
-                        $field_name,
159
-                        get_class($this),
160
-                        implode(', ', array_keys($model_fields))
161
-                    )
162
-                );
163
-            }
164
-        }
165
-        $this->_timezone = EEH_DTT_Helper::get_valid_timezone_string($timezone);
166
-        if (! empty($date_formats) && is_array($date_formats)) {
167
-            list($this->_dt_frmt, $this->_tm_frmt) = $date_formats;
168
-        } else {
169
-            //set default formats for date and time
170
-            $this->_dt_frmt = (string) get_option('date_format', 'Y-m-d');
171
-            $this->_tm_frmt = (string) get_option('time_format', 'g:i a');
172
-        }
173
-        //if db model is instantiating
174
-        if ($bydb) {
175
-            //client code has indicated these field values are from the database
176
-            foreach ($model_fields as $fieldName => $field) {
177
-                $this->set_from_db(
178
-                    $fieldName,
179
-                    isset($fieldValues[ $fieldName ]) ? $fieldValues[ $fieldName ] : null
180
-                );
181
-            }
182
-        } else {
183
-            //we're constructing a brand
184
-            //new instance of the model object. Generally, this means we'll need to do more field validation
185
-            foreach ($model_fields as $fieldName => $field) {
186
-                $this->set(
187
-                    $fieldName,
188
-                    isset($fieldValues[ $fieldName ]) ? $fieldValues[ $fieldName ] : null, true
189
-                );
190
-            }
191
-        }
192
-        //remember what values were passed to this constructor
193
-        $this->_props_n_values_provided_in_constructor = $fieldValues;
194
-        //remember in entity mapper
195
-        if (! $bydb && $model->has_primary_key_field() && $this->ID()) {
196
-            $model->add_to_entity_map($this);
197
-        }
198
-        //setup all the relations
199
-        foreach ($model->relation_settings() as $relation_name => $relation_obj) {
200
-            if ($relation_obj instanceof EE_Belongs_To_Relation) {
201
-                $this->_model_relations[ $relation_name ] = null;
202
-            } else {
203
-                $this->_model_relations[ $relation_name ] = array();
204
-            }
205
-        }
206
-        /**
207
-         * Action done at the end of each model object construction
208
-         *
209
-         * @param EE_Base_Class $this the model object just created
210
-         */
211
-        do_action('AHEE__EE_Base_Class__construct__finished', $this);
212
-    }
213
-
214
-
215
-    /**
216
-     * Gets whether or not this model object is allowed to persist/be saved to the database.
217
-     *
218
-     * @return boolean
219
-     */
220
-    public function allow_persist()
221
-    {
222
-        return $this->_allow_persist;
223
-    }
224
-
225
-
226
-    /**
227
-     * Sets whether or not this model object should be allowed to be saved to the DB.
228
-     * Normally once this is set to FALSE you wouldn't set it back to TRUE, unless
229
-     * you got new information that somehow made you change your mind.
230
-     *
231
-     * @param boolean $allow_persist
232
-     * @return boolean
233
-     */
234
-    public function set_allow_persist($allow_persist)
235
-    {
236
-        return $this->_allow_persist = $allow_persist;
237
-    }
238
-
239
-
240
-    /**
241
-     * Gets the field's original value when this object was constructed during this request.
242
-     * This can be helpful when determining if a model object has changed or not
243
-     *
244
-     * @param string $field_name
245
-     * @return mixed|null
246
-     * @throws ReflectionException
247
-     * @throws InvalidArgumentException
248
-     * @throws InvalidInterfaceException
249
-     * @throws InvalidDataTypeException
250
-     * @throws EE_Error
251
-     */
252
-    public function get_original($field_name)
253
-    {
254
-        if (isset($this->_props_n_values_provided_in_constructor[ $field_name ])
255
-            && $field_settings = $this->get_model()->field_settings_for($field_name)
256
-        ) {
257
-            return $field_settings->prepare_for_get($this->_props_n_values_provided_in_constructor[ $field_name ]);
258
-        }
259
-        return null;
260
-    }
261
-
262
-
263
-    /**
264
-     * @param EE_Base_Class $obj
265
-     * @return string
266
-     */
267
-    public function get_class($obj)
268
-    {
269
-        return get_class($obj);
270
-    }
271
-
272
-
273
-    /**
274
-     * Overrides parent because parent expects old models.
275
-     * This also doesn't do any validation, and won't work for serialized arrays
276
-     *
277
-     * @param    string $field_name
278
-     * @param    mixed  $field_value
279
-     * @param bool      $use_default
280
-     * @throws InvalidArgumentException
281
-     * @throws InvalidInterfaceException
282
-     * @throws InvalidDataTypeException
283
-     * @throws EE_Error
284
-     * @throws ReflectionException
285
-     * @throws ReflectionException
286
-     * @throws ReflectionException
287
-     */
288
-    public function set($field_name, $field_value, $use_default = false)
289
-    {
290
-        // if not using default and nothing has changed, and object has already been setup (has ID),
291
-        // then don't do anything
292
-        if (
293
-            ! $use_default
294
-            && $this->_fields[ $field_name ] === $field_value
295
-            && $this->ID()
296
-        ) {
297
-            return;
298
-        }
299
-        $model              = $this->get_model();
300
-        $this->_has_changes = true;
301
-        $field_obj          = $model->field_settings_for($field_name);
302
-        if ($field_obj instanceof EE_Model_Field_Base) {
303
-            //			if ( method_exists( $field_obj, 'set_timezone' )) {
304
-            if ($field_obj instanceof EE_Datetime_Field) {
305
-                $field_obj->set_timezone($this->_timezone);
306
-                $field_obj->set_date_format($this->_dt_frmt);
307
-                $field_obj->set_time_format($this->_tm_frmt);
308
-            }
309
-            $holder_of_value = $field_obj->prepare_for_set($field_value);
310
-            //should the value be null?
311
-            if (($field_value === null || $holder_of_value === null || $holder_of_value === '') && $use_default) {
312
-                $this->_fields[ $field_name ] = $field_obj->get_default_value();
313
-                /**
314
-                 * To save having to refactor all the models, if a default value is used for a
315
-                 * EE_Datetime_Field, and that value is not null nor is it a DateTime
316
-                 * object.  Then let's do a set again to ensure that it becomes a DateTime
317
-                 * object.
318
-                 *
319
-                 * @since 4.6.10+
320
-                 */
321
-                if (
322
-                    $field_obj instanceof EE_Datetime_Field
323
-                    && $this->_fields[ $field_name ] !== null
324
-                    && ! $this->_fields[ $field_name ] instanceof DateTime
325
-                ) {
326
-                    empty($this->_fields[ $field_name ])
327
-                        ? $this->set($field_name, time())
328
-                        : $this->set($field_name, $this->_fields[ $field_name ]);
329
-                }
330
-            } else {
331
-                $this->_fields[ $field_name ] = $holder_of_value;
332
-            }
333
-            //if we're not in the constructor...
334
-            //now check if what we set was a primary key
335
-            if (
336
-                //note: props_n_values_provided_in_constructor is only set at the END of the constructor
337
-                $this->_props_n_values_provided_in_constructor
338
-                && $field_value
339
-                && $field_name === $model->primary_key_name()
340
-            ) {
341
-                //if so, we want all this object's fields to be filled either with
342
-                //what we've explicitly set on this model
343
-                //or what we have in the db
344
-                // echo "setting primary key!";
345
-                $fields_on_model = self::_get_model(get_class($this))->field_settings();
346
-                $obj_in_db       = self::_get_model(get_class($this))->get_one_by_ID($field_value);
347
-                foreach ($fields_on_model as $field_obj) {
348
-                    if (! array_key_exists($field_obj->get_name(), $this->_props_n_values_provided_in_constructor)
349
-                        && $field_obj->get_name() !== $field_name
350
-                    ) {
351
-                        $this->set($field_obj->get_name(), $obj_in_db->get($field_obj->get_name()));
352
-                    }
353
-                }
354
-                //oh this model object has an ID? well make sure its in the entity mapper
355
-                $model->add_to_entity_map($this);
356
-            }
357
-            //let's unset any cache for this field_name from the $_cached_properties property.
358
-            $this->_clear_cached_property($field_name);
359
-        } else {
360
-            throw new EE_Error(
361
-                sprintf(
362
-                    esc_html__(
363
-                        'A valid EE_Model_Field_Base could not be found for the given field name: %s',
364
-                        'event_espresso'
365
-                    ),
366
-                    $field_name
367
-                )
368
-            );
369
-        }
370
-    }
371
-
372
-
373
-    /**
374
-     * Set custom select values for model.
375
-     *
376
-     * @param array $custom_select_values
377
-     */
378
-    public function setCustomSelectsValues(array $custom_select_values)
379
-    {
380
-        $this->custom_selection_results = $custom_select_values;
381
-    }
382
-
383
-
384
-    /**
385
-     * Returns the custom select value for the provided alias if its set.
386
-     * If not set, returns null.
387
-     *
388
-     * @param string $alias
389
-     * @return string|int|float|null
390
-     */
391
-    public function getCustomSelect($alias)
392
-    {
393
-        return isset($this->custom_selection_results[ $alias ])
394
-            ? $this->custom_selection_results[ $alias ]
395
-            : null;
396
-    }
397
-
398
-
399
-    /**
400
-     * This sets the field value on the db column if it exists for the given $column_name or
401
-     * saves it to EE_Extra_Meta if the given $column_name does not match a db column.
402
-     *
403
-     * @see EE_message::get_column_value for related documentation on the necessity of this method.
404
-     * @param string $field_name  Must be the exact column name.
405
-     * @param mixed  $field_value The value to set.
406
-     * @return int|bool @see EE_Base_Class::update_extra_meta() for return docs.
407
-     * @throws InvalidArgumentException
408
-     * @throws InvalidInterfaceException
409
-     * @throws InvalidDataTypeException
410
-     * @throws EE_Error
411
-     * @throws ReflectionException
412
-     */
413
-    public function set_field_or_extra_meta($field_name, $field_value)
414
-    {
415
-        if ($this->get_model()->has_field($field_name)) {
416
-            $this->set($field_name, $field_value);
417
-            return true;
418
-        }
419
-        //ensure this object is saved first so that extra meta can be properly related.
420
-        $this->save();
421
-        return $this->update_extra_meta($field_name, $field_value);
422
-    }
423
-
424
-
425
-    /**
426
-     * This retrieves the value of the db column set on this class or if that's not present
427
-     * it will attempt to retrieve from extra_meta if found.
428
-     * Example Usage:
429
-     * Via EE_Message child class:
430
-     * Due to the dynamic nature of the EE_messages system, EE_messengers will always have a "to",
431
-     * "from", "subject", and "content" field (as represented in the EE_Message schema), however they may
432
-     * also have additional main fields specific to the messenger.  The system accommodates those extra
433
-     * fields through the EE_Extra_Meta table.  This method allows for EE_messengers to retrieve the
434
-     * value for those extra fields dynamically via the EE_message object.
435
-     *
436
-     * @param  string $field_name expecting the fully qualified field name.
437
-     * @return mixed|null  value for the field if found.  null if not found.
438
-     * @throws ReflectionException
439
-     * @throws InvalidArgumentException
440
-     * @throws InvalidInterfaceException
441
-     * @throws InvalidDataTypeException
442
-     * @throws EE_Error
443
-     */
444
-    public function get_field_or_extra_meta($field_name)
445
-    {
446
-        if ($this->get_model()->has_field($field_name)) {
447
-            $column_value = $this->get($field_name);
448
-        } else {
449
-            //This isn't a column in the main table, let's see if it is in the extra meta.
450
-            $column_value = $this->get_extra_meta($field_name, true, null);
451
-        }
452
-        return $column_value;
453
-    }
454
-
455
-
456
-    /**
457
-     * See $_timezone property for description of what the timezone property is for.  This SETS the timezone internally
458
-     * for being able to reference what timezone we are running conversions on when converting TO the internal timezone
459
-     * (UTC Unix Timestamp) for the object OR when converting FROM the internal timezone (UTC Unix Timestamp). This is
460
-     * available to all child classes that may be using the EE_Datetime_Field for a field data type.
461
-     *
462
-     * @access public
463
-     * @param string $timezone A valid timezone string as described by @link http://www.php.net/manual/en/timezones.php
464
-     * @return void
465
-     * @throws InvalidArgumentException
466
-     * @throws InvalidInterfaceException
467
-     * @throws InvalidDataTypeException
468
-     * @throws EE_Error
469
-     * @throws ReflectionException
470
-     */
471
-    public function set_timezone($timezone = '')
472
-    {
473
-        $this->_timezone = EEH_DTT_Helper::get_valid_timezone_string($timezone);
474
-        //make sure we clear all cached properties because they won't be relevant now
475
-        $this->_clear_cached_properties();
476
-        //make sure we update field settings and the date for all EE_Datetime_Fields
477
-        $model_fields = $this->get_model()->field_settings(false);
478
-        foreach ($model_fields as $field_name => $field_obj) {
479
-            if ($field_obj instanceof EE_Datetime_Field) {
480
-                $field_obj->set_timezone($this->_timezone);
481
-                if (isset($this->_fields[ $field_name ]) && $this->_fields[ $field_name ] instanceof DateTime) {
482
-                    $this->_fields[ $field_name ]->setTimezone(new DateTimeZone($this->_timezone));
483
-                }
484
-            }
485
-        }
486
-    }
487
-
488
-
489
-    /**
490
-     * This just returns whatever is set for the current timezone.
491
-     *
492
-     * @access public
493
-     * @return string timezone string
494
-     */
495
-    public function get_timezone()
496
-    {
497
-        return $this->_timezone;
498
-    }
499
-
500
-
501
-    /**
502
-     * This sets the internal date format to what is sent in to be used as the new default for the class
503
-     * internally instead of wp set date format options
504
-     *
505
-     * @since 4.6
506
-     * @param string $format should be a format recognizable by PHP date() functions.
507
-     */
508
-    public function set_date_format($format)
509
-    {
510
-        $this->_dt_frmt = $format;
511
-        //clear cached_properties because they won't be relevant now.
512
-        $this->_clear_cached_properties();
513
-    }
514
-
515
-
516
-    /**
517
-     * This sets the internal time format string to what is sent in to be used as the new default for the
518
-     * class internally instead of wp set time format options.
519
-     *
520
-     * @since 4.6
521
-     * @param string $format should be a format recognizable by PHP date() functions.
522
-     */
523
-    public function set_time_format($format)
524
-    {
525
-        $this->_tm_frmt = $format;
526
-        //clear cached_properties because they won't be relevant now.
527
-        $this->_clear_cached_properties();
528
-    }
529
-
530
-
531
-    /**
532
-     * This returns the current internal set format for the date and time formats.
533
-     *
534
-     * @param bool $full           if true (default), then return the full format.  Otherwise will return an array
535
-     *                             where the first value is the date format and the second value is the time format.
536
-     * @return mixed string|array
537
-     */
538
-    public function get_format($full = true)
539
-    {
540
-        return $full ? $this->_dt_frmt . ' ' . $this->_tm_frmt : array($this->_dt_frmt, $this->_tm_frmt);
541
-    }
542
-
543
-
544
-    /**
545
-     * cache
546
-     * stores the passed model object on the current model object.
547
-     * In certain circumstances, we can use this cached model object instead of querying for another one entirely.
548
-     *
549
-     * @param string        $relationName    one of the keys in the _model_relations array on the model. Eg
550
-     *                                       'Registration' associated with this model object
551
-     * @param EE_Base_Class $object_to_cache that has a relation to this model object. (Eg, if this is a Transaction,
552
-     *                                       that could be a payment or a registration)
553
-     * @param null          $cache_id        a string or number that will be used as the key for any Belongs_To_Many
554
-     *                                       items which will be stored in an array on this object
555
-     * @throws ReflectionException
556
-     * @throws InvalidArgumentException
557
-     * @throws InvalidInterfaceException
558
-     * @throws InvalidDataTypeException
559
-     * @throws EE_Error
560
-     * @return mixed    index into cache, or just TRUE if the relation is of type Belongs_To (because there's only one
561
-     *                                       related thing, no array)
562
-     */
563
-    public function cache($relationName = '', $object_to_cache = null, $cache_id = null)
564
-    {
565
-        // its entirely possible that there IS no related object yet in which case there is nothing to cache.
566
-        if (! $object_to_cache instanceof EE_Base_Class) {
567
-            return false;
568
-        }
569
-        // also get "how" the object is related, or throw an error
570
-        if (! $relationship_to_model = $this->get_model()->related_settings_for($relationName)) {
571
-            throw new EE_Error(
572
-                sprintf(
573
-                    esc_html__('There is no relationship to %s on a %s. Cannot cache it', 'event_espresso'),
574
-                    $relationName,
575
-                    get_class($this)
576
-                )
577
-            );
578
-        }
579
-        // how many things are related ?
580
-        if ($relationship_to_model instanceof EE_Belongs_To_Relation) {
581
-            // if it's a "belongs to" relationship, then there's only one related model object
582
-            // eg, if this is a registration, there's only 1 attendee for it
583
-            // so for these model objects just set it to be cached
584
-            $this->_model_relations[ $relationName ] = $object_to_cache;
585
-            $return                                  = true;
586
-        } else {
587
-            // otherwise, this is the "many" side of a one to many relationship,
588
-            // so we'll add the object to the array of related objects for that type.
589
-            // eg: if this is an event, there are many registrations for that event,
590
-            // so we cache the registrations in an array
591
-            if (! is_array($this->_model_relations[ $relationName ])) {
592
-                // if for some reason, the cached item is a model object,
593
-                // then stick that in the array, otherwise start with an empty array
594
-                $this->_model_relations[ $relationName ] = $this->_model_relations[ $relationName ]
595
-                                                           instanceof
596
-                                                           EE_Base_Class
597
-                    ? array($this->_model_relations[ $relationName ]) : array();
598
-            }
599
-            // first check for a cache_id which is normally empty
600
-            if (! empty($cache_id)) {
601
-                // if the cache_id exists, then it means we are purposely trying to cache this
602
-                // with a known key that can then be used to retrieve the object later on
603
-                $this->_model_relations[ $relationName ][ $cache_id ] = $object_to_cache;
604
-                $return                                               = $cache_id;
605
-            } elseif ($object_to_cache->ID()) {
606
-                // OR the cached object originally came from the db, so let's just use it's PK for an ID
607
-                $this->_model_relations[ $relationName ][ $object_to_cache->ID() ] = $object_to_cache;
608
-                $return                                                            = $object_to_cache->ID();
609
-            } else {
610
-                // OR it's a new object with no ID, so just throw it in the array with an auto-incremented ID
611
-                $this->_model_relations[ $relationName ][] = $object_to_cache;
612
-                // move the internal pointer to the end of the array
613
-                end($this->_model_relations[ $relationName ]);
614
-                // and grab the key so that we can return it
615
-                $return = key($this->_model_relations[ $relationName ]);
616
-            }
617
-        }
618
-        return $return;
619
-    }
620
-
621
-
622
-    /**
623
-     * For adding an item to the cached_properties property.
624
-     *
625
-     * @access protected
626
-     * @param string      $fieldname the property item the corresponding value is for.
627
-     * @param mixed       $value     The value we are caching.
628
-     * @param string|null $cache_type
629
-     * @return void
630
-     * @throws ReflectionException
631
-     * @throws InvalidArgumentException
632
-     * @throws InvalidInterfaceException
633
-     * @throws InvalidDataTypeException
634
-     * @throws EE_Error
635
-     */
636
-    protected function _set_cached_property($fieldname, $value, $cache_type = null)
637
-    {
638
-        //first make sure this property exists
639
-        $this->get_model()->field_settings_for($fieldname);
640
-        $cache_type                                            = empty($cache_type) ? 'standard' : $cache_type;
641
-        $this->_cached_properties[ $fieldname ][ $cache_type ] = $value;
642
-    }
643
-
644
-
645
-    /**
646
-     * This returns the value cached property if it exists OR the actual property value if the cache doesn't exist.
647
-     * This also SETS the cache if we return the actual property!
648
-     *
649
-     * @param string $fieldname        the name of the property we're trying to retrieve
650
-     * @param bool   $pretty
651
-     * @param string $extra_cache_ref  This allows the user to specify an extra cache ref for the given property
652
-     *                                 (in cases where the same property may be used for different outputs
653
-     *                                 - i.e. datetime, money etc.)
654
-     *                                 It can also accept certain pre-defined "schema" strings
655
-     *                                 to define how to output the property.
656
-     *                                 see the field's prepare_for_pretty_echoing for what strings can be used
657
-     * @return mixed                   whatever the value for the property is we're retrieving
658
-     * @throws ReflectionException
659
-     * @throws InvalidArgumentException
660
-     * @throws InvalidInterfaceException
661
-     * @throws InvalidDataTypeException
662
-     * @throws EE_Error
663
-     */
664
-    protected function _get_cached_property($fieldname, $pretty = false, $extra_cache_ref = null)
665
-    {
666
-        //verify the field exists
667
-        $model = $this->get_model();
668
-        $model->field_settings_for($fieldname);
669
-        $cache_type = $pretty ? 'pretty' : 'standard';
670
-        $cache_type .= ! empty($extra_cache_ref) ? '_' . $extra_cache_ref : '';
671
-        if (isset($this->_cached_properties[ $fieldname ][ $cache_type ])) {
672
-            return $this->_cached_properties[ $fieldname ][ $cache_type ];
673
-        }
674
-        $value = $this->_get_fresh_property($fieldname, $pretty, $extra_cache_ref);
675
-        $this->_set_cached_property($fieldname, $value, $cache_type);
676
-        return $value;
677
-    }
678
-
679
-
680
-    /**
681
-     * If the cache didn't fetch the needed item, this fetches it.
682
-     *
683
-     * @param string $fieldname
684
-     * @param bool   $pretty
685
-     * @param string $extra_cache_ref
686
-     * @return mixed
687
-     * @throws InvalidArgumentException
688
-     * @throws InvalidInterfaceException
689
-     * @throws InvalidDataTypeException
690
-     * @throws EE_Error
691
-     * @throws ReflectionException
692
-     */
693
-    protected function _get_fresh_property($fieldname, $pretty = false, $extra_cache_ref = null)
694
-    {
695
-        $field_obj = $this->get_model()->field_settings_for($fieldname);
696
-        // If this is an EE_Datetime_Field we need to make sure timezone, formats, and output are correct
697
-        if ($field_obj instanceof EE_Datetime_Field) {
698
-            $this->_prepare_datetime_field($field_obj, $pretty, $extra_cache_ref);
699
-        }
700
-        if (! isset($this->_fields[ $fieldname ])) {
701
-            $this->_fields[ $fieldname ] = null;
702
-        }
703
-        $value = $pretty
704
-            ? $field_obj->prepare_for_pretty_echoing($this->_fields[ $fieldname ], $extra_cache_ref)
705
-            : $field_obj->prepare_for_get($this->_fields[ $fieldname ]);
706
-        return $value;
707
-    }
708
-
709
-
710
-    /**
711
-     * set timezone, formats, and output for EE_Datetime_Field objects
712
-     *
713
-     * @param \EE_Datetime_Field $datetime_field
714
-     * @param bool               $pretty
715
-     * @param null               $date_or_time
716
-     * @return void
717
-     * @throws InvalidArgumentException
718
-     * @throws InvalidInterfaceException
719
-     * @throws InvalidDataTypeException
720
-     * @throws EE_Error
721
-     */
722
-    protected function _prepare_datetime_field(
723
-        EE_Datetime_Field $datetime_field,
724
-        $pretty = false,
725
-        $date_or_time = null
726
-    ) {
727
-        $datetime_field->set_timezone($this->_timezone);
728
-        $datetime_field->set_date_format($this->_dt_frmt, $pretty);
729
-        $datetime_field->set_time_format($this->_tm_frmt, $pretty);
730
-        //set the output returned
731
-        switch ($date_or_time) {
732
-            case 'D' :
733
-                $datetime_field->set_date_time_output('date');
734
-                break;
735
-            case 'T' :
736
-                $datetime_field->set_date_time_output('time');
737
-                break;
738
-            default :
739
-                $datetime_field->set_date_time_output();
740
-        }
741
-    }
742
-
743
-
744
-    /**
745
-     * This just takes care of clearing out the cached_properties
746
-     *
747
-     * @return void
748
-     */
749
-    protected function _clear_cached_properties()
750
-    {
751
-        $this->_cached_properties = array();
752
-    }
753
-
754
-
755
-    /**
756
-     * This just clears out ONE property if it exists in the cache
757
-     *
758
-     * @param  string $property_name the property to remove if it exists (from the _cached_properties array)
759
-     * @return void
760
-     */
761
-    protected function _clear_cached_property($property_name)
762
-    {
763
-        if (isset($this->_cached_properties[ $property_name ])) {
764
-            unset($this->_cached_properties[ $property_name ]);
765
-        }
766
-    }
767
-
768
-
769
-    /**
770
-     * Ensures that this related thing is a model object.
771
-     *
772
-     * @param mixed  $object_or_id EE_base_Class/int/string either a related model object, or its ID
773
-     * @param string $model_name   name of the related thing, eg 'Attendee',
774
-     * @return EE_Base_Class
775
-     * @throws ReflectionException
776
-     * @throws InvalidArgumentException
777
-     * @throws InvalidInterfaceException
778
-     * @throws InvalidDataTypeException
779
-     * @throws EE_Error
780
-     */
781
-    protected function ensure_related_thing_is_model_obj($object_or_id, $model_name)
782
-    {
783
-        $other_model_instance = self::_get_model_instance_with_name(
784
-            self::_get_model_classname($model_name),
785
-            $this->_timezone
786
-        );
787
-        return $other_model_instance->ensure_is_obj($object_or_id);
788
-    }
789
-
790
-
791
-    /**
792
-     * Forgets the cached model of the given relation Name. So the next time we request it,
793
-     * we will fetch it again from the database. (Handy if you know it's changed somehow).
794
-     * If a specific object is supplied, and the relationship to it is either a HasMany or HABTM,
795
-     * then only remove that one object from our cached array. Otherwise, clear the entire list
796
-     *
797
-     * @param string $relationName                         one of the keys in the _model_relations array on the model.
798
-     *                                                     Eg 'Registration'
799
-     * @param mixed  $object_to_remove_or_index_into_array or an index into the array of cached things, or NULL
800
-     *                                                     if you intend to use $clear_all = TRUE, or the relation only
801
-     *                                                     has 1 object anyways (ie, it's a BelongsToRelation)
802
-     * @param bool   $clear_all                            This flags clearing the entire cache relation property if
803
-     *                                                     this is HasMany or HABTM.
804
-     * @throws ReflectionException
805
-     * @throws InvalidArgumentException
806
-     * @throws InvalidInterfaceException
807
-     * @throws InvalidDataTypeException
808
-     * @throws EE_Error
809
-     * @return EE_Base_Class | boolean from which was cleared from the cache, or true if we requested to remove a
810
-     *                                                     relation from all
811
-     */
812
-    public function clear_cache($relationName, $object_to_remove_or_index_into_array = null, $clear_all = false)
813
-    {
814
-        $relationship_to_model = $this->get_model()->related_settings_for($relationName);
815
-        $index_in_cache        = '';
816
-        if (! $relationship_to_model) {
817
-            throw new EE_Error(
818
-                sprintf(
819
-                    esc_html__('There is no relationship to %s on a %s. Cannot clear that cache', 'event_espresso'),
820
-                    $relationName,
821
-                    get_class($this)
822
-                )
823
-            );
824
-        }
825
-        if ($clear_all) {
826
-            $obj_removed                             = true;
827
-            $this->_model_relations[ $relationName ] = null;
828
-        } elseif ($relationship_to_model instanceof EE_Belongs_To_Relation) {
829
-            $obj_removed                             = $this->_model_relations[ $relationName ];
830
-            $this->_model_relations[ $relationName ] = null;
831
-        } else {
832
-            if ($object_to_remove_or_index_into_array instanceof EE_Base_Class
833
-                && $object_to_remove_or_index_into_array->ID()
834
-            ) {
835
-                $index_in_cache = $object_to_remove_or_index_into_array->ID();
836
-                if (is_array($this->_model_relations[ $relationName ])
837
-                    && ! isset($this->_model_relations[ $relationName ][ $index_in_cache ])
838
-                ) {
839
-                    $index_found_at = null;
840
-                    //find this object in the array even though it has a different key
841
-                    foreach ($this->_model_relations[ $relationName ] as $index => $obj) {
842
-                        /** @noinspection TypeUnsafeComparisonInspection */
843
-                        if (
844
-                            $obj instanceof EE_Base_Class
845
-                            && (
846
-                                $obj == $object_to_remove_or_index_into_array
847
-                                || $obj->ID() === $object_to_remove_or_index_into_array->ID()
848
-                            )
849
-                        ) {
850
-                            $index_found_at = $index;
851
-                            break;
852
-                        }
853
-                    }
854
-                    if ($index_found_at) {
855
-                        $index_in_cache = $index_found_at;
856
-                    } else {
857
-                        //it wasn't found. huh. well obviously it doesn't need to be removed from teh cache
858
-                        //if it wasn't in it to begin with. So we're done
859
-                        return $object_to_remove_or_index_into_array;
860
-                    }
861
-                }
862
-            } elseif ($object_to_remove_or_index_into_array instanceof EE_Base_Class) {
863
-                //so they provided a model object, but it's not yet saved to the DB... so let's go hunting for it!
864
-                foreach ($this->get_all_from_cache($relationName) as $index => $potentially_obj_we_want) {
865
-                    /** @noinspection TypeUnsafeComparisonInspection */
866
-                    if ($potentially_obj_we_want == $object_to_remove_or_index_into_array) {
867
-                        $index_in_cache = $index;
868
-                    }
869
-                }
870
-            } else {
871
-                $index_in_cache = $object_to_remove_or_index_into_array;
872
-            }
873
-            //supposedly we've found it. But it could just be that the client code
874
-            //provided a bad index/object
875
-            if (isset($this->_model_relations[ $relationName ][ $index_in_cache ])) {
876
-                $obj_removed = $this->_model_relations[ $relationName ][ $index_in_cache ];
877
-                unset($this->_model_relations[ $relationName ][ $index_in_cache ]);
878
-            } else {
879
-                //that thing was never cached anyways.
880
-                $obj_removed = null;
881
-            }
882
-        }
883
-        return $obj_removed;
884
-    }
885
-
886
-
887
-    /**
888
-     * update_cache_after_object_save
889
-     * Allows a cached item to have it's cache ID (within the array of cached items) reset using the new ID it has
890
-     * obtained after being saved to the db
891
-     *
892
-     * @param string        $relationName       - the type of object that is cached
893
-     * @param EE_Base_Class $newly_saved_object - the newly saved object to be re-cached
894
-     * @param string        $current_cache_id   - the ID that was used when originally caching the object
895
-     * @return boolean TRUE on success, FALSE on fail
896
-     * @throws ReflectionException
897
-     * @throws InvalidArgumentException
898
-     * @throws InvalidInterfaceException
899
-     * @throws InvalidDataTypeException
900
-     * @throws EE_Error
901
-     */
902
-    public function update_cache_after_object_save(
903
-        $relationName,
904
-        EE_Base_Class $newly_saved_object,
905
-        $current_cache_id = ''
906
-    ) {
907
-        // verify that incoming object is of the correct type
908
-        $obj_class = 'EE_' . $relationName;
909
-        if ($newly_saved_object instanceof $obj_class) {
910
-            /* @type EE_Base_Class $newly_saved_object */
911
-            // now get the type of relation
912
-            $relationship_to_model = $this->get_model()->related_settings_for($relationName);
913
-            // if this is a 1:1 relationship
914
-            if ($relationship_to_model instanceof EE_Belongs_To_Relation) {
915
-                // then just replace the cached object with the newly saved object
916
-                $this->_model_relations[ $relationName ] = $newly_saved_object;
917
-                return true;
918
-                // or if it's some kind of sordid feral polyamorous relationship...
919
-            }
920
-            if (is_array($this->_model_relations[ $relationName ])
921
-                      && isset($this->_model_relations[ $relationName ][ $current_cache_id ])
922
-            ) {
923
-                // then remove the current cached item
924
-                unset($this->_model_relations[ $relationName ][ $current_cache_id ]);
925
-                // and cache the newly saved object using it's new ID
926
-                $this->_model_relations[ $relationName ][ $newly_saved_object->ID() ] = $newly_saved_object;
927
-                return true;
928
-            }
929
-        }
930
-        return false;
931
-    }
932
-
933
-
934
-    /**
935
-     * Fetches a single EE_Base_Class on that relation. (If the relation is of type
936
-     * BelongsTo, it will only ever have 1 object. However, other relations could have an array of objects)
937
-     *
938
-     * @param string $relationName
939
-     * @return EE_Base_Class
940
-     */
941
-    public function get_one_from_cache($relationName)
942
-    {
943
-        $cached_array_or_object = isset($this->_model_relations[ $relationName ])
944
-            ? $this->_model_relations[ $relationName ]
945
-            : null;
946
-        if (is_array($cached_array_or_object)) {
947
-            return array_shift($cached_array_or_object);
948
-        }
949
-        return $cached_array_or_object;
950
-    }
951
-
952
-
953
-    /**
954
-     * Fetches a single EE_Base_Class on that relation. (If the relation is of type
955
-     * BelongsTo, it will only ever have 1 object. However, other relations could have an array of objects)
956
-     *
957
-     * @param string $relationName
958
-     * @throws ReflectionException
959
-     * @throws InvalidArgumentException
960
-     * @throws InvalidInterfaceException
961
-     * @throws InvalidDataTypeException
962
-     * @throws EE_Error
963
-     * @return EE_Base_Class[] NOT necessarily indexed by primary keys
964
-     */
965
-    public function get_all_from_cache($relationName)
966
-    {
967
-        $objects = isset($this->_model_relations[ $relationName ]) ? $this->_model_relations[ $relationName ] : array();
968
-        // if the result is not an array, but exists, make it an array
969
-        $objects = is_array($objects) ? $objects : array($objects);
970
-        //bugfix for https://events.codebasehq.com/projects/event-espresso/tickets/7143
971
-        //basically, if this model object was stored in the session, and these cached model objects
972
-        //already have IDs, let's make sure they're in their model's entity mapper
973
-        //otherwise we will have duplicates next time we call
974
-        // EE_Registry::instance()->load_model( $relationName )->get_one_by_ID( $result->ID() );
975
-        $model = EE_Registry::instance()->load_model($relationName);
976
-        foreach ($objects as $model_object) {
977
-            if ($model instanceof EEM_Base && $model_object instanceof EE_Base_Class) {
978
-                //ensure its in the map if it has an ID; otherwise it will be added to the map when its saved
979
-                if ($model_object->ID()) {
980
-                    $model->add_to_entity_map($model_object);
981
-                }
982
-            } else {
983
-                throw new EE_Error(
984
-                    sprintf(
985
-                        esc_html__(
986
-                            'Error retrieving related model objects. Either $1%s is not a model or $2%s is not a model object',
987
-                            'event_espresso'
988
-                        ),
989
-                        $relationName,
990
-                        gettype($model_object)
991
-                    )
992
-                );
993
-            }
994
-        }
995
-        return $objects;
996
-    }
997
-
998
-
999
-    /**
1000
-     * Returns the next x number of EE_Base_Class objects in sequence from this object as found in the database
1001
-     * matching the given query conditions.
1002
-     *
1003
-     * @param null  $field_to_order_by  What field is being used as the reference point.
1004
-     * @param int   $limit              How many objects to return.
1005
-     * @param array $query_params       Any additional conditions on the query.
1006
-     * @param null  $columns_to_select  If left null, then an array of EE_Base_Class objects is returned, otherwise
1007
-     *                                  you can indicate just the columns you want returned
1008
-     * @return array|EE_Base_Class[]
1009
-     * @throws ReflectionException
1010
-     * @throws InvalidArgumentException
1011
-     * @throws InvalidInterfaceException
1012
-     * @throws InvalidDataTypeException
1013
-     * @throws EE_Error
1014
-     */
1015
-    public function next_x($field_to_order_by = null, $limit = 1, $query_params = array(), $columns_to_select = null)
1016
-    {
1017
-        $model         = $this->get_model();
1018
-        $field         = empty($field_to_order_by) && $model->has_primary_key_field()
1019
-            ? $model->get_primary_key_field()->get_name()
1020
-            : $field_to_order_by;
1021
-        $current_value = ! empty($field) ? $this->get($field) : null;
1022
-        if (empty($field) || empty($current_value)) {
1023
-            return array();
1024
-        }
1025
-        return $model->next_x($current_value, $field, $limit, $query_params, $columns_to_select);
1026
-    }
1027
-
1028
-
1029
-    /**
1030
-     * Returns the previous x number of EE_Base_Class objects in sequence from this object as found in the database
1031
-     * matching the given query conditions.
1032
-     *
1033
-     * @param null  $field_to_order_by  What field is being used as the reference point.
1034
-     * @param int   $limit              How many objects to return.
1035
-     * @param array $query_params       Any additional conditions on the query.
1036
-     * @param null  $columns_to_select  If left null, then an array of EE_Base_Class objects is returned, otherwise
1037
-     *                                  you can indicate just the columns you want returned
1038
-     * @return array|EE_Base_Class[]
1039
-     * @throws ReflectionException
1040
-     * @throws InvalidArgumentException
1041
-     * @throws InvalidInterfaceException
1042
-     * @throws InvalidDataTypeException
1043
-     * @throws EE_Error
1044
-     */
1045
-    public function previous_x(
1046
-        $field_to_order_by = null,
1047
-        $limit = 1,
1048
-        $query_params = array(),
1049
-        $columns_to_select = null
1050
-    ) {
1051
-        $model         = $this->get_model();
1052
-        $field         = empty($field_to_order_by) && $model->has_primary_key_field()
1053
-            ? $model->get_primary_key_field()->get_name()
1054
-            : $field_to_order_by;
1055
-        $current_value = ! empty($field) ? $this->get($field) : null;
1056
-        if (empty($field) || empty($current_value)) {
1057
-            return array();
1058
-        }
1059
-        return $model->previous_x($current_value, $field, $limit, $query_params, $columns_to_select);
1060
-    }
1061
-
1062
-
1063
-    /**
1064
-     * Returns the next EE_Base_Class object in sequence from this object as found in the database
1065
-     * matching the given query conditions.
1066
-     *
1067
-     * @param null  $field_to_order_by  What field is being used as the reference point.
1068
-     * @param array $query_params       Any additional conditions on the query.
1069
-     * @param null  $columns_to_select  If left null, then an array of EE_Base_Class objects is returned, otherwise
1070
-     *                                  you can indicate just the columns you want returned
1071
-     * @return array|EE_Base_Class
1072
-     * @throws ReflectionException
1073
-     * @throws InvalidArgumentException
1074
-     * @throws InvalidInterfaceException
1075
-     * @throws InvalidDataTypeException
1076
-     * @throws EE_Error
1077
-     */
1078
-    public function next($field_to_order_by = null, $query_params = array(), $columns_to_select = null)
1079
-    {
1080
-        $model         = $this->get_model();
1081
-        $field         = empty($field_to_order_by) && $model->has_primary_key_field()
1082
-            ? $model->get_primary_key_field()->get_name()
1083
-            : $field_to_order_by;
1084
-        $current_value = ! empty($field) ? $this->get($field) : null;
1085
-        if (empty($field) || empty($current_value)) {
1086
-            return array();
1087
-        }
1088
-        return $model->next($current_value, $field, $query_params, $columns_to_select);
1089
-    }
1090
-
1091
-
1092
-    /**
1093
-     * Returns the previous EE_Base_Class object in sequence from this object as found in the database
1094
-     * matching the given query conditions.
1095
-     *
1096
-     * @param null  $field_to_order_by  What field is being used as the reference point.
1097
-     * @param array $query_params       Any additional conditions on the query.
1098
-     * @param null  $columns_to_select  If left null, then an EE_Base_Class object is returned, otherwise
1099
-     *                                  you can indicate just the column you want returned
1100
-     * @return array|EE_Base_Class
1101
-     * @throws ReflectionException
1102
-     * @throws InvalidArgumentException
1103
-     * @throws InvalidInterfaceException
1104
-     * @throws InvalidDataTypeException
1105
-     * @throws EE_Error
1106
-     */
1107
-    public function previous($field_to_order_by = null, $query_params = array(), $columns_to_select = null)
1108
-    {
1109
-        $model         = $this->get_model();
1110
-        $field         = empty($field_to_order_by) && $model->has_primary_key_field()
1111
-            ? $model->get_primary_key_field()->get_name()
1112
-            : $field_to_order_by;
1113
-        $current_value = ! empty($field) ? $this->get($field) : null;
1114
-        if (empty($field) || empty($current_value)) {
1115
-            return array();
1116
-        }
1117
-        return $model->previous($current_value, $field, $query_params, $columns_to_select);
1118
-    }
1119
-
1120
-
1121
-    /**
1122
-     * Overrides parent because parent expects old models.
1123
-     * This also doesn't do any validation, and won't work for serialized arrays
1124
-     *
1125
-     * @param string $field_name
1126
-     * @param mixed  $field_value_from_db
1127
-     * @throws ReflectionException
1128
-     * @throws InvalidArgumentException
1129
-     * @throws InvalidInterfaceException
1130
-     * @throws InvalidDataTypeException
1131
-     * @throws EE_Error
1132
-     */
1133
-    public function set_from_db($field_name, $field_value_from_db)
1134
-    {
1135
-        $field_obj = $this->get_model()->field_settings_for($field_name);
1136
-        if ($field_obj instanceof EE_Model_Field_Base) {
1137
-            //you would think the DB has no NULLs for non-null label fields right? wrong!
1138
-            //eg, a CPT model object could have an entry in the posts table, but no
1139
-            //entry in the meta table. Meaning that all its columns in the meta table
1140
-            //are null! yikes! so when we find one like that, use defaults for its meta columns
1141
-            if ($field_value_from_db === null) {
1142
-                if ($field_obj->is_nullable()) {
1143
-                    //if the field allows nulls, then let it be null
1144
-                    $field_value = null;
1145
-                } else {
1146
-                    $field_value = $field_obj->get_default_value();
1147
-                }
1148
-            } else {
1149
-                $field_value = $field_obj->prepare_for_set_from_db($field_value_from_db);
1150
-            }
1151
-            $this->_fields[ $field_name ] = $field_value;
1152
-            $this->_clear_cached_property($field_name);
1153
-        }
1154
-    }
1155
-
1156
-
1157
-    /**
1158
-     * verifies that the specified field is of the correct type
1159
-     *
1160
-     * @param string $field_name
1161
-     * @param string $extra_cache_ref This allows the user to specify an extra cache ref for the given property
1162
-     *                                (in cases where the same property may be used for different outputs
1163
-     *                                - i.e. datetime, money etc.)
1164
-     * @return mixed
1165
-     * @throws ReflectionException
1166
-     * @throws InvalidArgumentException
1167
-     * @throws InvalidInterfaceException
1168
-     * @throws InvalidDataTypeException
1169
-     * @throws EE_Error
1170
-     */
1171
-    public function get($field_name, $extra_cache_ref = null)
1172
-    {
1173
-        return $this->_get_cached_property($field_name, false, $extra_cache_ref);
1174
-    }
1175
-
1176
-
1177
-    /**
1178
-     * This method simply returns the RAW unprocessed value for the given property in this class
1179
-     *
1180
-     * @param  string $field_name A valid fieldname
1181
-     * @return mixed              Whatever the raw value stored on the property is.
1182
-     * @throws ReflectionException
1183
-     * @throws InvalidArgumentException
1184
-     * @throws InvalidInterfaceException
1185
-     * @throws InvalidDataTypeException
1186
-     * @throws EE_Error if fieldSettings is misconfigured or the field doesn't exist.
1187
-     */
1188
-    public function get_raw($field_name)
1189
-    {
1190
-        $field_settings = $this->get_model()->field_settings_for($field_name);
1191
-        return $field_settings instanceof EE_Datetime_Field && $this->_fields[ $field_name ] instanceof DateTime
1192
-            ? $this->_fields[ $field_name ]->format('U')
1193
-            : $this->_fields[ $field_name ];
1194
-    }
1195
-
1196
-
1197
-    /**
1198
-     * This is used to return the internal DateTime object used for a field that is a
1199
-     * EE_Datetime_Field.
1200
-     *
1201
-     * @param string $field_name               The field name retrieving the DateTime object.
1202
-     * @return mixed null | false | DateTime  If the requested field is NOT a EE_Datetime_Field then
1203
-     * @throws ReflectionException
1204
-     * @throws InvalidArgumentException
1205
-     * @throws InvalidInterfaceException
1206
-     * @throws InvalidDataTypeException
1207
-     * @throws EE_Error
1208
-     *                                         an error is set and false returned.  If the field IS an
1209
-     *                                         EE_Datetime_Field and but the field value is null, then
1210
-     *                                         just null is returned (because that indicates that likely
1211
-     *                                         this field is nullable).
1212
-     */
1213
-    public function get_DateTime_object($field_name)
1214
-    {
1215
-        $field_settings = $this->get_model()->field_settings_for($field_name);
1216
-        if (! $field_settings instanceof EE_Datetime_Field) {
1217
-            EE_Error::add_error(
1218
-                sprintf(
1219
-                    esc_html__(
1220
-                        'The field %s is not an EE_Datetime_Field field.  There is no DateTime object stored on this field type.',
1221
-                        'event_espresso'
1222
-                    ),
1223
-                    $field_name
1224
-                ),
1225
-                __FILE__,
1226
-                __FUNCTION__,
1227
-                __LINE__
1228
-            );
1229
-            return false;
1230
-        }
1231
-        return $this->_fields[ $field_name ];
1232
-    }
1233
-
1234
-
1235
-    /**
1236
-     * To be used in template to immediately echo out the value, and format it for output.
1237
-     * Eg, should call stripslashes and whatnot before echoing
1238
-     *
1239
-     * @param string $field_name      the name of the field as it appears in the DB
1240
-     * @param string $extra_cache_ref This allows the user to specify an extra cache ref for the given property
1241
-     *                                (in cases where the same property may be used for different outputs
1242
-     *                                - i.e. datetime, money etc.)
1243
-     * @return void
1244
-     * @throws ReflectionException
1245
-     * @throws InvalidArgumentException
1246
-     * @throws InvalidInterfaceException
1247
-     * @throws InvalidDataTypeException
1248
-     * @throws EE_Error
1249
-     */
1250
-    public function e($field_name, $extra_cache_ref = null)
1251
-    {
1252
-        echo $this->get_pretty($field_name, $extra_cache_ref);
1253
-    }
1254
-
1255
-
1256
-    /**
1257
-     * Exactly like e(), echoes out the field, but sets its schema to 'form_input', so that it
1258
-     * can be easily used as the value of form input.
1259
-     *
1260
-     * @param string $field_name
1261
-     * @return void
1262
-     * @throws ReflectionException
1263
-     * @throws InvalidArgumentException
1264
-     * @throws InvalidInterfaceException
1265
-     * @throws InvalidDataTypeException
1266
-     * @throws EE_Error
1267
-     */
1268
-    public function f($field_name)
1269
-    {
1270
-        $this->e($field_name, 'form_input');
1271
-    }
1272
-
1273
-
1274
-    /**
1275
-     * Same as `f()` but just returns the value instead of echoing it
1276
-     *
1277
-     * @param string $field_name
1278
-     * @return string
1279
-     * @throws ReflectionException
1280
-     * @throws InvalidArgumentException
1281
-     * @throws InvalidInterfaceException
1282
-     * @throws InvalidDataTypeException
1283
-     * @throws EE_Error
1284
-     */
1285
-    public function get_f($field_name)
1286
-    {
1287
-        return (string) $this->get_pretty($field_name, 'form_input');
1288
-    }
1289
-
1290
-
1291
-    /**
1292
-     * Gets a pretty view of the field's value. $extra_cache_ref can specify different formats for this.
1293
-     * The $extra_cache_ref will be passed to the model field's prepare_for_pretty_echoing, so consult the field's class
1294
-     * to see what options are available.
1295
-     *
1296
-     * @param string $field_name
1297
-     * @param string $extra_cache_ref This allows the user to specify an extra cache ref for the given property
1298
-     *                                (in cases where the same property may be used for different outputs
1299
-     *                                - i.e. datetime, money etc.)
1300
-     * @return mixed
1301
-     * @throws ReflectionException
1302
-     * @throws InvalidArgumentException
1303
-     * @throws InvalidInterfaceException
1304
-     * @throws InvalidDataTypeException
1305
-     * @throws EE_Error
1306
-     */
1307
-    public function get_pretty($field_name, $extra_cache_ref = null)
1308
-    {
1309
-        return $this->_get_cached_property($field_name, true, $extra_cache_ref);
1310
-    }
1311
-
1312
-
1313
-    /**
1314
-     * This simply returns the datetime for the given field name
1315
-     * Note: this protected function is called by the wrapper get_date or get_time or get_datetime functions
1316
-     * (and the equivalent e_date, e_time, e_datetime).
1317
-     *
1318
-     * @access   protected
1319
-     * @param string   $field_name   Field on the instantiated EE_Base_Class child object
1320
-     * @param string   $dt_frmt      valid datetime format used for date
1321
-     *                               (if '' then we just use the default on the field,
1322
-     *                               if NULL we use the last-used format)
1323
-     * @param string   $tm_frmt      Same as above except this is for time format
1324
-     * @param string   $date_or_time if NULL then both are returned, otherwise "D" = only date and "T" = only time.
1325
-     * @param  boolean $echo         Whether the dtt is echoing using pretty echoing or just returned using vanilla get
1326
-     * @return string|bool|EE_Error string on success, FALSE on fail, or EE_Error Exception is thrown
1327
-     *                               if field is not a valid dtt field, or void if echoing
1328
-     * @throws ReflectionException
1329
-     * @throws InvalidArgumentException
1330
-     * @throws InvalidInterfaceException
1331
-     * @throws InvalidDataTypeException
1332
-     * @throws EE_Error
1333
-     */
1334
-    protected function _get_datetime($field_name, $dt_frmt = '', $tm_frmt = '', $date_or_time = '', $echo = false)
1335
-    {
1336
-        // clear cached property
1337
-        $this->_clear_cached_property($field_name);
1338
-        //reset format properties because they are used in get()
1339
-        $this->_dt_frmt = $dt_frmt !== '' ? $dt_frmt : $this->_dt_frmt;
1340
-        $this->_tm_frmt = $tm_frmt !== '' ? $tm_frmt : $this->_tm_frmt;
1341
-        if ($echo) {
1342
-            $this->e($field_name, $date_or_time);
1343
-            return '';
1344
-        }
1345
-        return $this->get($field_name, $date_or_time);
1346
-    }
1347
-
1348
-
1349
-    /**
1350
-     * below are wrapper functions for the various datetime outputs that can be obtained for JUST returning the date
1351
-     * portion of a datetime value. (note the only difference between get_ and e_ is one returns the value and the
1352
-     * other echoes the pretty value for dtt)
1353
-     *
1354
-     * @param  string $field_name name of model object datetime field holding the value
1355
-     * @param  string $format     format for the date returned (if NULL we use default in dt_frmt property)
1356
-     * @return string            datetime value formatted
1357
-     * @throws ReflectionException
1358
-     * @throws InvalidArgumentException
1359
-     * @throws InvalidInterfaceException
1360
-     * @throws InvalidDataTypeException
1361
-     * @throws EE_Error
1362
-     */
1363
-    public function get_date($field_name, $format = '')
1364
-    {
1365
-        return $this->_get_datetime($field_name, $format, null, 'D');
1366
-    }
1367
-
1368
-
1369
-    /**
1370
-     * @param        $field_name
1371
-     * @param string $format
1372
-     * @throws ReflectionException
1373
-     * @throws InvalidArgumentException
1374
-     * @throws InvalidInterfaceException
1375
-     * @throws InvalidDataTypeException
1376
-     * @throws EE_Error
1377
-     */
1378
-    public function e_date($field_name, $format = '')
1379
-    {
1380
-        $this->_get_datetime($field_name, $format, null, 'D', true);
1381
-    }
1382
-
1383
-
1384
-    /**
1385
-     * below are wrapper functions for the various datetime outputs that can be obtained for JUST returning the time
1386
-     * portion of a datetime value. (note the only difference between get_ and e_ is one returns the value and the
1387
-     * other echoes the pretty value for dtt)
1388
-     *
1389
-     * @param  string $field_name name of model object datetime field holding the value
1390
-     * @param  string $format     format for the time returned ( if NULL we use default in tm_frmt property)
1391
-     * @return string             datetime value formatted
1392
-     * @throws ReflectionException
1393
-     * @throws InvalidArgumentException
1394
-     * @throws InvalidInterfaceException
1395
-     * @throws InvalidDataTypeException
1396
-     * @throws EE_Error
1397
-     */
1398
-    public function get_time($field_name, $format = '')
1399
-    {
1400
-        return $this->_get_datetime($field_name, null, $format, 'T');
1401
-    }
1402
-
1403
-
1404
-    /**
1405
-     * @param        $field_name
1406
-     * @param string $format
1407
-     * @throws ReflectionException
1408
-     * @throws InvalidArgumentException
1409
-     * @throws InvalidInterfaceException
1410
-     * @throws InvalidDataTypeException
1411
-     * @throws EE_Error
1412
-     */
1413
-    public function e_time($field_name, $format = '')
1414
-    {
1415
-        $this->_get_datetime($field_name, null, $format, 'T', true);
1416
-    }
1417
-
1418
-
1419
-    /**
1420
-     * below are wrapper functions for the various datetime outputs that can be obtained for returning the date AND
1421
-     * time portion of a datetime value. (note the only difference between get_ and e_ is one returns the value and the
1422
-     * other echoes the pretty value for dtt)
1423
-     *
1424
-     * @param  string $field_name name of model object datetime field holding the value
1425
-     * @param  string $dt_frmt    format for the date returned (if NULL we use default in dt_frmt property)
1426
-     * @param  string $tm_frmt    format for the time returned (if NULL we use default in tm_frmt property)
1427
-     * @return string             datetime value formatted
1428
-     * @throws ReflectionException
1429
-     * @throws InvalidArgumentException
1430
-     * @throws InvalidInterfaceException
1431
-     * @throws InvalidDataTypeException
1432
-     * @throws EE_Error
1433
-     */
1434
-    public function get_datetime($field_name, $dt_frmt = '', $tm_frmt = '')
1435
-    {
1436
-        return $this->_get_datetime($field_name, $dt_frmt, $tm_frmt);
1437
-    }
1438
-
1439
-
1440
-    /**
1441
-     * @param string $field_name
1442
-     * @param string $dt_frmt
1443
-     * @param string $tm_frmt
1444
-     * @throws ReflectionException
1445
-     * @throws InvalidArgumentException
1446
-     * @throws InvalidInterfaceException
1447
-     * @throws InvalidDataTypeException
1448
-     * @throws EE_Error
1449
-     */
1450
-    public function e_datetime($field_name, $dt_frmt = '', $tm_frmt = '')
1451
-    {
1452
-        $this->_get_datetime($field_name, $dt_frmt, $tm_frmt, null, true);
1453
-    }
1454
-
1455
-
1456
-    /**
1457
-     * Get the i8ln value for a date using the WordPress @see date_i18n function.
1458
-     *
1459
-     * @param string $field_name The EE_Datetime_Field reference for the date being retrieved.
1460
-     * @param string $format     PHP valid date/time string format.  If none is provided then the internal set format
1461
-     *                           on the object will be used.
1462
-     * @return string Date and time string in set locale or false if no field exists for the given
1463
-     * @throws ReflectionException
1464
-     * @throws InvalidArgumentException
1465
-     * @throws InvalidInterfaceException
1466
-     * @throws InvalidDataTypeException
1467
-     * @throws EE_Error
1468
-     *                           field name.
1469
-     */
1470
-    public function get_i18n_datetime($field_name, $format = '')
1471
-    {
1472
-        $format = empty($format) ? $this->_dt_frmt . ' ' . $this->_tm_frmt : $format;
1473
-        return date_i18n(
1474
-            $format,
1475
-            EEH_DTT_Helper::get_timestamp_with_offset(
1476
-                $this->get_raw($field_name),
1477
-                $this->_timezone
1478
-            )
1479
-        );
1480
-    }
1481
-
1482
-
1483
-    /**
1484
-     * This method validates whether the given field name is a valid field on the model object as well as it is of a
1485
-     * type EE_Datetime_Field.  On success there will be returned the field settings.  On fail an EE_Error exception is
1486
-     * thrown.
1487
-     *
1488
-     * @param  string $field_name The field name being checked
1489
-     * @throws ReflectionException
1490
-     * @throws InvalidArgumentException
1491
-     * @throws InvalidInterfaceException
1492
-     * @throws InvalidDataTypeException
1493
-     * @throws EE_Error
1494
-     * @return EE_Datetime_Field
1495
-     */
1496
-    protected function _get_dtt_field_settings($field_name)
1497
-    {
1498
-        $field = $this->get_model()->field_settings_for($field_name);
1499
-        //check if field is dtt
1500
-        if ($field instanceof EE_Datetime_Field) {
1501
-            return $field;
1502
-        }
1503
-        throw new EE_Error(
1504
-            sprintf(
1505
-                esc_html__(
1506
-                    'The field name "%s" has been requested for the EE_Base_Class datetime functions and it is not a valid EE_Datetime_Field.  Please check the spelling of the field and make sure it has been setup as a EE_Datetime_Field in the %s model constructor',
1507
-                    'event_espresso'
1508
-                ),
1509
-                $field_name,
1510
-                self::_get_model_classname(get_class($this))
1511
-            )
1512
-        );
1513
-    }
1514
-
1515
-
1516
-
1517
-
1518
-    /**
1519
-     * NOTE ABOUT BELOW:
1520
-     * These convenience date and time setters are for setting date and time independently.  In other words you might
1521
-     * want to change the time on a datetime_field but leave the date the same (or vice versa). IF on the other hand
1522
-     * you want to set both date and time at the same time, you can just use the models default set($fieldname,$value)
1523
-     * method and make sure you send the entire datetime value for setting.
1524
-     */
1525
-    /**
1526
-     * sets the time on a datetime property
1527
-     *
1528
-     * @access protected
1529
-     * @param string|Datetime $time      a valid time string for php datetime functions (or DateTime object)
1530
-     * @param string          $fieldname the name of the field the time is being set on (must match a EE_Datetime_Field)
1531
-     * @throws ReflectionException
1532
-     * @throws InvalidArgumentException
1533
-     * @throws InvalidInterfaceException
1534
-     * @throws InvalidDataTypeException
1535
-     * @throws EE_Error
1536
-     */
1537
-    protected function _set_time_for($time, $fieldname)
1538
-    {
1539
-        $this->_set_date_time('T', $time, $fieldname);
1540
-    }
1541
-
1542
-
1543
-    /**
1544
-     * sets the date on a datetime property
1545
-     *
1546
-     * @access protected
1547
-     * @param string|DateTime $date      a valid date string for php datetime functions ( or DateTime object)
1548
-     * @param string          $fieldname the name of the field the date is being set on (must match a EE_Datetime_Field)
1549
-     * @throws ReflectionException
1550
-     * @throws InvalidArgumentException
1551
-     * @throws InvalidInterfaceException
1552
-     * @throws InvalidDataTypeException
1553
-     * @throws EE_Error
1554
-     */
1555
-    protected function _set_date_for($date, $fieldname)
1556
-    {
1557
-        $this->_set_date_time('D', $date, $fieldname);
1558
-    }
1559
-
1560
-
1561
-    /**
1562
-     * This takes care of setting a date or time independently on a given model object property. This method also
1563
-     * verifies that the given fieldname matches a model object property and is for a EE_Datetime_Field field
1564
-     *
1565
-     * @access protected
1566
-     * @param string          $what           "T" for time, 'B' for both, 'D' for Date.
1567
-     * @param string|DateTime $datetime_value A valid Date or Time string (or DateTime object)
1568
-     * @param string          $fieldname      the name of the field the date OR time is being set on (must match a
1569
-     *                                        EE_Datetime_Field property)
1570
-     * @throws ReflectionException
1571
-     * @throws InvalidArgumentException
1572
-     * @throws InvalidInterfaceException
1573
-     * @throws InvalidDataTypeException
1574
-     * @throws EE_Error
1575
-     */
1576
-    protected function _set_date_time($what = 'T', $datetime_value, $fieldname)
1577
-    {
1578
-        $field = $this->_get_dtt_field_settings($fieldname);
1579
-        $field->set_timezone($this->_timezone);
1580
-        $field->set_date_format($this->_dt_frmt);
1581
-        $field->set_time_format($this->_tm_frmt);
1582
-        switch ($what) {
1583
-            case 'T' :
1584
-                $this->_fields[ $fieldname ] = $field->prepare_for_set_with_new_time(
1585
-                    $datetime_value,
1586
-                    $this->_fields[ $fieldname ]
1587
-                );
1588
-                break;
1589
-            case 'D' :
1590
-                $this->_fields[ $fieldname ] = $field->prepare_for_set_with_new_date(
1591
-                    $datetime_value,
1592
-                    $this->_fields[ $fieldname ]
1593
-                );
1594
-                break;
1595
-            case 'B' :
1596
-                $this->_fields[ $fieldname ] = $field->prepare_for_set($datetime_value);
1597
-                break;
1598
-        }
1599
-        $this->_clear_cached_property($fieldname);
1600
-    }
1601
-
1602
-
1603
-    /**
1604
-     * This will return a timestamp for the website timezone but ONLY when the current website timezone is different
1605
-     * than the timezone set for the website. NOTE, this currently only works well with methods that return values.  If
1606
-     * you use it with methods that echo values the $_timestamp property may not get reset to its original value and
1607
-     * that could lead to some unexpected results!
1608
-     *
1609
-     * @access public
1610
-     * @param string $field_name               This is the name of the field on the object that contains the date/time
1611
-     *                                         value being returned.
1612
-     * @param string $callback                 must match a valid method in this class (defaults to get_datetime)
1613
-     * @param mixed (array|string) $args       This is the arguments that will be passed to the callback.
1614
-     * @param string $prepend                  You can include something to prepend on the timestamp
1615
-     * @param string $append                   You can include something to append on the timestamp
1616
-     * @throws ReflectionException
1617
-     * @throws InvalidArgumentException
1618
-     * @throws InvalidInterfaceException
1619
-     * @throws InvalidDataTypeException
1620
-     * @throws EE_Error
1621
-     * @return string timestamp
1622
-     */
1623
-    public function display_in_my_timezone(
1624
-        $field_name,
1625
-        $callback = 'get_datetime',
1626
-        $args = null,
1627
-        $prepend = '',
1628
-        $append = ''
1629
-    ) {
1630
-        $timezone = EEH_DTT_Helper::get_timezone();
1631
-        if ($timezone === $this->_timezone) {
1632
-            return '';
1633
-        }
1634
-        $original_timezone = $this->_timezone;
1635
-        $this->set_timezone($timezone);
1636
-        $fn   = (array) $field_name;
1637
-        $args = array_merge($fn, (array) $args);
1638
-        if (! method_exists($this, $callback)) {
1639
-            throw new EE_Error(
1640
-                sprintf(
1641
-                    esc_html__(
1642
-                        'The method named "%s" given as the callback param in "display_in_my_timezone" does not exist.  Please check your spelling',
1643
-                        'event_espresso'
1644
-                    ),
1645
-                    $callback
1646
-                )
1647
-            );
1648
-        }
1649
-        $args   = (array) $args;
1650
-        $return = $prepend . call_user_func_array(array($this, $callback), $args) . $append;
1651
-        $this->set_timezone($original_timezone);
1652
-        return $return;
1653
-    }
1654
-
1655
-
1656
-    /**
1657
-     * Deletes this model object.
1658
-     * This calls the `EE_Base_Class::_delete` method.  Child classes wishing to change default behaviour should
1659
-     * override
1660
-     * `EE_Base_Class::_delete` NOT this class.
1661
-     *
1662
-     * @return boolean | int
1663
-     * @throws ReflectionException
1664
-     * @throws InvalidArgumentException
1665
-     * @throws InvalidInterfaceException
1666
-     * @throws InvalidDataTypeException
1667
-     * @throws EE_Error
1668
-     */
1669
-    public function delete()
1670
-    {
1671
-        /**
1672
-         * Called just before the `EE_Base_Class::_delete` method call.
1673
-         * Note:
1674
-         * `EE_Base_Class::_delete` might be overridden by child classes so any client code hooking into these actions
1675
-         * should be aware that `_delete` may not always result in a permanent delete.
1676
-         * For example, `EE_Soft_Delete_Base_Class::_delete`
1677
-         * soft deletes (trash) the object and does not permanently delete it.
1678
-         *
1679
-         * @param EE_Base_Class $model_object about to be 'deleted'
1680
-         */
1681
-        do_action('AHEE__EE_Base_Class__delete__before', $this);
1682
-        $result = $this->_delete();
1683
-        /**
1684
-         * Called just after the `EE_Base_Class::_delete` method call.
1685
-         * Note:
1686
-         * `EE_Base_Class::_delete` might be overridden by child classes so any client code hooking into these actions
1687
-         * should be aware that `_delete` may not always result in a permanent delete.
1688
-         * For example `EE_Soft_Base_Class::_delete`
1689
-         * soft deletes (trash) the object and does not permanently delete it.
1690
-         *
1691
-         * @param EE_Base_Class $model_object that was just 'deleted'
1692
-         * @param boolean       $result
1693
-         */
1694
-        do_action('AHEE__EE_Base_Class__delete__end', $this, $result);
1695
-        return $result;
1696
-    }
1697
-
1698
-
1699
-    /**
1700
-     * Calls the specific delete method for the instantiated class.
1701
-     * This method is called by the public `EE_Base_Class::delete` method.  Any child classes desiring to override
1702
-     * default functionality for "delete" (which is to call `permanently_delete`) should override this method NOT
1703
-     * `EE_Base_Class::delete`
1704
-     *
1705
-     * @return bool|int
1706
-     * @throws ReflectionException
1707
-     * @throws InvalidArgumentException
1708
-     * @throws InvalidInterfaceException
1709
-     * @throws InvalidDataTypeException
1710
-     * @throws EE_Error
1711
-     */
1712
-    protected function _delete()
1713
-    {
1714
-        return $this->delete_permanently();
1715
-    }
1716
-
1717
-
1718
-    /**
1719
-     * Deletes this model object permanently from db
1720
-     * (but keep in mind related models may block the delete and return an error)
1721
-     *
1722
-     * @return bool | int
1723
-     * @throws ReflectionException
1724
-     * @throws InvalidArgumentException
1725
-     * @throws InvalidInterfaceException
1726
-     * @throws InvalidDataTypeException
1727
-     * @throws EE_Error
1728
-     */
1729
-    public function delete_permanently()
1730
-    {
1731
-        /**
1732
-         * Called just before HARD deleting a model object
1733
-         *
1734
-         * @param EE_Base_Class $model_object about to be 'deleted'
1735
-         */
1736
-        do_action('AHEE__EE_Base_Class__delete_permanently__before', $this);
1737
-        $model  = $this->get_model();
1738
-        $result = $model->delete_permanently_by_ID($this->ID());
1739
-        $this->refresh_cache_of_related_objects();
1740
-        /**
1741
-         * Called just after HARD deleting a model object
1742
-         *
1743
-         * @param EE_Base_Class $model_object that was just 'deleted'
1744
-         * @param boolean       $result
1745
-         */
1746
-        do_action('AHEE__EE_Base_Class__delete_permanently__end', $this, $result);
1747
-        return $result;
1748
-    }
1749
-
1750
-
1751
-    /**
1752
-     * When this model object is deleted, it may still be cached on related model objects. This clears the cache of
1753
-     * related model objects
1754
-     *
1755
-     * @throws ReflectionException
1756
-     * @throws InvalidArgumentException
1757
-     * @throws InvalidInterfaceException
1758
-     * @throws InvalidDataTypeException
1759
-     * @throws EE_Error
1760
-     */
1761
-    public function refresh_cache_of_related_objects()
1762
-    {
1763
-        $model = $this->get_model();
1764
-        foreach ($model->relation_settings() as $relation_name => $relation_obj) {
1765
-            if (! empty($this->_model_relations[ $relation_name ])) {
1766
-                $related_objects = $this->_model_relations[ $relation_name ];
1767
-                if ($relation_obj instanceof EE_Belongs_To_Relation) {
1768
-                    //this relation only stores a single model object, not an array
1769
-                    //but let's make it consistent
1770
-                    $related_objects = array($related_objects);
1771
-                }
1772
-                foreach ($related_objects as $related_object) {
1773
-                    //only refresh their cache if they're in memory
1774
-                    if ($related_object instanceof EE_Base_Class) {
1775
-                        $related_object->clear_cache(
1776
-                            $model->get_this_model_name(),
1777
-                            $this
1778
-                        );
1779
-                    }
1780
-                }
1781
-            }
1782
-        }
1783
-    }
1784
-
1785
-
1786
-    /**
1787
-     *        Saves this object to the database. An array may be supplied to set some values on this
1788
-     * object just before saving.
1789
-     *
1790
-     * @access public
1791
-     * @param array $set_cols_n_values keys are field names, values are their new values,
1792
-     *                                 if provided during the save() method (often client code will change the fields'
1793
-     *                                 values before calling save)
1794
-     * @throws InvalidArgumentException
1795
-     * @throws InvalidInterfaceException
1796
-     * @throws InvalidDataTypeException
1797
-     * @throws EE_Error
1798
-     * @return int , 1 on a successful update, the ID of the new entry on insert; 0 on failure or if the model object
1799
-     *                                 isn't allowed to persist (as determined by EE_Base_Class::allow_persist())
1800
-     * @throws ReflectionException
1801
-     * @throws ReflectionException
1802
-     * @throws ReflectionException
1803
-     */
1804
-    public function save($set_cols_n_values = array())
1805
-    {
1806
-        $model = $this->get_model();
1807
-        /**
1808
-         * Filters the fields we're about to save on the model object
1809
-         *
1810
-         * @param array         $set_cols_n_values
1811
-         * @param EE_Base_Class $model_object
1812
-         */
1813
-        $set_cols_n_values = (array) apply_filters(
1814
-            'FHEE__EE_Base_Class__save__set_cols_n_values',
1815
-            $set_cols_n_values,
1816
-            $this
1817
-        );
1818
-        //set attributes as provided in $set_cols_n_values
1819
-        foreach ($set_cols_n_values as $column => $value) {
1820
-            $this->set($column, $value);
1821
-        }
1822
-        // no changes ? then don't do anything
1823
-        if (! $this->_has_changes && $this->ID() && $model->get_primary_key_field()->is_auto_increment()) {
1824
-            return 0;
1825
-        }
1826
-        /**
1827
-         * Saving a model object.
1828
-         * Before we perform a save, this action is fired.
1829
-         *
1830
-         * @param EE_Base_Class $model_object the model object about to be saved.
1831
-         */
1832
-        do_action('AHEE__EE_Base_Class__save__begin', $this);
1833
-        if (! $this->allow_persist()) {
1834
-            return 0;
1835
-        }
1836
-        // now get current attribute values
1837
-        $save_cols_n_values = $this->_fields;
1838
-        // if the object already has an ID, update it. Otherwise, insert it
1839
-        // also: change the assumption about values passed to the model NOT being prepare dby the model object.
1840
-        // They have been
1841
-        $old_assumption_concerning_value_preparation = $model
1842
-            ->get_assumption_concerning_values_already_prepared_by_model_object();
1843
-        $model->assume_values_already_prepared_by_model_object(true);
1844
-        //does this model have an autoincrement PK?
1845
-        if ($model->has_primary_key_field()) {
1846
-            if ($model->get_primary_key_field()->is_auto_increment()) {
1847
-                //ok check if it's set, if so: update; if not, insert
1848
-                if (! empty($save_cols_n_values[ $model->primary_key_name() ])) {
1849
-                    $results = $model->update_by_ID($save_cols_n_values, $this->ID());
1850
-                } else {
1851
-                    unset($save_cols_n_values[ $model->primary_key_name() ]);
1852
-                    $results = $model->insert($save_cols_n_values);
1853
-                    if ($results) {
1854
-                        //if successful, set the primary key
1855
-                        //but don't use the normal SET method, because it will check if
1856
-                        //an item with the same ID exists in the mapper & db, then
1857
-                        //will find it in the db (because we just added it) and THAT object
1858
-                        //will get added to the mapper before we can add this one!
1859
-                        //but if we just avoid using the SET method, all that headache can be avoided
1860
-                        $pk_field_name                   = $model->primary_key_name();
1861
-                        $this->_fields[ $pk_field_name ] = $results;
1862
-                        $this->_clear_cached_property($pk_field_name);
1863
-                        $model->add_to_entity_map($this);
1864
-                        $this->_update_cached_related_model_objs_fks();
1865
-                    }
1866
-                }
1867
-            } else {//PK is NOT auto-increment
1868
-                //so check if one like it already exists in the db
1869
-                if ($model->exists_by_ID($this->ID())) {
1870
-                    if (WP_DEBUG && ! $this->in_entity_map()) {
1871
-                        throw new EE_Error(
1872
-                            sprintf(
1873
-                                esc_html__(
1874
-                                    'Using a model object %1$s that is NOT in the entity map, can lead to unexpected errors. You should either: %4$s 1. Put it in the entity mapper by calling %2$s %4$s 2. Discard this model object and use what is in the entity mapper %4$s 3. Fetch from the database using %3$s',
1875
-                                    'event_espresso'
1876
-                                ),
1877
-                                get_class($this),
1878
-                                get_class($model) . '::instance()->add_to_entity_map()',
1879
-                                get_class($model) . '::instance()->get_one_by_ID()',
1880
-                                '<br />'
1881
-                            )
1882
-                        );
1883
-                    }
1884
-                    $results = $model->update_by_ID($save_cols_n_values, $this->ID());
1885
-                } else {
1886
-                    $results = $model->insert($save_cols_n_values);
1887
-                    $this->_update_cached_related_model_objs_fks();
1888
-                }
1889
-            }
1890
-        } else {//there is NO primary key
1891
-            $already_in_db = false;
1892
-            foreach ($model->unique_indexes() as $index) {
1893
-                $uniqueness_where_params = array_intersect_key($save_cols_n_values, $index->fields());
1894
-                if ($model->exists(array($uniqueness_where_params))) {
1895
-                    $already_in_db = true;
1896
-                }
1897
-            }
1898
-            if ($already_in_db) {
1899
-                $combined_pk_fields_n_values = array_intersect_key($save_cols_n_values,
1900
-                    $model->get_combined_primary_key_fields());
1901
-                $results                     = $model->update(
1902
-                    $save_cols_n_values,
1903
-                    $combined_pk_fields_n_values
1904
-                );
1905
-            } else {
1906
-                $results = $model->insert($save_cols_n_values);
1907
-            }
1908
-        }
1909
-        //restore the old assumption about values being prepared by the model object
1910
-        $model->assume_values_already_prepared_by_model_object(
1911
-                $old_assumption_concerning_value_preparation
1912
-            );
1913
-        /**
1914
-         * After saving the model object this action is called
1915
-         *
1916
-         * @param EE_Base_Class $model_object which was just saved
1917
-         * @param boolean|int   $results      if it were updated, TRUE or FALSE; if it were newly inserted
1918
-         *                                    the new ID (or 0 if an error occurred and it wasn't updated)
1919
-         */
1920
-        do_action('AHEE__EE_Base_Class__save__end', $this, $results);
1921
-        $this->_has_changes = false;
1922
-        return $results;
1923
-    }
1924
-
1925
-
1926
-    /**
1927
-     * Updates the foreign key on related models objects pointing to this to have this model object's ID
1928
-     * as their foreign key.  If the cached related model objects already exist in the db, saves them (so that the DB
1929
-     * is consistent) Especially useful in case we JUST added this model object ot the database and we want to let its
1930
-     * cached relations with foreign keys to it know about that change. Eg: we've created a transaction but haven't
1931
-     * saved it to the db. We also create a registration and don't save it to the DB, but we DO cache it on the
1932
-     * transaction. Now, when we save the transaction, the registration's TXN_ID will be automatically updated, whether
1933
-     * or not they exist in the DB (if they do, their DB records will be automatically updated)
1934
-     *
1935
-     * @return void
1936
-     * @throws ReflectionException
1937
-     * @throws InvalidArgumentException
1938
-     * @throws InvalidInterfaceException
1939
-     * @throws InvalidDataTypeException
1940
-     * @throws EE_Error
1941
-     */
1942
-    protected function _update_cached_related_model_objs_fks()
1943
-    {
1944
-        $model = $this->get_model();
1945
-        foreach ($model->relation_settings() as $relation_name => $relation_obj) {
1946
-            if ($relation_obj instanceof EE_Has_Many_Relation) {
1947
-                foreach ($this->get_all_from_cache($relation_name) as $related_model_obj_in_cache) {
1948
-                    $fk_to_this = $related_model_obj_in_cache->get_model()->get_foreign_key_to(
1949
-                        $model->get_this_model_name()
1950
-                    );
1951
-                    $related_model_obj_in_cache->set($fk_to_this->get_name(), $this->ID());
1952
-                    if ($related_model_obj_in_cache->ID()) {
1953
-                        $related_model_obj_in_cache->save();
1954
-                    }
1955
-                }
1956
-            }
1957
-        }
1958
-    }
1959
-
1960
-
1961
-    /**
1962
-     * Saves this model object and its NEW cached relations to the database.
1963
-     * (Meaning, for now, IT DOES NOT WORK if the cached items already exist in the DB.
1964
-     * In order for that to work, we would need to mark model objects as dirty/clean...
1965
-     * because otherwise, there's a potential for infinite looping of saving
1966
-     * Saves the cached related model objects, and ensures the relation between them
1967
-     * and this object and properly setup
1968
-     *
1969
-     * @return int ID of new model object on save; 0 on failure+
1970
-     * @throws ReflectionException
1971
-     * @throws InvalidArgumentException
1972
-     * @throws InvalidInterfaceException
1973
-     * @throws InvalidDataTypeException
1974
-     * @throws EE_Error
1975
-     */
1976
-    public function save_new_cached_related_model_objs()
1977
-    {
1978
-        //make sure this has been saved
1979
-        if (! $this->ID()) {
1980
-            $id = $this->save();
1981
-        } else {
1982
-            $id = $this->ID();
1983
-        }
1984
-        //now save all the NEW cached model objects  (ie they don't exist in the DB)
1985
-        foreach ($this->get_model()->relation_settings() as $relationName => $relationObj) {
1986
-            if ($this->_model_relations[ $relationName ]) {
1987
-                //is this a relation where we should expect just ONE related object (ie, EE_Belongs_To_relation)
1988
-                //or MANY related objects (ie, EE_HABTM_Relation or EE_Has_Many_Relation)?
1989
-                /* @var $related_model_obj EE_Base_Class */
1990
-                if ($relationObj instanceof EE_Belongs_To_Relation) {
1991
-                    //add a relation to that relation type (which saves the appropriate thing in the process)
1992
-                    //but ONLY if it DOES NOT exist in the DB
1993
-                    $related_model_obj = $this->_model_relations[ $relationName ];
1994
-                    //					if( ! $related_model_obj->ID()){
1995
-                    $this->_add_relation_to($related_model_obj, $relationName);
1996
-                    $related_model_obj->save_new_cached_related_model_objs();
1997
-                    //					}
1998
-                } else {
1999
-                    foreach ($this->_model_relations[ $relationName ] as $related_model_obj) {
2000
-                        //add a relation to that relation type (which saves the appropriate thing in the process)
2001
-                        //but ONLY if it DOES NOT exist in the DB
2002
-                        //						if( ! $related_model_obj->ID()){
2003
-                        $this->_add_relation_to($related_model_obj, $relationName);
2004
-                        $related_model_obj->save_new_cached_related_model_objs();
2005
-                        //						}
2006
-                    }
2007
-                }
2008
-            }
2009
-        }
2010
-        return $id;
2011
-    }
2012
-
2013
-
2014
-    /**
2015
-     * for getting a model while instantiated.
2016
-     *
2017
-     * @return EEM_Base | EEM_CPT_Base
2018
-     * @throws ReflectionException
2019
-     * @throws InvalidArgumentException
2020
-     * @throws InvalidInterfaceException
2021
-     * @throws InvalidDataTypeException
2022
-     * @throws EE_Error
2023
-     */
2024
-    public function get_model()
2025
-    {
2026
-        if (! $this->_model) {
2027
-            $modelName    = self::_get_model_classname(get_class($this));
2028
-            $this->_model = self::_get_model_instance_with_name($modelName, $this->_timezone);
2029
-        } else {
2030
-            $this->_model->set_timezone($this->_timezone);
2031
-        }
2032
-        return $this->_model;
2033
-    }
2034
-
2035
-
2036
-    /**
2037
-     * @param $props_n_values
2038
-     * @param $classname
2039
-     * @return mixed bool|EE_Base_Class|EEM_CPT_Base
2040
-     * @throws ReflectionException
2041
-     * @throws InvalidArgumentException
2042
-     * @throws InvalidInterfaceException
2043
-     * @throws InvalidDataTypeException
2044
-     * @throws EE_Error
2045
-     */
2046
-    protected static function _get_object_from_entity_mapper($props_n_values, $classname)
2047
-    {
2048
-        //TODO: will not work for Term_Relationships because they have no PK!
2049
-        $primary_id_ref = self::_get_primary_key_name($classname);
2050
-        if (
2051
-            array_key_exists($primary_id_ref, $props_n_values)
2052
-            && ! empty($props_n_values[ $primary_id_ref ])
2053
-        ) {
2054
-            $id = $props_n_values[ $primary_id_ref ];
2055
-            return self::_get_model($classname)->get_from_entity_map($id);
2056
-        }
2057
-        return false;
2058
-    }
2059
-
2060
-
2061
-    /**
2062
-     * This is called by child static "new_instance" method and we'll check to see if there is an existing db entry for
2063
-     * the primary key (if present in incoming values). If there is a key in the incoming array that matches the
2064
-     * primary key for the model AND it is not null, then we check the db. If there's a an object we return it.  If not
2065
-     * we return false.
2066
-     *
2067
-     * @param  array  $props_n_values   incoming array of properties and their values
2068
-     * @param  string $classname        the classname of the child class
2069
-     * @param null    $timezone
2070
-     * @param array   $date_formats     incoming date_formats in an array where the first value is the
2071
-     *                                  date_format and the second value is the time format
2072
-     * @return mixed (EE_Base_Class|bool)
2073
-     * @throws InvalidArgumentException
2074
-     * @throws InvalidInterfaceException
2075
-     * @throws InvalidDataTypeException
2076
-     * @throws EE_Error
2077
-     * @throws ReflectionException
2078
-     * @throws ReflectionException
2079
-     * @throws ReflectionException
2080
-     */
2081
-    protected static function _check_for_object($props_n_values, $classname, $timezone = null, $date_formats = array())
2082
-    {
2083
-        $existing = null;
2084
-        $model    = self::_get_model($classname, $timezone);
2085
-        if ($model->has_primary_key_field()) {
2086
-            $primary_id_ref = self::_get_primary_key_name($classname);
2087
-            if (array_key_exists($primary_id_ref, $props_n_values)
2088
-                && ! empty($props_n_values[ $primary_id_ref ])
2089
-            ) {
2090
-                $existing = $model->get_one_by_ID(
2091
-                    $props_n_values[ $primary_id_ref ]
2092
-                );
2093
-            }
2094
-        } elseif ($model->has_all_combined_primary_key_fields($props_n_values)) {
2095
-            //no primary key on this model, but there's still a matching item in the DB
2096
-            $existing = self::_get_model($classname, $timezone)->get_one_by_ID(
2097
-                self::_get_model($classname, $timezone)
2098
-                    ->get_index_primary_key_string($props_n_values)
2099
-            );
2100
-        }
2101
-        if ($existing) {
2102
-            //set date formats if present before setting values
2103
-            if (! empty($date_formats) && is_array($date_formats)) {
2104
-                $existing->set_date_format($date_formats[0]);
2105
-                $existing->set_time_format($date_formats[1]);
2106
-            } else {
2107
-                //set default formats for date and time
2108
-                $existing->set_date_format(get_option('date_format'));
2109
-                $existing->set_time_format(get_option('time_format'));
2110
-            }
2111
-            foreach ($props_n_values as $property => $field_value) {
2112
-                $existing->set($property, $field_value);
2113
-            }
2114
-            return $existing;
2115
-        }
2116
-        return false;
2117
-    }
2118
-
2119
-
2120
-    /**
2121
-     * Gets the EEM_*_Model for this class
2122
-     *
2123
-     * @access public now, as this is more convenient
2124
-     * @param      $classname
2125
-     * @param null $timezone
2126
-     * @throws ReflectionException
2127
-     * @throws InvalidArgumentException
2128
-     * @throws InvalidInterfaceException
2129
-     * @throws InvalidDataTypeException
2130
-     * @throws EE_Error
2131
-     * @return EEM_Base
2132
-     */
2133
-    protected static function _get_model($classname, $timezone = null)
2134
-    {
2135
-        //find model for this class
2136
-        if (! $classname) {
2137
-            throw new EE_Error(
2138
-                sprintf(
2139
-                    esc_html__(
2140
-                        'What were you thinking calling _get_model(%s)?? You need to specify the class name',
2141
-                        'event_espresso'
2142
-                    ),
2143
-                    $classname
2144
-                )
2145
-            );
2146
-        }
2147
-        $modelName = self::_get_model_classname($classname);
2148
-        return self::_get_model_instance_with_name($modelName, $timezone);
2149
-    }
2150
-
2151
-
2152
-    /**
2153
-     * Gets the model instance (eg instance of EEM_Attendee) given its classname (eg EE_Attendee)
2154
-     *
2155
-     * @param string $model_classname
2156
-     * @param null   $timezone
2157
-     * @return EEM_Base
2158
-     * @throws ReflectionException
2159
-     * @throws InvalidArgumentException
2160
-     * @throws InvalidInterfaceException
2161
-     * @throws InvalidDataTypeException
2162
-     * @throws EE_Error
2163
-     */
2164
-    protected static function _get_model_instance_with_name($model_classname, $timezone = null)
2165
-    {
2166
-        $model_classname = str_replace('EEM_', '', $model_classname);
2167
-        $model           = EE_Registry::instance()->load_model($model_classname);
2168
-        $model->set_timezone($timezone);
2169
-        return $model;
2170
-    }
2171
-
2172
-
2173
-    /**
2174
-     * If a model name is provided (eg Registration), gets the model classname for that model.
2175
-     * Also works if a model class's classname is provided (eg EE_Registration).
2176
-     *
2177
-     * @param null $model_name
2178
-     * @return string like EEM_Attendee
2179
-     */
2180
-    private static function _get_model_classname($model_name = null)
2181
-    {
2182
-        if (strpos($model_name, 'EE_') === 0) {
2183
-            $model_classname = str_replace('EE_', 'EEM_', $model_name);
2184
-        } else {
2185
-            $model_classname = 'EEM_' . $model_name;
2186
-        }
2187
-        return $model_classname;
2188
-    }
2189
-
2190
-
2191
-    /**
2192
-     * returns the name of the primary key attribute
2193
-     *
2194
-     * @param null $classname
2195
-     * @throws ReflectionException
2196
-     * @throws InvalidArgumentException
2197
-     * @throws InvalidInterfaceException
2198
-     * @throws InvalidDataTypeException
2199
-     * @throws EE_Error
2200
-     * @return string
2201
-     */
2202
-    protected static function _get_primary_key_name($classname = null)
2203
-    {
2204
-        if (! $classname) {
2205
-            throw new EE_Error(
2206
-                sprintf(
2207
-                    esc_html__('What were you thinking calling _get_primary_key_name(%s)', 'event_espresso'),
2208
-                    $classname
2209
-                )
2210
-            );
2211
-        }
2212
-        return self::_get_model($classname)->get_primary_key_field()->get_name();
2213
-    }
2214
-
2215
-
2216
-    /**
2217
-     * Gets the value of the primary key.
2218
-     * If the object hasn't yet been saved, it should be whatever the model field's default was
2219
-     * (eg, if this were the EE_Event class, look at the primary key field on EEM_Event and see what its default value
2220
-     * is. Usually defaults for integer primary keys are 0; string primary keys are usually NULL).
2221
-     *
2222
-     * @return mixed, if the primary key is of type INT it'll be an int. Otherwise it could be a string
2223
-     * @throws ReflectionException
2224
-     * @throws InvalidArgumentException
2225
-     * @throws InvalidInterfaceException
2226
-     * @throws InvalidDataTypeException
2227
-     * @throws EE_Error
2228
-     */
2229
-    public function ID()
2230
-    {
2231
-        $model = $this->get_model();
2232
-        //now that we know the name of the variable, use a variable variable to get its value and return its
2233
-        if ($model->has_primary_key_field()) {
2234
-            return $this->_fields[ $model->primary_key_name() ];
2235
-        }
2236
-        return $model->get_index_primary_key_string($this->_fields);
2237
-    }
2238
-
2239
-
2240
-    /**
2241
-     * Adds a relationship to the specified EE_Base_Class object, given the relationship's name. Eg, if the current
2242
-     * model is related to a group of events, the $relationName should be 'Event', and should be a key in the EE
2243
-     * Model's $_model_relations array. If this model object doesn't exist in the DB, just caches the related thing
2244
-     *
2245
-     * @param mixed  $otherObjectModelObjectOrID       EE_Base_Class or the ID of the other object
2246
-     * @param string $relationName                     eg 'Events','Question',etc.
2247
-     *                                                 an attendee to a group, you also want to specify which role they
2248
-     *                                                 will have in that group. So you would use this parameter to
2249
-     *                                                 specify array('role-column-name'=>'role-id')
2250
-     * @param array  $extra_join_model_fields_n_values You can optionally include an array of key=>value pairs that
2251
-     *                                                 allow you to further constrict the relation to being added.
2252
-     *                                                 However, keep in mind that the columns (keys) given must match a
2253
-     *                                                 column on the JOIN table and currently only the HABTM models
2254
-     *                                                 accept these additional conditions.  Also remember that if an
2255
-     *                                                 exact match isn't found for these extra cols/val pairs, then a
2256
-     *                                                 NEW row is created in the join table.
2257
-     * @param null   $cache_id
2258
-     * @throws ReflectionException
2259
-     * @throws InvalidArgumentException
2260
-     * @throws InvalidInterfaceException
2261
-     * @throws InvalidDataTypeException
2262
-     * @throws EE_Error
2263
-     * @return EE_Base_Class the object the relation was added to
2264
-     */
2265
-    public function _add_relation_to(
2266
-        $otherObjectModelObjectOrID,
2267
-        $relationName,
2268
-        $extra_join_model_fields_n_values = array(),
2269
-        $cache_id = null
2270
-    ) {
2271
-        $model = $this->get_model();
2272
-        //if this thing exists in the DB, save the relation to the DB
2273
-        if ($this->ID()) {
2274
-            $otherObject = $model->add_relationship_to(
2275
-                $this,
2276
-                $otherObjectModelObjectOrID,
2277
-                $relationName,
2278
-                $extra_join_model_fields_n_values
2279
-            );
2280
-            //clear cache so future get_many_related and get_first_related() return new results.
2281
-            $this->clear_cache($relationName, $otherObject, true);
2282
-            if ($otherObject instanceof EE_Base_Class) {
2283
-                $otherObject->clear_cache($model->get_this_model_name(), $this);
2284
-            }
2285
-        } else {
2286
-            //this thing doesn't exist in the DB,  so just cache it
2287
-            if (! $otherObjectModelObjectOrID instanceof EE_Base_Class) {
2288
-                throw new EE_Error(
2289
-                    sprintf(
2290
-                        esc_html__(
2291
-                            'Before a model object is saved to the database, calls to _add_relation_to must be passed an actual object, not just an ID. You provided %s as the model object to a %s',
2292
-                            'event_espresso'
2293
-                        ),
2294
-                        $otherObjectModelObjectOrID,
2295
-                        get_class($this)
2296
-                    )
2297
-                );
2298
-            }
2299
-            $otherObject = $otherObjectModelObjectOrID;
2300
-            $this->cache($relationName, $otherObjectModelObjectOrID, $cache_id);
2301
-        }
2302
-        if ($otherObject instanceof EE_Base_Class) {
2303
-            //fix the reciprocal relation too
2304
-            if ($otherObject->ID()) {
2305
-                //its saved so assumed relations exist in the DB, so we can just
2306
-                //clear the cache so future queries use the updated info in the DB
2307
-                $otherObject->clear_cache(
2308
-                    $model->get_this_model_name(),
2309
-                    null,
2310
-                    true
2311
-                );
2312
-            } else {
2313
-                //it's not saved, so it caches relations like this
2314
-                $otherObject->cache($model->get_this_model_name(), $this);
2315
-            }
2316
-        }
2317
-        return $otherObject;
2318
-    }
2319
-
2320
-
2321
-    /**
2322
-     * Removes a relationship to the specified EE_Base_Class object, given the relationships' name. Eg, if the current
2323
-     * model is related to a group of events, the $relationName should be 'Events', and should be a key in the EE
2324
-     * Model's $_model_relations array. If this model object doesn't exist in the DB, just removes the related thing
2325
-     * from the cache
2326
-     *
2327
-     * @param mixed  $otherObjectModelObjectOrID
2328
-     *                EE_Base_Class or the ID of the other object, OR an array key into the cache if this isn't saved
2329
-     *                to the DB yet
2330
-     * @param string $relationName
2331
-     * @param array  $where_query
2332
-     *                You can optionally include an array of key=>value pairs that allow you to further constrict the
2333
-     *                relation to being added. However, keep in mind that the columns (keys) given must match a column
2334
-     *                on the JOIN table and currently only the HABTM models accept these additional conditions. Also
2335
-     *                remember that if an exact match isn't found for these extra cols/val pairs, then a NEW row is
2336
-     *                created in the join table.
2337
-     * @return EE_Base_Class the relation was removed from
2338
-     * @throws ReflectionException
2339
-     * @throws InvalidArgumentException
2340
-     * @throws InvalidInterfaceException
2341
-     * @throws InvalidDataTypeException
2342
-     * @throws EE_Error
2343
-     */
2344
-    public function _remove_relation_to($otherObjectModelObjectOrID, $relationName, $where_query = array())
2345
-    {
2346
-        if ($this->ID()) {
2347
-            //if this exists in the DB, save the relation change to the DB too
2348
-            $otherObject = $this->get_model()->remove_relationship_to(
2349
-                $this,
2350
-                $otherObjectModelObjectOrID,
2351
-                $relationName,
2352
-                $where_query
2353
-            );
2354
-            $this->clear_cache(
2355
-                $relationName,
2356
-                $otherObject
2357
-            );
2358
-        } else {
2359
-            //this doesn't exist in the DB, just remove it from the cache
2360
-            $otherObject = $this->clear_cache(
2361
-                $relationName,
2362
-                $otherObjectModelObjectOrID
2363
-            );
2364
-        }
2365
-        if ($otherObject instanceof EE_Base_Class) {
2366
-            $otherObject->clear_cache(
2367
-                $this->get_model()->get_this_model_name(),
2368
-                $this
2369
-            );
2370
-        }
2371
-        return $otherObject;
2372
-    }
2373
-
2374
-
2375
-    /**
2376
-     * Removes ALL the related things for the $relationName.
2377
-     *
2378
-     * @param string $relationName
2379
-     * @param array  $where_query_params like EEM_Base::get_all's $query_params[0] (where conditions)
2380
-     * @return EE_Base_Class
2381
-     * @throws ReflectionException
2382
-     * @throws InvalidArgumentException
2383
-     * @throws InvalidInterfaceException
2384
-     * @throws InvalidDataTypeException
2385
-     * @throws EE_Error
2386
-     */
2387
-    public function _remove_relations($relationName, $where_query_params = array())
2388
-    {
2389
-        if ($this->ID()) {
2390
-            //if this exists in the DB, save the relation change to the DB too
2391
-            $otherObjects = $this->get_model()->remove_relations(
2392
-                $this,
2393
-                $relationName,
2394
-                $where_query_params
2395
-            );
2396
-            $this->clear_cache(
2397
-                $relationName,
2398
-                null,
2399
-                true
2400
-            );
2401
-        } else {
2402
-            //this doesn't exist in the DB, just remove it from the cache
2403
-            $otherObjects = $this->clear_cache(
2404
-                $relationName,
2405
-                null,
2406
-                true
2407
-            );
2408
-        }
2409
-        if (is_array($otherObjects)) {
2410
-            foreach ($otherObjects as $otherObject) {
2411
-                $otherObject->clear_cache(
2412
-                    $this->get_model()->get_this_model_name(),
2413
-                    $this
2414
-                );
2415
-            }
2416
-        }
2417
-        return $otherObjects;
2418
-    }
2419
-
2420
-
2421
-    /**
2422
-     * Gets all the related model objects of the specified type. Eg, if the current class if
2423
-     * EE_Event, you could call $this->get_many_related('Registration') to get an array of all the
2424
-     * EE_Registration objects which related to this event. Note: by default, we remove the "default query params"
2425
-     * because we want to get even deleted items etc.
2426
-     *
2427
-     * @param string $relationName key in the model's _model_relations array
2428
-     * @param array  $query_params like EEM_Base::get_all
2429
-     * @return EE_Base_Class[]     Results not necessarily indexed by IDs, because some results might not have primary
2430
-     *                             keys or might not be saved yet. Consider using EEM_Base::get_IDs() on these
2431
-     *                             results if you want IDs
2432
-     * @throws ReflectionException
2433
-     * @throws InvalidArgumentException
2434
-     * @throws InvalidInterfaceException
2435
-     * @throws InvalidDataTypeException
2436
-     * @throws EE_Error
2437
-     */
2438
-    public function get_many_related($relationName, $query_params = array())
2439
-    {
2440
-        if ($this->ID()) {
2441
-            //this exists in the DB, so get the related things from either the cache or the DB
2442
-            //if there are query parameters, forget about caching the related model objects.
2443
-            if ($query_params) {
2444
-                $related_model_objects = $this->get_model()->get_all_related(
2445
-                    $this,
2446
-                    $relationName,
2447
-                    $query_params
2448
-                );
2449
-            } else {
2450
-                //did we already cache the result of this query?
2451
-                $cached_results = $this->get_all_from_cache($relationName);
2452
-                if (! $cached_results) {
2453
-                    $related_model_objects = $this->get_model()->get_all_related(
2454
-                        $this,
2455
-                        $relationName,
2456
-                        $query_params
2457
-                    );
2458
-                    //if no query parameters were passed, then we got all the related model objects
2459
-                    //for that relation. We can cache them then.
2460
-                    foreach ($related_model_objects as $related_model_object) {
2461
-                        $this->cache($relationName, $related_model_object);
2462
-                    }
2463
-                } else {
2464
-                    $related_model_objects = $cached_results;
2465
-                }
2466
-            }
2467
-        } else {
2468
-            //this doesn't exist in the DB, so just get the related things from the cache
2469
-            $related_model_objects = $this->get_all_from_cache($relationName);
2470
-        }
2471
-        return $related_model_objects;
2472
-    }
2473
-
2474
-
2475
-    /**
2476
-     * Instead of getting the related model objects, simply counts them. Ignores default_where_conditions by default,
2477
-     * unless otherwise specified in the $query_params
2478
-     *
2479
-     * @param string $relation_name  model_name like 'Event', or 'Registration'
2480
-     * @param array  $query_params   like EEM_Base::get_all's
2481
-     * @param string $field_to_count name of field to count by. By default, uses primary key
2482
-     * @param bool   $distinct       if we want to only count the distinct values for the column then you can trigger
2483
-     *                               that by the setting $distinct to TRUE;
2484
-     * @return int
2485
-     * @throws ReflectionException
2486
-     * @throws InvalidArgumentException
2487
-     * @throws InvalidInterfaceException
2488
-     * @throws InvalidDataTypeException
2489
-     * @throws EE_Error
2490
-     */
2491
-    public function count_related($relation_name, $query_params = array(), $field_to_count = null, $distinct = false)
2492
-    {
2493
-        return $this->get_model()->count_related(
2494
-            $this,
2495
-            $relation_name,
2496
-            $query_params,
2497
-            $field_to_count,
2498
-            $distinct
2499
-        );
2500
-    }
2501
-
2502
-
2503
-    /**
2504
-     * Instead of getting the related model objects, simply sums up the values of the specified field.
2505
-     * Note: ignores default_where_conditions by default, unless otherwise specified in the $query_params
2506
-     *
2507
-     * @param string $relation_name model_name like 'Event', or 'Registration'
2508
-     * @param array  $query_params  like EEM_Base::get_all's
2509
-     * @param string $field_to_sum  name of field to count by.
2510
-     *                              By default, uses primary key
2511
-     *                              (which doesn't make much sense, so you should probably change it)
2512
-     * @return int
2513
-     * @throws ReflectionException
2514
-     * @throws InvalidArgumentException
2515
-     * @throws InvalidInterfaceException
2516
-     * @throws InvalidDataTypeException
2517
-     * @throws EE_Error
2518
-     */
2519
-    public function sum_related($relation_name, $query_params = array(), $field_to_sum = null)
2520
-    {
2521
-        return $this->get_model()->sum_related(
2522
-            $this,
2523
-            $relation_name,
2524
-            $query_params,
2525
-            $field_to_sum
2526
-        );
2527
-    }
2528
-
2529
-
2530
-    /**
2531
-     * Gets the first (ie, one) related model object of the specified type.
2532
-     *
2533
-     * @param string $relationName key in the model's _model_relations array
2534
-     * @param array  $query_params like EEM_Base::get_all
2535
-     * @return EE_Base_Class (not an array, a single object)
2536
-     * @throws ReflectionException
2537
-     * @throws InvalidArgumentException
2538
-     * @throws InvalidInterfaceException
2539
-     * @throws InvalidDataTypeException
2540
-     * @throws EE_Error
2541
-     */
2542
-    public function get_first_related($relationName, $query_params = array())
2543
-    {
2544
-        $model = $this->get_model();
2545
-        if ($this->ID()) {//this exists in the DB, get from the cache OR the DB
2546
-            //if they've provided some query parameters, don't bother trying to cache the result
2547
-            //also make sure we're not caching the result of get_first_related
2548
-            //on a relation which should have an array of objects (because the cache might have an array of objects)
2549
-            if ($query_params
2550
-                || ! $model->related_settings_for($relationName)
2551
-                     instanceof
2552
-                     EE_Belongs_To_Relation
2553
-            ) {
2554
-                $related_model_object = $model->get_first_related(
2555
-                    $this,
2556
-                    $relationName,
2557
-                    $query_params
2558
-                );
2559
-            } else {
2560
-                //first, check if we've already cached the result of this query
2561
-                $cached_result = $this->get_one_from_cache($relationName);
2562
-                if (! $cached_result) {
2563
-                    $related_model_object = $model->get_first_related(
2564
-                        $this,
2565
-                        $relationName,
2566
-                        $query_params
2567
-                    );
2568
-                    $this->cache($relationName, $related_model_object);
2569
-                } else {
2570
-                    $related_model_object = $cached_result;
2571
-                }
2572
-            }
2573
-        } else {
2574
-            $related_model_object = null;
2575
-            // this doesn't exist in the Db,
2576
-            // but maybe the relation is of type belongs to, and so the related thing might
2577
-            if ($model->related_settings_for($relationName) instanceof EE_Belongs_To_Relation) {
2578
-                $related_model_object = $model->get_first_related(
2579
-                    $this,
2580
-                    $relationName,
2581
-                    $query_params
2582
-                );
2583
-            }
2584
-            // this doesn't exist in the DB and apparently the thing it belongs to doesn't either,
2585
-            // just get what's cached on this object
2586
-            if (! $related_model_object) {
2587
-                $related_model_object = $this->get_one_from_cache($relationName);
2588
-            }
2589
-        }
2590
-        return $related_model_object;
2591
-    }
2592
-
2593
-
2594
-    /**
2595
-     * Does a delete on all related objects of type $relationName and removes
2596
-     * the current model object's relation to them. If they can't be deleted (because
2597
-     * of blocking related model objects) does nothing. If the related model objects are
2598
-     * soft-deletable, they will be soft-deleted regardless of related blocking model objects.
2599
-     * If this model object doesn't exist yet in the DB, just removes its related things
2600
-     *
2601
-     * @param string $relationName
2602
-     * @param array  $query_params like EEM_Base::get_all's
2603
-     * @return int how many deleted
2604
-     * @throws ReflectionException
2605
-     * @throws InvalidArgumentException
2606
-     * @throws InvalidInterfaceException
2607
-     * @throws InvalidDataTypeException
2608
-     * @throws EE_Error
2609
-     */
2610
-    public function delete_related($relationName, $query_params = array())
2611
-    {
2612
-        if ($this->ID()) {
2613
-            $count = $this->get_model()->delete_related(
2614
-                $this,
2615
-                $relationName,
2616
-                $query_params
2617
-            );
2618
-        } else {
2619
-            $count = count($this->get_all_from_cache($relationName));
2620
-            $this->clear_cache($relationName, null, true);
2621
-        }
2622
-        return $count;
2623
-    }
2624
-
2625
-
2626
-    /**
2627
-     * Does a hard delete (ie, removes the DB row) on all related objects of type $relationName and removes
2628
-     * the current model object's relation to them. If they can't be deleted (because
2629
-     * of blocking related model objects) just does a soft delete on it instead, if possible.
2630
-     * If the related thing isn't a soft-deletable model object, this function is identical
2631
-     * to delete_related(). If this model object doesn't exist in the DB, just remove its related things
2632
-     *
2633
-     * @param string $relationName
2634
-     * @param array  $query_params like EEM_Base::get_all's
2635
-     * @return int how many deleted (including those soft deleted)
2636
-     * @throws ReflectionException
2637
-     * @throws InvalidArgumentException
2638
-     * @throws InvalidInterfaceException
2639
-     * @throws InvalidDataTypeException
2640
-     * @throws EE_Error
2641
-     */
2642
-    public function delete_related_permanently($relationName, $query_params = array())
2643
-    {
2644
-        if ($this->ID()) {
2645
-            $count = $this->get_model()->delete_related_permanently(
2646
-                $this,
2647
-                $relationName,
2648
-                $query_params
2649
-            );
2650
-        } else {
2651
-            $count = count($this->get_all_from_cache($relationName));
2652
-        }
2653
-        $this->clear_cache($relationName, null, true);
2654
-        return $count;
2655
-    }
2656
-
2657
-
2658
-    /**
2659
-     * is_set
2660
-     * Just a simple utility function children can use for checking if property exists
2661
-     *
2662
-     * @access  public
2663
-     * @param  string $field_name property to check
2664
-     * @return bool                              TRUE if existing,FALSE if not.
2665
-     */
2666
-    public function is_set($field_name)
2667
-    {
2668
-        return isset($this->_fields[ $field_name ]);
2669
-    }
2670
-
2671
-
2672
-    /**
2673
-     * Just a simple utility function children can use for checking if property (or properties) exists and throwing an
2674
-     * EE_Error exception if they don't
2675
-     *
2676
-     * @param  mixed (string|array) $properties properties to check
2677
-     * @throws EE_Error
2678
-     * @return bool                              TRUE if existing, throw EE_Error if not.
2679
-     */
2680
-    protected function _property_exists($properties)
2681
-    {
2682
-        foreach ((array) $properties as $property_name) {
2683
-            //first make sure this property exists
2684
-            if (! $this->_fields[ $property_name ]) {
2685
-                throw new EE_Error(
2686
-                    sprintf(
2687
-                        esc_html__(
2688
-                            'Trying to retrieve a non-existent property (%s).  Double check the spelling please',
2689
-                            'event_espresso'
2690
-                        ),
2691
-                        $property_name
2692
-                    )
2693
-                );
2694
-            }
2695
-        }
2696
-        return true;
2697
-    }
2698
-
2699
-
2700
-    /**
2701
-     * This simply returns an array of model fields for this object
2702
-     *
2703
-     * @return array
2704
-     * @throws ReflectionException
2705
-     * @throws InvalidArgumentException
2706
-     * @throws InvalidInterfaceException
2707
-     * @throws InvalidDataTypeException
2708
-     * @throws EE_Error
2709
-     */
2710
-    public function model_field_array()
2711
-    {
2712
-        $fields     = $this->get_model()->field_settings(false);
2713
-        $properties = array();
2714
-        //remove prepended underscore
2715
-        foreach ($fields as $field_name => $settings) {
2716
-            $properties[ $field_name ] = $this->get($field_name);
2717
-        }
2718
-        return $properties;
2719
-    }
2720
-
2721
-
2722
-    /**
2723
-     * Very handy general function to allow for plugins to extend any child of EE_Base_Class.
2724
-     * If a method is called on a child of EE_Base_Class that doesn't exist, this function is called
2725
-     * (http://www.garfieldtech.com/blog/php-magic-call) and passed the method's name and arguments.
2726
-     * Instead of requiring a plugin to extend the EE_Base_Class
2727
-     * (which works fine is there's only 1 plugin, but when will that happen?)
2728
-     * they can add a hook onto 'filters_hook_espresso__{className}__{methodName}'
2729
-     * (eg, filters_hook_espresso__EE_Answer__my_great_function)
2730
-     * and accepts 2 arguments: the object on which the function was called,
2731
-     * and an array of the original arguments passed to the function.
2732
-     * Whatever their callback function returns will be returned by this function.
2733
-     * Example: in functions.php (or in a plugin):
2734
-     *      add_filter('FHEE__EE_Answer__my_callback','my_callback',10,3);
2735
-     *      function my_callback($previousReturnValue,EE_Base_Class $object,$argsArray){
2736
-     *          $returnString= "you called my_callback! and passed args:".implode(",",$argsArray);
2737
-     *          return $previousReturnValue.$returnString;
2738
-     *      }
2739
-     * require('EE_Answer.class.php');
2740
-     * $answer= EE_Answer::new_instance(array('REG_ID' => 2,'QST_ID' => 3,'ANS_value' => The answer is 42'));
2741
-     * echo $answer->my_callback('monkeys',100);
2742
-     * //will output "you called my_callback! and passed args:monkeys,100"
2743
-     *
2744
-     * @param string $methodName name of method which was called on a child of EE_Base_Class, but which
2745
-     * @param array  $args       array of original arguments passed to the function
2746
-     * @throws EE_Error
2747
-     * @return mixed whatever the plugin which calls add_filter decides
2748
-     */
2749
-    public function __call($methodName, $args)
2750
-    {
2751
-        $className = get_class($this);
2752
-        $tagName   = "FHEE__{$className}__{$methodName}";
2753
-        if (! has_filter($tagName)) {
2754
-            throw new EE_Error(
2755
-                sprintf(
2756
-                    esc_html__(
2757
-                        "Method %s on class %s does not exist! You can create one with the following code in functions.php or in a plugin: add_filter('%s','my_callback',10,3);function my_callback(\$previousReturnValue,EE_Base_Class \$object, \$argsArray){/*function body*/return \$whatever;}",
2758
-                        'event_espresso'
2759
-                    ),
2760
-                    $methodName,
2761
-                    $className,
2762
-                    $tagName
2763
-                )
2764
-            );
2765
-        }
2766
-        return apply_filters($tagName, null, $this, $args);
2767
-    }
2768
-
2769
-
2770
-    /**
2771
-     * Similar to insert_post_meta, adds a record in the Extra_Meta model's table with the given key and value.
2772
-     * A $previous_value can be specified in case there are many meta rows with the same key
2773
-     *
2774
-     * @param string $meta_key
2775
-     * @param mixed  $meta_value
2776
-     * @param mixed  $previous_value
2777
-     * @return bool|int # of records updated (or BOOLEAN if we actually ended up inserting the extra meta row)
2778
-     *                  NOTE: if the values haven't changed, returns 0
2779
-     * @throws InvalidArgumentException
2780
-     * @throws InvalidInterfaceException
2781
-     * @throws InvalidDataTypeException
2782
-     * @throws EE_Error
2783
-     * @throws ReflectionException
2784
-     */
2785
-    public function update_extra_meta($meta_key, $meta_value, $previous_value = null)
2786
-    {
2787
-        $query_params = array(
2788
-            array(
2789
-                'EXM_key'  => $meta_key,
2790
-                'OBJ_ID'   => $this->ID(),
2791
-                'EXM_type' => $this->get_model()->get_this_model_name(),
2792
-            ),
2793
-        );
2794
-        if ($previous_value !== null) {
2795
-            $query_params[0]['EXM_value'] = $meta_value;
2796
-        }
2797
-        $existing_rows_like_that = EEM_Extra_Meta::instance()->get_all($query_params);
2798
-        if (! $existing_rows_like_that) {
2799
-            return $this->add_extra_meta($meta_key, $meta_value);
2800
-        }
2801
-        foreach ($existing_rows_like_that as $existing_row) {
2802
-            $existing_row->save(array('EXM_value' => $meta_value));
2803
-        }
2804
-        return count($existing_rows_like_that);
2805
-    }
2806
-
2807
-
2808
-    /**
2809
-     * Adds a new extra meta record. If $unique is set to TRUE, we'll first double-check
2810
-     * no other extra meta for this model object have the same key. Returns TRUE if the
2811
-     * extra meta row was entered, false if not
2812
-     *
2813
-     * @param string  $meta_key
2814
-     * @param mixed   $meta_value
2815
-     * @param boolean $unique
2816
-     * @return boolean
2817
-     * @throws InvalidArgumentException
2818
-     * @throws InvalidInterfaceException
2819
-     * @throws InvalidDataTypeException
2820
-     * @throws EE_Error
2821
-     * @throws ReflectionException
2822
-     * @throws ReflectionException
2823
-     */
2824
-    public function add_extra_meta($meta_key, $meta_value, $unique = false)
2825
-    {
2826
-        if ($unique) {
2827
-            $existing_extra_meta = EEM_Extra_Meta::instance()->get_one(
2828
-                array(
2829
-                    array(
2830
-                        'EXM_key'  => $meta_key,
2831
-                        'OBJ_ID'   => $this->ID(),
2832
-                        'EXM_type' => $this->get_model()->get_this_model_name(),
2833
-                    ),
2834
-                )
2835
-            );
2836
-            if ($existing_extra_meta) {
2837
-                return false;
2838
-            }
2839
-        }
2840
-        $new_extra_meta = EE_Extra_Meta::new_instance(
2841
-            array(
2842
-                'EXM_key'   => $meta_key,
2843
-                'EXM_value' => $meta_value,
2844
-                'OBJ_ID'    => $this->ID(),
2845
-                'EXM_type'  => $this->get_model()->get_this_model_name(),
2846
-            )
2847
-        );
2848
-        $new_extra_meta->save();
2849
-        return true;
2850
-    }
2851
-
2852
-
2853
-    /**
2854
-     * Deletes all the extra meta rows for this record as specified by key. If $meta_value
2855
-     * is specified, only deletes extra meta records with that value.
2856
-     *
2857
-     * @param string $meta_key
2858
-     * @param mixed  $meta_value
2859
-     * @return int number of extra meta rows deleted
2860
-     * @throws InvalidArgumentException
2861
-     * @throws InvalidInterfaceException
2862
-     * @throws InvalidDataTypeException
2863
-     * @throws EE_Error
2864
-     * @throws ReflectionException
2865
-     */
2866
-    public function delete_extra_meta($meta_key, $meta_value = null)
2867
-    {
2868
-        $query_params = array(
2869
-            array(
2870
-                'EXM_key'  => $meta_key,
2871
-                'OBJ_ID'   => $this->ID(),
2872
-                'EXM_type' => $this->get_model()->get_this_model_name(),
2873
-            ),
2874
-        );
2875
-        if ($meta_value !== null) {
2876
-            $query_params[0]['EXM_value'] = $meta_value;
2877
-        }
2878
-        return EEM_Extra_Meta::instance()->delete($query_params);
2879
-    }
2880
-
2881
-
2882
-    /**
2883
-     * Gets the extra meta with the given meta key. If you specify "single" we just return 1, otherwise
2884
-     * an array of everything found. Requires that this model actually have a relation of type EE_Has_Many_Any_Relation.
2885
-     * You can specify $default is case you haven't found the extra meta
2886
-     *
2887
-     * @param string  $meta_key
2888
-     * @param boolean $single
2889
-     * @param mixed   $default if we don't find anything, what should we return?
2890
-     * @return mixed single value if $single; array if ! $single
2891
-     * @throws ReflectionException
2892
-     * @throws InvalidArgumentException
2893
-     * @throws InvalidInterfaceException
2894
-     * @throws InvalidDataTypeException
2895
-     * @throws EE_Error
2896
-     */
2897
-    public function get_extra_meta($meta_key, $single = false, $default = null)
2898
-    {
2899
-        if ($single) {
2900
-            $result = $this->get_first_related(
2901
-                'Extra_Meta',
2902
-                array(array('EXM_key' => $meta_key))
2903
-            );
2904
-            if ($result instanceof EE_Extra_Meta) {
2905
-                return $result->value();
2906
-            }
2907
-        } else {
2908
-            $results = $this->get_many_related(
2909
-                'Extra_Meta',
2910
-                array(array('EXM_key' => $meta_key))
2911
-            );
2912
-            if ($results) {
2913
-                $values = array();
2914
-                foreach ($results as $result) {
2915
-                    if ($result instanceof EE_Extra_Meta) {
2916
-                        $values[ $result->ID() ] = $result->value();
2917
-                    }
2918
-                }
2919
-                return $values;
2920
-            }
2921
-        }
2922
-        //if nothing discovered yet return default.
2923
-        return apply_filters(
2924
-            'FHEE__EE_Base_Class__get_extra_meta__default_value',
2925
-            $default,
2926
-            $meta_key,
2927
-            $single,
2928
-            $this
2929
-        );
2930
-    }
2931
-
2932
-
2933
-    /**
2934
-     * Returns a simple array of all the extra meta associated with this model object.
2935
-     * If $one_of_each_key is true (Default), it will be an array of simple key-value pairs, keys being the
2936
-     * extra meta's key, and teh value being its value. However, if there are duplicate extra meta rows with
2937
-     * the same key, only one will be used. (eg array('foo'=>'bar','monkey'=>123))
2938
-     * If $one_of_each_key is false, it will return an array with the top-level keys being
2939
-     * the extra meta keys, but their values are also arrays, which have the extra-meta's ID as their sub-key, and
2940
-     * finally the extra meta's value as each sub-value. (eg
2941
-     * array('foo'=>array(1=>'bar',2=>'bill'),'monkey'=>array(3=>123)))
2942
-     *
2943
-     * @param boolean $one_of_each_key
2944
-     * @return array
2945
-     * @throws ReflectionException
2946
-     * @throws InvalidArgumentException
2947
-     * @throws InvalidInterfaceException
2948
-     * @throws InvalidDataTypeException
2949
-     * @throws EE_Error
2950
-     */
2951
-    public function all_extra_meta_array($one_of_each_key = true)
2952
-    {
2953
-        $return_array = array();
2954
-        if ($one_of_each_key) {
2955
-            $extra_meta_objs = $this->get_many_related(
2956
-                'Extra_Meta',
2957
-                array('group_by' => 'EXM_key')
2958
-            );
2959
-            foreach ($extra_meta_objs as $extra_meta_obj) {
2960
-                if ($extra_meta_obj instanceof EE_Extra_Meta) {
2961
-                    $return_array[ $extra_meta_obj->key() ] = $extra_meta_obj->value();
2962
-                }
2963
-            }
2964
-        } else {
2965
-            $extra_meta_objs = $this->get_many_related('Extra_Meta');
2966
-            foreach ($extra_meta_objs as $extra_meta_obj) {
2967
-                if ($extra_meta_obj instanceof EE_Extra_Meta) {
2968
-                    if (! isset($return_array[ $extra_meta_obj->key() ])) {
2969
-                        $return_array[ $extra_meta_obj->key() ] = array();
2970
-                    }
2971
-                    $return_array[ $extra_meta_obj->key() ][ $extra_meta_obj->ID() ] = $extra_meta_obj->value();
2972
-                }
2973
-            }
2974
-        }
2975
-        return $return_array;
2976
-    }
2977
-
2978
-
2979
-    /**
2980
-     * Gets a pretty nice displayable nice for this model object. Often overridden
2981
-     *
2982
-     * @return string
2983
-     * @throws ReflectionException
2984
-     * @throws InvalidArgumentException
2985
-     * @throws InvalidInterfaceException
2986
-     * @throws InvalidDataTypeException
2987
-     * @throws EE_Error
2988
-     */
2989
-    public function name()
2990
-    {
2991
-        //find a field that's not a text field
2992
-        $field_we_can_use = $this->get_model()->get_a_field_of_type('EE_Text_Field_Base');
2993
-        if ($field_we_can_use) {
2994
-            return $this->get($field_we_can_use->get_name());
2995
-        }
2996
-        $first_few_properties = $this->model_field_array();
2997
-        $first_few_properties = array_slice($first_few_properties, 0, 3);
2998
-        $name_parts           = array();
2999
-        foreach ($first_few_properties as $name => $value) {
3000
-            $name_parts[] = "$name:$value";
3001
-        }
3002
-        return implode(',', $name_parts);
3003
-    }
3004
-
3005
-
3006
-    /**
3007
-     * in_entity_map
3008
-     * Checks if this model object has been proven to already be in the entity map
3009
-     *
3010
-     * @return boolean
3011
-     * @throws ReflectionException
3012
-     * @throws InvalidArgumentException
3013
-     * @throws InvalidInterfaceException
3014
-     * @throws InvalidDataTypeException
3015
-     * @throws EE_Error
3016
-     */
3017
-    public function in_entity_map()
3018
-    {
3019
-        // well, if we looked, did we find it in the entity map?
3020
-        return $this->ID() && $this->get_model()->get_from_entity_map($this->ID()) === $this;
3021
-    }
3022
-
3023
-
3024
-    /**
3025
-     * refresh_from_db
3026
-     * Makes sure the fields and values on this model object are in-sync with what's in the database.
3027
-     *
3028
-     * @throws ReflectionException
3029
-     * @throws InvalidArgumentException
3030
-     * @throws InvalidInterfaceException
3031
-     * @throws InvalidDataTypeException
3032
-     * @throws EE_Error if this model object isn't in the entity mapper (because then you should
3033
-     * just use what's in the entity mapper and refresh it) and WP_DEBUG is TRUE
3034
-     */
3035
-    public function refresh_from_db()
3036
-    {
3037
-        if ($this->ID() && $this->in_entity_map()) {
3038
-            $this->get_model()->refresh_entity_map_from_db($this->ID());
3039
-        } else {
3040
-            //if it doesn't have ID, you shouldn't be asking to refresh it from teh database (because its not in the database)
3041
-            //if it has an ID but it's not in the map, and you're asking me to refresh it
3042
-            //that's kinda dangerous. You should just use what's in the entity map, or add this to the entity map if there's
3043
-            //absolutely nothing in it for this ID
3044
-            if (WP_DEBUG) {
3045
-                throw new EE_Error(
3046
-                    sprintf(
3047
-                        esc_html__('Trying to refresh a model object with ID "%1$s" that\'s not in the entity map? First off: you should put it in the entity map by calling %2$s. Second off, if you want what\'s in the database right now, you should just call %3$s yourself and discard this model object.',
3048
-                            'event_espresso'),
3049
-                        $this->ID(),
3050
-                        get_class($this->get_model()) . '::instance()->add_to_entity_map()',
3051
-                        get_class($this->get_model()) . '::instance()->refresh_entity_map()'
3052
-                    )
3053
-                );
3054
-            }
3055
-        }
3056
-    }
3057
-
3058
-
3059
-    /**
3060
-     * Because some other plugins, like Advanced Cron Manager, expect all objects to have this method
3061
-     * (probably a bad assumption they have made, oh well)
3062
-     *
3063
-     * @return string
3064
-     */
3065
-    public function __toString()
3066
-    {
3067
-        try {
3068
-            return sprintf('%s (%s)', $this->name(), $this->ID());
3069
-        } catch (Exception $e) {
3070
-            EE_Error::add_error($e->getMessage(), __FILE__, __FUNCTION__, __LINE__);
3071
-            return '';
3072
-        }
3073
-    }
3074
-
3075
-
3076
-    /**
3077
-     * Clear related model objects if they're already in the DB, because otherwise when we
3078
-     * UN-serialize this model object we'll need to be careful to add them to the entity map.
3079
-     * This means if we have made changes to those related model objects, and want to unserialize
3080
-     * the this model object on a subsequent request, changes to those related model objects will be lost.
3081
-     * Instead, those related model objects should be directly serialized and stored.
3082
-     * Eg, the following won't work:
3083
-     * $reg = EEM_Registration::instance()->get_one_by_ID( 123 );
3084
-     * $att = $reg->attendee();
3085
-     * $att->set( 'ATT_fname', 'Dirk' );
3086
-     * update_option( 'my_option', serialize( $reg ) );
3087
-     * //END REQUEST
3088
-     * //START NEXT REQUEST
3089
-     * $reg = get_option( 'my_option' );
3090
-     * $reg->attendee()->save();
3091
-     * And would need to be replace with:
3092
-     * $reg = EEM_Registration::instance()->get_one_by_ID( 123 );
3093
-     * $att = $reg->attendee();
3094
-     * $att->set( 'ATT_fname', 'Dirk' );
3095
-     * update_option( 'my_option', serialize( $reg ) );
3096
-     * //END REQUEST
3097
-     * //START NEXT REQUEST
3098
-     * $att = get_option( 'my_option' );
3099
-     * $att->save();
3100
-     *
3101
-     * @return array
3102
-     * @throws ReflectionException
3103
-     * @throws InvalidArgumentException
3104
-     * @throws InvalidInterfaceException
3105
-     * @throws InvalidDataTypeException
3106
-     * @throws EE_Error
3107
-     */
3108
-    public function __sleep()
3109
-    {
3110
-        $model = $this->get_model();
3111
-        foreach ($model->relation_settings() as $relation_name => $relation_obj) {
3112
-            if ($relation_obj instanceof EE_Belongs_To_Relation) {
3113
-                $classname = 'EE_' . $model->get_this_model_name();
3114
-                if (
3115
-                    $this->get_one_from_cache($relation_name) instanceof $classname
3116
-                    && $this->get_one_from_cache($relation_name)->ID()
3117
-                ) {
3118
-                    $this->clear_cache(
3119
-                        $relation_name,
3120
-                        $this->get_one_from_cache($relation_name)->ID()
3121
-                    );
3122
-                }
3123
-            }
3124
-        }
3125
-        $this->_props_n_values_provided_in_constructor = array();
3126
-        $properties_to_serialize                       = get_object_vars($this);
3127
-        //don't serialize the model. It's big and that risks recursion
3128
-        unset($properties_to_serialize['_model']);
3129
-        return array_keys($properties_to_serialize);
3130
-    }
3131
-
3132
-
3133
-    /**
3134
-     * restore _props_n_values_provided_in_constructor
3135
-     * PLZ NOTE: this will reset the array to whatever fields values were present prior to serialization,
3136
-     * and therefore should NOT be used to determine if state change has occurred since initial construction.
3137
-     * At best, you would only be able to detect if state change has occurred during THIS request.
3138
-     */
3139
-    public function __wakeup()
3140
-    {
3141
-        $this->_props_n_values_provided_in_constructor = $this->_fields;
3142
-    }
18
+	/**
19
+	 * This is an array of the original properties and values provided during construction
20
+	 * of this model object. (keys are model field names, values are their values).
21
+	 * This list is important to remember so that when we are merging data from the db, we know
22
+	 * which values to override and which to not override.
23
+	 *
24
+	 * @var array
25
+	 */
26
+	protected $_props_n_values_provided_in_constructor;
27
+
28
+	/**
29
+	 * Timezone
30
+	 * This gets set by the "set_timezone()" method so that we know what timezone incoming strings|timestamps are in.
31
+	 * This can also be used before a get to set what timezone you want strings coming out of the object to be in.  NOT
32
+	 * all EE_Base_Class child classes use this property but any that use a EE_Datetime_Field data type will have
33
+	 * access to it.
34
+	 *
35
+	 * @var string
36
+	 */
37
+	protected $_timezone;
38
+
39
+	/**
40
+	 * date format
41
+	 * pattern or format for displaying dates
42
+	 *
43
+	 * @var string $_dt_frmt
44
+	 */
45
+	protected $_dt_frmt;
46
+
47
+	/**
48
+	 * time format
49
+	 * pattern or format for displaying time
50
+	 *
51
+	 * @var string $_tm_frmt
52
+	 */
53
+	protected $_tm_frmt;
54
+
55
+	/**
56
+	 * This property is for holding a cached array of object properties indexed by property name as the key.
57
+	 * The purpose of this is for setting a cache on properties that may have calculated values after a
58
+	 * prepare_for_get.  That way the cache can be checked first and the calculated property returned instead of having
59
+	 * to recalculate. Used by _set_cached_property() and _get_cached_property() methods.
60
+	 *
61
+	 * @var array
62
+	 */
63
+	protected $_cached_properties = array();
64
+
65
+	/**
66
+	 * An array containing keys of the related model, and values are either an array of related mode objects or a
67
+	 * single
68
+	 * related model object. see the model's _model_relations. The keys should match those specified. And if the
69
+	 * relation is of type EE_Belongs_To (or one of its children), then there should only be ONE related model object,
70
+	 * all others have an array)
71
+	 *
72
+	 * @var array
73
+	 */
74
+	protected $_model_relations = array();
75
+
76
+	/**
77
+	 * Array where keys are field names (see the model's _fields property) and values are their values. To see what
78
+	 * their types should be, look at what that field object returns on its prepare_for_get and prepare_for_set methods)
79
+	 *
80
+	 * @var array
81
+	 */
82
+	protected $_fields = array();
83
+
84
+	/**
85
+	 * @var boolean indicating whether or not this model object is intended to ever be saved
86
+	 * For example, we might create model objects intended to only be used for the duration
87
+	 * of this request and to be thrown away, and if they were accidentally saved
88
+	 * it would be a bug.
89
+	 */
90
+	protected $_allow_persist = true;
91
+
92
+	/**
93
+	 * @var boolean indicating whether or not this model object's properties have changed since construction
94
+	 */
95
+	protected $_has_changes = false;
96
+
97
+	/**
98
+	 * @var EEM_Base
99
+	 */
100
+	protected $_model;
101
+
102
+	/**
103
+	 * This is a cache of results from custom selections done on a query that constructs this entity. The only purpose
104
+	 * for these values is for retrieval of the results, they are not further queryable and they are not persisted to
105
+	 * the db.  They also do not automatically update if there are any changes to the data that produced their results.
106
+	 * The format is a simple array of field_alias => field_value.  So for instance if a custom select was something
107
+	 * like,  "Select COUNT(Registration.REG_ID) as Registration_Count ...", then the resulting value will be in this
108
+	 * array as:
109
+	 * array(
110
+	 *  'Registration_Count' => 24
111
+	 * );
112
+	 * Note: if the custom select configuration for the query included a data type, the value will be in the data type
113
+	 * provided for the query (@see EventEspresso\core\domain\values\model\CustomSelects::__construct phpdocs for more
114
+	 * info)
115
+	 *
116
+	 * @var array
117
+	 */
118
+	protected $custom_selection_results = array();
119
+
120
+
121
+	/**
122
+	 * basic constructor for Event Espresso classes, performs any necessary initialization, and verifies it's children
123
+	 * play nice
124
+	 *
125
+	 * @param array   $fieldValues                             where each key is a field (ie, array key in the 2nd
126
+	 *                                                         layer of the model's _fields array, (eg, EVT_ID,
127
+	 *                                                         TXN_amount, QST_name, etc) and values are their values
128
+	 * @param boolean $bydb                                    a flag for setting if the class is instantiated by the
129
+	 *                                                         corresponding db model or not.
130
+	 * @param string  $timezone                                indicate what timezone you want any datetime fields to
131
+	 *                                                         be in when instantiating a EE_Base_Class object.
132
+	 * @param array   $date_formats                            An array of date formats to set on construct where first
133
+	 *                                                         value is the date_format and second value is the time
134
+	 *                                                         format.
135
+	 * @throws InvalidArgumentException
136
+	 * @throws InvalidInterfaceException
137
+	 * @throws InvalidDataTypeException
138
+	 * @throws EE_Error
139
+	 * @throws ReflectionException
140
+	 */
141
+	protected function __construct($fieldValues = array(), $bydb = false, $timezone = '', $date_formats = array())
142
+	{
143
+		$className = get_class($this);
144
+		do_action("AHEE__{$className}__construct", $this, $fieldValues);
145
+		$model        = $this->get_model();
146
+		$model_fields = $model->field_settings(false);
147
+		// ensure $fieldValues is an array
148
+		$fieldValues = is_array($fieldValues) ? $fieldValues : array($fieldValues);
149
+		// verify client code has not passed any invalid field names
150
+		foreach ($fieldValues as $field_name => $field_value) {
151
+			if (! isset($model_fields[ $field_name ])) {
152
+				throw new EE_Error(
153
+					sprintf(
154
+						esc_html__(
155
+							'Invalid field (%s) passed to constructor of %s. Allowed fields are :%s',
156
+							'event_espresso'
157
+						),
158
+						$field_name,
159
+						get_class($this),
160
+						implode(', ', array_keys($model_fields))
161
+					)
162
+				);
163
+			}
164
+		}
165
+		$this->_timezone = EEH_DTT_Helper::get_valid_timezone_string($timezone);
166
+		if (! empty($date_formats) && is_array($date_formats)) {
167
+			list($this->_dt_frmt, $this->_tm_frmt) = $date_formats;
168
+		} else {
169
+			//set default formats for date and time
170
+			$this->_dt_frmt = (string) get_option('date_format', 'Y-m-d');
171
+			$this->_tm_frmt = (string) get_option('time_format', 'g:i a');
172
+		}
173
+		//if db model is instantiating
174
+		if ($bydb) {
175
+			//client code has indicated these field values are from the database
176
+			foreach ($model_fields as $fieldName => $field) {
177
+				$this->set_from_db(
178
+					$fieldName,
179
+					isset($fieldValues[ $fieldName ]) ? $fieldValues[ $fieldName ] : null
180
+				);
181
+			}
182
+		} else {
183
+			//we're constructing a brand
184
+			//new instance of the model object. Generally, this means we'll need to do more field validation
185
+			foreach ($model_fields as $fieldName => $field) {
186
+				$this->set(
187
+					$fieldName,
188
+					isset($fieldValues[ $fieldName ]) ? $fieldValues[ $fieldName ] : null, true
189
+				);
190
+			}
191
+		}
192
+		//remember what values were passed to this constructor
193
+		$this->_props_n_values_provided_in_constructor = $fieldValues;
194
+		//remember in entity mapper
195
+		if (! $bydb && $model->has_primary_key_field() && $this->ID()) {
196
+			$model->add_to_entity_map($this);
197
+		}
198
+		//setup all the relations
199
+		foreach ($model->relation_settings() as $relation_name => $relation_obj) {
200
+			if ($relation_obj instanceof EE_Belongs_To_Relation) {
201
+				$this->_model_relations[ $relation_name ] = null;
202
+			} else {
203
+				$this->_model_relations[ $relation_name ] = array();
204
+			}
205
+		}
206
+		/**
207
+		 * Action done at the end of each model object construction
208
+		 *
209
+		 * @param EE_Base_Class $this the model object just created
210
+		 */
211
+		do_action('AHEE__EE_Base_Class__construct__finished', $this);
212
+	}
213
+
214
+
215
+	/**
216
+	 * Gets whether or not this model object is allowed to persist/be saved to the database.
217
+	 *
218
+	 * @return boolean
219
+	 */
220
+	public function allow_persist()
221
+	{
222
+		return $this->_allow_persist;
223
+	}
224
+
225
+
226
+	/**
227
+	 * Sets whether or not this model object should be allowed to be saved to the DB.
228
+	 * Normally once this is set to FALSE you wouldn't set it back to TRUE, unless
229
+	 * you got new information that somehow made you change your mind.
230
+	 *
231
+	 * @param boolean $allow_persist
232
+	 * @return boolean
233
+	 */
234
+	public function set_allow_persist($allow_persist)
235
+	{
236
+		return $this->_allow_persist = $allow_persist;
237
+	}
238
+
239
+
240
+	/**
241
+	 * Gets the field's original value when this object was constructed during this request.
242
+	 * This can be helpful when determining if a model object has changed or not
243
+	 *
244
+	 * @param string $field_name
245
+	 * @return mixed|null
246
+	 * @throws ReflectionException
247
+	 * @throws InvalidArgumentException
248
+	 * @throws InvalidInterfaceException
249
+	 * @throws InvalidDataTypeException
250
+	 * @throws EE_Error
251
+	 */
252
+	public function get_original($field_name)
253
+	{
254
+		if (isset($this->_props_n_values_provided_in_constructor[ $field_name ])
255
+			&& $field_settings = $this->get_model()->field_settings_for($field_name)
256
+		) {
257
+			return $field_settings->prepare_for_get($this->_props_n_values_provided_in_constructor[ $field_name ]);
258
+		}
259
+		return null;
260
+	}
261
+
262
+
263
+	/**
264
+	 * @param EE_Base_Class $obj
265
+	 * @return string
266
+	 */
267
+	public function get_class($obj)
268
+	{
269
+		return get_class($obj);
270
+	}
271
+
272
+
273
+	/**
274
+	 * Overrides parent because parent expects old models.
275
+	 * This also doesn't do any validation, and won't work for serialized arrays
276
+	 *
277
+	 * @param    string $field_name
278
+	 * @param    mixed  $field_value
279
+	 * @param bool      $use_default
280
+	 * @throws InvalidArgumentException
281
+	 * @throws InvalidInterfaceException
282
+	 * @throws InvalidDataTypeException
283
+	 * @throws EE_Error
284
+	 * @throws ReflectionException
285
+	 * @throws ReflectionException
286
+	 * @throws ReflectionException
287
+	 */
288
+	public function set($field_name, $field_value, $use_default = false)
289
+	{
290
+		// if not using default and nothing has changed, and object has already been setup (has ID),
291
+		// then don't do anything
292
+		if (
293
+			! $use_default
294
+			&& $this->_fields[ $field_name ] === $field_value
295
+			&& $this->ID()
296
+		) {
297
+			return;
298
+		}
299
+		$model              = $this->get_model();
300
+		$this->_has_changes = true;
301
+		$field_obj          = $model->field_settings_for($field_name);
302
+		if ($field_obj instanceof EE_Model_Field_Base) {
303
+			//			if ( method_exists( $field_obj, 'set_timezone' )) {
304
+			if ($field_obj instanceof EE_Datetime_Field) {
305
+				$field_obj->set_timezone($this->_timezone);
306
+				$field_obj->set_date_format($this->_dt_frmt);
307
+				$field_obj->set_time_format($this->_tm_frmt);
308
+			}
309
+			$holder_of_value = $field_obj->prepare_for_set($field_value);
310
+			//should the value be null?
311
+			if (($field_value === null || $holder_of_value === null || $holder_of_value === '') && $use_default) {
312
+				$this->_fields[ $field_name ] = $field_obj->get_default_value();
313
+				/**
314
+				 * To save having to refactor all the models, if a default value is used for a
315
+				 * EE_Datetime_Field, and that value is not null nor is it a DateTime
316
+				 * object.  Then let's do a set again to ensure that it becomes a DateTime
317
+				 * object.
318
+				 *
319
+				 * @since 4.6.10+
320
+				 */
321
+				if (
322
+					$field_obj instanceof EE_Datetime_Field
323
+					&& $this->_fields[ $field_name ] !== null
324
+					&& ! $this->_fields[ $field_name ] instanceof DateTime
325
+				) {
326
+					empty($this->_fields[ $field_name ])
327
+						? $this->set($field_name, time())
328
+						: $this->set($field_name, $this->_fields[ $field_name ]);
329
+				}
330
+			} else {
331
+				$this->_fields[ $field_name ] = $holder_of_value;
332
+			}
333
+			//if we're not in the constructor...
334
+			//now check if what we set was a primary key
335
+			if (
336
+				//note: props_n_values_provided_in_constructor is only set at the END of the constructor
337
+				$this->_props_n_values_provided_in_constructor
338
+				&& $field_value
339
+				&& $field_name === $model->primary_key_name()
340
+			) {
341
+				//if so, we want all this object's fields to be filled either with
342
+				//what we've explicitly set on this model
343
+				//or what we have in the db
344
+				// echo "setting primary key!";
345
+				$fields_on_model = self::_get_model(get_class($this))->field_settings();
346
+				$obj_in_db       = self::_get_model(get_class($this))->get_one_by_ID($field_value);
347
+				foreach ($fields_on_model as $field_obj) {
348
+					if (! array_key_exists($field_obj->get_name(), $this->_props_n_values_provided_in_constructor)
349
+						&& $field_obj->get_name() !== $field_name
350
+					) {
351
+						$this->set($field_obj->get_name(), $obj_in_db->get($field_obj->get_name()));
352
+					}
353
+				}
354
+				//oh this model object has an ID? well make sure its in the entity mapper
355
+				$model->add_to_entity_map($this);
356
+			}
357
+			//let's unset any cache for this field_name from the $_cached_properties property.
358
+			$this->_clear_cached_property($field_name);
359
+		} else {
360
+			throw new EE_Error(
361
+				sprintf(
362
+					esc_html__(
363
+						'A valid EE_Model_Field_Base could not be found for the given field name: %s',
364
+						'event_espresso'
365
+					),
366
+					$field_name
367
+				)
368
+			);
369
+		}
370
+	}
371
+
372
+
373
+	/**
374
+	 * Set custom select values for model.
375
+	 *
376
+	 * @param array $custom_select_values
377
+	 */
378
+	public function setCustomSelectsValues(array $custom_select_values)
379
+	{
380
+		$this->custom_selection_results = $custom_select_values;
381
+	}
382
+
383
+
384
+	/**
385
+	 * Returns the custom select value for the provided alias if its set.
386
+	 * If not set, returns null.
387
+	 *
388
+	 * @param string $alias
389
+	 * @return string|int|float|null
390
+	 */
391
+	public function getCustomSelect($alias)
392
+	{
393
+		return isset($this->custom_selection_results[ $alias ])
394
+			? $this->custom_selection_results[ $alias ]
395
+			: null;
396
+	}
397
+
398
+
399
+	/**
400
+	 * This sets the field value on the db column if it exists for the given $column_name or
401
+	 * saves it to EE_Extra_Meta if the given $column_name does not match a db column.
402
+	 *
403
+	 * @see EE_message::get_column_value for related documentation on the necessity of this method.
404
+	 * @param string $field_name  Must be the exact column name.
405
+	 * @param mixed  $field_value The value to set.
406
+	 * @return int|bool @see EE_Base_Class::update_extra_meta() for return docs.
407
+	 * @throws InvalidArgumentException
408
+	 * @throws InvalidInterfaceException
409
+	 * @throws InvalidDataTypeException
410
+	 * @throws EE_Error
411
+	 * @throws ReflectionException
412
+	 */
413
+	public function set_field_or_extra_meta($field_name, $field_value)
414
+	{
415
+		if ($this->get_model()->has_field($field_name)) {
416
+			$this->set($field_name, $field_value);
417
+			return true;
418
+		}
419
+		//ensure this object is saved first so that extra meta can be properly related.
420
+		$this->save();
421
+		return $this->update_extra_meta($field_name, $field_value);
422
+	}
423
+
424
+
425
+	/**
426
+	 * This retrieves the value of the db column set on this class or if that's not present
427
+	 * it will attempt to retrieve from extra_meta if found.
428
+	 * Example Usage:
429
+	 * Via EE_Message child class:
430
+	 * Due to the dynamic nature of the EE_messages system, EE_messengers will always have a "to",
431
+	 * "from", "subject", and "content" field (as represented in the EE_Message schema), however they may
432
+	 * also have additional main fields specific to the messenger.  The system accommodates those extra
433
+	 * fields through the EE_Extra_Meta table.  This method allows for EE_messengers to retrieve the
434
+	 * value for those extra fields dynamically via the EE_message object.
435
+	 *
436
+	 * @param  string $field_name expecting the fully qualified field name.
437
+	 * @return mixed|null  value for the field if found.  null if not found.
438
+	 * @throws ReflectionException
439
+	 * @throws InvalidArgumentException
440
+	 * @throws InvalidInterfaceException
441
+	 * @throws InvalidDataTypeException
442
+	 * @throws EE_Error
443
+	 */
444
+	public function get_field_or_extra_meta($field_name)
445
+	{
446
+		if ($this->get_model()->has_field($field_name)) {
447
+			$column_value = $this->get($field_name);
448
+		} else {
449
+			//This isn't a column in the main table, let's see if it is in the extra meta.
450
+			$column_value = $this->get_extra_meta($field_name, true, null);
451
+		}
452
+		return $column_value;
453
+	}
454
+
455
+
456
+	/**
457
+	 * See $_timezone property for description of what the timezone property is for.  This SETS the timezone internally
458
+	 * for being able to reference what timezone we are running conversions on when converting TO the internal timezone
459
+	 * (UTC Unix Timestamp) for the object OR when converting FROM the internal timezone (UTC Unix Timestamp). This is
460
+	 * available to all child classes that may be using the EE_Datetime_Field for a field data type.
461
+	 *
462
+	 * @access public
463
+	 * @param string $timezone A valid timezone string as described by @link http://www.php.net/manual/en/timezones.php
464
+	 * @return void
465
+	 * @throws InvalidArgumentException
466
+	 * @throws InvalidInterfaceException
467
+	 * @throws InvalidDataTypeException
468
+	 * @throws EE_Error
469
+	 * @throws ReflectionException
470
+	 */
471
+	public function set_timezone($timezone = '')
472
+	{
473
+		$this->_timezone = EEH_DTT_Helper::get_valid_timezone_string($timezone);
474
+		//make sure we clear all cached properties because they won't be relevant now
475
+		$this->_clear_cached_properties();
476
+		//make sure we update field settings and the date for all EE_Datetime_Fields
477
+		$model_fields = $this->get_model()->field_settings(false);
478
+		foreach ($model_fields as $field_name => $field_obj) {
479
+			if ($field_obj instanceof EE_Datetime_Field) {
480
+				$field_obj->set_timezone($this->_timezone);
481
+				if (isset($this->_fields[ $field_name ]) && $this->_fields[ $field_name ] instanceof DateTime) {
482
+					$this->_fields[ $field_name ]->setTimezone(new DateTimeZone($this->_timezone));
483
+				}
484
+			}
485
+		}
486
+	}
487
+
488
+
489
+	/**
490
+	 * This just returns whatever is set for the current timezone.
491
+	 *
492
+	 * @access public
493
+	 * @return string timezone string
494
+	 */
495
+	public function get_timezone()
496
+	{
497
+		return $this->_timezone;
498
+	}
499
+
500
+
501
+	/**
502
+	 * This sets the internal date format to what is sent in to be used as the new default for the class
503
+	 * internally instead of wp set date format options
504
+	 *
505
+	 * @since 4.6
506
+	 * @param string $format should be a format recognizable by PHP date() functions.
507
+	 */
508
+	public function set_date_format($format)
509
+	{
510
+		$this->_dt_frmt = $format;
511
+		//clear cached_properties because they won't be relevant now.
512
+		$this->_clear_cached_properties();
513
+	}
514
+
515
+
516
+	/**
517
+	 * This sets the internal time format string to what is sent in to be used as the new default for the
518
+	 * class internally instead of wp set time format options.
519
+	 *
520
+	 * @since 4.6
521
+	 * @param string $format should be a format recognizable by PHP date() functions.
522
+	 */
523
+	public function set_time_format($format)
524
+	{
525
+		$this->_tm_frmt = $format;
526
+		//clear cached_properties because they won't be relevant now.
527
+		$this->_clear_cached_properties();
528
+	}
529
+
530
+
531
+	/**
532
+	 * This returns the current internal set format for the date and time formats.
533
+	 *
534
+	 * @param bool $full           if true (default), then return the full format.  Otherwise will return an array
535
+	 *                             where the first value is the date format and the second value is the time format.
536
+	 * @return mixed string|array
537
+	 */
538
+	public function get_format($full = true)
539
+	{
540
+		return $full ? $this->_dt_frmt . ' ' . $this->_tm_frmt : array($this->_dt_frmt, $this->_tm_frmt);
541
+	}
542
+
543
+
544
+	/**
545
+	 * cache
546
+	 * stores the passed model object on the current model object.
547
+	 * In certain circumstances, we can use this cached model object instead of querying for another one entirely.
548
+	 *
549
+	 * @param string        $relationName    one of the keys in the _model_relations array on the model. Eg
550
+	 *                                       'Registration' associated with this model object
551
+	 * @param EE_Base_Class $object_to_cache that has a relation to this model object. (Eg, if this is a Transaction,
552
+	 *                                       that could be a payment or a registration)
553
+	 * @param null          $cache_id        a string or number that will be used as the key for any Belongs_To_Many
554
+	 *                                       items which will be stored in an array on this object
555
+	 * @throws ReflectionException
556
+	 * @throws InvalidArgumentException
557
+	 * @throws InvalidInterfaceException
558
+	 * @throws InvalidDataTypeException
559
+	 * @throws EE_Error
560
+	 * @return mixed    index into cache, or just TRUE if the relation is of type Belongs_To (because there's only one
561
+	 *                                       related thing, no array)
562
+	 */
563
+	public function cache($relationName = '', $object_to_cache = null, $cache_id = null)
564
+	{
565
+		// its entirely possible that there IS no related object yet in which case there is nothing to cache.
566
+		if (! $object_to_cache instanceof EE_Base_Class) {
567
+			return false;
568
+		}
569
+		// also get "how" the object is related, or throw an error
570
+		if (! $relationship_to_model = $this->get_model()->related_settings_for($relationName)) {
571
+			throw new EE_Error(
572
+				sprintf(
573
+					esc_html__('There is no relationship to %s on a %s. Cannot cache it', 'event_espresso'),
574
+					$relationName,
575
+					get_class($this)
576
+				)
577
+			);
578
+		}
579
+		// how many things are related ?
580
+		if ($relationship_to_model instanceof EE_Belongs_To_Relation) {
581
+			// if it's a "belongs to" relationship, then there's only one related model object
582
+			// eg, if this is a registration, there's only 1 attendee for it
583
+			// so for these model objects just set it to be cached
584
+			$this->_model_relations[ $relationName ] = $object_to_cache;
585
+			$return                                  = true;
586
+		} else {
587
+			// otherwise, this is the "many" side of a one to many relationship,
588
+			// so we'll add the object to the array of related objects for that type.
589
+			// eg: if this is an event, there are many registrations for that event,
590
+			// so we cache the registrations in an array
591
+			if (! is_array($this->_model_relations[ $relationName ])) {
592
+				// if for some reason, the cached item is a model object,
593
+				// then stick that in the array, otherwise start with an empty array
594
+				$this->_model_relations[ $relationName ] = $this->_model_relations[ $relationName ]
595
+														   instanceof
596
+														   EE_Base_Class
597
+					? array($this->_model_relations[ $relationName ]) : array();
598
+			}
599
+			// first check for a cache_id which is normally empty
600
+			if (! empty($cache_id)) {
601
+				// if the cache_id exists, then it means we are purposely trying to cache this
602
+				// with a known key that can then be used to retrieve the object later on
603
+				$this->_model_relations[ $relationName ][ $cache_id ] = $object_to_cache;
604
+				$return                                               = $cache_id;
605
+			} elseif ($object_to_cache->ID()) {
606
+				// OR the cached object originally came from the db, so let's just use it's PK for an ID
607
+				$this->_model_relations[ $relationName ][ $object_to_cache->ID() ] = $object_to_cache;
608
+				$return                                                            = $object_to_cache->ID();
609
+			} else {
610
+				// OR it's a new object with no ID, so just throw it in the array with an auto-incremented ID
611
+				$this->_model_relations[ $relationName ][] = $object_to_cache;
612
+				// move the internal pointer to the end of the array
613
+				end($this->_model_relations[ $relationName ]);
614
+				// and grab the key so that we can return it
615
+				$return = key($this->_model_relations[ $relationName ]);
616
+			}
617
+		}
618
+		return $return;
619
+	}
620
+
621
+
622
+	/**
623
+	 * For adding an item to the cached_properties property.
624
+	 *
625
+	 * @access protected
626
+	 * @param string      $fieldname the property item the corresponding value is for.
627
+	 * @param mixed       $value     The value we are caching.
628
+	 * @param string|null $cache_type
629
+	 * @return void
630
+	 * @throws ReflectionException
631
+	 * @throws InvalidArgumentException
632
+	 * @throws InvalidInterfaceException
633
+	 * @throws InvalidDataTypeException
634
+	 * @throws EE_Error
635
+	 */
636
+	protected function _set_cached_property($fieldname, $value, $cache_type = null)
637
+	{
638
+		//first make sure this property exists
639
+		$this->get_model()->field_settings_for($fieldname);
640
+		$cache_type                                            = empty($cache_type) ? 'standard' : $cache_type;
641
+		$this->_cached_properties[ $fieldname ][ $cache_type ] = $value;
642
+	}
643
+
644
+
645
+	/**
646
+	 * This returns the value cached property if it exists OR the actual property value if the cache doesn't exist.
647
+	 * This also SETS the cache if we return the actual property!
648
+	 *
649
+	 * @param string $fieldname        the name of the property we're trying to retrieve
650
+	 * @param bool   $pretty
651
+	 * @param string $extra_cache_ref  This allows the user to specify an extra cache ref for the given property
652
+	 *                                 (in cases where the same property may be used for different outputs
653
+	 *                                 - i.e. datetime, money etc.)
654
+	 *                                 It can also accept certain pre-defined "schema" strings
655
+	 *                                 to define how to output the property.
656
+	 *                                 see the field's prepare_for_pretty_echoing for what strings can be used
657
+	 * @return mixed                   whatever the value for the property is we're retrieving
658
+	 * @throws ReflectionException
659
+	 * @throws InvalidArgumentException
660
+	 * @throws InvalidInterfaceException
661
+	 * @throws InvalidDataTypeException
662
+	 * @throws EE_Error
663
+	 */
664
+	protected function _get_cached_property($fieldname, $pretty = false, $extra_cache_ref = null)
665
+	{
666
+		//verify the field exists
667
+		$model = $this->get_model();
668
+		$model->field_settings_for($fieldname);
669
+		$cache_type = $pretty ? 'pretty' : 'standard';
670
+		$cache_type .= ! empty($extra_cache_ref) ? '_' . $extra_cache_ref : '';
671
+		if (isset($this->_cached_properties[ $fieldname ][ $cache_type ])) {
672
+			return $this->_cached_properties[ $fieldname ][ $cache_type ];
673
+		}
674
+		$value = $this->_get_fresh_property($fieldname, $pretty, $extra_cache_ref);
675
+		$this->_set_cached_property($fieldname, $value, $cache_type);
676
+		return $value;
677
+	}
678
+
679
+
680
+	/**
681
+	 * If the cache didn't fetch the needed item, this fetches it.
682
+	 *
683
+	 * @param string $fieldname
684
+	 * @param bool   $pretty
685
+	 * @param string $extra_cache_ref
686
+	 * @return mixed
687
+	 * @throws InvalidArgumentException
688
+	 * @throws InvalidInterfaceException
689
+	 * @throws InvalidDataTypeException
690
+	 * @throws EE_Error
691
+	 * @throws ReflectionException
692
+	 */
693
+	protected function _get_fresh_property($fieldname, $pretty = false, $extra_cache_ref = null)
694
+	{
695
+		$field_obj = $this->get_model()->field_settings_for($fieldname);
696
+		// If this is an EE_Datetime_Field we need to make sure timezone, formats, and output are correct
697
+		if ($field_obj instanceof EE_Datetime_Field) {
698
+			$this->_prepare_datetime_field($field_obj, $pretty, $extra_cache_ref);
699
+		}
700
+		if (! isset($this->_fields[ $fieldname ])) {
701
+			$this->_fields[ $fieldname ] = null;
702
+		}
703
+		$value = $pretty
704
+			? $field_obj->prepare_for_pretty_echoing($this->_fields[ $fieldname ], $extra_cache_ref)
705
+			: $field_obj->prepare_for_get($this->_fields[ $fieldname ]);
706
+		return $value;
707
+	}
708
+
709
+
710
+	/**
711
+	 * set timezone, formats, and output for EE_Datetime_Field objects
712
+	 *
713
+	 * @param \EE_Datetime_Field $datetime_field
714
+	 * @param bool               $pretty
715
+	 * @param null               $date_or_time
716
+	 * @return void
717
+	 * @throws InvalidArgumentException
718
+	 * @throws InvalidInterfaceException
719
+	 * @throws InvalidDataTypeException
720
+	 * @throws EE_Error
721
+	 */
722
+	protected function _prepare_datetime_field(
723
+		EE_Datetime_Field $datetime_field,
724
+		$pretty = false,
725
+		$date_or_time = null
726
+	) {
727
+		$datetime_field->set_timezone($this->_timezone);
728
+		$datetime_field->set_date_format($this->_dt_frmt, $pretty);
729
+		$datetime_field->set_time_format($this->_tm_frmt, $pretty);
730
+		//set the output returned
731
+		switch ($date_or_time) {
732
+			case 'D' :
733
+				$datetime_field->set_date_time_output('date');
734
+				break;
735
+			case 'T' :
736
+				$datetime_field->set_date_time_output('time');
737
+				break;
738
+			default :
739
+				$datetime_field->set_date_time_output();
740
+		}
741
+	}
742
+
743
+
744
+	/**
745
+	 * This just takes care of clearing out the cached_properties
746
+	 *
747
+	 * @return void
748
+	 */
749
+	protected function _clear_cached_properties()
750
+	{
751
+		$this->_cached_properties = array();
752
+	}
753
+
754
+
755
+	/**
756
+	 * This just clears out ONE property if it exists in the cache
757
+	 *
758
+	 * @param  string $property_name the property to remove if it exists (from the _cached_properties array)
759
+	 * @return void
760
+	 */
761
+	protected function _clear_cached_property($property_name)
762
+	{
763
+		if (isset($this->_cached_properties[ $property_name ])) {
764
+			unset($this->_cached_properties[ $property_name ]);
765
+		}
766
+	}
767
+
768
+
769
+	/**
770
+	 * Ensures that this related thing is a model object.
771
+	 *
772
+	 * @param mixed  $object_or_id EE_base_Class/int/string either a related model object, or its ID
773
+	 * @param string $model_name   name of the related thing, eg 'Attendee',
774
+	 * @return EE_Base_Class
775
+	 * @throws ReflectionException
776
+	 * @throws InvalidArgumentException
777
+	 * @throws InvalidInterfaceException
778
+	 * @throws InvalidDataTypeException
779
+	 * @throws EE_Error
780
+	 */
781
+	protected function ensure_related_thing_is_model_obj($object_or_id, $model_name)
782
+	{
783
+		$other_model_instance = self::_get_model_instance_with_name(
784
+			self::_get_model_classname($model_name),
785
+			$this->_timezone
786
+		);
787
+		return $other_model_instance->ensure_is_obj($object_or_id);
788
+	}
789
+
790
+
791
+	/**
792
+	 * Forgets the cached model of the given relation Name. So the next time we request it,
793
+	 * we will fetch it again from the database. (Handy if you know it's changed somehow).
794
+	 * If a specific object is supplied, and the relationship to it is either a HasMany or HABTM,
795
+	 * then only remove that one object from our cached array. Otherwise, clear the entire list
796
+	 *
797
+	 * @param string $relationName                         one of the keys in the _model_relations array on the model.
798
+	 *                                                     Eg 'Registration'
799
+	 * @param mixed  $object_to_remove_or_index_into_array or an index into the array of cached things, or NULL
800
+	 *                                                     if you intend to use $clear_all = TRUE, or the relation only
801
+	 *                                                     has 1 object anyways (ie, it's a BelongsToRelation)
802
+	 * @param bool   $clear_all                            This flags clearing the entire cache relation property if
803
+	 *                                                     this is HasMany or HABTM.
804
+	 * @throws ReflectionException
805
+	 * @throws InvalidArgumentException
806
+	 * @throws InvalidInterfaceException
807
+	 * @throws InvalidDataTypeException
808
+	 * @throws EE_Error
809
+	 * @return EE_Base_Class | boolean from which was cleared from the cache, or true if we requested to remove a
810
+	 *                                                     relation from all
811
+	 */
812
+	public function clear_cache($relationName, $object_to_remove_or_index_into_array = null, $clear_all = false)
813
+	{
814
+		$relationship_to_model = $this->get_model()->related_settings_for($relationName);
815
+		$index_in_cache        = '';
816
+		if (! $relationship_to_model) {
817
+			throw new EE_Error(
818
+				sprintf(
819
+					esc_html__('There is no relationship to %s on a %s. Cannot clear that cache', 'event_espresso'),
820
+					$relationName,
821
+					get_class($this)
822
+				)
823
+			);
824
+		}
825
+		if ($clear_all) {
826
+			$obj_removed                             = true;
827
+			$this->_model_relations[ $relationName ] = null;
828
+		} elseif ($relationship_to_model instanceof EE_Belongs_To_Relation) {
829
+			$obj_removed                             = $this->_model_relations[ $relationName ];
830
+			$this->_model_relations[ $relationName ] = null;
831
+		} else {
832
+			if ($object_to_remove_or_index_into_array instanceof EE_Base_Class
833
+				&& $object_to_remove_or_index_into_array->ID()
834
+			) {
835
+				$index_in_cache = $object_to_remove_or_index_into_array->ID();
836
+				if (is_array($this->_model_relations[ $relationName ])
837
+					&& ! isset($this->_model_relations[ $relationName ][ $index_in_cache ])
838
+				) {
839
+					$index_found_at = null;
840
+					//find this object in the array even though it has a different key
841
+					foreach ($this->_model_relations[ $relationName ] as $index => $obj) {
842
+						/** @noinspection TypeUnsafeComparisonInspection */
843
+						if (
844
+							$obj instanceof EE_Base_Class
845
+							&& (
846
+								$obj == $object_to_remove_or_index_into_array
847
+								|| $obj->ID() === $object_to_remove_or_index_into_array->ID()
848
+							)
849
+						) {
850
+							$index_found_at = $index;
851
+							break;
852
+						}
853
+					}
854
+					if ($index_found_at) {
855
+						$index_in_cache = $index_found_at;
856
+					} else {
857
+						//it wasn't found. huh. well obviously it doesn't need to be removed from teh cache
858
+						//if it wasn't in it to begin with. So we're done
859
+						return $object_to_remove_or_index_into_array;
860
+					}
861
+				}
862
+			} elseif ($object_to_remove_or_index_into_array instanceof EE_Base_Class) {
863
+				//so they provided a model object, but it's not yet saved to the DB... so let's go hunting for it!
864
+				foreach ($this->get_all_from_cache($relationName) as $index => $potentially_obj_we_want) {
865
+					/** @noinspection TypeUnsafeComparisonInspection */
866
+					if ($potentially_obj_we_want == $object_to_remove_or_index_into_array) {
867
+						$index_in_cache = $index;
868
+					}
869
+				}
870
+			} else {
871
+				$index_in_cache = $object_to_remove_or_index_into_array;
872
+			}
873
+			//supposedly we've found it. But it could just be that the client code
874
+			//provided a bad index/object
875
+			if (isset($this->_model_relations[ $relationName ][ $index_in_cache ])) {
876
+				$obj_removed = $this->_model_relations[ $relationName ][ $index_in_cache ];
877
+				unset($this->_model_relations[ $relationName ][ $index_in_cache ]);
878
+			} else {
879
+				//that thing was never cached anyways.
880
+				$obj_removed = null;
881
+			}
882
+		}
883
+		return $obj_removed;
884
+	}
885
+
886
+
887
+	/**
888
+	 * update_cache_after_object_save
889
+	 * Allows a cached item to have it's cache ID (within the array of cached items) reset using the new ID it has
890
+	 * obtained after being saved to the db
891
+	 *
892
+	 * @param string        $relationName       - the type of object that is cached
893
+	 * @param EE_Base_Class $newly_saved_object - the newly saved object to be re-cached
894
+	 * @param string        $current_cache_id   - the ID that was used when originally caching the object
895
+	 * @return boolean TRUE on success, FALSE on fail
896
+	 * @throws ReflectionException
897
+	 * @throws InvalidArgumentException
898
+	 * @throws InvalidInterfaceException
899
+	 * @throws InvalidDataTypeException
900
+	 * @throws EE_Error
901
+	 */
902
+	public function update_cache_after_object_save(
903
+		$relationName,
904
+		EE_Base_Class $newly_saved_object,
905
+		$current_cache_id = ''
906
+	) {
907
+		// verify that incoming object is of the correct type
908
+		$obj_class = 'EE_' . $relationName;
909
+		if ($newly_saved_object instanceof $obj_class) {
910
+			/* @type EE_Base_Class $newly_saved_object */
911
+			// now get the type of relation
912
+			$relationship_to_model = $this->get_model()->related_settings_for($relationName);
913
+			// if this is a 1:1 relationship
914
+			if ($relationship_to_model instanceof EE_Belongs_To_Relation) {
915
+				// then just replace the cached object with the newly saved object
916
+				$this->_model_relations[ $relationName ] = $newly_saved_object;
917
+				return true;
918
+				// or if it's some kind of sordid feral polyamorous relationship...
919
+			}
920
+			if (is_array($this->_model_relations[ $relationName ])
921
+					  && isset($this->_model_relations[ $relationName ][ $current_cache_id ])
922
+			) {
923
+				// then remove the current cached item
924
+				unset($this->_model_relations[ $relationName ][ $current_cache_id ]);
925
+				// and cache the newly saved object using it's new ID
926
+				$this->_model_relations[ $relationName ][ $newly_saved_object->ID() ] = $newly_saved_object;
927
+				return true;
928
+			}
929
+		}
930
+		return false;
931
+	}
932
+
933
+
934
+	/**
935
+	 * Fetches a single EE_Base_Class on that relation. (If the relation is of type
936
+	 * BelongsTo, it will only ever have 1 object. However, other relations could have an array of objects)
937
+	 *
938
+	 * @param string $relationName
939
+	 * @return EE_Base_Class
940
+	 */
941
+	public function get_one_from_cache($relationName)
942
+	{
943
+		$cached_array_or_object = isset($this->_model_relations[ $relationName ])
944
+			? $this->_model_relations[ $relationName ]
945
+			: null;
946
+		if (is_array($cached_array_or_object)) {
947
+			return array_shift($cached_array_or_object);
948
+		}
949
+		return $cached_array_or_object;
950
+	}
951
+
952
+
953
+	/**
954
+	 * Fetches a single EE_Base_Class on that relation. (If the relation is of type
955
+	 * BelongsTo, it will only ever have 1 object. However, other relations could have an array of objects)
956
+	 *
957
+	 * @param string $relationName
958
+	 * @throws ReflectionException
959
+	 * @throws InvalidArgumentException
960
+	 * @throws InvalidInterfaceException
961
+	 * @throws InvalidDataTypeException
962
+	 * @throws EE_Error
963
+	 * @return EE_Base_Class[] NOT necessarily indexed by primary keys
964
+	 */
965
+	public function get_all_from_cache($relationName)
966
+	{
967
+		$objects = isset($this->_model_relations[ $relationName ]) ? $this->_model_relations[ $relationName ] : array();
968
+		// if the result is not an array, but exists, make it an array
969
+		$objects = is_array($objects) ? $objects : array($objects);
970
+		//bugfix for https://events.codebasehq.com/projects/event-espresso/tickets/7143
971
+		//basically, if this model object was stored in the session, and these cached model objects
972
+		//already have IDs, let's make sure they're in their model's entity mapper
973
+		//otherwise we will have duplicates next time we call
974
+		// EE_Registry::instance()->load_model( $relationName )->get_one_by_ID( $result->ID() );
975
+		$model = EE_Registry::instance()->load_model($relationName);
976
+		foreach ($objects as $model_object) {
977
+			if ($model instanceof EEM_Base && $model_object instanceof EE_Base_Class) {
978
+				//ensure its in the map if it has an ID; otherwise it will be added to the map when its saved
979
+				if ($model_object->ID()) {
980
+					$model->add_to_entity_map($model_object);
981
+				}
982
+			} else {
983
+				throw new EE_Error(
984
+					sprintf(
985
+						esc_html__(
986
+							'Error retrieving related model objects. Either $1%s is not a model or $2%s is not a model object',
987
+							'event_espresso'
988
+						),
989
+						$relationName,
990
+						gettype($model_object)
991
+					)
992
+				);
993
+			}
994
+		}
995
+		return $objects;
996
+	}
997
+
998
+
999
+	/**
1000
+	 * Returns the next x number of EE_Base_Class objects in sequence from this object as found in the database
1001
+	 * matching the given query conditions.
1002
+	 *
1003
+	 * @param null  $field_to_order_by  What field is being used as the reference point.
1004
+	 * @param int   $limit              How many objects to return.
1005
+	 * @param array $query_params       Any additional conditions on the query.
1006
+	 * @param null  $columns_to_select  If left null, then an array of EE_Base_Class objects is returned, otherwise
1007
+	 *                                  you can indicate just the columns you want returned
1008
+	 * @return array|EE_Base_Class[]
1009
+	 * @throws ReflectionException
1010
+	 * @throws InvalidArgumentException
1011
+	 * @throws InvalidInterfaceException
1012
+	 * @throws InvalidDataTypeException
1013
+	 * @throws EE_Error
1014
+	 */
1015
+	public function next_x($field_to_order_by = null, $limit = 1, $query_params = array(), $columns_to_select = null)
1016
+	{
1017
+		$model         = $this->get_model();
1018
+		$field         = empty($field_to_order_by) && $model->has_primary_key_field()
1019
+			? $model->get_primary_key_field()->get_name()
1020
+			: $field_to_order_by;
1021
+		$current_value = ! empty($field) ? $this->get($field) : null;
1022
+		if (empty($field) || empty($current_value)) {
1023
+			return array();
1024
+		}
1025
+		return $model->next_x($current_value, $field, $limit, $query_params, $columns_to_select);
1026
+	}
1027
+
1028
+
1029
+	/**
1030
+	 * Returns the previous x number of EE_Base_Class objects in sequence from this object as found in the database
1031
+	 * matching the given query conditions.
1032
+	 *
1033
+	 * @param null  $field_to_order_by  What field is being used as the reference point.
1034
+	 * @param int   $limit              How many objects to return.
1035
+	 * @param array $query_params       Any additional conditions on the query.
1036
+	 * @param null  $columns_to_select  If left null, then an array of EE_Base_Class objects is returned, otherwise
1037
+	 *                                  you can indicate just the columns you want returned
1038
+	 * @return array|EE_Base_Class[]
1039
+	 * @throws ReflectionException
1040
+	 * @throws InvalidArgumentException
1041
+	 * @throws InvalidInterfaceException
1042
+	 * @throws InvalidDataTypeException
1043
+	 * @throws EE_Error
1044
+	 */
1045
+	public function previous_x(
1046
+		$field_to_order_by = null,
1047
+		$limit = 1,
1048
+		$query_params = array(),
1049
+		$columns_to_select = null
1050
+	) {
1051
+		$model         = $this->get_model();
1052
+		$field         = empty($field_to_order_by) && $model->has_primary_key_field()
1053
+			? $model->get_primary_key_field()->get_name()
1054
+			: $field_to_order_by;
1055
+		$current_value = ! empty($field) ? $this->get($field) : null;
1056
+		if (empty($field) || empty($current_value)) {
1057
+			return array();
1058
+		}
1059
+		return $model->previous_x($current_value, $field, $limit, $query_params, $columns_to_select);
1060
+	}
1061
+
1062
+
1063
+	/**
1064
+	 * Returns the next EE_Base_Class object in sequence from this object as found in the database
1065
+	 * matching the given query conditions.
1066
+	 *
1067
+	 * @param null  $field_to_order_by  What field is being used as the reference point.
1068
+	 * @param array $query_params       Any additional conditions on the query.
1069
+	 * @param null  $columns_to_select  If left null, then an array of EE_Base_Class objects is returned, otherwise
1070
+	 *                                  you can indicate just the columns you want returned
1071
+	 * @return array|EE_Base_Class
1072
+	 * @throws ReflectionException
1073
+	 * @throws InvalidArgumentException
1074
+	 * @throws InvalidInterfaceException
1075
+	 * @throws InvalidDataTypeException
1076
+	 * @throws EE_Error
1077
+	 */
1078
+	public function next($field_to_order_by = null, $query_params = array(), $columns_to_select = null)
1079
+	{
1080
+		$model         = $this->get_model();
1081
+		$field         = empty($field_to_order_by) && $model->has_primary_key_field()
1082
+			? $model->get_primary_key_field()->get_name()
1083
+			: $field_to_order_by;
1084
+		$current_value = ! empty($field) ? $this->get($field) : null;
1085
+		if (empty($field) || empty($current_value)) {
1086
+			return array();
1087
+		}
1088
+		return $model->next($current_value, $field, $query_params, $columns_to_select);
1089
+	}
1090
+
1091
+
1092
+	/**
1093
+	 * Returns the previous EE_Base_Class object in sequence from this object as found in the database
1094
+	 * matching the given query conditions.
1095
+	 *
1096
+	 * @param null  $field_to_order_by  What field is being used as the reference point.
1097
+	 * @param array $query_params       Any additional conditions on the query.
1098
+	 * @param null  $columns_to_select  If left null, then an EE_Base_Class object is returned, otherwise
1099
+	 *                                  you can indicate just the column you want returned
1100
+	 * @return array|EE_Base_Class
1101
+	 * @throws ReflectionException
1102
+	 * @throws InvalidArgumentException
1103
+	 * @throws InvalidInterfaceException
1104
+	 * @throws InvalidDataTypeException
1105
+	 * @throws EE_Error
1106
+	 */
1107
+	public function previous($field_to_order_by = null, $query_params = array(), $columns_to_select = null)
1108
+	{
1109
+		$model         = $this->get_model();
1110
+		$field         = empty($field_to_order_by) && $model->has_primary_key_field()
1111
+			? $model->get_primary_key_field()->get_name()
1112
+			: $field_to_order_by;
1113
+		$current_value = ! empty($field) ? $this->get($field) : null;
1114
+		if (empty($field) || empty($current_value)) {
1115
+			return array();
1116
+		}
1117
+		return $model->previous($current_value, $field, $query_params, $columns_to_select);
1118
+	}
1119
+
1120
+
1121
+	/**
1122
+	 * Overrides parent because parent expects old models.
1123
+	 * This also doesn't do any validation, and won't work for serialized arrays
1124
+	 *
1125
+	 * @param string $field_name
1126
+	 * @param mixed  $field_value_from_db
1127
+	 * @throws ReflectionException
1128
+	 * @throws InvalidArgumentException
1129
+	 * @throws InvalidInterfaceException
1130
+	 * @throws InvalidDataTypeException
1131
+	 * @throws EE_Error
1132
+	 */
1133
+	public function set_from_db($field_name, $field_value_from_db)
1134
+	{
1135
+		$field_obj = $this->get_model()->field_settings_for($field_name);
1136
+		if ($field_obj instanceof EE_Model_Field_Base) {
1137
+			//you would think the DB has no NULLs for non-null label fields right? wrong!
1138
+			//eg, a CPT model object could have an entry in the posts table, but no
1139
+			//entry in the meta table. Meaning that all its columns in the meta table
1140
+			//are null! yikes! so when we find one like that, use defaults for its meta columns
1141
+			if ($field_value_from_db === null) {
1142
+				if ($field_obj->is_nullable()) {
1143
+					//if the field allows nulls, then let it be null
1144
+					$field_value = null;
1145
+				} else {
1146
+					$field_value = $field_obj->get_default_value();
1147
+				}
1148
+			} else {
1149
+				$field_value = $field_obj->prepare_for_set_from_db($field_value_from_db);
1150
+			}
1151
+			$this->_fields[ $field_name ] = $field_value;
1152
+			$this->_clear_cached_property($field_name);
1153
+		}
1154
+	}
1155
+
1156
+
1157
+	/**
1158
+	 * verifies that the specified field is of the correct type
1159
+	 *
1160
+	 * @param string $field_name
1161
+	 * @param string $extra_cache_ref This allows the user to specify an extra cache ref for the given property
1162
+	 *                                (in cases where the same property may be used for different outputs
1163
+	 *                                - i.e. datetime, money etc.)
1164
+	 * @return mixed
1165
+	 * @throws ReflectionException
1166
+	 * @throws InvalidArgumentException
1167
+	 * @throws InvalidInterfaceException
1168
+	 * @throws InvalidDataTypeException
1169
+	 * @throws EE_Error
1170
+	 */
1171
+	public function get($field_name, $extra_cache_ref = null)
1172
+	{
1173
+		return $this->_get_cached_property($field_name, false, $extra_cache_ref);
1174
+	}
1175
+
1176
+
1177
+	/**
1178
+	 * This method simply returns the RAW unprocessed value for the given property in this class
1179
+	 *
1180
+	 * @param  string $field_name A valid fieldname
1181
+	 * @return mixed              Whatever the raw value stored on the property is.
1182
+	 * @throws ReflectionException
1183
+	 * @throws InvalidArgumentException
1184
+	 * @throws InvalidInterfaceException
1185
+	 * @throws InvalidDataTypeException
1186
+	 * @throws EE_Error if fieldSettings is misconfigured or the field doesn't exist.
1187
+	 */
1188
+	public function get_raw($field_name)
1189
+	{
1190
+		$field_settings = $this->get_model()->field_settings_for($field_name);
1191
+		return $field_settings instanceof EE_Datetime_Field && $this->_fields[ $field_name ] instanceof DateTime
1192
+			? $this->_fields[ $field_name ]->format('U')
1193
+			: $this->_fields[ $field_name ];
1194
+	}
1195
+
1196
+
1197
+	/**
1198
+	 * This is used to return the internal DateTime object used for a field that is a
1199
+	 * EE_Datetime_Field.
1200
+	 *
1201
+	 * @param string $field_name               The field name retrieving the DateTime object.
1202
+	 * @return mixed null | false | DateTime  If the requested field is NOT a EE_Datetime_Field then
1203
+	 * @throws ReflectionException
1204
+	 * @throws InvalidArgumentException
1205
+	 * @throws InvalidInterfaceException
1206
+	 * @throws InvalidDataTypeException
1207
+	 * @throws EE_Error
1208
+	 *                                         an error is set and false returned.  If the field IS an
1209
+	 *                                         EE_Datetime_Field and but the field value is null, then
1210
+	 *                                         just null is returned (because that indicates that likely
1211
+	 *                                         this field is nullable).
1212
+	 */
1213
+	public function get_DateTime_object($field_name)
1214
+	{
1215
+		$field_settings = $this->get_model()->field_settings_for($field_name);
1216
+		if (! $field_settings instanceof EE_Datetime_Field) {
1217
+			EE_Error::add_error(
1218
+				sprintf(
1219
+					esc_html__(
1220
+						'The field %s is not an EE_Datetime_Field field.  There is no DateTime object stored on this field type.',
1221
+						'event_espresso'
1222
+					),
1223
+					$field_name
1224
+				),
1225
+				__FILE__,
1226
+				__FUNCTION__,
1227
+				__LINE__
1228
+			);
1229
+			return false;
1230
+		}
1231
+		return $this->_fields[ $field_name ];
1232
+	}
1233
+
1234
+
1235
+	/**
1236
+	 * To be used in template to immediately echo out the value, and format it for output.
1237
+	 * Eg, should call stripslashes and whatnot before echoing
1238
+	 *
1239
+	 * @param string $field_name      the name of the field as it appears in the DB
1240
+	 * @param string $extra_cache_ref This allows the user to specify an extra cache ref for the given property
1241
+	 *                                (in cases where the same property may be used for different outputs
1242
+	 *                                - i.e. datetime, money etc.)
1243
+	 * @return void
1244
+	 * @throws ReflectionException
1245
+	 * @throws InvalidArgumentException
1246
+	 * @throws InvalidInterfaceException
1247
+	 * @throws InvalidDataTypeException
1248
+	 * @throws EE_Error
1249
+	 */
1250
+	public function e($field_name, $extra_cache_ref = null)
1251
+	{
1252
+		echo $this->get_pretty($field_name, $extra_cache_ref);
1253
+	}
1254
+
1255
+
1256
+	/**
1257
+	 * Exactly like e(), echoes out the field, but sets its schema to 'form_input', so that it
1258
+	 * can be easily used as the value of form input.
1259
+	 *
1260
+	 * @param string $field_name
1261
+	 * @return void
1262
+	 * @throws ReflectionException
1263
+	 * @throws InvalidArgumentException
1264
+	 * @throws InvalidInterfaceException
1265
+	 * @throws InvalidDataTypeException
1266
+	 * @throws EE_Error
1267
+	 */
1268
+	public function f($field_name)
1269
+	{
1270
+		$this->e($field_name, 'form_input');
1271
+	}
1272
+
1273
+
1274
+	/**
1275
+	 * Same as `f()` but just returns the value instead of echoing it
1276
+	 *
1277
+	 * @param string $field_name
1278
+	 * @return string
1279
+	 * @throws ReflectionException
1280
+	 * @throws InvalidArgumentException
1281
+	 * @throws InvalidInterfaceException
1282
+	 * @throws InvalidDataTypeException
1283
+	 * @throws EE_Error
1284
+	 */
1285
+	public function get_f($field_name)
1286
+	{
1287
+		return (string) $this->get_pretty($field_name, 'form_input');
1288
+	}
1289
+
1290
+
1291
+	/**
1292
+	 * Gets a pretty view of the field's value. $extra_cache_ref can specify different formats for this.
1293
+	 * The $extra_cache_ref will be passed to the model field's prepare_for_pretty_echoing, so consult the field's class
1294
+	 * to see what options are available.
1295
+	 *
1296
+	 * @param string $field_name
1297
+	 * @param string $extra_cache_ref This allows the user to specify an extra cache ref for the given property
1298
+	 *                                (in cases where the same property may be used for different outputs
1299
+	 *                                - i.e. datetime, money etc.)
1300
+	 * @return mixed
1301
+	 * @throws ReflectionException
1302
+	 * @throws InvalidArgumentException
1303
+	 * @throws InvalidInterfaceException
1304
+	 * @throws InvalidDataTypeException
1305
+	 * @throws EE_Error
1306
+	 */
1307
+	public function get_pretty($field_name, $extra_cache_ref = null)
1308
+	{
1309
+		return $this->_get_cached_property($field_name, true, $extra_cache_ref);
1310
+	}
1311
+
1312
+
1313
+	/**
1314
+	 * This simply returns the datetime for the given field name
1315
+	 * Note: this protected function is called by the wrapper get_date or get_time or get_datetime functions
1316
+	 * (and the equivalent e_date, e_time, e_datetime).
1317
+	 *
1318
+	 * @access   protected
1319
+	 * @param string   $field_name   Field on the instantiated EE_Base_Class child object
1320
+	 * @param string   $dt_frmt      valid datetime format used for date
1321
+	 *                               (if '' then we just use the default on the field,
1322
+	 *                               if NULL we use the last-used format)
1323
+	 * @param string   $tm_frmt      Same as above except this is for time format
1324
+	 * @param string   $date_or_time if NULL then both are returned, otherwise "D" = only date and "T" = only time.
1325
+	 * @param  boolean $echo         Whether the dtt is echoing using pretty echoing or just returned using vanilla get
1326
+	 * @return string|bool|EE_Error string on success, FALSE on fail, or EE_Error Exception is thrown
1327
+	 *                               if field is not a valid dtt field, or void if echoing
1328
+	 * @throws ReflectionException
1329
+	 * @throws InvalidArgumentException
1330
+	 * @throws InvalidInterfaceException
1331
+	 * @throws InvalidDataTypeException
1332
+	 * @throws EE_Error
1333
+	 */
1334
+	protected function _get_datetime($field_name, $dt_frmt = '', $tm_frmt = '', $date_or_time = '', $echo = false)
1335
+	{
1336
+		// clear cached property
1337
+		$this->_clear_cached_property($field_name);
1338
+		//reset format properties because they are used in get()
1339
+		$this->_dt_frmt = $dt_frmt !== '' ? $dt_frmt : $this->_dt_frmt;
1340
+		$this->_tm_frmt = $tm_frmt !== '' ? $tm_frmt : $this->_tm_frmt;
1341
+		if ($echo) {
1342
+			$this->e($field_name, $date_or_time);
1343
+			return '';
1344
+		}
1345
+		return $this->get($field_name, $date_or_time);
1346
+	}
1347
+
1348
+
1349
+	/**
1350
+	 * below are wrapper functions for the various datetime outputs that can be obtained for JUST returning the date
1351
+	 * portion of a datetime value. (note the only difference between get_ and e_ is one returns the value and the
1352
+	 * other echoes the pretty value for dtt)
1353
+	 *
1354
+	 * @param  string $field_name name of model object datetime field holding the value
1355
+	 * @param  string $format     format for the date returned (if NULL we use default in dt_frmt property)
1356
+	 * @return string            datetime value formatted
1357
+	 * @throws ReflectionException
1358
+	 * @throws InvalidArgumentException
1359
+	 * @throws InvalidInterfaceException
1360
+	 * @throws InvalidDataTypeException
1361
+	 * @throws EE_Error
1362
+	 */
1363
+	public function get_date($field_name, $format = '')
1364
+	{
1365
+		return $this->_get_datetime($field_name, $format, null, 'D');
1366
+	}
1367
+
1368
+
1369
+	/**
1370
+	 * @param        $field_name
1371
+	 * @param string $format
1372
+	 * @throws ReflectionException
1373
+	 * @throws InvalidArgumentException
1374
+	 * @throws InvalidInterfaceException
1375
+	 * @throws InvalidDataTypeException
1376
+	 * @throws EE_Error
1377
+	 */
1378
+	public function e_date($field_name, $format = '')
1379
+	{
1380
+		$this->_get_datetime($field_name, $format, null, 'D', true);
1381
+	}
1382
+
1383
+
1384
+	/**
1385
+	 * below are wrapper functions for the various datetime outputs that can be obtained for JUST returning the time
1386
+	 * portion of a datetime value. (note the only difference between get_ and e_ is one returns the value and the
1387
+	 * other echoes the pretty value for dtt)
1388
+	 *
1389
+	 * @param  string $field_name name of model object datetime field holding the value
1390
+	 * @param  string $format     format for the time returned ( if NULL we use default in tm_frmt property)
1391
+	 * @return string             datetime value formatted
1392
+	 * @throws ReflectionException
1393
+	 * @throws InvalidArgumentException
1394
+	 * @throws InvalidInterfaceException
1395
+	 * @throws InvalidDataTypeException
1396
+	 * @throws EE_Error
1397
+	 */
1398
+	public function get_time($field_name, $format = '')
1399
+	{
1400
+		return $this->_get_datetime($field_name, null, $format, 'T');
1401
+	}
1402
+
1403
+
1404
+	/**
1405
+	 * @param        $field_name
1406
+	 * @param string $format
1407
+	 * @throws ReflectionException
1408
+	 * @throws InvalidArgumentException
1409
+	 * @throws InvalidInterfaceException
1410
+	 * @throws InvalidDataTypeException
1411
+	 * @throws EE_Error
1412
+	 */
1413
+	public function e_time($field_name, $format = '')
1414
+	{
1415
+		$this->_get_datetime($field_name, null, $format, 'T', true);
1416
+	}
1417
+
1418
+
1419
+	/**
1420
+	 * below are wrapper functions for the various datetime outputs that can be obtained for returning the date AND
1421
+	 * time portion of a datetime value. (note the only difference between get_ and e_ is one returns the value and the
1422
+	 * other echoes the pretty value for dtt)
1423
+	 *
1424
+	 * @param  string $field_name name of model object datetime field holding the value
1425
+	 * @param  string $dt_frmt    format for the date returned (if NULL we use default in dt_frmt property)
1426
+	 * @param  string $tm_frmt    format for the time returned (if NULL we use default in tm_frmt property)
1427
+	 * @return string             datetime value formatted
1428
+	 * @throws ReflectionException
1429
+	 * @throws InvalidArgumentException
1430
+	 * @throws InvalidInterfaceException
1431
+	 * @throws InvalidDataTypeException
1432
+	 * @throws EE_Error
1433
+	 */
1434
+	public function get_datetime($field_name, $dt_frmt = '', $tm_frmt = '')
1435
+	{
1436
+		return $this->_get_datetime($field_name, $dt_frmt, $tm_frmt);
1437
+	}
1438
+
1439
+
1440
+	/**
1441
+	 * @param string $field_name
1442
+	 * @param string $dt_frmt
1443
+	 * @param string $tm_frmt
1444
+	 * @throws ReflectionException
1445
+	 * @throws InvalidArgumentException
1446
+	 * @throws InvalidInterfaceException
1447
+	 * @throws InvalidDataTypeException
1448
+	 * @throws EE_Error
1449
+	 */
1450
+	public function e_datetime($field_name, $dt_frmt = '', $tm_frmt = '')
1451
+	{
1452
+		$this->_get_datetime($field_name, $dt_frmt, $tm_frmt, null, true);
1453
+	}
1454
+
1455
+
1456
+	/**
1457
+	 * Get the i8ln value for a date using the WordPress @see date_i18n function.
1458
+	 *
1459
+	 * @param string $field_name The EE_Datetime_Field reference for the date being retrieved.
1460
+	 * @param string $format     PHP valid date/time string format.  If none is provided then the internal set format
1461
+	 *                           on the object will be used.
1462
+	 * @return string Date and time string in set locale or false if no field exists for the given
1463
+	 * @throws ReflectionException
1464
+	 * @throws InvalidArgumentException
1465
+	 * @throws InvalidInterfaceException
1466
+	 * @throws InvalidDataTypeException
1467
+	 * @throws EE_Error
1468
+	 *                           field name.
1469
+	 */
1470
+	public function get_i18n_datetime($field_name, $format = '')
1471
+	{
1472
+		$format = empty($format) ? $this->_dt_frmt . ' ' . $this->_tm_frmt : $format;
1473
+		return date_i18n(
1474
+			$format,
1475
+			EEH_DTT_Helper::get_timestamp_with_offset(
1476
+				$this->get_raw($field_name),
1477
+				$this->_timezone
1478
+			)
1479
+		);
1480
+	}
1481
+
1482
+
1483
+	/**
1484
+	 * This method validates whether the given field name is a valid field on the model object as well as it is of a
1485
+	 * type EE_Datetime_Field.  On success there will be returned the field settings.  On fail an EE_Error exception is
1486
+	 * thrown.
1487
+	 *
1488
+	 * @param  string $field_name The field name being checked
1489
+	 * @throws ReflectionException
1490
+	 * @throws InvalidArgumentException
1491
+	 * @throws InvalidInterfaceException
1492
+	 * @throws InvalidDataTypeException
1493
+	 * @throws EE_Error
1494
+	 * @return EE_Datetime_Field
1495
+	 */
1496
+	protected function _get_dtt_field_settings($field_name)
1497
+	{
1498
+		$field = $this->get_model()->field_settings_for($field_name);
1499
+		//check if field is dtt
1500
+		if ($field instanceof EE_Datetime_Field) {
1501
+			return $field;
1502
+		}
1503
+		throw new EE_Error(
1504
+			sprintf(
1505
+				esc_html__(
1506
+					'The field name "%s" has been requested for the EE_Base_Class datetime functions and it is not a valid EE_Datetime_Field.  Please check the spelling of the field and make sure it has been setup as a EE_Datetime_Field in the %s model constructor',
1507
+					'event_espresso'
1508
+				),
1509
+				$field_name,
1510
+				self::_get_model_classname(get_class($this))
1511
+			)
1512
+		);
1513
+	}
1514
+
1515
+
1516
+
1517
+
1518
+	/**
1519
+	 * NOTE ABOUT BELOW:
1520
+	 * These convenience date and time setters are for setting date and time independently.  In other words you might
1521
+	 * want to change the time on a datetime_field but leave the date the same (or vice versa). IF on the other hand
1522
+	 * you want to set both date and time at the same time, you can just use the models default set($fieldname,$value)
1523
+	 * method and make sure you send the entire datetime value for setting.
1524
+	 */
1525
+	/**
1526
+	 * sets the time on a datetime property
1527
+	 *
1528
+	 * @access protected
1529
+	 * @param string|Datetime $time      a valid time string for php datetime functions (or DateTime object)
1530
+	 * @param string          $fieldname the name of the field the time is being set on (must match a EE_Datetime_Field)
1531
+	 * @throws ReflectionException
1532
+	 * @throws InvalidArgumentException
1533
+	 * @throws InvalidInterfaceException
1534
+	 * @throws InvalidDataTypeException
1535
+	 * @throws EE_Error
1536
+	 */
1537
+	protected function _set_time_for($time, $fieldname)
1538
+	{
1539
+		$this->_set_date_time('T', $time, $fieldname);
1540
+	}
1541
+
1542
+
1543
+	/**
1544
+	 * sets the date on a datetime property
1545
+	 *
1546
+	 * @access protected
1547
+	 * @param string|DateTime $date      a valid date string for php datetime functions ( or DateTime object)
1548
+	 * @param string          $fieldname the name of the field the date is being set on (must match a EE_Datetime_Field)
1549
+	 * @throws ReflectionException
1550
+	 * @throws InvalidArgumentException
1551
+	 * @throws InvalidInterfaceException
1552
+	 * @throws InvalidDataTypeException
1553
+	 * @throws EE_Error
1554
+	 */
1555
+	protected function _set_date_for($date, $fieldname)
1556
+	{
1557
+		$this->_set_date_time('D', $date, $fieldname);
1558
+	}
1559
+
1560
+
1561
+	/**
1562
+	 * This takes care of setting a date or time independently on a given model object property. This method also
1563
+	 * verifies that the given fieldname matches a model object property and is for a EE_Datetime_Field field
1564
+	 *
1565
+	 * @access protected
1566
+	 * @param string          $what           "T" for time, 'B' for both, 'D' for Date.
1567
+	 * @param string|DateTime $datetime_value A valid Date or Time string (or DateTime object)
1568
+	 * @param string          $fieldname      the name of the field the date OR time is being set on (must match a
1569
+	 *                                        EE_Datetime_Field property)
1570
+	 * @throws ReflectionException
1571
+	 * @throws InvalidArgumentException
1572
+	 * @throws InvalidInterfaceException
1573
+	 * @throws InvalidDataTypeException
1574
+	 * @throws EE_Error
1575
+	 */
1576
+	protected function _set_date_time($what = 'T', $datetime_value, $fieldname)
1577
+	{
1578
+		$field = $this->_get_dtt_field_settings($fieldname);
1579
+		$field->set_timezone($this->_timezone);
1580
+		$field->set_date_format($this->_dt_frmt);
1581
+		$field->set_time_format($this->_tm_frmt);
1582
+		switch ($what) {
1583
+			case 'T' :
1584
+				$this->_fields[ $fieldname ] = $field->prepare_for_set_with_new_time(
1585
+					$datetime_value,
1586
+					$this->_fields[ $fieldname ]
1587
+				);
1588
+				break;
1589
+			case 'D' :
1590
+				$this->_fields[ $fieldname ] = $field->prepare_for_set_with_new_date(
1591
+					$datetime_value,
1592
+					$this->_fields[ $fieldname ]
1593
+				);
1594
+				break;
1595
+			case 'B' :
1596
+				$this->_fields[ $fieldname ] = $field->prepare_for_set($datetime_value);
1597
+				break;
1598
+		}
1599
+		$this->_clear_cached_property($fieldname);
1600
+	}
1601
+
1602
+
1603
+	/**
1604
+	 * This will return a timestamp for the website timezone but ONLY when the current website timezone is different
1605
+	 * than the timezone set for the website. NOTE, this currently only works well with methods that return values.  If
1606
+	 * you use it with methods that echo values the $_timestamp property may not get reset to its original value and
1607
+	 * that could lead to some unexpected results!
1608
+	 *
1609
+	 * @access public
1610
+	 * @param string $field_name               This is the name of the field on the object that contains the date/time
1611
+	 *                                         value being returned.
1612
+	 * @param string $callback                 must match a valid method in this class (defaults to get_datetime)
1613
+	 * @param mixed (array|string) $args       This is the arguments that will be passed to the callback.
1614
+	 * @param string $prepend                  You can include something to prepend on the timestamp
1615
+	 * @param string $append                   You can include something to append on the timestamp
1616
+	 * @throws ReflectionException
1617
+	 * @throws InvalidArgumentException
1618
+	 * @throws InvalidInterfaceException
1619
+	 * @throws InvalidDataTypeException
1620
+	 * @throws EE_Error
1621
+	 * @return string timestamp
1622
+	 */
1623
+	public function display_in_my_timezone(
1624
+		$field_name,
1625
+		$callback = 'get_datetime',
1626
+		$args = null,
1627
+		$prepend = '',
1628
+		$append = ''
1629
+	) {
1630
+		$timezone = EEH_DTT_Helper::get_timezone();
1631
+		if ($timezone === $this->_timezone) {
1632
+			return '';
1633
+		}
1634
+		$original_timezone = $this->_timezone;
1635
+		$this->set_timezone($timezone);
1636
+		$fn   = (array) $field_name;
1637
+		$args = array_merge($fn, (array) $args);
1638
+		if (! method_exists($this, $callback)) {
1639
+			throw new EE_Error(
1640
+				sprintf(
1641
+					esc_html__(
1642
+						'The method named "%s" given as the callback param in "display_in_my_timezone" does not exist.  Please check your spelling',
1643
+						'event_espresso'
1644
+					),
1645
+					$callback
1646
+				)
1647
+			);
1648
+		}
1649
+		$args   = (array) $args;
1650
+		$return = $prepend . call_user_func_array(array($this, $callback), $args) . $append;
1651
+		$this->set_timezone($original_timezone);
1652
+		return $return;
1653
+	}
1654
+
1655
+
1656
+	/**
1657
+	 * Deletes this model object.
1658
+	 * This calls the `EE_Base_Class::_delete` method.  Child classes wishing to change default behaviour should
1659
+	 * override
1660
+	 * `EE_Base_Class::_delete` NOT this class.
1661
+	 *
1662
+	 * @return boolean | int
1663
+	 * @throws ReflectionException
1664
+	 * @throws InvalidArgumentException
1665
+	 * @throws InvalidInterfaceException
1666
+	 * @throws InvalidDataTypeException
1667
+	 * @throws EE_Error
1668
+	 */
1669
+	public function delete()
1670
+	{
1671
+		/**
1672
+		 * Called just before the `EE_Base_Class::_delete` method call.
1673
+		 * Note:
1674
+		 * `EE_Base_Class::_delete` might be overridden by child classes so any client code hooking into these actions
1675
+		 * should be aware that `_delete` may not always result in a permanent delete.
1676
+		 * For example, `EE_Soft_Delete_Base_Class::_delete`
1677
+		 * soft deletes (trash) the object and does not permanently delete it.
1678
+		 *
1679
+		 * @param EE_Base_Class $model_object about to be 'deleted'
1680
+		 */
1681
+		do_action('AHEE__EE_Base_Class__delete__before', $this);
1682
+		$result = $this->_delete();
1683
+		/**
1684
+		 * Called just after the `EE_Base_Class::_delete` method call.
1685
+		 * Note:
1686
+		 * `EE_Base_Class::_delete` might be overridden by child classes so any client code hooking into these actions
1687
+		 * should be aware that `_delete` may not always result in a permanent delete.
1688
+		 * For example `EE_Soft_Base_Class::_delete`
1689
+		 * soft deletes (trash) the object and does not permanently delete it.
1690
+		 *
1691
+		 * @param EE_Base_Class $model_object that was just 'deleted'
1692
+		 * @param boolean       $result
1693
+		 */
1694
+		do_action('AHEE__EE_Base_Class__delete__end', $this, $result);
1695
+		return $result;
1696
+	}
1697
+
1698
+
1699
+	/**
1700
+	 * Calls the specific delete method for the instantiated class.
1701
+	 * This method is called by the public `EE_Base_Class::delete` method.  Any child classes desiring to override
1702
+	 * default functionality for "delete" (which is to call `permanently_delete`) should override this method NOT
1703
+	 * `EE_Base_Class::delete`
1704
+	 *
1705
+	 * @return bool|int
1706
+	 * @throws ReflectionException
1707
+	 * @throws InvalidArgumentException
1708
+	 * @throws InvalidInterfaceException
1709
+	 * @throws InvalidDataTypeException
1710
+	 * @throws EE_Error
1711
+	 */
1712
+	protected function _delete()
1713
+	{
1714
+		return $this->delete_permanently();
1715
+	}
1716
+
1717
+
1718
+	/**
1719
+	 * Deletes this model object permanently from db
1720
+	 * (but keep in mind related models may block the delete and return an error)
1721
+	 *
1722
+	 * @return bool | int
1723
+	 * @throws ReflectionException
1724
+	 * @throws InvalidArgumentException
1725
+	 * @throws InvalidInterfaceException
1726
+	 * @throws InvalidDataTypeException
1727
+	 * @throws EE_Error
1728
+	 */
1729
+	public function delete_permanently()
1730
+	{
1731
+		/**
1732
+		 * Called just before HARD deleting a model object
1733
+		 *
1734
+		 * @param EE_Base_Class $model_object about to be 'deleted'
1735
+		 */
1736
+		do_action('AHEE__EE_Base_Class__delete_permanently__before', $this);
1737
+		$model  = $this->get_model();
1738
+		$result = $model->delete_permanently_by_ID($this->ID());
1739
+		$this->refresh_cache_of_related_objects();
1740
+		/**
1741
+		 * Called just after HARD deleting a model object
1742
+		 *
1743
+		 * @param EE_Base_Class $model_object that was just 'deleted'
1744
+		 * @param boolean       $result
1745
+		 */
1746
+		do_action('AHEE__EE_Base_Class__delete_permanently__end', $this, $result);
1747
+		return $result;
1748
+	}
1749
+
1750
+
1751
+	/**
1752
+	 * When this model object is deleted, it may still be cached on related model objects. This clears the cache of
1753
+	 * related model objects
1754
+	 *
1755
+	 * @throws ReflectionException
1756
+	 * @throws InvalidArgumentException
1757
+	 * @throws InvalidInterfaceException
1758
+	 * @throws InvalidDataTypeException
1759
+	 * @throws EE_Error
1760
+	 */
1761
+	public function refresh_cache_of_related_objects()
1762
+	{
1763
+		$model = $this->get_model();
1764
+		foreach ($model->relation_settings() as $relation_name => $relation_obj) {
1765
+			if (! empty($this->_model_relations[ $relation_name ])) {
1766
+				$related_objects = $this->_model_relations[ $relation_name ];
1767
+				if ($relation_obj instanceof EE_Belongs_To_Relation) {
1768
+					//this relation only stores a single model object, not an array
1769
+					//but let's make it consistent
1770
+					$related_objects = array($related_objects);
1771
+				}
1772
+				foreach ($related_objects as $related_object) {
1773
+					//only refresh their cache if they're in memory
1774
+					if ($related_object instanceof EE_Base_Class) {
1775
+						$related_object->clear_cache(
1776
+							$model->get_this_model_name(),
1777
+							$this
1778
+						);
1779
+					}
1780
+				}
1781
+			}
1782
+		}
1783
+	}
1784
+
1785
+
1786
+	/**
1787
+	 *        Saves this object to the database. An array may be supplied to set some values on this
1788
+	 * object just before saving.
1789
+	 *
1790
+	 * @access public
1791
+	 * @param array $set_cols_n_values keys are field names, values are their new values,
1792
+	 *                                 if provided during the save() method (often client code will change the fields'
1793
+	 *                                 values before calling save)
1794
+	 * @throws InvalidArgumentException
1795
+	 * @throws InvalidInterfaceException
1796
+	 * @throws InvalidDataTypeException
1797
+	 * @throws EE_Error
1798
+	 * @return int , 1 on a successful update, the ID of the new entry on insert; 0 on failure or if the model object
1799
+	 *                                 isn't allowed to persist (as determined by EE_Base_Class::allow_persist())
1800
+	 * @throws ReflectionException
1801
+	 * @throws ReflectionException
1802
+	 * @throws ReflectionException
1803
+	 */
1804
+	public function save($set_cols_n_values = array())
1805
+	{
1806
+		$model = $this->get_model();
1807
+		/**
1808
+		 * Filters the fields we're about to save on the model object
1809
+		 *
1810
+		 * @param array         $set_cols_n_values
1811
+		 * @param EE_Base_Class $model_object
1812
+		 */
1813
+		$set_cols_n_values = (array) apply_filters(
1814
+			'FHEE__EE_Base_Class__save__set_cols_n_values',
1815
+			$set_cols_n_values,
1816
+			$this
1817
+		);
1818
+		//set attributes as provided in $set_cols_n_values
1819
+		foreach ($set_cols_n_values as $column => $value) {
1820
+			$this->set($column, $value);
1821
+		}
1822
+		// no changes ? then don't do anything
1823
+		if (! $this->_has_changes && $this->ID() && $model->get_primary_key_field()->is_auto_increment()) {
1824
+			return 0;
1825
+		}
1826
+		/**
1827
+		 * Saving a model object.
1828
+		 * Before we perform a save, this action is fired.
1829
+		 *
1830
+		 * @param EE_Base_Class $model_object the model object about to be saved.
1831
+		 */
1832
+		do_action('AHEE__EE_Base_Class__save__begin', $this);
1833
+		if (! $this->allow_persist()) {
1834
+			return 0;
1835
+		}
1836
+		// now get current attribute values
1837
+		$save_cols_n_values = $this->_fields;
1838
+		// if the object already has an ID, update it. Otherwise, insert it
1839
+		// also: change the assumption about values passed to the model NOT being prepare dby the model object.
1840
+		// They have been
1841
+		$old_assumption_concerning_value_preparation = $model
1842
+			->get_assumption_concerning_values_already_prepared_by_model_object();
1843
+		$model->assume_values_already_prepared_by_model_object(true);
1844
+		//does this model have an autoincrement PK?
1845
+		if ($model->has_primary_key_field()) {
1846
+			if ($model->get_primary_key_field()->is_auto_increment()) {
1847
+				//ok check if it's set, if so: update; if not, insert
1848
+				if (! empty($save_cols_n_values[ $model->primary_key_name() ])) {
1849
+					$results = $model->update_by_ID($save_cols_n_values, $this->ID());
1850
+				} else {
1851
+					unset($save_cols_n_values[ $model->primary_key_name() ]);
1852
+					$results = $model->insert($save_cols_n_values);
1853
+					if ($results) {
1854
+						//if successful, set the primary key
1855
+						//but don't use the normal SET method, because it will check if
1856
+						//an item with the same ID exists in the mapper & db, then
1857
+						//will find it in the db (because we just added it) and THAT object
1858
+						//will get added to the mapper before we can add this one!
1859
+						//but if we just avoid using the SET method, all that headache can be avoided
1860
+						$pk_field_name                   = $model->primary_key_name();
1861
+						$this->_fields[ $pk_field_name ] = $results;
1862
+						$this->_clear_cached_property($pk_field_name);
1863
+						$model->add_to_entity_map($this);
1864
+						$this->_update_cached_related_model_objs_fks();
1865
+					}
1866
+				}
1867
+			} else {//PK is NOT auto-increment
1868
+				//so check if one like it already exists in the db
1869
+				if ($model->exists_by_ID($this->ID())) {
1870
+					if (WP_DEBUG && ! $this->in_entity_map()) {
1871
+						throw new EE_Error(
1872
+							sprintf(
1873
+								esc_html__(
1874
+									'Using a model object %1$s that is NOT in the entity map, can lead to unexpected errors. You should either: %4$s 1. Put it in the entity mapper by calling %2$s %4$s 2. Discard this model object and use what is in the entity mapper %4$s 3. Fetch from the database using %3$s',
1875
+									'event_espresso'
1876
+								),
1877
+								get_class($this),
1878
+								get_class($model) . '::instance()->add_to_entity_map()',
1879
+								get_class($model) . '::instance()->get_one_by_ID()',
1880
+								'<br />'
1881
+							)
1882
+						);
1883
+					}
1884
+					$results = $model->update_by_ID($save_cols_n_values, $this->ID());
1885
+				} else {
1886
+					$results = $model->insert($save_cols_n_values);
1887
+					$this->_update_cached_related_model_objs_fks();
1888
+				}
1889
+			}
1890
+		} else {//there is NO primary key
1891
+			$already_in_db = false;
1892
+			foreach ($model->unique_indexes() as $index) {
1893
+				$uniqueness_where_params = array_intersect_key($save_cols_n_values, $index->fields());
1894
+				if ($model->exists(array($uniqueness_where_params))) {
1895
+					$already_in_db = true;
1896
+				}
1897
+			}
1898
+			if ($already_in_db) {
1899
+				$combined_pk_fields_n_values = array_intersect_key($save_cols_n_values,
1900
+					$model->get_combined_primary_key_fields());
1901
+				$results                     = $model->update(
1902
+					$save_cols_n_values,
1903
+					$combined_pk_fields_n_values
1904
+				);
1905
+			} else {
1906
+				$results = $model->insert($save_cols_n_values);
1907
+			}
1908
+		}
1909
+		//restore the old assumption about values being prepared by the model object
1910
+		$model->assume_values_already_prepared_by_model_object(
1911
+				$old_assumption_concerning_value_preparation
1912
+			);
1913
+		/**
1914
+		 * After saving the model object this action is called
1915
+		 *
1916
+		 * @param EE_Base_Class $model_object which was just saved
1917
+		 * @param boolean|int   $results      if it were updated, TRUE or FALSE; if it were newly inserted
1918
+		 *                                    the new ID (or 0 if an error occurred and it wasn't updated)
1919
+		 */
1920
+		do_action('AHEE__EE_Base_Class__save__end', $this, $results);
1921
+		$this->_has_changes = false;
1922
+		return $results;
1923
+	}
1924
+
1925
+
1926
+	/**
1927
+	 * Updates the foreign key on related models objects pointing to this to have this model object's ID
1928
+	 * as their foreign key.  If the cached related model objects already exist in the db, saves them (so that the DB
1929
+	 * is consistent) Especially useful in case we JUST added this model object ot the database and we want to let its
1930
+	 * cached relations with foreign keys to it know about that change. Eg: we've created a transaction but haven't
1931
+	 * saved it to the db. We also create a registration and don't save it to the DB, but we DO cache it on the
1932
+	 * transaction. Now, when we save the transaction, the registration's TXN_ID will be automatically updated, whether
1933
+	 * or not they exist in the DB (if they do, their DB records will be automatically updated)
1934
+	 *
1935
+	 * @return void
1936
+	 * @throws ReflectionException
1937
+	 * @throws InvalidArgumentException
1938
+	 * @throws InvalidInterfaceException
1939
+	 * @throws InvalidDataTypeException
1940
+	 * @throws EE_Error
1941
+	 */
1942
+	protected function _update_cached_related_model_objs_fks()
1943
+	{
1944
+		$model = $this->get_model();
1945
+		foreach ($model->relation_settings() as $relation_name => $relation_obj) {
1946
+			if ($relation_obj instanceof EE_Has_Many_Relation) {
1947
+				foreach ($this->get_all_from_cache($relation_name) as $related_model_obj_in_cache) {
1948
+					$fk_to_this = $related_model_obj_in_cache->get_model()->get_foreign_key_to(
1949
+						$model->get_this_model_name()
1950
+					);
1951
+					$related_model_obj_in_cache->set($fk_to_this->get_name(), $this->ID());
1952
+					if ($related_model_obj_in_cache->ID()) {
1953
+						$related_model_obj_in_cache->save();
1954
+					}
1955
+				}
1956
+			}
1957
+		}
1958
+	}
1959
+
1960
+
1961
+	/**
1962
+	 * Saves this model object and its NEW cached relations to the database.
1963
+	 * (Meaning, for now, IT DOES NOT WORK if the cached items already exist in the DB.
1964
+	 * In order for that to work, we would need to mark model objects as dirty/clean...
1965
+	 * because otherwise, there's a potential for infinite looping of saving
1966
+	 * Saves the cached related model objects, and ensures the relation between them
1967
+	 * and this object and properly setup
1968
+	 *
1969
+	 * @return int ID of new model object on save; 0 on failure+
1970
+	 * @throws ReflectionException
1971
+	 * @throws InvalidArgumentException
1972
+	 * @throws InvalidInterfaceException
1973
+	 * @throws InvalidDataTypeException
1974
+	 * @throws EE_Error
1975
+	 */
1976
+	public function save_new_cached_related_model_objs()
1977
+	{
1978
+		//make sure this has been saved
1979
+		if (! $this->ID()) {
1980
+			$id = $this->save();
1981
+		} else {
1982
+			$id = $this->ID();
1983
+		}
1984
+		//now save all the NEW cached model objects  (ie they don't exist in the DB)
1985
+		foreach ($this->get_model()->relation_settings() as $relationName => $relationObj) {
1986
+			if ($this->_model_relations[ $relationName ]) {
1987
+				//is this a relation where we should expect just ONE related object (ie, EE_Belongs_To_relation)
1988
+				//or MANY related objects (ie, EE_HABTM_Relation or EE_Has_Many_Relation)?
1989
+				/* @var $related_model_obj EE_Base_Class */
1990
+				if ($relationObj instanceof EE_Belongs_To_Relation) {
1991
+					//add a relation to that relation type (which saves the appropriate thing in the process)
1992
+					//but ONLY if it DOES NOT exist in the DB
1993
+					$related_model_obj = $this->_model_relations[ $relationName ];
1994
+					//					if( ! $related_model_obj->ID()){
1995
+					$this->_add_relation_to($related_model_obj, $relationName);
1996
+					$related_model_obj->save_new_cached_related_model_objs();
1997
+					//					}
1998
+				} else {
1999
+					foreach ($this->_model_relations[ $relationName ] as $related_model_obj) {
2000
+						//add a relation to that relation type (which saves the appropriate thing in the process)
2001
+						//but ONLY if it DOES NOT exist in the DB
2002
+						//						if( ! $related_model_obj->ID()){
2003
+						$this->_add_relation_to($related_model_obj, $relationName);
2004
+						$related_model_obj->save_new_cached_related_model_objs();
2005
+						//						}
2006
+					}
2007
+				}
2008
+			}
2009
+		}
2010
+		return $id;
2011
+	}
2012
+
2013
+
2014
+	/**
2015
+	 * for getting a model while instantiated.
2016
+	 *
2017
+	 * @return EEM_Base | EEM_CPT_Base
2018
+	 * @throws ReflectionException
2019
+	 * @throws InvalidArgumentException
2020
+	 * @throws InvalidInterfaceException
2021
+	 * @throws InvalidDataTypeException
2022
+	 * @throws EE_Error
2023
+	 */
2024
+	public function get_model()
2025
+	{
2026
+		if (! $this->_model) {
2027
+			$modelName    = self::_get_model_classname(get_class($this));
2028
+			$this->_model = self::_get_model_instance_with_name($modelName, $this->_timezone);
2029
+		} else {
2030
+			$this->_model->set_timezone($this->_timezone);
2031
+		}
2032
+		return $this->_model;
2033
+	}
2034
+
2035
+
2036
+	/**
2037
+	 * @param $props_n_values
2038
+	 * @param $classname
2039
+	 * @return mixed bool|EE_Base_Class|EEM_CPT_Base
2040
+	 * @throws ReflectionException
2041
+	 * @throws InvalidArgumentException
2042
+	 * @throws InvalidInterfaceException
2043
+	 * @throws InvalidDataTypeException
2044
+	 * @throws EE_Error
2045
+	 */
2046
+	protected static function _get_object_from_entity_mapper($props_n_values, $classname)
2047
+	{
2048
+		//TODO: will not work for Term_Relationships because they have no PK!
2049
+		$primary_id_ref = self::_get_primary_key_name($classname);
2050
+		if (
2051
+			array_key_exists($primary_id_ref, $props_n_values)
2052
+			&& ! empty($props_n_values[ $primary_id_ref ])
2053
+		) {
2054
+			$id = $props_n_values[ $primary_id_ref ];
2055
+			return self::_get_model($classname)->get_from_entity_map($id);
2056
+		}
2057
+		return false;
2058
+	}
2059
+
2060
+
2061
+	/**
2062
+	 * This is called by child static "new_instance" method and we'll check to see if there is an existing db entry for
2063
+	 * the primary key (if present in incoming values). If there is a key in the incoming array that matches the
2064
+	 * primary key for the model AND it is not null, then we check the db. If there's a an object we return it.  If not
2065
+	 * we return false.
2066
+	 *
2067
+	 * @param  array  $props_n_values   incoming array of properties and their values
2068
+	 * @param  string $classname        the classname of the child class
2069
+	 * @param null    $timezone
2070
+	 * @param array   $date_formats     incoming date_formats in an array where the first value is the
2071
+	 *                                  date_format and the second value is the time format
2072
+	 * @return mixed (EE_Base_Class|bool)
2073
+	 * @throws InvalidArgumentException
2074
+	 * @throws InvalidInterfaceException
2075
+	 * @throws InvalidDataTypeException
2076
+	 * @throws EE_Error
2077
+	 * @throws ReflectionException
2078
+	 * @throws ReflectionException
2079
+	 * @throws ReflectionException
2080
+	 */
2081
+	protected static function _check_for_object($props_n_values, $classname, $timezone = null, $date_formats = array())
2082
+	{
2083
+		$existing = null;
2084
+		$model    = self::_get_model($classname, $timezone);
2085
+		if ($model->has_primary_key_field()) {
2086
+			$primary_id_ref = self::_get_primary_key_name($classname);
2087
+			if (array_key_exists($primary_id_ref, $props_n_values)
2088
+				&& ! empty($props_n_values[ $primary_id_ref ])
2089
+			) {
2090
+				$existing = $model->get_one_by_ID(
2091
+					$props_n_values[ $primary_id_ref ]
2092
+				);
2093
+			}
2094
+		} elseif ($model->has_all_combined_primary_key_fields($props_n_values)) {
2095
+			//no primary key on this model, but there's still a matching item in the DB
2096
+			$existing = self::_get_model($classname, $timezone)->get_one_by_ID(
2097
+				self::_get_model($classname, $timezone)
2098
+					->get_index_primary_key_string($props_n_values)
2099
+			);
2100
+		}
2101
+		if ($existing) {
2102
+			//set date formats if present before setting values
2103
+			if (! empty($date_formats) && is_array($date_formats)) {
2104
+				$existing->set_date_format($date_formats[0]);
2105
+				$existing->set_time_format($date_formats[1]);
2106
+			} else {
2107
+				//set default formats for date and time
2108
+				$existing->set_date_format(get_option('date_format'));
2109
+				$existing->set_time_format(get_option('time_format'));
2110
+			}
2111
+			foreach ($props_n_values as $property => $field_value) {
2112
+				$existing->set($property, $field_value);
2113
+			}
2114
+			return $existing;
2115
+		}
2116
+		return false;
2117
+	}
2118
+
2119
+
2120
+	/**
2121
+	 * Gets the EEM_*_Model for this class
2122
+	 *
2123
+	 * @access public now, as this is more convenient
2124
+	 * @param      $classname
2125
+	 * @param null $timezone
2126
+	 * @throws ReflectionException
2127
+	 * @throws InvalidArgumentException
2128
+	 * @throws InvalidInterfaceException
2129
+	 * @throws InvalidDataTypeException
2130
+	 * @throws EE_Error
2131
+	 * @return EEM_Base
2132
+	 */
2133
+	protected static function _get_model($classname, $timezone = null)
2134
+	{
2135
+		//find model for this class
2136
+		if (! $classname) {
2137
+			throw new EE_Error(
2138
+				sprintf(
2139
+					esc_html__(
2140
+						'What were you thinking calling _get_model(%s)?? You need to specify the class name',
2141
+						'event_espresso'
2142
+					),
2143
+					$classname
2144
+				)
2145
+			);
2146
+		}
2147
+		$modelName = self::_get_model_classname($classname);
2148
+		return self::_get_model_instance_with_name($modelName, $timezone);
2149
+	}
2150
+
2151
+
2152
+	/**
2153
+	 * Gets the model instance (eg instance of EEM_Attendee) given its classname (eg EE_Attendee)
2154
+	 *
2155
+	 * @param string $model_classname
2156
+	 * @param null   $timezone
2157
+	 * @return EEM_Base
2158
+	 * @throws ReflectionException
2159
+	 * @throws InvalidArgumentException
2160
+	 * @throws InvalidInterfaceException
2161
+	 * @throws InvalidDataTypeException
2162
+	 * @throws EE_Error
2163
+	 */
2164
+	protected static function _get_model_instance_with_name($model_classname, $timezone = null)
2165
+	{
2166
+		$model_classname = str_replace('EEM_', '', $model_classname);
2167
+		$model           = EE_Registry::instance()->load_model($model_classname);
2168
+		$model->set_timezone($timezone);
2169
+		return $model;
2170
+	}
2171
+
2172
+
2173
+	/**
2174
+	 * If a model name is provided (eg Registration), gets the model classname for that model.
2175
+	 * Also works if a model class's classname is provided (eg EE_Registration).
2176
+	 *
2177
+	 * @param null $model_name
2178
+	 * @return string like EEM_Attendee
2179
+	 */
2180
+	private static function _get_model_classname($model_name = null)
2181
+	{
2182
+		if (strpos($model_name, 'EE_') === 0) {
2183
+			$model_classname = str_replace('EE_', 'EEM_', $model_name);
2184
+		} else {
2185
+			$model_classname = 'EEM_' . $model_name;
2186
+		}
2187
+		return $model_classname;
2188
+	}
2189
+
2190
+
2191
+	/**
2192
+	 * returns the name of the primary key attribute
2193
+	 *
2194
+	 * @param null $classname
2195
+	 * @throws ReflectionException
2196
+	 * @throws InvalidArgumentException
2197
+	 * @throws InvalidInterfaceException
2198
+	 * @throws InvalidDataTypeException
2199
+	 * @throws EE_Error
2200
+	 * @return string
2201
+	 */
2202
+	protected static function _get_primary_key_name($classname = null)
2203
+	{
2204
+		if (! $classname) {
2205
+			throw new EE_Error(
2206
+				sprintf(
2207
+					esc_html__('What were you thinking calling _get_primary_key_name(%s)', 'event_espresso'),
2208
+					$classname
2209
+				)
2210
+			);
2211
+		}
2212
+		return self::_get_model($classname)->get_primary_key_field()->get_name();
2213
+	}
2214
+
2215
+
2216
+	/**
2217
+	 * Gets the value of the primary key.
2218
+	 * If the object hasn't yet been saved, it should be whatever the model field's default was
2219
+	 * (eg, if this were the EE_Event class, look at the primary key field on EEM_Event and see what its default value
2220
+	 * is. Usually defaults for integer primary keys are 0; string primary keys are usually NULL).
2221
+	 *
2222
+	 * @return mixed, if the primary key is of type INT it'll be an int. Otherwise it could be a string
2223
+	 * @throws ReflectionException
2224
+	 * @throws InvalidArgumentException
2225
+	 * @throws InvalidInterfaceException
2226
+	 * @throws InvalidDataTypeException
2227
+	 * @throws EE_Error
2228
+	 */
2229
+	public function ID()
2230
+	{
2231
+		$model = $this->get_model();
2232
+		//now that we know the name of the variable, use a variable variable to get its value and return its
2233
+		if ($model->has_primary_key_field()) {
2234
+			return $this->_fields[ $model->primary_key_name() ];
2235
+		}
2236
+		return $model->get_index_primary_key_string($this->_fields);
2237
+	}
2238
+
2239
+
2240
+	/**
2241
+	 * Adds a relationship to the specified EE_Base_Class object, given the relationship's name. Eg, if the current
2242
+	 * model is related to a group of events, the $relationName should be 'Event', and should be a key in the EE
2243
+	 * Model's $_model_relations array. If this model object doesn't exist in the DB, just caches the related thing
2244
+	 *
2245
+	 * @param mixed  $otherObjectModelObjectOrID       EE_Base_Class or the ID of the other object
2246
+	 * @param string $relationName                     eg 'Events','Question',etc.
2247
+	 *                                                 an attendee to a group, you also want to specify which role they
2248
+	 *                                                 will have in that group. So you would use this parameter to
2249
+	 *                                                 specify array('role-column-name'=>'role-id')
2250
+	 * @param array  $extra_join_model_fields_n_values You can optionally include an array of key=>value pairs that
2251
+	 *                                                 allow you to further constrict the relation to being added.
2252
+	 *                                                 However, keep in mind that the columns (keys) given must match a
2253
+	 *                                                 column on the JOIN table and currently only the HABTM models
2254
+	 *                                                 accept these additional conditions.  Also remember that if an
2255
+	 *                                                 exact match isn't found for these extra cols/val pairs, then a
2256
+	 *                                                 NEW row is created in the join table.
2257
+	 * @param null   $cache_id
2258
+	 * @throws ReflectionException
2259
+	 * @throws InvalidArgumentException
2260
+	 * @throws InvalidInterfaceException
2261
+	 * @throws InvalidDataTypeException
2262
+	 * @throws EE_Error
2263
+	 * @return EE_Base_Class the object the relation was added to
2264
+	 */
2265
+	public function _add_relation_to(
2266
+		$otherObjectModelObjectOrID,
2267
+		$relationName,
2268
+		$extra_join_model_fields_n_values = array(),
2269
+		$cache_id = null
2270
+	) {
2271
+		$model = $this->get_model();
2272
+		//if this thing exists in the DB, save the relation to the DB
2273
+		if ($this->ID()) {
2274
+			$otherObject = $model->add_relationship_to(
2275
+				$this,
2276
+				$otherObjectModelObjectOrID,
2277
+				$relationName,
2278
+				$extra_join_model_fields_n_values
2279
+			);
2280
+			//clear cache so future get_many_related and get_first_related() return new results.
2281
+			$this->clear_cache($relationName, $otherObject, true);
2282
+			if ($otherObject instanceof EE_Base_Class) {
2283
+				$otherObject->clear_cache($model->get_this_model_name(), $this);
2284
+			}
2285
+		} else {
2286
+			//this thing doesn't exist in the DB,  so just cache it
2287
+			if (! $otherObjectModelObjectOrID instanceof EE_Base_Class) {
2288
+				throw new EE_Error(
2289
+					sprintf(
2290
+						esc_html__(
2291
+							'Before a model object is saved to the database, calls to _add_relation_to must be passed an actual object, not just an ID. You provided %s as the model object to a %s',
2292
+							'event_espresso'
2293
+						),
2294
+						$otherObjectModelObjectOrID,
2295
+						get_class($this)
2296
+					)
2297
+				);
2298
+			}
2299
+			$otherObject = $otherObjectModelObjectOrID;
2300
+			$this->cache($relationName, $otherObjectModelObjectOrID, $cache_id);
2301
+		}
2302
+		if ($otherObject instanceof EE_Base_Class) {
2303
+			//fix the reciprocal relation too
2304
+			if ($otherObject->ID()) {
2305
+				//its saved so assumed relations exist in the DB, so we can just
2306
+				//clear the cache so future queries use the updated info in the DB
2307
+				$otherObject->clear_cache(
2308
+					$model->get_this_model_name(),
2309
+					null,
2310
+					true
2311
+				);
2312
+			} else {
2313
+				//it's not saved, so it caches relations like this
2314
+				$otherObject->cache($model->get_this_model_name(), $this);
2315
+			}
2316
+		}
2317
+		return $otherObject;
2318
+	}
2319
+
2320
+
2321
+	/**
2322
+	 * Removes a relationship to the specified EE_Base_Class object, given the relationships' name. Eg, if the current
2323
+	 * model is related to a group of events, the $relationName should be 'Events', and should be a key in the EE
2324
+	 * Model's $_model_relations array. If this model object doesn't exist in the DB, just removes the related thing
2325
+	 * from the cache
2326
+	 *
2327
+	 * @param mixed  $otherObjectModelObjectOrID
2328
+	 *                EE_Base_Class or the ID of the other object, OR an array key into the cache if this isn't saved
2329
+	 *                to the DB yet
2330
+	 * @param string $relationName
2331
+	 * @param array  $where_query
2332
+	 *                You can optionally include an array of key=>value pairs that allow you to further constrict the
2333
+	 *                relation to being added. However, keep in mind that the columns (keys) given must match a column
2334
+	 *                on the JOIN table and currently only the HABTM models accept these additional conditions. Also
2335
+	 *                remember that if an exact match isn't found for these extra cols/val pairs, then a NEW row is
2336
+	 *                created in the join table.
2337
+	 * @return EE_Base_Class the relation was removed from
2338
+	 * @throws ReflectionException
2339
+	 * @throws InvalidArgumentException
2340
+	 * @throws InvalidInterfaceException
2341
+	 * @throws InvalidDataTypeException
2342
+	 * @throws EE_Error
2343
+	 */
2344
+	public function _remove_relation_to($otherObjectModelObjectOrID, $relationName, $where_query = array())
2345
+	{
2346
+		if ($this->ID()) {
2347
+			//if this exists in the DB, save the relation change to the DB too
2348
+			$otherObject = $this->get_model()->remove_relationship_to(
2349
+				$this,
2350
+				$otherObjectModelObjectOrID,
2351
+				$relationName,
2352
+				$where_query
2353
+			);
2354
+			$this->clear_cache(
2355
+				$relationName,
2356
+				$otherObject
2357
+			);
2358
+		} else {
2359
+			//this doesn't exist in the DB, just remove it from the cache
2360
+			$otherObject = $this->clear_cache(
2361
+				$relationName,
2362
+				$otherObjectModelObjectOrID
2363
+			);
2364
+		}
2365
+		if ($otherObject instanceof EE_Base_Class) {
2366
+			$otherObject->clear_cache(
2367
+				$this->get_model()->get_this_model_name(),
2368
+				$this
2369
+			);
2370
+		}
2371
+		return $otherObject;
2372
+	}
2373
+
2374
+
2375
+	/**
2376
+	 * Removes ALL the related things for the $relationName.
2377
+	 *
2378
+	 * @param string $relationName
2379
+	 * @param array  $where_query_params like EEM_Base::get_all's $query_params[0] (where conditions)
2380
+	 * @return EE_Base_Class
2381
+	 * @throws ReflectionException
2382
+	 * @throws InvalidArgumentException
2383
+	 * @throws InvalidInterfaceException
2384
+	 * @throws InvalidDataTypeException
2385
+	 * @throws EE_Error
2386
+	 */
2387
+	public function _remove_relations($relationName, $where_query_params = array())
2388
+	{
2389
+		if ($this->ID()) {
2390
+			//if this exists in the DB, save the relation change to the DB too
2391
+			$otherObjects = $this->get_model()->remove_relations(
2392
+				$this,
2393
+				$relationName,
2394
+				$where_query_params
2395
+			);
2396
+			$this->clear_cache(
2397
+				$relationName,
2398
+				null,
2399
+				true
2400
+			);
2401
+		} else {
2402
+			//this doesn't exist in the DB, just remove it from the cache
2403
+			$otherObjects = $this->clear_cache(
2404
+				$relationName,
2405
+				null,
2406
+				true
2407
+			);
2408
+		}
2409
+		if (is_array($otherObjects)) {
2410
+			foreach ($otherObjects as $otherObject) {
2411
+				$otherObject->clear_cache(
2412
+					$this->get_model()->get_this_model_name(),
2413
+					$this
2414
+				);
2415
+			}
2416
+		}
2417
+		return $otherObjects;
2418
+	}
2419
+
2420
+
2421
+	/**
2422
+	 * Gets all the related model objects of the specified type. Eg, if the current class if
2423
+	 * EE_Event, you could call $this->get_many_related('Registration') to get an array of all the
2424
+	 * EE_Registration objects which related to this event. Note: by default, we remove the "default query params"
2425
+	 * because we want to get even deleted items etc.
2426
+	 *
2427
+	 * @param string $relationName key in the model's _model_relations array
2428
+	 * @param array  $query_params like EEM_Base::get_all
2429
+	 * @return EE_Base_Class[]     Results not necessarily indexed by IDs, because some results might not have primary
2430
+	 *                             keys or might not be saved yet. Consider using EEM_Base::get_IDs() on these
2431
+	 *                             results if you want IDs
2432
+	 * @throws ReflectionException
2433
+	 * @throws InvalidArgumentException
2434
+	 * @throws InvalidInterfaceException
2435
+	 * @throws InvalidDataTypeException
2436
+	 * @throws EE_Error
2437
+	 */
2438
+	public function get_many_related($relationName, $query_params = array())
2439
+	{
2440
+		if ($this->ID()) {
2441
+			//this exists in the DB, so get the related things from either the cache or the DB
2442
+			//if there are query parameters, forget about caching the related model objects.
2443
+			if ($query_params) {
2444
+				$related_model_objects = $this->get_model()->get_all_related(
2445
+					$this,
2446
+					$relationName,
2447
+					$query_params
2448
+				);
2449
+			} else {
2450
+				//did we already cache the result of this query?
2451
+				$cached_results = $this->get_all_from_cache($relationName);
2452
+				if (! $cached_results) {
2453
+					$related_model_objects = $this->get_model()->get_all_related(
2454
+						$this,
2455
+						$relationName,
2456
+						$query_params
2457
+					);
2458
+					//if no query parameters were passed, then we got all the related model objects
2459
+					//for that relation. We can cache them then.
2460
+					foreach ($related_model_objects as $related_model_object) {
2461
+						$this->cache($relationName, $related_model_object);
2462
+					}
2463
+				} else {
2464
+					$related_model_objects = $cached_results;
2465
+				}
2466
+			}
2467
+		} else {
2468
+			//this doesn't exist in the DB, so just get the related things from the cache
2469
+			$related_model_objects = $this->get_all_from_cache($relationName);
2470
+		}
2471
+		return $related_model_objects;
2472
+	}
2473
+
2474
+
2475
+	/**
2476
+	 * Instead of getting the related model objects, simply counts them. Ignores default_where_conditions by default,
2477
+	 * unless otherwise specified in the $query_params
2478
+	 *
2479
+	 * @param string $relation_name  model_name like 'Event', or 'Registration'
2480
+	 * @param array  $query_params   like EEM_Base::get_all's
2481
+	 * @param string $field_to_count name of field to count by. By default, uses primary key
2482
+	 * @param bool   $distinct       if we want to only count the distinct values for the column then you can trigger
2483
+	 *                               that by the setting $distinct to TRUE;
2484
+	 * @return int
2485
+	 * @throws ReflectionException
2486
+	 * @throws InvalidArgumentException
2487
+	 * @throws InvalidInterfaceException
2488
+	 * @throws InvalidDataTypeException
2489
+	 * @throws EE_Error
2490
+	 */
2491
+	public function count_related($relation_name, $query_params = array(), $field_to_count = null, $distinct = false)
2492
+	{
2493
+		return $this->get_model()->count_related(
2494
+			$this,
2495
+			$relation_name,
2496
+			$query_params,
2497
+			$field_to_count,
2498
+			$distinct
2499
+		);
2500
+	}
2501
+
2502
+
2503
+	/**
2504
+	 * Instead of getting the related model objects, simply sums up the values of the specified field.
2505
+	 * Note: ignores default_where_conditions by default, unless otherwise specified in the $query_params
2506
+	 *
2507
+	 * @param string $relation_name model_name like 'Event', or 'Registration'
2508
+	 * @param array  $query_params  like EEM_Base::get_all's
2509
+	 * @param string $field_to_sum  name of field to count by.
2510
+	 *                              By default, uses primary key
2511
+	 *                              (which doesn't make much sense, so you should probably change it)
2512
+	 * @return int
2513
+	 * @throws ReflectionException
2514
+	 * @throws InvalidArgumentException
2515
+	 * @throws InvalidInterfaceException
2516
+	 * @throws InvalidDataTypeException
2517
+	 * @throws EE_Error
2518
+	 */
2519
+	public function sum_related($relation_name, $query_params = array(), $field_to_sum = null)
2520
+	{
2521
+		return $this->get_model()->sum_related(
2522
+			$this,
2523
+			$relation_name,
2524
+			$query_params,
2525
+			$field_to_sum
2526
+		);
2527
+	}
2528
+
2529
+
2530
+	/**
2531
+	 * Gets the first (ie, one) related model object of the specified type.
2532
+	 *
2533
+	 * @param string $relationName key in the model's _model_relations array
2534
+	 * @param array  $query_params like EEM_Base::get_all
2535
+	 * @return EE_Base_Class (not an array, a single object)
2536
+	 * @throws ReflectionException
2537
+	 * @throws InvalidArgumentException
2538
+	 * @throws InvalidInterfaceException
2539
+	 * @throws InvalidDataTypeException
2540
+	 * @throws EE_Error
2541
+	 */
2542
+	public function get_first_related($relationName, $query_params = array())
2543
+	{
2544
+		$model = $this->get_model();
2545
+		if ($this->ID()) {//this exists in the DB, get from the cache OR the DB
2546
+			//if they've provided some query parameters, don't bother trying to cache the result
2547
+			//also make sure we're not caching the result of get_first_related
2548
+			//on a relation which should have an array of objects (because the cache might have an array of objects)
2549
+			if ($query_params
2550
+				|| ! $model->related_settings_for($relationName)
2551
+					 instanceof
2552
+					 EE_Belongs_To_Relation
2553
+			) {
2554
+				$related_model_object = $model->get_first_related(
2555
+					$this,
2556
+					$relationName,
2557
+					$query_params
2558
+				);
2559
+			} else {
2560
+				//first, check if we've already cached the result of this query
2561
+				$cached_result = $this->get_one_from_cache($relationName);
2562
+				if (! $cached_result) {
2563
+					$related_model_object = $model->get_first_related(
2564
+						$this,
2565
+						$relationName,
2566
+						$query_params
2567
+					);
2568
+					$this->cache($relationName, $related_model_object);
2569
+				} else {
2570
+					$related_model_object = $cached_result;
2571
+				}
2572
+			}
2573
+		} else {
2574
+			$related_model_object = null;
2575
+			// this doesn't exist in the Db,
2576
+			// but maybe the relation is of type belongs to, and so the related thing might
2577
+			if ($model->related_settings_for($relationName) instanceof EE_Belongs_To_Relation) {
2578
+				$related_model_object = $model->get_first_related(
2579
+					$this,
2580
+					$relationName,
2581
+					$query_params
2582
+				);
2583
+			}
2584
+			// this doesn't exist in the DB and apparently the thing it belongs to doesn't either,
2585
+			// just get what's cached on this object
2586
+			if (! $related_model_object) {
2587
+				$related_model_object = $this->get_one_from_cache($relationName);
2588
+			}
2589
+		}
2590
+		return $related_model_object;
2591
+	}
2592
+
2593
+
2594
+	/**
2595
+	 * Does a delete on all related objects of type $relationName and removes
2596
+	 * the current model object's relation to them. If they can't be deleted (because
2597
+	 * of blocking related model objects) does nothing. If the related model objects are
2598
+	 * soft-deletable, they will be soft-deleted regardless of related blocking model objects.
2599
+	 * If this model object doesn't exist yet in the DB, just removes its related things
2600
+	 *
2601
+	 * @param string $relationName
2602
+	 * @param array  $query_params like EEM_Base::get_all's
2603
+	 * @return int how many deleted
2604
+	 * @throws ReflectionException
2605
+	 * @throws InvalidArgumentException
2606
+	 * @throws InvalidInterfaceException
2607
+	 * @throws InvalidDataTypeException
2608
+	 * @throws EE_Error
2609
+	 */
2610
+	public function delete_related($relationName, $query_params = array())
2611
+	{
2612
+		if ($this->ID()) {
2613
+			$count = $this->get_model()->delete_related(
2614
+				$this,
2615
+				$relationName,
2616
+				$query_params
2617
+			);
2618
+		} else {
2619
+			$count = count($this->get_all_from_cache($relationName));
2620
+			$this->clear_cache($relationName, null, true);
2621
+		}
2622
+		return $count;
2623
+	}
2624
+
2625
+
2626
+	/**
2627
+	 * Does a hard delete (ie, removes the DB row) on all related objects of type $relationName and removes
2628
+	 * the current model object's relation to them. If they can't be deleted (because
2629
+	 * of blocking related model objects) just does a soft delete on it instead, if possible.
2630
+	 * If the related thing isn't a soft-deletable model object, this function is identical
2631
+	 * to delete_related(). If this model object doesn't exist in the DB, just remove its related things
2632
+	 *
2633
+	 * @param string $relationName
2634
+	 * @param array  $query_params like EEM_Base::get_all's
2635
+	 * @return int how many deleted (including those soft deleted)
2636
+	 * @throws ReflectionException
2637
+	 * @throws InvalidArgumentException
2638
+	 * @throws InvalidInterfaceException
2639
+	 * @throws InvalidDataTypeException
2640
+	 * @throws EE_Error
2641
+	 */
2642
+	public function delete_related_permanently($relationName, $query_params = array())
2643
+	{
2644
+		if ($this->ID()) {
2645
+			$count = $this->get_model()->delete_related_permanently(
2646
+				$this,
2647
+				$relationName,
2648
+				$query_params
2649
+			);
2650
+		} else {
2651
+			$count = count($this->get_all_from_cache($relationName));
2652
+		}
2653
+		$this->clear_cache($relationName, null, true);
2654
+		return $count;
2655
+	}
2656
+
2657
+
2658
+	/**
2659
+	 * is_set
2660
+	 * Just a simple utility function children can use for checking if property exists
2661
+	 *
2662
+	 * @access  public
2663
+	 * @param  string $field_name property to check
2664
+	 * @return bool                              TRUE if existing,FALSE if not.
2665
+	 */
2666
+	public function is_set($field_name)
2667
+	{
2668
+		return isset($this->_fields[ $field_name ]);
2669
+	}
2670
+
2671
+
2672
+	/**
2673
+	 * Just a simple utility function children can use for checking if property (or properties) exists and throwing an
2674
+	 * EE_Error exception if they don't
2675
+	 *
2676
+	 * @param  mixed (string|array) $properties properties to check
2677
+	 * @throws EE_Error
2678
+	 * @return bool                              TRUE if existing, throw EE_Error if not.
2679
+	 */
2680
+	protected function _property_exists($properties)
2681
+	{
2682
+		foreach ((array) $properties as $property_name) {
2683
+			//first make sure this property exists
2684
+			if (! $this->_fields[ $property_name ]) {
2685
+				throw new EE_Error(
2686
+					sprintf(
2687
+						esc_html__(
2688
+							'Trying to retrieve a non-existent property (%s).  Double check the spelling please',
2689
+							'event_espresso'
2690
+						),
2691
+						$property_name
2692
+					)
2693
+				);
2694
+			}
2695
+		}
2696
+		return true;
2697
+	}
2698
+
2699
+
2700
+	/**
2701
+	 * This simply returns an array of model fields for this object
2702
+	 *
2703
+	 * @return array
2704
+	 * @throws ReflectionException
2705
+	 * @throws InvalidArgumentException
2706
+	 * @throws InvalidInterfaceException
2707
+	 * @throws InvalidDataTypeException
2708
+	 * @throws EE_Error
2709
+	 */
2710
+	public function model_field_array()
2711
+	{
2712
+		$fields     = $this->get_model()->field_settings(false);
2713
+		$properties = array();
2714
+		//remove prepended underscore
2715
+		foreach ($fields as $field_name => $settings) {
2716
+			$properties[ $field_name ] = $this->get($field_name);
2717
+		}
2718
+		return $properties;
2719
+	}
2720
+
2721
+
2722
+	/**
2723
+	 * Very handy general function to allow for plugins to extend any child of EE_Base_Class.
2724
+	 * If a method is called on a child of EE_Base_Class that doesn't exist, this function is called
2725
+	 * (http://www.garfieldtech.com/blog/php-magic-call) and passed the method's name and arguments.
2726
+	 * Instead of requiring a plugin to extend the EE_Base_Class
2727
+	 * (which works fine is there's only 1 plugin, but when will that happen?)
2728
+	 * they can add a hook onto 'filters_hook_espresso__{className}__{methodName}'
2729
+	 * (eg, filters_hook_espresso__EE_Answer__my_great_function)
2730
+	 * and accepts 2 arguments: the object on which the function was called,
2731
+	 * and an array of the original arguments passed to the function.
2732
+	 * Whatever their callback function returns will be returned by this function.
2733
+	 * Example: in functions.php (or in a plugin):
2734
+	 *      add_filter('FHEE__EE_Answer__my_callback','my_callback',10,3);
2735
+	 *      function my_callback($previousReturnValue,EE_Base_Class $object,$argsArray){
2736
+	 *          $returnString= "you called my_callback! and passed args:".implode(",",$argsArray);
2737
+	 *          return $previousReturnValue.$returnString;
2738
+	 *      }
2739
+	 * require('EE_Answer.class.php');
2740
+	 * $answer= EE_Answer::new_instance(array('REG_ID' => 2,'QST_ID' => 3,'ANS_value' => The answer is 42'));
2741
+	 * echo $answer->my_callback('monkeys',100);
2742
+	 * //will output "you called my_callback! and passed args:monkeys,100"
2743
+	 *
2744
+	 * @param string $methodName name of method which was called on a child of EE_Base_Class, but which
2745
+	 * @param array  $args       array of original arguments passed to the function
2746
+	 * @throws EE_Error
2747
+	 * @return mixed whatever the plugin which calls add_filter decides
2748
+	 */
2749
+	public function __call($methodName, $args)
2750
+	{
2751
+		$className = get_class($this);
2752
+		$tagName   = "FHEE__{$className}__{$methodName}";
2753
+		if (! has_filter($tagName)) {
2754
+			throw new EE_Error(
2755
+				sprintf(
2756
+					esc_html__(
2757
+						"Method %s on class %s does not exist! You can create one with the following code in functions.php or in a plugin: add_filter('%s','my_callback',10,3);function my_callback(\$previousReturnValue,EE_Base_Class \$object, \$argsArray){/*function body*/return \$whatever;}",
2758
+						'event_espresso'
2759
+					),
2760
+					$methodName,
2761
+					$className,
2762
+					$tagName
2763
+				)
2764
+			);
2765
+		}
2766
+		return apply_filters($tagName, null, $this, $args);
2767
+	}
2768
+
2769
+
2770
+	/**
2771
+	 * Similar to insert_post_meta, adds a record in the Extra_Meta model's table with the given key and value.
2772
+	 * A $previous_value can be specified in case there are many meta rows with the same key
2773
+	 *
2774
+	 * @param string $meta_key
2775
+	 * @param mixed  $meta_value
2776
+	 * @param mixed  $previous_value
2777
+	 * @return bool|int # of records updated (or BOOLEAN if we actually ended up inserting the extra meta row)
2778
+	 *                  NOTE: if the values haven't changed, returns 0
2779
+	 * @throws InvalidArgumentException
2780
+	 * @throws InvalidInterfaceException
2781
+	 * @throws InvalidDataTypeException
2782
+	 * @throws EE_Error
2783
+	 * @throws ReflectionException
2784
+	 */
2785
+	public function update_extra_meta($meta_key, $meta_value, $previous_value = null)
2786
+	{
2787
+		$query_params = array(
2788
+			array(
2789
+				'EXM_key'  => $meta_key,
2790
+				'OBJ_ID'   => $this->ID(),
2791
+				'EXM_type' => $this->get_model()->get_this_model_name(),
2792
+			),
2793
+		);
2794
+		if ($previous_value !== null) {
2795
+			$query_params[0]['EXM_value'] = $meta_value;
2796
+		}
2797
+		$existing_rows_like_that = EEM_Extra_Meta::instance()->get_all($query_params);
2798
+		if (! $existing_rows_like_that) {
2799
+			return $this->add_extra_meta($meta_key, $meta_value);
2800
+		}
2801
+		foreach ($existing_rows_like_that as $existing_row) {
2802
+			$existing_row->save(array('EXM_value' => $meta_value));
2803
+		}
2804
+		return count($existing_rows_like_that);
2805
+	}
2806
+
2807
+
2808
+	/**
2809
+	 * Adds a new extra meta record. If $unique is set to TRUE, we'll first double-check
2810
+	 * no other extra meta for this model object have the same key. Returns TRUE if the
2811
+	 * extra meta row was entered, false if not
2812
+	 *
2813
+	 * @param string  $meta_key
2814
+	 * @param mixed   $meta_value
2815
+	 * @param boolean $unique
2816
+	 * @return boolean
2817
+	 * @throws InvalidArgumentException
2818
+	 * @throws InvalidInterfaceException
2819
+	 * @throws InvalidDataTypeException
2820
+	 * @throws EE_Error
2821
+	 * @throws ReflectionException
2822
+	 * @throws ReflectionException
2823
+	 */
2824
+	public function add_extra_meta($meta_key, $meta_value, $unique = false)
2825
+	{
2826
+		if ($unique) {
2827
+			$existing_extra_meta = EEM_Extra_Meta::instance()->get_one(
2828
+				array(
2829
+					array(
2830
+						'EXM_key'  => $meta_key,
2831
+						'OBJ_ID'   => $this->ID(),
2832
+						'EXM_type' => $this->get_model()->get_this_model_name(),
2833
+					),
2834
+				)
2835
+			);
2836
+			if ($existing_extra_meta) {
2837
+				return false;
2838
+			}
2839
+		}
2840
+		$new_extra_meta = EE_Extra_Meta::new_instance(
2841
+			array(
2842
+				'EXM_key'   => $meta_key,
2843
+				'EXM_value' => $meta_value,
2844
+				'OBJ_ID'    => $this->ID(),
2845
+				'EXM_type'  => $this->get_model()->get_this_model_name(),
2846
+			)
2847
+		);
2848
+		$new_extra_meta->save();
2849
+		return true;
2850
+	}
2851
+
2852
+
2853
+	/**
2854
+	 * Deletes all the extra meta rows for this record as specified by key. If $meta_value
2855
+	 * is specified, only deletes extra meta records with that value.
2856
+	 *
2857
+	 * @param string $meta_key
2858
+	 * @param mixed  $meta_value
2859
+	 * @return int number of extra meta rows deleted
2860
+	 * @throws InvalidArgumentException
2861
+	 * @throws InvalidInterfaceException
2862
+	 * @throws InvalidDataTypeException
2863
+	 * @throws EE_Error
2864
+	 * @throws ReflectionException
2865
+	 */
2866
+	public function delete_extra_meta($meta_key, $meta_value = null)
2867
+	{
2868
+		$query_params = array(
2869
+			array(
2870
+				'EXM_key'  => $meta_key,
2871
+				'OBJ_ID'   => $this->ID(),
2872
+				'EXM_type' => $this->get_model()->get_this_model_name(),
2873
+			),
2874
+		);
2875
+		if ($meta_value !== null) {
2876
+			$query_params[0]['EXM_value'] = $meta_value;
2877
+		}
2878
+		return EEM_Extra_Meta::instance()->delete($query_params);
2879
+	}
2880
+
2881
+
2882
+	/**
2883
+	 * Gets the extra meta with the given meta key. If you specify "single" we just return 1, otherwise
2884
+	 * an array of everything found. Requires that this model actually have a relation of type EE_Has_Many_Any_Relation.
2885
+	 * You can specify $default is case you haven't found the extra meta
2886
+	 *
2887
+	 * @param string  $meta_key
2888
+	 * @param boolean $single
2889
+	 * @param mixed   $default if we don't find anything, what should we return?
2890
+	 * @return mixed single value if $single; array if ! $single
2891
+	 * @throws ReflectionException
2892
+	 * @throws InvalidArgumentException
2893
+	 * @throws InvalidInterfaceException
2894
+	 * @throws InvalidDataTypeException
2895
+	 * @throws EE_Error
2896
+	 */
2897
+	public function get_extra_meta($meta_key, $single = false, $default = null)
2898
+	{
2899
+		if ($single) {
2900
+			$result = $this->get_first_related(
2901
+				'Extra_Meta',
2902
+				array(array('EXM_key' => $meta_key))
2903
+			);
2904
+			if ($result instanceof EE_Extra_Meta) {
2905
+				return $result->value();
2906
+			}
2907
+		} else {
2908
+			$results = $this->get_many_related(
2909
+				'Extra_Meta',
2910
+				array(array('EXM_key' => $meta_key))
2911
+			);
2912
+			if ($results) {
2913
+				$values = array();
2914
+				foreach ($results as $result) {
2915
+					if ($result instanceof EE_Extra_Meta) {
2916
+						$values[ $result->ID() ] = $result->value();
2917
+					}
2918
+				}
2919
+				return $values;
2920
+			}
2921
+		}
2922
+		//if nothing discovered yet return default.
2923
+		return apply_filters(
2924
+			'FHEE__EE_Base_Class__get_extra_meta__default_value',
2925
+			$default,
2926
+			$meta_key,
2927
+			$single,
2928
+			$this
2929
+		);
2930
+	}
2931
+
2932
+
2933
+	/**
2934
+	 * Returns a simple array of all the extra meta associated with this model object.
2935
+	 * If $one_of_each_key is true (Default), it will be an array of simple key-value pairs, keys being the
2936
+	 * extra meta's key, and teh value being its value. However, if there are duplicate extra meta rows with
2937
+	 * the same key, only one will be used. (eg array('foo'=>'bar','monkey'=>123))
2938
+	 * If $one_of_each_key is false, it will return an array with the top-level keys being
2939
+	 * the extra meta keys, but their values are also arrays, which have the extra-meta's ID as their sub-key, and
2940
+	 * finally the extra meta's value as each sub-value. (eg
2941
+	 * array('foo'=>array(1=>'bar',2=>'bill'),'monkey'=>array(3=>123)))
2942
+	 *
2943
+	 * @param boolean $one_of_each_key
2944
+	 * @return array
2945
+	 * @throws ReflectionException
2946
+	 * @throws InvalidArgumentException
2947
+	 * @throws InvalidInterfaceException
2948
+	 * @throws InvalidDataTypeException
2949
+	 * @throws EE_Error
2950
+	 */
2951
+	public function all_extra_meta_array($one_of_each_key = true)
2952
+	{
2953
+		$return_array = array();
2954
+		if ($one_of_each_key) {
2955
+			$extra_meta_objs = $this->get_many_related(
2956
+				'Extra_Meta',
2957
+				array('group_by' => 'EXM_key')
2958
+			);
2959
+			foreach ($extra_meta_objs as $extra_meta_obj) {
2960
+				if ($extra_meta_obj instanceof EE_Extra_Meta) {
2961
+					$return_array[ $extra_meta_obj->key() ] = $extra_meta_obj->value();
2962
+				}
2963
+			}
2964
+		} else {
2965
+			$extra_meta_objs = $this->get_many_related('Extra_Meta');
2966
+			foreach ($extra_meta_objs as $extra_meta_obj) {
2967
+				if ($extra_meta_obj instanceof EE_Extra_Meta) {
2968
+					if (! isset($return_array[ $extra_meta_obj->key() ])) {
2969
+						$return_array[ $extra_meta_obj->key() ] = array();
2970
+					}
2971
+					$return_array[ $extra_meta_obj->key() ][ $extra_meta_obj->ID() ] = $extra_meta_obj->value();
2972
+				}
2973
+			}
2974
+		}
2975
+		return $return_array;
2976
+	}
2977
+
2978
+
2979
+	/**
2980
+	 * Gets a pretty nice displayable nice for this model object. Often overridden
2981
+	 *
2982
+	 * @return string
2983
+	 * @throws ReflectionException
2984
+	 * @throws InvalidArgumentException
2985
+	 * @throws InvalidInterfaceException
2986
+	 * @throws InvalidDataTypeException
2987
+	 * @throws EE_Error
2988
+	 */
2989
+	public function name()
2990
+	{
2991
+		//find a field that's not a text field
2992
+		$field_we_can_use = $this->get_model()->get_a_field_of_type('EE_Text_Field_Base');
2993
+		if ($field_we_can_use) {
2994
+			return $this->get($field_we_can_use->get_name());
2995
+		}
2996
+		$first_few_properties = $this->model_field_array();
2997
+		$first_few_properties = array_slice($first_few_properties, 0, 3);
2998
+		$name_parts           = array();
2999
+		foreach ($first_few_properties as $name => $value) {
3000
+			$name_parts[] = "$name:$value";
3001
+		}
3002
+		return implode(',', $name_parts);
3003
+	}
3004
+
3005
+
3006
+	/**
3007
+	 * in_entity_map
3008
+	 * Checks if this model object has been proven to already be in the entity map
3009
+	 *
3010
+	 * @return boolean
3011
+	 * @throws ReflectionException
3012
+	 * @throws InvalidArgumentException
3013
+	 * @throws InvalidInterfaceException
3014
+	 * @throws InvalidDataTypeException
3015
+	 * @throws EE_Error
3016
+	 */
3017
+	public function in_entity_map()
3018
+	{
3019
+		// well, if we looked, did we find it in the entity map?
3020
+		return $this->ID() && $this->get_model()->get_from_entity_map($this->ID()) === $this;
3021
+	}
3022
+
3023
+
3024
+	/**
3025
+	 * refresh_from_db
3026
+	 * Makes sure the fields and values on this model object are in-sync with what's in the database.
3027
+	 *
3028
+	 * @throws ReflectionException
3029
+	 * @throws InvalidArgumentException
3030
+	 * @throws InvalidInterfaceException
3031
+	 * @throws InvalidDataTypeException
3032
+	 * @throws EE_Error if this model object isn't in the entity mapper (because then you should
3033
+	 * just use what's in the entity mapper and refresh it) and WP_DEBUG is TRUE
3034
+	 */
3035
+	public function refresh_from_db()
3036
+	{
3037
+		if ($this->ID() && $this->in_entity_map()) {
3038
+			$this->get_model()->refresh_entity_map_from_db($this->ID());
3039
+		} else {
3040
+			//if it doesn't have ID, you shouldn't be asking to refresh it from teh database (because its not in the database)
3041
+			//if it has an ID but it's not in the map, and you're asking me to refresh it
3042
+			//that's kinda dangerous. You should just use what's in the entity map, or add this to the entity map if there's
3043
+			//absolutely nothing in it for this ID
3044
+			if (WP_DEBUG) {
3045
+				throw new EE_Error(
3046
+					sprintf(
3047
+						esc_html__('Trying to refresh a model object with ID "%1$s" that\'s not in the entity map? First off: you should put it in the entity map by calling %2$s. Second off, if you want what\'s in the database right now, you should just call %3$s yourself and discard this model object.',
3048
+							'event_espresso'),
3049
+						$this->ID(),
3050
+						get_class($this->get_model()) . '::instance()->add_to_entity_map()',
3051
+						get_class($this->get_model()) . '::instance()->refresh_entity_map()'
3052
+					)
3053
+				);
3054
+			}
3055
+		}
3056
+	}
3057
+
3058
+
3059
+	/**
3060
+	 * Because some other plugins, like Advanced Cron Manager, expect all objects to have this method
3061
+	 * (probably a bad assumption they have made, oh well)
3062
+	 *
3063
+	 * @return string
3064
+	 */
3065
+	public function __toString()
3066
+	{
3067
+		try {
3068
+			return sprintf('%s (%s)', $this->name(), $this->ID());
3069
+		} catch (Exception $e) {
3070
+			EE_Error::add_error($e->getMessage(), __FILE__, __FUNCTION__, __LINE__);
3071
+			return '';
3072
+		}
3073
+	}
3074
+
3075
+
3076
+	/**
3077
+	 * Clear related model objects if they're already in the DB, because otherwise when we
3078
+	 * UN-serialize this model object we'll need to be careful to add them to the entity map.
3079
+	 * This means if we have made changes to those related model objects, and want to unserialize
3080
+	 * the this model object on a subsequent request, changes to those related model objects will be lost.
3081
+	 * Instead, those related model objects should be directly serialized and stored.
3082
+	 * Eg, the following won't work:
3083
+	 * $reg = EEM_Registration::instance()->get_one_by_ID( 123 );
3084
+	 * $att = $reg->attendee();
3085
+	 * $att->set( 'ATT_fname', 'Dirk' );
3086
+	 * update_option( 'my_option', serialize( $reg ) );
3087
+	 * //END REQUEST
3088
+	 * //START NEXT REQUEST
3089
+	 * $reg = get_option( 'my_option' );
3090
+	 * $reg->attendee()->save();
3091
+	 * And would need to be replace with:
3092
+	 * $reg = EEM_Registration::instance()->get_one_by_ID( 123 );
3093
+	 * $att = $reg->attendee();
3094
+	 * $att->set( 'ATT_fname', 'Dirk' );
3095
+	 * update_option( 'my_option', serialize( $reg ) );
3096
+	 * //END REQUEST
3097
+	 * //START NEXT REQUEST
3098
+	 * $att = get_option( 'my_option' );
3099
+	 * $att->save();
3100
+	 *
3101
+	 * @return array
3102
+	 * @throws ReflectionException
3103
+	 * @throws InvalidArgumentException
3104
+	 * @throws InvalidInterfaceException
3105
+	 * @throws InvalidDataTypeException
3106
+	 * @throws EE_Error
3107
+	 */
3108
+	public function __sleep()
3109
+	{
3110
+		$model = $this->get_model();
3111
+		foreach ($model->relation_settings() as $relation_name => $relation_obj) {
3112
+			if ($relation_obj instanceof EE_Belongs_To_Relation) {
3113
+				$classname = 'EE_' . $model->get_this_model_name();
3114
+				if (
3115
+					$this->get_one_from_cache($relation_name) instanceof $classname
3116
+					&& $this->get_one_from_cache($relation_name)->ID()
3117
+				) {
3118
+					$this->clear_cache(
3119
+						$relation_name,
3120
+						$this->get_one_from_cache($relation_name)->ID()
3121
+					);
3122
+				}
3123
+			}
3124
+		}
3125
+		$this->_props_n_values_provided_in_constructor = array();
3126
+		$properties_to_serialize                       = get_object_vars($this);
3127
+		//don't serialize the model. It's big and that risks recursion
3128
+		unset($properties_to_serialize['_model']);
3129
+		return array_keys($properties_to_serialize);
3130
+	}
3131
+
3132
+
3133
+	/**
3134
+	 * restore _props_n_values_provided_in_constructor
3135
+	 * PLZ NOTE: this will reset the array to whatever fields values were present prior to serialization,
3136
+	 * and therefore should NOT be used to determine if state change has occurred since initial construction.
3137
+	 * At best, you would only be able to detect if state change has occurred during THIS request.
3138
+	 */
3139
+	public function __wakeup()
3140
+	{
3141
+		$this->_props_n_values_provided_in_constructor = $this->_fields;
3142
+	}
3143 3143
 }
3144 3144
 
3145 3145
 
Please login to merge, or discard this patch.
Spacing   +117 added lines, -117 removed lines patch added patch discarded remove patch
@@ -148,7 +148,7 @@  discard block
 block discarded – undo
148 148
         $fieldValues = is_array($fieldValues) ? $fieldValues : array($fieldValues);
149 149
         // verify client code has not passed any invalid field names
150 150
         foreach ($fieldValues as $field_name => $field_value) {
151
-            if (! isset($model_fields[ $field_name ])) {
151
+            if ( ! isset($model_fields[$field_name])) {
152 152
                 throw new EE_Error(
153 153
                     sprintf(
154 154
                         esc_html__(
@@ -163,7 +163,7 @@  discard block
 block discarded – undo
163 163
             }
164 164
         }
165 165
         $this->_timezone = EEH_DTT_Helper::get_valid_timezone_string($timezone);
166
-        if (! empty($date_formats) && is_array($date_formats)) {
166
+        if ( ! empty($date_formats) && is_array($date_formats)) {
167 167
             list($this->_dt_frmt, $this->_tm_frmt) = $date_formats;
168 168
         } else {
169 169
             //set default formats for date and time
@@ -176,7 +176,7 @@  discard block
 block discarded – undo
176 176
             foreach ($model_fields as $fieldName => $field) {
177 177
                 $this->set_from_db(
178 178
                     $fieldName,
179
-                    isset($fieldValues[ $fieldName ]) ? $fieldValues[ $fieldName ] : null
179
+                    isset($fieldValues[$fieldName]) ? $fieldValues[$fieldName] : null
180 180
                 );
181 181
             }
182 182
         } else {
@@ -185,22 +185,22 @@  discard block
 block discarded – undo
185 185
             foreach ($model_fields as $fieldName => $field) {
186 186
                 $this->set(
187 187
                     $fieldName,
188
-                    isset($fieldValues[ $fieldName ]) ? $fieldValues[ $fieldName ] : null, true
188
+                    isset($fieldValues[$fieldName]) ? $fieldValues[$fieldName] : null, true
189 189
                 );
190 190
             }
191 191
         }
192 192
         //remember what values were passed to this constructor
193 193
         $this->_props_n_values_provided_in_constructor = $fieldValues;
194 194
         //remember in entity mapper
195
-        if (! $bydb && $model->has_primary_key_field() && $this->ID()) {
195
+        if ( ! $bydb && $model->has_primary_key_field() && $this->ID()) {
196 196
             $model->add_to_entity_map($this);
197 197
         }
198 198
         //setup all the relations
199 199
         foreach ($model->relation_settings() as $relation_name => $relation_obj) {
200 200
             if ($relation_obj instanceof EE_Belongs_To_Relation) {
201
-                $this->_model_relations[ $relation_name ] = null;
201
+                $this->_model_relations[$relation_name] = null;
202 202
             } else {
203
-                $this->_model_relations[ $relation_name ] = array();
203
+                $this->_model_relations[$relation_name] = array();
204 204
             }
205 205
         }
206 206
         /**
@@ -251,10 +251,10 @@  discard block
 block discarded – undo
251 251
      */
252 252
     public function get_original($field_name)
253 253
     {
254
-        if (isset($this->_props_n_values_provided_in_constructor[ $field_name ])
254
+        if (isset($this->_props_n_values_provided_in_constructor[$field_name])
255 255
             && $field_settings = $this->get_model()->field_settings_for($field_name)
256 256
         ) {
257
-            return $field_settings->prepare_for_get($this->_props_n_values_provided_in_constructor[ $field_name ]);
257
+            return $field_settings->prepare_for_get($this->_props_n_values_provided_in_constructor[$field_name]);
258 258
         }
259 259
         return null;
260 260
     }
@@ -291,7 +291,7 @@  discard block
 block discarded – undo
291 291
         // then don't do anything
292 292
         if (
293 293
             ! $use_default
294
-            && $this->_fields[ $field_name ] === $field_value
294
+            && $this->_fields[$field_name] === $field_value
295 295
             && $this->ID()
296 296
         ) {
297 297
             return;
@@ -309,7 +309,7 @@  discard block
 block discarded – undo
309 309
             $holder_of_value = $field_obj->prepare_for_set($field_value);
310 310
             //should the value be null?
311 311
             if (($field_value === null || $holder_of_value === null || $holder_of_value === '') && $use_default) {
312
-                $this->_fields[ $field_name ] = $field_obj->get_default_value();
312
+                $this->_fields[$field_name] = $field_obj->get_default_value();
313 313
                 /**
314 314
                  * To save having to refactor all the models, if a default value is used for a
315 315
                  * EE_Datetime_Field, and that value is not null nor is it a DateTime
@@ -320,15 +320,15 @@  discard block
 block discarded – undo
320 320
                  */
321 321
                 if (
322 322
                     $field_obj instanceof EE_Datetime_Field
323
-                    && $this->_fields[ $field_name ] !== null
324
-                    && ! $this->_fields[ $field_name ] instanceof DateTime
323
+                    && $this->_fields[$field_name] !== null
324
+                    && ! $this->_fields[$field_name] instanceof DateTime
325 325
                 ) {
326
-                    empty($this->_fields[ $field_name ])
326
+                    empty($this->_fields[$field_name])
327 327
                         ? $this->set($field_name, time())
328
-                        : $this->set($field_name, $this->_fields[ $field_name ]);
328
+                        : $this->set($field_name, $this->_fields[$field_name]);
329 329
                 }
330 330
             } else {
331
-                $this->_fields[ $field_name ] = $holder_of_value;
331
+                $this->_fields[$field_name] = $holder_of_value;
332 332
             }
333 333
             //if we're not in the constructor...
334 334
             //now check if what we set was a primary key
@@ -345,7 +345,7 @@  discard block
 block discarded – undo
345 345
                 $fields_on_model = self::_get_model(get_class($this))->field_settings();
346 346
                 $obj_in_db       = self::_get_model(get_class($this))->get_one_by_ID($field_value);
347 347
                 foreach ($fields_on_model as $field_obj) {
348
-                    if (! array_key_exists($field_obj->get_name(), $this->_props_n_values_provided_in_constructor)
348
+                    if ( ! array_key_exists($field_obj->get_name(), $this->_props_n_values_provided_in_constructor)
349 349
                         && $field_obj->get_name() !== $field_name
350 350
                     ) {
351 351
                         $this->set($field_obj->get_name(), $obj_in_db->get($field_obj->get_name()));
@@ -390,8 +390,8 @@  discard block
 block discarded – undo
390 390
      */
391 391
     public function getCustomSelect($alias)
392 392
     {
393
-        return isset($this->custom_selection_results[ $alias ])
394
-            ? $this->custom_selection_results[ $alias ]
393
+        return isset($this->custom_selection_results[$alias])
394
+            ? $this->custom_selection_results[$alias]
395 395
             : null;
396 396
     }
397 397
 
@@ -478,8 +478,8 @@  discard block
 block discarded – undo
478 478
         foreach ($model_fields as $field_name => $field_obj) {
479 479
             if ($field_obj instanceof EE_Datetime_Field) {
480 480
                 $field_obj->set_timezone($this->_timezone);
481
-                if (isset($this->_fields[ $field_name ]) && $this->_fields[ $field_name ] instanceof DateTime) {
482
-                    $this->_fields[ $field_name ]->setTimezone(new DateTimeZone($this->_timezone));
481
+                if (isset($this->_fields[$field_name]) && $this->_fields[$field_name] instanceof DateTime) {
482
+                    $this->_fields[$field_name]->setTimezone(new DateTimeZone($this->_timezone));
483 483
                 }
484 484
             }
485 485
         }
@@ -537,7 +537,7 @@  discard block
 block discarded – undo
537 537
      */
538 538
     public function get_format($full = true)
539 539
     {
540
-        return $full ? $this->_dt_frmt . ' ' . $this->_tm_frmt : array($this->_dt_frmt, $this->_tm_frmt);
540
+        return $full ? $this->_dt_frmt.' '.$this->_tm_frmt : array($this->_dt_frmt, $this->_tm_frmt);
541 541
     }
542 542
 
543 543
 
@@ -563,11 +563,11 @@  discard block
 block discarded – undo
563 563
     public function cache($relationName = '', $object_to_cache = null, $cache_id = null)
564 564
     {
565 565
         // its entirely possible that there IS no related object yet in which case there is nothing to cache.
566
-        if (! $object_to_cache instanceof EE_Base_Class) {
566
+        if ( ! $object_to_cache instanceof EE_Base_Class) {
567 567
             return false;
568 568
         }
569 569
         // also get "how" the object is related, or throw an error
570
-        if (! $relationship_to_model = $this->get_model()->related_settings_for($relationName)) {
570
+        if ( ! $relationship_to_model = $this->get_model()->related_settings_for($relationName)) {
571 571
             throw new EE_Error(
572 572
                 sprintf(
573 573
                     esc_html__('There is no relationship to %s on a %s. Cannot cache it', 'event_espresso'),
@@ -581,38 +581,38 @@  discard block
 block discarded – undo
581 581
             // if it's a "belongs to" relationship, then there's only one related model object
582 582
             // eg, if this is a registration, there's only 1 attendee for it
583 583
             // so for these model objects just set it to be cached
584
-            $this->_model_relations[ $relationName ] = $object_to_cache;
584
+            $this->_model_relations[$relationName] = $object_to_cache;
585 585
             $return                                  = true;
586 586
         } else {
587 587
             // otherwise, this is the "many" side of a one to many relationship,
588 588
             // so we'll add the object to the array of related objects for that type.
589 589
             // eg: if this is an event, there are many registrations for that event,
590 590
             // so we cache the registrations in an array
591
-            if (! is_array($this->_model_relations[ $relationName ])) {
591
+            if ( ! is_array($this->_model_relations[$relationName])) {
592 592
                 // if for some reason, the cached item is a model object,
593 593
                 // then stick that in the array, otherwise start with an empty array
594
-                $this->_model_relations[ $relationName ] = $this->_model_relations[ $relationName ]
594
+                $this->_model_relations[$relationName] = $this->_model_relations[$relationName]
595 595
                                                            instanceof
596 596
                                                            EE_Base_Class
597
-                    ? array($this->_model_relations[ $relationName ]) : array();
597
+                    ? array($this->_model_relations[$relationName]) : array();
598 598
             }
599 599
             // first check for a cache_id which is normally empty
600
-            if (! empty($cache_id)) {
600
+            if ( ! empty($cache_id)) {
601 601
                 // if the cache_id exists, then it means we are purposely trying to cache this
602 602
                 // with a known key that can then be used to retrieve the object later on
603
-                $this->_model_relations[ $relationName ][ $cache_id ] = $object_to_cache;
603
+                $this->_model_relations[$relationName][$cache_id] = $object_to_cache;
604 604
                 $return                                               = $cache_id;
605 605
             } elseif ($object_to_cache->ID()) {
606 606
                 // OR the cached object originally came from the db, so let's just use it's PK for an ID
607
-                $this->_model_relations[ $relationName ][ $object_to_cache->ID() ] = $object_to_cache;
607
+                $this->_model_relations[$relationName][$object_to_cache->ID()] = $object_to_cache;
608 608
                 $return                                                            = $object_to_cache->ID();
609 609
             } else {
610 610
                 // OR it's a new object with no ID, so just throw it in the array with an auto-incremented ID
611
-                $this->_model_relations[ $relationName ][] = $object_to_cache;
611
+                $this->_model_relations[$relationName][] = $object_to_cache;
612 612
                 // move the internal pointer to the end of the array
613
-                end($this->_model_relations[ $relationName ]);
613
+                end($this->_model_relations[$relationName]);
614 614
                 // and grab the key so that we can return it
615
-                $return = key($this->_model_relations[ $relationName ]);
615
+                $return = key($this->_model_relations[$relationName]);
616 616
             }
617 617
         }
618 618
         return $return;
@@ -638,7 +638,7 @@  discard block
 block discarded – undo
638 638
         //first make sure this property exists
639 639
         $this->get_model()->field_settings_for($fieldname);
640 640
         $cache_type                                            = empty($cache_type) ? 'standard' : $cache_type;
641
-        $this->_cached_properties[ $fieldname ][ $cache_type ] = $value;
641
+        $this->_cached_properties[$fieldname][$cache_type] = $value;
642 642
     }
643 643
 
644 644
 
@@ -667,9 +667,9 @@  discard block
 block discarded – undo
667 667
         $model = $this->get_model();
668 668
         $model->field_settings_for($fieldname);
669 669
         $cache_type = $pretty ? 'pretty' : 'standard';
670
-        $cache_type .= ! empty($extra_cache_ref) ? '_' . $extra_cache_ref : '';
671
-        if (isset($this->_cached_properties[ $fieldname ][ $cache_type ])) {
672
-            return $this->_cached_properties[ $fieldname ][ $cache_type ];
670
+        $cache_type .= ! empty($extra_cache_ref) ? '_'.$extra_cache_ref : '';
671
+        if (isset($this->_cached_properties[$fieldname][$cache_type])) {
672
+            return $this->_cached_properties[$fieldname][$cache_type];
673 673
         }
674 674
         $value = $this->_get_fresh_property($fieldname, $pretty, $extra_cache_ref);
675 675
         $this->_set_cached_property($fieldname, $value, $cache_type);
@@ -697,12 +697,12 @@  discard block
 block discarded – undo
697 697
         if ($field_obj instanceof EE_Datetime_Field) {
698 698
             $this->_prepare_datetime_field($field_obj, $pretty, $extra_cache_ref);
699 699
         }
700
-        if (! isset($this->_fields[ $fieldname ])) {
701
-            $this->_fields[ $fieldname ] = null;
700
+        if ( ! isset($this->_fields[$fieldname])) {
701
+            $this->_fields[$fieldname] = null;
702 702
         }
703 703
         $value = $pretty
704
-            ? $field_obj->prepare_for_pretty_echoing($this->_fields[ $fieldname ], $extra_cache_ref)
705
-            : $field_obj->prepare_for_get($this->_fields[ $fieldname ]);
704
+            ? $field_obj->prepare_for_pretty_echoing($this->_fields[$fieldname], $extra_cache_ref)
705
+            : $field_obj->prepare_for_get($this->_fields[$fieldname]);
706 706
         return $value;
707 707
     }
708 708
 
@@ -760,8 +760,8 @@  discard block
 block discarded – undo
760 760
      */
761 761
     protected function _clear_cached_property($property_name)
762 762
     {
763
-        if (isset($this->_cached_properties[ $property_name ])) {
764
-            unset($this->_cached_properties[ $property_name ]);
763
+        if (isset($this->_cached_properties[$property_name])) {
764
+            unset($this->_cached_properties[$property_name]);
765 765
         }
766 766
     }
767 767
 
@@ -813,7 +813,7 @@  discard block
 block discarded – undo
813 813
     {
814 814
         $relationship_to_model = $this->get_model()->related_settings_for($relationName);
815 815
         $index_in_cache        = '';
816
-        if (! $relationship_to_model) {
816
+        if ( ! $relationship_to_model) {
817 817
             throw new EE_Error(
818 818
                 sprintf(
819 819
                     esc_html__('There is no relationship to %s on a %s. Cannot clear that cache', 'event_espresso'),
@@ -824,21 +824,21 @@  discard block
 block discarded – undo
824 824
         }
825 825
         if ($clear_all) {
826 826
             $obj_removed                             = true;
827
-            $this->_model_relations[ $relationName ] = null;
827
+            $this->_model_relations[$relationName] = null;
828 828
         } elseif ($relationship_to_model instanceof EE_Belongs_To_Relation) {
829
-            $obj_removed                             = $this->_model_relations[ $relationName ];
830
-            $this->_model_relations[ $relationName ] = null;
829
+            $obj_removed                             = $this->_model_relations[$relationName];
830
+            $this->_model_relations[$relationName] = null;
831 831
         } else {
832 832
             if ($object_to_remove_or_index_into_array instanceof EE_Base_Class
833 833
                 && $object_to_remove_or_index_into_array->ID()
834 834
             ) {
835 835
                 $index_in_cache = $object_to_remove_or_index_into_array->ID();
836
-                if (is_array($this->_model_relations[ $relationName ])
837
-                    && ! isset($this->_model_relations[ $relationName ][ $index_in_cache ])
836
+                if (is_array($this->_model_relations[$relationName])
837
+                    && ! isset($this->_model_relations[$relationName][$index_in_cache])
838 838
                 ) {
839 839
                     $index_found_at = null;
840 840
                     //find this object in the array even though it has a different key
841
-                    foreach ($this->_model_relations[ $relationName ] as $index => $obj) {
841
+                    foreach ($this->_model_relations[$relationName] as $index => $obj) {
842 842
                         /** @noinspection TypeUnsafeComparisonInspection */
843 843
                         if (
844 844
                             $obj instanceof EE_Base_Class
@@ -872,9 +872,9 @@  discard block
 block discarded – undo
872 872
             }
873 873
             //supposedly we've found it. But it could just be that the client code
874 874
             //provided a bad index/object
875
-            if (isset($this->_model_relations[ $relationName ][ $index_in_cache ])) {
876
-                $obj_removed = $this->_model_relations[ $relationName ][ $index_in_cache ];
877
-                unset($this->_model_relations[ $relationName ][ $index_in_cache ]);
875
+            if (isset($this->_model_relations[$relationName][$index_in_cache])) {
876
+                $obj_removed = $this->_model_relations[$relationName][$index_in_cache];
877
+                unset($this->_model_relations[$relationName][$index_in_cache]);
878 878
             } else {
879 879
                 //that thing was never cached anyways.
880 880
                 $obj_removed = null;
@@ -905,7 +905,7 @@  discard block
 block discarded – undo
905 905
         $current_cache_id = ''
906 906
     ) {
907 907
         // verify that incoming object is of the correct type
908
-        $obj_class = 'EE_' . $relationName;
908
+        $obj_class = 'EE_'.$relationName;
909 909
         if ($newly_saved_object instanceof $obj_class) {
910 910
             /* @type EE_Base_Class $newly_saved_object */
911 911
             // now get the type of relation
@@ -913,17 +913,17 @@  discard block
 block discarded – undo
913 913
             // if this is a 1:1 relationship
914 914
             if ($relationship_to_model instanceof EE_Belongs_To_Relation) {
915 915
                 // then just replace the cached object with the newly saved object
916
-                $this->_model_relations[ $relationName ] = $newly_saved_object;
916
+                $this->_model_relations[$relationName] = $newly_saved_object;
917 917
                 return true;
918 918
                 // or if it's some kind of sordid feral polyamorous relationship...
919 919
             }
920
-            if (is_array($this->_model_relations[ $relationName ])
921
-                      && isset($this->_model_relations[ $relationName ][ $current_cache_id ])
920
+            if (is_array($this->_model_relations[$relationName])
921
+                      && isset($this->_model_relations[$relationName][$current_cache_id])
922 922
             ) {
923 923
                 // then remove the current cached item
924
-                unset($this->_model_relations[ $relationName ][ $current_cache_id ]);
924
+                unset($this->_model_relations[$relationName][$current_cache_id]);
925 925
                 // and cache the newly saved object using it's new ID
926
-                $this->_model_relations[ $relationName ][ $newly_saved_object->ID() ] = $newly_saved_object;
926
+                $this->_model_relations[$relationName][$newly_saved_object->ID()] = $newly_saved_object;
927 927
                 return true;
928 928
             }
929 929
         }
@@ -940,8 +940,8 @@  discard block
 block discarded – undo
940 940
      */
941 941
     public function get_one_from_cache($relationName)
942 942
     {
943
-        $cached_array_or_object = isset($this->_model_relations[ $relationName ])
944
-            ? $this->_model_relations[ $relationName ]
943
+        $cached_array_or_object = isset($this->_model_relations[$relationName])
944
+            ? $this->_model_relations[$relationName]
945 945
             : null;
946 946
         if (is_array($cached_array_or_object)) {
947 947
             return array_shift($cached_array_or_object);
@@ -964,7 +964,7 @@  discard block
 block discarded – undo
964 964
      */
965 965
     public function get_all_from_cache($relationName)
966 966
     {
967
-        $objects = isset($this->_model_relations[ $relationName ]) ? $this->_model_relations[ $relationName ] : array();
967
+        $objects = isset($this->_model_relations[$relationName]) ? $this->_model_relations[$relationName] : array();
968 968
         // if the result is not an array, but exists, make it an array
969 969
         $objects = is_array($objects) ? $objects : array($objects);
970 970
         //bugfix for https://events.codebasehq.com/projects/event-espresso/tickets/7143
@@ -1148,7 +1148,7 @@  discard block
 block discarded – undo
1148 1148
             } else {
1149 1149
                 $field_value = $field_obj->prepare_for_set_from_db($field_value_from_db);
1150 1150
             }
1151
-            $this->_fields[ $field_name ] = $field_value;
1151
+            $this->_fields[$field_name] = $field_value;
1152 1152
             $this->_clear_cached_property($field_name);
1153 1153
         }
1154 1154
     }
@@ -1188,9 +1188,9 @@  discard block
 block discarded – undo
1188 1188
     public function get_raw($field_name)
1189 1189
     {
1190 1190
         $field_settings = $this->get_model()->field_settings_for($field_name);
1191
-        return $field_settings instanceof EE_Datetime_Field && $this->_fields[ $field_name ] instanceof DateTime
1192
-            ? $this->_fields[ $field_name ]->format('U')
1193
-            : $this->_fields[ $field_name ];
1191
+        return $field_settings instanceof EE_Datetime_Field && $this->_fields[$field_name] instanceof DateTime
1192
+            ? $this->_fields[$field_name]->format('U')
1193
+            : $this->_fields[$field_name];
1194 1194
     }
1195 1195
 
1196 1196
 
@@ -1213,7 +1213,7 @@  discard block
 block discarded – undo
1213 1213
     public function get_DateTime_object($field_name)
1214 1214
     {
1215 1215
         $field_settings = $this->get_model()->field_settings_for($field_name);
1216
-        if (! $field_settings instanceof EE_Datetime_Field) {
1216
+        if ( ! $field_settings instanceof EE_Datetime_Field) {
1217 1217
             EE_Error::add_error(
1218 1218
                 sprintf(
1219 1219
                     esc_html__(
@@ -1228,7 +1228,7 @@  discard block
 block discarded – undo
1228 1228
             );
1229 1229
             return false;
1230 1230
         }
1231
-        return $this->_fields[ $field_name ];
1231
+        return $this->_fields[$field_name];
1232 1232
     }
1233 1233
 
1234 1234
 
@@ -1469,7 +1469,7 @@  discard block
 block discarded – undo
1469 1469
      */
1470 1470
     public function get_i18n_datetime($field_name, $format = '')
1471 1471
     {
1472
-        $format = empty($format) ? $this->_dt_frmt . ' ' . $this->_tm_frmt : $format;
1472
+        $format = empty($format) ? $this->_dt_frmt.' '.$this->_tm_frmt : $format;
1473 1473
         return date_i18n(
1474 1474
             $format,
1475 1475
             EEH_DTT_Helper::get_timestamp_with_offset(
@@ -1581,19 +1581,19 @@  discard block
 block discarded – undo
1581 1581
         $field->set_time_format($this->_tm_frmt);
1582 1582
         switch ($what) {
1583 1583
             case 'T' :
1584
-                $this->_fields[ $fieldname ] = $field->prepare_for_set_with_new_time(
1584
+                $this->_fields[$fieldname] = $field->prepare_for_set_with_new_time(
1585 1585
                     $datetime_value,
1586
-                    $this->_fields[ $fieldname ]
1586
+                    $this->_fields[$fieldname]
1587 1587
                 );
1588 1588
                 break;
1589 1589
             case 'D' :
1590
-                $this->_fields[ $fieldname ] = $field->prepare_for_set_with_new_date(
1590
+                $this->_fields[$fieldname] = $field->prepare_for_set_with_new_date(
1591 1591
                     $datetime_value,
1592
-                    $this->_fields[ $fieldname ]
1592
+                    $this->_fields[$fieldname]
1593 1593
                 );
1594 1594
                 break;
1595 1595
             case 'B' :
1596
-                $this->_fields[ $fieldname ] = $field->prepare_for_set($datetime_value);
1596
+                $this->_fields[$fieldname] = $field->prepare_for_set($datetime_value);
1597 1597
                 break;
1598 1598
         }
1599 1599
         $this->_clear_cached_property($fieldname);
@@ -1635,7 +1635,7 @@  discard block
 block discarded – undo
1635 1635
         $this->set_timezone($timezone);
1636 1636
         $fn   = (array) $field_name;
1637 1637
         $args = array_merge($fn, (array) $args);
1638
-        if (! method_exists($this, $callback)) {
1638
+        if ( ! method_exists($this, $callback)) {
1639 1639
             throw new EE_Error(
1640 1640
                 sprintf(
1641 1641
                     esc_html__(
@@ -1647,7 +1647,7 @@  discard block
 block discarded – undo
1647 1647
             );
1648 1648
         }
1649 1649
         $args   = (array) $args;
1650
-        $return = $prepend . call_user_func_array(array($this, $callback), $args) . $append;
1650
+        $return = $prepend.call_user_func_array(array($this, $callback), $args).$append;
1651 1651
         $this->set_timezone($original_timezone);
1652 1652
         return $return;
1653 1653
     }
@@ -1762,8 +1762,8 @@  discard block
 block discarded – undo
1762 1762
     {
1763 1763
         $model = $this->get_model();
1764 1764
         foreach ($model->relation_settings() as $relation_name => $relation_obj) {
1765
-            if (! empty($this->_model_relations[ $relation_name ])) {
1766
-                $related_objects = $this->_model_relations[ $relation_name ];
1765
+            if ( ! empty($this->_model_relations[$relation_name])) {
1766
+                $related_objects = $this->_model_relations[$relation_name];
1767 1767
                 if ($relation_obj instanceof EE_Belongs_To_Relation) {
1768 1768
                     //this relation only stores a single model object, not an array
1769 1769
                     //but let's make it consistent
@@ -1820,7 +1820,7 @@  discard block
 block discarded – undo
1820 1820
             $this->set($column, $value);
1821 1821
         }
1822 1822
         // no changes ? then don't do anything
1823
-        if (! $this->_has_changes && $this->ID() && $model->get_primary_key_field()->is_auto_increment()) {
1823
+        if ( ! $this->_has_changes && $this->ID() && $model->get_primary_key_field()->is_auto_increment()) {
1824 1824
             return 0;
1825 1825
         }
1826 1826
         /**
@@ -1830,7 +1830,7 @@  discard block
 block discarded – undo
1830 1830
          * @param EE_Base_Class $model_object the model object about to be saved.
1831 1831
          */
1832 1832
         do_action('AHEE__EE_Base_Class__save__begin', $this);
1833
-        if (! $this->allow_persist()) {
1833
+        if ( ! $this->allow_persist()) {
1834 1834
             return 0;
1835 1835
         }
1836 1836
         // now get current attribute values
@@ -1845,10 +1845,10 @@  discard block
 block discarded – undo
1845 1845
         if ($model->has_primary_key_field()) {
1846 1846
             if ($model->get_primary_key_field()->is_auto_increment()) {
1847 1847
                 //ok check if it's set, if so: update; if not, insert
1848
-                if (! empty($save_cols_n_values[ $model->primary_key_name() ])) {
1848
+                if ( ! empty($save_cols_n_values[$model->primary_key_name()])) {
1849 1849
                     $results = $model->update_by_ID($save_cols_n_values, $this->ID());
1850 1850
                 } else {
1851
-                    unset($save_cols_n_values[ $model->primary_key_name() ]);
1851
+                    unset($save_cols_n_values[$model->primary_key_name()]);
1852 1852
                     $results = $model->insert($save_cols_n_values);
1853 1853
                     if ($results) {
1854 1854
                         //if successful, set the primary key
@@ -1858,7 +1858,7 @@  discard block
 block discarded – undo
1858 1858
                         //will get added to the mapper before we can add this one!
1859 1859
                         //but if we just avoid using the SET method, all that headache can be avoided
1860 1860
                         $pk_field_name                   = $model->primary_key_name();
1861
-                        $this->_fields[ $pk_field_name ] = $results;
1861
+                        $this->_fields[$pk_field_name] = $results;
1862 1862
                         $this->_clear_cached_property($pk_field_name);
1863 1863
                         $model->add_to_entity_map($this);
1864 1864
                         $this->_update_cached_related_model_objs_fks();
@@ -1875,8 +1875,8 @@  discard block
 block discarded – undo
1875 1875
                                     'event_espresso'
1876 1876
                                 ),
1877 1877
                                 get_class($this),
1878
-                                get_class($model) . '::instance()->add_to_entity_map()',
1879
-                                get_class($model) . '::instance()->get_one_by_ID()',
1878
+                                get_class($model).'::instance()->add_to_entity_map()',
1879
+                                get_class($model).'::instance()->get_one_by_ID()',
1880 1880
                                 '<br />'
1881 1881
                             )
1882 1882
                         );
@@ -1976,27 +1976,27 @@  discard block
 block discarded – undo
1976 1976
     public function save_new_cached_related_model_objs()
1977 1977
     {
1978 1978
         //make sure this has been saved
1979
-        if (! $this->ID()) {
1979
+        if ( ! $this->ID()) {
1980 1980
             $id = $this->save();
1981 1981
         } else {
1982 1982
             $id = $this->ID();
1983 1983
         }
1984 1984
         //now save all the NEW cached model objects  (ie they don't exist in the DB)
1985 1985
         foreach ($this->get_model()->relation_settings() as $relationName => $relationObj) {
1986
-            if ($this->_model_relations[ $relationName ]) {
1986
+            if ($this->_model_relations[$relationName]) {
1987 1987
                 //is this a relation where we should expect just ONE related object (ie, EE_Belongs_To_relation)
1988 1988
                 //or MANY related objects (ie, EE_HABTM_Relation or EE_Has_Many_Relation)?
1989 1989
                 /* @var $related_model_obj EE_Base_Class */
1990 1990
                 if ($relationObj instanceof EE_Belongs_To_Relation) {
1991 1991
                     //add a relation to that relation type (which saves the appropriate thing in the process)
1992 1992
                     //but ONLY if it DOES NOT exist in the DB
1993
-                    $related_model_obj = $this->_model_relations[ $relationName ];
1993
+                    $related_model_obj = $this->_model_relations[$relationName];
1994 1994
                     //					if( ! $related_model_obj->ID()){
1995 1995
                     $this->_add_relation_to($related_model_obj, $relationName);
1996 1996
                     $related_model_obj->save_new_cached_related_model_objs();
1997 1997
                     //					}
1998 1998
                 } else {
1999
-                    foreach ($this->_model_relations[ $relationName ] as $related_model_obj) {
1999
+                    foreach ($this->_model_relations[$relationName] as $related_model_obj) {
2000 2000
                         //add a relation to that relation type (which saves the appropriate thing in the process)
2001 2001
                         //but ONLY if it DOES NOT exist in the DB
2002 2002
                         //						if( ! $related_model_obj->ID()){
@@ -2023,7 +2023,7 @@  discard block
 block discarded – undo
2023 2023
      */
2024 2024
     public function get_model()
2025 2025
     {
2026
-        if (! $this->_model) {
2026
+        if ( ! $this->_model) {
2027 2027
             $modelName    = self::_get_model_classname(get_class($this));
2028 2028
             $this->_model = self::_get_model_instance_with_name($modelName, $this->_timezone);
2029 2029
         } else {
@@ -2049,9 +2049,9 @@  discard block
 block discarded – undo
2049 2049
         $primary_id_ref = self::_get_primary_key_name($classname);
2050 2050
         if (
2051 2051
             array_key_exists($primary_id_ref, $props_n_values)
2052
-            && ! empty($props_n_values[ $primary_id_ref ])
2052
+            && ! empty($props_n_values[$primary_id_ref])
2053 2053
         ) {
2054
-            $id = $props_n_values[ $primary_id_ref ];
2054
+            $id = $props_n_values[$primary_id_ref];
2055 2055
             return self::_get_model($classname)->get_from_entity_map($id);
2056 2056
         }
2057 2057
         return false;
@@ -2085,10 +2085,10 @@  discard block
 block discarded – undo
2085 2085
         if ($model->has_primary_key_field()) {
2086 2086
             $primary_id_ref = self::_get_primary_key_name($classname);
2087 2087
             if (array_key_exists($primary_id_ref, $props_n_values)
2088
-                && ! empty($props_n_values[ $primary_id_ref ])
2088
+                && ! empty($props_n_values[$primary_id_ref])
2089 2089
             ) {
2090 2090
                 $existing = $model->get_one_by_ID(
2091
-                    $props_n_values[ $primary_id_ref ]
2091
+                    $props_n_values[$primary_id_ref]
2092 2092
                 );
2093 2093
             }
2094 2094
         } elseif ($model->has_all_combined_primary_key_fields($props_n_values)) {
@@ -2100,7 +2100,7 @@  discard block
 block discarded – undo
2100 2100
         }
2101 2101
         if ($existing) {
2102 2102
             //set date formats if present before setting values
2103
-            if (! empty($date_formats) && is_array($date_formats)) {
2103
+            if ( ! empty($date_formats) && is_array($date_formats)) {
2104 2104
                 $existing->set_date_format($date_formats[0]);
2105 2105
                 $existing->set_time_format($date_formats[1]);
2106 2106
             } else {
@@ -2133,7 +2133,7 @@  discard block
 block discarded – undo
2133 2133
     protected static function _get_model($classname, $timezone = null)
2134 2134
     {
2135 2135
         //find model for this class
2136
-        if (! $classname) {
2136
+        if ( ! $classname) {
2137 2137
             throw new EE_Error(
2138 2138
                 sprintf(
2139 2139
                     esc_html__(
@@ -2182,7 +2182,7 @@  discard block
 block discarded – undo
2182 2182
         if (strpos($model_name, 'EE_') === 0) {
2183 2183
             $model_classname = str_replace('EE_', 'EEM_', $model_name);
2184 2184
         } else {
2185
-            $model_classname = 'EEM_' . $model_name;
2185
+            $model_classname = 'EEM_'.$model_name;
2186 2186
         }
2187 2187
         return $model_classname;
2188 2188
     }
@@ -2201,7 +2201,7 @@  discard block
 block discarded – undo
2201 2201
      */
2202 2202
     protected static function _get_primary_key_name($classname = null)
2203 2203
     {
2204
-        if (! $classname) {
2204
+        if ( ! $classname) {
2205 2205
             throw new EE_Error(
2206 2206
                 sprintf(
2207 2207
                     esc_html__('What were you thinking calling _get_primary_key_name(%s)', 'event_espresso'),
@@ -2231,7 +2231,7 @@  discard block
 block discarded – undo
2231 2231
         $model = $this->get_model();
2232 2232
         //now that we know the name of the variable, use a variable variable to get its value and return its
2233 2233
         if ($model->has_primary_key_field()) {
2234
-            return $this->_fields[ $model->primary_key_name() ];
2234
+            return $this->_fields[$model->primary_key_name()];
2235 2235
         }
2236 2236
         return $model->get_index_primary_key_string($this->_fields);
2237 2237
     }
@@ -2284,7 +2284,7 @@  discard block
 block discarded – undo
2284 2284
             }
2285 2285
         } else {
2286 2286
             //this thing doesn't exist in the DB,  so just cache it
2287
-            if (! $otherObjectModelObjectOrID instanceof EE_Base_Class) {
2287
+            if ( ! $otherObjectModelObjectOrID instanceof EE_Base_Class) {
2288 2288
                 throw new EE_Error(
2289 2289
                     sprintf(
2290 2290
                         esc_html__(
@@ -2449,7 +2449,7 @@  discard block
 block discarded – undo
2449 2449
             } else {
2450 2450
                 //did we already cache the result of this query?
2451 2451
                 $cached_results = $this->get_all_from_cache($relationName);
2452
-                if (! $cached_results) {
2452
+                if ( ! $cached_results) {
2453 2453
                     $related_model_objects = $this->get_model()->get_all_related(
2454 2454
                         $this,
2455 2455
                         $relationName,
@@ -2559,7 +2559,7 @@  discard block
 block discarded – undo
2559 2559
             } else {
2560 2560
                 //first, check if we've already cached the result of this query
2561 2561
                 $cached_result = $this->get_one_from_cache($relationName);
2562
-                if (! $cached_result) {
2562
+                if ( ! $cached_result) {
2563 2563
                     $related_model_object = $model->get_first_related(
2564 2564
                         $this,
2565 2565
                         $relationName,
@@ -2583,7 +2583,7 @@  discard block
 block discarded – undo
2583 2583
             }
2584 2584
             // this doesn't exist in the DB and apparently the thing it belongs to doesn't either,
2585 2585
             // just get what's cached on this object
2586
-            if (! $related_model_object) {
2586
+            if ( ! $related_model_object) {
2587 2587
                 $related_model_object = $this->get_one_from_cache($relationName);
2588 2588
             }
2589 2589
         }
@@ -2665,7 +2665,7 @@  discard block
 block discarded – undo
2665 2665
      */
2666 2666
     public function is_set($field_name)
2667 2667
     {
2668
-        return isset($this->_fields[ $field_name ]);
2668
+        return isset($this->_fields[$field_name]);
2669 2669
     }
2670 2670
 
2671 2671
 
@@ -2681,7 +2681,7 @@  discard block
 block discarded – undo
2681 2681
     {
2682 2682
         foreach ((array) $properties as $property_name) {
2683 2683
             //first make sure this property exists
2684
-            if (! $this->_fields[ $property_name ]) {
2684
+            if ( ! $this->_fields[$property_name]) {
2685 2685
                 throw new EE_Error(
2686 2686
                     sprintf(
2687 2687
                         esc_html__(
@@ -2713,7 +2713,7 @@  discard block
 block discarded – undo
2713 2713
         $properties = array();
2714 2714
         //remove prepended underscore
2715 2715
         foreach ($fields as $field_name => $settings) {
2716
-            $properties[ $field_name ] = $this->get($field_name);
2716
+            $properties[$field_name] = $this->get($field_name);
2717 2717
         }
2718 2718
         return $properties;
2719 2719
     }
@@ -2750,7 +2750,7 @@  discard block
 block discarded – undo
2750 2750
     {
2751 2751
         $className = get_class($this);
2752 2752
         $tagName   = "FHEE__{$className}__{$methodName}";
2753
-        if (! has_filter($tagName)) {
2753
+        if ( ! has_filter($tagName)) {
2754 2754
             throw new EE_Error(
2755 2755
                 sprintf(
2756 2756
                     esc_html__(
@@ -2795,7 +2795,7 @@  discard block
 block discarded – undo
2795 2795
             $query_params[0]['EXM_value'] = $meta_value;
2796 2796
         }
2797 2797
         $existing_rows_like_that = EEM_Extra_Meta::instance()->get_all($query_params);
2798
-        if (! $existing_rows_like_that) {
2798
+        if ( ! $existing_rows_like_that) {
2799 2799
             return $this->add_extra_meta($meta_key, $meta_value);
2800 2800
         }
2801 2801
         foreach ($existing_rows_like_that as $existing_row) {
@@ -2913,7 +2913,7 @@  discard block
 block discarded – undo
2913 2913
                 $values = array();
2914 2914
                 foreach ($results as $result) {
2915 2915
                     if ($result instanceof EE_Extra_Meta) {
2916
-                        $values[ $result->ID() ] = $result->value();
2916
+                        $values[$result->ID()] = $result->value();
2917 2917
                     }
2918 2918
                 }
2919 2919
                 return $values;
@@ -2958,17 +2958,17 @@  discard block
 block discarded – undo
2958 2958
             );
2959 2959
             foreach ($extra_meta_objs as $extra_meta_obj) {
2960 2960
                 if ($extra_meta_obj instanceof EE_Extra_Meta) {
2961
-                    $return_array[ $extra_meta_obj->key() ] = $extra_meta_obj->value();
2961
+                    $return_array[$extra_meta_obj->key()] = $extra_meta_obj->value();
2962 2962
                 }
2963 2963
             }
2964 2964
         } else {
2965 2965
             $extra_meta_objs = $this->get_many_related('Extra_Meta');
2966 2966
             foreach ($extra_meta_objs as $extra_meta_obj) {
2967 2967
                 if ($extra_meta_obj instanceof EE_Extra_Meta) {
2968
-                    if (! isset($return_array[ $extra_meta_obj->key() ])) {
2969
-                        $return_array[ $extra_meta_obj->key() ] = array();
2968
+                    if ( ! isset($return_array[$extra_meta_obj->key()])) {
2969
+                        $return_array[$extra_meta_obj->key()] = array();
2970 2970
                     }
2971
-                    $return_array[ $extra_meta_obj->key() ][ $extra_meta_obj->ID() ] = $extra_meta_obj->value();
2971
+                    $return_array[$extra_meta_obj->key()][$extra_meta_obj->ID()] = $extra_meta_obj->value();
2972 2972
                 }
2973 2973
             }
2974 2974
         }
@@ -3047,8 +3047,8 @@  discard block
 block discarded – undo
3047 3047
                         esc_html__('Trying to refresh a model object with ID "%1$s" that\'s not in the entity map? First off: you should put it in the entity map by calling %2$s. Second off, if you want what\'s in the database right now, you should just call %3$s yourself and discard this model object.',
3048 3048
                             'event_espresso'),
3049 3049
                         $this->ID(),
3050
-                        get_class($this->get_model()) . '::instance()->add_to_entity_map()',
3051
-                        get_class($this->get_model()) . '::instance()->refresh_entity_map()'
3050
+                        get_class($this->get_model()).'::instance()->add_to_entity_map()',
3051
+                        get_class($this->get_model()).'::instance()->refresh_entity_map()'
3052 3052
                     )
3053 3053
                 );
3054 3054
             }
@@ -3110,7 +3110,7 @@  discard block
 block discarded – undo
3110 3110
         $model = $this->get_model();
3111 3111
         foreach ($model->relation_settings() as $relation_name => $relation_obj) {
3112 3112
             if ($relation_obj instanceof EE_Belongs_To_Relation) {
3113
-                $classname = 'EE_' . $model->get_this_model_name();
3113
+                $classname = 'EE_'.$model->get_this_model_name();
3114 3114
                 if (
3115 3115
                     $this->get_one_from_cache($relation_name) instanceof $classname
3116 3116
                     && $this->get_one_from_cache($relation_name)->ID()
Please login to merge, or discard this patch.
Doc Comments   +9 added lines, -9 removed lines patch added patch discarded remove patch
@@ -712,7 +712,7 @@  discard block
 block discarded – undo
712 712
      *
713 713
      * @param \EE_Datetime_Field $datetime_field
714 714
      * @param bool               $pretty
715
-     * @param null               $date_or_time
715
+     * @param string|null               $date_or_time
716 716
      * @return void
717 717
      * @throws InvalidArgumentException
718 718
      * @throws InvalidInterfaceException
@@ -1066,7 +1066,7 @@  discard block
 block discarded – undo
1066 1066
      *
1067 1067
      * @param null  $field_to_order_by  What field is being used as the reference point.
1068 1068
      * @param array $query_params       Any additional conditions on the query.
1069
-     * @param null  $columns_to_select  If left null, then an array of EE_Base_Class objects is returned, otherwise
1069
+     * @param string  $columns_to_select  If left null, then an array of EE_Base_Class objects is returned, otherwise
1070 1070
      *                                  you can indicate just the columns you want returned
1071 1071
      * @return array|EE_Base_Class
1072 1072
      * @throws ReflectionException
@@ -1095,7 +1095,7 @@  discard block
 block discarded – undo
1095 1095
      *
1096 1096
      * @param null  $field_to_order_by  What field is being used as the reference point.
1097 1097
      * @param array $query_params       Any additional conditions on the query.
1098
-     * @param null  $columns_to_select  If left null, then an EE_Base_Class object is returned, otherwise
1098
+     * @param string  $columns_to_select  If left null, then an EE_Base_Class object is returned, otherwise
1099 1099
      *                                  you can indicate just the column you want returned
1100 1100
      * @return array|EE_Base_Class
1101 1101
      * @throws ReflectionException
@@ -1178,7 +1178,7 @@  discard block
 block discarded – undo
1178 1178
      * This method simply returns the RAW unprocessed value for the given property in this class
1179 1179
      *
1180 1180
      * @param  string $field_name A valid fieldname
1181
-     * @return mixed              Whatever the raw value stored on the property is.
1181
+     * @return integer|null              Whatever the raw value stored on the property is.
1182 1182
      * @throws ReflectionException
1183 1183
      * @throws InvalidArgumentException
1184 1184
      * @throws InvalidInterfaceException
@@ -1526,7 +1526,7 @@  discard block
 block discarded – undo
1526 1526
      * sets the time on a datetime property
1527 1527
      *
1528 1528
      * @access protected
1529
-     * @param string|Datetime $time      a valid time string for php datetime functions (or DateTime object)
1529
+     * @param string $time      a valid time string for php datetime functions (or DateTime object)
1530 1530
      * @param string          $fieldname the name of the field the time is being set on (must match a EE_Datetime_Field)
1531 1531
      * @throws ReflectionException
1532 1532
      * @throws InvalidArgumentException
@@ -1544,7 +1544,7 @@  discard block
 block discarded – undo
1544 1544
      * sets the date on a datetime property
1545 1545
      *
1546 1546
      * @access protected
1547
-     * @param string|DateTime $date      a valid date string for php datetime functions ( or DateTime object)
1547
+     * @param string $date      a valid date string for php datetime functions ( or DateTime object)
1548 1548
      * @param string          $fieldname the name of the field the date is being set on (must match a EE_Datetime_Field)
1549 1549
      * @throws ReflectionException
1550 1550
      * @throws InvalidArgumentException
@@ -2066,7 +2066,7 @@  discard block
 block discarded – undo
2066 2066
      *
2067 2067
      * @param  array  $props_n_values   incoming array of properties and their values
2068 2068
      * @param  string $classname        the classname of the child class
2069
-     * @param null    $timezone
2069
+     * @param string|null    $timezone
2070 2070
      * @param array   $date_formats     incoming date_formats in an array where the first value is the
2071 2071
      *                                  date_format and the second value is the time format
2072 2072
      * @return mixed (EE_Base_Class|bool)
@@ -2153,7 +2153,7 @@  discard block
 block discarded – undo
2153 2153
      * Gets the model instance (eg instance of EEM_Attendee) given its classname (eg EE_Attendee)
2154 2154
      *
2155 2155
      * @param string $model_classname
2156
-     * @param null   $timezone
2156
+     * @param string|null   $timezone
2157 2157
      * @return EEM_Base
2158 2158
      * @throws ReflectionException
2159 2159
      * @throws InvalidArgumentException
@@ -2773,7 +2773,7 @@  discard block
 block discarded – undo
2773 2773
      *
2774 2774
      * @param string $meta_key
2775 2775
      * @param mixed  $meta_value
2776
-     * @param mixed  $previous_value
2776
+     * @param boolean  $previous_value
2777 2777
      * @return bool|int # of records updated (or BOOLEAN if we actually ended up inserting the extra meta row)
2778 2778
      *                  NOTE: if the values haven't changed, returns 0
2779 2779
      * @throws InvalidArgumentException
Please login to merge, or discard this patch.
core/middleware/EE_Recommended_Versions.core.php 2 patches
Indentation   +189 added lines, -189 removed lines patch added patch discarded remove patch
@@ -21,199 +21,199 @@
 block discarded – undo
21 21
 {
22 22
 
23 23
 
24
-    /**
25
-     * converts a Request to a Response
26
-     *
27
-     * @param EE_Request  $request
28
-     * @param EE_Response $response
29
-     * @return EE_Response
30
-     * @throws InvalidDataTypeException
31
-     */
32
-    public function handle_request(EE_Request $request, EE_Response $response)
33
-    {
34
-        $this->_request  = $request;
35
-        $this->_response = $response;
36
-        //$this->_response->add_output( "\n\t IN >>  " . __CLASS__ );
37
-        //$this->_response->set_notice( 1, 'hey look at this' );
38
-        // check required WP version
39
-        if (! $this->_minimum_wp_version_required()) {
40
-            $this->_request->un_set('activate', true);
41
-            add_action('admin_notices', array($this, 'minimum_wp_version_error'), 1);
42
-            //$this->_response->add_output( "\n<br />" . 'minimum_wp_version_error' );
43
-            $this->_response->terminate_request();
44
-            $this->_response->deactivate_plugin();
45
-        }
46
-        // check recommended PHP version
47
-        if (! $this->_minimum_php_version_recommended()) {
48
-            $this->_display_minimum_recommended_php_version_notice();
49
-        }
50
-
51
-        //upcoming required version
52
-        if (! $this->upcomingRequiredPhpVersion()) {
53
-            $this->displayUpcomingRequiredVersion();
54
-        }
55
-        $this->_response = $this->process_request_stack($this->_request, $this->_response);
56
-        //$this->_response->add_output( "\n\t OUT << " . __CLASS__ );
57
-        return $this->_response;
58
-    }
59
-
60
-
61
-    /**
62
-     * Helper method to assess installed wp version against given values.
63
-     * By default this compares the required minimum version of WP for EE against the installed version of WP
64
-     * Note, $wp_version is the first parameter sent into the PHP version_compare function (what is being checked
65
-     * against) so consider that when sending in your values.
66
-     *
67
-     * @param string $version_to_check
68
-     * @param string $operator
69
-     * @return bool
70
-     */
71
-    public static function check_wp_version($version_to_check = EE_MIN_WP_VER_REQUIRED, $operator = '>=')
72
-    {
73
-        global $wp_version;
74
-        return version_compare(
75
-            // first account for wp_version being pre-release
76
-            // (like RC, beta etc) which are usually in the format like 4.7-RC3-39519
77
-            strpos($wp_version, '-') > 0
78
-                ? substr($wp_version, 0, strpos($wp_version, '-'))
79
-                : $wp_version,
80
-            $version_to_check,
81
-            $operator
82
-        );
83
-    }
84
-
85
-
86
-
87
-    /**
88
-     *    _minimum_wp_version_required
89
-     *
90
-     * @access private
91
-     * @return boolean
92
-     */
93
-    private function _minimum_wp_version_required()
94
-    {
95
-        return EE_Recommended_Versions::check_wp_version();
96
-    }
97
-
98
-
99
-
100
-    /**
101
-     *    _check_php_version
102
-     *
103
-     * @access private
104
-     * @param string $min_version
105
-     * @return boolean
106
-     */
107
-    private function _check_php_version($min_version = EE_MIN_PHP_VER_RECOMMENDED)
108
-    {
109
-        return version_compare(PHP_VERSION, $min_version, '>=');
110
-    }
111
-
112
-
113
-
114
-    /**
115
-     *    _minimum_php_version_recommended
116
-     *
117
-     * @access private
118
-     * @return boolean
119
-     */
120
-    private function _minimum_php_version_recommended()
121
-    {
122
-        return $this->_check_php_version();
123
-    }
124
-
125
-
126
-    /**
127
-     * Returns whether the provided php version number is greater than the current version of php installed on the server.
128
-     * @param string $version_required
129
-     * @return bool
130
-     */
131
-    private function upcomingRequiredPhpVersion($version_required = '5.4.45')
132
-    {
133
-        return $this->_check_php_version($version_required);
134
-    }
135
-
136
-
137
-
138
-    /**
139
-     *    minimum_wp_version_error
140
-     *
141
-     * @return void
142
-     */
143
-    public function minimum_wp_version_error()
144
-    {
145
-        global $wp_version;
146
-        ?>
24
+	/**
25
+	 * converts a Request to a Response
26
+	 *
27
+	 * @param EE_Request  $request
28
+	 * @param EE_Response $response
29
+	 * @return EE_Response
30
+	 * @throws InvalidDataTypeException
31
+	 */
32
+	public function handle_request(EE_Request $request, EE_Response $response)
33
+	{
34
+		$this->_request  = $request;
35
+		$this->_response = $response;
36
+		//$this->_response->add_output( "\n\t IN >>  " . __CLASS__ );
37
+		//$this->_response->set_notice( 1, 'hey look at this' );
38
+		// check required WP version
39
+		if (! $this->_minimum_wp_version_required()) {
40
+			$this->_request->un_set('activate', true);
41
+			add_action('admin_notices', array($this, 'minimum_wp_version_error'), 1);
42
+			//$this->_response->add_output( "\n<br />" . 'minimum_wp_version_error' );
43
+			$this->_response->terminate_request();
44
+			$this->_response->deactivate_plugin();
45
+		}
46
+		// check recommended PHP version
47
+		if (! $this->_minimum_php_version_recommended()) {
48
+			$this->_display_minimum_recommended_php_version_notice();
49
+		}
50
+
51
+		//upcoming required version
52
+		if (! $this->upcomingRequiredPhpVersion()) {
53
+			$this->displayUpcomingRequiredVersion();
54
+		}
55
+		$this->_response = $this->process_request_stack($this->_request, $this->_response);
56
+		//$this->_response->add_output( "\n\t OUT << " . __CLASS__ );
57
+		return $this->_response;
58
+	}
59
+
60
+
61
+	/**
62
+	 * Helper method to assess installed wp version against given values.
63
+	 * By default this compares the required minimum version of WP for EE against the installed version of WP
64
+	 * Note, $wp_version is the first parameter sent into the PHP version_compare function (what is being checked
65
+	 * against) so consider that when sending in your values.
66
+	 *
67
+	 * @param string $version_to_check
68
+	 * @param string $operator
69
+	 * @return bool
70
+	 */
71
+	public static function check_wp_version($version_to_check = EE_MIN_WP_VER_REQUIRED, $operator = '>=')
72
+	{
73
+		global $wp_version;
74
+		return version_compare(
75
+			// first account for wp_version being pre-release
76
+			// (like RC, beta etc) which are usually in the format like 4.7-RC3-39519
77
+			strpos($wp_version, '-') > 0
78
+				? substr($wp_version, 0, strpos($wp_version, '-'))
79
+				: $wp_version,
80
+			$version_to_check,
81
+			$operator
82
+		);
83
+	}
84
+
85
+
86
+
87
+	/**
88
+	 *    _minimum_wp_version_required
89
+	 *
90
+	 * @access private
91
+	 * @return boolean
92
+	 */
93
+	private function _minimum_wp_version_required()
94
+	{
95
+		return EE_Recommended_Versions::check_wp_version();
96
+	}
97
+
98
+
99
+
100
+	/**
101
+	 *    _check_php_version
102
+	 *
103
+	 * @access private
104
+	 * @param string $min_version
105
+	 * @return boolean
106
+	 */
107
+	private function _check_php_version($min_version = EE_MIN_PHP_VER_RECOMMENDED)
108
+	{
109
+		return version_compare(PHP_VERSION, $min_version, '>=');
110
+	}
111
+
112
+
113
+
114
+	/**
115
+	 *    _minimum_php_version_recommended
116
+	 *
117
+	 * @access private
118
+	 * @return boolean
119
+	 */
120
+	private function _minimum_php_version_recommended()
121
+	{
122
+		return $this->_check_php_version();
123
+	}
124
+
125
+
126
+	/**
127
+	 * Returns whether the provided php version number is greater than the current version of php installed on the server.
128
+	 * @param string $version_required
129
+	 * @return bool
130
+	 */
131
+	private function upcomingRequiredPhpVersion($version_required = '5.4.45')
132
+	{
133
+		return $this->_check_php_version($version_required);
134
+	}
135
+
136
+
137
+
138
+	/**
139
+	 *    minimum_wp_version_error
140
+	 *
141
+	 * @return void
142
+	 */
143
+	public function minimum_wp_version_error()
144
+	{
145
+		global $wp_version;
146
+		?>
147 147
         <div class="error">
148 148
             <p>
149 149
                 <?php
150
-                printf(
151
-                    __('We\'re sorry, but Event Espresso requires WordPress version %1$s or greater in order to operate. You are currently running version %2$s.%3$sFor information on how to update your version of WordPress, please go to %4$s.',
152
-                        'event_espresso'),
153
-                    EE_MIN_WP_VER_REQUIRED,
154
-                    $wp_version,
155
-                    '<br/>',
156
-                    '<a href="http://codex.wordpress.org/Updating_WordPress">http://codex.wordpress.org/Updating_WordPress</a>'
157
-                );
158
-                ?>
150
+				printf(
151
+					__('We\'re sorry, but Event Espresso requires WordPress version %1$s or greater in order to operate. You are currently running version %2$s.%3$sFor information on how to update your version of WordPress, please go to %4$s.',
152
+						'event_espresso'),
153
+					EE_MIN_WP_VER_REQUIRED,
154
+					$wp_version,
155
+					'<br/>',
156
+					'<a href="http://codex.wordpress.org/Updating_WordPress">http://codex.wordpress.org/Updating_WordPress</a>'
157
+				);
158
+				?>
159 159
             </p>
160 160
         </div>
161 161
         <?php
162
-    }
163
-
164
-
165
-
166
-    /**
167
-     *    _display_minimum_recommended_php_version_notice
168
-     *
169
-     * @access private
170
-     * @return void
171
-     * @throws InvalidDataTypeException
172
-     */
173
-    private function _display_minimum_recommended_php_version_notice()
174
-    {
175
-        if($this->_request->isAdmin()){
176
-            new PersistentAdminNotice(
177
-                'php_version_' . str_replace('.', '-', EE_MIN_PHP_VER_RECOMMENDED) . '_recommended',
178
-                sprintf(
179
-                    __(
180
-                        'Event Espresso recommends PHP version %1$s or greater for optimal performance. You are currently running version %2$s.%3$sIn order to update your version of PHP, you will need to contact your current hosting provider.%3$sFor information on stable PHP versions, please go to %4$s.',
181
-                        'event_espresso'
182
-                    ),
183
-                    EE_MIN_PHP_VER_RECOMMENDED,
184
-                    PHP_VERSION,
185
-                    '<br/>',
186
-                    '<a href="http://php.net/downloads.php">http://php.net/downloads.php</a>'
187
-                )
188
-            );
189
-        }
190
-    }
191
-
192
-
193
-    /**
194
-     *  Sets a notice for an upcoming required version of PHP in the next update of EE core.
195
-     */
196
-    private function displayUpcomingRequiredVersion()
197
-    {
198
-        if ($this->_request->isAdmin()
199
-            && apply_filters('FHEE__EE_Recommended_Versions__displayUpcomingRequiredVersion', true, $this->_request)
200
-            && current_user_can('update_plugins')
201
-        ) {
202
-            add_action('admin_notices', function () {
203
-                echo '<div class="notice event-espresso-admin-notice notice-warning"><p>'
204
-                     . sprintf(
205
-                         esc_html__(
206
-                             'Please note: The next update of Event Espresso 4 will %1$srequire%2$s PHP 5.4.45 or greater.  Your web server\'s PHP version is %3$s.  You can contact your host and ask them to update your PHP version to at least PHP 5.6.  Please do not update to the new version of Event Espresso 4 until the PHP update is completed. Read about why keeping your server on the latest version of PHP is a good idea %4$shere%5$s',
207
-                             'event_espresso'
208
-                         ),
209
-                         '<strong>',
210
-                         '</strong>',
211
-                         PHP_VERSION,
212
-                         '<a href="https://wordpress.org/support/upgrade-php/">',
213
-                         '</a>'
214
-                     )
215
-                     . '</p></div>';
216
-            });
217
-        }
218
-    }
162
+	}
163
+
164
+
165
+
166
+	/**
167
+	 *    _display_minimum_recommended_php_version_notice
168
+	 *
169
+	 * @access private
170
+	 * @return void
171
+	 * @throws InvalidDataTypeException
172
+	 */
173
+	private function _display_minimum_recommended_php_version_notice()
174
+	{
175
+		if($this->_request->isAdmin()){
176
+			new PersistentAdminNotice(
177
+				'php_version_' . str_replace('.', '-', EE_MIN_PHP_VER_RECOMMENDED) . '_recommended',
178
+				sprintf(
179
+					__(
180
+						'Event Espresso recommends PHP version %1$s or greater for optimal performance. You are currently running version %2$s.%3$sIn order to update your version of PHP, you will need to contact your current hosting provider.%3$sFor information on stable PHP versions, please go to %4$s.',
181
+						'event_espresso'
182
+					),
183
+					EE_MIN_PHP_VER_RECOMMENDED,
184
+					PHP_VERSION,
185
+					'<br/>',
186
+					'<a href="http://php.net/downloads.php">http://php.net/downloads.php</a>'
187
+				)
188
+			);
189
+		}
190
+	}
191
+
192
+
193
+	/**
194
+	 *  Sets a notice for an upcoming required version of PHP in the next update of EE core.
195
+	 */
196
+	private function displayUpcomingRequiredVersion()
197
+	{
198
+		if ($this->_request->isAdmin()
199
+			&& apply_filters('FHEE__EE_Recommended_Versions__displayUpcomingRequiredVersion', true, $this->_request)
200
+			&& current_user_can('update_plugins')
201
+		) {
202
+			add_action('admin_notices', function () {
203
+				echo '<div class="notice event-espresso-admin-notice notice-warning"><p>'
204
+					 . sprintf(
205
+						 esc_html__(
206
+							 'Please note: The next update of Event Espresso 4 will %1$srequire%2$s PHP 5.4.45 or greater.  Your web server\'s PHP version is %3$s.  You can contact your host and ask them to update your PHP version to at least PHP 5.6.  Please do not update to the new version of Event Espresso 4 until the PHP update is completed. Read about why keeping your server on the latest version of PHP is a good idea %4$shere%5$s',
207
+							 'event_espresso'
208
+						 ),
209
+						 '<strong>',
210
+						 '</strong>',
211
+						 PHP_VERSION,
212
+						 '<a href="https://wordpress.org/support/upgrade-php/">',
213
+						 '</a>'
214
+					 )
215
+					 . '</p></div>';
216
+			});
217
+		}
218
+	}
219 219
 }
Please login to merge, or discard this patch.
Spacing   +6 added lines, -6 removed lines patch added patch discarded remove patch
@@ -36,7 +36,7 @@  discard block
 block discarded – undo
36 36
         //$this->_response->add_output( "\n\t IN >>  " . __CLASS__ );
37 37
         //$this->_response->set_notice( 1, 'hey look at this' );
38 38
         // check required WP version
39
-        if (! $this->_minimum_wp_version_required()) {
39
+        if ( ! $this->_minimum_wp_version_required()) {
40 40
             $this->_request->un_set('activate', true);
41 41
             add_action('admin_notices', array($this, 'minimum_wp_version_error'), 1);
42 42
             //$this->_response->add_output( "\n<br />" . 'minimum_wp_version_error' );
@@ -44,12 +44,12 @@  discard block
 block discarded – undo
44 44
             $this->_response->deactivate_plugin();
45 45
         }
46 46
         // check recommended PHP version
47
-        if (! $this->_minimum_php_version_recommended()) {
47
+        if ( ! $this->_minimum_php_version_recommended()) {
48 48
             $this->_display_minimum_recommended_php_version_notice();
49 49
         }
50 50
 
51 51
         //upcoming required version
52
-        if (! $this->upcomingRequiredPhpVersion()) {
52
+        if ( ! $this->upcomingRequiredPhpVersion()) {
53 53
             $this->displayUpcomingRequiredVersion();
54 54
         }
55 55
         $this->_response = $this->process_request_stack($this->_request, $this->_response);
@@ -172,9 +172,9 @@  discard block
 block discarded – undo
172 172
      */
173 173
     private function _display_minimum_recommended_php_version_notice()
174 174
     {
175
-        if($this->_request->isAdmin()){
175
+        if ($this->_request->isAdmin()) {
176 176
             new PersistentAdminNotice(
177
-                'php_version_' . str_replace('.', '-', EE_MIN_PHP_VER_RECOMMENDED) . '_recommended',
177
+                'php_version_'.str_replace('.', '-', EE_MIN_PHP_VER_RECOMMENDED).'_recommended',
178 178
                 sprintf(
179 179
                     __(
180 180
                         'Event Espresso recommends PHP version %1$s or greater for optimal performance. You are currently running version %2$s.%3$sIn order to update your version of PHP, you will need to contact your current hosting provider.%3$sFor information on stable PHP versions, please go to %4$s.',
@@ -199,7 +199,7 @@  discard block
 block discarded – undo
199 199
             && apply_filters('FHEE__EE_Recommended_Versions__displayUpcomingRequiredVersion', true, $this->_request)
200 200
             && current_user_can('update_plugins')
201 201
         ) {
202
-            add_action('admin_notices', function () {
202
+            add_action('admin_notices', function() {
203 203
                 echo '<div class="notice event-espresso-admin-notice notice-warning"><p>'
204 204
                      . sprintf(
205 205
                          esc_html__(
Please login to merge, or discard this patch.
admin_pages/events/Events_Admin_List_Table.class.php 1 patch
Indentation   +526 added lines, -526 removed lines patch added patch discarded remove patch
@@ -18,530 +18,530 @@
 block discarded – undo
18 18
 class Events_Admin_List_Table extends EE_Admin_List_Table
19 19
 {
20 20
 
21
-    /**
22
-     * @var EE_Datetime
23
-     */
24
-    private $_dtt;
25
-
26
-
27
-
28
-    /**
29
-     * Initial setup of data properties for the list table.
30
-     */
31
-    protected function _setup_data()
32
-    {
33
-        $this->_data = $this->_admin_page->get_events($this->_per_page, $this->_current_page);
34
-        $this->_all_data_count = $this->_admin_page->get_events(0, 0, true);
35
-    }
36
-
37
-
38
-
39
-    /**
40
-     * Set up of additional properties for the list table.
41
-     */
42
-    protected function _set_properties()
43
-    {
44
-        $this->_wp_list_args = array(
45
-            'singular' => esc_html__('event', 'event_espresso'),
46
-            'plural'   => esc_html__('events', 'event_espresso'),
47
-            'ajax'     => true, //for now
48
-            'screen'   => $this->_admin_page->get_current_screen()->id,
49
-        );
50
-        $this->_columns = array(
51
-            'cb'              => '<input type="checkbox" />',
52
-            'id'              => esc_html__('ID', 'event_espresso'),
53
-            'name'            => esc_html__('Name', 'event_espresso'),
54
-            'author'          => esc_html__('Author', 'event_espresso'),
55
-            'venue'           => esc_html__('Venue', 'event_espresso'),
56
-            'start_date_time' => esc_html__('Event Start', 'event_espresso'),
57
-            'reg_begins'      => esc_html__('On Sale', 'event_espresso'),
58
-            'attendees'       => '<span class="dashicons dashicons-groups ee-icon-color-ee-green ee-icon-size-20">'
59
-                                 . '<span class="screen-reader-text">'
60
-                                 . esc_html__('Approved Registrations', 'event_espresso')
61
-                                 . '</span>'
62
-                                 . '</span>',
63
-            //'tkts_sold' => esc_html__('Tickets Sold', 'event_espresso'),
64
-            'actions'         => esc_html__('Actions', 'event_espresso'),
65
-        );
66
-        $this->_sortable_columns = array(
67
-            'id'              => array('EVT_ID' => true),
68
-            'name'            => array('EVT_name' => false),
69
-            'author'          => array('EVT_wp_user' => false),
70
-            'venue'           => array('Venue.VNU_name' => false),
71
-            'start_date_time' => array('Datetime.DTT_EVT_start' => false),
72
-            'reg_begins'      => array('Datetime.Ticket.TKT_start_date' => false),
73
-        );
74
-        $this->_primary_column = 'id';
75
-        $this->_hidden_columns = array('author');
76
-    }
77
-
78
-
79
-
80
-    /**
81
-     * @return array
82
-     */
83
-    protected function _get_table_filters()
84
-    {
85
-        return array(); //no filters with decaf
86
-    }
87
-
88
-
89
-
90
-    /**
91
-     * Setup of views properties.
92
-     *
93
-     * @throws InvalidDataTypeException
94
-     * @throws InvalidInterfaceException
95
-     * @throws InvalidArgumentException
96
-     */
97
-    protected function _add_view_counts()
98
-    {
99
-        $this->_views['all']['count'] = $this->_admin_page->total_events();
100
-        $this->_views['draft']['count'] = $this->_admin_page->total_events_draft();
101
-        if (EE_Registry::instance()->CAP->current_user_can(
102
-            'ee_delete_events',
103
-            'espresso_events_trash_events'
104
-        )) {
105
-            $this->_views['trash']['count'] = $this->_admin_page->total_trashed_events();
106
-        }
107
-    }
108
-
109
-
110
-
111
-    /**
112
-     * @param EE_Event $item
113
-     * @return string
114
-     * @throws EE_Error
115
-     */
116
-    protected function _get_row_class($item)
117
-    {
118
-        $class = parent::_get_row_class($item);
119
-        //add status class
120
-        $class .= $item instanceof EE_Event
121
-            ? ' ee-status-strip event-status-' . $item->get_active_status()
122
-            : '';
123
-        if ($this->_has_checkbox_column) {
124
-            $class .= ' has-checkbox-column';
125
-        }
126
-        return $class;
127
-    }
128
-
129
-
130
-
131
-    /**
132
-     * @param EE_Event $item
133
-     * @return string
134
-     * @throws EE_Error
135
-     */
136
-    public function column_status(EE_Event $item)
137
-    {
138
-        return '<span class="ee-status-strip ee-status-strip-td event-status-'
139
-               . $item->get_active_status()
140
-               . '"></span>';
141
-    }
142
-
143
-
144
-
145
-    /**
146
-     * @param  EE_Event $item
147
-     * @return string
148
-     * @throws EE_Error
149
-     */
150
-    public function column_cb($item)
151
-    {
152
-        if (! $item instanceof EE_Event) {
153
-            return '';
154
-        }
155
-        $this->_dtt = $item->primary_datetime(); //set this for use in other columns
156
-        //does event have any attached registrations?
157
-        $regs = $item->count_related('Registration');
158
-        return $regs > 0 && $this->_view === 'trash'
159
-            ? '<span class="ee-lock-icon"></span>'
160
-            : sprintf(
161
-                '<input type="checkbox" name="EVT_IDs[]" value="%s" />',
162
-                $item->ID()
163
-            );
164
-    }
165
-
166
-
167
-
168
-    /**
169
-     * @param EE_Event $item
170
-     * @return mixed|string
171
-     * @throws EE_Error
172
-     */
173
-    public function column_id(EE_Event $item)
174
-    {
175
-        $content = $item->ID();
176
-        $content .= '  <span class="show-on-mobile-view-only">' . $item->name() . '</span>';
177
-        return $content;
178
-    }
179
-
180
-
181
-
182
-    /**
183
-     * @param EE_Event $item
184
-     * @return string
185
-     * @throws EE_Error
186
-     * @throws InvalidArgumentException
187
-     * @throws InvalidDataTypeException
188
-     * @throws InvalidInterfaceException
189
-     */
190
-    public function column_name(EE_Event $item)
191
-    {
192
-        $edit_query_args = array(
193
-            'action' => 'edit',
194
-            'post'   => $item->ID(),
195
-        );
196
-        $edit_link = EE_Admin_Page::add_query_args_and_nonce($edit_query_args, EVENTS_ADMIN_URL);
197
-        $actions = $this->_column_name_action_setup($item);
198
-        $status = ''; //$item->status() !== 'publish' ? ' (' . $item->status() . ')' : '';
199
-        $content = '<strong><a class="row-title" href="'
200
-                   . $edit_link . '">'
201
-                   . $item->name()
202
-                   . '</a></strong>'
203
-                   . $status;
204
-        $content .= '<br><span class="ee-status-text-small">'
205
-                    . EEH_Template::pretty_status(
206
-                $item->get_active_status(),
207
-                false,
208
-                'sentence'
209
-            )
210
-                    . '</span>';
211
-        $content .= $this->row_actions($actions);
212
-        return $content;
213
-    }
214
-
215
-
216
-
217
-    /**
218
-     * Just a method for setting up the actions for the name column
219
-     *
220
-     * @param EE_Event $item
221
-     * @return array array of actions
222
-     * @throws EE_Error
223
-     * @throws InvalidArgumentException
224
-     * @throws InvalidDataTypeException
225
-     * @throws InvalidInterfaceException
226
-     */
227
-    protected function _column_name_action_setup(EE_Event $item)
228
-    {
229
-        //todo: remove when attendees is active
230
-        if (! defined('REG_ADMIN_URL')) {
231
-            define('REG_ADMIN_URL', EVENTS_ADMIN_URL);
232
-        }
233
-        $actions = array();
234
-        $restore_event_link = '';
235
-        $delete_event_link = '';
236
-        $trash_event_link = '';
237
-        if (EE_Registry::instance()->CAP->current_user_can(
238
-            'ee_edit_event',
239
-            'espresso_events_edit',
240
-            $item->ID()
241
-        )) {
242
-            $edit_query_args = array(
243
-                'action' => 'edit',
244
-                'post'   => $item->ID(),
245
-            );
246
-            $edit_link = EE_Admin_Page::add_query_args_and_nonce($edit_query_args, EVENTS_ADMIN_URL);
247
-            $actions['edit'] = '<a href="' . $edit_link . '"'
248
-                               . ' title="' . esc_attr__('Edit Event', 'event_espresso') . '">'
249
-                               . esc_html__('Edit', 'event_espresso')
250
-                               . '</a>';
251
-        }
252
-        if (
253
-            EE_Registry::instance()->CAP->current_user_can(
254
-                'ee_read_registrations',
255
-                'espresso_registrations_view_registration'
256
-            )
257
-            && EE_Registry::instance()->CAP->current_user_can(
258
-                'ee_read_event',
259
-                'espresso_registrations_view_registration',
260
-                $item->ID()
261
-            )
262
-        ) {
263
-            $attendees_query_args = array(
264
-                'action'   => 'default',
265
-                'event_id' => $item->ID(),
266
-            );
267
-            $attendees_link = EE_Admin_Page::add_query_args_and_nonce($attendees_query_args, REG_ADMIN_URL);
268
-            $actions['attendees'] = '<a href="' . $attendees_link . '"'
269
-                                    . ' title="' . esc_attr__('View Registrations', 'event_espresso') . '">'
270
-                                    . esc_html__('Registrations', 'event_espresso')
271
-                                    . '</a>';
272
-        }
273
-        if (
274
-        EE_Registry::instance()->CAP->current_user_can(
275
-            'ee_delete_event',
276
-            'espresso_events_trash_event',
277
-            $item->ID()
278
-        )
279
-        ) {
280
-            $trash_event_query_args = array(
281
-                'action' => 'trash_event',
282
-                'EVT_ID' => $item->ID(),
283
-            );
284
-            $trash_event_link = EE_Admin_Page::add_query_args_and_nonce(
285
-                $trash_event_query_args,
286
-                EVENTS_ADMIN_URL
287
-            );
288
-        }
289
-        if (
290
-        EE_Registry::instance()->CAP->current_user_can(
291
-            'ee_delete_event',
292
-            'espresso_events_restore_event',
293
-            $item->ID()
294
-        )
295
-        ) {
296
-            $restore_event_query_args = array(
297
-                'action' => 'restore_event',
298
-                'EVT_ID' => $item->ID(),
299
-            );
300
-            $restore_event_link = EE_Admin_Page::add_query_args_and_nonce(
301
-                $restore_event_query_args,
302
-                EVENTS_ADMIN_URL
303
-            );
304
-        }
305
-        if (
306
-        EE_Registry::instance()->CAP->current_user_can(
307
-            'ee_delete_event',
308
-            'espresso_events_delete_event',
309
-            $item->ID()
310
-        )
311
-        ) {
312
-            $delete_event_query_args = array(
313
-                'action' => 'delete_event',
314
-                'EVT_ID' => $item->ID(),
315
-            );
316
-            $delete_event_link = EE_Admin_Page::add_query_args_and_nonce(
317
-                $delete_event_query_args,
318
-                EVENTS_ADMIN_URL
319
-            );
320
-        }
321
-        $view_link = get_permalink($item->ID());
322
-        $actions['view'] = '<a href="' . $view_link . '"'
323
-                           . ' title="' . esc_attr__('View Event', 'event_espresso') . '">'
324
-                           . esc_html__('View', 'event_espresso')
325
-                           . '</a>';
326
-        if ($item->get('status') === 'trash') {
327
-            if (EE_Registry::instance()->CAP->current_user_can(
328
-                'ee_delete_event',
329
-                'espresso_events_restore_event',
330
-                $item->ID()
331
-            )) {
332
-                $actions['restore_from_trash'] = '<a href="' . $restore_event_link . '"'
333
-                                                 . ' title="' . esc_attr__('Restore from Trash', 'event_espresso')
334
-                                                 . '">'
335
-                                                 . esc_html__('Restore from Trash', 'event_espresso')
336
-                                                 . '</a>';
337
-            }
338
-            if (
339
-                $item->count_related('Registration') === 0
340
-                && EE_Registry::instance()->CAP->current_user_can(
341
-                    'ee_delete_event',
342
-                    'espresso_events_delete_event',
343
-                    $item->ID()
344
-                )
345
-            ) {
346
-                $actions['delete'] = '<a href="' . $delete_event_link . '"'
347
-                                     . ' title="' . esc_attr__('Delete Permanently', 'event_espresso') . '">'
348
-                                     . esc_html__('Delete Permanently', 'event_espresso')
349
-                                     . '</a>';
350
-            }
351
-        } else {
352
-            if (
353
-                EE_Registry::instance()->CAP->current_user_can(
354
-                    'ee_delete_event',
355
-                    'espresso_events_trash_event',
356
-                    $item->ID()
357
-                )
358
-            ) {
359
-                $actions['move to trash'] = '<a href="' . $trash_event_link . '"'
360
-                                            . ' title="' . esc_attr__('Trash Event', 'event_espresso') . '">'
361
-                                            . esc_html__('Trash', 'event_espresso')
362
-                                            . '</a>';
363
-            }
364
-        }
365
-        return $actions;
366
-    }
367
-
368
-
369
-
370
-    /**
371
-     * @param EE_Event $item
372
-     * @return string
373
-     * @throws EE_Error
374
-     */
375
-    public function column_author(EE_Event $item)
376
-    {
377
-        //user author info
378
-        $event_author = get_userdata($item->wp_user());
379
-        $gravatar = get_avatar($item->wp_user(), '15');
380
-        //filter link
381
-        $query_args = array(
382
-            'action'      => 'default',
383
-            'EVT_wp_user' => $item->wp_user(),
384
-        );
385
-        $filter_url = EE_Admin_Page::add_query_args_and_nonce($query_args, EVENTS_ADMIN_URL);
386
-        return $gravatar . '  <a href="' . $filter_url . '"'
387
-               . ' title="' . esc_attr__('Click to filter events by this author.', 'event_espresso') . '">'
388
-               . $event_author->display_name
389
-               . '</a>';
390
-    }
391
-
392
-
393
-
394
-    /**
395
-     * @param EE_Event $item
396
-     * @return string
397
-     * @throws EE_Error
398
-     */
399
-    public function column_venue(EE_Event $item)
400
-    {
401
-        $venue = $item->get_first_related('Venue');
402
-        return ! empty($venue)
403
-            ? $venue->name()
404
-            : '';
405
-    }
406
-
407
-
408
-    /**
409
-     * @param EE_Event $item
410
-     * @return string
411
-     * @throws EE_Error
412
-     */
413
-    public function column_start_date_time(EE_Event $item)
414
-    {
415
-        return $this->_dtt instanceof EE_Datetime
416
-            ? $this->_dtt->get_i18n_datetime('DTT_EVT_start')
417
-            : esc_html__('No Date was saved for this Event', 'event_espresso');
418
-    }
419
-
420
-
421
-    /**
422
-     * @param EE_Event $item
423
-     * @return string
424
-     * @throws EE_Error
425
-     */
426
-    public function column_reg_begins(EE_Event $item)
427
-    {
428
-        $reg_start = $item->get_ticket_with_earliest_start_time();
429
-        return $reg_start instanceof EE_Ticket
430
-            ? $reg_start->get_i18n_datetime('TKT_start_date')
431
-            : esc_html__('No Tickets have been setup for this Event', 'event_espresso');
432
-    }
433
-
434
-
435
-
436
-    /**
437
-     * @param EE_Event $item
438
-     * @return int|string
439
-     * @throws EE_Error
440
-     * @throws InvalidArgumentException
441
-     * @throws InvalidDataTypeException
442
-     * @throws InvalidInterfaceException
443
-     */
444
-    public function column_attendees(EE_Event $item)
445
-    {
446
-        $attendees_query_args = array(
447
-            'action'   => 'default',
448
-            'event_id' => $item->ID(),
449
-        );
450
-        $attendees_link = EE_Admin_Page::add_query_args_and_nonce($attendees_query_args, REG_ADMIN_URL);
451
-        $registered_attendees = EEM_Registration::instance()->get_event_registration_count($item->ID());
452
-        return EE_Registry::instance()->CAP->current_user_can(
453
-            'ee_read_event',
454
-            'espresso_registrations_view_registration',
455
-            $item->ID()
456
-        )
457
-               && EE_Registry::instance()->CAP->current_user_can(
458
-            'ee_read_registrations',
459
-            'espresso_registrations_view_registration'
460
-        )
461
-            ? '<a href="' . $attendees_link . '">' . $registered_attendees . '</a>'
462
-            : $registered_attendees;
463
-    }
464
-
465
-
466
-
467
-    /**
468
-     * @param EE_Event $item
469
-     * @return float
470
-     * @throws EE_Error
471
-     * @throws InvalidArgumentException
472
-     * @throws InvalidDataTypeException
473
-     * @throws InvalidInterfaceException
474
-     */
475
-    public function column_tkts_sold(EE_Event $item)
476
-    {
477
-        return EEM_Ticket::instance()->sum(array(array('Datetime.EVT_ID' => $item->ID())), 'TKT_sold');
478
-    }
479
-
480
-
481
-
482
-    /**
483
-     * @param EE_Event $item
484
-     * @return string
485
-     * @throws EE_Error
486
-     * @throws InvalidArgumentException
487
-     * @throws InvalidDataTypeException
488
-     * @throws InvalidInterfaceException
489
-     */
490
-    public function column_actions(EE_Event $item)
491
-    {
492
-        //todo: remove when attendees is active
493
-        if (! defined('REG_ADMIN_URL')) {
494
-            define('REG_ADMIN_URL', EVENTS_ADMIN_URL);
495
-        }
496
-        $action_links = array();
497
-        $view_link = get_permalink($item->ID());
498
-        $action_links[] = '<a href="' . $view_link . '"'
499
-                         . ' title="' . esc_attr__('View Event', 'event_espresso') . '" target="_blank">';
500
-        $action_links[] = '<div class="dashicons dashicons-search"></div></a>';
501
-        if (EE_Registry::instance()->CAP->current_user_can(
502
-            'ee_edit_event',
503
-            'espresso_events_edit',
504
-            $item->ID()
505
-        )) {
506
-            $edit_query_args = array(
507
-                'action' => 'edit',
508
-                'post'   => $item->ID(),
509
-            );
510
-            $edit_link = EE_Admin_Page::add_query_args_and_nonce($edit_query_args, EVENTS_ADMIN_URL);
511
-            $action_links[] = '<a href="' . $edit_link . '"'
512
-                             . ' title="' . esc_attr__('Edit Event', 'event_espresso') . '">'
513
-                             . '<div class="ee-icon ee-icon-calendar-edit"></div>'
514
-                             . '</a>';
515
-        }
516
-        if (
517
-            EE_Registry::instance()->CAP->current_user_can(
518
-                'ee_read_registrations',
519
-                'espresso_registrations_view_registration'
520
-            ) && EE_Registry::instance()->CAP->current_user_can(
521
-                'ee_read_event',
522
-                'espresso_registrations_view_registration',
523
-                $item->ID()
524
-            )
525
-        ) {
526
-            $attendees_query_args = array(
527
-                'action'   => 'default',
528
-                'event_id' => $item->ID(),
529
-            );
530
-            $attendees_link = EE_Admin_Page::add_query_args_and_nonce($attendees_query_args, REG_ADMIN_URL);
531
-            $action_links[] = '<a href="' . $attendees_link . '"'
532
-                             . ' title="' . esc_attr__('View Registrants', 'event_espresso') . '">'
533
-                             . '<div class="dashicons dashicons-groups"></div>'
534
-                             . '</a>';
535
-        }
536
-        $action_links = apply_filters(
537
-            'FHEE__Events_Admin_List_Table__column_actions__action_links',
538
-            $action_links,
539
-            $item
540
-        );
541
-        return $this->_action_string(
542
-            implode("\n\t", $action_links),
543
-            $item,
544
-            'div'
545
-        );
546
-    }
21
+	/**
22
+	 * @var EE_Datetime
23
+	 */
24
+	private $_dtt;
25
+
26
+
27
+
28
+	/**
29
+	 * Initial setup of data properties for the list table.
30
+	 */
31
+	protected function _setup_data()
32
+	{
33
+		$this->_data = $this->_admin_page->get_events($this->_per_page, $this->_current_page);
34
+		$this->_all_data_count = $this->_admin_page->get_events(0, 0, true);
35
+	}
36
+
37
+
38
+
39
+	/**
40
+	 * Set up of additional properties for the list table.
41
+	 */
42
+	protected function _set_properties()
43
+	{
44
+		$this->_wp_list_args = array(
45
+			'singular' => esc_html__('event', 'event_espresso'),
46
+			'plural'   => esc_html__('events', 'event_espresso'),
47
+			'ajax'     => true, //for now
48
+			'screen'   => $this->_admin_page->get_current_screen()->id,
49
+		);
50
+		$this->_columns = array(
51
+			'cb'              => '<input type="checkbox" />',
52
+			'id'              => esc_html__('ID', 'event_espresso'),
53
+			'name'            => esc_html__('Name', 'event_espresso'),
54
+			'author'          => esc_html__('Author', 'event_espresso'),
55
+			'venue'           => esc_html__('Venue', 'event_espresso'),
56
+			'start_date_time' => esc_html__('Event Start', 'event_espresso'),
57
+			'reg_begins'      => esc_html__('On Sale', 'event_espresso'),
58
+			'attendees'       => '<span class="dashicons dashicons-groups ee-icon-color-ee-green ee-icon-size-20">'
59
+								 . '<span class="screen-reader-text">'
60
+								 . esc_html__('Approved Registrations', 'event_espresso')
61
+								 . '</span>'
62
+								 . '</span>',
63
+			//'tkts_sold' => esc_html__('Tickets Sold', 'event_espresso'),
64
+			'actions'         => esc_html__('Actions', 'event_espresso'),
65
+		);
66
+		$this->_sortable_columns = array(
67
+			'id'              => array('EVT_ID' => true),
68
+			'name'            => array('EVT_name' => false),
69
+			'author'          => array('EVT_wp_user' => false),
70
+			'venue'           => array('Venue.VNU_name' => false),
71
+			'start_date_time' => array('Datetime.DTT_EVT_start' => false),
72
+			'reg_begins'      => array('Datetime.Ticket.TKT_start_date' => false),
73
+		);
74
+		$this->_primary_column = 'id';
75
+		$this->_hidden_columns = array('author');
76
+	}
77
+
78
+
79
+
80
+	/**
81
+	 * @return array
82
+	 */
83
+	protected function _get_table_filters()
84
+	{
85
+		return array(); //no filters with decaf
86
+	}
87
+
88
+
89
+
90
+	/**
91
+	 * Setup of views properties.
92
+	 *
93
+	 * @throws InvalidDataTypeException
94
+	 * @throws InvalidInterfaceException
95
+	 * @throws InvalidArgumentException
96
+	 */
97
+	protected function _add_view_counts()
98
+	{
99
+		$this->_views['all']['count'] = $this->_admin_page->total_events();
100
+		$this->_views['draft']['count'] = $this->_admin_page->total_events_draft();
101
+		if (EE_Registry::instance()->CAP->current_user_can(
102
+			'ee_delete_events',
103
+			'espresso_events_trash_events'
104
+		)) {
105
+			$this->_views['trash']['count'] = $this->_admin_page->total_trashed_events();
106
+		}
107
+	}
108
+
109
+
110
+
111
+	/**
112
+	 * @param EE_Event $item
113
+	 * @return string
114
+	 * @throws EE_Error
115
+	 */
116
+	protected function _get_row_class($item)
117
+	{
118
+		$class = parent::_get_row_class($item);
119
+		//add status class
120
+		$class .= $item instanceof EE_Event
121
+			? ' ee-status-strip event-status-' . $item->get_active_status()
122
+			: '';
123
+		if ($this->_has_checkbox_column) {
124
+			$class .= ' has-checkbox-column';
125
+		}
126
+		return $class;
127
+	}
128
+
129
+
130
+
131
+	/**
132
+	 * @param EE_Event $item
133
+	 * @return string
134
+	 * @throws EE_Error
135
+	 */
136
+	public function column_status(EE_Event $item)
137
+	{
138
+		return '<span class="ee-status-strip ee-status-strip-td event-status-'
139
+			   . $item->get_active_status()
140
+			   . '"></span>';
141
+	}
142
+
143
+
144
+
145
+	/**
146
+	 * @param  EE_Event $item
147
+	 * @return string
148
+	 * @throws EE_Error
149
+	 */
150
+	public function column_cb($item)
151
+	{
152
+		if (! $item instanceof EE_Event) {
153
+			return '';
154
+		}
155
+		$this->_dtt = $item->primary_datetime(); //set this for use in other columns
156
+		//does event have any attached registrations?
157
+		$regs = $item->count_related('Registration');
158
+		return $regs > 0 && $this->_view === 'trash'
159
+			? '<span class="ee-lock-icon"></span>'
160
+			: sprintf(
161
+				'<input type="checkbox" name="EVT_IDs[]" value="%s" />',
162
+				$item->ID()
163
+			);
164
+	}
165
+
166
+
167
+
168
+	/**
169
+	 * @param EE_Event $item
170
+	 * @return mixed|string
171
+	 * @throws EE_Error
172
+	 */
173
+	public function column_id(EE_Event $item)
174
+	{
175
+		$content = $item->ID();
176
+		$content .= '  <span class="show-on-mobile-view-only">' . $item->name() . '</span>';
177
+		return $content;
178
+	}
179
+
180
+
181
+
182
+	/**
183
+	 * @param EE_Event $item
184
+	 * @return string
185
+	 * @throws EE_Error
186
+	 * @throws InvalidArgumentException
187
+	 * @throws InvalidDataTypeException
188
+	 * @throws InvalidInterfaceException
189
+	 */
190
+	public function column_name(EE_Event $item)
191
+	{
192
+		$edit_query_args = array(
193
+			'action' => 'edit',
194
+			'post'   => $item->ID(),
195
+		);
196
+		$edit_link = EE_Admin_Page::add_query_args_and_nonce($edit_query_args, EVENTS_ADMIN_URL);
197
+		$actions = $this->_column_name_action_setup($item);
198
+		$status = ''; //$item->status() !== 'publish' ? ' (' . $item->status() . ')' : '';
199
+		$content = '<strong><a class="row-title" href="'
200
+				   . $edit_link . '">'
201
+				   . $item->name()
202
+				   . '</a></strong>'
203
+				   . $status;
204
+		$content .= '<br><span class="ee-status-text-small">'
205
+					. EEH_Template::pretty_status(
206
+				$item->get_active_status(),
207
+				false,
208
+				'sentence'
209
+			)
210
+					. '</span>';
211
+		$content .= $this->row_actions($actions);
212
+		return $content;
213
+	}
214
+
215
+
216
+
217
+	/**
218
+	 * Just a method for setting up the actions for the name column
219
+	 *
220
+	 * @param EE_Event $item
221
+	 * @return array array of actions
222
+	 * @throws EE_Error
223
+	 * @throws InvalidArgumentException
224
+	 * @throws InvalidDataTypeException
225
+	 * @throws InvalidInterfaceException
226
+	 */
227
+	protected function _column_name_action_setup(EE_Event $item)
228
+	{
229
+		//todo: remove when attendees is active
230
+		if (! defined('REG_ADMIN_URL')) {
231
+			define('REG_ADMIN_URL', EVENTS_ADMIN_URL);
232
+		}
233
+		$actions = array();
234
+		$restore_event_link = '';
235
+		$delete_event_link = '';
236
+		$trash_event_link = '';
237
+		if (EE_Registry::instance()->CAP->current_user_can(
238
+			'ee_edit_event',
239
+			'espresso_events_edit',
240
+			$item->ID()
241
+		)) {
242
+			$edit_query_args = array(
243
+				'action' => 'edit',
244
+				'post'   => $item->ID(),
245
+			);
246
+			$edit_link = EE_Admin_Page::add_query_args_and_nonce($edit_query_args, EVENTS_ADMIN_URL);
247
+			$actions['edit'] = '<a href="' . $edit_link . '"'
248
+							   . ' title="' . esc_attr__('Edit Event', 'event_espresso') . '">'
249
+							   . esc_html__('Edit', 'event_espresso')
250
+							   . '</a>';
251
+		}
252
+		if (
253
+			EE_Registry::instance()->CAP->current_user_can(
254
+				'ee_read_registrations',
255
+				'espresso_registrations_view_registration'
256
+			)
257
+			&& EE_Registry::instance()->CAP->current_user_can(
258
+				'ee_read_event',
259
+				'espresso_registrations_view_registration',
260
+				$item->ID()
261
+			)
262
+		) {
263
+			$attendees_query_args = array(
264
+				'action'   => 'default',
265
+				'event_id' => $item->ID(),
266
+			);
267
+			$attendees_link = EE_Admin_Page::add_query_args_and_nonce($attendees_query_args, REG_ADMIN_URL);
268
+			$actions['attendees'] = '<a href="' . $attendees_link . '"'
269
+									. ' title="' . esc_attr__('View Registrations', 'event_espresso') . '">'
270
+									. esc_html__('Registrations', 'event_espresso')
271
+									. '</a>';
272
+		}
273
+		if (
274
+		EE_Registry::instance()->CAP->current_user_can(
275
+			'ee_delete_event',
276
+			'espresso_events_trash_event',
277
+			$item->ID()
278
+		)
279
+		) {
280
+			$trash_event_query_args = array(
281
+				'action' => 'trash_event',
282
+				'EVT_ID' => $item->ID(),
283
+			);
284
+			$trash_event_link = EE_Admin_Page::add_query_args_and_nonce(
285
+				$trash_event_query_args,
286
+				EVENTS_ADMIN_URL
287
+			);
288
+		}
289
+		if (
290
+		EE_Registry::instance()->CAP->current_user_can(
291
+			'ee_delete_event',
292
+			'espresso_events_restore_event',
293
+			$item->ID()
294
+		)
295
+		) {
296
+			$restore_event_query_args = array(
297
+				'action' => 'restore_event',
298
+				'EVT_ID' => $item->ID(),
299
+			);
300
+			$restore_event_link = EE_Admin_Page::add_query_args_and_nonce(
301
+				$restore_event_query_args,
302
+				EVENTS_ADMIN_URL
303
+			);
304
+		}
305
+		if (
306
+		EE_Registry::instance()->CAP->current_user_can(
307
+			'ee_delete_event',
308
+			'espresso_events_delete_event',
309
+			$item->ID()
310
+		)
311
+		) {
312
+			$delete_event_query_args = array(
313
+				'action' => 'delete_event',
314
+				'EVT_ID' => $item->ID(),
315
+			);
316
+			$delete_event_link = EE_Admin_Page::add_query_args_and_nonce(
317
+				$delete_event_query_args,
318
+				EVENTS_ADMIN_URL
319
+			);
320
+		}
321
+		$view_link = get_permalink($item->ID());
322
+		$actions['view'] = '<a href="' . $view_link . '"'
323
+						   . ' title="' . esc_attr__('View Event', 'event_espresso') . '">'
324
+						   . esc_html__('View', 'event_espresso')
325
+						   . '</a>';
326
+		if ($item->get('status') === 'trash') {
327
+			if (EE_Registry::instance()->CAP->current_user_can(
328
+				'ee_delete_event',
329
+				'espresso_events_restore_event',
330
+				$item->ID()
331
+			)) {
332
+				$actions['restore_from_trash'] = '<a href="' . $restore_event_link . '"'
333
+												 . ' title="' . esc_attr__('Restore from Trash', 'event_espresso')
334
+												 . '">'
335
+												 . esc_html__('Restore from Trash', 'event_espresso')
336
+												 . '</a>';
337
+			}
338
+			if (
339
+				$item->count_related('Registration') === 0
340
+				&& EE_Registry::instance()->CAP->current_user_can(
341
+					'ee_delete_event',
342
+					'espresso_events_delete_event',
343
+					$item->ID()
344
+				)
345
+			) {
346
+				$actions['delete'] = '<a href="' . $delete_event_link . '"'
347
+									 . ' title="' . esc_attr__('Delete Permanently', 'event_espresso') . '">'
348
+									 . esc_html__('Delete Permanently', 'event_espresso')
349
+									 . '</a>';
350
+			}
351
+		} else {
352
+			if (
353
+				EE_Registry::instance()->CAP->current_user_can(
354
+					'ee_delete_event',
355
+					'espresso_events_trash_event',
356
+					$item->ID()
357
+				)
358
+			) {
359
+				$actions['move to trash'] = '<a href="' . $trash_event_link . '"'
360
+											. ' title="' . esc_attr__('Trash Event', 'event_espresso') . '">'
361
+											. esc_html__('Trash', 'event_espresso')
362
+											. '</a>';
363
+			}
364
+		}
365
+		return $actions;
366
+	}
367
+
368
+
369
+
370
+	/**
371
+	 * @param EE_Event $item
372
+	 * @return string
373
+	 * @throws EE_Error
374
+	 */
375
+	public function column_author(EE_Event $item)
376
+	{
377
+		//user author info
378
+		$event_author = get_userdata($item->wp_user());
379
+		$gravatar = get_avatar($item->wp_user(), '15');
380
+		//filter link
381
+		$query_args = array(
382
+			'action'      => 'default',
383
+			'EVT_wp_user' => $item->wp_user(),
384
+		);
385
+		$filter_url = EE_Admin_Page::add_query_args_and_nonce($query_args, EVENTS_ADMIN_URL);
386
+		return $gravatar . '  <a href="' . $filter_url . '"'
387
+			   . ' title="' . esc_attr__('Click to filter events by this author.', 'event_espresso') . '">'
388
+			   . $event_author->display_name
389
+			   . '</a>';
390
+	}
391
+
392
+
393
+
394
+	/**
395
+	 * @param EE_Event $item
396
+	 * @return string
397
+	 * @throws EE_Error
398
+	 */
399
+	public function column_venue(EE_Event $item)
400
+	{
401
+		$venue = $item->get_first_related('Venue');
402
+		return ! empty($venue)
403
+			? $venue->name()
404
+			: '';
405
+	}
406
+
407
+
408
+	/**
409
+	 * @param EE_Event $item
410
+	 * @return string
411
+	 * @throws EE_Error
412
+	 */
413
+	public function column_start_date_time(EE_Event $item)
414
+	{
415
+		return $this->_dtt instanceof EE_Datetime
416
+			? $this->_dtt->get_i18n_datetime('DTT_EVT_start')
417
+			: esc_html__('No Date was saved for this Event', 'event_espresso');
418
+	}
419
+
420
+
421
+	/**
422
+	 * @param EE_Event $item
423
+	 * @return string
424
+	 * @throws EE_Error
425
+	 */
426
+	public function column_reg_begins(EE_Event $item)
427
+	{
428
+		$reg_start = $item->get_ticket_with_earliest_start_time();
429
+		return $reg_start instanceof EE_Ticket
430
+			? $reg_start->get_i18n_datetime('TKT_start_date')
431
+			: esc_html__('No Tickets have been setup for this Event', 'event_espresso');
432
+	}
433
+
434
+
435
+
436
+	/**
437
+	 * @param EE_Event $item
438
+	 * @return int|string
439
+	 * @throws EE_Error
440
+	 * @throws InvalidArgumentException
441
+	 * @throws InvalidDataTypeException
442
+	 * @throws InvalidInterfaceException
443
+	 */
444
+	public function column_attendees(EE_Event $item)
445
+	{
446
+		$attendees_query_args = array(
447
+			'action'   => 'default',
448
+			'event_id' => $item->ID(),
449
+		);
450
+		$attendees_link = EE_Admin_Page::add_query_args_and_nonce($attendees_query_args, REG_ADMIN_URL);
451
+		$registered_attendees = EEM_Registration::instance()->get_event_registration_count($item->ID());
452
+		return EE_Registry::instance()->CAP->current_user_can(
453
+			'ee_read_event',
454
+			'espresso_registrations_view_registration',
455
+			$item->ID()
456
+		)
457
+			   && EE_Registry::instance()->CAP->current_user_can(
458
+			'ee_read_registrations',
459
+			'espresso_registrations_view_registration'
460
+		)
461
+			? '<a href="' . $attendees_link . '">' . $registered_attendees . '</a>'
462
+			: $registered_attendees;
463
+	}
464
+
465
+
466
+
467
+	/**
468
+	 * @param EE_Event $item
469
+	 * @return float
470
+	 * @throws EE_Error
471
+	 * @throws InvalidArgumentException
472
+	 * @throws InvalidDataTypeException
473
+	 * @throws InvalidInterfaceException
474
+	 */
475
+	public function column_tkts_sold(EE_Event $item)
476
+	{
477
+		return EEM_Ticket::instance()->sum(array(array('Datetime.EVT_ID' => $item->ID())), 'TKT_sold');
478
+	}
479
+
480
+
481
+
482
+	/**
483
+	 * @param EE_Event $item
484
+	 * @return string
485
+	 * @throws EE_Error
486
+	 * @throws InvalidArgumentException
487
+	 * @throws InvalidDataTypeException
488
+	 * @throws InvalidInterfaceException
489
+	 */
490
+	public function column_actions(EE_Event $item)
491
+	{
492
+		//todo: remove when attendees is active
493
+		if (! defined('REG_ADMIN_URL')) {
494
+			define('REG_ADMIN_URL', EVENTS_ADMIN_URL);
495
+		}
496
+		$action_links = array();
497
+		$view_link = get_permalink($item->ID());
498
+		$action_links[] = '<a href="' . $view_link . '"'
499
+						 . ' title="' . esc_attr__('View Event', 'event_espresso') . '" target="_blank">';
500
+		$action_links[] = '<div class="dashicons dashicons-search"></div></a>';
501
+		if (EE_Registry::instance()->CAP->current_user_can(
502
+			'ee_edit_event',
503
+			'espresso_events_edit',
504
+			$item->ID()
505
+		)) {
506
+			$edit_query_args = array(
507
+				'action' => 'edit',
508
+				'post'   => $item->ID(),
509
+			);
510
+			$edit_link = EE_Admin_Page::add_query_args_and_nonce($edit_query_args, EVENTS_ADMIN_URL);
511
+			$action_links[] = '<a href="' . $edit_link . '"'
512
+							 . ' title="' . esc_attr__('Edit Event', 'event_espresso') . '">'
513
+							 . '<div class="ee-icon ee-icon-calendar-edit"></div>'
514
+							 . '</a>';
515
+		}
516
+		if (
517
+			EE_Registry::instance()->CAP->current_user_can(
518
+				'ee_read_registrations',
519
+				'espresso_registrations_view_registration'
520
+			) && EE_Registry::instance()->CAP->current_user_can(
521
+				'ee_read_event',
522
+				'espresso_registrations_view_registration',
523
+				$item->ID()
524
+			)
525
+		) {
526
+			$attendees_query_args = array(
527
+				'action'   => 'default',
528
+				'event_id' => $item->ID(),
529
+			);
530
+			$attendees_link = EE_Admin_Page::add_query_args_and_nonce($attendees_query_args, REG_ADMIN_URL);
531
+			$action_links[] = '<a href="' . $attendees_link . '"'
532
+							 . ' title="' . esc_attr__('View Registrants', 'event_espresso') . '">'
533
+							 . '<div class="dashicons dashicons-groups"></div>'
534
+							 . '</a>';
535
+		}
536
+		$action_links = apply_filters(
537
+			'FHEE__Events_Admin_List_Table__column_actions__action_links',
538
+			$action_links,
539
+			$item
540
+		);
541
+		return $this->_action_string(
542
+			implode("\n\t", $action_links),
543
+			$item,
544
+			'div'
545
+		);
546
+	}
547 547
 }
Please login to merge, or discard this patch.