Completed
Branch dev (3710f0)
by
unknown
25:03 queued 15:31
created
core/db_models/EEM_Event.model.php 1 patch
Indentation   +928 added lines, -928 removed lines patch added patch discarded remove patch
@@ -13,932 +13,932 @@
 block discarded – undo
13 13
  */
14 14
 class EEM_Event extends EEM_CPT_Base
15 15
 {
16
-    /**
17
-     * constant used by status(), indicating that no more tickets can be purchased for any of the datetimes for the
18
-     * event
19
-     */
20
-    const sold_out = 'sold_out';
21
-
22
-    /**
23
-     * constant used by status(), indicating that upcoming event dates have been postponed (may be pushed to a later
24
-     * date)
25
-     */
26
-    const postponed = 'postponed';
27
-
28
-    /**
29
-     * constant used by status(), indicating that the event will no longer occur
30
-     */
31
-    const cancelled = 'cancelled';
32
-
33
-
34
-    /**
35
-     * @var string
36
-     */
37
-    protected static $_default_reg_status;
38
-
39
-
40
-    /**
41
-     * This is the default for the additional limit field.
42
-     * @var int
43
-     */
44
-    protected static $_default_additional_limit = 10;
45
-
46
-
47
-    /**
48
-     * private instance of the Event object
49
-     *
50
-     * @var EEM_Event
51
-     */
52
-    protected static $_instance;
53
-
54
-
55
-    /**
56
-     * Adds a relationship to Term_Taxonomy for each CPT_Base
57
-     *
58
-     * @param string $timezone
59
-     * @throws EE_Error
60
-     * @throws ReflectionException
61
-     */
62
-    protected function __construct($timezone = null)
63
-    {
64
-        EE_Registry::instance()->load_model('Registration');
65
-        $this->singular_item = esc_html__('Event', 'event_espresso');
66
-        $this->plural_item = esc_html__('Events', 'event_espresso');
67
-        // to remove Cancelled events from the frontend, copy the following filter to your functions.php file
68
-        // add_filter( 'AFEE__EEM_Event__construct___custom_stati__cancelled__Public', '__return_false' );
69
-        // to remove Postponed events from the frontend, copy the following filter to your functions.php file
70
-        // add_filter( 'AFEE__EEM_Event__construct___custom_stati__postponed__Public', '__return_false' );
71
-        // to remove Sold Out events from the frontend, copy the following filter to your functions.php file
72
-        //  add_filter( 'AFEE__EEM_Event__construct___custom_stati__sold_out__Public', '__return_false' );
73
-        $this->_custom_stati = apply_filters(
74
-            'AFEE__EEM_Event__construct___custom_stati',
75
-            array(
76
-                EEM_Event::cancelled => array(
77
-                    'label'  => esc_html__('Cancelled', 'event_espresso'),
78
-                    'public' => apply_filters('AFEE__EEM_Event__construct___custom_stati__cancelled__Public', true),
79
-                ),
80
-                EEM_Event::postponed => array(
81
-                    'label'  => esc_html__('Postponed', 'event_espresso'),
82
-                    'public' => apply_filters('AFEE__EEM_Event__construct___custom_stati__postponed__Public', true),
83
-                ),
84
-                EEM_Event::sold_out  => array(
85
-                    'label'  => esc_html__('Sold Out', 'event_espresso'),
86
-                    'public' => apply_filters('AFEE__EEM_Event__construct___custom_stati__sold_out__Public', true),
87
-                ),
88
-            )
89
-        );
90
-        self::$_default_reg_status = empty(self::$_default_reg_status)
91
-            ? EEM_Registration::status_id_pending_payment
92
-            : self::$_default_reg_status;
93
-        $this->_tables = array(
94
-            'Event_CPT'  => new EE_Primary_Table('posts', 'ID'),
95
-            'Event_Meta' => new EE_Secondary_Table('esp_event_meta', 'EVTM_ID', 'EVT_ID'),
96
-        );
97
-        $this->_fields = array(
98
-            'Event_CPT'  => array(
99
-                'EVT_ID'         => new EE_Primary_Key_Int_Field(
100
-                    'ID',
101
-                    esc_html__('Post ID for Event', 'event_espresso')
102
-                ),
103
-                'EVT_name'       => new EE_Plain_Text_Field(
104
-                    'post_title',
105
-                    esc_html__('Event Name', 'event_espresso'),
106
-                    false,
107
-                    ''
108
-                ),
109
-                'EVT_desc'       => new EE_Post_Content_Field(
110
-                    'post_content',
111
-                    esc_html__('Event Description', 'event_espresso'),
112
-                    false,
113
-                    ''
114
-                ),
115
-                'EVT_slug'       => new EE_Slug_Field(
116
-                    'post_name',
117
-                    esc_html__('Event Slug', 'event_espresso'),
118
-                    false,
119
-                    ''
120
-                ),
121
-                'EVT_created'    => new EE_Datetime_Field(
122
-                    'post_date',
123
-                    esc_html__('Date/Time Event Created', 'event_espresso'),
124
-                    false,
125
-                    EE_Datetime_Field::now
126
-                ),
127
-                'EVT_short_desc' => new EE_Simple_HTML_Field(
128
-                    'post_excerpt',
129
-                    esc_html__('Event Short Description', 'event_espresso'),
130
-                    false,
131
-                    ''
132
-                ),
133
-                'EVT_modified'   => new EE_Datetime_Field(
134
-                    'post_modified',
135
-                    esc_html__('Date/Time Event Modified', 'event_espresso'),
136
-                    false,
137
-                    EE_Datetime_Field::now
138
-                ),
139
-                'EVT_wp_user'    => new EE_WP_User_Field(
140
-                    'post_author',
141
-                    esc_html__('Event Creator ID', 'event_espresso'),
142
-                    false
143
-                ),
144
-                'parent'         => new EE_Integer_Field(
145
-                    'post_parent',
146
-                    esc_html__('Event Parent ID', 'event_espresso'),
147
-                    false,
148
-                    0
149
-                ),
150
-                'EVT_order'      => new EE_Integer_Field(
151
-                    'menu_order',
152
-                    esc_html__('Event Menu Order', 'event_espresso'),
153
-                    false,
154
-                    1
155
-                ),
156
-                'post_type'      => new EE_WP_Post_Type_Field('espresso_events'),
157
-                // EE_Plain_Text_Field( 'post_type', esc_html__( 'Event Post Type', 'event_espresso' ), FALSE, 'espresso_events' ),
158
-                'status'         => new EE_WP_Post_Status_Field(
159
-                    'post_status',
160
-                    esc_html__('Event Status', 'event_espresso'),
161
-                    false,
162
-                    'draft',
163
-                    $this->_custom_stati
164
-                ),
165
-                'password' => new EE_Password_Field(
166
-                    'post_password',
167
-                    esc_html__('Password', 'event_espresso'),
168
-                    false,
169
-                    '',
170
-                    array(
171
-                        'EVT_desc',
172
-                        'EVT_short_desc',
173
-                        'EVT_display_desc',
174
-                        'EVT_display_ticket_selector',
175
-                        'EVT_visible_on',
176
-                        'EVT_additional_limit',
177
-                        'EVT_default_registration_status',
178
-                        'EVT_member_only',
179
-                        'EVT_phone',
180
-                        'EVT_allow_overflow',
181
-                        'EVT_timezone_string',
182
-                        'EVT_external_URL',
183
-                        'EVT_donations'
184
-                    )
185
-                )
186
-            ),
187
-            'Event_Meta' => array(
188
-                'EVTM_ID'                         => new EE_DB_Only_Float_Field(
189
-                    'EVTM_ID',
190
-                    esc_html__('Event Meta Row ID', 'event_espresso'),
191
-                    false
192
-                ),
193
-                'EVT_ID_fk'                       => new EE_DB_Only_Int_Field(
194
-                    'EVT_ID',
195
-                    esc_html__('Foreign key to Event ID from Event Meta table', 'event_espresso'),
196
-                    false
197
-                ),
198
-                'VNU_ID' => new EE_Foreign_Key_Int_Field(
199
-                    'VNU_ID',
200
-                    __('Venue ID', 'event_espresso'),
201
-                    false,
202
-                    0,
203
-                    'Venue'
204
-                ),
205
-                'EVT_display_desc'                => new EE_Boolean_Field(
206
-                    'EVT_display_desc',
207
-                    esc_html__('Display Description Flag', 'event_espresso'),
208
-                    false,
209
-                    true
210
-                ),
211
-                'EVT_display_ticket_selector'     => new EE_Boolean_Field(
212
-                    'EVT_display_ticket_selector',
213
-                    esc_html__('Display Ticket Selector Flag', 'event_espresso'),
214
-                    false,
215
-                    true
216
-                ),
217
-                'EVT_visible_on'                  => new EE_Datetime_Field(
218
-                    'EVT_visible_on',
219
-                    esc_html__('Event Visible Date', 'event_espresso'),
220
-                    true,
221
-                    EE_Datetime_Field::now
222
-                ),
223
-                'EVT_additional_limit'            => new EE_Integer_Field(
224
-                    'EVT_additional_limit',
225
-                    esc_html__('Limit of Additional Registrations on Same Transaction', 'event_espresso'),
226
-                    false,
227
-                    self::$_default_additional_limit
228
-                ),
229
-                'EVT_default_registration_status' => new EE_Enum_Text_Field(
230
-                    'EVT_default_registration_status',
231
-                    esc_html__('Default Registration Status on this Event', 'event_espresso'),
232
-                    false,
233
-                    EEM_Event::$_default_reg_status,
234
-                    EEM_Registration::reg_status_array()
235
-                ),
236
-                'EVT_member_only'                 => new EE_Boolean_Field(
237
-                    'EVT_member_only',
238
-                    esc_html__('Member-Only Event Flag', 'event_espresso'),
239
-                    false,
240
-                    false
241
-                ),
242
-                'EVT_phone'                       => new EE_Plain_Text_Field(
243
-                    'EVT_phone',
244
-                    esc_html__('Event Phone Number', 'event_espresso'),
245
-                    false,
246
-                    ''
247
-                ),
248
-                'EVT_allow_overflow'              => new EE_Boolean_Field(
249
-                    'EVT_allow_overflow',
250
-                    esc_html__('Allow Overflow on Event', 'event_espresso'),
251
-                    false,
252
-                    false
253
-                ),
254
-                'EVT_timezone_string'             => new EE_Plain_Text_Field(
255
-                    'EVT_timezone_string',
256
-                    esc_html__('Timezone (name) for Event times', 'event_espresso'),
257
-                    false,
258
-                    ''
259
-                ),
260
-                'EVT_external_URL'                => new EE_Plain_Text_Field(
261
-                    'EVT_external_URL',
262
-                    esc_html__('URL of Event Page if hosted elsewhere', 'event_espresso'),
263
-                    true
264
-                ),
265
-                'EVT_donations'                   => new EE_Boolean_Field(
266
-                    'EVT_donations',
267
-                    esc_html__('Accept Donations?', 'event_espresso'),
268
-                    false,
269
-                    false
270
-                ),
271
-                'FSC_UUID'                        => new EE_Foreign_Key_String_Field(
272
-                    'FSC_UUID',
273
-                    esc_html__('Registration Form UUID (universally unique identifier)', 'event_espresso'),
274
-                    true,
275
-                    null,
276
-                    'Form_Section',
277
-                    false
278
-                ),
279
-            ),
280
-        );
281
-        $this->_model_relations = array(
282
-            'Attendee'               => new EE_HABTM_Relation('Registration'),
283
-            'Datetime'               => new EE_Has_Many_Relation(),
284
-            'Event_Question_Group'   => new EE_Has_Many_Relation(),
285
-            'Form_Section'           => new EE_Belongs_To_Relation(),
286
-            'Message_Template_Group' => new EE_HABTM_Relation('Event_Message_Template'),
287
-            'Question_Group'         => new EE_HABTM_Relation('Event_Question_Group'),
288
-            'Registration'           => new EE_Has_Many_Relation(),
289
-            'Term_Relationship'      => new EE_Has_Many_Relation(),
290
-            'Term_Taxonomy'          => new EE_HABTM_Relation('Term_Relationship'),
291
-            'Venue'                  => new EE_Belongs_To_Relation(),
292
-            'WP_User'                => new EE_Belongs_To_Relation(),
293
-        );
294
-        // this model is generally available for reading
295
-        $this->_cap_restriction_generators[ EEM_Base::caps_read ] = new EE_Restriction_Generator_Public();
296
-        $this->model_chain_to_password = '';
297
-        parent::__construct($timezone);
298
-    }
299
-
300
-
301
-    /**
302
-     * @param string $default_reg_status
303
-     * @throws EE_Error
304
-     * @throws EE_Error
305
-     */
306
-    public static function set_default_reg_status($default_reg_status)
307
-    {
308
-        self::$_default_reg_status = $default_reg_status;
309
-        // if EEM_Event has already been instantiated,
310
-        // then we need to reset the `EVT_default_reg_status` field to use the new default.
311
-        if (self::$_instance instanceof EEM_Event) {
312
-            $default_reg_status = new EE_Enum_Text_Field(
313
-                'EVT_default_registration_status',
314
-                esc_html__('Default Registration Status on this Event', 'event_espresso'),
315
-                false,
316
-                $default_reg_status,
317
-                EEM_Registration::reg_status_array()
318
-            );
319
-            $default_reg_status->_construct_finalize(
320
-                'Event_Meta',
321
-                'EVT_default_registration_status',
322
-                'EEM_Event'
323
-            );
324
-            self::$_instance->_fields['Event_Meta']['EVT_default_registration_status'] = $default_reg_status;
325
-        }
326
-    }
327
-
328
-
329
-    /**
330
-     * Used to override the default for the additional limit field.
331
-     * @param $additional_limit
332
-     */
333
-    public static function set_default_additional_limit($additional_limit)
334
-    {
335
-        self::$_default_additional_limit = (int) $additional_limit;
336
-        if (self::$_instance instanceof EEM_Event) {
337
-            self::$_instance->_fields['Event_Meta']['EVT_additional_limit'] = new EE_Integer_Field(
338
-                'EVT_additional_limit',
339
-                esc_html__('Limit of Additional Registrations on Same Transaction', 'event_espresso'),
340
-                false,
341
-                self::$_default_additional_limit
342
-            );
343
-            self::$_instance->_fields['Event_Meta']['EVT_additional_limit']->_construct_finalize(
344
-                'Event_Meta',
345
-                'EVT_additional_limit',
346
-                'EEM_Event'
347
-            );
348
-        }
349
-    }
350
-
351
-
352
-    /**
353
-     * Return what is currently set as the default additional limit for the event.
354
-     * @return int
355
-     */
356
-    public static function get_default_additional_limit()
357
-    {
358
-        return apply_filters('FHEE__EEM_Event__get_default_additional_limit', self::$_default_additional_limit);
359
-    }
360
-
361
-
362
-    /**
363
-     * get_question_groups
364
-     *
365
-     * @return array
366
-     * @throws EE_Error
367
-     * @throws ReflectionException
368
-     */
369
-    public function get_all_question_groups()
370
-    {
371
-        return EE_Registry::instance()->load_model('Question_Group')->get_all(
372
-            array(
373
-                array('QSG_deleted' => false),
374
-                'order_by' => array('QSG_order' => 'ASC'),
375
-            )
376
-        );
377
-    }
378
-
379
-
380
-    /**
381
-     * get_question_groups
382
-     *
383
-     * @param int $EVT_ID
384
-     * @return array|bool
385
-     * @throws EE_Error
386
-     * @throws ReflectionException
387
-     */
388
-    public function get_all_event_question_groups($EVT_ID = 0)
389
-    {
390
-        if (! isset($EVT_ID) || ! absint($EVT_ID)) {
391
-            EE_Error::add_error(
392
-                esc_html__(
393
-                    'An error occurred. No Event Question Groups could be retrieved because an Event ID was not received.',
394
-                    'event_espresso'
395
-                ),
396
-                __FILE__,
397
-                __FUNCTION__,
398
-                __LINE__
399
-            );
400
-            return false;
401
-        }
402
-        return EE_Registry::instance()->load_model('Event_Question_Group')->get_all(
403
-            array(
404
-                array('EVT_ID' => $EVT_ID),
405
-            )
406
-        );
407
-    }
408
-
409
-
410
-    /**
411
-     * get_question_groups
412
-     *
413
-     * @param int $EVT_ID
414
-     * @param boolean $for_primary_attendee
415
-     * @return array|bool
416
-     * @throws EE_Error
417
-     * @throws InvalidArgumentException
418
-     * @throws ReflectionException
419
-     * @throws InvalidDataTypeException
420
-     * @throws InvalidInterfaceException
421
-     */
422
-    public function get_event_question_groups($EVT_ID = 0, $for_primary_attendee = true)
423
-    {
424
-        if (! isset($EVT_ID) || ! absint($EVT_ID)) {
425
-            EE_Error::add_error(
426
-                esc_html__(
427
-                    // @codingStandardsIgnoreStart
428
-                    'An error occurred. No Event Question Groups could be retrieved because an Event ID was not received.',
429
-                    // @codingStandardsIgnoreEnd
430
-                    'event_espresso'
431
-                ),
432
-                __FILE__,
433
-                __FUNCTION__,
434
-                __LINE__
435
-            );
436
-            return false;
437
-        }
438
-        $query_params = [
439
-            [
440
-                'EVT_ID' => $EVT_ID,
441
-                EEM_Event_Question_Group::instance()->fieldNameForContext($for_primary_attendee) => true
442
-            ]
443
-        ];
444
-        if ($for_primary_attendee) {
445
-            $query_params[0]['EQG_primary'] = true;
446
-        } else {
447
-            $query_params[0]['EQG_additional'] = true;
448
-        }
449
-        return EE_Registry::instance()->load_model('Event_Question_Group')->get_all($query_params);
450
-    }
451
-
452
-
453
-    /**
454
-     * get_question_groups
455
-     *
456
-     * @param int $EVT_ID
457
-     * @param EE_Registration $registration
458
-     * @return array|bool
459
-     * @throws EE_Error
460
-     * @throws InvalidArgumentException
461
-     * @throws InvalidDataTypeException
462
-     * @throws InvalidInterfaceException
463
-     * @throws ReflectionException
464
-     */
465
-    public function get_question_groups_for_event($EVT_ID, EE_Registration $registration)
466
-    {
467
-        if (! absint($EVT_ID)) {
468
-            EE_Error::add_error(
469
-                esc_html__(
470
-                    'An error occurred. No Question Groups could be retrieved because an Event ID was not received.',
471
-                    'event_espresso'
472
-                ),
473
-                __FILE__,
474
-                __FUNCTION__,
475
-                __LINE__
476
-            );
477
-            return false;
478
-        }
479
-        return EE_Registry::instance()->load_model('Question_Group')->get_all(
480
-            [
481
-                [
482
-                    'Event_Question_Group.EVT_ID'      => $EVT_ID,
483
-                    'Event_Question_Group.'
484
-                        . EEM_Event_Question_Group::instance()->fieldNameForContext(
485
-                            $registration->is_primary_registrant()
486
-                        ) => true
487
-                ],
488
-                'order_by' => ['QSG_order' => 'ASC'],
489
-            ]
490
-        );
491
-    }
492
-
493
-
494
-    /**
495
-     * get_question_target_db_column
496
-     *
497
-     * @param string $QSG_IDs csv list of $QSG IDs
498
-     * @return array|bool
499
-     * @throws EE_Error
500
-     * @throws ReflectionException
501
-     */
502
-    public function get_questions_in_groups($QSG_IDs = '')
503
-    {
504
-        if (empty($QSG_IDs)) {
505
-            EE_Error::add_error(
506
-                esc_html__('An error occurred. No Question Group IDs were received.', 'event_espresso'),
507
-                __FILE__,
508
-                __FUNCTION__,
509
-                __LINE__
510
-            );
511
-            return false;
512
-        }
513
-        return EE_Registry::instance()->load_model('Question')->get_all(
514
-            array(
515
-                array(
516
-                    'Question_Group.QSG_ID' => array('IN', $QSG_IDs),
517
-                    'QST_deleted'           => false,
518
-                    'QST_admin_only'        => is_admin(),
519
-                ),
520
-                'order_by' => 'QST_order',
521
-            )
522
-        );
523
-    }
524
-
525
-
526
-    /**
527
-     * get_options_for_question
528
-     *
529
-     * @param string $QST_IDs csv list of $QST IDs
530
-     * @return array|bool
531
-     * @throws EE_Error
532
-     * @throws ReflectionException
533
-     */
534
-    public function get_options_for_question($QST_IDs)
535
-    {
536
-        if (empty($QST_IDs)) {
537
-            EE_Error::add_error(
538
-                esc_html__('An error occurred. No Question IDs were received.', 'event_espresso'),
539
-                __FILE__,
540
-                __FUNCTION__,
541
-                __LINE__
542
-            );
543
-            return false;
544
-        }
545
-        return EE_Registry::instance()->load_model('Question_Option')->get_all(
546
-            array(
547
-                array(
548
-                    'Question.QST_ID' => array('IN', $QST_IDs),
549
-                    'QSO_deleted'     => false,
550
-                ),
551
-                'order_by' => 'QSO_ID',
552
-            )
553
-        );
554
-    }
555
-
556
-
557
-    /**
558
-     * Gets all events that are published
559
-     * and have event start time earlier than now and an event end time later than now
560
-     *
561
-     * @param array $query_params  An array of query params to further filter on
562
-     *                             (note that status and DTT_EVT_start and DTT_EVT_end will be overridden)
563
-     * @param bool  $count         whether to return the count or not (default FALSE)
564
-     * @return EE_Event[]|int
565
-     * @throws EE_Error
566
-     * @throws ReflectionException
567
-     */
568
-    public function get_active_events($query_params, $count = false)
569
-    {
570
-        if (array_key_exists(0, $query_params)) {
571
-            $where_params = $query_params[0];
572
-            unset($query_params[0]);
573
-        } else {
574
-            $where_params = array();
575
-        }
576
-        // if we have count make sure we don't include group by
577
-        if ($count && isset($query_params['group_by'])) {
578
-            unset($query_params['group_by']);
579
-        }
580
-        // let's add specific query_params for active_events
581
-        // keep in mind this will override any sent status in the query AND any date queries.
582
-        $where_params['status'] = array('IN', array('publish', EEM_Event::sold_out));
583
-        // if already have where params for DTT_EVT_start or DTT_EVT_end then append these conditions
584
-        if (isset($where_params['Datetime.DTT_EVT_start'])) {
585
-            $where_params['Datetime.DTT_EVT_start******'] = array(
586
-                '<',
587
-                EEM_Datetime::instance()->current_time_for_query('DTT_EVT_start'),
588
-            );
589
-        } else {
590
-            $where_params['Datetime.DTT_EVT_start'] = array(
591
-                '<',
592
-                EEM_Datetime::instance()->current_time_for_query('DTT_EVT_start'),
593
-            );
594
-        }
595
-        if (isset($where_params['Datetime.DTT_EVT_end'])) {
596
-            $where_params['Datetime.DTT_EVT_end*****'] = array(
597
-                '>',
598
-                EEM_Datetime::instance()->current_time_for_query('DTT_EVT_end'),
599
-            );
600
-        } else {
601
-            $where_params['Datetime.DTT_EVT_end'] = array(
602
-                '>',
603
-                EEM_Datetime::instance()->current_time_for_query('DTT_EVT_end'),
604
-            );
605
-        }
606
-        $query_params[0] = $where_params;
607
-        // don't use $query_params with count()
608
-        // because we don't want to include additional query clauses like "GROUP BY"
609
-        return $count
610
-            ? $this->count(array($where_params), 'EVT_ID', true)
611
-            : $this->get_all($query_params);
612
-    }
613
-
614
-
615
-    /**
616
-     * get all events that are published and have an event start time later than now
617
-     *
618
-     * @param array $query_params  An array of query params to further filter on
619
-     *                             (Note that status and DTT_EVT_start will be overridden)
620
-     * @param bool  $count         whether to return the count or not (default FALSE)
621
-     * @return EE_Event[]|int
622
-     * @throws EE_Error
623
-     * @throws ReflectionException
624
-     */
625
-    public function get_upcoming_events($query_params, $count = false)
626
-    {
627
-        if (array_key_exists(0, $query_params)) {
628
-            $where_params = $query_params[0];
629
-            unset($query_params[0]);
630
-        } else {
631
-            $where_params = array();
632
-        }
633
-        // if we have count make sure we don't include group by
634
-        if ($count && isset($query_params['group_by'])) {
635
-            unset($query_params['group_by']);
636
-        }
637
-        // let's add specific query_params for active_events
638
-        // keep in mind this will override any sent status in the query AND any date queries.
639
-        // we need to pull events with a status of publish and sold_out
640
-        $event_status = array('publish', EEM_Event::sold_out);
641
-        // check if the user can read private events and if so add the 'private status to the were params'
642
-        if (EE_Registry::instance()->CAP->current_user_can('ee_read_private_events', 'get_upcoming_events')) {
643
-            $event_status[] = 'private';
644
-        }
645
-        $where_params['status'] = array('IN', $event_status);
646
-        // if there are already query_params matching DTT_EVT_start then we need to modify that to add them.
647
-        if (isset($where_params['Datetime.DTT_EVT_start'])) {
648
-            $where_params['Datetime.DTT_EVT_start*****'] = array(
649
-                '>',
650
-                EEM_Datetime::instance()->current_time_for_query('DTT_EVT_start'),
651
-            );
652
-        } else {
653
-            $where_params['Datetime.DTT_EVT_start'] = array(
654
-                '>',
655
-                EEM_Datetime::instance()->current_time_for_query('DTT_EVT_start'),
656
-            );
657
-        }
658
-        $query_params[0] = $where_params;
659
-        // don't use $query_params with count()
660
-        // because we don't want to include additional query clauses like "GROUP BY"
661
-        return $count
662
-            ? $this->count(array($where_params), 'EVT_ID', true)
663
-            : $this->get_all($query_params);
664
-    }
665
-
666
-
667
-    /**
668
-     * Gets all events that are published
669
-     * and have an event end time later than now
670
-     *
671
-     * @param array $query_params  An array of query params to further filter on
672
-     *                             (note that status and DTT_EVT_end will be overridden)
673
-     * @param bool  $count         whether to return the count or not (default FALSE)
674
-     * @return EE_Event[]|int
675
-     * @throws EE_Error
676
-     * @throws ReflectionException
677
-     */
678
-    public function get_active_and_upcoming_events($query_params, $count = false)
679
-    {
680
-        if (array_key_exists(0, $query_params)) {
681
-            $where_params = $query_params[0];
682
-            unset($query_params[0]);
683
-        } else {
684
-            $where_params = array();
685
-        }
686
-        // if we have count make sure we don't include group by
687
-        if ($count && isset($query_params['group_by'])) {
688
-            unset($query_params['group_by']);
689
-        }
690
-        // let's add specific query_params for active_events
691
-        // keep in mind this will override any sent status in the query AND any date queries.
692
-        $where_params['status'] = array('IN', array('publish', EEM_Event::sold_out));
693
-        // add where params for DTT_EVT_end
694
-        if (isset($where_params['Datetime.DTT_EVT_end'])) {
695
-            $where_params['Datetime.DTT_EVT_end*****'] = array(
696
-                '>',
697
-                EEM_Datetime::instance()->current_time_for_query('DTT_EVT_end'),
698
-            );
699
-        } else {
700
-            $where_params['Datetime.DTT_EVT_end'] = array(
701
-                '>',
702
-                EEM_Datetime::instance()->current_time_for_query('DTT_EVT_end'),
703
-            );
704
-        }
705
-        $query_params[0] = $where_params;
706
-        // don't use $query_params with count()
707
-        // because we don't want to include additional query clauses like "GROUP BY"
708
-        return $count
709
-            ? $this->count(array($where_params), 'EVT_ID', true)
710
-            : $this->get_all($query_params);
711
-    }
712
-
713
-
714
-    /**
715
-     * This only returns events that are expired.
716
-     * They may still be published but all their datetimes have expired.
717
-     *
718
-     * @param array $query_params  An array of query params to further filter on
719
-     *                             (note that status and DTT_EVT_end will be overridden)
720
-     * @param bool  $count         whether to return the count or not (default FALSE)
721
-     * @return EE_Event[]|int
722
-     * @throws EE_Error
723
-     * @throws ReflectionException
724
-     */
725
-    public function get_expired_events($query_params, $count = false)
726
-    {
727
-        $where_params = isset($query_params[0]) ? $query_params[0] : array();
728
-        // if we have count make sure we don't include group by
729
-        if ($count && isset($query_params['group_by'])) {
730
-            unset($query_params['group_by']);
731
-        }
732
-        // let's add specific query_params for active_events
733
-        // keep in mind this will override any sent status in the query AND any date queries.
734
-        if (isset($where_params['status'])) {
735
-            unset($where_params['status']);
736
-        }
737
-        $exclude_query = $query_params;
738
-        if (isset($exclude_query[0])) {
739
-            unset($exclude_query[0]);
740
-        }
741
-        $exclude_query[0] = array(
742
-            'Datetime.DTT_EVT_end' => array(
743
-                '>',
744
-                EEM_Datetime::instance()->current_time_for_query('DTT_EVT_end'),
745
-            ),
746
-        );
747
-        // first get all events that have datetimes where its not expired.
748
-        $event_ids = $this->_get_all_wpdb_results($exclude_query, OBJECT_K, 'Event_CPT.ID');
749
-        $event_ids = array_keys($event_ids);
750
-        // if we have any additional query_params, let's add them to the 'AND' condition
751
-        $and_condition = array(
752
-            'Datetime.DTT_EVT_end' => array('<', EEM_Datetime::instance()->current_time_for_query('DTT_EVT_end')),
753
-            'EVT_ID'               => array('NOT IN', $event_ids),
754
-        );
755
-        if (isset($where_params['OR'])) {
756
-            $and_condition['OR'] = $where_params['OR'];
757
-            unset($where_params['OR']);
758
-        }
759
-        if (isset($where_params['Datetime.DTT_EVT_end'])) {
760
-            $and_condition['Datetime.DTT_EVT_end****'] = $where_params['Datetime.DTT_EVT_end'];
761
-            unset($where_params['Datetime.DTT_EVT_end']);
762
-        }
763
-        if (isset($where_params['Datetime.DTT_EVT_start'])) {
764
-            $and_condition['Datetime.DTT_EVT_start'] = $where_params['Datetime.DTT_EVT_start'];
765
-            unset($where_params['Datetime.DTT_EVT_start']);
766
-        }
767
-        // merge remaining $where params with the and conditions.
768
-        $where_params['AND'] = array_merge($and_condition, $where_params);
769
-        $query_params[0] = $where_params;
770
-        // don't use $query_params with count()
771
-        // because we don't want to include additional query clauses like "GROUP BY"
772
-        return $count
773
-            ? $this->count(array($where_params), 'EVT_ID', true)
774
-            : $this->get_all($query_params);
775
-    }
776
-
777
-
778
-
779
-    /**
780
-     * This basically just returns the events that do not have the publish status.
781
-     *
782
-     * @param  array   $query_params An array of query params to further filter on
783
-     *                               (note that status will be overwritten)
784
-     * @param  boolean $count        whether to return the count or not (default FALSE)
785
-     * @return EE_Event[]|int
786
-     * @throws EE_Error
787
-     */
788
-    public function get_inactive_events($query_params, $count = false)
789
-    {
790
-        $where_params = isset($query_params[0]) ? $query_params[0] : array();
791
-        // let's add in specific query_params for inactive events.
792
-        if (isset($where_params['status'])) {
793
-            unset($where_params['status']);
794
-        }
795
-        // if we have count make sure we don't include group by
796
-        if ($count && isset($query_params['group_by'])) {
797
-            unset($query_params['group_by']);
798
-        }
799
-        // if we have any additional query_params, let's add them to the 'AND' condition
800
-        $where_params['AND']['status'] = array('!=', 'publish');
801
-        if (isset($where_params['OR'])) {
802
-            $where_params['AND']['OR'] = $where_params['OR'];
803
-            unset($where_params['OR']);
804
-        }
805
-        if (isset($where_params['Datetime.DTT_EVT_end'])) {
806
-            $where_params['AND']['Datetime.DTT_EVT_end****'] = $where_params['Datetime.DTT_EVT_end'];
807
-            unset($where_params['Datetime.DTT_EVT_end']);
808
-        }
809
-        if (isset($where_params['Datetime.DTT_EVT_start'])) {
810
-            $where_params['AND']['Datetime.DTT_EVT_start'] = $where_params['Datetime.DTT_EVT_start'];
811
-            unset($where_params['Datetime.DTT_EVT_start']);
812
-        }
813
-        $query_params[0] = $where_params;
814
-        // don't use $query_params with count()
815
-        // because we don't want to include additional query clauses like "GROUP BY"
816
-        return $count
817
-            ? $this->count(array($where_params), 'EVT_ID', true)
818
-            : $this->get_all($query_params);
819
-    }
820
-
821
-
822
-    /**
823
-     * This is just injecting into the parent add_relationship_to so we do special handling on price relationships
824
-     * because we don't want to override any existing global default prices but instead insert NEW prices that get
825
-     * attached to the event. See parent for param descriptions
826
-     *
827
-     * @param        $id_or_obj
828
-     * @param        $other_model_id_or_obj
829
-     * @param string $relationName
830
-     * @param array  $where_query
831
-     * @return EE_Base_Class
832
-     * @throws EE_Error
833
-     * @throws ReflectionException
834
-     */
835
-    public function add_relationship_to($id_or_obj, $other_model_id_or_obj, $relationName, $where_query = array())
836
-    {
837
-        if ($relationName === 'Price') {
838
-            // let's get the PRC object for the given ID to make sure that we aren't dealing with a default
839
-            $prc_chk = $this->get_related_model_obj($relationName)->ensure_is_obj($other_model_id_or_obj);
840
-            // if EVT_ID = 0, then this is a default
841
-            if ((int) $prc_chk->get('EVT_ID') === 0) {
842
-                // let's set the prc_id as 0 so we force an insert on the add_relation_to carried out by relation
843
-                $prc_chk->set('PRC_ID', 0);
844
-            }
845
-            // run parent
846
-            return parent::add_relationship_to($id_or_obj, $prc_chk, $relationName, $where_query);
847
-        }
848
-        // otherwise carry on as normal
849
-        return parent::add_relationship_to($id_or_obj, $other_model_id_or_obj, $relationName, $where_query);
850
-    }
851
-
852
-
853
-
854
-    /******************** DEPRECATED METHODS ********************/
855
-
856
-
857
-    /**
858
-     * _get_question_target_db_column
859
-     *
860
-     * @param EE_Registration $registration    (so existing answers for registration are included)
861
-     * @param int             $EVT_ID          so all question groups are included for event (not just answers from
862
-     *                                         registration).
863
-     * @return    array
864
-     * @throws ReflectionException
865
-     * @throws EE_Error*@deprecated as of 4.8.32.rc.001. Instead consider using
866
-     *                                         EE_Registration_Custom_Questions_Form located in
867
-     *                                         admin_pages/registrations/form_sections/EE_Registration_Custom_Questions_Form.form.php
868
-     * @access     public
869
-     */
870
-    public function assemble_array_of_groups_questions_and_options(EE_Registration $registration, $EVT_ID = 0)
871
-    {
872
-        if (empty($EVT_ID)) {
873
-            throw new EE_Error(esc_html__(
874
-                'An error occurred. No EVT_ID is included.  Needed to know which question groups to retrieve.',
875
-                'event_espresso'
876
-            ));
877
-        }
878
-        $questions = array();
879
-        // get all question groups for event
880
-        $qgs = $this->get_question_groups_for_event($EVT_ID, $registration);
881
-        if (! empty($qgs)) {
882
-            foreach ($qgs as $qg) {
883
-                $qsts = $qg->questions();
884
-                $questions[ $qg->ID() ] = $qg->model_field_array();
885
-                $questions[ $qg->ID() ]['QSG_questions'] = array();
886
-                foreach ($qsts as $qst) {
887
-                    if ($qst->is_system_question()) {
888
-                        continue;
889
-                    }
890
-                    $answer = EEM_Answer::instance()->get_one(array(
891
-                        array(
892
-                            'QST_ID' => $qst->ID(),
893
-                            'REG_ID' => $registration->ID(),
894
-                        ),
895
-                    ));
896
-                    $answer = $answer instanceof EE_Answer ? $answer : EEM_Answer::instance()->create_default_object();
897
-                    $qst_name = $qstn_id = $qst->ID();
898
-                    $ans_id = $answer->ID();
899
-                    $qst_name = ! empty($ans_id) ? '[' . $qst_name . '][' . $ans_id . ']' : '[' . $qst_name . ']';
900
-                    $input_name = '';
901
-                    $input_id = sanitize_key($qst->display_text());
902
-                    $input_class = '';
903
-                    $questions[ $qg->ID() ]['QSG_questions'][ $qst->ID() ] = $qst->model_field_array();
904
-                    $questions[ $qg->ID() ]['QSG_questions'][ $qst->ID() ]['QST_input_name'] = 'qstn'
905
-                                                                                           . $input_name
906
-                                                                                           . $qst_name;
907
-                    $questions[ $qg->ID() ]['QSG_questions'][ $qst->ID() ]['QST_input_id'] = $input_id . '-' . $qstn_id;
908
-                    $questions[ $qg->ID() ]['QSG_questions'][ $qst->ID() ]['QST_input_class'] = $input_class;
909
-                    $questions[ $qg->ID() ]['QSG_questions'][ $qst->ID() ]['QST_options'] = array();
910
-                    $questions[ $qg->ID() ]['QSG_questions'][ $qst->ID() ]['qst_obj'] = $qst;
911
-                    $questions[ $qg->ID() ]['QSG_questions'][ $qst->ID() ]['ans_obj'] = $answer;
912
-                    // leave responses as-is, don't convert stuff into html entities please!
913
-                    $questions[ $qg->ID() ]['QSG_questions'][ $qst->ID() ]['htmlentities'] = false;
914
-                    if ($qst->type() == 'RADIO_BTN' || $qst->type() == 'CHECKBOX' || $qst->type() == 'DROPDOWN') {
915
-                        $QSOs = $qst->options(true, $answer->value());
916
-                        if (is_array($QSOs)) {
917
-                            foreach ($QSOs as $QSO_ID => $QSO) {
918
-                                $questions[ $qg->ID() ]['QSG_questions'][ $qst->ID() ]['QST_options'][ $QSO_ID ] = $QSO->model_field_array();
919
-                            }
920
-                        }
921
-                    }
922
-                }
923
-            }
924
-        }
925
-        return $questions;
926
-    }
927
-
928
-
929
-    /**
930
-     * @param mixed $cols_n_values either an array of where each key is the name of a field, and the value is its value
931
-     *                             or an stdClass where each property is the name of a column,
932
-     * @return EE_Base_Class
933
-     * @throws EE_Error
934
-     */
935
-    public function instantiate_class_from_array_or_object($cols_n_values)
936
-    {
937
-        $classInstance = parent::instantiate_class_from_array_or_object($cols_n_values);
938
-        if ($classInstance instanceof EE_Event) {
939
-            // events have their timezone defined in the DB, so use it immediately
940
-            $this->set_timezone($classInstance->get_timezone());
941
-        }
942
-        return $classInstance;
943
-    }
16
+	/**
17
+	 * constant used by status(), indicating that no more tickets can be purchased for any of the datetimes for the
18
+	 * event
19
+	 */
20
+	const sold_out = 'sold_out';
21
+
22
+	/**
23
+	 * constant used by status(), indicating that upcoming event dates have been postponed (may be pushed to a later
24
+	 * date)
25
+	 */
26
+	const postponed = 'postponed';
27
+
28
+	/**
29
+	 * constant used by status(), indicating that the event will no longer occur
30
+	 */
31
+	const cancelled = 'cancelled';
32
+
33
+
34
+	/**
35
+	 * @var string
36
+	 */
37
+	protected static $_default_reg_status;
38
+
39
+
40
+	/**
41
+	 * This is the default for the additional limit field.
42
+	 * @var int
43
+	 */
44
+	protected static $_default_additional_limit = 10;
45
+
46
+
47
+	/**
48
+	 * private instance of the Event object
49
+	 *
50
+	 * @var EEM_Event
51
+	 */
52
+	protected static $_instance;
53
+
54
+
55
+	/**
56
+	 * Adds a relationship to Term_Taxonomy for each CPT_Base
57
+	 *
58
+	 * @param string $timezone
59
+	 * @throws EE_Error
60
+	 * @throws ReflectionException
61
+	 */
62
+	protected function __construct($timezone = null)
63
+	{
64
+		EE_Registry::instance()->load_model('Registration');
65
+		$this->singular_item = esc_html__('Event', 'event_espresso');
66
+		$this->plural_item = esc_html__('Events', 'event_espresso');
67
+		// to remove Cancelled events from the frontend, copy the following filter to your functions.php file
68
+		// add_filter( 'AFEE__EEM_Event__construct___custom_stati__cancelled__Public', '__return_false' );
69
+		// to remove Postponed events from the frontend, copy the following filter to your functions.php file
70
+		// add_filter( 'AFEE__EEM_Event__construct___custom_stati__postponed__Public', '__return_false' );
71
+		// to remove Sold Out events from the frontend, copy the following filter to your functions.php file
72
+		//  add_filter( 'AFEE__EEM_Event__construct___custom_stati__sold_out__Public', '__return_false' );
73
+		$this->_custom_stati = apply_filters(
74
+			'AFEE__EEM_Event__construct___custom_stati',
75
+			array(
76
+				EEM_Event::cancelled => array(
77
+					'label'  => esc_html__('Cancelled', 'event_espresso'),
78
+					'public' => apply_filters('AFEE__EEM_Event__construct___custom_stati__cancelled__Public', true),
79
+				),
80
+				EEM_Event::postponed => array(
81
+					'label'  => esc_html__('Postponed', 'event_espresso'),
82
+					'public' => apply_filters('AFEE__EEM_Event__construct___custom_stati__postponed__Public', true),
83
+				),
84
+				EEM_Event::sold_out  => array(
85
+					'label'  => esc_html__('Sold Out', 'event_espresso'),
86
+					'public' => apply_filters('AFEE__EEM_Event__construct___custom_stati__sold_out__Public', true),
87
+				),
88
+			)
89
+		);
90
+		self::$_default_reg_status = empty(self::$_default_reg_status)
91
+			? EEM_Registration::status_id_pending_payment
92
+			: self::$_default_reg_status;
93
+		$this->_tables = array(
94
+			'Event_CPT'  => new EE_Primary_Table('posts', 'ID'),
95
+			'Event_Meta' => new EE_Secondary_Table('esp_event_meta', 'EVTM_ID', 'EVT_ID'),
96
+		);
97
+		$this->_fields = array(
98
+			'Event_CPT'  => array(
99
+				'EVT_ID'         => new EE_Primary_Key_Int_Field(
100
+					'ID',
101
+					esc_html__('Post ID for Event', 'event_espresso')
102
+				),
103
+				'EVT_name'       => new EE_Plain_Text_Field(
104
+					'post_title',
105
+					esc_html__('Event Name', 'event_espresso'),
106
+					false,
107
+					''
108
+				),
109
+				'EVT_desc'       => new EE_Post_Content_Field(
110
+					'post_content',
111
+					esc_html__('Event Description', 'event_espresso'),
112
+					false,
113
+					''
114
+				),
115
+				'EVT_slug'       => new EE_Slug_Field(
116
+					'post_name',
117
+					esc_html__('Event Slug', 'event_espresso'),
118
+					false,
119
+					''
120
+				),
121
+				'EVT_created'    => new EE_Datetime_Field(
122
+					'post_date',
123
+					esc_html__('Date/Time Event Created', 'event_espresso'),
124
+					false,
125
+					EE_Datetime_Field::now
126
+				),
127
+				'EVT_short_desc' => new EE_Simple_HTML_Field(
128
+					'post_excerpt',
129
+					esc_html__('Event Short Description', 'event_espresso'),
130
+					false,
131
+					''
132
+				),
133
+				'EVT_modified'   => new EE_Datetime_Field(
134
+					'post_modified',
135
+					esc_html__('Date/Time Event Modified', 'event_espresso'),
136
+					false,
137
+					EE_Datetime_Field::now
138
+				),
139
+				'EVT_wp_user'    => new EE_WP_User_Field(
140
+					'post_author',
141
+					esc_html__('Event Creator ID', 'event_espresso'),
142
+					false
143
+				),
144
+				'parent'         => new EE_Integer_Field(
145
+					'post_parent',
146
+					esc_html__('Event Parent ID', 'event_espresso'),
147
+					false,
148
+					0
149
+				),
150
+				'EVT_order'      => new EE_Integer_Field(
151
+					'menu_order',
152
+					esc_html__('Event Menu Order', 'event_espresso'),
153
+					false,
154
+					1
155
+				),
156
+				'post_type'      => new EE_WP_Post_Type_Field('espresso_events'),
157
+				// EE_Plain_Text_Field( 'post_type', esc_html__( 'Event Post Type', 'event_espresso' ), FALSE, 'espresso_events' ),
158
+				'status'         => new EE_WP_Post_Status_Field(
159
+					'post_status',
160
+					esc_html__('Event Status', 'event_espresso'),
161
+					false,
162
+					'draft',
163
+					$this->_custom_stati
164
+				),
165
+				'password' => new EE_Password_Field(
166
+					'post_password',
167
+					esc_html__('Password', 'event_espresso'),
168
+					false,
169
+					'',
170
+					array(
171
+						'EVT_desc',
172
+						'EVT_short_desc',
173
+						'EVT_display_desc',
174
+						'EVT_display_ticket_selector',
175
+						'EVT_visible_on',
176
+						'EVT_additional_limit',
177
+						'EVT_default_registration_status',
178
+						'EVT_member_only',
179
+						'EVT_phone',
180
+						'EVT_allow_overflow',
181
+						'EVT_timezone_string',
182
+						'EVT_external_URL',
183
+						'EVT_donations'
184
+					)
185
+				)
186
+			),
187
+			'Event_Meta' => array(
188
+				'EVTM_ID'                         => new EE_DB_Only_Float_Field(
189
+					'EVTM_ID',
190
+					esc_html__('Event Meta Row ID', 'event_espresso'),
191
+					false
192
+				),
193
+				'EVT_ID_fk'                       => new EE_DB_Only_Int_Field(
194
+					'EVT_ID',
195
+					esc_html__('Foreign key to Event ID from Event Meta table', 'event_espresso'),
196
+					false
197
+				),
198
+				'VNU_ID' => new EE_Foreign_Key_Int_Field(
199
+					'VNU_ID',
200
+					__('Venue ID', 'event_espresso'),
201
+					false,
202
+					0,
203
+					'Venue'
204
+				),
205
+				'EVT_display_desc'                => new EE_Boolean_Field(
206
+					'EVT_display_desc',
207
+					esc_html__('Display Description Flag', 'event_espresso'),
208
+					false,
209
+					true
210
+				),
211
+				'EVT_display_ticket_selector'     => new EE_Boolean_Field(
212
+					'EVT_display_ticket_selector',
213
+					esc_html__('Display Ticket Selector Flag', 'event_espresso'),
214
+					false,
215
+					true
216
+				),
217
+				'EVT_visible_on'                  => new EE_Datetime_Field(
218
+					'EVT_visible_on',
219
+					esc_html__('Event Visible Date', 'event_espresso'),
220
+					true,
221
+					EE_Datetime_Field::now
222
+				),
223
+				'EVT_additional_limit'            => new EE_Integer_Field(
224
+					'EVT_additional_limit',
225
+					esc_html__('Limit of Additional Registrations on Same Transaction', 'event_espresso'),
226
+					false,
227
+					self::$_default_additional_limit
228
+				),
229
+				'EVT_default_registration_status' => new EE_Enum_Text_Field(
230
+					'EVT_default_registration_status',
231
+					esc_html__('Default Registration Status on this Event', 'event_espresso'),
232
+					false,
233
+					EEM_Event::$_default_reg_status,
234
+					EEM_Registration::reg_status_array()
235
+				),
236
+				'EVT_member_only'                 => new EE_Boolean_Field(
237
+					'EVT_member_only',
238
+					esc_html__('Member-Only Event Flag', 'event_espresso'),
239
+					false,
240
+					false
241
+				),
242
+				'EVT_phone'                       => new EE_Plain_Text_Field(
243
+					'EVT_phone',
244
+					esc_html__('Event Phone Number', 'event_espresso'),
245
+					false,
246
+					''
247
+				),
248
+				'EVT_allow_overflow'              => new EE_Boolean_Field(
249
+					'EVT_allow_overflow',
250
+					esc_html__('Allow Overflow on Event', 'event_espresso'),
251
+					false,
252
+					false
253
+				),
254
+				'EVT_timezone_string'             => new EE_Plain_Text_Field(
255
+					'EVT_timezone_string',
256
+					esc_html__('Timezone (name) for Event times', 'event_espresso'),
257
+					false,
258
+					''
259
+				),
260
+				'EVT_external_URL'                => new EE_Plain_Text_Field(
261
+					'EVT_external_URL',
262
+					esc_html__('URL of Event Page if hosted elsewhere', 'event_espresso'),
263
+					true
264
+				),
265
+				'EVT_donations'                   => new EE_Boolean_Field(
266
+					'EVT_donations',
267
+					esc_html__('Accept Donations?', 'event_espresso'),
268
+					false,
269
+					false
270
+				),
271
+				'FSC_UUID'                        => new EE_Foreign_Key_String_Field(
272
+					'FSC_UUID',
273
+					esc_html__('Registration Form UUID (universally unique identifier)', 'event_espresso'),
274
+					true,
275
+					null,
276
+					'Form_Section',
277
+					false
278
+				),
279
+			),
280
+		);
281
+		$this->_model_relations = array(
282
+			'Attendee'               => new EE_HABTM_Relation('Registration'),
283
+			'Datetime'               => new EE_Has_Many_Relation(),
284
+			'Event_Question_Group'   => new EE_Has_Many_Relation(),
285
+			'Form_Section'           => new EE_Belongs_To_Relation(),
286
+			'Message_Template_Group' => new EE_HABTM_Relation('Event_Message_Template'),
287
+			'Question_Group'         => new EE_HABTM_Relation('Event_Question_Group'),
288
+			'Registration'           => new EE_Has_Many_Relation(),
289
+			'Term_Relationship'      => new EE_Has_Many_Relation(),
290
+			'Term_Taxonomy'          => new EE_HABTM_Relation('Term_Relationship'),
291
+			'Venue'                  => new EE_Belongs_To_Relation(),
292
+			'WP_User'                => new EE_Belongs_To_Relation(),
293
+		);
294
+		// this model is generally available for reading
295
+		$this->_cap_restriction_generators[ EEM_Base::caps_read ] = new EE_Restriction_Generator_Public();
296
+		$this->model_chain_to_password = '';
297
+		parent::__construct($timezone);
298
+	}
299
+
300
+
301
+	/**
302
+	 * @param string $default_reg_status
303
+	 * @throws EE_Error
304
+	 * @throws EE_Error
305
+	 */
306
+	public static function set_default_reg_status($default_reg_status)
307
+	{
308
+		self::$_default_reg_status = $default_reg_status;
309
+		// if EEM_Event has already been instantiated,
310
+		// then we need to reset the `EVT_default_reg_status` field to use the new default.
311
+		if (self::$_instance instanceof EEM_Event) {
312
+			$default_reg_status = new EE_Enum_Text_Field(
313
+				'EVT_default_registration_status',
314
+				esc_html__('Default Registration Status on this Event', 'event_espresso'),
315
+				false,
316
+				$default_reg_status,
317
+				EEM_Registration::reg_status_array()
318
+			);
319
+			$default_reg_status->_construct_finalize(
320
+				'Event_Meta',
321
+				'EVT_default_registration_status',
322
+				'EEM_Event'
323
+			);
324
+			self::$_instance->_fields['Event_Meta']['EVT_default_registration_status'] = $default_reg_status;
325
+		}
326
+	}
327
+
328
+
329
+	/**
330
+	 * Used to override the default for the additional limit field.
331
+	 * @param $additional_limit
332
+	 */
333
+	public static function set_default_additional_limit($additional_limit)
334
+	{
335
+		self::$_default_additional_limit = (int) $additional_limit;
336
+		if (self::$_instance instanceof EEM_Event) {
337
+			self::$_instance->_fields['Event_Meta']['EVT_additional_limit'] = new EE_Integer_Field(
338
+				'EVT_additional_limit',
339
+				esc_html__('Limit of Additional Registrations on Same Transaction', 'event_espresso'),
340
+				false,
341
+				self::$_default_additional_limit
342
+			);
343
+			self::$_instance->_fields['Event_Meta']['EVT_additional_limit']->_construct_finalize(
344
+				'Event_Meta',
345
+				'EVT_additional_limit',
346
+				'EEM_Event'
347
+			);
348
+		}
349
+	}
350
+
351
+
352
+	/**
353
+	 * Return what is currently set as the default additional limit for the event.
354
+	 * @return int
355
+	 */
356
+	public static function get_default_additional_limit()
357
+	{
358
+		return apply_filters('FHEE__EEM_Event__get_default_additional_limit', self::$_default_additional_limit);
359
+	}
360
+
361
+
362
+	/**
363
+	 * get_question_groups
364
+	 *
365
+	 * @return array
366
+	 * @throws EE_Error
367
+	 * @throws ReflectionException
368
+	 */
369
+	public function get_all_question_groups()
370
+	{
371
+		return EE_Registry::instance()->load_model('Question_Group')->get_all(
372
+			array(
373
+				array('QSG_deleted' => false),
374
+				'order_by' => array('QSG_order' => 'ASC'),
375
+			)
376
+		);
377
+	}
378
+
379
+
380
+	/**
381
+	 * get_question_groups
382
+	 *
383
+	 * @param int $EVT_ID
384
+	 * @return array|bool
385
+	 * @throws EE_Error
386
+	 * @throws ReflectionException
387
+	 */
388
+	public function get_all_event_question_groups($EVT_ID = 0)
389
+	{
390
+		if (! isset($EVT_ID) || ! absint($EVT_ID)) {
391
+			EE_Error::add_error(
392
+				esc_html__(
393
+					'An error occurred. No Event Question Groups could be retrieved because an Event ID was not received.',
394
+					'event_espresso'
395
+				),
396
+				__FILE__,
397
+				__FUNCTION__,
398
+				__LINE__
399
+			);
400
+			return false;
401
+		}
402
+		return EE_Registry::instance()->load_model('Event_Question_Group')->get_all(
403
+			array(
404
+				array('EVT_ID' => $EVT_ID),
405
+			)
406
+		);
407
+	}
408
+
409
+
410
+	/**
411
+	 * get_question_groups
412
+	 *
413
+	 * @param int $EVT_ID
414
+	 * @param boolean $for_primary_attendee
415
+	 * @return array|bool
416
+	 * @throws EE_Error
417
+	 * @throws InvalidArgumentException
418
+	 * @throws ReflectionException
419
+	 * @throws InvalidDataTypeException
420
+	 * @throws InvalidInterfaceException
421
+	 */
422
+	public function get_event_question_groups($EVT_ID = 0, $for_primary_attendee = true)
423
+	{
424
+		if (! isset($EVT_ID) || ! absint($EVT_ID)) {
425
+			EE_Error::add_error(
426
+				esc_html__(
427
+					// @codingStandardsIgnoreStart
428
+					'An error occurred. No Event Question Groups could be retrieved because an Event ID was not received.',
429
+					// @codingStandardsIgnoreEnd
430
+					'event_espresso'
431
+				),
432
+				__FILE__,
433
+				__FUNCTION__,
434
+				__LINE__
435
+			);
436
+			return false;
437
+		}
438
+		$query_params = [
439
+			[
440
+				'EVT_ID' => $EVT_ID,
441
+				EEM_Event_Question_Group::instance()->fieldNameForContext($for_primary_attendee) => true
442
+			]
443
+		];
444
+		if ($for_primary_attendee) {
445
+			$query_params[0]['EQG_primary'] = true;
446
+		} else {
447
+			$query_params[0]['EQG_additional'] = true;
448
+		}
449
+		return EE_Registry::instance()->load_model('Event_Question_Group')->get_all($query_params);
450
+	}
451
+
452
+
453
+	/**
454
+	 * get_question_groups
455
+	 *
456
+	 * @param int $EVT_ID
457
+	 * @param EE_Registration $registration
458
+	 * @return array|bool
459
+	 * @throws EE_Error
460
+	 * @throws InvalidArgumentException
461
+	 * @throws InvalidDataTypeException
462
+	 * @throws InvalidInterfaceException
463
+	 * @throws ReflectionException
464
+	 */
465
+	public function get_question_groups_for_event($EVT_ID, EE_Registration $registration)
466
+	{
467
+		if (! absint($EVT_ID)) {
468
+			EE_Error::add_error(
469
+				esc_html__(
470
+					'An error occurred. No Question Groups could be retrieved because an Event ID was not received.',
471
+					'event_espresso'
472
+				),
473
+				__FILE__,
474
+				__FUNCTION__,
475
+				__LINE__
476
+			);
477
+			return false;
478
+		}
479
+		return EE_Registry::instance()->load_model('Question_Group')->get_all(
480
+			[
481
+				[
482
+					'Event_Question_Group.EVT_ID'      => $EVT_ID,
483
+					'Event_Question_Group.'
484
+						. EEM_Event_Question_Group::instance()->fieldNameForContext(
485
+							$registration->is_primary_registrant()
486
+						) => true
487
+				],
488
+				'order_by' => ['QSG_order' => 'ASC'],
489
+			]
490
+		);
491
+	}
492
+
493
+
494
+	/**
495
+	 * get_question_target_db_column
496
+	 *
497
+	 * @param string $QSG_IDs csv list of $QSG IDs
498
+	 * @return array|bool
499
+	 * @throws EE_Error
500
+	 * @throws ReflectionException
501
+	 */
502
+	public function get_questions_in_groups($QSG_IDs = '')
503
+	{
504
+		if (empty($QSG_IDs)) {
505
+			EE_Error::add_error(
506
+				esc_html__('An error occurred. No Question Group IDs were received.', 'event_espresso'),
507
+				__FILE__,
508
+				__FUNCTION__,
509
+				__LINE__
510
+			);
511
+			return false;
512
+		}
513
+		return EE_Registry::instance()->load_model('Question')->get_all(
514
+			array(
515
+				array(
516
+					'Question_Group.QSG_ID' => array('IN', $QSG_IDs),
517
+					'QST_deleted'           => false,
518
+					'QST_admin_only'        => is_admin(),
519
+				),
520
+				'order_by' => 'QST_order',
521
+			)
522
+		);
523
+	}
524
+
525
+
526
+	/**
527
+	 * get_options_for_question
528
+	 *
529
+	 * @param string $QST_IDs csv list of $QST IDs
530
+	 * @return array|bool
531
+	 * @throws EE_Error
532
+	 * @throws ReflectionException
533
+	 */
534
+	public function get_options_for_question($QST_IDs)
535
+	{
536
+		if (empty($QST_IDs)) {
537
+			EE_Error::add_error(
538
+				esc_html__('An error occurred. No Question IDs were received.', 'event_espresso'),
539
+				__FILE__,
540
+				__FUNCTION__,
541
+				__LINE__
542
+			);
543
+			return false;
544
+		}
545
+		return EE_Registry::instance()->load_model('Question_Option')->get_all(
546
+			array(
547
+				array(
548
+					'Question.QST_ID' => array('IN', $QST_IDs),
549
+					'QSO_deleted'     => false,
550
+				),
551
+				'order_by' => 'QSO_ID',
552
+			)
553
+		);
554
+	}
555
+
556
+
557
+	/**
558
+	 * Gets all events that are published
559
+	 * and have event start time earlier than now and an event end time later than now
560
+	 *
561
+	 * @param array $query_params  An array of query params to further filter on
562
+	 *                             (note that status and DTT_EVT_start and DTT_EVT_end will be overridden)
563
+	 * @param bool  $count         whether to return the count or not (default FALSE)
564
+	 * @return EE_Event[]|int
565
+	 * @throws EE_Error
566
+	 * @throws ReflectionException
567
+	 */
568
+	public function get_active_events($query_params, $count = false)
569
+	{
570
+		if (array_key_exists(0, $query_params)) {
571
+			$where_params = $query_params[0];
572
+			unset($query_params[0]);
573
+		} else {
574
+			$where_params = array();
575
+		}
576
+		// if we have count make sure we don't include group by
577
+		if ($count && isset($query_params['group_by'])) {
578
+			unset($query_params['group_by']);
579
+		}
580
+		// let's add specific query_params for active_events
581
+		// keep in mind this will override any sent status in the query AND any date queries.
582
+		$where_params['status'] = array('IN', array('publish', EEM_Event::sold_out));
583
+		// if already have where params for DTT_EVT_start or DTT_EVT_end then append these conditions
584
+		if (isset($where_params['Datetime.DTT_EVT_start'])) {
585
+			$where_params['Datetime.DTT_EVT_start******'] = array(
586
+				'<',
587
+				EEM_Datetime::instance()->current_time_for_query('DTT_EVT_start'),
588
+			);
589
+		} else {
590
+			$where_params['Datetime.DTT_EVT_start'] = array(
591
+				'<',
592
+				EEM_Datetime::instance()->current_time_for_query('DTT_EVT_start'),
593
+			);
594
+		}
595
+		if (isset($where_params['Datetime.DTT_EVT_end'])) {
596
+			$where_params['Datetime.DTT_EVT_end*****'] = array(
597
+				'>',
598
+				EEM_Datetime::instance()->current_time_for_query('DTT_EVT_end'),
599
+			);
600
+		} else {
601
+			$where_params['Datetime.DTT_EVT_end'] = array(
602
+				'>',
603
+				EEM_Datetime::instance()->current_time_for_query('DTT_EVT_end'),
604
+			);
605
+		}
606
+		$query_params[0] = $where_params;
607
+		// don't use $query_params with count()
608
+		// because we don't want to include additional query clauses like "GROUP BY"
609
+		return $count
610
+			? $this->count(array($where_params), 'EVT_ID', true)
611
+			: $this->get_all($query_params);
612
+	}
613
+
614
+
615
+	/**
616
+	 * get all events that are published and have an event start time later than now
617
+	 *
618
+	 * @param array $query_params  An array of query params to further filter on
619
+	 *                             (Note that status and DTT_EVT_start will be overridden)
620
+	 * @param bool  $count         whether to return the count or not (default FALSE)
621
+	 * @return EE_Event[]|int
622
+	 * @throws EE_Error
623
+	 * @throws ReflectionException
624
+	 */
625
+	public function get_upcoming_events($query_params, $count = false)
626
+	{
627
+		if (array_key_exists(0, $query_params)) {
628
+			$where_params = $query_params[0];
629
+			unset($query_params[0]);
630
+		} else {
631
+			$where_params = array();
632
+		}
633
+		// if we have count make sure we don't include group by
634
+		if ($count && isset($query_params['group_by'])) {
635
+			unset($query_params['group_by']);
636
+		}
637
+		// let's add specific query_params for active_events
638
+		// keep in mind this will override any sent status in the query AND any date queries.
639
+		// we need to pull events with a status of publish and sold_out
640
+		$event_status = array('publish', EEM_Event::sold_out);
641
+		// check if the user can read private events and if so add the 'private status to the were params'
642
+		if (EE_Registry::instance()->CAP->current_user_can('ee_read_private_events', 'get_upcoming_events')) {
643
+			$event_status[] = 'private';
644
+		}
645
+		$where_params['status'] = array('IN', $event_status);
646
+		// if there are already query_params matching DTT_EVT_start then we need to modify that to add them.
647
+		if (isset($where_params['Datetime.DTT_EVT_start'])) {
648
+			$where_params['Datetime.DTT_EVT_start*****'] = array(
649
+				'>',
650
+				EEM_Datetime::instance()->current_time_for_query('DTT_EVT_start'),
651
+			);
652
+		} else {
653
+			$where_params['Datetime.DTT_EVT_start'] = array(
654
+				'>',
655
+				EEM_Datetime::instance()->current_time_for_query('DTT_EVT_start'),
656
+			);
657
+		}
658
+		$query_params[0] = $where_params;
659
+		// don't use $query_params with count()
660
+		// because we don't want to include additional query clauses like "GROUP BY"
661
+		return $count
662
+			? $this->count(array($where_params), 'EVT_ID', true)
663
+			: $this->get_all($query_params);
664
+	}
665
+
666
+
667
+	/**
668
+	 * Gets all events that are published
669
+	 * and have an event end time later than now
670
+	 *
671
+	 * @param array $query_params  An array of query params to further filter on
672
+	 *                             (note that status and DTT_EVT_end will be overridden)
673
+	 * @param bool  $count         whether to return the count or not (default FALSE)
674
+	 * @return EE_Event[]|int
675
+	 * @throws EE_Error
676
+	 * @throws ReflectionException
677
+	 */
678
+	public function get_active_and_upcoming_events($query_params, $count = false)
679
+	{
680
+		if (array_key_exists(0, $query_params)) {
681
+			$where_params = $query_params[0];
682
+			unset($query_params[0]);
683
+		} else {
684
+			$where_params = array();
685
+		}
686
+		// if we have count make sure we don't include group by
687
+		if ($count && isset($query_params['group_by'])) {
688
+			unset($query_params['group_by']);
689
+		}
690
+		// let's add specific query_params for active_events
691
+		// keep in mind this will override any sent status in the query AND any date queries.
692
+		$where_params['status'] = array('IN', array('publish', EEM_Event::sold_out));
693
+		// add where params for DTT_EVT_end
694
+		if (isset($where_params['Datetime.DTT_EVT_end'])) {
695
+			$where_params['Datetime.DTT_EVT_end*****'] = array(
696
+				'>',
697
+				EEM_Datetime::instance()->current_time_for_query('DTT_EVT_end'),
698
+			);
699
+		} else {
700
+			$where_params['Datetime.DTT_EVT_end'] = array(
701
+				'>',
702
+				EEM_Datetime::instance()->current_time_for_query('DTT_EVT_end'),
703
+			);
704
+		}
705
+		$query_params[0] = $where_params;
706
+		// don't use $query_params with count()
707
+		// because we don't want to include additional query clauses like "GROUP BY"
708
+		return $count
709
+			? $this->count(array($where_params), 'EVT_ID', true)
710
+			: $this->get_all($query_params);
711
+	}
712
+
713
+
714
+	/**
715
+	 * This only returns events that are expired.
716
+	 * They may still be published but all their datetimes have expired.
717
+	 *
718
+	 * @param array $query_params  An array of query params to further filter on
719
+	 *                             (note that status and DTT_EVT_end will be overridden)
720
+	 * @param bool  $count         whether to return the count or not (default FALSE)
721
+	 * @return EE_Event[]|int
722
+	 * @throws EE_Error
723
+	 * @throws ReflectionException
724
+	 */
725
+	public function get_expired_events($query_params, $count = false)
726
+	{
727
+		$where_params = isset($query_params[0]) ? $query_params[0] : array();
728
+		// if we have count make sure we don't include group by
729
+		if ($count && isset($query_params['group_by'])) {
730
+			unset($query_params['group_by']);
731
+		}
732
+		// let's add specific query_params for active_events
733
+		// keep in mind this will override any sent status in the query AND any date queries.
734
+		if (isset($where_params['status'])) {
735
+			unset($where_params['status']);
736
+		}
737
+		$exclude_query = $query_params;
738
+		if (isset($exclude_query[0])) {
739
+			unset($exclude_query[0]);
740
+		}
741
+		$exclude_query[0] = array(
742
+			'Datetime.DTT_EVT_end' => array(
743
+				'>',
744
+				EEM_Datetime::instance()->current_time_for_query('DTT_EVT_end'),
745
+			),
746
+		);
747
+		// first get all events that have datetimes where its not expired.
748
+		$event_ids = $this->_get_all_wpdb_results($exclude_query, OBJECT_K, 'Event_CPT.ID');
749
+		$event_ids = array_keys($event_ids);
750
+		// if we have any additional query_params, let's add them to the 'AND' condition
751
+		$and_condition = array(
752
+			'Datetime.DTT_EVT_end' => array('<', EEM_Datetime::instance()->current_time_for_query('DTT_EVT_end')),
753
+			'EVT_ID'               => array('NOT IN', $event_ids),
754
+		);
755
+		if (isset($where_params['OR'])) {
756
+			$and_condition['OR'] = $where_params['OR'];
757
+			unset($where_params['OR']);
758
+		}
759
+		if (isset($where_params['Datetime.DTT_EVT_end'])) {
760
+			$and_condition['Datetime.DTT_EVT_end****'] = $where_params['Datetime.DTT_EVT_end'];
761
+			unset($where_params['Datetime.DTT_EVT_end']);
762
+		}
763
+		if (isset($where_params['Datetime.DTT_EVT_start'])) {
764
+			$and_condition['Datetime.DTT_EVT_start'] = $where_params['Datetime.DTT_EVT_start'];
765
+			unset($where_params['Datetime.DTT_EVT_start']);
766
+		}
767
+		// merge remaining $where params with the and conditions.
768
+		$where_params['AND'] = array_merge($and_condition, $where_params);
769
+		$query_params[0] = $where_params;
770
+		// don't use $query_params with count()
771
+		// because we don't want to include additional query clauses like "GROUP BY"
772
+		return $count
773
+			? $this->count(array($where_params), 'EVT_ID', true)
774
+			: $this->get_all($query_params);
775
+	}
776
+
777
+
778
+
779
+	/**
780
+	 * This basically just returns the events that do not have the publish status.
781
+	 *
782
+	 * @param  array   $query_params An array of query params to further filter on
783
+	 *                               (note that status will be overwritten)
784
+	 * @param  boolean $count        whether to return the count or not (default FALSE)
785
+	 * @return EE_Event[]|int
786
+	 * @throws EE_Error
787
+	 */
788
+	public function get_inactive_events($query_params, $count = false)
789
+	{
790
+		$where_params = isset($query_params[0]) ? $query_params[0] : array();
791
+		// let's add in specific query_params for inactive events.
792
+		if (isset($where_params['status'])) {
793
+			unset($where_params['status']);
794
+		}
795
+		// if we have count make sure we don't include group by
796
+		if ($count && isset($query_params['group_by'])) {
797
+			unset($query_params['group_by']);
798
+		}
799
+		// if we have any additional query_params, let's add them to the 'AND' condition
800
+		$where_params['AND']['status'] = array('!=', 'publish');
801
+		if (isset($where_params['OR'])) {
802
+			$where_params['AND']['OR'] = $where_params['OR'];
803
+			unset($where_params['OR']);
804
+		}
805
+		if (isset($where_params['Datetime.DTT_EVT_end'])) {
806
+			$where_params['AND']['Datetime.DTT_EVT_end****'] = $where_params['Datetime.DTT_EVT_end'];
807
+			unset($where_params['Datetime.DTT_EVT_end']);
808
+		}
809
+		if (isset($where_params['Datetime.DTT_EVT_start'])) {
810
+			$where_params['AND']['Datetime.DTT_EVT_start'] = $where_params['Datetime.DTT_EVT_start'];
811
+			unset($where_params['Datetime.DTT_EVT_start']);
812
+		}
813
+		$query_params[0] = $where_params;
814
+		// don't use $query_params with count()
815
+		// because we don't want to include additional query clauses like "GROUP BY"
816
+		return $count
817
+			? $this->count(array($where_params), 'EVT_ID', true)
818
+			: $this->get_all($query_params);
819
+	}
820
+
821
+
822
+	/**
823
+	 * This is just injecting into the parent add_relationship_to so we do special handling on price relationships
824
+	 * because we don't want to override any existing global default prices but instead insert NEW prices that get
825
+	 * attached to the event. See parent for param descriptions
826
+	 *
827
+	 * @param        $id_or_obj
828
+	 * @param        $other_model_id_or_obj
829
+	 * @param string $relationName
830
+	 * @param array  $where_query
831
+	 * @return EE_Base_Class
832
+	 * @throws EE_Error
833
+	 * @throws ReflectionException
834
+	 */
835
+	public function add_relationship_to($id_or_obj, $other_model_id_or_obj, $relationName, $where_query = array())
836
+	{
837
+		if ($relationName === 'Price') {
838
+			// let's get the PRC object for the given ID to make sure that we aren't dealing with a default
839
+			$prc_chk = $this->get_related_model_obj($relationName)->ensure_is_obj($other_model_id_or_obj);
840
+			// if EVT_ID = 0, then this is a default
841
+			if ((int) $prc_chk->get('EVT_ID') === 0) {
842
+				// let's set the prc_id as 0 so we force an insert on the add_relation_to carried out by relation
843
+				$prc_chk->set('PRC_ID', 0);
844
+			}
845
+			// run parent
846
+			return parent::add_relationship_to($id_or_obj, $prc_chk, $relationName, $where_query);
847
+		}
848
+		// otherwise carry on as normal
849
+		return parent::add_relationship_to($id_or_obj, $other_model_id_or_obj, $relationName, $where_query);
850
+	}
851
+
852
+
853
+
854
+	/******************** DEPRECATED METHODS ********************/
855
+
856
+
857
+	/**
858
+	 * _get_question_target_db_column
859
+	 *
860
+	 * @param EE_Registration $registration    (so existing answers for registration are included)
861
+	 * @param int             $EVT_ID          so all question groups are included for event (not just answers from
862
+	 *                                         registration).
863
+	 * @return    array
864
+	 * @throws ReflectionException
865
+	 * @throws EE_Error*@deprecated as of 4.8.32.rc.001. Instead consider using
866
+	 *                                         EE_Registration_Custom_Questions_Form located in
867
+	 *                                         admin_pages/registrations/form_sections/EE_Registration_Custom_Questions_Form.form.php
868
+	 * @access     public
869
+	 */
870
+	public function assemble_array_of_groups_questions_and_options(EE_Registration $registration, $EVT_ID = 0)
871
+	{
872
+		if (empty($EVT_ID)) {
873
+			throw new EE_Error(esc_html__(
874
+				'An error occurred. No EVT_ID is included.  Needed to know which question groups to retrieve.',
875
+				'event_espresso'
876
+			));
877
+		}
878
+		$questions = array();
879
+		// get all question groups for event
880
+		$qgs = $this->get_question_groups_for_event($EVT_ID, $registration);
881
+		if (! empty($qgs)) {
882
+			foreach ($qgs as $qg) {
883
+				$qsts = $qg->questions();
884
+				$questions[ $qg->ID() ] = $qg->model_field_array();
885
+				$questions[ $qg->ID() ]['QSG_questions'] = array();
886
+				foreach ($qsts as $qst) {
887
+					if ($qst->is_system_question()) {
888
+						continue;
889
+					}
890
+					$answer = EEM_Answer::instance()->get_one(array(
891
+						array(
892
+							'QST_ID' => $qst->ID(),
893
+							'REG_ID' => $registration->ID(),
894
+						),
895
+					));
896
+					$answer = $answer instanceof EE_Answer ? $answer : EEM_Answer::instance()->create_default_object();
897
+					$qst_name = $qstn_id = $qst->ID();
898
+					$ans_id = $answer->ID();
899
+					$qst_name = ! empty($ans_id) ? '[' . $qst_name . '][' . $ans_id . ']' : '[' . $qst_name . ']';
900
+					$input_name = '';
901
+					$input_id = sanitize_key($qst->display_text());
902
+					$input_class = '';
903
+					$questions[ $qg->ID() ]['QSG_questions'][ $qst->ID() ] = $qst->model_field_array();
904
+					$questions[ $qg->ID() ]['QSG_questions'][ $qst->ID() ]['QST_input_name'] = 'qstn'
905
+																						   . $input_name
906
+																						   . $qst_name;
907
+					$questions[ $qg->ID() ]['QSG_questions'][ $qst->ID() ]['QST_input_id'] = $input_id . '-' . $qstn_id;
908
+					$questions[ $qg->ID() ]['QSG_questions'][ $qst->ID() ]['QST_input_class'] = $input_class;
909
+					$questions[ $qg->ID() ]['QSG_questions'][ $qst->ID() ]['QST_options'] = array();
910
+					$questions[ $qg->ID() ]['QSG_questions'][ $qst->ID() ]['qst_obj'] = $qst;
911
+					$questions[ $qg->ID() ]['QSG_questions'][ $qst->ID() ]['ans_obj'] = $answer;
912
+					// leave responses as-is, don't convert stuff into html entities please!
913
+					$questions[ $qg->ID() ]['QSG_questions'][ $qst->ID() ]['htmlentities'] = false;
914
+					if ($qst->type() == 'RADIO_BTN' || $qst->type() == 'CHECKBOX' || $qst->type() == 'DROPDOWN') {
915
+						$QSOs = $qst->options(true, $answer->value());
916
+						if (is_array($QSOs)) {
917
+							foreach ($QSOs as $QSO_ID => $QSO) {
918
+								$questions[ $qg->ID() ]['QSG_questions'][ $qst->ID() ]['QST_options'][ $QSO_ID ] = $QSO->model_field_array();
919
+							}
920
+						}
921
+					}
922
+				}
923
+			}
924
+		}
925
+		return $questions;
926
+	}
927
+
928
+
929
+	/**
930
+	 * @param mixed $cols_n_values either an array of where each key is the name of a field, and the value is its value
931
+	 *                             or an stdClass where each property is the name of a column,
932
+	 * @return EE_Base_Class
933
+	 * @throws EE_Error
934
+	 */
935
+	public function instantiate_class_from_array_or_object($cols_n_values)
936
+	{
937
+		$classInstance = parent::instantiate_class_from_array_or_object($cols_n_values);
938
+		if ($classInstance instanceof EE_Event) {
939
+			// events have their timezone defined in the DB, so use it immediately
940
+			$this->set_timezone($classInstance->get_timezone());
941
+		}
942
+		return $classInstance;
943
+	}
944 944
 }
Please login to merge, or discard this patch.
core/admin/EE_Admin_Page.core.php 2 patches
Indentation   +4197 added lines, -4197 removed lines patch added patch discarded remove patch
@@ -22,4285 +22,4285 @@
 block discarded – undo
22 22
  */
23 23
 abstract class EE_Admin_Page extends EE_Base implements InterminableInterface
24 24
 {
25
-    /**
26
-     * @var EE_Admin_Config
27
-     */
28
-    protected $admin_config;
25
+	/**
26
+	 * @var EE_Admin_Config
27
+	 */
28
+	protected $admin_config;
29 29
 
30
-    /**
31
-     * @var LoaderInterface
32
-     */
33
-    protected $loader;
30
+	/**
31
+	 * @var LoaderInterface
32
+	 */
33
+	protected $loader;
34 34
 
35
-    /**
36
-     * @var RequestInterface
37
-     */
38
-    protected $request;
35
+	/**
36
+	 * @var RequestInterface
37
+	 */
38
+	protected $request;
39 39
 
40
-    // set in _init_page_props()
41
-    public $page_slug;
40
+	// set in _init_page_props()
41
+	public $page_slug;
42 42
 
43
-    public $page_label;
43
+	public $page_label;
44 44
 
45
-    public $page_folder;
45
+	public $page_folder;
46 46
 
47
-    // set in define_page_props()
48
-    protected $_admin_base_url;
47
+	// set in define_page_props()
48
+	protected $_admin_base_url;
49 49
 
50
-    protected $_admin_base_path;
50
+	protected $_admin_base_path;
51 51
 
52
-    protected $_admin_page_title;
52
+	protected $_admin_page_title;
53 53
 
54
-    protected $_labels;
54
+	protected $_labels;
55 55
 
56 56
 
57
-    // set early within EE_Admin_Init
58
-    protected $_wp_page_slug;
57
+	// set early within EE_Admin_Init
58
+	protected $_wp_page_slug;
59 59
 
60
-    // nav tabs
61
-    protected $_nav_tabs;
60
+	// nav tabs
61
+	protected $_nav_tabs;
62 62
 
63
-    protected $_default_nav_tab_name;
63
+	protected $_default_nav_tab_name;
64 64
 
65 65
 
66
-    // template variables (used by templates)
67
-    protected $_template_path;
66
+	// template variables (used by templates)
67
+	protected $_template_path;
68 68
 
69
-    protected $_column_template_path;
69
+	protected $_column_template_path;
70 70
 
71
-    /**
72
-     * @var array $_template_args
73
-     */
74
-    protected $_template_args = [];
71
+	/**
72
+	 * @var array $_template_args
73
+	 */
74
+	protected $_template_args = [];
75 75
 
76
-    /**
77
-     * this will hold the list table object for a given view.
78
-     *
79
-     * @var EE_Admin_List_Table $_list_table_object
80
-     */
81
-    protected $_list_table_object;
76
+	/**
77
+	 * this will hold the list table object for a given view.
78
+	 *
79
+	 * @var EE_Admin_List_Table $_list_table_object
80
+	 */
81
+	protected $_list_table_object;
82 82
 
83
-    // boolean
84
-    protected $_is_UI_request; // this starts at null so we can have no header routes progress through two states.
83
+	// boolean
84
+	protected $_is_UI_request; // this starts at null so we can have no header routes progress through two states.
85 85
 
86
-    protected $_routing;
86
+	protected $_routing;
87 87
 
88
-    // list table args
89
-    protected $_view;
88
+	// list table args
89
+	protected $_view;
90 90
 
91
-    protected $_views;
91
+	protected $_views;
92 92
 
93 93
 
94
-    // action => method pairs used for routing incoming requests
95
-    protected $_page_routes;
94
+	// action => method pairs used for routing incoming requests
95
+	protected $_page_routes;
96 96
 
97
-    /**
98
-     * @var array $_page_config
99
-     */
100
-    protected $_page_config;
97
+	/**
98
+	 * @var array $_page_config
99
+	 */
100
+	protected $_page_config;
101 101
 
102
-    /**
103
-     * the current page route and route config
104
-     *
105
-     * @var string $_route
106
-     */
107
-    protected $_route;
102
+	/**
103
+	 * the current page route and route config
104
+	 *
105
+	 * @var string $_route
106
+	 */
107
+	protected $_route;
108 108
 
109
-    /**
110
-     * @var string $_cpt_route
111
-     */
112
-    protected $_cpt_route;
109
+	/**
110
+	 * @var string $_cpt_route
111
+	 */
112
+	protected $_cpt_route;
113 113
 
114
-    /**
115
-     * @var array $_route_config
116
-     */
117
-    protected $_route_config;
114
+	/**
115
+	 * @var array $_route_config
116
+	 */
117
+	protected $_route_config;
118 118
 
119
-    /**
120
-     * Used to hold default query args for list table routes to help preserve stickiness of filters for carried out
121
-     * actions.
122
-     *
123
-     * @since 4.6.x
124
-     * @var array.
125
-     */
126
-    protected $_default_route_query_args;
119
+	/**
120
+	 * Used to hold default query args for list table routes to help preserve stickiness of filters for carried out
121
+	 * actions.
122
+	 *
123
+	 * @since 4.6.x
124
+	 * @var array.
125
+	 */
126
+	protected $_default_route_query_args;
127 127
 
128
-    // set via request page and action args.
129
-    protected $_current_page;
128
+	// set via request page and action args.
129
+	protected $_current_page;
130 130
 
131
-    protected $_current_view;
131
+	protected $_current_view;
132 132
 
133
-    protected $_current_page_view_url;
133
+	protected $_current_page_view_url;
134 134
 
135
-    /**
136
-     * unprocessed value for the 'action' request param (default '')
137
-     *
138
-     * @var string
139
-     */
140
-    protected $raw_req_action = '';
135
+	/**
136
+	 * unprocessed value for the 'action' request param (default '')
137
+	 *
138
+	 * @var string
139
+	 */
140
+	protected $raw_req_action = '';
141 141
 
142
-    /**
143
-     * unprocessed value for the 'page' request param (default '')
144
-     *
145
-     * @var string
146
-     */
147
-    protected $raw_req_page = '';
148
-
149
-    /**
150
-     * sanitized request action (and nonce)
151
-     *
152
-     * @var string
153
-     */
154
-    protected $_req_action = '';
155
-
156
-    /**
157
-     * sanitized request action nonce
158
-     *
159
-     * @var string
160
-     */
161
-    protected $_req_nonce = '';
162
-
163
-    /**
164
-     * @var string
165
-     */
166
-    protected $_search_btn_label = '';
167
-
168
-    /**
169
-     * @var string
170
-     */
171
-    protected $_search_box_callback = '';
172
-
173
-    /**
174
-     * @var WP_Screen
175
-     */
176
-    protected $_current_screen;
177
-
178
-    // for holding EE_Admin_Hooks object when needed (set via set_hook_object())
179
-    protected $_hook_obj;
180
-
181
-    // for holding incoming request data
182
-    protected $_req_data = [];
183
-
184
-    // yes / no array for admin form fields
185
-    protected $_yes_no_values = [];
186
-
187
-    // some default things shared by all child classes
188
-    protected $_default_espresso_metaboxes = [
189
-        '_espresso_news_post_box',
190
-        '_espresso_links_post_box',
191
-        '_espresso_ratings_request',
192
-        '_espresso_sponsors_post_box',
193
-    ];
194
-
195
-    /**
196
-     * @var EE_Registry
197
-     */
198
-    protected $EE;
199
-
200
-
201
-    /**
202
-     * This is just a property that flags whether the given route is a caffeinated route or not.
203
-     *
204
-     * @var boolean
205
-     */
206
-    protected $_is_caf = false;
207
-
208
-    /**
209
-     * whether or not initializePage() has run
210
-     *
211
-     * @var boolean
212
-     */
213
-    protected $initialized = false;
214
-
215
-    /**
216
-     * @var FeatureFlags
217
-     */
218
-    protected $feature;
219
-
220
-
221
-    /**
222
-     * @var string
223
-     */
224
-    protected $class_name;
225
-
226
-    /**
227
-     * if the current class is an admin page extension, like: Extend_Events_Admin_Page,
228
-     * then this would be the parent classname: Events_Admin_Page
229
-     *
230
-     * @var string
231
-     */
232
-    protected $base_class_name;
233
-
234
-
235
-    /**
236
-     * @Constructor
237
-     * @param bool $routing indicate whether we want to just load the object and handle routing or just load the object.
238
-     * @throws InvalidArgumentException
239
-     * @throws InvalidDataTypeException
240
-     * @throws InvalidInterfaceException
241
-     * @throws ReflectionException
242
-     */
243
-    public function __construct($routing = true)
244
-    {
245
-        $this->loader = LoaderFactory::getLoader();
246
-        $this->admin_config = $this->loader->getShared('EE_Admin_Config');
247
-        $this->feature = $this->loader->getShared(FeatureFlags::class);
248
-        $this->request = $this->loader->getShared(RequestInterface::class);
249
-        // routing enabled?
250
-        $this->_routing = $routing;
251
-
252
-        $this->class_name = get_class($this);
253
-        $this->base_class_name = strpos($this->class_name, 'Extend_') === 0
254
-            ? str_replace('Extend_', '', $this->class_name)
255
-            : '';
256
-
257
-        if (strpos($this->_get_dir(), 'caffeinated') !== false) {
258
-            $this->_is_caf = true;
259
-        }
260
-        $this->_yes_no_values = [
261
-            ['id' => true, 'text' => esc_html__('Yes', 'event_espresso')],
262
-            ['id' => false, 'text' => esc_html__('No', 'event_espresso')],
263
-        ];
264
-        // set the _req_data property.
265
-        $this->_req_data = $this->request->requestParams();
266
-    }
267
-
268
-
269
-    /**
270
-     * @return EE_Admin_Config
271
-     */
272
-    public function adminConfig(): EE_Admin_Config
273
-    {
274
-        return $this->admin_config;
275
-    }
276
-
277
-
278
-    /**
279
-     * @return FeatureFlags
280
-     */
281
-    public function feature(): FeatureFlags
282
-    {
283
-        return $this->feature;
284
-    }
285
-
286
-
287
-    /**
288
-     * This logic used to be in the constructor, but that caused a chicken <--> egg scenario
289
-     * for child classes that needed to set properties prior to these methods getting called,
290
-     * but also needed the parent class to have its construction completed as well.
291
-     * Bottom line is that constructors should ONLY be used for setting initial properties
292
-     * and any complex initialization logic should only run after instantiation is complete.
293
-     *
294
-     * This method gets called immediately after construction from within
295
-     *      EE_Admin_Page_Init::_initialize_admin_page()
296
-     *
297
-     * @throws EE_Error
298
-     * @throws InvalidArgumentException
299
-     * @throws InvalidDataTypeException
300
-     * @throws InvalidInterfaceException
301
-     * @throws ReflectionException
302
-     * @since $VID:$
303
-     */
304
-    public function initializePage()
305
-    {
306
-        if ($this->initialized) {
307
-            return;
308
-        }
309
-        // set initial page props (child method)
310
-        $this->_init_page_props();
311
-        // set global defaults
312
-        $this->_set_defaults();
313
-        // set early because incoming requests could be ajax related and we need to register those hooks.
314
-        $this->_global_ajax_hooks();
315
-        $this->_ajax_hooks();
316
-        // other_page_hooks have to be early too.
317
-        $this->_do_other_page_hooks();
318
-        // set up page dependencies
319
-        $this->_before_page_setup();
320
-        $this->_page_setup();
321
-        $this->initialized = true;
322
-    }
323
-
324
-
325
-    /**
326
-     * _init_page_props
327
-     * Child classes use to set at least the following properties:
328
-     * $page_slug.
329
-     * $page_label.
330
-     *
331
-     * @abstract
332
-     * @return void
333
-     */
334
-    abstract protected function _init_page_props();
335
-
336
-
337
-    /**
338
-     * _ajax_hooks
339
-     * child classes put all their add_action('wp_ajax_{name_of_hook}') hooks in here.
340
-     * Note: within the ajax callback methods.
341
-     *
342
-     * @abstract
343
-     * @return void
344
-     */
345
-    abstract protected function _ajax_hooks();
346
-
347
-
348
-    /**
349
-     * _define_page_props
350
-     * child classes define page properties in here.  Must include at least:
351
-     * $_admin_base_url = base_url for all admin pages
352
-     * $_admin_page_title = default admin_page_title for admin pages
353
-     * $_labels = array of default labels for various automatically generated elements:
354
-     *    array(
355
-     *        'buttons' => array(
356
-     *            'add' => esc_html__('label for add new button'),
357
-     *            'edit' => esc_html__('label for edit button'),
358
-     *            'delete' => esc_html__('label for delete button')
359
-     *            )
360
-     *        )
361
-     *
362
-     * @abstract
363
-     * @return void
364
-     */
365
-    abstract protected function _define_page_props();
366
-
367
-
368
-    /**
369
-     * _set_page_routes
370
-     * child classes use this to define the page routes for all subpages handled by the class.  Page routes are
371
-     * assigned to a action => method pairs in an array and to the $_page_routes property.  Each page route must also
372
-     * have a 'default' route. Here's the format
373
-     * $this->_page_routes = array(
374
-     *        'default' => array(
375
-     *            'func' => '_default_method_handling_route',
376
-     *            'args' => array('array','of','args'),
377
-     *            'noheader' => true, //add this in if this page route is processed before any headers are loaded (i.e.
378
-     *            ajax request, backend processing)
379
-     *            'headers_sent_route'=>'headers_route_reference', //add this if noheader=>true, and you want to load a
380
-     *            headers route after.  The string you enter here should match the defined route reference for a
381
-     *            headers sent route.
382
-     *            'capability' => 'route_capability', //indicate a string for minimum capability required to access
383
-     *            this route.
384
-     *            'obj_id' => 10 // if this route has an object id, then this can include it (used for capability
385
-     *            checks).
386
-     *        ),
387
-     *        'insert_item' => '_method_for_handling_insert_item' //this can be used if all we need to have is a
388
-     *        handling method.
389
-     *        )
390
-     * )
391
-     *
392
-     * @abstract
393
-     * @return void
394
-     */
395
-    abstract protected function _set_page_routes();
396
-
397
-
398
-    /**
399
-     * _set_page_config
400
-     * child classes use this to define the _page_config array for all subpages handled by the class. Each key in the
401
-     * array corresponds to the page_route for the loaded page. Format:
402
-     * $this->_page_config = array(
403
-     *        'default' => array(
404
-     *            'labels' => array(
405
-     *                'buttons' => array(
406
-     *                    'add' => esc_html__('label for adding item'),
407
-     *                    'edit' => esc_html__('label for editing item'),
408
-     *                    'delete' => esc_html__('label for deleting item')
409
-     *                ),
410
-     *                'publishbox' => esc_html__('Localized Title for Publish metabox', 'event_espresso')
411
-     *            ), //optional an array of custom labels for various automatically generated elements to use on the
412
-     *            page. If this isn't present then the defaults will be used as set for the $this->_labels in
413
-     *            _define_page_props() method
414
-     *            'nav' => array(
415
-     *                'label' => esc_html__('Label for Tab', 'event_espresso').
416
-     *                'url' => 'http://someurl', //automatically generated UNLESS you define
417
-     *                'css_class' => 'css-class', //automatically generated UNLESS you define
418
-     *                'order' => 10, //required to indicate tab position.
419
-     *                'persistent' => false //if you want the nav tab to ONLY display when the specific route is
420
-     *                displayed then add this parameter.
421
-     *            'list_table' => 'name_of_list_table' //string for list table class to be loaded for this admin_page.
422
-     *            'metaboxes' => array('metabox1', 'metabox2'), //if present this key indicates we want to load
423
-     *            metaboxes set for eventespresso admin pages.
424
-     *            'has_metaboxes' => true, //this boolean flag can simply be used to indicate if the route will have
425
-     *            metaboxes.  Typically this is used if the 'metaboxes' index is not used because metaboxes are added
426
-     *            later.  We just use this flag to make sure the necessary js gets enqueued on page load.
427
-     *            'has_help_popups' => false //defaults(true) //this boolean flag can simply be used to indicate if the
428
-     *            given route has help popups setup and if it does then we need to make sure thickbox is enqueued.
429
-     *            'columns' => array(4, 2), //this key triggers the setup of a page that uses columns (metaboxes).  The
430
-     *            array indicates the max number of columns (4) and the default number of columns on page load (2).
431
-     *            There is an option in the "screen_options" dropdown that is setup so users can pick what columns they
432
-     *            want to display.
433
-     *            'help_tabs' => array( //this is used for adding help tabs to a page
434
-     *                'tab_id' => array(
435
-     *                    'title' => 'tab_title',
436
-     *                    'filename' => 'name_of_file_containing_content', //this is the primary method for setting
437
-     *                    help tab content.  The fallback if it isn't present is to try a the callback.  Filename
438
-     *                    should match a file in the admin folder's "help_tabs" dir (ie..
439
-     *                    events/help_tabs/name_of_file_containing_content.help_tab.php)
440
-     *                    'callback' => 'callback_method_for_content', //if 'filename' isn't present then system will
441
-     *                    attempt to use the callback which should match the name of a method in the class
442
-     *                    ),
443
-     *                'tab2_id' => array(
444
-     *                    'title' => 'tab2 title',
445
-     *                    'filename' => 'file_name_2'
446
-     *                    'callback' => 'callback_method_for_content',
447
-     *                 ),
448
-     *            'help_sidebar' => 'callback_for_sidebar_content', //this is used for setting up the sidebar in the
449
-     *            help tab area on an admin page. @return void
450
-     *
451
-     * @abstract
452
-     */
453
-    abstract protected function _set_page_config();
454
-
455
-
456
-    /**
457
-     * _add_screen_options
458
-     * Child classes can add any extra wp_screen_options within this method using built-in WP functions/methods for
459
-     * doing so. Note child classes can also define _add_screen_options_($this->_current_view) to limit screen options
460
-     * to a particular view.
461
-     *
462
-     * @link   http://chrismarslender.com/wp-tutorials/wordpress-screen-options-tutorial/
463
-     *         see also WP_Screen object documents...
464
-     * @link   http://codex.wordpress.org/Class_Reference/WP_Screen
465
-     * @abstract
466
-     * @return void
467
-     */
468
-    abstract protected function _add_screen_options();
469
-
470
-
471
-    /**
472
-     * _add_feature_pointers
473
-     * Child classes should use this method for implementing any "feature pointers" (using built-in WP styling js).
474
-     * Note child classes can also define _add_feature_pointers_($this->_current_view) to limit screen options to a
475
-     * particular view. Note: this is just a placeholder for now.  Implementation will come down the road See:
476
-     * WP_Internal_Pointers class in wp-admin/includes/template.php for example (its a final class so can't be
477
-     * extended) also see:
478
-     *
479
-     * @link   http://eamann.com/tech/wordpress-portland/
480
-     * @abstract
481
-     * @return void
482
-     */
483
-    abstract protected function _add_feature_pointers();
484
-
485
-
486
-    /**
487
-     * load_scripts_styles
488
-     * child classes put their wp_enqueue_script and wp_enqueue_style hooks in here for anything they need loaded for
489
-     * their pages/subpages.  Note this is for all pages/subpages of the system.  You can also load only specific
490
-     * scripts/styles per view by putting them in a dynamic function in this format
491
-     * (load_scripts_styles_{$this->_current_view}) which matches your page route (action request arg)
492
-     *
493
-     * @abstract
494
-     * @return void
495
-     */
496
-    abstract public function load_scripts_styles();
497
-
498
-
499
-    /**
500
-     * admin_init
501
-     * Anything that should be set/executed at 'admin_init' WP hook runtime should be put in here.  This will apply to
502
-     * all pages/views loaded by child class.
503
-     *
504
-     * @abstract
505
-     * @return void
506
-     */
507
-    abstract public function admin_init();
508
-
509
-
510
-    /**
511
-     * admin_notices
512
-     * Anything triggered by the 'admin_notices' WP hook should be put in here.  This particular method will apply to
513
-     * all pages/views loaded by child class.
514
-     *
515
-     * @abstract
516
-     * @return void
517
-     */
518
-    abstract public function admin_notices();
519
-
520
-
521
-    /**
522
-     * admin_footer_scripts
523
-     * Anything triggered by the 'admin_print_footer_scripts' WP hook should be put in here. This particular method
524
-     * will apply to all pages/views loaded by child class.
525
-     *
526
-     * @return void
527
-     */
528
-    abstract public function admin_footer_scripts();
529
-
530
-
531
-    /**
532
-     * admin_footer
533
-     * anything triggered by the 'admin_footer' WP action hook should be added to here. This particular method will
534
-     * apply to all pages/views loaded by child class.
535
-     *
536
-     * @return void
537
-     */
538
-    public function admin_footer()
539
-    {
540
-    }
541
-
542
-
543
-    /**
544
-     * _global_ajax_hooks
545
-     * all global add_action('wp_ajax_{name_of_hook}') hooks in here.
546
-     * Note: within the ajax callback methods.
547
-     *
548
-     * @abstract
549
-     * @return void
550
-     */
551
-    protected function _global_ajax_hooks()
552
-    {
553
-        // for lazy loading of metabox content
554
-        add_action('wp_ajax_espresso-ajax-content', [$this, 'ajax_metabox_content'], 10);
555
-
556
-        add_action(
557
-            'wp_ajax_espresso_hide_status_change_notice',
558
-            [$this, 'hideStatusChangeNotice']
559
-        );
560
-        add_action(
561
-            'wp_ajax_nopriv_espresso_hide_status_change_notice',
562
-            [$this, 'hideStatusChangeNotice']
563
-        );
564
-    }
565
-
566
-
567
-    public function ajax_metabox_content()
568
-    {
569
-        $content_id  = $this->request->getRequestParam('contentid', '');
570
-        $content_url = $this->request->getRequestParam('contenturl', '', 'url');
571
-        EE_Admin_Page::cached_rss_display($content_id, $content_url);
572
-        wp_die();
573
-    }
574
-
575
-
576
-    public function hideStatusChangeNotice()
577
-    {
578
-        $response = [];
579
-        try {
580
-            /** @var StatusChangeNotice $status_change_notice */
581
-            $status_change_notice = $this->loader->getShared(
582
-                'EventEspresso\core\domain\services\admin\notices\status_change\StatusChangeNotice'
583
-            );
584
-            $response['success'] = $status_change_notice->dismiss() > -1;
585
-        } catch (Exception $exception) {
586
-            $response['errors'] = $exception->getMessage();
587
-        }
588
-        echo wp_json_encode($response);
589
-        exit();
590
-    }
591
-
592
-
593
-    /**
594
-     * allows extending classes do something specific before the parent constructor runs _page_setup().
595
-     *
596
-     * @return void
597
-     */
598
-    protected function _before_page_setup()
599
-    {
600
-        // default is to do nothing
601
-    }
602
-
603
-
604
-    /**
605
-     * Makes sure any things that need to be loaded early get handled.
606
-     * We also escape early here if the page requested doesn't match the object.
607
-     *
608
-     * @final
609
-     * @return void
610
-     * @throws EE_Error
611
-     * @throws InvalidArgumentException
612
-     * @throws ReflectionException
613
-     * @throws InvalidDataTypeException
614
-     * @throws InvalidInterfaceException
615
-     */
616
-    final protected function _page_setup()
617
-    {
618
-        // requires?
619
-        // admin_init stuff - global - we're setting this REALLY early
620
-        // so if EE_Admin pages have to hook into other WP pages they can.
621
-        // But keep in mind, not everything is available from the EE_Admin Page object at this point.
622
-        add_action('admin_init', [$this, 'admin_init_global'], 5);
623
-        // next verify if we need to load anything...
624
-        $this->_current_page = $this->request->getRequestParam('page', '', 'key');
625
-        $this->page_folder   = strtolower(
626
-            str_replace(['_Admin_Page', 'Extend_'], '', $this->class_name)
627
-        );
628
-        global $ee_menu_slugs;
629
-        $ee_menu_slugs = (array) $ee_menu_slugs;
630
-        if (
631
-            ! $this->request->isAjax()
632
-            && (! $this->_current_page || ! isset($ee_menu_slugs[ $this->_current_page ]))
633
-        ) {
634
-            return;
635
-        }
636
-        // because WP List tables have two duplicate select inputs for choosing bulk actions,
637
-        // we need to copy the action from the second to the first
638
-        $action     = $this->request->getRequestParam('action', '-1', 'key');
639
-        $action2    = $this->request->getRequestParam('action2', '-1', 'key');
640
-        $action     = $action !== '-1' ? $action : $action2;
641
-        $req_action = $action !== '-1' ? $action : 'default';
642
-
643
-        // if a specific 'route' has been set, and the action is 'default' OR we are doing_ajax
644
-        // then let's use the route as the action.
645
-        // This covers cases where we're coming in from a list table that isn't on the default route.
646
-        $route = $this->request->getRequestParam('route');
647
-        $this->_req_action = $route && ($req_action === 'default' || $this->request->isAjax())
648
-            ? $route
649
-            : $req_action;
650
-
651
-        $this->_current_view = $this->_req_action;
652
-        $this->_req_nonce    = $this->_req_action . '_nonce';
653
-        $this->_define_page_props();
654
-        $this->_current_page_view_url = add_query_arg(
655
-            ['page' => $this->_current_page, 'action' => $this->_current_view],
656
-            $this->_admin_base_url
657
-        );
658
-        // set page configs
659
-        $this->_set_page_routes();
660
-        $this->_set_page_config();
661
-        // let's include any referrer data in our default_query_args for this route for "stickiness".
662
-        if ($this->request->requestParamIsSet('wp_referer')) {
663
-            $wp_referer = $this->request->getRequestParam('wp_referer');
664
-            if ($wp_referer) {
665
-                $this->_default_route_query_args['wp_referer'] = $wp_referer;
666
-            }
667
-        }
668
-        // for caffeinated and other extended functionality.
669
-        //  If there is a _extend_page_config method
670
-        // then let's run that to modify the all the various page configuration arrays
671
-        if (method_exists($this, '_extend_page_config')) {
672
-            $this->_extend_page_config();
673
-        }
674
-        // for CPT and other extended functionality.
675
-        // If there is an _extend_page_config_for_cpt
676
-        // then let's run that to modify all the various page configuration arrays.
677
-        if (method_exists($this, '_extend_page_config_for_cpt')) {
678
-            $this->_extend_page_config_for_cpt();
679
-        }
680
-        // filter routes and page_config so addons can add their stuff. Filtering done per class
681
-        $this->_page_routes = apply_filters(
682
-            'FHEE__' . $this->class_name . '__page_setup__page_routes',
683
-            $this->_page_routes,
684
-            $this
685
-        );
686
-        $this->_page_config = apply_filters(
687
-            'FHEE__' . $this->class_name . '__page_setup__page_config',
688
-            $this->_page_config,
689
-            $this
690
-        );
691
-        if ($this->base_class_name !== '') {
692
-            $this->_page_routes = apply_filters(
693
-                'FHEE__' . $this->base_class_name . '__page_setup__page_routes',
694
-                $this->_page_routes,
695
-                $this
696
-            );
697
-            $this->_page_config = apply_filters(
698
-                'FHEE__' . $this->base_class_name . '__page_setup__page_config',
699
-                $this->_page_config,
700
-                $this
701
-            );
702
-        }
703
-        // if AHEE__EE_Admin_Page__route_admin_request_$this->_current_view method is present
704
-        // then we call it hooked into the AHEE__EE_Admin_Page__route_admin_request action
705
-        if (method_exists($this, 'AHEE__EE_Admin_Page__route_admin_request_' . $this->_current_view)) {
706
-            add_action(
707
-                'AHEE__EE_Admin_Page__route_admin_request',
708
-                [$this, 'AHEE__EE_Admin_Page__route_admin_request_' . $this->_current_view],
709
-                10,
710
-                2
711
-            );
712
-        }
713
-        // next route only if routing enabled
714
-        if ($this->_routing && ! $this->request->isAjax()) {
715
-            $this->_verify_routes();
716
-            // next let's just check user_access and kill if no access
717
-            $this->check_user_access();
718
-            if ($this->_is_UI_request) {
719
-                // admin_init stuff - global, all views for this page class, specific view
720
-                add_action('admin_init', [$this, 'admin_init'], 10);
721
-                if (method_exists($this, 'admin_init_' . $this->_current_view)) {
722
-                    add_action('admin_init', [$this, 'admin_init_' . $this->_current_view], 15);
723
-                }
724
-            } else {
725
-                // hijack regular WP loading and route admin request immediately
726
-                @ini_set('memory_limit', apply_filters('admin_memory_limit', WP_MAX_MEMORY_LIMIT));
727
-                $this->route_admin_request();
728
-            }
729
-        }
730
-    }
731
-
732
-
733
-    /**
734
-     * Provides a way for related child admin pages to load stuff on the loaded admin page.
735
-     *
736
-     * @return void
737
-     * @throws EE_Error
738
-     */
739
-    private function _do_other_page_hooks()
740
-    {
741
-        $registered_pages = apply_filters('FHEE_do_other_page_hooks_' . $this->page_slug, []);
742
-        foreach ($registered_pages as $page) {
743
-            // now let's setup the file name and class that should be present
744
-            $classname = str_replace('.class.php', '', $page);
745
-            // autoloaders should take care of loading file
746
-            if (! class_exists($classname)) {
747
-                $error_msg[] = sprintf(
748
-                    esc_html__(
749
-                        'Something went wrong with loading the %s admin hooks page.',
750
-                        'event_espresso'
751
-                    ),
752
-                    $page
753
-                );
754
-                $error_msg[] = $error_msg[0]
755
-                               . "\r\n"
756
-                               . sprintf(
757
-                                   esc_html__(
758
-                                       'There is no class in place for the %1$s admin hooks page.%2$sMake sure you have %3$s defined. If this is a non-EE-core admin page then you also must have an autoloader in place for your class',
759
-                                       'event_espresso'
760
-                                   ),
761
-                                   $page,
762
-                                   '<br />',
763
-                                   '<strong>' . $classname . '</strong>'
764
-                               );
765
-                throw new EE_Error(implode('||', $error_msg));
766
-            }
767
-            // notice we are passing the instance of this class to the hook object.
768
-            $this->loader->getShared($classname, [$this]);
769
-        }
770
-    }
771
-
772
-
773
-    /**
774
-     * @throws ReflectionException
775
-     * @throws EE_Error
776
-     */
777
-    public function load_page_dependencies()
778
-    {
779
-        try {
780
-            $this->_load_page_dependencies();
781
-        } catch (EE_Error $e) {
782
-            $e->get_error();
783
-        }
784
-    }
785
-
786
-
787
-    /**
788
-     * load_page_dependencies
789
-     * loads things specific to this page class when its loaded.  Really helps with efficiency.
790
-     *
791
-     * @return void
792
-     * @throws DomainException
793
-     * @throws EE_Error
794
-     * @throws InvalidArgumentException
795
-     * @throws InvalidDataTypeException
796
-     * @throws InvalidInterfaceException
797
-     */
798
-    protected function _load_page_dependencies()
799
-    {
800
-        // let's set the current_screen and screen options to override what WP set
801
-        $this->_current_screen = get_current_screen();
802
-        // load admin_notices - global, page class, and view specific
803
-        add_action('admin_notices', [$this, 'admin_notices_global'], 5);
804
-        add_action('admin_notices', [$this, 'admin_notices'], 10);
805
-        if (method_exists($this, 'admin_notices_' . $this->_current_view)) {
806
-            add_action('admin_notices', [$this, 'admin_notices_' . $this->_current_view], 15);
807
-        }
808
-        // load network admin_notices - global, page class, and view specific
809
-        add_action('network_admin_notices', [$this, 'network_admin_notices_global'], 5);
810
-        if (method_exists($this, 'network_admin_notices_' . $this->_current_view)) {
811
-            add_action('network_admin_notices', [$this, 'network_admin_notices_' . $this->_current_view]);
812
-        }
813
-        // this will save any per_page screen options if they are present
814
-        $this->_set_per_page_screen_options();
815
-        // setup list table properties
816
-        $this->_set_list_table();
817
-        // child classes can "register" a metabox to be automatically handled via the _page_config array property.
818
-        // However in some cases the metaboxes will need to be added within a route handling callback.
819
-        $this->_add_registered_meta_boxes();
820
-        $this->_add_screen_columns();
821
-        // add screen options - global, page child class, and view specific
822
-        $this->_add_global_screen_options();
823
-        $this->_add_screen_options();
824
-        $add_screen_options = "_add_screen_options_{$this->_current_view}";
825
-        if (method_exists($this, $add_screen_options)) {
826
-            $this->{$add_screen_options}();
827
-        }
828
-        // add help tab(s) - set via page_config and qtips.
829
-        $this->_add_help_tabs();
830
-        $this->_add_qtips();
831
-        // add feature_pointers - global, page child class, and view specific
832
-        $this->_add_feature_pointers();
833
-        $this->_add_global_feature_pointers();
834
-        $add_feature_pointer = "_add_feature_pointer_{$this->_current_view}";
835
-        if (method_exists($this, $add_feature_pointer)) {
836
-            $this->{$add_feature_pointer}();
837
-        }
838
-        // enqueue scripts/styles - global, page class, and view specific
839
-        add_action('admin_enqueue_scripts', [$this, 'load_global_scripts_styles'], 5);
840
-        add_action('admin_enqueue_scripts', [$this, 'load_scripts_styles'], 10);
841
-        if (method_exists($this, "load_scripts_styles_{$this->_current_view}")) {
842
-            add_action('admin_enqueue_scripts', [$this, "load_scripts_styles_{$this->_current_view}"], 15);
843
-        }
844
-        add_action('admin_enqueue_scripts', [$this, 'admin_footer_scripts_eei18n_js_strings'], 100);
845
-        // admin_print_footer_scripts - global, page child class, and view specific.
846
-        // NOTE, despite the name, whenever possible, scripts should NOT be loaded using this.
847
-        // In most cases that's doing_it_wrong().  But adding hidden container elements etc.
848
-        // is a good use case. Notice the late priority we're giving these
849
-        add_action('admin_print_footer_scripts', [$this, 'admin_footer_scripts_global'], 99);
850
-        add_action('admin_print_footer_scripts', [$this, 'admin_footer_scripts'], 100);
851
-        if (method_exists($this, "admin_footer_scripts_{$this->_current_view}")) {
852
-            add_action('admin_print_footer_scripts', [$this, "admin_footer_scripts_{$this->_current_view}"], 101);
853
-        }
854
-        // admin footer scripts
855
-        add_action('admin_footer', [$this, 'admin_footer_global'], 99);
856
-        add_action('admin_footer', [$this, 'admin_footer'], 100);
857
-        if (method_exists($this, "admin_footer_{$this->_current_view}")) {
858
-            add_action('admin_footer', [$this, "admin_footer_{$this->_current_view}"], 101);
859
-        }
860
-        do_action('FHEE__EE_Admin_Page___load_page_dependencies__after_load', $this->page_slug);
861
-        // targeted hook
862
-        do_action(
863
-            "FHEE__EE_Admin_Page___load_page_dependencies__after_load__{$this->page_slug}__{$this->_req_action}"
864
-        );
865
-    }
866
-
867
-
868
-    /**
869
-     * _set_defaults
870
-     * This sets some global defaults for class properties.
871
-     */
872
-    private function _set_defaults()
873
-    {
874
-        $this->_current_screen       = $this->_admin_page_title = $this->_req_action = $this->_req_nonce = null;
875
-        $this->_event                = $this->_template_path = $this->_column_template_path = null;
876
-        $this->_nav_tabs             = $this->_views = $this->_page_routes = [];
877
-        $this->_page_config          = $this->_default_route_query_args = [];
878
-        $this->_default_nav_tab_name = 'overview';
879
-        // init template args
880
-        $this->_template_args = [
881
-            'admin_page_header'  => '',
882
-            'admin_page_content' => '',
883
-            'post_body_content'  => '',
884
-            'before_list_table'  => '',
885
-            'after_list_table'   => '',
886
-        ];
887
-    }
888
-
889
-
890
-    /**
891
-     * route_admin_request
892
-     *
893
-     * @return void
894
-     * @throws InvalidArgumentException
895
-     * @throws InvalidInterfaceException
896
-     * @throws InvalidDataTypeException
897
-     * @throws EE_Error
898
-     * @throws ReflectionException
899
-     * @see    _route_admin_request()
900
-     */
901
-    public function route_admin_request()
902
-    {
903
-        try {
904
-            $this->_route_admin_request();
905
-        } catch (EE_Error $e) {
906
-            $e->get_error();
907
-        }
908
-    }
909
-
910
-
911
-    public function set_wp_page_slug($wp_page_slug)
912
-    {
913
-        $this->_wp_page_slug = $wp_page_slug;
914
-        // if in network admin then we need to append "-network" to the page slug. Why? Because that's how WP rolls...
915
-        if (is_network_admin()) {
916
-            $this->_wp_page_slug .= '-network';
917
-        }
918
-    }
919
-
920
-
921
-    /**
922
-     * _verify_routes
923
-     * All this method does is verify the incoming request and make sure that routes exist for it.  We do this early so
924
-     * we know if we need to drop out.
925
-     *
926
-     * @return bool
927
-     * @throws EE_Error
928
-     */
929
-    protected function _verify_routes()
930
-    {
931
-        do_action('AHEE_log', __FILE__, __FUNCTION__, '');
932
-        if (! $this->_current_page && ! $this->request->isAjax()) {
933
-            return false;
934
-        }
935
-        $this->_route = false;
936
-        // check that the page_routes array is not empty
937
-        if (empty($this->_page_routes)) {
938
-            // user error msg
939
-            $error_msg = sprintf(
940
-                esc_html__('No page routes have been set for the %s admin page.', 'event_espresso'),
941
-                $this->_admin_page_title
942
-            );
943
-            // developer error msg
944
-            $error_msg .= '||' . $error_msg
945
-                          . esc_html__(
946
-                              ' Make sure the "set_page_routes()" method exists, and is setting the "_page_routes" array properly.',
947
-                              'event_espresso'
948
-                          );
949
-            throw new EE_Error($error_msg);
950
-        }
951
-        // and that the requested page route exists
952
-        if (array_key_exists($this->_req_action, $this->_page_routes)) {
953
-            $this->_route        = $this->_page_routes[ $this->_req_action ];
954
-            $this->_route_config = $this->_page_config[ $this->_req_action ] ?? [];
955
-        } else {
956
-            // user error msg
957
-            $error_msg = sprintf(
958
-                esc_html__(
959
-                    'The requested page route does not exist for the %s admin page.',
960
-                    'event_espresso'
961
-                ),
962
-                $this->_admin_page_title
963
-            );
964
-            // developer error msg
965
-            $error_msg .= '||' . $error_msg
966
-                          . sprintf(
967
-                              esc_html__(
968
-                                  ' Create a key in the "_page_routes" array named "%s" and set its value to the appropriate method.',
969
-                                  'event_espresso'
970
-                              ),
971
-                              $this->_req_action
972
-                          );
973
-            throw new EE_Error($error_msg);
974
-        }
975
-        // and that a default route exists
976
-        if (! array_key_exists('default', $this->_page_routes)) {
977
-            // user error msg
978
-            $error_msg = sprintf(
979
-                esc_html__(
980
-                    'A default page route has not been set for the % admin page.',
981
-                    'event_espresso'
982
-                ),
983
-                $this->_admin_page_title
984
-            );
985
-            // developer error msg
986
-            $error_msg .= '||' . $error_msg
987
-                          . esc_html__(
988
-                              ' Create a key in the "_page_routes" array named "default" and set its value to your default page method.',
989
-                              'event_espresso'
990
-                          );
991
-            throw new EE_Error($error_msg);
992
-        }
993
-
994
-        // first lets' catch if the UI request has EVER been set.
995
-        if ($this->_is_UI_request === null) {
996
-            // lets set if this is a UI request or not.
997
-            $this->_is_UI_request = ! $this->request->getRequestParam('noheader', false, 'bool');
998
-            // wait a minute... we might have a noheader in the route array
999
-            $this->_is_UI_request = ! (
1000
-                is_array($this->_route) && isset($this->_route['noheader']) && $this->_route['noheader']
1001
-            )
1002
-                ? $this->_is_UI_request
1003
-                : false;
1004
-        }
1005
-        $this->_set_current_labels();
1006
-        return true;
1007
-    }
1008
-
1009
-
1010
-    /**
1011
-     * this method simply verifies a given route and makes sure its an actual route available for the loaded page
1012
-     *
1013
-     * @param string $route the route name we're verifying
1014
-     * @return bool we'll throw an exception if this isn't a valid route.
1015
-     * @throws EE_Error
1016
-     */
1017
-    protected function _verify_route($route)
1018
-    {
1019
-        if (array_key_exists($this->_req_action, $this->_page_routes)) {
1020
-            return true;
1021
-        }
1022
-        // user error msg
1023
-        $error_msg = sprintf(
1024
-            esc_html__('The given page route does not exist for the %s admin page.', 'event_espresso'),
1025
-            $this->_admin_page_title
1026
-        );
1027
-        // developer error msg
1028
-        $error_msg .= '||' . $error_msg
1029
-                      . sprintf(
1030
-                          esc_html__(
1031
-                              ' Check the route you are using in your method (%s) and make sure it matches a route set in your "_page_routes" array property',
1032
-                              'event_espresso'
1033
-                          ),
1034
-                          $route
1035
-                      );
1036
-        throw new EE_Error($error_msg);
1037
-    }
1038
-
1039
-
1040
-    /**
1041
-     * perform nonce verification
1042
-     * This method has be encapsulated here so that any ajax requests that bypass normal routes can verify their nonces
1043
-     * using this method (and save retyping!)
1044
-     *
1045
-     * @param string $nonce     The nonce sent
1046
-     * @param string $nonce_ref The nonce reference string (name0)
1047
-     * @return void
1048
-     * @throws EE_Error
1049
-     * @throws InvalidArgumentException
1050
-     * @throws InvalidDataTypeException
1051
-     * @throws InvalidInterfaceException
1052
-     */
1053
-    protected function _verify_nonce($nonce, $nonce_ref)
1054
-    {
1055
-        // verify nonce against expected value
1056
-        if (! wp_verify_nonce($nonce, $nonce_ref)) {
1057
-            // these are not the droids you are looking for !!!
1058
-            $msg = sprintf(
1059
-                esc_html__('%sNonce Fail.%s', 'event_espresso'),
1060
-                '<a href="https://www.youtube.com/watch?v=56_S0WeTkzs">',
1061
-                '</a>'
1062
-            );
1063
-            if (WP_DEBUG) {
1064
-                $msg .= "\n  ";
1065
-                $msg .= sprintf(
1066
-                    esc_html__(
1067
-                        'In order to dynamically generate nonces for your actions, use the %s::add_query_args_and_nonce() method. May the Nonce be with you!',
1068
-                        'event_espresso'
1069
-                    ),
1070
-                    __CLASS__
1071
-                );
1072
-            }
1073
-            if (! $this->request->isAjax()) {
1074
-                wp_die($msg);
1075
-            }
1076
-            EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
1077
-            $this->_return_json();
1078
-        }
1079
-    }
1080
-
1081
-
1082
-    /**
1083
-     * _route_admin_request()
1084
-     * Meat and potatoes of the class.  Basically, this dude checks out what's being requested and sees if there are
1085
-     * some doodads to work the magic and handle the flingjangy. Translation:  Checks if the requested action is listed
1086
-     * in the page routes and then will try to load the corresponding method.
1087
-     *
1088
-     * @return void
1089
-     * @throws EE_Error
1090
-     * @throws InvalidArgumentException
1091
-     * @throws InvalidDataTypeException
1092
-     * @throws InvalidInterfaceException
1093
-     * @throws ReflectionException
1094
-     */
1095
-    protected function _route_admin_request()
1096
-    {
1097
-        if (! $this->_is_UI_request) {
1098
-            $this->_verify_routes();
1099
-        }
1100
-        $nonce_check = ! isset($this->_route_config['require_nonce']) || $this->_route_config['require_nonce'];
1101
-        if ($this->_req_action !== 'default' && $nonce_check) {
1102
-            // set nonce from post data
1103
-            $nonce = $this->request->getRequestParam($this->_req_nonce, '');
1104
-            $this->_verify_nonce($nonce, $this->_req_nonce);
1105
-        }
1106
-        // set the nav_tabs array but ONLY if this is  UI_request
1107
-        if ($this->_is_UI_request) {
1108
-            $this->_set_nav_tabs();
1109
-        }
1110
-        // grab callback function
1111
-        $func = is_array($this->_route) && isset($this->_route['func']) ? $this->_route['func'] : $this->_route;
1112
-        // check if callback has args
1113
-        $args      = is_array($this->_route) && isset($this->_route['args']) ? $this->_route['args'] : [];
1114
-        $error_msg = '';
1115
-        // action right before calling route
1116
-        // (hook is something like 'AHEE__Registrations_Admin_Page__route_admin_request')
1117
-        if (! did_action('AHEE__EE_Admin_Page__route_admin_request')) {
1118
-            do_action('AHEE__EE_Admin_Page__route_admin_request', $this->_current_view, $this);
1119
-        }
1120
-        // strip _wp_http_referer from the server REQUEST_URI
1121
-        // else it grows in length on every submission due to recursion,
1122
-        // ultimately causing a "Request-URI Too Large" error
1123
-        $this->request->unSetRequestParam('_wp_http_referer');
1124
-        $this->request->unSetServerParam('_wp_http_referer');
1125
-        $cleaner_request_uri = remove_query_arg(
1126
-            '_wp_http_referer',
1127
-            wp_unslash($this->request->getServerParam('REQUEST_URI'))
1128
-        );
1129
-        $this->request->setRequestParam('_wp_http_referer', $cleaner_request_uri, true);
1130
-        $this->request->setServerParam('REQUEST_URI', $cleaner_request_uri, true);
1131
-        if (! empty($func)) {
1132
-            if (is_array($func)) {
1133
-                [$class, $method] = $func;
1134
-            } elseif (strpos($func, '::') !== false) {
1135
-                [$class, $method] = explode('::', $func);
1136
-            } else {
1137
-                $class  = $this;
1138
-                $method = $func;
1139
-            }
1140
-            if (! (is_object($class) && $class === $this)) {
1141
-                // send along this admin page object for access by addons.
1142
-                $args['admin_page_object'] = $this;
1143
-            }
1144
-            if (
1145
-                // is it a method on a class that doesn't work?
1146
-                (
1147
-                    (
1148
-                        method_exists($class, $method)
1149
-                        && call_user_func_array([$class, $method], $args) === false
1150
-                    )
1151
-                    && (
1152
-                        // is it a standalone function that doesn't work?
1153
-                        function_exists($method)
1154
-                        && call_user_func_array(
1155
-                            $func,
1156
-                            array_merge(['admin_page_object' => $this], $args)
1157
-                        ) === false
1158
-                    )
1159
-                )
1160
-                || (
1161
-                    // is it neither a class method NOR a standalone function?
1162
-                    ! method_exists($class, $method)
1163
-                    && ! function_exists($method)
1164
-                )
1165
-            ) {
1166
-                // user error msg
1167
-                $error_msg = esc_html__(
1168
-                    'An error occurred. The  requested page route could not be found.',
1169
-                    'event_espresso'
1170
-                );
1171
-                // developer error msg
1172
-                $error_msg .= '||';
1173
-                $error_msg .= sprintf(
1174
-                    esc_html__(
1175
-                        'Page route "%s" could not be called. Check that the spelling for method names and actions in the "_page_routes" array are all correct.',
1176
-                        'event_espresso'
1177
-                    ),
1178
-                    $method
1179
-                );
1180
-            }
1181
-            if (! empty($error_msg)) {
1182
-                throw new EE_Error($error_msg);
1183
-            }
1184
-        }
1185
-        // if we've routed and this route has a no headers route AND a sent_headers_route,
1186
-        // then we need to reset the routing properties to the new route.
1187
-        // now if UI request is FALSE and noheader is true AND we have a headers_sent_route in the route array then let's set UI_request to true because the no header route has a second func after headers have been sent.
1188
-        if (
1189
-            $this->_is_UI_request === false
1190
-            && is_array($this->_route)
1191
-            && ! empty($this->_route['headers_sent_route'])
1192
-        ) {
1193
-            $this->_reset_routing_properties($this->_route['headers_sent_route']);
1194
-        }
1195
-    }
1196
-
1197
-
1198
-    /**
1199
-     * This method just allows the resetting of page properties in the case where a no headers
1200
-     * route redirects to a headers route in its route config.
1201
-     *
1202
-     * @param string $new_route New (non header) route to redirect to.
1203
-     * @return   void
1204
-     * @throws ReflectionException
1205
-     * @throws InvalidArgumentException
1206
-     * @throws InvalidInterfaceException
1207
-     * @throws InvalidDataTypeException
1208
-     * @throws EE_Error
1209
-     * @since   4.3.0
1210
-     */
1211
-    protected function _reset_routing_properties($new_route)
1212
-    {
1213
-        $this->_is_UI_request = true;
1214
-        // now we set the current route to whatever the headers_sent_route is set at
1215
-        $this->request->setRequestParam('action', $new_route);
1216
-        // rerun page setup
1217
-        $this->_page_setup();
1218
-    }
1219
-
1220
-
1221
-    /**
1222
-     * _add_query_arg
1223
-     * adds nonce to array of arguments then calls WP add_query_arg function
1224
-     *(internally just uses EEH_URL's function with the same name)
1225
-     *
1226
-     * @param array  $args
1227
-     * @param string $url
1228
-     * @param bool   $sticky                  if true, then the existing Request params will be appended to the
1229
-     *                                        generated url in an associative array indexed by the key 'wp_referer';
1230
-     *                                        Example usage: If the current page is:
1231
-     *                                        http://mydomain.com/wp-admin/admin.php?page=espresso_registrations
1232
-     *                                        &action=default&event_id=20&month_range=March%202015
1233
-     *                                        &_wpnonce=5467821
1234
-     *                                        and you call:
1235
-     *                                        EE_Admin_Page::add_query_args_and_nonce(
1236
-     *                                        array(
1237
-     *                                        'action' => 'resend_something',
1238
-     *                                        'page=>espresso_registrations'
1239
-     *                                        ),
1240
-     *                                        $some_url,
1241
-     *                                        true
1242
-     *                                        );
1243
-     *                                        It will produce a url in this structure:
1244
-     *                                        http://{$some_url}/?page=espresso_registrations&action=resend_something
1245
-     *                                        &wp_referer[action]=default&wp_referer[event_id]=20&wpreferer[
1246
-     *                                        month_range]=March%202015
1247
-     * @param bool   $exclude_nonce           If true, the the nonce will be excluded from the generated nonce.
1248
-     * @return string
1249
-     */
1250
-    public static function add_query_args_and_nonce(
1251
-        $args = [],
1252
-        $url = '',
1253
-        $sticky = false,
1254
-        $exclude_nonce = false
1255
-    ) {
1256
-        // if there is a _wp_http_referer include the values from the request but only if sticky = true
1257
-        if ($sticky) {
1258
-            /** @var RequestInterface $request */
1259
-            $request = LoaderFactory::getLoader()->getShared(RequestInterface::class);
1260
-            $request->unSetRequestParams(['_wp_http_referer', 'wp_referer'], true);
1261
-            $request->unSetServerParam('_wp_http_referer', true);
1262
-            foreach ($request->requestParams() as $key => $value) {
1263
-                // do not add nonces
1264
-                if (strpos($key, 'nonce') !== false) {
1265
-                    continue;
1266
-                }
1267
-                $args[ 'wp_referer[' . $key . ']' ] = is_string($value) ? htmlspecialchars($value) : $value;
1268
-            }
1269
-        }
1270
-        return EEH_URL::add_query_args_and_nonce($args, $url, $exclude_nonce);
1271
-    }
1272
-
1273
-
1274
-    /**
1275
-     * This returns a generated link that will load the related help tab.
1276
-     *
1277
-     * @param string $help_tab_id the id for the connected help tab
1278
-     * @param string $icon_style  (optional) include css class for the style you want to use for the help icon.
1279
-     * @param string $help_text   (optional) send help text you want to use for the link if default not to be used
1280
-     * @return string              generated link
1281
-     * @uses EEH_Template::get_help_tab_link()
1282
-     */
1283
-    protected function _get_help_tab_link($help_tab_id, $icon_style = '', $help_text = '')
1284
-    {
1285
-        return EEH_Template::get_help_tab_link(
1286
-            $help_tab_id,
1287
-            $this->page_slug,
1288
-            $this->_req_action,
1289
-            $icon_style,
1290
-            $help_text
1291
-        );
1292
-    }
1293
-
1294
-
1295
-    /**
1296
-     * _add_help_tabs
1297
-     * Note child classes define their help tabs within the page_config array.
1298
-     *
1299
-     * @link   http://codex.wordpress.org/Function_Reference/add_help_tab
1300
-     * @return void
1301
-     * @throws DomainException
1302
-     * @throws EE_Error
1303
-     * @throws ReflectionException
1304
-     */
1305
-    protected function _add_help_tabs()
1306
-    {
1307
-        if (isset($this->_page_config[ $this->_req_action ])) {
1308
-            $config = $this->_page_config[ $this->_req_action ];
1309
-            // let's see if there is a help_sidebar set for the current route and we'll set that up for usage as well.
1310
-            if (is_array($config) && isset($config['help_sidebar'])) {
1311
-                // check that the callback given is valid
1312
-                if (! method_exists($this, $config['help_sidebar'])) {
1313
-                    throw new EE_Error(
1314
-                        sprintf(
1315
-                            esc_html__(
1316
-                                'The _page_config array has a callback set for the "help_sidebar" option.  However the callback given (%s) is not a valid callback.  Doublecheck the spelling and make sure this method exists for the class %s',
1317
-                                'event_espresso'
1318
-                            ),
1319
-                            $config['help_sidebar'],
1320
-                            $this->class_name
1321
-                        )
1322
-                    );
1323
-                }
1324
-                $content = apply_filters(
1325
-                    'FHEE__' . $this->class_name . '__add_help_tabs__help_sidebar',
1326
-                    $this->{$config['help_sidebar']}()
1327
-                );
1328
-                $this->_current_screen->set_help_sidebar($content);
1329
-            }
1330
-            if (! isset($config['help_tabs'])) {
1331
-                return;
1332
-            } //no help tabs for this route
1333
-            foreach ((array) $config['help_tabs'] as $tab_id => $cfg) {
1334
-                // we're here so there ARE help tabs!
1335
-                // make sure we've got what we need
1336
-                if (! isset($cfg['title'])) {
1337
-                    throw new EE_Error(
1338
-                        esc_html__(
1339
-                            'The _page_config array is not set up properly for help tabs.  It is missing a title',
1340
-                            'event_espresso'
1341
-                        )
1342
-                    );
1343
-                }
1344
-                if (! isset($cfg['filename']) && ! isset($cfg['callback']) && ! isset($cfg['content'])) {
1345
-                    throw new EE_Error(
1346
-                        esc_html__(
1347
-                            'The _page_config array is not setup properly for help tabs. It is missing a either a filename reference, or a callback reference or a content reference so there is no way to know the content for the help tab',
1348
-                            'event_espresso'
1349
-                        )
1350
-                    );
1351
-                }
1352
-                // first priority goes to content.
1353
-                if (! empty($cfg['content'])) {
1354
-                    $content = ! empty($cfg['content']) ? $cfg['content'] : null;
1355
-                    // second priority goes to filename
1356
-                } elseif (! empty($cfg['filename'])) {
1357
-                    $file_path = $this->_get_dir() . '/help_tabs/' . $cfg['filename'] . '.help_tab.php';
1358
-                    // it's possible that the file is located on decaf route (and above sets up for caf route, if this is the case then lets check decaf route too)
1359
-                    $file_path = ! is_readable($file_path) ? EE_ADMIN_PAGES
1360
-                                                             . basename($this->_get_dir())
1361
-                                                             . '/help_tabs/'
1362
-                                                             . $cfg['filename']
1363
-                                                             . '.help_tab.php' : $file_path;
1364
-                    // if file is STILL not readable then let's do a EE_Error so its more graceful than a fatal error.
1365
-                    if (! isset($cfg['callback']) && ! is_readable($file_path)) {
1366
-                        EE_Error::add_error(
1367
-                            sprintf(
1368
-                                esc_html__(
1369
-                                    'The filename given for the help tab %s is not a valid file and there is no other configuration for the tab content.  Please check that the string you set for the help tab on this route (%s) is the correct spelling.  The file should be in %s',
1370
-                                    'event_espresso'
1371
-                                ),
1372
-                                $tab_id,
1373
-                                key($config),
1374
-                                $file_path
1375
-                            ),
1376
-                            __FILE__,
1377
-                            __FUNCTION__,
1378
-                            __LINE__
1379
-                        );
1380
-                        return;
1381
-                    }
1382
-                    $template_args['admin_page_obj'] = $this;
1383
-                    $content                         = EEH_Template::display_template(
1384
-                        $file_path,
1385
-                        $template_args,
1386
-                        true
1387
-                    );
1388
-                } else {
1389
-                    $content = '';
1390
-                }
1391
-                // check if callback is valid
1392
-                if (
1393
-                    empty($content)
1394
-                    && (
1395
-                        ! isset($cfg['callback']) || ! method_exists($this, $cfg['callback'])
1396
-                    )
1397
-                ) {
1398
-                    EE_Error::add_error(
1399
-                        sprintf(
1400
-                            esc_html__(
1401
-                                'The callback given for a %s help tab on this page does not content OR a corresponding method for generating the content.  Check the spelling or make sure the method is present.',
1402
-                                'event_espresso'
1403
-                            ),
1404
-                            $cfg['title']
1405
-                        ),
1406
-                        __FILE__,
1407
-                        __FUNCTION__,
1408
-                        __LINE__
1409
-                    );
1410
-                    return;
1411
-                }
1412
-                // setup config array for help tab method
1413
-                $id  = $this->page_slug . '-' . $this->_req_action . '-' . $tab_id;
1414
-                $_ht = [
1415
-                    'id'       => $id,
1416
-                    'title'    => $cfg['title'],
1417
-                    'callback' => isset($cfg['callback']) && empty($content) ? [$this, $cfg['callback']] : null,
1418
-                    'content'  => $content,
1419
-                ];
1420
-                $this->_current_screen->add_help_tab($_ht);
1421
-            }
1422
-        }
1423
-    }
1424
-
1425
-
1426
-    /**
1427
-     * This simply sets up any qtips that have been defined in the page config
1428
-     *
1429
-     * @return void
1430
-     * @throws ReflectionException
1431
-     * @throws EE_Error
1432
-     */
1433
-    protected function _add_qtips()
1434
-    {
1435
-        if (isset($this->_route_config['qtips'])) {
1436
-            $qtips = (array) $this->_route_config['qtips'];
1437
-            // load qtip loader
1438
-            $path = [
1439
-                $this->_get_dir() . '/qtips/',
1440
-                EE_ADMIN_PAGES . basename($this->_get_dir()) . '/qtips/',
1441
-            ];
1442
-            EEH_Qtip_Loader::instance()->register($qtips, $path);
1443
-        }
1444
-    }
1445
-
1446
-
1447
-    /**
1448
-     * _set_nav_tabs
1449
-     * This sets up the nav tabs from the page_routes array.  This method can be overwritten by child classes if you
1450
-     * wish to add additional tabs or modify accordingly.
1451
-     *
1452
-     * @return void
1453
-     * @throws InvalidArgumentException
1454
-     * @throws InvalidInterfaceException
1455
-     * @throws InvalidDataTypeException
1456
-     */
1457
-    protected function _set_nav_tabs()
1458
-    {
1459
-        $i = 0;
1460
-        $only_tab = count($this->_page_config) < 2;
1461
-        foreach ($this->_page_config as $slug => $config) {
1462
-            if (! is_array($config) || empty($config['nav'])) {
1463
-                continue;
1464
-            }
1465
-            // no nav tab for this config
1466
-            // check for persistent flag
1467
-            if ($slug !== $this->_req_action && isset($config['nav']['persistent']) && ! $config['nav']['persistent']) {
1468
-                // nav tab is only to appear when route requested.
1469
-                continue;
1470
-            }
1471
-            if (! $this->check_user_access($slug, true)) {
1472
-                // no nav tab because current user does not have access.
1473
-                continue;
1474
-            }
1475
-            $css_class = isset($config['css_class']) ? $config['css_class'] . ' ' : '';
1476
-            $css_class .= $only_tab ? ' ee-only-tab' : '';
1477
-
1478
-            $this->_nav_tabs[ $slug ] = [
1479
-                'url'       => $config['nav']['url'] ?? EE_Admin_Page::add_query_args_and_nonce(
1480
-                    ['action' => $slug],
1481
-                    $this->_admin_base_url
1482
-                ),
1483
-                'link_text' => $this->navTabLabel($config['nav'], $slug),
1484
-                'css_class' => $this->_req_action === $slug ? $css_class . ' nav-tab-active' : $css_class,
1485
-                'order'     => $config['nav']['order'] ?? $i,
1486
-            ];
1487
-            $i++;
1488
-        }
1489
-        // if $this->_nav_tabs is empty then lets set the default
1490
-        if (empty($this->_nav_tabs)) {
1491
-            $this->_nav_tabs[ $this->_default_nav_tab_name ] = [
1492
-                'url'       => $this->_admin_base_url,
1493
-                'link_text' => ucwords(str_replace('_', ' ', $this->_default_nav_tab_name)),
1494
-                'css_class' => 'nav-tab-active',
1495
-                'order'     => 10,
1496
-            ];
1497
-        }
1498
-        // now let's sort the tabs according to order
1499
-        usort($this->_nav_tabs, [$this, '_sort_nav_tabs']);
1500
-    }
1501
-
1502
-
1503
-    private function navTabLabel(array $nav_tab, string $slug): string
1504
-    {
1505
-        $label = $nav_tab['label'] ?? ucwords(str_replace('_', ' ', $slug));
1506
-        $icon = $nav_tab['icon'] ?? null;
1507
-        $icon = $icon ? '<span class="dashicons ' . $icon . '"></span>' : '';
1508
-        return '
142
+	/**
143
+	 * unprocessed value for the 'page' request param (default '')
144
+	 *
145
+	 * @var string
146
+	 */
147
+	protected $raw_req_page = '';
148
+
149
+	/**
150
+	 * sanitized request action (and nonce)
151
+	 *
152
+	 * @var string
153
+	 */
154
+	protected $_req_action = '';
155
+
156
+	/**
157
+	 * sanitized request action nonce
158
+	 *
159
+	 * @var string
160
+	 */
161
+	protected $_req_nonce = '';
162
+
163
+	/**
164
+	 * @var string
165
+	 */
166
+	protected $_search_btn_label = '';
167
+
168
+	/**
169
+	 * @var string
170
+	 */
171
+	protected $_search_box_callback = '';
172
+
173
+	/**
174
+	 * @var WP_Screen
175
+	 */
176
+	protected $_current_screen;
177
+
178
+	// for holding EE_Admin_Hooks object when needed (set via set_hook_object())
179
+	protected $_hook_obj;
180
+
181
+	// for holding incoming request data
182
+	protected $_req_data = [];
183
+
184
+	// yes / no array for admin form fields
185
+	protected $_yes_no_values = [];
186
+
187
+	// some default things shared by all child classes
188
+	protected $_default_espresso_metaboxes = [
189
+		'_espresso_news_post_box',
190
+		'_espresso_links_post_box',
191
+		'_espresso_ratings_request',
192
+		'_espresso_sponsors_post_box',
193
+	];
194
+
195
+	/**
196
+	 * @var EE_Registry
197
+	 */
198
+	protected $EE;
199
+
200
+
201
+	/**
202
+	 * This is just a property that flags whether the given route is a caffeinated route or not.
203
+	 *
204
+	 * @var boolean
205
+	 */
206
+	protected $_is_caf = false;
207
+
208
+	/**
209
+	 * whether or not initializePage() has run
210
+	 *
211
+	 * @var boolean
212
+	 */
213
+	protected $initialized = false;
214
+
215
+	/**
216
+	 * @var FeatureFlags
217
+	 */
218
+	protected $feature;
219
+
220
+
221
+	/**
222
+	 * @var string
223
+	 */
224
+	protected $class_name;
225
+
226
+	/**
227
+	 * if the current class is an admin page extension, like: Extend_Events_Admin_Page,
228
+	 * then this would be the parent classname: Events_Admin_Page
229
+	 *
230
+	 * @var string
231
+	 */
232
+	protected $base_class_name;
233
+
234
+
235
+	/**
236
+	 * @Constructor
237
+	 * @param bool $routing indicate whether we want to just load the object and handle routing or just load the object.
238
+	 * @throws InvalidArgumentException
239
+	 * @throws InvalidDataTypeException
240
+	 * @throws InvalidInterfaceException
241
+	 * @throws ReflectionException
242
+	 */
243
+	public function __construct($routing = true)
244
+	{
245
+		$this->loader = LoaderFactory::getLoader();
246
+		$this->admin_config = $this->loader->getShared('EE_Admin_Config');
247
+		$this->feature = $this->loader->getShared(FeatureFlags::class);
248
+		$this->request = $this->loader->getShared(RequestInterface::class);
249
+		// routing enabled?
250
+		$this->_routing = $routing;
251
+
252
+		$this->class_name = get_class($this);
253
+		$this->base_class_name = strpos($this->class_name, 'Extend_') === 0
254
+			? str_replace('Extend_', '', $this->class_name)
255
+			: '';
256
+
257
+		if (strpos($this->_get_dir(), 'caffeinated') !== false) {
258
+			$this->_is_caf = true;
259
+		}
260
+		$this->_yes_no_values = [
261
+			['id' => true, 'text' => esc_html__('Yes', 'event_espresso')],
262
+			['id' => false, 'text' => esc_html__('No', 'event_espresso')],
263
+		];
264
+		// set the _req_data property.
265
+		$this->_req_data = $this->request->requestParams();
266
+	}
267
+
268
+
269
+	/**
270
+	 * @return EE_Admin_Config
271
+	 */
272
+	public function adminConfig(): EE_Admin_Config
273
+	{
274
+		return $this->admin_config;
275
+	}
276
+
277
+
278
+	/**
279
+	 * @return FeatureFlags
280
+	 */
281
+	public function feature(): FeatureFlags
282
+	{
283
+		return $this->feature;
284
+	}
285
+
286
+
287
+	/**
288
+	 * This logic used to be in the constructor, but that caused a chicken <--> egg scenario
289
+	 * for child classes that needed to set properties prior to these methods getting called,
290
+	 * but also needed the parent class to have its construction completed as well.
291
+	 * Bottom line is that constructors should ONLY be used for setting initial properties
292
+	 * and any complex initialization logic should only run after instantiation is complete.
293
+	 *
294
+	 * This method gets called immediately after construction from within
295
+	 *      EE_Admin_Page_Init::_initialize_admin_page()
296
+	 *
297
+	 * @throws EE_Error
298
+	 * @throws InvalidArgumentException
299
+	 * @throws InvalidDataTypeException
300
+	 * @throws InvalidInterfaceException
301
+	 * @throws ReflectionException
302
+	 * @since $VID:$
303
+	 */
304
+	public function initializePage()
305
+	{
306
+		if ($this->initialized) {
307
+			return;
308
+		}
309
+		// set initial page props (child method)
310
+		$this->_init_page_props();
311
+		// set global defaults
312
+		$this->_set_defaults();
313
+		// set early because incoming requests could be ajax related and we need to register those hooks.
314
+		$this->_global_ajax_hooks();
315
+		$this->_ajax_hooks();
316
+		// other_page_hooks have to be early too.
317
+		$this->_do_other_page_hooks();
318
+		// set up page dependencies
319
+		$this->_before_page_setup();
320
+		$this->_page_setup();
321
+		$this->initialized = true;
322
+	}
323
+
324
+
325
+	/**
326
+	 * _init_page_props
327
+	 * Child classes use to set at least the following properties:
328
+	 * $page_slug.
329
+	 * $page_label.
330
+	 *
331
+	 * @abstract
332
+	 * @return void
333
+	 */
334
+	abstract protected function _init_page_props();
335
+
336
+
337
+	/**
338
+	 * _ajax_hooks
339
+	 * child classes put all their add_action('wp_ajax_{name_of_hook}') hooks in here.
340
+	 * Note: within the ajax callback methods.
341
+	 *
342
+	 * @abstract
343
+	 * @return void
344
+	 */
345
+	abstract protected function _ajax_hooks();
346
+
347
+
348
+	/**
349
+	 * _define_page_props
350
+	 * child classes define page properties in here.  Must include at least:
351
+	 * $_admin_base_url = base_url for all admin pages
352
+	 * $_admin_page_title = default admin_page_title for admin pages
353
+	 * $_labels = array of default labels for various automatically generated elements:
354
+	 *    array(
355
+	 *        'buttons' => array(
356
+	 *            'add' => esc_html__('label for add new button'),
357
+	 *            'edit' => esc_html__('label for edit button'),
358
+	 *            'delete' => esc_html__('label for delete button')
359
+	 *            )
360
+	 *        )
361
+	 *
362
+	 * @abstract
363
+	 * @return void
364
+	 */
365
+	abstract protected function _define_page_props();
366
+
367
+
368
+	/**
369
+	 * _set_page_routes
370
+	 * child classes use this to define the page routes for all subpages handled by the class.  Page routes are
371
+	 * assigned to a action => method pairs in an array and to the $_page_routes property.  Each page route must also
372
+	 * have a 'default' route. Here's the format
373
+	 * $this->_page_routes = array(
374
+	 *        'default' => array(
375
+	 *            'func' => '_default_method_handling_route',
376
+	 *            'args' => array('array','of','args'),
377
+	 *            'noheader' => true, //add this in if this page route is processed before any headers are loaded (i.e.
378
+	 *            ajax request, backend processing)
379
+	 *            'headers_sent_route'=>'headers_route_reference', //add this if noheader=>true, and you want to load a
380
+	 *            headers route after.  The string you enter here should match the defined route reference for a
381
+	 *            headers sent route.
382
+	 *            'capability' => 'route_capability', //indicate a string for minimum capability required to access
383
+	 *            this route.
384
+	 *            'obj_id' => 10 // if this route has an object id, then this can include it (used for capability
385
+	 *            checks).
386
+	 *        ),
387
+	 *        'insert_item' => '_method_for_handling_insert_item' //this can be used if all we need to have is a
388
+	 *        handling method.
389
+	 *        )
390
+	 * )
391
+	 *
392
+	 * @abstract
393
+	 * @return void
394
+	 */
395
+	abstract protected function _set_page_routes();
396
+
397
+
398
+	/**
399
+	 * _set_page_config
400
+	 * child classes use this to define the _page_config array for all subpages handled by the class. Each key in the
401
+	 * array corresponds to the page_route for the loaded page. Format:
402
+	 * $this->_page_config = array(
403
+	 *        'default' => array(
404
+	 *            'labels' => array(
405
+	 *                'buttons' => array(
406
+	 *                    'add' => esc_html__('label for adding item'),
407
+	 *                    'edit' => esc_html__('label for editing item'),
408
+	 *                    'delete' => esc_html__('label for deleting item')
409
+	 *                ),
410
+	 *                'publishbox' => esc_html__('Localized Title for Publish metabox', 'event_espresso')
411
+	 *            ), //optional an array of custom labels for various automatically generated elements to use on the
412
+	 *            page. If this isn't present then the defaults will be used as set for the $this->_labels in
413
+	 *            _define_page_props() method
414
+	 *            'nav' => array(
415
+	 *                'label' => esc_html__('Label for Tab', 'event_espresso').
416
+	 *                'url' => 'http://someurl', //automatically generated UNLESS you define
417
+	 *                'css_class' => 'css-class', //automatically generated UNLESS you define
418
+	 *                'order' => 10, //required to indicate tab position.
419
+	 *                'persistent' => false //if you want the nav tab to ONLY display when the specific route is
420
+	 *                displayed then add this parameter.
421
+	 *            'list_table' => 'name_of_list_table' //string for list table class to be loaded for this admin_page.
422
+	 *            'metaboxes' => array('metabox1', 'metabox2'), //if present this key indicates we want to load
423
+	 *            metaboxes set for eventespresso admin pages.
424
+	 *            'has_metaboxes' => true, //this boolean flag can simply be used to indicate if the route will have
425
+	 *            metaboxes.  Typically this is used if the 'metaboxes' index is not used because metaboxes are added
426
+	 *            later.  We just use this flag to make sure the necessary js gets enqueued on page load.
427
+	 *            'has_help_popups' => false //defaults(true) //this boolean flag can simply be used to indicate if the
428
+	 *            given route has help popups setup and if it does then we need to make sure thickbox is enqueued.
429
+	 *            'columns' => array(4, 2), //this key triggers the setup of a page that uses columns (metaboxes).  The
430
+	 *            array indicates the max number of columns (4) and the default number of columns on page load (2).
431
+	 *            There is an option in the "screen_options" dropdown that is setup so users can pick what columns they
432
+	 *            want to display.
433
+	 *            'help_tabs' => array( //this is used for adding help tabs to a page
434
+	 *                'tab_id' => array(
435
+	 *                    'title' => 'tab_title',
436
+	 *                    'filename' => 'name_of_file_containing_content', //this is the primary method for setting
437
+	 *                    help tab content.  The fallback if it isn't present is to try a the callback.  Filename
438
+	 *                    should match a file in the admin folder's "help_tabs" dir (ie..
439
+	 *                    events/help_tabs/name_of_file_containing_content.help_tab.php)
440
+	 *                    'callback' => 'callback_method_for_content', //if 'filename' isn't present then system will
441
+	 *                    attempt to use the callback which should match the name of a method in the class
442
+	 *                    ),
443
+	 *                'tab2_id' => array(
444
+	 *                    'title' => 'tab2 title',
445
+	 *                    'filename' => 'file_name_2'
446
+	 *                    'callback' => 'callback_method_for_content',
447
+	 *                 ),
448
+	 *            'help_sidebar' => 'callback_for_sidebar_content', //this is used for setting up the sidebar in the
449
+	 *            help tab area on an admin page. @return void
450
+	 *
451
+	 * @abstract
452
+	 */
453
+	abstract protected function _set_page_config();
454
+
455
+
456
+	/**
457
+	 * _add_screen_options
458
+	 * Child classes can add any extra wp_screen_options within this method using built-in WP functions/methods for
459
+	 * doing so. Note child classes can also define _add_screen_options_($this->_current_view) to limit screen options
460
+	 * to a particular view.
461
+	 *
462
+	 * @link   http://chrismarslender.com/wp-tutorials/wordpress-screen-options-tutorial/
463
+	 *         see also WP_Screen object documents...
464
+	 * @link   http://codex.wordpress.org/Class_Reference/WP_Screen
465
+	 * @abstract
466
+	 * @return void
467
+	 */
468
+	abstract protected function _add_screen_options();
469
+
470
+
471
+	/**
472
+	 * _add_feature_pointers
473
+	 * Child classes should use this method for implementing any "feature pointers" (using built-in WP styling js).
474
+	 * Note child classes can also define _add_feature_pointers_($this->_current_view) to limit screen options to a
475
+	 * particular view. Note: this is just a placeholder for now.  Implementation will come down the road See:
476
+	 * WP_Internal_Pointers class in wp-admin/includes/template.php for example (its a final class so can't be
477
+	 * extended) also see:
478
+	 *
479
+	 * @link   http://eamann.com/tech/wordpress-portland/
480
+	 * @abstract
481
+	 * @return void
482
+	 */
483
+	abstract protected function _add_feature_pointers();
484
+
485
+
486
+	/**
487
+	 * load_scripts_styles
488
+	 * child classes put their wp_enqueue_script and wp_enqueue_style hooks in here for anything they need loaded for
489
+	 * their pages/subpages.  Note this is for all pages/subpages of the system.  You can also load only specific
490
+	 * scripts/styles per view by putting them in a dynamic function in this format
491
+	 * (load_scripts_styles_{$this->_current_view}) which matches your page route (action request arg)
492
+	 *
493
+	 * @abstract
494
+	 * @return void
495
+	 */
496
+	abstract public function load_scripts_styles();
497
+
498
+
499
+	/**
500
+	 * admin_init
501
+	 * Anything that should be set/executed at 'admin_init' WP hook runtime should be put in here.  This will apply to
502
+	 * all pages/views loaded by child class.
503
+	 *
504
+	 * @abstract
505
+	 * @return void
506
+	 */
507
+	abstract public function admin_init();
508
+
509
+
510
+	/**
511
+	 * admin_notices
512
+	 * Anything triggered by the 'admin_notices' WP hook should be put in here.  This particular method will apply to
513
+	 * all pages/views loaded by child class.
514
+	 *
515
+	 * @abstract
516
+	 * @return void
517
+	 */
518
+	abstract public function admin_notices();
519
+
520
+
521
+	/**
522
+	 * admin_footer_scripts
523
+	 * Anything triggered by the 'admin_print_footer_scripts' WP hook should be put in here. This particular method
524
+	 * will apply to all pages/views loaded by child class.
525
+	 *
526
+	 * @return void
527
+	 */
528
+	abstract public function admin_footer_scripts();
529
+
530
+
531
+	/**
532
+	 * admin_footer
533
+	 * anything triggered by the 'admin_footer' WP action hook should be added to here. This particular method will
534
+	 * apply to all pages/views loaded by child class.
535
+	 *
536
+	 * @return void
537
+	 */
538
+	public function admin_footer()
539
+	{
540
+	}
541
+
542
+
543
+	/**
544
+	 * _global_ajax_hooks
545
+	 * all global add_action('wp_ajax_{name_of_hook}') hooks in here.
546
+	 * Note: within the ajax callback methods.
547
+	 *
548
+	 * @abstract
549
+	 * @return void
550
+	 */
551
+	protected function _global_ajax_hooks()
552
+	{
553
+		// for lazy loading of metabox content
554
+		add_action('wp_ajax_espresso-ajax-content', [$this, 'ajax_metabox_content'], 10);
555
+
556
+		add_action(
557
+			'wp_ajax_espresso_hide_status_change_notice',
558
+			[$this, 'hideStatusChangeNotice']
559
+		);
560
+		add_action(
561
+			'wp_ajax_nopriv_espresso_hide_status_change_notice',
562
+			[$this, 'hideStatusChangeNotice']
563
+		);
564
+	}
565
+
566
+
567
+	public function ajax_metabox_content()
568
+	{
569
+		$content_id  = $this->request->getRequestParam('contentid', '');
570
+		$content_url = $this->request->getRequestParam('contenturl', '', 'url');
571
+		EE_Admin_Page::cached_rss_display($content_id, $content_url);
572
+		wp_die();
573
+	}
574
+
575
+
576
+	public function hideStatusChangeNotice()
577
+	{
578
+		$response = [];
579
+		try {
580
+			/** @var StatusChangeNotice $status_change_notice */
581
+			$status_change_notice = $this->loader->getShared(
582
+				'EventEspresso\core\domain\services\admin\notices\status_change\StatusChangeNotice'
583
+			);
584
+			$response['success'] = $status_change_notice->dismiss() > -1;
585
+		} catch (Exception $exception) {
586
+			$response['errors'] = $exception->getMessage();
587
+		}
588
+		echo wp_json_encode($response);
589
+		exit();
590
+	}
591
+
592
+
593
+	/**
594
+	 * allows extending classes do something specific before the parent constructor runs _page_setup().
595
+	 *
596
+	 * @return void
597
+	 */
598
+	protected function _before_page_setup()
599
+	{
600
+		// default is to do nothing
601
+	}
602
+
603
+
604
+	/**
605
+	 * Makes sure any things that need to be loaded early get handled.
606
+	 * We also escape early here if the page requested doesn't match the object.
607
+	 *
608
+	 * @final
609
+	 * @return void
610
+	 * @throws EE_Error
611
+	 * @throws InvalidArgumentException
612
+	 * @throws ReflectionException
613
+	 * @throws InvalidDataTypeException
614
+	 * @throws InvalidInterfaceException
615
+	 */
616
+	final protected function _page_setup()
617
+	{
618
+		// requires?
619
+		// admin_init stuff - global - we're setting this REALLY early
620
+		// so if EE_Admin pages have to hook into other WP pages they can.
621
+		// But keep in mind, not everything is available from the EE_Admin Page object at this point.
622
+		add_action('admin_init', [$this, 'admin_init_global'], 5);
623
+		// next verify if we need to load anything...
624
+		$this->_current_page = $this->request->getRequestParam('page', '', 'key');
625
+		$this->page_folder   = strtolower(
626
+			str_replace(['_Admin_Page', 'Extend_'], '', $this->class_name)
627
+		);
628
+		global $ee_menu_slugs;
629
+		$ee_menu_slugs = (array) $ee_menu_slugs;
630
+		if (
631
+			! $this->request->isAjax()
632
+			&& (! $this->_current_page || ! isset($ee_menu_slugs[ $this->_current_page ]))
633
+		) {
634
+			return;
635
+		}
636
+		// because WP List tables have two duplicate select inputs for choosing bulk actions,
637
+		// we need to copy the action from the second to the first
638
+		$action     = $this->request->getRequestParam('action', '-1', 'key');
639
+		$action2    = $this->request->getRequestParam('action2', '-1', 'key');
640
+		$action     = $action !== '-1' ? $action : $action2;
641
+		$req_action = $action !== '-1' ? $action : 'default';
642
+
643
+		// if a specific 'route' has been set, and the action is 'default' OR we are doing_ajax
644
+		// then let's use the route as the action.
645
+		// This covers cases where we're coming in from a list table that isn't on the default route.
646
+		$route = $this->request->getRequestParam('route');
647
+		$this->_req_action = $route && ($req_action === 'default' || $this->request->isAjax())
648
+			? $route
649
+			: $req_action;
650
+
651
+		$this->_current_view = $this->_req_action;
652
+		$this->_req_nonce    = $this->_req_action . '_nonce';
653
+		$this->_define_page_props();
654
+		$this->_current_page_view_url = add_query_arg(
655
+			['page' => $this->_current_page, 'action' => $this->_current_view],
656
+			$this->_admin_base_url
657
+		);
658
+		// set page configs
659
+		$this->_set_page_routes();
660
+		$this->_set_page_config();
661
+		// let's include any referrer data in our default_query_args for this route for "stickiness".
662
+		if ($this->request->requestParamIsSet('wp_referer')) {
663
+			$wp_referer = $this->request->getRequestParam('wp_referer');
664
+			if ($wp_referer) {
665
+				$this->_default_route_query_args['wp_referer'] = $wp_referer;
666
+			}
667
+		}
668
+		// for caffeinated and other extended functionality.
669
+		//  If there is a _extend_page_config method
670
+		// then let's run that to modify the all the various page configuration arrays
671
+		if (method_exists($this, '_extend_page_config')) {
672
+			$this->_extend_page_config();
673
+		}
674
+		// for CPT and other extended functionality.
675
+		// If there is an _extend_page_config_for_cpt
676
+		// then let's run that to modify all the various page configuration arrays.
677
+		if (method_exists($this, '_extend_page_config_for_cpt')) {
678
+			$this->_extend_page_config_for_cpt();
679
+		}
680
+		// filter routes and page_config so addons can add their stuff. Filtering done per class
681
+		$this->_page_routes = apply_filters(
682
+			'FHEE__' . $this->class_name . '__page_setup__page_routes',
683
+			$this->_page_routes,
684
+			$this
685
+		);
686
+		$this->_page_config = apply_filters(
687
+			'FHEE__' . $this->class_name . '__page_setup__page_config',
688
+			$this->_page_config,
689
+			$this
690
+		);
691
+		if ($this->base_class_name !== '') {
692
+			$this->_page_routes = apply_filters(
693
+				'FHEE__' . $this->base_class_name . '__page_setup__page_routes',
694
+				$this->_page_routes,
695
+				$this
696
+			);
697
+			$this->_page_config = apply_filters(
698
+				'FHEE__' . $this->base_class_name . '__page_setup__page_config',
699
+				$this->_page_config,
700
+				$this
701
+			);
702
+		}
703
+		// if AHEE__EE_Admin_Page__route_admin_request_$this->_current_view method is present
704
+		// then we call it hooked into the AHEE__EE_Admin_Page__route_admin_request action
705
+		if (method_exists($this, 'AHEE__EE_Admin_Page__route_admin_request_' . $this->_current_view)) {
706
+			add_action(
707
+				'AHEE__EE_Admin_Page__route_admin_request',
708
+				[$this, 'AHEE__EE_Admin_Page__route_admin_request_' . $this->_current_view],
709
+				10,
710
+				2
711
+			);
712
+		}
713
+		// next route only if routing enabled
714
+		if ($this->_routing && ! $this->request->isAjax()) {
715
+			$this->_verify_routes();
716
+			// next let's just check user_access and kill if no access
717
+			$this->check_user_access();
718
+			if ($this->_is_UI_request) {
719
+				// admin_init stuff - global, all views for this page class, specific view
720
+				add_action('admin_init', [$this, 'admin_init'], 10);
721
+				if (method_exists($this, 'admin_init_' . $this->_current_view)) {
722
+					add_action('admin_init', [$this, 'admin_init_' . $this->_current_view], 15);
723
+				}
724
+			} else {
725
+				// hijack regular WP loading and route admin request immediately
726
+				@ini_set('memory_limit', apply_filters('admin_memory_limit', WP_MAX_MEMORY_LIMIT));
727
+				$this->route_admin_request();
728
+			}
729
+		}
730
+	}
731
+
732
+
733
+	/**
734
+	 * Provides a way for related child admin pages to load stuff on the loaded admin page.
735
+	 *
736
+	 * @return void
737
+	 * @throws EE_Error
738
+	 */
739
+	private function _do_other_page_hooks()
740
+	{
741
+		$registered_pages = apply_filters('FHEE_do_other_page_hooks_' . $this->page_slug, []);
742
+		foreach ($registered_pages as $page) {
743
+			// now let's setup the file name and class that should be present
744
+			$classname = str_replace('.class.php', '', $page);
745
+			// autoloaders should take care of loading file
746
+			if (! class_exists($classname)) {
747
+				$error_msg[] = sprintf(
748
+					esc_html__(
749
+						'Something went wrong with loading the %s admin hooks page.',
750
+						'event_espresso'
751
+					),
752
+					$page
753
+				);
754
+				$error_msg[] = $error_msg[0]
755
+							   . "\r\n"
756
+							   . sprintf(
757
+								   esc_html__(
758
+									   'There is no class in place for the %1$s admin hooks page.%2$sMake sure you have %3$s defined. If this is a non-EE-core admin page then you also must have an autoloader in place for your class',
759
+									   'event_espresso'
760
+								   ),
761
+								   $page,
762
+								   '<br />',
763
+								   '<strong>' . $classname . '</strong>'
764
+							   );
765
+				throw new EE_Error(implode('||', $error_msg));
766
+			}
767
+			// notice we are passing the instance of this class to the hook object.
768
+			$this->loader->getShared($classname, [$this]);
769
+		}
770
+	}
771
+
772
+
773
+	/**
774
+	 * @throws ReflectionException
775
+	 * @throws EE_Error
776
+	 */
777
+	public function load_page_dependencies()
778
+	{
779
+		try {
780
+			$this->_load_page_dependencies();
781
+		} catch (EE_Error $e) {
782
+			$e->get_error();
783
+		}
784
+	}
785
+
786
+
787
+	/**
788
+	 * load_page_dependencies
789
+	 * loads things specific to this page class when its loaded.  Really helps with efficiency.
790
+	 *
791
+	 * @return void
792
+	 * @throws DomainException
793
+	 * @throws EE_Error
794
+	 * @throws InvalidArgumentException
795
+	 * @throws InvalidDataTypeException
796
+	 * @throws InvalidInterfaceException
797
+	 */
798
+	protected function _load_page_dependencies()
799
+	{
800
+		// let's set the current_screen and screen options to override what WP set
801
+		$this->_current_screen = get_current_screen();
802
+		// load admin_notices - global, page class, and view specific
803
+		add_action('admin_notices', [$this, 'admin_notices_global'], 5);
804
+		add_action('admin_notices', [$this, 'admin_notices'], 10);
805
+		if (method_exists($this, 'admin_notices_' . $this->_current_view)) {
806
+			add_action('admin_notices', [$this, 'admin_notices_' . $this->_current_view], 15);
807
+		}
808
+		// load network admin_notices - global, page class, and view specific
809
+		add_action('network_admin_notices', [$this, 'network_admin_notices_global'], 5);
810
+		if (method_exists($this, 'network_admin_notices_' . $this->_current_view)) {
811
+			add_action('network_admin_notices', [$this, 'network_admin_notices_' . $this->_current_view]);
812
+		}
813
+		// this will save any per_page screen options if they are present
814
+		$this->_set_per_page_screen_options();
815
+		// setup list table properties
816
+		$this->_set_list_table();
817
+		// child classes can "register" a metabox to be automatically handled via the _page_config array property.
818
+		// However in some cases the metaboxes will need to be added within a route handling callback.
819
+		$this->_add_registered_meta_boxes();
820
+		$this->_add_screen_columns();
821
+		// add screen options - global, page child class, and view specific
822
+		$this->_add_global_screen_options();
823
+		$this->_add_screen_options();
824
+		$add_screen_options = "_add_screen_options_{$this->_current_view}";
825
+		if (method_exists($this, $add_screen_options)) {
826
+			$this->{$add_screen_options}();
827
+		}
828
+		// add help tab(s) - set via page_config and qtips.
829
+		$this->_add_help_tabs();
830
+		$this->_add_qtips();
831
+		// add feature_pointers - global, page child class, and view specific
832
+		$this->_add_feature_pointers();
833
+		$this->_add_global_feature_pointers();
834
+		$add_feature_pointer = "_add_feature_pointer_{$this->_current_view}";
835
+		if (method_exists($this, $add_feature_pointer)) {
836
+			$this->{$add_feature_pointer}();
837
+		}
838
+		// enqueue scripts/styles - global, page class, and view specific
839
+		add_action('admin_enqueue_scripts', [$this, 'load_global_scripts_styles'], 5);
840
+		add_action('admin_enqueue_scripts', [$this, 'load_scripts_styles'], 10);
841
+		if (method_exists($this, "load_scripts_styles_{$this->_current_view}")) {
842
+			add_action('admin_enqueue_scripts', [$this, "load_scripts_styles_{$this->_current_view}"], 15);
843
+		}
844
+		add_action('admin_enqueue_scripts', [$this, 'admin_footer_scripts_eei18n_js_strings'], 100);
845
+		// admin_print_footer_scripts - global, page child class, and view specific.
846
+		// NOTE, despite the name, whenever possible, scripts should NOT be loaded using this.
847
+		// In most cases that's doing_it_wrong().  But adding hidden container elements etc.
848
+		// is a good use case. Notice the late priority we're giving these
849
+		add_action('admin_print_footer_scripts', [$this, 'admin_footer_scripts_global'], 99);
850
+		add_action('admin_print_footer_scripts', [$this, 'admin_footer_scripts'], 100);
851
+		if (method_exists($this, "admin_footer_scripts_{$this->_current_view}")) {
852
+			add_action('admin_print_footer_scripts', [$this, "admin_footer_scripts_{$this->_current_view}"], 101);
853
+		}
854
+		// admin footer scripts
855
+		add_action('admin_footer', [$this, 'admin_footer_global'], 99);
856
+		add_action('admin_footer', [$this, 'admin_footer'], 100);
857
+		if (method_exists($this, "admin_footer_{$this->_current_view}")) {
858
+			add_action('admin_footer', [$this, "admin_footer_{$this->_current_view}"], 101);
859
+		}
860
+		do_action('FHEE__EE_Admin_Page___load_page_dependencies__after_load', $this->page_slug);
861
+		// targeted hook
862
+		do_action(
863
+			"FHEE__EE_Admin_Page___load_page_dependencies__after_load__{$this->page_slug}__{$this->_req_action}"
864
+		);
865
+	}
866
+
867
+
868
+	/**
869
+	 * _set_defaults
870
+	 * This sets some global defaults for class properties.
871
+	 */
872
+	private function _set_defaults()
873
+	{
874
+		$this->_current_screen       = $this->_admin_page_title = $this->_req_action = $this->_req_nonce = null;
875
+		$this->_event                = $this->_template_path = $this->_column_template_path = null;
876
+		$this->_nav_tabs             = $this->_views = $this->_page_routes = [];
877
+		$this->_page_config          = $this->_default_route_query_args = [];
878
+		$this->_default_nav_tab_name = 'overview';
879
+		// init template args
880
+		$this->_template_args = [
881
+			'admin_page_header'  => '',
882
+			'admin_page_content' => '',
883
+			'post_body_content'  => '',
884
+			'before_list_table'  => '',
885
+			'after_list_table'   => '',
886
+		];
887
+	}
888
+
889
+
890
+	/**
891
+	 * route_admin_request
892
+	 *
893
+	 * @return void
894
+	 * @throws InvalidArgumentException
895
+	 * @throws InvalidInterfaceException
896
+	 * @throws InvalidDataTypeException
897
+	 * @throws EE_Error
898
+	 * @throws ReflectionException
899
+	 * @see    _route_admin_request()
900
+	 */
901
+	public function route_admin_request()
902
+	{
903
+		try {
904
+			$this->_route_admin_request();
905
+		} catch (EE_Error $e) {
906
+			$e->get_error();
907
+		}
908
+	}
909
+
910
+
911
+	public function set_wp_page_slug($wp_page_slug)
912
+	{
913
+		$this->_wp_page_slug = $wp_page_slug;
914
+		// if in network admin then we need to append "-network" to the page slug. Why? Because that's how WP rolls...
915
+		if (is_network_admin()) {
916
+			$this->_wp_page_slug .= '-network';
917
+		}
918
+	}
919
+
920
+
921
+	/**
922
+	 * _verify_routes
923
+	 * All this method does is verify the incoming request and make sure that routes exist for it.  We do this early so
924
+	 * we know if we need to drop out.
925
+	 *
926
+	 * @return bool
927
+	 * @throws EE_Error
928
+	 */
929
+	protected function _verify_routes()
930
+	{
931
+		do_action('AHEE_log', __FILE__, __FUNCTION__, '');
932
+		if (! $this->_current_page && ! $this->request->isAjax()) {
933
+			return false;
934
+		}
935
+		$this->_route = false;
936
+		// check that the page_routes array is not empty
937
+		if (empty($this->_page_routes)) {
938
+			// user error msg
939
+			$error_msg = sprintf(
940
+				esc_html__('No page routes have been set for the %s admin page.', 'event_espresso'),
941
+				$this->_admin_page_title
942
+			);
943
+			// developer error msg
944
+			$error_msg .= '||' . $error_msg
945
+						  . esc_html__(
946
+							  ' Make sure the "set_page_routes()" method exists, and is setting the "_page_routes" array properly.',
947
+							  'event_espresso'
948
+						  );
949
+			throw new EE_Error($error_msg);
950
+		}
951
+		// and that the requested page route exists
952
+		if (array_key_exists($this->_req_action, $this->_page_routes)) {
953
+			$this->_route        = $this->_page_routes[ $this->_req_action ];
954
+			$this->_route_config = $this->_page_config[ $this->_req_action ] ?? [];
955
+		} else {
956
+			// user error msg
957
+			$error_msg = sprintf(
958
+				esc_html__(
959
+					'The requested page route does not exist for the %s admin page.',
960
+					'event_espresso'
961
+				),
962
+				$this->_admin_page_title
963
+			);
964
+			// developer error msg
965
+			$error_msg .= '||' . $error_msg
966
+						  . sprintf(
967
+							  esc_html__(
968
+								  ' Create a key in the "_page_routes" array named "%s" and set its value to the appropriate method.',
969
+								  'event_espresso'
970
+							  ),
971
+							  $this->_req_action
972
+						  );
973
+			throw new EE_Error($error_msg);
974
+		}
975
+		// and that a default route exists
976
+		if (! array_key_exists('default', $this->_page_routes)) {
977
+			// user error msg
978
+			$error_msg = sprintf(
979
+				esc_html__(
980
+					'A default page route has not been set for the % admin page.',
981
+					'event_espresso'
982
+				),
983
+				$this->_admin_page_title
984
+			);
985
+			// developer error msg
986
+			$error_msg .= '||' . $error_msg
987
+						  . esc_html__(
988
+							  ' Create a key in the "_page_routes" array named "default" and set its value to your default page method.',
989
+							  'event_espresso'
990
+						  );
991
+			throw new EE_Error($error_msg);
992
+		}
993
+
994
+		// first lets' catch if the UI request has EVER been set.
995
+		if ($this->_is_UI_request === null) {
996
+			// lets set if this is a UI request or not.
997
+			$this->_is_UI_request = ! $this->request->getRequestParam('noheader', false, 'bool');
998
+			// wait a minute... we might have a noheader in the route array
999
+			$this->_is_UI_request = ! (
1000
+				is_array($this->_route) && isset($this->_route['noheader']) && $this->_route['noheader']
1001
+			)
1002
+				? $this->_is_UI_request
1003
+				: false;
1004
+		}
1005
+		$this->_set_current_labels();
1006
+		return true;
1007
+	}
1008
+
1009
+
1010
+	/**
1011
+	 * this method simply verifies a given route and makes sure its an actual route available for the loaded page
1012
+	 *
1013
+	 * @param string $route the route name we're verifying
1014
+	 * @return bool we'll throw an exception if this isn't a valid route.
1015
+	 * @throws EE_Error
1016
+	 */
1017
+	protected function _verify_route($route)
1018
+	{
1019
+		if (array_key_exists($this->_req_action, $this->_page_routes)) {
1020
+			return true;
1021
+		}
1022
+		// user error msg
1023
+		$error_msg = sprintf(
1024
+			esc_html__('The given page route does not exist for the %s admin page.', 'event_espresso'),
1025
+			$this->_admin_page_title
1026
+		);
1027
+		// developer error msg
1028
+		$error_msg .= '||' . $error_msg
1029
+					  . sprintf(
1030
+						  esc_html__(
1031
+							  ' Check the route you are using in your method (%s) and make sure it matches a route set in your "_page_routes" array property',
1032
+							  'event_espresso'
1033
+						  ),
1034
+						  $route
1035
+					  );
1036
+		throw new EE_Error($error_msg);
1037
+	}
1038
+
1039
+
1040
+	/**
1041
+	 * perform nonce verification
1042
+	 * This method has be encapsulated here so that any ajax requests that bypass normal routes can verify their nonces
1043
+	 * using this method (and save retyping!)
1044
+	 *
1045
+	 * @param string $nonce     The nonce sent
1046
+	 * @param string $nonce_ref The nonce reference string (name0)
1047
+	 * @return void
1048
+	 * @throws EE_Error
1049
+	 * @throws InvalidArgumentException
1050
+	 * @throws InvalidDataTypeException
1051
+	 * @throws InvalidInterfaceException
1052
+	 */
1053
+	protected function _verify_nonce($nonce, $nonce_ref)
1054
+	{
1055
+		// verify nonce against expected value
1056
+		if (! wp_verify_nonce($nonce, $nonce_ref)) {
1057
+			// these are not the droids you are looking for !!!
1058
+			$msg = sprintf(
1059
+				esc_html__('%sNonce Fail.%s', 'event_espresso'),
1060
+				'<a href="https://www.youtube.com/watch?v=56_S0WeTkzs">',
1061
+				'</a>'
1062
+			);
1063
+			if (WP_DEBUG) {
1064
+				$msg .= "\n  ";
1065
+				$msg .= sprintf(
1066
+					esc_html__(
1067
+						'In order to dynamically generate nonces for your actions, use the %s::add_query_args_and_nonce() method. May the Nonce be with you!',
1068
+						'event_espresso'
1069
+					),
1070
+					__CLASS__
1071
+				);
1072
+			}
1073
+			if (! $this->request->isAjax()) {
1074
+				wp_die($msg);
1075
+			}
1076
+			EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
1077
+			$this->_return_json();
1078
+		}
1079
+	}
1080
+
1081
+
1082
+	/**
1083
+	 * _route_admin_request()
1084
+	 * Meat and potatoes of the class.  Basically, this dude checks out what's being requested and sees if there are
1085
+	 * some doodads to work the magic and handle the flingjangy. Translation:  Checks if the requested action is listed
1086
+	 * in the page routes and then will try to load the corresponding method.
1087
+	 *
1088
+	 * @return void
1089
+	 * @throws EE_Error
1090
+	 * @throws InvalidArgumentException
1091
+	 * @throws InvalidDataTypeException
1092
+	 * @throws InvalidInterfaceException
1093
+	 * @throws ReflectionException
1094
+	 */
1095
+	protected function _route_admin_request()
1096
+	{
1097
+		if (! $this->_is_UI_request) {
1098
+			$this->_verify_routes();
1099
+		}
1100
+		$nonce_check = ! isset($this->_route_config['require_nonce']) || $this->_route_config['require_nonce'];
1101
+		if ($this->_req_action !== 'default' && $nonce_check) {
1102
+			// set nonce from post data
1103
+			$nonce = $this->request->getRequestParam($this->_req_nonce, '');
1104
+			$this->_verify_nonce($nonce, $this->_req_nonce);
1105
+		}
1106
+		// set the nav_tabs array but ONLY if this is  UI_request
1107
+		if ($this->_is_UI_request) {
1108
+			$this->_set_nav_tabs();
1109
+		}
1110
+		// grab callback function
1111
+		$func = is_array($this->_route) && isset($this->_route['func']) ? $this->_route['func'] : $this->_route;
1112
+		// check if callback has args
1113
+		$args      = is_array($this->_route) && isset($this->_route['args']) ? $this->_route['args'] : [];
1114
+		$error_msg = '';
1115
+		// action right before calling route
1116
+		// (hook is something like 'AHEE__Registrations_Admin_Page__route_admin_request')
1117
+		if (! did_action('AHEE__EE_Admin_Page__route_admin_request')) {
1118
+			do_action('AHEE__EE_Admin_Page__route_admin_request', $this->_current_view, $this);
1119
+		}
1120
+		// strip _wp_http_referer from the server REQUEST_URI
1121
+		// else it grows in length on every submission due to recursion,
1122
+		// ultimately causing a "Request-URI Too Large" error
1123
+		$this->request->unSetRequestParam('_wp_http_referer');
1124
+		$this->request->unSetServerParam('_wp_http_referer');
1125
+		$cleaner_request_uri = remove_query_arg(
1126
+			'_wp_http_referer',
1127
+			wp_unslash($this->request->getServerParam('REQUEST_URI'))
1128
+		);
1129
+		$this->request->setRequestParam('_wp_http_referer', $cleaner_request_uri, true);
1130
+		$this->request->setServerParam('REQUEST_URI', $cleaner_request_uri, true);
1131
+		if (! empty($func)) {
1132
+			if (is_array($func)) {
1133
+				[$class, $method] = $func;
1134
+			} elseif (strpos($func, '::') !== false) {
1135
+				[$class, $method] = explode('::', $func);
1136
+			} else {
1137
+				$class  = $this;
1138
+				$method = $func;
1139
+			}
1140
+			if (! (is_object($class) && $class === $this)) {
1141
+				// send along this admin page object for access by addons.
1142
+				$args['admin_page_object'] = $this;
1143
+			}
1144
+			if (
1145
+				// is it a method on a class that doesn't work?
1146
+				(
1147
+					(
1148
+						method_exists($class, $method)
1149
+						&& call_user_func_array([$class, $method], $args) === false
1150
+					)
1151
+					&& (
1152
+						// is it a standalone function that doesn't work?
1153
+						function_exists($method)
1154
+						&& call_user_func_array(
1155
+							$func,
1156
+							array_merge(['admin_page_object' => $this], $args)
1157
+						) === false
1158
+					)
1159
+				)
1160
+				|| (
1161
+					// is it neither a class method NOR a standalone function?
1162
+					! method_exists($class, $method)
1163
+					&& ! function_exists($method)
1164
+				)
1165
+			) {
1166
+				// user error msg
1167
+				$error_msg = esc_html__(
1168
+					'An error occurred. The  requested page route could not be found.',
1169
+					'event_espresso'
1170
+				);
1171
+				// developer error msg
1172
+				$error_msg .= '||';
1173
+				$error_msg .= sprintf(
1174
+					esc_html__(
1175
+						'Page route "%s" could not be called. Check that the spelling for method names and actions in the "_page_routes" array are all correct.',
1176
+						'event_espresso'
1177
+					),
1178
+					$method
1179
+				);
1180
+			}
1181
+			if (! empty($error_msg)) {
1182
+				throw new EE_Error($error_msg);
1183
+			}
1184
+		}
1185
+		// if we've routed and this route has a no headers route AND a sent_headers_route,
1186
+		// then we need to reset the routing properties to the new route.
1187
+		// now if UI request is FALSE and noheader is true AND we have a headers_sent_route in the route array then let's set UI_request to true because the no header route has a second func after headers have been sent.
1188
+		if (
1189
+			$this->_is_UI_request === false
1190
+			&& is_array($this->_route)
1191
+			&& ! empty($this->_route['headers_sent_route'])
1192
+		) {
1193
+			$this->_reset_routing_properties($this->_route['headers_sent_route']);
1194
+		}
1195
+	}
1196
+
1197
+
1198
+	/**
1199
+	 * This method just allows the resetting of page properties in the case where a no headers
1200
+	 * route redirects to a headers route in its route config.
1201
+	 *
1202
+	 * @param string $new_route New (non header) route to redirect to.
1203
+	 * @return   void
1204
+	 * @throws ReflectionException
1205
+	 * @throws InvalidArgumentException
1206
+	 * @throws InvalidInterfaceException
1207
+	 * @throws InvalidDataTypeException
1208
+	 * @throws EE_Error
1209
+	 * @since   4.3.0
1210
+	 */
1211
+	protected function _reset_routing_properties($new_route)
1212
+	{
1213
+		$this->_is_UI_request = true;
1214
+		// now we set the current route to whatever the headers_sent_route is set at
1215
+		$this->request->setRequestParam('action', $new_route);
1216
+		// rerun page setup
1217
+		$this->_page_setup();
1218
+	}
1219
+
1220
+
1221
+	/**
1222
+	 * _add_query_arg
1223
+	 * adds nonce to array of arguments then calls WP add_query_arg function
1224
+	 *(internally just uses EEH_URL's function with the same name)
1225
+	 *
1226
+	 * @param array  $args
1227
+	 * @param string $url
1228
+	 * @param bool   $sticky                  if true, then the existing Request params will be appended to the
1229
+	 *                                        generated url in an associative array indexed by the key 'wp_referer';
1230
+	 *                                        Example usage: If the current page is:
1231
+	 *                                        http://mydomain.com/wp-admin/admin.php?page=espresso_registrations
1232
+	 *                                        &action=default&event_id=20&month_range=March%202015
1233
+	 *                                        &_wpnonce=5467821
1234
+	 *                                        and you call:
1235
+	 *                                        EE_Admin_Page::add_query_args_and_nonce(
1236
+	 *                                        array(
1237
+	 *                                        'action' => 'resend_something',
1238
+	 *                                        'page=>espresso_registrations'
1239
+	 *                                        ),
1240
+	 *                                        $some_url,
1241
+	 *                                        true
1242
+	 *                                        );
1243
+	 *                                        It will produce a url in this structure:
1244
+	 *                                        http://{$some_url}/?page=espresso_registrations&action=resend_something
1245
+	 *                                        &wp_referer[action]=default&wp_referer[event_id]=20&wpreferer[
1246
+	 *                                        month_range]=March%202015
1247
+	 * @param bool   $exclude_nonce           If true, the the nonce will be excluded from the generated nonce.
1248
+	 * @return string
1249
+	 */
1250
+	public static function add_query_args_and_nonce(
1251
+		$args = [],
1252
+		$url = '',
1253
+		$sticky = false,
1254
+		$exclude_nonce = false
1255
+	) {
1256
+		// if there is a _wp_http_referer include the values from the request but only if sticky = true
1257
+		if ($sticky) {
1258
+			/** @var RequestInterface $request */
1259
+			$request = LoaderFactory::getLoader()->getShared(RequestInterface::class);
1260
+			$request->unSetRequestParams(['_wp_http_referer', 'wp_referer'], true);
1261
+			$request->unSetServerParam('_wp_http_referer', true);
1262
+			foreach ($request->requestParams() as $key => $value) {
1263
+				// do not add nonces
1264
+				if (strpos($key, 'nonce') !== false) {
1265
+					continue;
1266
+				}
1267
+				$args[ 'wp_referer[' . $key . ']' ] = is_string($value) ? htmlspecialchars($value) : $value;
1268
+			}
1269
+		}
1270
+		return EEH_URL::add_query_args_and_nonce($args, $url, $exclude_nonce);
1271
+	}
1272
+
1273
+
1274
+	/**
1275
+	 * This returns a generated link that will load the related help tab.
1276
+	 *
1277
+	 * @param string $help_tab_id the id for the connected help tab
1278
+	 * @param string $icon_style  (optional) include css class for the style you want to use for the help icon.
1279
+	 * @param string $help_text   (optional) send help text you want to use for the link if default not to be used
1280
+	 * @return string              generated link
1281
+	 * @uses EEH_Template::get_help_tab_link()
1282
+	 */
1283
+	protected function _get_help_tab_link($help_tab_id, $icon_style = '', $help_text = '')
1284
+	{
1285
+		return EEH_Template::get_help_tab_link(
1286
+			$help_tab_id,
1287
+			$this->page_slug,
1288
+			$this->_req_action,
1289
+			$icon_style,
1290
+			$help_text
1291
+		);
1292
+	}
1293
+
1294
+
1295
+	/**
1296
+	 * _add_help_tabs
1297
+	 * Note child classes define their help tabs within the page_config array.
1298
+	 *
1299
+	 * @link   http://codex.wordpress.org/Function_Reference/add_help_tab
1300
+	 * @return void
1301
+	 * @throws DomainException
1302
+	 * @throws EE_Error
1303
+	 * @throws ReflectionException
1304
+	 */
1305
+	protected function _add_help_tabs()
1306
+	{
1307
+		if (isset($this->_page_config[ $this->_req_action ])) {
1308
+			$config = $this->_page_config[ $this->_req_action ];
1309
+			// let's see if there is a help_sidebar set for the current route and we'll set that up for usage as well.
1310
+			if (is_array($config) && isset($config['help_sidebar'])) {
1311
+				// check that the callback given is valid
1312
+				if (! method_exists($this, $config['help_sidebar'])) {
1313
+					throw new EE_Error(
1314
+						sprintf(
1315
+							esc_html__(
1316
+								'The _page_config array has a callback set for the "help_sidebar" option.  However the callback given (%s) is not a valid callback.  Doublecheck the spelling and make sure this method exists for the class %s',
1317
+								'event_espresso'
1318
+							),
1319
+							$config['help_sidebar'],
1320
+							$this->class_name
1321
+						)
1322
+					);
1323
+				}
1324
+				$content = apply_filters(
1325
+					'FHEE__' . $this->class_name . '__add_help_tabs__help_sidebar',
1326
+					$this->{$config['help_sidebar']}()
1327
+				);
1328
+				$this->_current_screen->set_help_sidebar($content);
1329
+			}
1330
+			if (! isset($config['help_tabs'])) {
1331
+				return;
1332
+			} //no help tabs for this route
1333
+			foreach ((array) $config['help_tabs'] as $tab_id => $cfg) {
1334
+				// we're here so there ARE help tabs!
1335
+				// make sure we've got what we need
1336
+				if (! isset($cfg['title'])) {
1337
+					throw new EE_Error(
1338
+						esc_html__(
1339
+							'The _page_config array is not set up properly for help tabs.  It is missing a title',
1340
+							'event_espresso'
1341
+						)
1342
+					);
1343
+				}
1344
+				if (! isset($cfg['filename']) && ! isset($cfg['callback']) && ! isset($cfg['content'])) {
1345
+					throw new EE_Error(
1346
+						esc_html__(
1347
+							'The _page_config array is not setup properly for help tabs. It is missing a either a filename reference, or a callback reference or a content reference so there is no way to know the content for the help tab',
1348
+							'event_espresso'
1349
+						)
1350
+					);
1351
+				}
1352
+				// first priority goes to content.
1353
+				if (! empty($cfg['content'])) {
1354
+					$content = ! empty($cfg['content']) ? $cfg['content'] : null;
1355
+					// second priority goes to filename
1356
+				} elseif (! empty($cfg['filename'])) {
1357
+					$file_path = $this->_get_dir() . '/help_tabs/' . $cfg['filename'] . '.help_tab.php';
1358
+					// it's possible that the file is located on decaf route (and above sets up for caf route, if this is the case then lets check decaf route too)
1359
+					$file_path = ! is_readable($file_path) ? EE_ADMIN_PAGES
1360
+															 . basename($this->_get_dir())
1361
+															 . '/help_tabs/'
1362
+															 . $cfg['filename']
1363
+															 . '.help_tab.php' : $file_path;
1364
+					// if file is STILL not readable then let's do a EE_Error so its more graceful than a fatal error.
1365
+					if (! isset($cfg['callback']) && ! is_readable($file_path)) {
1366
+						EE_Error::add_error(
1367
+							sprintf(
1368
+								esc_html__(
1369
+									'The filename given for the help tab %s is not a valid file and there is no other configuration for the tab content.  Please check that the string you set for the help tab on this route (%s) is the correct spelling.  The file should be in %s',
1370
+									'event_espresso'
1371
+								),
1372
+								$tab_id,
1373
+								key($config),
1374
+								$file_path
1375
+							),
1376
+							__FILE__,
1377
+							__FUNCTION__,
1378
+							__LINE__
1379
+						);
1380
+						return;
1381
+					}
1382
+					$template_args['admin_page_obj'] = $this;
1383
+					$content                         = EEH_Template::display_template(
1384
+						$file_path,
1385
+						$template_args,
1386
+						true
1387
+					);
1388
+				} else {
1389
+					$content = '';
1390
+				}
1391
+				// check if callback is valid
1392
+				if (
1393
+					empty($content)
1394
+					&& (
1395
+						! isset($cfg['callback']) || ! method_exists($this, $cfg['callback'])
1396
+					)
1397
+				) {
1398
+					EE_Error::add_error(
1399
+						sprintf(
1400
+							esc_html__(
1401
+								'The callback given for a %s help tab on this page does not content OR a corresponding method for generating the content.  Check the spelling or make sure the method is present.',
1402
+								'event_espresso'
1403
+							),
1404
+							$cfg['title']
1405
+						),
1406
+						__FILE__,
1407
+						__FUNCTION__,
1408
+						__LINE__
1409
+					);
1410
+					return;
1411
+				}
1412
+				// setup config array for help tab method
1413
+				$id  = $this->page_slug . '-' . $this->_req_action . '-' . $tab_id;
1414
+				$_ht = [
1415
+					'id'       => $id,
1416
+					'title'    => $cfg['title'],
1417
+					'callback' => isset($cfg['callback']) && empty($content) ? [$this, $cfg['callback']] : null,
1418
+					'content'  => $content,
1419
+				];
1420
+				$this->_current_screen->add_help_tab($_ht);
1421
+			}
1422
+		}
1423
+	}
1424
+
1425
+
1426
+	/**
1427
+	 * This simply sets up any qtips that have been defined in the page config
1428
+	 *
1429
+	 * @return void
1430
+	 * @throws ReflectionException
1431
+	 * @throws EE_Error
1432
+	 */
1433
+	protected function _add_qtips()
1434
+	{
1435
+		if (isset($this->_route_config['qtips'])) {
1436
+			$qtips = (array) $this->_route_config['qtips'];
1437
+			// load qtip loader
1438
+			$path = [
1439
+				$this->_get_dir() . '/qtips/',
1440
+				EE_ADMIN_PAGES . basename($this->_get_dir()) . '/qtips/',
1441
+			];
1442
+			EEH_Qtip_Loader::instance()->register($qtips, $path);
1443
+		}
1444
+	}
1445
+
1446
+
1447
+	/**
1448
+	 * _set_nav_tabs
1449
+	 * This sets up the nav tabs from the page_routes array.  This method can be overwritten by child classes if you
1450
+	 * wish to add additional tabs or modify accordingly.
1451
+	 *
1452
+	 * @return void
1453
+	 * @throws InvalidArgumentException
1454
+	 * @throws InvalidInterfaceException
1455
+	 * @throws InvalidDataTypeException
1456
+	 */
1457
+	protected function _set_nav_tabs()
1458
+	{
1459
+		$i = 0;
1460
+		$only_tab = count($this->_page_config) < 2;
1461
+		foreach ($this->_page_config as $slug => $config) {
1462
+			if (! is_array($config) || empty($config['nav'])) {
1463
+				continue;
1464
+			}
1465
+			// no nav tab for this config
1466
+			// check for persistent flag
1467
+			if ($slug !== $this->_req_action && isset($config['nav']['persistent']) && ! $config['nav']['persistent']) {
1468
+				// nav tab is only to appear when route requested.
1469
+				continue;
1470
+			}
1471
+			if (! $this->check_user_access($slug, true)) {
1472
+				// no nav tab because current user does not have access.
1473
+				continue;
1474
+			}
1475
+			$css_class = isset($config['css_class']) ? $config['css_class'] . ' ' : '';
1476
+			$css_class .= $only_tab ? ' ee-only-tab' : '';
1477
+
1478
+			$this->_nav_tabs[ $slug ] = [
1479
+				'url'       => $config['nav']['url'] ?? EE_Admin_Page::add_query_args_and_nonce(
1480
+					['action' => $slug],
1481
+					$this->_admin_base_url
1482
+				),
1483
+				'link_text' => $this->navTabLabel($config['nav'], $slug),
1484
+				'css_class' => $this->_req_action === $slug ? $css_class . ' nav-tab-active' : $css_class,
1485
+				'order'     => $config['nav']['order'] ?? $i,
1486
+			];
1487
+			$i++;
1488
+		}
1489
+		// if $this->_nav_tabs is empty then lets set the default
1490
+		if (empty($this->_nav_tabs)) {
1491
+			$this->_nav_tabs[ $this->_default_nav_tab_name ] = [
1492
+				'url'       => $this->_admin_base_url,
1493
+				'link_text' => ucwords(str_replace('_', ' ', $this->_default_nav_tab_name)),
1494
+				'css_class' => 'nav-tab-active',
1495
+				'order'     => 10,
1496
+			];
1497
+		}
1498
+		// now let's sort the tabs according to order
1499
+		usort($this->_nav_tabs, [$this, '_sort_nav_tabs']);
1500
+	}
1501
+
1502
+
1503
+	private function navTabLabel(array $nav_tab, string $slug): string
1504
+	{
1505
+		$label = $nav_tab['label'] ?? ucwords(str_replace('_', ' ', $slug));
1506
+		$icon = $nav_tab['icon'] ?? null;
1507
+		$icon = $icon ? '<span class="dashicons ' . $icon . '"></span>' : '';
1508
+		return '
1509 1509
             <span class="ee-admin-screen-tab__label">
1510 1510
                 ' . $icon . '
1511 1511
                 <span class="ee-nav-label__text">' . $label . '</span>
1512 1512
             </span>';
1513
-    }
1514
-
1515
-    /**
1516
-     * _set_current_labels
1517
-     * This method modifies the _labels property with any optional specific labels indicated in the _page_routes
1518
-     * property array
1519
-     *
1520
-     * @return void
1521
-     */
1522
-    private function _set_current_labels()
1523
-    {
1524
-        if (is_array($this->_route_config) && isset($this->_route_config['labels'])) {
1525
-            foreach ($this->_route_config['labels'] as $label => $text) {
1526
-                if (is_array($text)) {
1527
-                    foreach ($text as $sublabel => $subtext) {
1528
-                        $this->_labels[ $label ][ $sublabel ] = $subtext;
1529
-                    }
1530
-                } else {
1531
-                    $this->_labels[ $label ] = $text;
1532
-                }
1533
-            }
1534
-        }
1535
-    }
1536
-
1537
-
1538
-    /**
1539
-     *        verifies user access for this admin page
1540
-     *
1541
-     * @param string $route_to_check if present then the capability for the route matching this string is checked.
1542
-     * @param bool   $verify_only    Default is FALSE which means if user check fails then wp_die().  Otherwise just
1543
-     *                               return false if verify fail.
1544
-     * @return bool
1545
-     * @throws InvalidArgumentException
1546
-     * @throws InvalidDataTypeException
1547
-     * @throws InvalidInterfaceException
1548
-     */
1549
-    public function check_user_access($route_to_check = '', $verify_only = false)
1550
-    {
1551
-        do_action('AHEE_log', __FILE__, __FUNCTION__, '');
1552
-        $route_to_check = empty($route_to_check) ? $this->_req_action : $route_to_check;
1553
-        $capability = ! empty($route_to_check) && isset($this->_page_routes[ $route_to_check ])
1554
-                                      && is_array($this->_page_routes[ $route_to_check ])
1555
-                        && ! empty($this->_page_routes[ $route_to_check ]['capability'])
1556
-            ? $this->_page_routes[ $route_to_check ]['capability']
1557
-            : null;
1558
-
1559
-        if (empty($capability) && empty($route_to_check)) {
1560
-            $capability = is_array($this->_route) && empty($this->_route['capability']) ? 'manage_options'
1561
-                : $this->_route['capability'];
1562
-        } else {
1563
-            $capability = empty($capability) ? 'manage_options' : $capability;
1564
-        }
1565
-        $id = is_array($this->_route) && ! empty($this->_route['obj_id']) ? $this->_route['obj_id'] : 0;
1566
-        if (
1567
-            ! $this->request->isAjax()
1568
-            && (
1569
-                ! function_exists('is_admin')
1570
-                || ! EE_Registry::instance()->CAP->current_user_can(
1571
-                    $capability,
1572
-                    $this->page_slug
1573
-                    . '_'
1574
-                    . $route_to_check,
1575
-                    $id
1576
-                )
1577
-            )
1578
-        ) {
1579
-            if ($verify_only) {
1580
-                return false;
1581
-            }
1582
-            if (is_user_logged_in()) {
1583
-                wp_die(esc_html__('You do not have access to this route.', 'event_espresso'));
1584
-            } else {
1585
-                return false;
1586
-            }
1587
-        }
1588
-        return true;
1589
-    }
1590
-
1591
-
1592
-    /**
1593
-     * @param string                 $box_id
1594
-     * @param string                 $title
1595
-     * @param callable|string|null   $callback
1596
-     * @param string|array|WP_Screen $screen
1597
-     * @param string                 $context
1598
-     * @param string                 $priority
1599
-     * @param array|null             $callback_args
1600
-     */
1601
-    protected function addMetaBox(
1602
-        string $box_id,
1603
-        string $title,
1604
-        $callback,
1605
-        $screen,
1606
-        string $context = 'normal',
1607
-        string $priority = 'default',
1608
-        ?array $callback_args = null
1609
-    ) {
1610
-        if (! is_callable($callback)) {
1611
-            return;
1612
-        }
1613
-
1614
-        add_meta_box($box_id, $title, $callback, $screen, $context, $priority, $callback_args);
1615
-        add_filter(
1616
-            "postbox_classes_{$this->_wp_page_slug}_{$box_id}",
1617
-            function ($classes) {
1618
-                array_push($classes, 'ee-admin-container');
1619
-                return $classes;
1620
-            }
1621
-        );
1622
-    }
1623
-
1624
-
1625
-    /**
1626
-     * admin_init_global
1627
-     * This runs all the code that we want executed within the WP admin_init hook.
1628
-     * This method executes for ALL EE Admin pages.
1629
-     *
1630
-     * @return void
1631
-     */
1632
-    public function admin_init_global()
1633
-    {
1634
-    }
1635
-
1636
-
1637
-    /**
1638
-     * wp_loaded_global
1639
-     * This runs all the code that we want executed within the WP wp_loaded hook.  This method is optional for an
1640
-     * EE_Admin page and will execute on every EE Admin Page load
1641
-     *
1642
-     * @return void
1643
-     */
1644
-    public function wp_loaded()
1645
-    {
1646
-    }
1647
-
1648
-
1649
-    /**
1650
-     * admin_notices
1651
-     * Anything triggered by the 'admin_notices' WP hook should be put in here.  This particular method will apply on
1652
-     * ALL EE_Admin pages.
1653
-     *
1654
-     * @return void
1655
-     */
1656
-    public function admin_notices_global()
1657
-    {
1658
-        $this->_display_no_javascript_warning();
1659
-        $this->_display_espresso_notices();
1660
-    }
1661
-
1662
-
1663
-    public function network_admin_notices_global()
1664
-    {
1665
-        $this->_display_no_javascript_warning();
1666
-        $this->_display_espresso_notices();
1667
-    }
1668
-
1669
-
1670
-    /**
1671
-     * admin_footer_scripts_global
1672
-     * Anything triggered by the 'admin_print_footer_scripts' WP hook should be put in here. This particular method
1673
-     * will apply on ALL EE_Admin pages.
1674
-     *
1675
-     * @return void
1676
-     */
1677
-    public function admin_footer_scripts_global()
1678
-    {
1679
-        $this->_add_admin_page_ajax_loading_img();
1680
-        $this->_add_admin_page_overlay();
1681
-        // if metaboxes are present we need to add the nonce field
1682
-        if (
1683
-            isset($this->_route_config['metaboxes'])
1684
-            || isset($this->_route_config['list_table'])
1685
-            || (isset($this->_route_config['has_metaboxes']) && $this->_route_config['has_metaboxes'])
1686
-        ) {
1687
-            wp_nonce_field('closedpostboxes', 'closedpostboxesnonce', false);
1688
-            wp_nonce_field('meta-box-order', 'meta-box-order-nonce', false);
1689
-        }
1690
-    }
1691
-
1692
-
1693
-    /**
1694
-     * admin_footer_global
1695
-     * Anything triggered by the wp 'admin_footer' wp hook should be put in here.
1696
-     * This particular method will apply on ALL EE_Admin Pages.
1697
-     *
1698
-     * @return void
1699
-     */
1700
-    public function admin_footer_global()
1701
-    {
1702
-        // dialog container for dialog helper
1703
-        echo '
1513
+	}
1514
+
1515
+	/**
1516
+	 * _set_current_labels
1517
+	 * This method modifies the _labels property with any optional specific labels indicated in the _page_routes
1518
+	 * property array
1519
+	 *
1520
+	 * @return void
1521
+	 */
1522
+	private function _set_current_labels()
1523
+	{
1524
+		if (is_array($this->_route_config) && isset($this->_route_config['labels'])) {
1525
+			foreach ($this->_route_config['labels'] as $label => $text) {
1526
+				if (is_array($text)) {
1527
+					foreach ($text as $sublabel => $subtext) {
1528
+						$this->_labels[ $label ][ $sublabel ] = $subtext;
1529
+					}
1530
+				} else {
1531
+					$this->_labels[ $label ] = $text;
1532
+				}
1533
+			}
1534
+		}
1535
+	}
1536
+
1537
+
1538
+	/**
1539
+	 *        verifies user access for this admin page
1540
+	 *
1541
+	 * @param string $route_to_check if present then the capability for the route matching this string is checked.
1542
+	 * @param bool   $verify_only    Default is FALSE which means if user check fails then wp_die().  Otherwise just
1543
+	 *                               return false if verify fail.
1544
+	 * @return bool
1545
+	 * @throws InvalidArgumentException
1546
+	 * @throws InvalidDataTypeException
1547
+	 * @throws InvalidInterfaceException
1548
+	 */
1549
+	public function check_user_access($route_to_check = '', $verify_only = false)
1550
+	{
1551
+		do_action('AHEE_log', __FILE__, __FUNCTION__, '');
1552
+		$route_to_check = empty($route_to_check) ? $this->_req_action : $route_to_check;
1553
+		$capability = ! empty($route_to_check) && isset($this->_page_routes[ $route_to_check ])
1554
+									  && is_array($this->_page_routes[ $route_to_check ])
1555
+						&& ! empty($this->_page_routes[ $route_to_check ]['capability'])
1556
+			? $this->_page_routes[ $route_to_check ]['capability']
1557
+			: null;
1558
+
1559
+		if (empty($capability) && empty($route_to_check)) {
1560
+			$capability = is_array($this->_route) && empty($this->_route['capability']) ? 'manage_options'
1561
+				: $this->_route['capability'];
1562
+		} else {
1563
+			$capability = empty($capability) ? 'manage_options' : $capability;
1564
+		}
1565
+		$id = is_array($this->_route) && ! empty($this->_route['obj_id']) ? $this->_route['obj_id'] : 0;
1566
+		if (
1567
+			! $this->request->isAjax()
1568
+			&& (
1569
+				! function_exists('is_admin')
1570
+				|| ! EE_Registry::instance()->CAP->current_user_can(
1571
+					$capability,
1572
+					$this->page_slug
1573
+					. '_'
1574
+					. $route_to_check,
1575
+					$id
1576
+				)
1577
+			)
1578
+		) {
1579
+			if ($verify_only) {
1580
+				return false;
1581
+			}
1582
+			if (is_user_logged_in()) {
1583
+				wp_die(esc_html__('You do not have access to this route.', 'event_espresso'));
1584
+			} else {
1585
+				return false;
1586
+			}
1587
+		}
1588
+		return true;
1589
+	}
1590
+
1591
+
1592
+	/**
1593
+	 * @param string                 $box_id
1594
+	 * @param string                 $title
1595
+	 * @param callable|string|null   $callback
1596
+	 * @param string|array|WP_Screen $screen
1597
+	 * @param string                 $context
1598
+	 * @param string                 $priority
1599
+	 * @param array|null             $callback_args
1600
+	 */
1601
+	protected function addMetaBox(
1602
+		string $box_id,
1603
+		string $title,
1604
+		$callback,
1605
+		$screen,
1606
+		string $context = 'normal',
1607
+		string $priority = 'default',
1608
+		?array $callback_args = null
1609
+	) {
1610
+		if (! is_callable($callback)) {
1611
+			return;
1612
+		}
1613
+
1614
+		add_meta_box($box_id, $title, $callback, $screen, $context, $priority, $callback_args);
1615
+		add_filter(
1616
+			"postbox_classes_{$this->_wp_page_slug}_{$box_id}",
1617
+			function ($classes) {
1618
+				array_push($classes, 'ee-admin-container');
1619
+				return $classes;
1620
+			}
1621
+		);
1622
+	}
1623
+
1624
+
1625
+	/**
1626
+	 * admin_init_global
1627
+	 * This runs all the code that we want executed within the WP admin_init hook.
1628
+	 * This method executes for ALL EE Admin pages.
1629
+	 *
1630
+	 * @return void
1631
+	 */
1632
+	public function admin_init_global()
1633
+	{
1634
+	}
1635
+
1636
+
1637
+	/**
1638
+	 * wp_loaded_global
1639
+	 * This runs all the code that we want executed within the WP wp_loaded hook.  This method is optional for an
1640
+	 * EE_Admin page and will execute on every EE Admin Page load
1641
+	 *
1642
+	 * @return void
1643
+	 */
1644
+	public function wp_loaded()
1645
+	{
1646
+	}
1647
+
1648
+
1649
+	/**
1650
+	 * admin_notices
1651
+	 * Anything triggered by the 'admin_notices' WP hook should be put in here.  This particular method will apply on
1652
+	 * ALL EE_Admin pages.
1653
+	 *
1654
+	 * @return void
1655
+	 */
1656
+	public function admin_notices_global()
1657
+	{
1658
+		$this->_display_no_javascript_warning();
1659
+		$this->_display_espresso_notices();
1660
+	}
1661
+
1662
+
1663
+	public function network_admin_notices_global()
1664
+	{
1665
+		$this->_display_no_javascript_warning();
1666
+		$this->_display_espresso_notices();
1667
+	}
1668
+
1669
+
1670
+	/**
1671
+	 * admin_footer_scripts_global
1672
+	 * Anything triggered by the 'admin_print_footer_scripts' WP hook should be put in here. This particular method
1673
+	 * will apply on ALL EE_Admin pages.
1674
+	 *
1675
+	 * @return void
1676
+	 */
1677
+	public function admin_footer_scripts_global()
1678
+	{
1679
+		$this->_add_admin_page_ajax_loading_img();
1680
+		$this->_add_admin_page_overlay();
1681
+		// if metaboxes are present we need to add the nonce field
1682
+		if (
1683
+			isset($this->_route_config['metaboxes'])
1684
+			|| isset($this->_route_config['list_table'])
1685
+			|| (isset($this->_route_config['has_metaboxes']) && $this->_route_config['has_metaboxes'])
1686
+		) {
1687
+			wp_nonce_field('closedpostboxes', 'closedpostboxesnonce', false);
1688
+			wp_nonce_field('meta-box-order', 'meta-box-order-nonce', false);
1689
+		}
1690
+	}
1691
+
1692
+
1693
+	/**
1694
+	 * admin_footer_global
1695
+	 * Anything triggered by the wp 'admin_footer' wp hook should be put in here.
1696
+	 * This particular method will apply on ALL EE_Admin Pages.
1697
+	 *
1698
+	 * @return void
1699
+	 */
1700
+	public function admin_footer_global()
1701
+	{
1702
+		// dialog container for dialog helper
1703
+		echo '
1704 1704
         <div class="ee-admin-dialog-container auto-hide hidden">
1705 1705
             <div class="ee-notices"></div>
1706 1706
             <div class="ee-admin-dialog-container-inner-content"></div>
1707 1707
         </div>
1708 1708
         ';
1709 1709
 
1710
-        // current set timezone for timezone js
1711
-        echo '<span id="current_timezone" class="hidden">' . esc_html(EEH_DTT_Helper::get_timezone()) . '</span>';
1712
-    }
1713
-
1714
-
1715
-    /**
1716
-     * This function sees if there is a method for help popup content existing for the given route.  If there is then
1717
-     * we'll use the retrieved array to output the content using the template. For child classes: If you want to have
1718
-     * help popups then in your templates or your content you set "triggers" for the content using the
1719
-     * "_set_help_trigger('help_trigger_id')" where "help_trigger_id" is what you will use later in your custom method
1720
-     * for the help popup content on that page. Then in your Child_Admin_Page class you need to define a help popup
1721
-     * method for the content in the format "_help_popup_content_{route_name}()"  So if you are setting help content
1722
-     * for the
1723
-     * 'edit_event' route you should have a method named "_help_popup_content_edit_route". In your defined
1724
-     * "help_popup_content_..." method.  You must prepare and return an array in the following format array(
1725
-     *    'help_trigger_id' => array(
1726
-     *        'title' => esc_html__('localized title for popup', 'event_espresso'),
1727
-     *        'content' => esc_html__('localized content for popup', 'event_espresso')
1728
-     *    )
1729
-     * );
1730
-     * Then the EE_Admin_Parent will take care of making sure that is setup properly on the correct route.
1731
-     *
1732
-     * @param array $help_array
1733
-     * @param bool  $display
1734
-     * @return string content
1735
-     * @throws DomainException
1736
-     * @throws EE_Error
1737
-     */
1738
-    protected function _set_help_popup_content($help_array = [], $display = false)
1739
-    {
1740
-        $content    = '';
1741
-        $help_array = empty($help_array) ? $this->_get_help_content() : $help_array;
1742
-        // loop through the array and setup content
1743
-        foreach ($help_array as $trigger => $help) {
1744
-            // make sure the array is setup properly
1745
-            if (! isset($help['title'], $help['content'])) {
1746
-                throw new EE_Error(
1747
-                    esc_html__(
1748
-                        'Does not look like the popup content array has been setup correctly.  Might want to double check that.  Read the comments for the _get_help_popup_content method found in "EE_Admin_Page" class',
1749
-                        'event_espresso'
1750
-                    )
1751
-                );
1752
-            }
1753
-            // we're good so let's setup the template vars and then assign parsed template content to our content.
1754
-            $template_args = [
1755
-                'help_popup_id'      => $trigger,
1756
-                'help_popup_title'   => $help['title'],
1757
-                'help_popup_content' => $help['content'],
1758
-            ];
1759
-            $content       .= EEH_Template::display_template(
1760
-                EE_ADMIN_TEMPLATE . 'admin_help_popup.template.php',
1761
-                $template_args,
1762
-                true
1763
-            );
1764
-        }
1765
-        if ($display) {
1766
-            echo wp_kses($content, AllowedTags::getWithFormTags());
1767
-            return '';
1768
-        }
1769
-        return $content;
1770
-    }
1771
-
1772
-
1773
-    /**
1774
-     * All this does is retrieve the help content array if set by the EE_Admin_Page child
1775
-     *
1776
-     * @return array properly formatted array for help popup content
1777
-     * @throws EE_Error
1778
-     */
1779
-    private function _get_help_content()
1780
-    {
1781
-        // what is the method we're looking for?
1782
-        $method_name = '_help_popup_content_' . $this->_req_action;
1783
-        // if method doesn't exist let's get out.
1784
-        if (! method_exists($this, $method_name)) {
1785
-            return [];
1786
-        }
1787
-        // k we're good to go let's retrieve the help array
1788
-        $help_array = $this->{$method_name}();
1789
-        // make sure we've got an array!
1790
-        if (! is_array($help_array)) {
1791
-            throw new EE_Error(
1792
-                esc_html__(
1793
-                    'Something went wrong with help popup content generation. Expecting an array and well, this ain\'t no array bub.',
1794
-                    'event_espresso'
1795
-                )
1796
-            );
1797
-        }
1798
-        return $help_array;
1799
-    }
1800
-
1801
-
1802
-    /**
1803
-     * EE Admin Pages can use this to set a properly formatted trigger for a help popup.
1804
-     * By default the trigger html is printed.  Otherwise it can be returned if the $display flag is set "false"
1805
-     * See comments made on the _set_help_content method for understanding other parts to the help popup tool.
1806
-     *
1807
-     * @param string  $trigger_id reference for retrieving the trigger content for the popup
1808
-     * @param boolean $display    if false then we return the trigger string
1809
-     * @param array   $dimensions an array of dimensions for the box (array(h,w))
1810
-     * @return string
1811
-     * @throws DomainException
1812
-     * @throws EE_Error
1813
-     */
1814
-    protected function _set_help_trigger($trigger_id, $display = true, $dimensions = ['400', '640'])
1815
-    {
1816
-        if ($this->request->isAjax()) {
1817
-            return '';
1818
-        }
1819
-        // let's check and see if there is any content set for this popup.  If there isn't then we'll include a default title and content so that developers know something needs to be corrected
1820
-        $help_array   = $this->_get_help_content();
1821
-        $help_content = '';
1822
-        if (empty($help_array) || ! isset($help_array[ $trigger_id ])) {
1823
-            $help_array[ $trigger_id ] = [
1824
-                'title'   => esc_html__('Missing Content', 'event_espresso'),
1825
-                'content' => esc_html__(
1826
-                    'A trigger has been set that doesn\'t have any corresponding content. Make sure you have set the help content. (see the "_set_help_popup_content" method in the EE_Admin_Page for instructions.)',
1827
-                    'event_espresso'
1828
-                ),
1829
-            ];
1830
-            $help_content = $this->_set_help_popup_content($help_array);
1831
-        }
1832
-        // let's setup the trigger
1833
-        $content = '<a class="ee-dialog" href="?height='
1834
-                   . esc_attr($dimensions[0])
1835
-                   . '&width='
1836
-                   . esc_attr($dimensions[1])
1837
-                   . '&inlineId='
1838
-                   . esc_attr($trigger_id)
1839
-                   . '" target="_blank"><span class="question ee-help-popup-question"></span></a>';
1840
-        $content .= $help_content;
1841
-        if ($display) {
1842
-            echo wp_kses($content, AllowedTags::getWithFormTags());
1843
-            return '';
1844
-        }
1845
-        return $content;
1846
-    }
1847
-
1848
-
1849
-    /**
1850
-     * _add_global_screen_options
1851
-     * Add any extra wp_screen_options within this method using built-in WP functions/methods for doing so.
1852
-     * This particular method will add_screen_options on ALL EE_Admin Pages
1853
-     *
1854
-     * @link   http://chrismarslender.com/wp-tutorials/wordpress-screen-options-tutorial/
1855
-     *         see also WP_Screen object documents...
1856
-     * @link   http://codex.wordpress.org/Class_Reference/WP_Screen
1857
-     * @abstract
1858
-     * @return void
1859
-     */
1860
-    private function _add_global_screen_options()
1861
-    {
1862
-    }
1863
-
1864
-
1865
-    /**
1866
-     * _add_global_feature_pointers
1867
-     * This method is used for implementing any "feature pointers" (using built-in WP styling js).
1868
-     * This particular method will implement feature pointers for ALL EE_Admin pages.
1869
-     * Note: this is just a placeholder for now.  Implementation will come down the road
1870
-     *
1871
-     * @see    WP_Internal_Pointers class in wp-admin/includes/template.php for example (its a final class so can't be
1872
-     *         extended) also see:
1873
-     * @link   http://eamann.com/tech/wordpress-portland/
1874
-     * @abstract
1875
-     * @return void
1876
-     */
1877
-    private function _add_global_feature_pointers()
1878
-    {
1879
-    }
1880
-
1881
-
1882
-    /**
1883
-     * load_global_scripts_styles
1884
-     * The scripts and styles enqueued in here will be loaded on every EE Admin page
1885
-     *
1886
-     * @return void
1887
-     */
1888
-    public function load_global_scripts_styles()
1889
-    {
1890
-        // add debugging styles
1891
-        if (WP_DEBUG) {
1892
-            add_action('admin_head', [$this, 'add_xdebug_style']);
1893
-        }
1894
-        // taking care of metaboxes
1895
-        if (
1896
-            empty($this->_cpt_route)
1897
-            && (isset($this->_route_config['metaboxes']) || isset($this->_route_config['has_metaboxes']))
1898
-        ) {
1899
-            wp_enqueue_script('dashboard');
1900
-        }
1901
-
1902
-        wp_enqueue_script(JqueryAssetManager::JS_HANDLE_JQUERY_UI_TOUCH_PUNCH);
1903
-        wp_enqueue_script(EspressoLegacyAdminAssetManager::JS_HANDLE_EE_ADMIN);
1904
-        // LOCALIZED DATA
1905
-        // localize script for ajax lazy loading
1906
-        wp_localize_script(
1907
-            EspressoLegacyAdminAssetManager::JS_HANDLE_EE_ADMIN,
1908
-            'eeLazyLoadingContainers',
1909
-            apply_filters(
1910
-                'FHEE__EE_Admin_Page_Core__load_global_scripts_styles__loader_containers',
1911
-                ['espresso_news_post_box_content']
1912
-            )
1913
-        );
1914
-        StatusChangeNotice::loadAssets();
1915
-
1916
-        add_filter(
1917
-            'admin_body_class',
1918
-            function ($classes) {
1919
-                if (strpos($classes, 'espresso-admin') === false) {
1920
-                    $classes .= ' espresso-admin';
1921
-                }
1922
-                return $classes;
1923
-            }
1924
-        );
1925
-    }
1926
-
1927
-
1928
-    /**
1929
-     *        admin_footer_scripts_eei18n_js_strings
1930
-     *
1931
-     * @return        void
1932
-     */
1933
-    public function admin_footer_scripts_eei18n_js_strings()
1934
-    {
1935
-        EE_Registry::$i18n_js_strings['ajax_url']       = WP_AJAX_URL;
1936
-        EE_Registry::$i18n_js_strings['confirm_delete'] = wp_strip_all_tags(
1937
-            __(
1938
-                'Are you absolutely sure you want to delete this item?\nThis action will delete ALL DATA associated with this item!!!\nThis can NOT be undone!!!',
1939
-                'event_espresso'
1940
-            )
1941
-        );
1942
-        EE_Registry::$i18n_js_strings['January']        = wp_strip_all_tags(__('January', 'event_espresso'));
1943
-        EE_Registry::$i18n_js_strings['February']       = wp_strip_all_tags(__('February', 'event_espresso'));
1944
-        EE_Registry::$i18n_js_strings['March']          = wp_strip_all_tags(__('March', 'event_espresso'));
1945
-        EE_Registry::$i18n_js_strings['April']          = wp_strip_all_tags(__('April', 'event_espresso'));
1946
-        EE_Registry::$i18n_js_strings['May']            = wp_strip_all_tags(__('May', 'event_espresso'));
1947
-        EE_Registry::$i18n_js_strings['June']           = wp_strip_all_tags(__('June', 'event_espresso'));
1948
-        EE_Registry::$i18n_js_strings['July']           = wp_strip_all_tags(__('July', 'event_espresso'));
1949
-        EE_Registry::$i18n_js_strings['August']         = wp_strip_all_tags(__('August', 'event_espresso'));
1950
-        EE_Registry::$i18n_js_strings['September']      = wp_strip_all_tags(__('September', 'event_espresso'));
1951
-        EE_Registry::$i18n_js_strings['October']        = wp_strip_all_tags(__('October', 'event_espresso'));
1952
-        EE_Registry::$i18n_js_strings['November']       = wp_strip_all_tags(__('November', 'event_espresso'));
1953
-        EE_Registry::$i18n_js_strings['December']       = wp_strip_all_tags(__('December', 'event_espresso'));
1954
-        EE_Registry::$i18n_js_strings['Jan']            = wp_strip_all_tags(__('Jan', 'event_espresso'));
1955
-        EE_Registry::$i18n_js_strings['Feb']            = wp_strip_all_tags(__('Feb', 'event_espresso'));
1956
-        EE_Registry::$i18n_js_strings['Mar']            = wp_strip_all_tags(__('Mar', 'event_espresso'));
1957
-        EE_Registry::$i18n_js_strings['Apr']            = wp_strip_all_tags(__('Apr', 'event_espresso'));
1958
-        EE_Registry::$i18n_js_strings['May']            = wp_strip_all_tags(__('May', 'event_espresso'));
1959
-        EE_Registry::$i18n_js_strings['Jun']            = wp_strip_all_tags(__('Jun', 'event_espresso'));
1960
-        EE_Registry::$i18n_js_strings['Jul']            = wp_strip_all_tags(__('Jul', 'event_espresso'));
1961
-        EE_Registry::$i18n_js_strings['Aug']            = wp_strip_all_tags(__('Aug', 'event_espresso'));
1962
-        EE_Registry::$i18n_js_strings['Sep']            = wp_strip_all_tags(__('Sep', 'event_espresso'));
1963
-        EE_Registry::$i18n_js_strings['Oct']            = wp_strip_all_tags(__('Oct', 'event_espresso'));
1964
-        EE_Registry::$i18n_js_strings['Nov']            = wp_strip_all_tags(__('Nov', 'event_espresso'));
1965
-        EE_Registry::$i18n_js_strings['Dec']            = wp_strip_all_tags(__('Dec', 'event_espresso'));
1966
-        EE_Registry::$i18n_js_strings['Sunday']         = wp_strip_all_tags(__('Sunday', 'event_espresso'));
1967
-        EE_Registry::$i18n_js_strings['Monday']         = wp_strip_all_tags(__('Monday', 'event_espresso'));
1968
-        EE_Registry::$i18n_js_strings['Tuesday']        = wp_strip_all_tags(__('Tuesday', 'event_espresso'));
1969
-        EE_Registry::$i18n_js_strings['Wednesday']      = wp_strip_all_tags(__('Wednesday', 'event_espresso'));
1970
-        EE_Registry::$i18n_js_strings['Thursday']       = wp_strip_all_tags(__('Thursday', 'event_espresso'));
1971
-        EE_Registry::$i18n_js_strings['Friday']         = wp_strip_all_tags(__('Friday', 'event_espresso'));
1972
-        EE_Registry::$i18n_js_strings['Saturday']       = wp_strip_all_tags(__('Saturday', 'event_espresso'));
1973
-        EE_Registry::$i18n_js_strings['Sun']            = wp_strip_all_tags(__('Sun', 'event_espresso'));
1974
-        EE_Registry::$i18n_js_strings['Mon']            = wp_strip_all_tags(__('Mon', 'event_espresso'));
1975
-        EE_Registry::$i18n_js_strings['Tue']            = wp_strip_all_tags(__('Tue', 'event_espresso'));
1976
-        EE_Registry::$i18n_js_strings['Wed']            = wp_strip_all_tags(__('Wed', 'event_espresso'));
1977
-        EE_Registry::$i18n_js_strings['Thu']            = wp_strip_all_tags(__('Thu', 'event_espresso'));
1978
-        EE_Registry::$i18n_js_strings['Fri']            = wp_strip_all_tags(__('Fri', 'event_espresso'));
1979
-        EE_Registry::$i18n_js_strings['Sat']            = wp_strip_all_tags(__('Sat', 'event_espresso'));
1980
-    }
1981
-
1982
-
1983
-    /**
1984
-     *        load enhanced xdebug styles for ppl with failing eyesight
1985
-     *
1986
-     * @return        void
1987
-     */
1988
-    public function add_xdebug_style()
1989
-    {
1990
-        echo '<style>.xdebug-error { font-size:1.5em; }</style>';
1991
-    }
1992
-
1993
-
1994
-    /************************/
1995
-    /** LIST TABLE METHODS **/
1996
-    /************************/
1997
-    /**
1998
-     * this sets up the list table if the current view requires it.
1999
-     *
2000
-     * @return void
2001
-     * @throws EE_Error
2002
-     * @throws InvalidArgumentException
2003
-     * @throws InvalidDataTypeException
2004
-     * @throws InvalidInterfaceException
2005
-     */
2006
-    protected function _set_list_table()
2007
-    {
2008
-        // first is this a list_table view?
2009
-        if (! isset($this->_route_config['list_table'])) {
2010
-            return;
2011
-        } //not a list_table view so get out.
2012
-        // list table functions are per view specific (because some admin pages might have more than one list table!)
2013
-        $list_table_view = '_set_list_table_views_' . $this->_req_action;
2014
-        if (! method_exists($this, $list_table_view) || $this->{$list_table_view}() === false) {
2015
-            // user error msg
2016
-            $error_msg = esc_html__(
2017
-                'An error occurred. The requested list table views could not be found.',
2018
-                'event_espresso'
2019
-            );
2020
-            // developer error msg
2021
-            $error_msg .= '||'
2022
-                          . sprintf(
2023
-                              esc_html__(
2024
-                                  'List table views for "%s" route could not be setup. Check that you have the corresponding method, "%s" set up for defining list_table_views for this route.',
2025
-                                  'event_espresso'
2026
-                              ),
2027
-                              $this->_req_action,
2028
-                              $list_table_view
2029
-                          );
2030
-            throw new EE_Error($error_msg);
2031
-        }
2032
-        // let's provide the ability to filter the views per PAGE AND ROUTE, per PAGE, and globally
2033
-        $this->_views = apply_filters(
2034
-            'FHEE_list_table_views_' . $this->page_slug . '_' . $this->_req_action,
2035
-            $this->_views
2036
-        );
2037
-        $this->_views = apply_filters('FHEE_list_table_views_' . $this->page_slug, $this->_views);
2038
-        $this->_views = apply_filters('FHEE_list_table_views', $this->_views);
2039
-        $this->_set_list_table_view();
2040
-        $this->_set_list_table_object();
2041
-    }
2042
-
2043
-
2044
-    /**
2045
-     * set current view for List Table
2046
-     *
2047
-     * @return void
2048
-     */
2049
-    protected function _set_list_table_view()
2050
-    {
2051
-        $this->_view = isset($this->_views['in_use']) ? 'in_use' : 'all';
2052
-        $status = $this->request->getRequestParam('status', null, 'key');
2053
-        $this->_view = $status && array_key_exists($status, $this->_views)
2054
-            ? $status
2055
-            : $this->_view;
2056
-    }
2057
-
2058
-
2059
-    /**
2060
-     * _set_list_table_object
2061
-     * WP_List_Table objects need to be loaded fairly early so automatic stuff WP does is taken care of.
2062
-     *
2063
-     * @throws InvalidInterfaceException
2064
-     * @throws InvalidArgumentException
2065
-     * @throws InvalidDataTypeException
2066
-     * @throws EE_Error
2067
-     * @throws InvalidInterfaceException
2068
-     */
2069
-    protected function _set_list_table_object()
2070
-    {
2071
-        if (isset($this->_route_config['list_table'])) {
2072
-            if (! class_exists($this->_route_config['list_table'])) {
2073
-                throw new EE_Error(
2074
-                    sprintf(
2075
-                        esc_html__(
2076
-                            'The %s class defined for the list table does not exist.  Please check the spelling of the class ref in the $_page_config property on %s.',
2077
-                            'event_espresso'
2078
-                        ),
2079
-                        $this->_route_config['list_table'],
2080
-                        $this->class_name
2081
-                    )
2082
-                );
2083
-            }
2084
-            $this->_list_table_object = $this->loader->getShared(
2085
-                $this->_route_config['list_table'],
2086
-                [$this]
2087
-            );
2088
-        }
2089
-    }
2090
-
2091
-
2092
-    /**
2093
-     * get_list_table_view_RLs - get it? View RL ?? VU-RL???  URL ??
2094
-     *
2095
-     * @param array $extra_query_args                     Optional. An array of extra query args to add to the generated
2096
-     *                                                    urls.  The array should be indexed by the view it is being
2097
-     *                                                    added to.
2098
-     * @return array
2099
-     */
2100
-    public function get_list_table_view_RLs($extra_query_args = [])
2101
-    {
2102
-        do_action('AHEE_log', __FILE__, __FUNCTION__, '');
2103
-        if (empty($this->_views)) {
2104
-            $this->_views = [];
2105
-        }
2106
-        // cycle thru views
2107
-        foreach ($this->_views as $key => $view) {
2108
-            $query_args = [];
2109
-            // check for current view
2110
-            $this->_views[ $key ]['class']               = $this->_view === $view['slug'] ? 'current' : '';
2111
-            $query_args['action']                        = $this->_req_action;
2112
-            $query_args[ $this->_req_action . '_nonce' ] = wp_create_nonce($query_args['action'] . '_nonce');
2113
-            $query_args['status']                        = $view['slug'];
2114
-            // merge any other arguments sent in.
2115
-            if (isset($extra_query_args[ $view['slug'] ])) {
2116
-                foreach ($extra_query_args[ $view['slug'] ] as $extra_query_arg) {
2117
-                    $query_args[] = $extra_query_arg;
2118
-                }
2119
-            }
2120
-            $this->_views[ $key ]['url'] = EE_Admin_Page::add_query_args_and_nonce($query_args, $this->_admin_base_url);
2121
-        }
2122
-        return $this->_views;
2123
-    }
2124
-
2125
-
2126
-    /**
2127
-     * _entries_per_page_dropdown
2128
-     * generates a dropdown box for selecting the number of visible rows in an admin page list table
2129
-     *
2130
-     * @param int $max_entries total number of rows in the table
2131
-     * @return string
2132
-     * @todo   : Note: ideally this should be added to the screen options dropdown as that would be consistent with how
2133
-     *         WP does it.
2134
-     */
2135
-    protected function _entries_per_page_dropdown($max_entries = 0)
2136
-    {
2137
-        do_action('AHEE_log', __FILE__, __FUNCTION__, '');
2138
-        $values   = [10, 25, 50, 100];
2139
-        $per_page = $this->request->getRequestParam('per_page', 10, 'int');
2140
-        if ($max_entries) {
2141
-            $values[] = $max_entries;
2142
-            sort($values);
2143
-        }
2144
-        $entries_per_page_dropdown = '
1710
+		// current set timezone for timezone js
1711
+		echo '<span id="current_timezone" class="hidden">' . esc_html(EEH_DTT_Helper::get_timezone()) . '</span>';
1712
+	}
1713
+
1714
+
1715
+	/**
1716
+	 * This function sees if there is a method for help popup content existing for the given route.  If there is then
1717
+	 * we'll use the retrieved array to output the content using the template. For child classes: If you want to have
1718
+	 * help popups then in your templates or your content you set "triggers" for the content using the
1719
+	 * "_set_help_trigger('help_trigger_id')" where "help_trigger_id" is what you will use later in your custom method
1720
+	 * for the help popup content on that page. Then in your Child_Admin_Page class you need to define a help popup
1721
+	 * method for the content in the format "_help_popup_content_{route_name}()"  So if you are setting help content
1722
+	 * for the
1723
+	 * 'edit_event' route you should have a method named "_help_popup_content_edit_route". In your defined
1724
+	 * "help_popup_content_..." method.  You must prepare and return an array in the following format array(
1725
+	 *    'help_trigger_id' => array(
1726
+	 *        'title' => esc_html__('localized title for popup', 'event_espresso'),
1727
+	 *        'content' => esc_html__('localized content for popup', 'event_espresso')
1728
+	 *    )
1729
+	 * );
1730
+	 * Then the EE_Admin_Parent will take care of making sure that is setup properly on the correct route.
1731
+	 *
1732
+	 * @param array $help_array
1733
+	 * @param bool  $display
1734
+	 * @return string content
1735
+	 * @throws DomainException
1736
+	 * @throws EE_Error
1737
+	 */
1738
+	protected function _set_help_popup_content($help_array = [], $display = false)
1739
+	{
1740
+		$content    = '';
1741
+		$help_array = empty($help_array) ? $this->_get_help_content() : $help_array;
1742
+		// loop through the array and setup content
1743
+		foreach ($help_array as $trigger => $help) {
1744
+			// make sure the array is setup properly
1745
+			if (! isset($help['title'], $help['content'])) {
1746
+				throw new EE_Error(
1747
+					esc_html__(
1748
+						'Does not look like the popup content array has been setup correctly.  Might want to double check that.  Read the comments for the _get_help_popup_content method found in "EE_Admin_Page" class',
1749
+						'event_espresso'
1750
+					)
1751
+				);
1752
+			}
1753
+			// we're good so let's setup the template vars and then assign parsed template content to our content.
1754
+			$template_args = [
1755
+				'help_popup_id'      => $trigger,
1756
+				'help_popup_title'   => $help['title'],
1757
+				'help_popup_content' => $help['content'],
1758
+			];
1759
+			$content       .= EEH_Template::display_template(
1760
+				EE_ADMIN_TEMPLATE . 'admin_help_popup.template.php',
1761
+				$template_args,
1762
+				true
1763
+			);
1764
+		}
1765
+		if ($display) {
1766
+			echo wp_kses($content, AllowedTags::getWithFormTags());
1767
+			return '';
1768
+		}
1769
+		return $content;
1770
+	}
1771
+
1772
+
1773
+	/**
1774
+	 * All this does is retrieve the help content array if set by the EE_Admin_Page child
1775
+	 *
1776
+	 * @return array properly formatted array for help popup content
1777
+	 * @throws EE_Error
1778
+	 */
1779
+	private function _get_help_content()
1780
+	{
1781
+		// what is the method we're looking for?
1782
+		$method_name = '_help_popup_content_' . $this->_req_action;
1783
+		// if method doesn't exist let's get out.
1784
+		if (! method_exists($this, $method_name)) {
1785
+			return [];
1786
+		}
1787
+		// k we're good to go let's retrieve the help array
1788
+		$help_array = $this->{$method_name}();
1789
+		// make sure we've got an array!
1790
+		if (! is_array($help_array)) {
1791
+			throw new EE_Error(
1792
+				esc_html__(
1793
+					'Something went wrong with help popup content generation. Expecting an array and well, this ain\'t no array bub.',
1794
+					'event_espresso'
1795
+				)
1796
+			);
1797
+		}
1798
+		return $help_array;
1799
+	}
1800
+
1801
+
1802
+	/**
1803
+	 * EE Admin Pages can use this to set a properly formatted trigger for a help popup.
1804
+	 * By default the trigger html is printed.  Otherwise it can be returned if the $display flag is set "false"
1805
+	 * See comments made on the _set_help_content method for understanding other parts to the help popup tool.
1806
+	 *
1807
+	 * @param string  $trigger_id reference for retrieving the trigger content for the popup
1808
+	 * @param boolean $display    if false then we return the trigger string
1809
+	 * @param array   $dimensions an array of dimensions for the box (array(h,w))
1810
+	 * @return string
1811
+	 * @throws DomainException
1812
+	 * @throws EE_Error
1813
+	 */
1814
+	protected function _set_help_trigger($trigger_id, $display = true, $dimensions = ['400', '640'])
1815
+	{
1816
+		if ($this->request->isAjax()) {
1817
+			return '';
1818
+		}
1819
+		// let's check and see if there is any content set for this popup.  If there isn't then we'll include a default title and content so that developers know something needs to be corrected
1820
+		$help_array   = $this->_get_help_content();
1821
+		$help_content = '';
1822
+		if (empty($help_array) || ! isset($help_array[ $trigger_id ])) {
1823
+			$help_array[ $trigger_id ] = [
1824
+				'title'   => esc_html__('Missing Content', 'event_espresso'),
1825
+				'content' => esc_html__(
1826
+					'A trigger has been set that doesn\'t have any corresponding content. Make sure you have set the help content. (see the "_set_help_popup_content" method in the EE_Admin_Page for instructions.)',
1827
+					'event_espresso'
1828
+				),
1829
+			];
1830
+			$help_content = $this->_set_help_popup_content($help_array);
1831
+		}
1832
+		// let's setup the trigger
1833
+		$content = '<a class="ee-dialog" href="?height='
1834
+				   . esc_attr($dimensions[0])
1835
+				   . '&width='
1836
+				   . esc_attr($dimensions[1])
1837
+				   . '&inlineId='
1838
+				   . esc_attr($trigger_id)
1839
+				   . '" target="_blank"><span class="question ee-help-popup-question"></span></a>';
1840
+		$content .= $help_content;
1841
+		if ($display) {
1842
+			echo wp_kses($content, AllowedTags::getWithFormTags());
1843
+			return '';
1844
+		}
1845
+		return $content;
1846
+	}
1847
+
1848
+
1849
+	/**
1850
+	 * _add_global_screen_options
1851
+	 * Add any extra wp_screen_options within this method using built-in WP functions/methods for doing so.
1852
+	 * This particular method will add_screen_options on ALL EE_Admin Pages
1853
+	 *
1854
+	 * @link   http://chrismarslender.com/wp-tutorials/wordpress-screen-options-tutorial/
1855
+	 *         see also WP_Screen object documents...
1856
+	 * @link   http://codex.wordpress.org/Class_Reference/WP_Screen
1857
+	 * @abstract
1858
+	 * @return void
1859
+	 */
1860
+	private function _add_global_screen_options()
1861
+	{
1862
+	}
1863
+
1864
+
1865
+	/**
1866
+	 * _add_global_feature_pointers
1867
+	 * This method is used for implementing any "feature pointers" (using built-in WP styling js).
1868
+	 * This particular method will implement feature pointers for ALL EE_Admin pages.
1869
+	 * Note: this is just a placeholder for now.  Implementation will come down the road
1870
+	 *
1871
+	 * @see    WP_Internal_Pointers class in wp-admin/includes/template.php for example (its a final class so can't be
1872
+	 *         extended) also see:
1873
+	 * @link   http://eamann.com/tech/wordpress-portland/
1874
+	 * @abstract
1875
+	 * @return void
1876
+	 */
1877
+	private function _add_global_feature_pointers()
1878
+	{
1879
+	}
1880
+
1881
+
1882
+	/**
1883
+	 * load_global_scripts_styles
1884
+	 * The scripts and styles enqueued in here will be loaded on every EE Admin page
1885
+	 *
1886
+	 * @return void
1887
+	 */
1888
+	public function load_global_scripts_styles()
1889
+	{
1890
+		// add debugging styles
1891
+		if (WP_DEBUG) {
1892
+			add_action('admin_head', [$this, 'add_xdebug_style']);
1893
+		}
1894
+		// taking care of metaboxes
1895
+		if (
1896
+			empty($this->_cpt_route)
1897
+			&& (isset($this->_route_config['metaboxes']) || isset($this->_route_config['has_metaboxes']))
1898
+		) {
1899
+			wp_enqueue_script('dashboard');
1900
+		}
1901
+
1902
+		wp_enqueue_script(JqueryAssetManager::JS_HANDLE_JQUERY_UI_TOUCH_PUNCH);
1903
+		wp_enqueue_script(EspressoLegacyAdminAssetManager::JS_HANDLE_EE_ADMIN);
1904
+		// LOCALIZED DATA
1905
+		// localize script for ajax lazy loading
1906
+		wp_localize_script(
1907
+			EspressoLegacyAdminAssetManager::JS_HANDLE_EE_ADMIN,
1908
+			'eeLazyLoadingContainers',
1909
+			apply_filters(
1910
+				'FHEE__EE_Admin_Page_Core__load_global_scripts_styles__loader_containers',
1911
+				['espresso_news_post_box_content']
1912
+			)
1913
+		);
1914
+		StatusChangeNotice::loadAssets();
1915
+
1916
+		add_filter(
1917
+			'admin_body_class',
1918
+			function ($classes) {
1919
+				if (strpos($classes, 'espresso-admin') === false) {
1920
+					$classes .= ' espresso-admin';
1921
+				}
1922
+				return $classes;
1923
+			}
1924
+		);
1925
+	}
1926
+
1927
+
1928
+	/**
1929
+	 *        admin_footer_scripts_eei18n_js_strings
1930
+	 *
1931
+	 * @return        void
1932
+	 */
1933
+	public function admin_footer_scripts_eei18n_js_strings()
1934
+	{
1935
+		EE_Registry::$i18n_js_strings['ajax_url']       = WP_AJAX_URL;
1936
+		EE_Registry::$i18n_js_strings['confirm_delete'] = wp_strip_all_tags(
1937
+			__(
1938
+				'Are you absolutely sure you want to delete this item?\nThis action will delete ALL DATA associated with this item!!!\nThis can NOT be undone!!!',
1939
+				'event_espresso'
1940
+			)
1941
+		);
1942
+		EE_Registry::$i18n_js_strings['January']        = wp_strip_all_tags(__('January', 'event_espresso'));
1943
+		EE_Registry::$i18n_js_strings['February']       = wp_strip_all_tags(__('February', 'event_espresso'));
1944
+		EE_Registry::$i18n_js_strings['March']          = wp_strip_all_tags(__('March', 'event_espresso'));
1945
+		EE_Registry::$i18n_js_strings['April']          = wp_strip_all_tags(__('April', 'event_espresso'));
1946
+		EE_Registry::$i18n_js_strings['May']            = wp_strip_all_tags(__('May', 'event_espresso'));
1947
+		EE_Registry::$i18n_js_strings['June']           = wp_strip_all_tags(__('June', 'event_espresso'));
1948
+		EE_Registry::$i18n_js_strings['July']           = wp_strip_all_tags(__('July', 'event_espresso'));
1949
+		EE_Registry::$i18n_js_strings['August']         = wp_strip_all_tags(__('August', 'event_espresso'));
1950
+		EE_Registry::$i18n_js_strings['September']      = wp_strip_all_tags(__('September', 'event_espresso'));
1951
+		EE_Registry::$i18n_js_strings['October']        = wp_strip_all_tags(__('October', 'event_espresso'));
1952
+		EE_Registry::$i18n_js_strings['November']       = wp_strip_all_tags(__('November', 'event_espresso'));
1953
+		EE_Registry::$i18n_js_strings['December']       = wp_strip_all_tags(__('December', 'event_espresso'));
1954
+		EE_Registry::$i18n_js_strings['Jan']            = wp_strip_all_tags(__('Jan', 'event_espresso'));
1955
+		EE_Registry::$i18n_js_strings['Feb']            = wp_strip_all_tags(__('Feb', 'event_espresso'));
1956
+		EE_Registry::$i18n_js_strings['Mar']            = wp_strip_all_tags(__('Mar', 'event_espresso'));
1957
+		EE_Registry::$i18n_js_strings['Apr']            = wp_strip_all_tags(__('Apr', 'event_espresso'));
1958
+		EE_Registry::$i18n_js_strings['May']            = wp_strip_all_tags(__('May', 'event_espresso'));
1959
+		EE_Registry::$i18n_js_strings['Jun']            = wp_strip_all_tags(__('Jun', 'event_espresso'));
1960
+		EE_Registry::$i18n_js_strings['Jul']            = wp_strip_all_tags(__('Jul', 'event_espresso'));
1961
+		EE_Registry::$i18n_js_strings['Aug']            = wp_strip_all_tags(__('Aug', 'event_espresso'));
1962
+		EE_Registry::$i18n_js_strings['Sep']            = wp_strip_all_tags(__('Sep', 'event_espresso'));
1963
+		EE_Registry::$i18n_js_strings['Oct']            = wp_strip_all_tags(__('Oct', 'event_espresso'));
1964
+		EE_Registry::$i18n_js_strings['Nov']            = wp_strip_all_tags(__('Nov', 'event_espresso'));
1965
+		EE_Registry::$i18n_js_strings['Dec']            = wp_strip_all_tags(__('Dec', 'event_espresso'));
1966
+		EE_Registry::$i18n_js_strings['Sunday']         = wp_strip_all_tags(__('Sunday', 'event_espresso'));
1967
+		EE_Registry::$i18n_js_strings['Monday']         = wp_strip_all_tags(__('Monday', 'event_espresso'));
1968
+		EE_Registry::$i18n_js_strings['Tuesday']        = wp_strip_all_tags(__('Tuesday', 'event_espresso'));
1969
+		EE_Registry::$i18n_js_strings['Wednesday']      = wp_strip_all_tags(__('Wednesday', 'event_espresso'));
1970
+		EE_Registry::$i18n_js_strings['Thursday']       = wp_strip_all_tags(__('Thursday', 'event_espresso'));
1971
+		EE_Registry::$i18n_js_strings['Friday']         = wp_strip_all_tags(__('Friday', 'event_espresso'));
1972
+		EE_Registry::$i18n_js_strings['Saturday']       = wp_strip_all_tags(__('Saturday', 'event_espresso'));
1973
+		EE_Registry::$i18n_js_strings['Sun']            = wp_strip_all_tags(__('Sun', 'event_espresso'));
1974
+		EE_Registry::$i18n_js_strings['Mon']            = wp_strip_all_tags(__('Mon', 'event_espresso'));
1975
+		EE_Registry::$i18n_js_strings['Tue']            = wp_strip_all_tags(__('Tue', 'event_espresso'));
1976
+		EE_Registry::$i18n_js_strings['Wed']            = wp_strip_all_tags(__('Wed', 'event_espresso'));
1977
+		EE_Registry::$i18n_js_strings['Thu']            = wp_strip_all_tags(__('Thu', 'event_espresso'));
1978
+		EE_Registry::$i18n_js_strings['Fri']            = wp_strip_all_tags(__('Fri', 'event_espresso'));
1979
+		EE_Registry::$i18n_js_strings['Sat']            = wp_strip_all_tags(__('Sat', 'event_espresso'));
1980
+	}
1981
+
1982
+
1983
+	/**
1984
+	 *        load enhanced xdebug styles for ppl with failing eyesight
1985
+	 *
1986
+	 * @return        void
1987
+	 */
1988
+	public function add_xdebug_style()
1989
+	{
1990
+		echo '<style>.xdebug-error { font-size:1.5em; }</style>';
1991
+	}
1992
+
1993
+
1994
+	/************************/
1995
+	/** LIST TABLE METHODS **/
1996
+	/************************/
1997
+	/**
1998
+	 * this sets up the list table if the current view requires it.
1999
+	 *
2000
+	 * @return void
2001
+	 * @throws EE_Error
2002
+	 * @throws InvalidArgumentException
2003
+	 * @throws InvalidDataTypeException
2004
+	 * @throws InvalidInterfaceException
2005
+	 */
2006
+	protected function _set_list_table()
2007
+	{
2008
+		// first is this a list_table view?
2009
+		if (! isset($this->_route_config['list_table'])) {
2010
+			return;
2011
+		} //not a list_table view so get out.
2012
+		// list table functions are per view specific (because some admin pages might have more than one list table!)
2013
+		$list_table_view = '_set_list_table_views_' . $this->_req_action;
2014
+		if (! method_exists($this, $list_table_view) || $this->{$list_table_view}() === false) {
2015
+			// user error msg
2016
+			$error_msg = esc_html__(
2017
+				'An error occurred. The requested list table views could not be found.',
2018
+				'event_espresso'
2019
+			);
2020
+			// developer error msg
2021
+			$error_msg .= '||'
2022
+						  . sprintf(
2023
+							  esc_html__(
2024
+								  'List table views for "%s" route could not be setup. Check that you have the corresponding method, "%s" set up for defining list_table_views for this route.',
2025
+								  'event_espresso'
2026
+							  ),
2027
+							  $this->_req_action,
2028
+							  $list_table_view
2029
+						  );
2030
+			throw new EE_Error($error_msg);
2031
+		}
2032
+		// let's provide the ability to filter the views per PAGE AND ROUTE, per PAGE, and globally
2033
+		$this->_views = apply_filters(
2034
+			'FHEE_list_table_views_' . $this->page_slug . '_' . $this->_req_action,
2035
+			$this->_views
2036
+		);
2037
+		$this->_views = apply_filters('FHEE_list_table_views_' . $this->page_slug, $this->_views);
2038
+		$this->_views = apply_filters('FHEE_list_table_views', $this->_views);
2039
+		$this->_set_list_table_view();
2040
+		$this->_set_list_table_object();
2041
+	}
2042
+
2043
+
2044
+	/**
2045
+	 * set current view for List Table
2046
+	 *
2047
+	 * @return void
2048
+	 */
2049
+	protected function _set_list_table_view()
2050
+	{
2051
+		$this->_view = isset($this->_views['in_use']) ? 'in_use' : 'all';
2052
+		$status = $this->request->getRequestParam('status', null, 'key');
2053
+		$this->_view = $status && array_key_exists($status, $this->_views)
2054
+			? $status
2055
+			: $this->_view;
2056
+	}
2057
+
2058
+
2059
+	/**
2060
+	 * _set_list_table_object
2061
+	 * WP_List_Table objects need to be loaded fairly early so automatic stuff WP does is taken care of.
2062
+	 *
2063
+	 * @throws InvalidInterfaceException
2064
+	 * @throws InvalidArgumentException
2065
+	 * @throws InvalidDataTypeException
2066
+	 * @throws EE_Error
2067
+	 * @throws InvalidInterfaceException
2068
+	 */
2069
+	protected function _set_list_table_object()
2070
+	{
2071
+		if (isset($this->_route_config['list_table'])) {
2072
+			if (! class_exists($this->_route_config['list_table'])) {
2073
+				throw new EE_Error(
2074
+					sprintf(
2075
+						esc_html__(
2076
+							'The %s class defined for the list table does not exist.  Please check the spelling of the class ref in the $_page_config property on %s.',
2077
+							'event_espresso'
2078
+						),
2079
+						$this->_route_config['list_table'],
2080
+						$this->class_name
2081
+					)
2082
+				);
2083
+			}
2084
+			$this->_list_table_object = $this->loader->getShared(
2085
+				$this->_route_config['list_table'],
2086
+				[$this]
2087
+			);
2088
+		}
2089
+	}
2090
+
2091
+
2092
+	/**
2093
+	 * get_list_table_view_RLs - get it? View RL ?? VU-RL???  URL ??
2094
+	 *
2095
+	 * @param array $extra_query_args                     Optional. An array of extra query args to add to the generated
2096
+	 *                                                    urls.  The array should be indexed by the view it is being
2097
+	 *                                                    added to.
2098
+	 * @return array
2099
+	 */
2100
+	public function get_list_table_view_RLs($extra_query_args = [])
2101
+	{
2102
+		do_action('AHEE_log', __FILE__, __FUNCTION__, '');
2103
+		if (empty($this->_views)) {
2104
+			$this->_views = [];
2105
+		}
2106
+		// cycle thru views
2107
+		foreach ($this->_views as $key => $view) {
2108
+			$query_args = [];
2109
+			// check for current view
2110
+			$this->_views[ $key ]['class']               = $this->_view === $view['slug'] ? 'current' : '';
2111
+			$query_args['action']                        = $this->_req_action;
2112
+			$query_args[ $this->_req_action . '_nonce' ] = wp_create_nonce($query_args['action'] . '_nonce');
2113
+			$query_args['status']                        = $view['slug'];
2114
+			// merge any other arguments sent in.
2115
+			if (isset($extra_query_args[ $view['slug'] ])) {
2116
+				foreach ($extra_query_args[ $view['slug'] ] as $extra_query_arg) {
2117
+					$query_args[] = $extra_query_arg;
2118
+				}
2119
+			}
2120
+			$this->_views[ $key ]['url'] = EE_Admin_Page::add_query_args_and_nonce($query_args, $this->_admin_base_url);
2121
+		}
2122
+		return $this->_views;
2123
+	}
2124
+
2125
+
2126
+	/**
2127
+	 * _entries_per_page_dropdown
2128
+	 * generates a dropdown box for selecting the number of visible rows in an admin page list table
2129
+	 *
2130
+	 * @param int $max_entries total number of rows in the table
2131
+	 * @return string
2132
+	 * @todo   : Note: ideally this should be added to the screen options dropdown as that would be consistent with how
2133
+	 *         WP does it.
2134
+	 */
2135
+	protected function _entries_per_page_dropdown($max_entries = 0)
2136
+	{
2137
+		do_action('AHEE_log', __FILE__, __FUNCTION__, '');
2138
+		$values   = [10, 25, 50, 100];
2139
+		$per_page = $this->request->getRequestParam('per_page', 10, 'int');
2140
+		if ($max_entries) {
2141
+			$values[] = $max_entries;
2142
+			sort($values);
2143
+		}
2144
+		$entries_per_page_dropdown = '
2145 2145
 			<div id="entries-per-page-dv" class="alignleft actions">
2146 2146
 				<label class="hide-if-no-js">
2147 2147
 					Show
2148 2148
 					<select id="entries-per-page-slct" name="entries-per-page-slct">';
2149
-        foreach ($values as $value) {
2150
-            if ($value < $max_entries) {
2151
-                $selected                  = $value === $per_page ? ' selected="' . $per_page . '"' : '';
2152
-                $entries_per_page_dropdown .= '
2149
+		foreach ($values as $value) {
2150
+			if ($value < $max_entries) {
2151
+				$selected                  = $value === $per_page ? ' selected="' . $per_page . '"' : '';
2152
+				$entries_per_page_dropdown .= '
2153 2153
 						<option value="' . $value . '"' . $selected . '>' . $value . '&nbsp;&nbsp;</option>';
2154
-            }
2155
-        }
2156
-        $selected                  = $max_entries === $per_page ? ' selected="' . $per_page . '"' : '';
2157
-        $entries_per_page_dropdown .= '
2154
+			}
2155
+		}
2156
+		$selected                  = $max_entries === $per_page ? ' selected="' . $per_page . '"' : '';
2157
+		$entries_per_page_dropdown .= '
2158 2158
 						<option value="' . $max_entries . '"' . $selected . '>All&nbsp;&nbsp;</option>';
2159
-        $entries_per_page_dropdown .= '
2159
+		$entries_per_page_dropdown .= '
2160 2160
 					</select>
2161 2161
 					entries
2162 2162
 				</label>
2163 2163
 				<input id="entries-per-page-btn" class="button button--secondary" type="submit" value="Go" >
2164 2164
 			</div>
2165 2165
 		';
2166
-        return $entries_per_page_dropdown;
2167
-    }
2168
-
2169
-
2170
-    /**
2171
-     *        _set_search_attributes
2172
-     *
2173
-     * @return        void
2174
-     */
2175
-    public function _set_search_attributes()
2176
-    {
2177
-        $this->_template_args['search']['btn_label'] = sprintf(
2178
-            esc_html__('Search %s', 'event_espresso'),
2179
-            empty($this->_search_btn_label) ? $this->page_label
2180
-                : $this->_search_btn_label
2181
-        );
2182
-        $this->_template_args['search']['callback']  = 'search_' . $this->page_slug;
2183
-    }
2184
-
2185
-
2186
-
2187
-    /*** END LIST TABLE METHODS **/
2188
-
2189
-
2190
-    /**
2191
-     * _add_registered_metaboxes
2192
-     *  this loads any registered metaboxes via the 'metaboxes' index in the _page_config property array.
2193
-     *
2194
-     * @link   http://codex.wordpress.org/Function_Reference/add_meta_box
2195
-     * @return void
2196
-     * @throws EE_Error
2197
-     */
2198
-    private function _add_registered_meta_boxes()
2199
-    {
2200
-        do_action('AHEE_log', __FILE__, __FUNCTION__, '');
2201
-        // we only add meta boxes if the page_route calls for it
2202
-        if (
2203
-            is_array($this->_route_config) && isset($this->_route_config['metaboxes'])
2204
-            && is_array(
2205
-                $this->_route_config['metaboxes']
2206
-            )
2207
-        ) {
2208
-            // this simply loops through the callbacks provided
2209
-            // and checks if there is a corresponding callback registered by the child
2210
-            // if there is then we go ahead and process the metabox loader.
2211
-            foreach ($this->_route_config['metaboxes'] as $metabox_callback) {
2212
-                // first check for Closures
2213
-                if ($metabox_callback instanceof Closure) {
2214
-                    $result = $metabox_callback();
2215
-                } elseif (is_array($metabox_callback) && isset($metabox_callback[0], $metabox_callback[1])) {
2216
-                    $result = call_user_func([$metabox_callback[0], $metabox_callback[1]]);
2217
-                } else {
2218
-                    $result = $this->{$metabox_callback}();
2219
-                }
2220
-                if ($result === false) {
2221
-                    // user error msg
2222
-                    $error_msg = esc_html__(
2223
-                        'An error occurred. The  requested metabox could not be found.',
2224
-                        'event_espresso'
2225
-                    );
2226
-                    // developer error msg
2227
-                    $error_msg .= '||'
2228
-                                  . sprintf(
2229
-                                      esc_html__(
2230
-                                          'The metabox with the string "%s" could not be called. Check that the spelling for method names and actions in the "_page_config[\'metaboxes\']" array are all correct.',
2231
-                                          'event_espresso'
2232
-                                      ),
2233
-                                      $metabox_callback
2234
-                                  );
2235
-                    throw new EE_Error($error_msg);
2236
-                }
2237
-            }
2238
-        }
2239
-    }
2240
-
2241
-
2242
-    /**
2243
-     * _add_screen_columns
2244
-     * This will check the _page_config array and if there is "columns" key index indicated, we'll set the template as
2245
-     * the dynamic column template and we'll setup the column options for the page.
2246
-     *
2247
-     * @return void
2248
-     */
2249
-    private function _add_screen_columns()
2250
-    {
2251
-        if (
2252
-            is_array($this->_route_config)
2253
-            && isset($this->_route_config['columns'])
2254
-            && is_array($this->_route_config['columns'])
2255
-            && count($this->_route_config['columns']) === 2
2256
-        ) {
2257
-            add_screen_option(
2258
-                'layout_columns',
2259
-                [
2260
-                    'max'     => (int) $this->_route_config['columns'][0],
2261
-                    'default' => (int) $this->_route_config['columns'][1],
2262
-                ]
2263
-            );
2264
-            $this->_template_args['num_columns']                 = $this->_route_config['columns'][0];
2265
-            $screen_id                                           = $this->_current_screen->id;
2266
-            $screen_columns                                      = (int) get_user_option("screen_layout_{$screen_id}");
2267
-            $total_columns                                       = ! empty($screen_columns)
2268
-                ? $screen_columns
2269
-                : $this->_route_config['columns'][1];
2270
-            $this->_template_args['current_screen_widget_class'] = 'columns-' . $total_columns;
2271
-            $this->_template_args['current_page']                = $this->_wp_page_slug;
2272
-            $this->_template_args['screen']                      = $this->_current_screen;
2273
-            $this->_column_template_path                         = EE_ADMIN_TEMPLATE
2274
-                                                                   . 'admin_details_metabox_column_wrapper.template.php';
2275
-            // finally if we don't have has_metaboxes set in the route config
2276
-            // let's make sure it IS set other wise the necessary hidden fields for this won't be loaded.
2277
-            $this->_route_config['has_metaboxes'] = true;
2278
-        }
2279
-    }
2280
-
2281
-
2282
-
2283
-    /** GLOBALLY AVAILABLE METABOXES **/
2284
-
2285
-
2286
-    /**
2287
-     * In this section we put any globally available EE metaboxes for all EE Admin pages.  They are called by simply
2288
-     * referencing the callback in the _page_config array property.  This way you can be very specific about what pages
2289
-     * these get loaded on.
2290
-     */
2291
-    private function _espresso_news_post_box()
2292
-    {
2293
-        $news_box_title = apply_filters(
2294
-            'FHEE__EE_Admin_Page___espresso_news_post_box__news_box_title',
2295
-            esc_html__('New @ Event Espresso', 'event_espresso')
2296
-        );
2297
-        $this->addMetaBox(
2298
-            'espresso_news_post_box',
2299
-            $news_box_title,
2300
-            [
2301
-                $this,
2302
-                'espresso_news_post_box',
2303
-            ],
2304
-            $this->_wp_page_slug,
2305
-            'side',
2306
-            'low'
2307
-        );
2308
-    }
2309
-
2310
-
2311
-    /**
2312
-     * Code for setting up espresso ratings request metabox.
2313
-     */
2314
-    protected function _espresso_ratings_request()
2315
-    {
2316
-        if (! apply_filters('FHEE_show_ratings_request_meta_box', true)) {
2317
-            return;
2318
-        }
2319
-        $ratings_box_title = apply_filters(
2320
-            'FHEE__EE_Admin_Page___espresso_news_post_box__news_box_title',
2321
-            esc_html__('Keep Event Espresso Decaf Free', 'event_espresso')
2322
-        );
2323
-        $this->addMetaBox(
2324
-            'espresso_ratings_request',
2325
-            $ratings_box_title,
2326
-            [
2327
-                $this,
2328
-                'espresso_ratings_request',
2329
-            ],
2330
-            $this->_wp_page_slug,
2331
-            'side'
2332
-        );
2333
-    }
2334
-
2335
-
2336
-    /**
2337
-     * Code for setting up espresso ratings request metabox content.
2338
-     *
2339
-     * @throws DomainException
2340
-     */
2341
-    public function espresso_ratings_request()
2342
-    {
2343
-        EEH_Template::display_template(EE_ADMIN_TEMPLATE . 'espresso_ratings_request_content.template.php');
2344
-    }
2345
-
2346
-
2347
-    public static function cached_rss_display($rss_id, $url)
2348
-    {
2349
-        $loading   = '<p class="widget-loading hide-if-no-js">'
2350
-                     . esc_html__('Loading&#8230;', 'event_espresso')
2351
-                     . '</p><p class="hide-if-js">'
2352
-                     . esc_html__('This widget requires JavaScript.', 'event_espresso')
2353
-                     . '</p>';
2354
-        $pre       = '<div class="espresso-rss-display">' . "\n\t";
2355
-        $pre       .= '<span id="' . esc_attr($rss_id) . '_url" class="hidden">' . esc_url_raw($url) . '</span>';
2356
-        $post      = '</div>' . "\n";
2357
-        $cache_key = 'ee_rss_' . md5($rss_id);
2358
-        $output    = get_transient($cache_key);
2359
-        if ($output !== false) {
2360
-            echo wp_kses($pre . $output . $post, AllowedTags::getWithFormTags());
2361
-            return true;
2362
-        }
2363
-        if (! (defined('DOING_AJAX') && DOING_AJAX)) {
2364
-            echo wp_kses($pre . $loading . $post, AllowedTags::getWithFormTags());
2365
-            return false;
2366
-        }
2367
-        ob_start();
2368
-        wp_widget_rss_output($url, ['show_date' => 0, 'items' => 5]);
2369
-        set_transient($cache_key, ob_get_flush(), 12 * HOUR_IN_SECONDS);
2370
-        return true;
2371
-    }
2372
-
2373
-
2374
-    public function espresso_news_post_box()
2375
-    {
2376
-        ?>
2166
+		return $entries_per_page_dropdown;
2167
+	}
2168
+
2169
+
2170
+	/**
2171
+	 *        _set_search_attributes
2172
+	 *
2173
+	 * @return        void
2174
+	 */
2175
+	public function _set_search_attributes()
2176
+	{
2177
+		$this->_template_args['search']['btn_label'] = sprintf(
2178
+			esc_html__('Search %s', 'event_espresso'),
2179
+			empty($this->_search_btn_label) ? $this->page_label
2180
+				: $this->_search_btn_label
2181
+		);
2182
+		$this->_template_args['search']['callback']  = 'search_' . $this->page_slug;
2183
+	}
2184
+
2185
+
2186
+
2187
+	/*** END LIST TABLE METHODS **/
2188
+
2189
+
2190
+	/**
2191
+	 * _add_registered_metaboxes
2192
+	 *  this loads any registered metaboxes via the 'metaboxes' index in the _page_config property array.
2193
+	 *
2194
+	 * @link   http://codex.wordpress.org/Function_Reference/add_meta_box
2195
+	 * @return void
2196
+	 * @throws EE_Error
2197
+	 */
2198
+	private function _add_registered_meta_boxes()
2199
+	{
2200
+		do_action('AHEE_log', __FILE__, __FUNCTION__, '');
2201
+		// we only add meta boxes if the page_route calls for it
2202
+		if (
2203
+			is_array($this->_route_config) && isset($this->_route_config['metaboxes'])
2204
+			&& is_array(
2205
+				$this->_route_config['metaboxes']
2206
+			)
2207
+		) {
2208
+			// this simply loops through the callbacks provided
2209
+			// and checks if there is a corresponding callback registered by the child
2210
+			// if there is then we go ahead and process the metabox loader.
2211
+			foreach ($this->_route_config['metaboxes'] as $metabox_callback) {
2212
+				// first check for Closures
2213
+				if ($metabox_callback instanceof Closure) {
2214
+					$result = $metabox_callback();
2215
+				} elseif (is_array($metabox_callback) && isset($metabox_callback[0], $metabox_callback[1])) {
2216
+					$result = call_user_func([$metabox_callback[0], $metabox_callback[1]]);
2217
+				} else {
2218
+					$result = $this->{$metabox_callback}();
2219
+				}
2220
+				if ($result === false) {
2221
+					// user error msg
2222
+					$error_msg = esc_html__(
2223
+						'An error occurred. The  requested metabox could not be found.',
2224
+						'event_espresso'
2225
+					);
2226
+					// developer error msg
2227
+					$error_msg .= '||'
2228
+								  . sprintf(
2229
+									  esc_html__(
2230
+										  'The metabox with the string "%s" could not be called. Check that the spelling for method names and actions in the "_page_config[\'metaboxes\']" array are all correct.',
2231
+										  'event_espresso'
2232
+									  ),
2233
+									  $metabox_callback
2234
+								  );
2235
+					throw new EE_Error($error_msg);
2236
+				}
2237
+			}
2238
+		}
2239
+	}
2240
+
2241
+
2242
+	/**
2243
+	 * _add_screen_columns
2244
+	 * This will check the _page_config array and if there is "columns" key index indicated, we'll set the template as
2245
+	 * the dynamic column template and we'll setup the column options for the page.
2246
+	 *
2247
+	 * @return void
2248
+	 */
2249
+	private function _add_screen_columns()
2250
+	{
2251
+		if (
2252
+			is_array($this->_route_config)
2253
+			&& isset($this->_route_config['columns'])
2254
+			&& is_array($this->_route_config['columns'])
2255
+			&& count($this->_route_config['columns']) === 2
2256
+		) {
2257
+			add_screen_option(
2258
+				'layout_columns',
2259
+				[
2260
+					'max'     => (int) $this->_route_config['columns'][0],
2261
+					'default' => (int) $this->_route_config['columns'][1],
2262
+				]
2263
+			);
2264
+			$this->_template_args['num_columns']                 = $this->_route_config['columns'][0];
2265
+			$screen_id                                           = $this->_current_screen->id;
2266
+			$screen_columns                                      = (int) get_user_option("screen_layout_{$screen_id}");
2267
+			$total_columns                                       = ! empty($screen_columns)
2268
+				? $screen_columns
2269
+				: $this->_route_config['columns'][1];
2270
+			$this->_template_args['current_screen_widget_class'] = 'columns-' . $total_columns;
2271
+			$this->_template_args['current_page']                = $this->_wp_page_slug;
2272
+			$this->_template_args['screen']                      = $this->_current_screen;
2273
+			$this->_column_template_path                         = EE_ADMIN_TEMPLATE
2274
+																   . 'admin_details_metabox_column_wrapper.template.php';
2275
+			// finally if we don't have has_metaboxes set in the route config
2276
+			// let's make sure it IS set other wise the necessary hidden fields for this won't be loaded.
2277
+			$this->_route_config['has_metaboxes'] = true;
2278
+		}
2279
+	}
2280
+
2281
+
2282
+
2283
+	/** GLOBALLY AVAILABLE METABOXES **/
2284
+
2285
+
2286
+	/**
2287
+	 * In this section we put any globally available EE metaboxes for all EE Admin pages.  They are called by simply
2288
+	 * referencing the callback in the _page_config array property.  This way you can be very specific about what pages
2289
+	 * these get loaded on.
2290
+	 */
2291
+	private function _espresso_news_post_box()
2292
+	{
2293
+		$news_box_title = apply_filters(
2294
+			'FHEE__EE_Admin_Page___espresso_news_post_box__news_box_title',
2295
+			esc_html__('New @ Event Espresso', 'event_espresso')
2296
+		);
2297
+		$this->addMetaBox(
2298
+			'espresso_news_post_box',
2299
+			$news_box_title,
2300
+			[
2301
+				$this,
2302
+				'espresso_news_post_box',
2303
+			],
2304
+			$this->_wp_page_slug,
2305
+			'side',
2306
+			'low'
2307
+		);
2308
+	}
2309
+
2310
+
2311
+	/**
2312
+	 * Code for setting up espresso ratings request metabox.
2313
+	 */
2314
+	protected function _espresso_ratings_request()
2315
+	{
2316
+		if (! apply_filters('FHEE_show_ratings_request_meta_box', true)) {
2317
+			return;
2318
+		}
2319
+		$ratings_box_title = apply_filters(
2320
+			'FHEE__EE_Admin_Page___espresso_news_post_box__news_box_title',
2321
+			esc_html__('Keep Event Espresso Decaf Free', 'event_espresso')
2322
+		);
2323
+		$this->addMetaBox(
2324
+			'espresso_ratings_request',
2325
+			$ratings_box_title,
2326
+			[
2327
+				$this,
2328
+				'espresso_ratings_request',
2329
+			],
2330
+			$this->_wp_page_slug,
2331
+			'side'
2332
+		);
2333
+	}
2334
+
2335
+
2336
+	/**
2337
+	 * Code for setting up espresso ratings request metabox content.
2338
+	 *
2339
+	 * @throws DomainException
2340
+	 */
2341
+	public function espresso_ratings_request()
2342
+	{
2343
+		EEH_Template::display_template(EE_ADMIN_TEMPLATE . 'espresso_ratings_request_content.template.php');
2344
+	}
2345
+
2346
+
2347
+	public static function cached_rss_display($rss_id, $url)
2348
+	{
2349
+		$loading   = '<p class="widget-loading hide-if-no-js">'
2350
+					 . esc_html__('Loading&#8230;', 'event_espresso')
2351
+					 . '</p><p class="hide-if-js">'
2352
+					 . esc_html__('This widget requires JavaScript.', 'event_espresso')
2353
+					 . '</p>';
2354
+		$pre       = '<div class="espresso-rss-display">' . "\n\t";
2355
+		$pre       .= '<span id="' . esc_attr($rss_id) . '_url" class="hidden">' . esc_url_raw($url) . '</span>';
2356
+		$post      = '</div>' . "\n";
2357
+		$cache_key = 'ee_rss_' . md5($rss_id);
2358
+		$output    = get_transient($cache_key);
2359
+		if ($output !== false) {
2360
+			echo wp_kses($pre . $output . $post, AllowedTags::getWithFormTags());
2361
+			return true;
2362
+		}
2363
+		if (! (defined('DOING_AJAX') && DOING_AJAX)) {
2364
+			echo wp_kses($pre . $loading . $post, AllowedTags::getWithFormTags());
2365
+			return false;
2366
+		}
2367
+		ob_start();
2368
+		wp_widget_rss_output($url, ['show_date' => 0, 'items' => 5]);
2369
+		set_transient($cache_key, ob_get_flush(), 12 * HOUR_IN_SECONDS);
2370
+		return true;
2371
+	}
2372
+
2373
+
2374
+	public function espresso_news_post_box()
2375
+	{
2376
+		?>
2377 2377
         <div class="padding">
2378 2378
             <div id="espresso_news_post_box_content" class="infolinks">
2379 2379
                 <?php
2380
-                // Get RSS Feed(s)
2381
-                EE_Admin_Page::cached_rss_display(
2382
-                    'espresso_news_post_box_content',
2383
-                    esc_url_raw(
2384
-                        apply_filters(
2385
-                            'FHEE__EE_Admin_Page__espresso_news_post_box__feed_url',
2386
-                            'https://eventespresso.com/feed/'
2387
-                        )
2388
-                    )
2389
-                );
2390
-                ?>
2380
+				// Get RSS Feed(s)
2381
+				EE_Admin_Page::cached_rss_display(
2382
+					'espresso_news_post_box_content',
2383
+					esc_url_raw(
2384
+						apply_filters(
2385
+							'FHEE__EE_Admin_Page__espresso_news_post_box__feed_url',
2386
+							'https://eventespresso.com/feed/'
2387
+						)
2388
+					)
2389
+				);
2390
+				?>
2391 2391
             </div>
2392 2392
             <?php do_action('AHEE__EE_Admin_Page__espresso_news_post_box__after_content'); ?>
2393 2393
         </div>
2394 2394
         <?php
2395
-    }
2396
-
2397
-
2398
-    private function _espresso_links_post_box()
2399
-    {
2400
-        // Hiding until we actually have content to put in here...
2401
-        // $this->addMetaBox('espresso_links_post_box', esc_html__('Helpful Plugin Links', 'event_espresso'), array( $this, 'espresso_links_post_box'), $this->_wp_page_slug, 'side');
2402
-    }
2403
-
2404
-
2405
-    public function espresso_links_post_box()
2406
-    {
2407
-        // Hiding until we actually have content to put in here...
2408
-        // EEH_Template::display_template(
2409
-        //     EE_ADMIN_TEMPLATE . 'admin_general_metabox_contents_espresso_links.template.php'
2410
-        // );
2411
-    }
2412
-
2413
-
2414
-    protected function _espresso_sponsors_post_box()
2415
-    {
2416
-        if (apply_filters('FHEE_show_sponsors_meta_box', true)) {
2417
-            $this->addMetaBox(
2418
-                'espresso_sponsors_post_box',
2419
-                esc_html__('Event Espresso Highlights', 'event_espresso'),
2420
-                [$this, 'espresso_sponsors_post_box'],
2421
-                $this->_wp_page_slug,
2422
-                'side'
2423
-            );
2424
-        }
2425
-    }
2426
-
2427
-
2428
-    public function espresso_sponsors_post_box()
2429
-    {
2430
-        EEH_Template::display_template(
2431
-            EE_ADMIN_TEMPLATE . 'admin_general_metabox_contents_espresso_sponsors.template.php'
2432
-        );
2433
-    }
2434
-
2435
-
2436
-    private function _publish_post_box()
2437
-    {
2438
-        $meta_box_ref = 'espresso_' . $this->page_slug . '_editor_overview';
2439
-        // if there is a array('label' => array('publishbox' => 'some title') ) present in the _page_config array
2440
-        // then we'll use that for the metabox label.
2441
-        // Otherwise we'll just use publish (publishbox itself could be an array of labels indexed by routes)
2442
-        if (! empty($this->_labels['publishbox'])) {
2443
-            $box_label = is_array($this->_labels['publishbox']) ? $this->_labels['publishbox'][ $this->_req_action ]
2444
-                : $this->_labels['publishbox'];
2445
-        } else {
2446
-            $box_label = esc_html__('Publish', 'event_espresso');
2447
-        }
2448
-        $box_label = apply_filters(
2449
-            'FHEE__EE_Admin_Page___publish_post_box__box_label',
2450
-            $box_label,
2451
-            $this->_req_action,
2452
-            $this
2453
-        );
2454
-        $this->addMetaBox(
2455
-            $meta_box_ref,
2456
-            $box_label,
2457
-            [$this, 'editor_overview'],
2458
-            $this->_current_screen->id,
2459
-            'side',
2460
-            'high'
2461
-        );
2462
-    }
2463
-
2464
-
2465
-    public function editor_overview()
2466
-    {
2467
-        // if we have extra content set let's add it in if not make sure its empty
2468
-        $this->_template_args['publish_box_extra_content'] = isset($this->_template_args['publish_box_extra_content'])
2469
-            ? $this->_template_args['publish_box_extra_content']
2470
-            : '';
2471
-        echo EEH_Template::display_template(
2472
-            EE_ADMIN_TEMPLATE . 'admin_details_publish_metabox.template.php',
2473
-            $this->_template_args,
2474
-            true
2475
-        );
2476
-    }
2477
-
2478
-
2479
-    /** end of globally available metaboxes section **/
2480
-
2481
-
2482
-    /**
2483
-     * Public wrapper for the protected method.  Allows plugins/addons to externally call the
2484
-     * protected method.
2485
-     *
2486
-     * @param string $name
2487
-     * @param int    $id
2488
-     * @param bool   $delete
2489
-     * @param string $save_close_redirect_URL
2490
-     * @param bool   $both_btns
2491
-     * @throws EE_Error
2492
-     * @throws InvalidArgumentException
2493
-     * @throws InvalidDataTypeException
2494
-     * @throws InvalidInterfaceException
2495
-     * @see   $this->_set_publish_post_box_vars for param details
2496
-     * @since 4.6.0
2497
-     */
2498
-    public function set_publish_post_box_vars(
2499
-        $name = '',
2500
-        $id = 0,
2501
-        $delete = false,
2502
-        $save_close_redirect_URL = '',
2503
-        $both_btns = true
2504
-    ) {
2505
-        $this->_set_publish_post_box_vars(
2506
-            $name,
2507
-            $id,
2508
-            $delete,
2509
-            $save_close_redirect_URL,
2510
-            $both_btns
2511
-        );
2512
-    }
2513
-
2514
-
2515
-    /**
2516
-     * Sets the _template_args arguments used by the _publish_post_box shortcut
2517
-     * Note: currently there is no validation for this.  However if you want the delete button, the
2518
-     * save, and save and close buttons to work properly, then you will want to include a
2519
-     * values for the name and id arguments.
2520
-     *
2521
-     * @param string  $name                       key used for the action ID (i.e. event_id)
2522
-     * @param int     $id                         id attached to the item published
2523
-     * @param string  $delete                     page route callback for the delete action
2524
-     * @param string  $save_close_redirect_URL    custom URL to redirect to after Save & Close has been completed
2525
-     * @param boolean $both_btns                  whether to display BOTH the "Save & Close" and "Save" buttons or just
2526
-     *                                            the Save button
2527
-     * @throws EE_Error
2528
-     * @throws InvalidArgumentException
2529
-     * @throws InvalidDataTypeException
2530
-     * @throws InvalidInterfaceException
2531
-     * @todo  Add in validation for name/id arguments.
2532
-     */
2533
-    protected function _set_publish_post_box_vars(
2534
-        $name = '',
2535
-        $id = 0,
2536
-        $delete = '',
2537
-        $save_close_redirect_URL = '',
2538
-        $both_btns = true
2539
-    ) {
2540
-        // if Save & Close, use a custom redirect URL or default to the main page?
2541
-        $save_close_redirect_URL = ! empty($save_close_redirect_URL)
2542
-            ? $save_close_redirect_URL
2543
-            : $this->_admin_base_url;
2544
-        // create the Save & Close and Save buttons
2545
-        $this->_set_save_buttons($both_btns, [], [], $save_close_redirect_URL);
2546
-        // if we have extra content set let's add it in if not make sure its empty
2547
-        $this->_template_args['publish_box_extra_content'] = $this->_template_args['publish_box_extra_content'] ?? '';
2548
-        $delete_link = '';
2549
-        if ($delete && ! empty($id)) {
2550
-            // make sure we have a default if just true is sent.
2551
-            $delete           = ! empty($delete) ? $delete : 'delete';
2552
-            $delete_link      = $this->get_action_link_or_button(
2553
-                $delete,
2554
-                $delete,
2555
-                [$name => $id],
2556
-                'submitdelete deletion button button--outline button--caution'
2557
-            );
2558
-        }
2559
-        $this->_template_args['publish_delete_link'] = $delete_link;
2560
-        if (! empty($name) && ! empty($id)) {
2561
-            $hidden_field_arr[ $name ] = [
2562
-                'type'  => 'hidden',
2563
-                'value' => $id,
2564
-            ];
2565
-            $hf                        = $this->_generate_admin_form_fields($hidden_field_arr, 'array');
2566
-        } else {
2567
-            $hf = '';
2568
-        }
2569
-        // add hidden field
2570
-        $this->_template_args['publish_hidden_fields'] = is_array($hf) && ! empty($name)
2571
-            ? $hf[ $name ]['field']
2572
-            : $hf;
2573
-    }
2574
-
2575
-
2576
-    /**
2577
-     * displays an error message to ppl who have javascript disabled
2578
-     *
2579
-     * @return void
2580
-     */
2581
-    private function _display_no_javascript_warning()
2582
-    {
2583
-        ?>
2395
+	}
2396
+
2397
+
2398
+	private function _espresso_links_post_box()
2399
+	{
2400
+		// Hiding until we actually have content to put in here...
2401
+		// $this->addMetaBox('espresso_links_post_box', esc_html__('Helpful Plugin Links', 'event_espresso'), array( $this, 'espresso_links_post_box'), $this->_wp_page_slug, 'side');
2402
+	}
2403
+
2404
+
2405
+	public function espresso_links_post_box()
2406
+	{
2407
+		// Hiding until we actually have content to put in here...
2408
+		// EEH_Template::display_template(
2409
+		//     EE_ADMIN_TEMPLATE . 'admin_general_metabox_contents_espresso_links.template.php'
2410
+		// );
2411
+	}
2412
+
2413
+
2414
+	protected function _espresso_sponsors_post_box()
2415
+	{
2416
+		if (apply_filters('FHEE_show_sponsors_meta_box', true)) {
2417
+			$this->addMetaBox(
2418
+				'espresso_sponsors_post_box',
2419
+				esc_html__('Event Espresso Highlights', 'event_espresso'),
2420
+				[$this, 'espresso_sponsors_post_box'],
2421
+				$this->_wp_page_slug,
2422
+				'side'
2423
+			);
2424
+		}
2425
+	}
2426
+
2427
+
2428
+	public function espresso_sponsors_post_box()
2429
+	{
2430
+		EEH_Template::display_template(
2431
+			EE_ADMIN_TEMPLATE . 'admin_general_metabox_contents_espresso_sponsors.template.php'
2432
+		);
2433
+	}
2434
+
2435
+
2436
+	private function _publish_post_box()
2437
+	{
2438
+		$meta_box_ref = 'espresso_' . $this->page_slug . '_editor_overview';
2439
+		// if there is a array('label' => array('publishbox' => 'some title') ) present in the _page_config array
2440
+		// then we'll use that for the metabox label.
2441
+		// Otherwise we'll just use publish (publishbox itself could be an array of labels indexed by routes)
2442
+		if (! empty($this->_labels['publishbox'])) {
2443
+			$box_label = is_array($this->_labels['publishbox']) ? $this->_labels['publishbox'][ $this->_req_action ]
2444
+				: $this->_labels['publishbox'];
2445
+		} else {
2446
+			$box_label = esc_html__('Publish', 'event_espresso');
2447
+		}
2448
+		$box_label = apply_filters(
2449
+			'FHEE__EE_Admin_Page___publish_post_box__box_label',
2450
+			$box_label,
2451
+			$this->_req_action,
2452
+			$this
2453
+		);
2454
+		$this->addMetaBox(
2455
+			$meta_box_ref,
2456
+			$box_label,
2457
+			[$this, 'editor_overview'],
2458
+			$this->_current_screen->id,
2459
+			'side',
2460
+			'high'
2461
+		);
2462
+	}
2463
+
2464
+
2465
+	public function editor_overview()
2466
+	{
2467
+		// if we have extra content set let's add it in if not make sure its empty
2468
+		$this->_template_args['publish_box_extra_content'] = isset($this->_template_args['publish_box_extra_content'])
2469
+			? $this->_template_args['publish_box_extra_content']
2470
+			: '';
2471
+		echo EEH_Template::display_template(
2472
+			EE_ADMIN_TEMPLATE . 'admin_details_publish_metabox.template.php',
2473
+			$this->_template_args,
2474
+			true
2475
+		);
2476
+	}
2477
+
2478
+
2479
+	/** end of globally available metaboxes section **/
2480
+
2481
+
2482
+	/**
2483
+	 * Public wrapper for the protected method.  Allows plugins/addons to externally call the
2484
+	 * protected method.
2485
+	 *
2486
+	 * @param string $name
2487
+	 * @param int    $id
2488
+	 * @param bool   $delete
2489
+	 * @param string $save_close_redirect_URL
2490
+	 * @param bool   $both_btns
2491
+	 * @throws EE_Error
2492
+	 * @throws InvalidArgumentException
2493
+	 * @throws InvalidDataTypeException
2494
+	 * @throws InvalidInterfaceException
2495
+	 * @see   $this->_set_publish_post_box_vars for param details
2496
+	 * @since 4.6.0
2497
+	 */
2498
+	public function set_publish_post_box_vars(
2499
+		$name = '',
2500
+		$id = 0,
2501
+		$delete = false,
2502
+		$save_close_redirect_URL = '',
2503
+		$both_btns = true
2504
+	) {
2505
+		$this->_set_publish_post_box_vars(
2506
+			$name,
2507
+			$id,
2508
+			$delete,
2509
+			$save_close_redirect_URL,
2510
+			$both_btns
2511
+		);
2512
+	}
2513
+
2514
+
2515
+	/**
2516
+	 * Sets the _template_args arguments used by the _publish_post_box shortcut
2517
+	 * Note: currently there is no validation for this.  However if you want the delete button, the
2518
+	 * save, and save and close buttons to work properly, then you will want to include a
2519
+	 * values for the name and id arguments.
2520
+	 *
2521
+	 * @param string  $name                       key used for the action ID (i.e. event_id)
2522
+	 * @param int     $id                         id attached to the item published
2523
+	 * @param string  $delete                     page route callback for the delete action
2524
+	 * @param string  $save_close_redirect_URL    custom URL to redirect to after Save & Close has been completed
2525
+	 * @param boolean $both_btns                  whether to display BOTH the "Save & Close" and "Save" buttons or just
2526
+	 *                                            the Save button
2527
+	 * @throws EE_Error
2528
+	 * @throws InvalidArgumentException
2529
+	 * @throws InvalidDataTypeException
2530
+	 * @throws InvalidInterfaceException
2531
+	 * @todo  Add in validation for name/id arguments.
2532
+	 */
2533
+	protected function _set_publish_post_box_vars(
2534
+		$name = '',
2535
+		$id = 0,
2536
+		$delete = '',
2537
+		$save_close_redirect_URL = '',
2538
+		$both_btns = true
2539
+	) {
2540
+		// if Save & Close, use a custom redirect URL or default to the main page?
2541
+		$save_close_redirect_URL = ! empty($save_close_redirect_URL)
2542
+			? $save_close_redirect_URL
2543
+			: $this->_admin_base_url;
2544
+		// create the Save & Close and Save buttons
2545
+		$this->_set_save_buttons($both_btns, [], [], $save_close_redirect_URL);
2546
+		// if we have extra content set let's add it in if not make sure its empty
2547
+		$this->_template_args['publish_box_extra_content'] = $this->_template_args['publish_box_extra_content'] ?? '';
2548
+		$delete_link = '';
2549
+		if ($delete && ! empty($id)) {
2550
+			// make sure we have a default if just true is sent.
2551
+			$delete           = ! empty($delete) ? $delete : 'delete';
2552
+			$delete_link      = $this->get_action_link_or_button(
2553
+				$delete,
2554
+				$delete,
2555
+				[$name => $id],
2556
+				'submitdelete deletion button button--outline button--caution'
2557
+			);
2558
+		}
2559
+		$this->_template_args['publish_delete_link'] = $delete_link;
2560
+		if (! empty($name) && ! empty($id)) {
2561
+			$hidden_field_arr[ $name ] = [
2562
+				'type'  => 'hidden',
2563
+				'value' => $id,
2564
+			];
2565
+			$hf                        = $this->_generate_admin_form_fields($hidden_field_arr, 'array');
2566
+		} else {
2567
+			$hf = '';
2568
+		}
2569
+		// add hidden field
2570
+		$this->_template_args['publish_hidden_fields'] = is_array($hf) && ! empty($name)
2571
+			? $hf[ $name ]['field']
2572
+			: $hf;
2573
+	}
2574
+
2575
+
2576
+	/**
2577
+	 * displays an error message to ppl who have javascript disabled
2578
+	 *
2579
+	 * @return void
2580
+	 */
2581
+	private function _display_no_javascript_warning()
2582
+	{
2583
+		?>
2584 2584
         <noscript>
2585 2585
             <div id="no-js-message" class="error">
2586 2586
                 <p style="font-size:1.3em;">
2587 2587
                     <span style="color:red;"><?php esc_html_e('Warning!', 'event_espresso'); ?></span>
2588 2588
                     <?php esc_html_e(
2589
-                        'Javascript is currently turned off for your browser. Javascript must be enabled in order for all of the features on this page to function properly. Please turn your javascript back on.',
2590
-                        'event_espresso'
2591
-                    ); ?>
2589
+						'Javascript is currently turned off for your browser. Javascript must be enabled in order for all of the features on this page to function properly. Please turn your javascript back on.',
2590
+						'event_espresso'
2591
+					); ?>
2592 2592
                 </p>
2593 2593
             </div>
2594 2594
         </noscript>
2595 2595
         <?php
2596
-    }
2597
-
2598
-
2599
-    /**
2600
-     * displays espresso success and/or error notices
2601
-     *
2602
-     * @return void
2603
-     */
2604
-    protected function _display_espresso_notices()
2605
-    {
2606
-        $notices = $this->_get_transient(true);
2607
-        echo stripslashes($notices);
2608
-    }
2609
-
2610
-
2611
-    /**
2612
-     * spinny things pacify the masses
2613
-     *
2614
-     * @return void
2615
-     */
2616
-    protected function _add_admin_page_ajax_loading_img()
2617
-    {
2618
-        ?>
2596
+	}
2597
+
2598
+
2599
+	/**
2600
+	 * displays espresso success and/or error notices
2601
+	 *
2602
+	 * @return void
2603
+	 */
2604
+	protected function _display_espresso_notices()
2605
+	{
2606
+		$notices = $this->_get_transient(true);
2607
+		echo stripslashes($notices);
2608
+	}
2609
+
2610
+
2611
+	/**
2612
+	 * spinny things pacify the masses
2613
+	 *
2614
+	 * @return void
2615
+	 */
2616
+	protected function _add_admin_page_ajax_loading_img()
2617
+	{
2618
+		?>
2619 2619
         <div id="espresso-ajax-loading" class="ajax-loading-grey">
2620 2620
             <span class="ee-spinner ee-spin"></span><span class="hidden"><?php
2621
-                esc_html_e('loading...', 'event_espresso'); ?></span>
2621
+				esc_html_e('loading...', 'event_espresso'); ?></span>
2622 2622
         </div>
2623 2623
         <?php
2624
-    }
2624
+	}
2625 2625
 
2626 2626
 
2627
-    /**
2628
-     * add admin page overlay for modal boxes
2629
-     *
2630
-     * @return void
2631
-     */
2632
-    protected function _add_admin_page_overlay()
2633
-    {
2634
-        ?>
2627
+	/**
2628
+	 * add admin page overlay for modal boxes
2629
+	 *
2630
+	 * @return void
2631
+	 */
2632
+	protected function _add_admin_page_overlay()
2633
+	{
2634
+		?>
2635 2635
         <div id="espresso-admin-page-overlay-dv" class=""></div>
2636 2636
         <?php
2637
-    }
2638
-
2639
-
2640
-    /**
2641
-     * facade for $this->addMetaBox()
2642
-     *
2643
-     * @param string  $action        where the metabox gets displayed
2644
-     * @param string  $title         Title of Metabox (output in metabox header)
2645
-     * @param string  $callback      If not empty and $create_fun is set to false then we'll use a custom callback
2646
-     *                               instead of the one created in here.
2647
-     * @param array   $callback_args an array of args supplied for the metabox
2648
-     * @param string  $column        what metabox column
2649
-     * @param string  $priority      give this metabox a priority (using accepted priorities for wp meta boxes)
2650
-     * @param boolean $create_func   default is true.  Basically we can say we don't WANT to have the runtime function
2651
-     *                               created but just set our own callback for wp's add_meta_box.
2652
-     * @throws DomainException
2653
-     */
2654
-    public function _add_admin_page_meta_box(
2655
-        $action,
2656
-        $title,
2657
-        $callback,
2658
-        $callback_args,
2659
-        $column = 'normal',
2660
-        $priority = 'high',
2661
-        $create_func = true
2662
-    ) {
2663
-        do_action('AHEE_log', __FILE__, __FUNCTION__, $callback);
2664
-        // if we have empty callback args and we want to automatically create the metabox callback then we need to make sure the callback args are generated.
2665
-        if (empty($callback_args) && $create_func) {
2666
-            $callback_args = [
2667
-                'template_path' => $this->_template_path,
2668
-                'template_args' => $this->_template_args,
2669
-            ];
2670
-        }
2671
-        // if $create_func is true (default) then we automatically create the function for displaying the actual meta box.  If false then we take the $callback reference passed through and use it instead (so callers can define their own callback function/method if they wish)
2672
-        $call_back_func = $create_func
2673
-            ? static function ($post, $metabox) {
2674
-                do_action('AHEE_log', __FILE__, __FUNCTION__, '');
2675
-                echo EEH_Template::display_template(
2676
-                    $metabox['args']['template_path'],
2677
-                    $metabox['args']['template_args'],
2678
-                    true
2679
-                );
2680
-            }
2681
-            : $callback;
2682
-        $this->addMetaBox(
2683
-            str_replace('_', '-', $action) . '-mbox',
2684
-            $title,
2685
-            $call_back_func,
2686
-            $this->_wp_page_slug,
2687
-            $column,
2688
-            $priority,
2689
-            $callback_args
2690
-        );
2691
-    }
2692
-
2693
-
2694
-    /**
2695
-     * generates HTML wrapper for and admin details page that contains metaboxes in columns
2696
-     *
2697
-     * @throws DomainException
2698
-     * @throws EE_Error
2699
-     * @throws InvalidArgumentException
2700
-     * @throws InvalidDataTypeException
2701
-     * @throws InvalidInterfaceException
2702
-     */
2703
-    public function display_admin_page_with_metabox_columns()
2704
-    {
2705
-        $this->_template_args['post_body_content']  = $this->_template_args['admin_page_content'];
2706
-        $this->_template_args['admin_page_content'] = EEH_Template::display_template(
2707
-            $this->_column_template_path,
2708
-            $this->_template_args,
2709
-            true
2710
-        );
2711
-        // the final wrapper
2712
-        $this->admin_page_wrapper();
2713
-    }
2714
-
2715
-
2716
-    /**
2717
-     * generates  HTML wrapper for an admin details page
2718
-     *
2719
-     * @return void
2720
-     * @throws DomainException
2721
-     * @throws EE_Error
2722
-     * @throws InvalidArgumentException
2723
-     * @throws InvalidDataTypeException
2724
-     * @throws InvalidInterfaceException
2725
-     */
2726
-    public function display_admin_page_with_sidebar()
2727
-    {
2728
-        $this->_display_admin_page(true);
2729
-    }
2730
-
2731
-
2732
-    /**
2733
-     * generates  HTML wrapper for an admin details page (except no sidebar)
2734
-     *
2735
-     * @return void
2736
-     * @throws DomainException
2737
-     * @throws EE_Error
2738
-     * @throws InvalidArgumentException
2739
-     * @throws InvalidDataTypeException
2740
-     * @throws InvalidInterfaceException
2741
-     */
2742
-    public function display_admin_page_with_no_sidebar()
2743
-    {
2744
-        $this->_display_admin_page();
2745
-    }
2746
-
2747
-
2748
-    /**
2749
-     * generates HTML wrapper for an EE about admin page (no sidebar)
2750
-     *
2751
-     * @return void
2752
-     * @throws DomainException
2753
-     * @throws EE_Error
2754
-     * @throws InvalidArgumentException
2755
-     * @throws InvalidDataTypeException
2756
-     * @throws InvalidInterfaceException
2757
-     */
2758
-    public function display_about_admin_page()
2759
-    {
2760
-        $this->_display_admin_page(false, true);
2761
-    }
2762
-
2763
-
2764
-    /**
2765
-     * display_admin_page
2766
-     * contains the code for actually displaying an admin page
2767
-     *
2768
-     * @param boolean $sidebar true with sidebar, false without
2769
-     * @param boolean $about   use the about admin wrapper instead of the default.
2770
-     * @return void
2771
-     * @throws DomainException
2772
-     * @throws EE_Error
2773
-     * @throws InvalidArgumentException
2774
-     * @throws InvalidDataTypeException
2775
-     * @throws InvalidInterfaceException
2776
-     */
2777
-    private function _display_admin_page($sidebar = false, $about = false)
2778
-    {
2779
-        do_action('AHEE_log', __FILE__, __FUNCTION__, '');
2780
-        // custom remove metaboxes hook to add or remove any metaboxes to/from Admin pages.
2781
-        do_action('AHEE__EE_Admin_Page___display_admin_page__modify_metaboxes');
2782
-        // set current wp page slug - looks like: event-espresso_page_event_categories
2783
-        // keep in mind "event-espresso" COULD be something else if the top level menu label has been translated.
2784
-
2785
-        $post_body_content = $this->_template_args['before_admin_page_content'] ?? '';
2786
-
2787
-        $this->_template_args['add_page_frame'] = $this->_req_action !== 'system_status'
2788
-                                                 && $this->_req_action !== 'data_reset'
2789
-                                                 && $this->_wp_page_slug !== 'event-espresso_page_espresso_packages'
2790
-                                                 && strpos($post_body_content, 'wp-list-table') === false;
2791
-
2792
-        $this->_template_args['current_page']              = $this->_wp_page_slug;
2793
-        $this->_template_args['admin_page_wrapper_div_id'] = $this->_cpt_route
2794
-            ? 'poststuff'
2795
-            : 'espresso-default-admin';
2796
-        $this->_template_args['admin_page_wrapper_div_class'] = str_replace(
2797
-            'event-espresso_page_espresso_',
2798
-            '',
2799
-            $this->_wp_page_slug
2800
-        ) . ' ' . $this->_req_action . '-route';
2801
-
2802
-        $template_path = $sidebar
2803
-            ? EE_ADMIN_TEMPLATE . 'admin_details_wrapper.template.php'
2804
-            : EE_ADMIN_TEMPLATE . 'admin_details_wrapper_no_sidebar.template.php';
2805
-        if ($this->request->isAjax()) {
2806
-            $template_path = EE_ADMIN_TEMPLATE . 'admin_details_wrapper_no_sidebar_ajax.template.php';
2807
-        }
2808
-        $template_path = ! empty($this->_column_template_path) ? $this->_column_template_path : $template_path;
2809
-
2810
-        $this->_template_args['post_body_content']         = $this->_template_args['admin_page_content'] ?? '';
2811
-        $this->_template_args['before_admin_page_content'] = $post_body_content;
2812
-        $this->_template_args['after_admin_page_content']  = $this->_template_args['after_admin_page_content'] ?? '';
2813
-        $this->_template_args['admin_page_content']        = EEH_Template::display_template(
2814
-            $template_path,
2815
-            $this->_template_args,
2816
-            true
2817
-        );
2818
-        // the final template wrapper
2819
-        $this->admin_page_wrapper($about);
2820
-    }
2821
-
2822
-
2823
-    /**
2824
-     * This is used to display caf preview pages.
2825
-     *
2826
-     * @param string $utm_campaign_source what is the key used for google analytics link
2827
-     * @param bool   $display_sidebar     whether to use the sidebar template or the full template for the page.  TRUE
2828
-     *                                    = SHOW sidebar, FALSE = no sidebar. Default no sidebar.
2829
-     * @return void
2830
-     * @throws DomainException
2831
-     * @throws EE_Error
2832
-     * @throws InvalidArgumentException
2833
-     * @throws InvalidDataTypeException
2834
-     * @throws InvalidInterfaceException
2835
-     * @since 4.3.2
2836
-     */
2837
-    public function display_admin_caf_preview_page($utm_campaign_source = '', $display_sidebar = true)
2838
-    {
2839
-        // let's generate a default preview action button if there isn't one already present.
2840
-        $this->_labels['buttons']['buy_now']           = esc_html__(
2841
-            'Upgrade to Event Espresso 4 Right Now',
2842
-            'event_espresso'
2843
-        );
2844
-        $buy_now_url                                   = add_query_arg(
2845
-            [
2846
-                'ee_ver'       => 'ee4',
2847
-                'utm_source'   => 'ee4_plugin_admin',
2848
-                'utm_medium'   => 'link',
2849
-                'utm_campaign' => $utm_campaign_source,
2850
-                'utm_content'  => 'buy_now_button',
2851
-            ],
2852
-            'https://eventespresso.com/pricing/'
2853
-        );
2854
-        $this->_template_args['preview_action_button'] = ! isset($this->_template_args['preview_action_button'])
2855
-            ? $this->get_action_link_or_button(
2856
-                '',
2857
-                'buy_now',
2858
-                [],
2859
-                'button button--primary button--big',
2860
-                esc_url_raw($buy_now_url),
2861
-                true
2862
-            )
2863
-            : $this->_template_args['preview_action_button'];
2864
-        $this->_template_args['admin_page_content']    = EEH_Template::display_template(
2865
-            EE_ADMIN_TEMPLATE . 'admin_caf_full_page_preview.template.php',
2866
-            $this->_template_args,
2867
-            true
2868
-        );
2869
-        $this->_display_admin_page($display_sidebar);
2870
-    }
2871
-
2872
-
2873
-    /**
2874
-     * display_admin_list_table_page_with_sidebar
2875
-     * generates HTML wrapper for an admin_page with list_table
2876
-     *
2877
-     * @return void
2878
-     * @throws DomainException
2879
-     * @throws EE_Error
2880
-     * @throws InvalidArgumentException
2881
-     * @throws InvalidDataTypeException
2882
-     * @throws InvalidInterfaceException
2883
-     */
2884
-    public function display_admin_list_table_page_with_sidebar()
2885
-    {
2886
-        $this->_display_admin_list_table_page(true);
2887
-    }
2888
-
2889
-
2890
-    /**
2891
-     * display_admin_list_table_page_with_no_sidebar
2892
-     * generates HTML wrapper for an admin_page with list_table (but with no sidebar)
2893
-     *
2894
-     * @return void
2895
-     * @throws DomainException
2896
-     * @throws EE_Error
2897
-     * @throws InvalidArgumentException
2898
-     * @throws InvalidDataTypeException
2899
-     * @throws InvalidInterfaceException
2900
-     */
2901
-    public function display_admin_list_table_page_with_no_sidebar()
2902
-    {
2903
-        $this->_display_admin_list_table_page();
2904
-    }
2905
-
2906
-
2907
-    /**
2908
-     * generates html wrapper for an admin_list_table page
2909
-     *
2910
-     * @param boolean $sidebar whether to display with sidebar or not.
2911
-     * @return void
2912
-     * @throws DomainException
2913
-     * @throws EE_Error
2914
-     * @throws InvalidArgumentException
2915
-     * @throws InvalidDataTypeException
2916
-     * @throws InvalidInterfaceException
2917
-     */
2918
-    private function _display_admin_list_table_page($sidebar = false)
2919
-    {
2920
-        // setup search attributes
2921
-        $this->_set_search_attributes();
2922
-        $this->_template_args['current_page']     = $this->_wp_page_slug;
2923
-        $template_path                            = EE_ADMIN_TEMPLATE . 'admin_list_wrapper.template.php';
2924
-        $this->_template_args['table_url']        = $this->request->isAjax()
2925
-            ? add_query_arg(['noheader' => 'true', 'route' => $this->_req_action], $this->_admin_base_url)
2926
-            : add_query_arg(['route' => $this->_req_action], $this->_admin_base_url);
2927
-        $this->_template_args['list_table']       = $this->_list_table_object;
2928
-        $this->_template_args['current_route']    = $this->_req_action;
2929
-        $this->_template_args['list_table_class'] = get_class($this->_list_table_object);
2930
-        $ajax_sorting_callback                    = $this->_list_table_object->get_ajax_sorting_callback();
2931
-        if (! empty($ajax_sorting_callback)) {
2932
-            $sortable_list_table_form_fields = wp_nonce_field(
2933
-                $ajax_sorting_callback . '_nonce',
2934
-                $ajax_sorting_callback . '_nonce',
2935
-                false,
2936
-                false
2937
-            );
2938
-            $sortable_list_table_form_fields .= '<input type="hidden" id="ajax_table_sort_page" name="ajax_table_sort_page" value="'
2939
-                                                . $this->page_slug
2940
-                                                . '" />';
2941
-            $sortable_list_table_form_fields .= '<input type="hidden" id="ajax_table_sort_action" name="ajax_table_sort_action" value="'
2942
-                                                . $ajax_sorting_callback
2943
-                                                . '" />';
2944
-        } else {
2945
-            $sortable_list_table_form_fields = '';
2946
-        }
2947
-        $this->_template_args['sortable_list_table_form_fields'] = $sortable_list_table_form_fields;
2948
-
2949
-        $hidden_form_fields = $this->_template_args['list_table_hidden_fields'] ?? '';
2950
-
2951
-        $nonce_ref          = $this->_req_action . '_nonce';
2952
-        $hidden_form_fields .= '
2637
+	}
2638
+
2639
+
2640
+	/**
2641
+	 * facade for $this->addMetaBox()
2642
+	 *
2643
+	 * @param string  $action        where the metabox gets displayed
2644
+	 * @param string  $title         Title of Metabox (output in metabox header)
2645
+	 * @param string  $callback      If not empty and $create_fun is set to false then we'll use a custom callback
2646
+	 *                               instead of the one created in here.
2647
+	 * @param array   $callback_args an array of args supplied for the metabox
2648
+	 * @param string  $column        what metabox column
2649
+	 * @param string  $priority      give this metabox a priority (using accepted priorities for wp meta boxes)
2650
+	 * @param boolean $create_func   default is true.  Basically we can say we don't WANT to have the runtime function
2651
+	 *                               created but just set our own callback for wp's add_meta_box.
2652
+	 * @throws DomainException
2653
+	 */
2654
+	public function _add_admin_page_meta_box(
2655
+		$action,
2656
+		$title,
2657
+		$callback,
2658
+		$callback_args,
2659
+		$column = 'normal',
2660
+		$priority = 'high',
2661
+		$create_func = true
2662
+	) {
2663
+		do_action('AHEE_log', __FILE__, __FUNCTION__, $callback);
2664
+		// if we have empty callback args and we want to automatically create the metabox callback then we need to make sure the callback args are generated.
2665
+		if (empty($callback_args) && $create_func) {
2666
+			$callback_args = [
2667
+				'template_path' => $this->_template_path,
2668
+				'template_args' => $this->_template_args,
2669
+			];
2670
+		}
2671
+		// if $create_func is true (default) then we automatically create the function for displaying the actual meta box.  If false then we take the $callback reference passed through and use it instead (so callers can define their own callback function/method if they wish)
2672
+		$call_back_func = $create_func
2673
+			? static function ($post, $metabox) {
2674
+				do_action('AHEE_log', __FILE__, __FUNCTION__, '');
2675
+				echo EEH_Template::display_template(
2676
+					$metabox['args']['template_path'],
2677
+					$metabox['args']['template_args'],
2678
+					true
2679
+				);
2680
+			}
2681
+			: $callback;
2682
+		$this->addMetaBox(
2683
+			str_replace('_', '-', $action) . '-mbox',
2684
+			$title,
2685
+			$call_back_func,
2686
+			$this->_wp_page_slug,
2687
+			$column,
2688
+			$priority,
2689
+			$callback_args
2690
+		);
2691
+	}
2692
+
2693
+
2694
+	/**
2695
+	 * generates HTML wrapper for and admin details page that contains metaboxes in columns
2696
+	 *
2697
+	 * @throws DomainException
2698
+	 * @throws EE_Error
2699
+	 * @throws InvalidArgumentException
2700
+	 * @throws InvalidDataTypeException
2701
+	 * @throws InvalidInterfaceException
2702
+	 */
2703
+	public function display_admin_page_with_metabox_columns()
2704
+	{
2705
+		$this->_template_args['post_body_content']  = $this->_template_args['admin_page_content'];
2706
+		$this->_template_args['admin_page_content'] = EEH_Template::display_template(
2707
+			$this->_column_template_path,
2708
+			$this->_template_args,
2709
+			true
2710
+		);
2711
+		// the final wrapper
2712
+		$this->admin_page_wrapper();
2713
+	}
2714
+
2715
+
2716
+	/**
2717
+	 * generates  HTML wrapper for an admin details page
2718
+	 *
2719
+	 * @return void
2720
+	 * @throws DomainException
2721
+	 * @throws EE_Error
2722
+	 * @throws InvalidArgumentException
2723
+	 * @throws InvalidDataTypeException
2724
+	 * @throws InvalidInterfaceException
2725
+	 */
2726
+	public function display_admin_page_with_sidebar()
2727
+	{
2728
+		$this->_display_admin_page(true);
2729
+	}
2730
+
2731
+
2732
+	/**
2733
+	 * generates  HTML wrapper for an admin details page (except no sidebar)
2734
+	 *
2735
+	 * @return void
2736
+	 * @throws DomainException
2737
+	 * @throws EE_Error
2738
+	 * @throws InvalidArgumentException
2739
+	 * @throws InvalidDataTypeException
2740
+	 * @throws InvalidInterfaceException
2741
+	 */
2742
+	public function display_admin_page_with_no_sidebar()
2743
+	{
2744
+		$this->_display_admin_page();
2745
+	}
2746
+
2747
+
2748
+	/**
2749
+	 * generates HTML wrapper for an EE about admin page (no sidebar)
2750
+	 *
2751
+	 * @return void
2752
+	 * @throws DomainException
2753
+	 * @throws EE_Error
2754
+	 * @throws InvalidArgumentException
2755
+	 * @throws InvalidDataTypeException
2756
+	 * @throws InvalidInterfaceException
2757
+	 */
2758
+	public function display_about_admin_page()
2759
+	{
2760
+		$this->_display_admin_page(false, true);
2761
+	}
2762
+
2763
+
2764
+	/**
2765
+	 * display_admin_page
2766
+	 * contains the code for actually displaying an admin page
2767
+	 *
2768
+	 * @param boolean $sidebar true with sidebar, false without
2769
+	 * @param boolean $about   use the about admin wrapper instead of the default.
2770
+	 * @return void
2771
+	 * @throws DomainException
2772
+	 * @throws EE_Error
2773
+	 * @throws InvalidArgumentException
2774
+	 * @throws InvalidDataTypeException
2775
+	 * @throws InvalidInterfaceException
2776
+	 */
2777
+	private function _display_admin_page($sidebar = false, $about = false)
2778
+	{
2779
+		do_action('AHEE_log', __FILE__, __FUNCTION__, '');
2780
+		// custom remove metaboxes hook to add or remove any metaboxes to/from Admin pages.
2781
+		do_action('AHEE__EE_Admin_Page___display_admin_page__modify_metaboxes');
2782
+		// set current wp page slug - looks like: event-espresso_page_event_categories
2783
+		// keep in mind "event-espresso" COULD be something else if the top level menu label has been translated.
2784
+
2785
+		$post_body_content = $this->_template_args['before_admin_page_content'] ?? '';
2786
+
2787
+		$this->_template_args['add_page_frame'] = $this->_req_action !== 'system_status'
2788
+												 && $this->_req_action !== 'data_reset'
2789
+												 && $this->_wp_page_slug !== 'event-espresso_page_espresso_packages'
2790
+												 && strpos($post_body_content, 'wp-list-table') === false;
2791
+
2792
+		$this->_template_args['current_page']              = $this->_wp_page_slug;
2793
+		$this->_template_args['admin_page_wrapper_div_id'] = $this->_cpt_route
2794
+			? 'poststuff'
2795
+			: 'espresso-default-admin';
2796
+		$this->_template_args['admin_page_wrapper_div_class'] = str_replace(
2797
+			'event-espresso_page_espresso_',
2798
+			'',
2799
+			$this->_wp_page_slug
2800
+		) . ' ' . $this->_req_action . '-route';
2801
+
2802
+		$template_path = $sidebar
2803
+			? EE_ADMIN_TEMPLATE . 'admin_details_wrapper.template.php'
2804
+			: EE_ADMIN_TEMPLATE . 'admin_details_wrapper_no_sidebar.template.php';
2805
+		if ($this->request->isAjax()) {
2806
+			$template_path = EE_ADMIN_TEMPLATE . 'admin_details_wrapper_no_sidebar_ajax.template.php';
2807
+		}
2808
+		$template_path = ! empty($this->_column_template_path) ? $this->_column_template_path : $template_path;
2809
+
2810
+		$this->_template_args['post_body_content']         = $this->_template_args['admin_page_content'] ?? '';
2811
+		$this->_template_args['before_admin_page_content'] = $post_body_content;
2812
+		$this->_template_args['after_admin_page_content']  = $this->_template_args['after_admin_page_content'] ?? '';
2813
+		$this->_template_args['admin_page_content']        = EEH_Template::display_template(
2814
+			$template_path,
2815
+			$this->_template_args,
2816
+			true
2817
+		);
2818
+		// the final template wrapper
2819
+		$this->admin_page_wrapper($about);
2820
+	}
2821
+
2822
+
2823
+	/**
2824
+	 * This is used to display caf preview pages.
2825
+	 *
2826
+	 * @param string $utm_campaign_source what is the key used for google analytics link
2827
+	 * @param bool   $display_sidebar     whether to use the sidebar template or the full template for the page.  TRUE
2828
+	 *                                    = SHOW sidebar, FALSE = no sidebar. Default no sidebar.
2829
+	 * @return void
2830
+	 * @throws DomainException
2831
+	 * @throws EE_Error
2832
+	 * @throws InvalidArgumentException
2833
+	 * @throws InvalidDataTypeException
2834
+	 * @throws InvalidInterfaceException
2835
+	 * @since 4.3.2
2836
+	 */
2837
+	public function display_admin_caf_preview_page($utm_campaign_source = '', $display_sidebar = true)
2838
+	{
2839
+		// let's generate a default preview action button if there isn't one already present.
2840
+		$this->_labels['buttons']['buy_now']           = esc_html__(
2841
+			'Upgrade to Event Espresso 4 Right Now',
2842
+			'event_espresso'
2843
+		);
2844
+		$buy_now_url                                   = add_query_arg(
2845
+			[
2846
+				'ee_ver'       => 'ee4',
2847
+				'utm_source'   => 'ee4_plugin_admin',
2848
+				'utm_medium'   => 'link',
2849
+				'utm_campaign' => $utm_campaign_source,
2850
+				'utm_content'  => 'buy_now_button',
2851
+			],
2852
+			'https://eventespresso.com/pricing/'
2853
+		);
2854
+		$this->_template_args['preview_action_button'] = ! isset($this->_template_args['preview_action_button'])
2855
+			? $this->get_action_link_or_button(
2856
+				'',
2857
+				'buy_now',
2858
+				[],
2859
+				'button button--primary button--big',
2860
+				esc_url_raw($buy_now_url),
2861
+				true
2862
+			)
2863
+			: $this->_template_args['preview_action_button'];
2864
+		$this->_template_args['admin_page_content']    = EEH_Template::display_template(
2865
+			EE_ADMIN_TEMPLATE . 'admin_caf_full_page_preview.template.php',
2866
+			$this->_template_args,
2867
+			true
2868
+		);
2869
+		$this->_display_admin_page($display_sidebar);
2870
+	}
2871
+
2872
+
2873
+	/**
2874
+	 * display_admin_list_table_page_with_sidebar
2875
+	 * generates HTML wrapper for an admin_page with list_table
2876
+	 *
2877
+	 * @return void
2878
+	 * @throws DomainException
2879
+	 * @throws EE_Error
2880
+	 * @throws InvalidArgumentException
2881
+	 * @throws InvalidDataTypeException
2882
+	 * @throws InvalidInterfaceException
2883
+	 */
2884
+	public function display_admin_list_table_page_with_sidebar()
2885
+	{
2886
+		$this->_display_admin_list_table_page(true);
2887
+	}
2888
+
2889
+
2890
+	/**
2891
+	 * display_admin_list_table_page_with_no_sidebar
2892
+	 * generates HTML wrapper for an admin_page with list_table (but with no sidebar)
2893
+	 *
2894
+	 * @return void
2895
+	 * @throws DomainException
2896
+	 * @throws EE_Error
2897
+	 * @throws InvalidArgumentException
2898
+	 * @throws InvalidDataTypeException
2899
+	 * @throws InvalidInterfaceException
2900
+	 */
2901
+	public function display_admin_list_table_page_with_no_sidebar()
2902
+	{
2903
+		$this->_display_admin_list_table_page();
2904
+	}
2905
+
2906
+
2907
+	/**
2908
+	 * generates html wrapper for an admin_list_table page
2909
+	 *
2910
+	 * @param boolean $sidebar whether to display with sidebar or not.
2911
+	 * @return void
2912
+	 * @throws DomainException
2913
+	 * @throws EE_Error
2914
+	 * @throws InvalidArgumentException
2915
+	 * @throws InvalidDataTypeException
2916
+	 * @throws InvalidInterfaceException
2917
+	 */
2918
+	private function _display_admin_list_table_page($sidebar = false)
2919
+	{
2920
+		// setup search attributes
2921
+		$this->_set_search_attributes();
2922
+		$this->_template_args['current_page']     = $this->_wp_page_slug;
2923
+		$template_path                            = EE_ADMIN_TEMPLATE . 'admin_list_wrapper.template.php';
2924
+		$this->_template_args['table_url']        = $this->request->isAjax()
2925
+			? add_query_arg(['noheader' => 'true', 'route' => $this->_req_action], $this->_admin_base_url)
2926
+			: add_query_arg(['route' => $this->_req_action], $this->_admin_base_url);
2927
+		$this->_template_args['list_table']       = $this->_list_table_object;
2928
+		$this->_template_args['current_route']    = $this->_req_action;
2929
+		$this->_template_args['list_table_class'] = get_class($this->_list_table_object);
2930
+		$ajax_sorting_callback                    = $this->_list_table_object->get_ajax_sorting_callback();
2931
+		if (! empty($ajax_sorting_callback)) {
2932
+			$sortable_list_table_form_fields = wp_nonce_field(
2933
+				$ajax_sorting_callback . '_nonce',
2934
+				$ajax_sorting_callback . '_nonce',
2935
+				false,
2936
+				false
2937
+			);
2938
+			$sortable_list_table_form_fields .= '<input type="hidden" id="ajax_table_sort_page" name="ajax_table_sort_page" value="'
2939
+												. $this->page_slug
2940
+												. '" />';
2941
+			$sortable_list_table_form_fields .= '<input type="hidden" id="ajax_table_sort_action" name="ajax_table_sort_action" value="'
2942
+												. $ajax_sorting_callback
2943
+												. '" />';
2944
+		} else {
2945
+			$sortable_list_table_form_fields = '';
2946
+		}
2947
+		$this->_template_args['sortable_list_table_form_fields'] = $sortable_list_table_form_fields;
2948
+
2949
+		$hidden_form_fields = $this->_template_args['list_table_hidden_fields'] ?? '';
2950
+
2951
+		$nonce_ref          = $this->_req_action . '_nonce';
2952
+		$hidden_form_fields .= '
2953 2953
             <input type="hidden" name="' . $nonce_ref . '" value="' . wp_create_nonce($nonce_ref) . '">';
2954 2954
 
2955
-        $this->_template_args['list_table_hidden_fields']        = $hidden_form_fields;
2956
-        // display message about search results?
2957
-        $search = $this->request->getRequestParam('s');
2958
-        $this->_template_args['before_list_table'] .= ! empty($search)
2959
-            ? '<p class="ee-search-results">' . sprintf(
2960
-                esc_html__('Displaying search results for the search string: %1$s', 'event_espresso'),
2961
-                trim($search, '%')
2962
-            ) . '</p>'
2963
-            : '';
2964
-        // filter before_list_table template arg
2965
-        $this->_template_args['before_list_table'] = apply_filters(
2966
-            'FHEE__EE_Admin_Page___display_admin_list_table_page__before_list_table__template_arg',
2967
-            $this->_template_args['before_list_table'],
2968
-            $this->page_slug,
2969
-            $this->request->requestParams(),
2970
-            $this->_req_action
2971
-        );
2972
-        // convert to array and filter again
2973
-        // arrays are easier to inject new items in a specific location,
2974
-        // but would not be backwards compatible, so we have to add a new filter
2975
-        $this->_template_args['before_list_table'] = implode(
2976
-            " \n",
2977
-            (array) apply_filters(
2978
-                'FHEE__EE_Admin_Page___display_admin_list_table_page__before_list_table__template_args_array',
2979
-                (array) $this->_template_args['before_list_table'],
2980
-                $this->page_slug,
2981
-                $this->request->requestParams(),
2982
-                $this->_req_action
2983
-            )
2984
-        );
2985
-        // filter after_list_table template arg
2986
-        $this->_template_args['after_list_table'] = apply_filters(
2987
-            'FHEE__EE_Admin_Page___display_admin_list_table_page__after_list_table__template_arg',
2988
-            $this->_template_args['after_list_table'],
2989
-            $this->page_slug,
2990
-            $this->request->requestParams(),
2991
-            $this->_req_action
2992
-        );
2993
-        // convert to array and filter again
2994
-        // arrays are easier to inject new items in a specific location,
2995
-        // but would not be backwards compatible, so we have to add a new filter
2996
-        $this->_template_args['after_list_table']   = implode(
2997
-            " \n",
2998
-            (array) apply_filters(
2999
-                'FHEE__EE_Admin_Page___display_admin_list_table_page__after_list_table__template_args_array',
3000
-                (array) $this->_template_args['after_list_table'],
3001
-                $this->page_slug,
3002
-                $this->request->requestParams(),
3003
-                $this->_req_action
3004
-            )
3005
-        );
3006
-        $this->_template_args['admin_page_content'] = EEH_Template::display_template(
3007
-            $template_path,
3008
-            $this->_template_args,
3009
-            true
3010
-        );
3011
-        // the final template wrapper
3012
-        if ($sidebar) {
3013
-            $this->display_admin_page_with_sidebar();
3014
-        } else {
3015
-            $this->display_admin_page_with_no_sidebar();
3016
-        }
3017
-    }
3018
-
3019
-
3020
-    /**
3021
-     * This just prepares a legend using the given items and the admin_details_legend.template.php file and returns the
3022
-     * html string for the legend.
3023
-     * $items are expected in an array in the following format:
3024
-     * $legend_items = array(
3025
-     *        'item_id' => array(
3026
-     *            'icon' => 'http://url_to_icon_being_described.png',
3027
-     *            'desc' => esc_html__('localized description of item');
3028
-     *        )
3029
-     * );
3030
-     *
3031
-     * @param array $items see above for format of array
3032
-     * @return string html string of legend
3033
-     * @throws DomainException
3034
-     */
3035
-    protected function _display_legend($items)
3036
-    {
3037
-        $this->_template_args['items'] = apply_filters(
3038
-            'FHEE__EE_Admin_Page___display_legend__items',
3039
-            (array) $items,
3040
-            $this
3041
-        );
3042
-        /** @var StatusChangeNotice $status_change_notice */
3043
-        $status_change_notice = $this->loader->getShared(
3044
-            'EventEspresso\core\domain\services\admin\notices\status_change\StatusChangeNotice'
3045
-        );
3046
-        $this->_template_args['status_change_notice'] = $status_change_notice->display(
3047
-            '__admin-legend',
3048
-            $this->page_slug
3049
-        );
3050
-        return EEH_Template::display_template(
3051
-            EE_ADMIN_TEMPLATE . 'admin_details_legend.template.php',
3052
-            $this->_template_args,
3053
-            true
3054
-        );
3055
-    }
3056
-
3057
-
3058
-    /**
3059
-     * This is used whenever we're DOING_AJAX to return a formatted json array that our calling javascript can expect
3060
-     * The returned json object is created from an array in the following format:
3061
-     * array(
3062
-     *  'error' => FALSE, //(default FALSE), contains any errors and/or exceptions (exceptions return json early),
3063
-     *  'success' => FALSE, //(default FALSE) - contains any special success message.
3064
-     *  'notices' => '', // - contains any EE_Error formatted notices
3065
-     *  'content' => 'string can be html', //this is a string of formatted content (can be html)
3066
-     *  'data' => array() //this can be any key/value pairs that a method returns for later json parsing by the js.
3067
-     *  We're also going to include the template args with every package (so js can pick out any specific template args
3068
-     *  that might be included in here)
3069
-     * )
3070
-     * The json object is populated by whatever is set in the $_template_args property.
3071
-     *
3072
-     * @param bool  $sticky_notices    Used to indicate whether you want to ensure notices are added to a transient
3073
-     *                                 instead of displayed.
3074
-     * @param array $notices_arguments Use this to pass any additional args on to the _process_notices.
3075
-     * @return void
3076
-     * @throws EE_Error
3077
-     * @throws InvalidArgumentException
3078
-     * @throws InvalidDataTypeException
3079
-     * @throws InvalidInterfaceException
3080
-     */
3081
-    protected function _return_json($sticky_notices = false, $notices_arguments = [])
3082
-    {
3083
-        // make sure any EE_Error notices have been handled.
3084
-        $this->_process_notices($notices_arguments, true, $sticky_notices);
3085
-        $data = isset($this->_template_args['data']) ? $this->_template_args['data'] : [];
3086
-        unset($this->_template_args['data']);
3087
-        $json = [
3088
-            'error'     => isset($this->_template_args['error']) ? $this->_template_args['error'] : false,
3089
-            'success'   => isset($this->_template_args['success']) ? $this->_template_args['success'] : false,
3090
-            'errors'    => isset($this->_template_args['errors']) ? $this->_template_args['errors'] : false,
3091
-            'attention' => isset($this->_template_args['attention']) ? $this->_template_args['attention'] : false,
3092
-            'notices'   => EE_Error::get_notices(),
3093
-            'content'   => isset($this->_template_args['admin_page_content'])
3094
-                ? $this->_template_args['admin_page_content'] : '',
3095
-            'data'      => array_merge($data, ['template_args' => $this->_template_args]),
3096
-            'isEEajax'  => true
3097
-            // special flag so any ajax.Success methods in js can identify this return package as a EEajax package.
3098
-        ];
3099
-        // make sure there are no php errors or headers_sent.  Then we can set correct json header.
3100
-        if (null === error_get_last() || ! headers_sent()) {
3101
-            header('Content-Type: application/json; charset=UTF-8');
3102
-        }
3103
-        echo wp_json_encode($json);
3104
-        exit();
3105
-    }
3106
-
3107
-
3108
-    /**
3109
-     * Simply a wrapper for the protected method so we can call this outside the class (ONLY when doing ajax)
3110
-     *
3111
-     * @return void
3112
-     * @throws EE_Error
3113
-     * @throws InvalidArgumentException
3114
-     * @throws InvalidDataTypeException
3115
-     * @throws InvalidInterfaceException
3116
-     */
3117
-    public function return_json()
3118
-    {
3119
-        if ($this->request->isAjax()) {
3120
-            $this->_return_json();
3121
-        } else {
3122
-            throw new EE_Error(
3123
-                sprintf(
3124
-                    esc_html__('The public %s method can only be called when DOING_AJAX = TRUE', 'event_espresso'),
3125
-                    __FUNCTION__
3126
-                )
3127
-            );
3128
-        }
3129
-    }
3130
-
3131
-
3132
-    /**
3133
-     * This provides a way for child hook classes to send along themselves by reference so methods/properties within
3134
-     * them can be accessed by EE_Admin_child pages. This is assigned to the $_hook_obj property.
3135
-     *
3136
-     * @param EE_Admin_Hooks $hook_obj This will be the object for the EE_Admin_Hooks child
3137
-     */
3138
-    public function set_hook_object(EE_Admin_Hooks $hook_obj)
3139
-    {
3140
-        $this->_hook_obj = $hook_obj;
3141
-    }
3142
-
3143
-
3144
-    /**
3145
-     *        generates  HTML wrapper with Tabbed nav for an admin page
3146
-     *
3147
-     * @param boolean $about whether to use the special about page wrapper or default.
3148
-     * @return void
3149
-     * @throws DomainException
3150
-     * @throws EE_Error
3151
-     * @throws InvalidArgumentException
3152
-     * @throws InvalidDataTypeException
3153
-     * @throws InvalidInterfaceException
3154
-     */
3155
-    public function admin_page_wrapper($about = false)
3156
-    {
3157
-        do_action('AHEE_log', __FILE__, __FUNCTION__, '');
3158
-        $this->_nav_tabs                                   = $this->_get_main_nav_tabs();
3159
-        $this->_template_args['nav_tabs']                  = $this->_nav_tabs;
3160
-        $this->_template_args['admin_page_title']          = $this->_admin_page_title;
3161
-
3162
-        $this->_template_args['before_admin_page_content'] = apply_filters(
3163
-            "FHEE_before_admin_page_content{$this->_current_page}{$this->_current_view}",
3164
-            $this->_template_args['before_admin_page_content'] ?? ''
3165
-        );
3166
-
3167
-        $this->_template_args['after_admin_page_content']  = apply_filters(
3168
-            "FHEE_after_admin_page_content{$this->_current_page}{$this->_current_view}",
3169
-            $this->_template_args['after_admin_page_content'] ?? ''
3170
-        );
3171
-        $this->_template_args['after_admin_page_content']  .= $this->_set_help_popup_content();
3172
-
3173
-        if ($this->request->isAjax()) {
3174
-            $this->_template_args['admin_page_content'] = EEH_Template::display_template(
3175
-                // $template_path,
3176
-                EE_ADMIN_TEMPLATE . 'admin_wrapper_ajax.template.php',
3177
-                $this->_template_args,
3178
-                true
3179
-            );
3180
-            $this->_return_json();
3181
-        }
3182
-        // load settings page wrapper template
3183
-        $template_path = $about
3184
-            ? EE_ADMIN_TEMPLATE . 'about_admin_wrapper.template.php'
3185
-            : EE_ADMIN_TEMPLATE . 'admin_wrapper.template.php';
3186
-
3187
-        EEH_Template::display_template($template_path, $this->_template_args);
3188
-    }
3189
-
3190
-
3191
-    /**
3192
-     * This returns the admin_nav tabs html using the configuration in the _nav_tabs property
3193
-     *
3194
-     * @return string html
3195
-     * @throws EE_Error
3196
-     */
3197
-    protected function _get_main_nav_tabs()
3198
-    {
3199
-        // let's generate the html using the EEH_Tabbed_Content helper.
3200
-        // We do this here so that it's possible for child classes to add in nav tabs dynamically at the last minute
3201
-        // (rather than setting in the page_routes array)
3202
-        return EEH_Tabbed_Content::display_admin_nav_tabs($this->_nav_tabs, $this->page_slug);
3203
-    }
3204
-
3205
-
3206
-    /**
3207
-     *        sort nav tabs
3208
-     *
3209
-     * @param $a
3210
-     * @param $b
3211
-     * @return int
3212
-     */
3213
-    private function _sort_nav_tabs($a, $b)
3214
-    {
3215
-        if ($a['order'] === $b['order']) {
3216
-            return 0;
3217
-        }
3218
-        return ($a['order'] < $b['order']) ? -1 : 1;
3219
-    }
3220
-
3221
-
3222
-    /**
3223
-     * generates HTML for the forms used on admin pages
3224
-     *
3225
-     * @param array  $input_vars - array of input field details
3226
-     * @param string $generator  indicates which generator to use: options are 'string' or 'array'
3227
-     * @param bool   $id
3228
-     * @return array|string
3229
-     * @uses   EEH_Form_Fields::get_form_fields (/helper/EEH_Form_Fields.helper.php)
3230
-     * @uses   EEH_Form_Fields::get_form_fields_array (/helper/EEH_Form_Fields.helper.php)
3231
-     */
3232
-    protected function _generate_admin_form_fields($input_vars = [], $generator = 'string', $id = false)
3233
-    {
3234
-        return $generator === 'string'
3235
-            ? EEH_Form_Fields::get_form_fields($input_vars, $id)
3236
-            : EEH_Form_Fields::get_form_fields_array($input_vars);
3237
-    }
3238
-
3239
-
3240
-    /**
3241
-     * generates the "Save" and "Save & Close" buttons for edit forms
3242
-     *
3243
-     * @param bool             $both     if true then both buttons will be generated.  If false then just the "Save &
3244
-     *                                   Close" button.
3245
-     * @param array            $text     if included, generator will use the given text for the buttons ( array([0] =>
3246
-     *                                   'Save', [1] => 'save & close')
3247
-     * @param array            $actions  if included allows us to set the actions that each button will carry out (i.e.
3248
-     *                                   via the "name" value in the button).  We can also use this to just dump
3249
-     *                                   default actions by submitting some other value.
3250
-     * @param bool|string|null $referrer if false then we just do the default action on save and close.  Other wise it
3251
-     *                                   will use the $referrer string. IF null, then we don't do ANYTHING on save and
3252
-     *                                   close (normal form handling).
3253
-     */
3254
-    protected function _set_save_buttons($both = true, $text = [], $actions = [], $referrer = null)
3255
-    {
3256
-        // make sure $text and $actions are in an array
3257
-        $text          = (array) $text;
3258
-        $actions       = (array) $actions;
3259
-        $referrer_url  = ! empty($referrer) ? $referrer : $this->request->getServerParam('REQUEST_URI');
3260
-        $button_text   = ! empty($text)
3261
-            ? $text
3262
-            : [
3263
-                esc_html__('Save', 'event_espresso'),
3264
-                esc_html__('Save and Close', 'event_espresso'),
3265
-            ];
3266
-        $default_names = ['save', 'save_and_close'];
3267
-        $buttons = '';
3268
-        foreach ($button_text as $key => $button) {
3269
-            $ref     = $default_names[ $key ];
3270
-            $name    = ! empty($actions) ? $actions[ $key ] : $ref;
3271
-            $buttons .= '<input type="submit" class="button button--primary ' . $ref . '" '
3272
-                        . 'value="' . $button . '" name="' . $name . '" '
3273
-                        . 'id="' . $this->_current_view . '_' . $ref . '" />';
3274
-            if (! $both) {
3275
-                break;
3276
-            }
3277
-        }
3278
-        // add in a hidden index for the current page (so save and close redirects properly)
3279
-        $buttons .= '<input type="hidden" id="save_and_close_referrer" name="save_and_close_referrer" value="'
3280
-                   . $referrer_url
3281
-                   . '" />';
3282
-        $this->_template_args['save_buttons'] = $buttons;
3283
-    }
3284
-
3285
-
3286
-    /**
3287
-     * Wrapper for the protected function.  Allows plugins/addons to call this to set the form tags.
3288
-     *
3289
-     * @param string $route
3290
-     * @param array  $additional_hidden_fields
3291
-     * @see   $this->_set_add_edit_form_tags() for details on params
3292
-     * @since 4.6.0
3293
-     */
3294
-    public function set_add_edit_form_tags($route = '', $additional_hidden_fields = [])
3295
-    {
3296
-        $this->_set_add_edit_form_tags($route, $additional_hidden_fields);
3297
-    }
3298
-
3299
-
3300
-    /**
3301
-     * set form open and close tags on add/edit pages.
3302
-     *
3303
-     * @param string $route                    the route you want the form to direct to
3304
-     * @param array  $additional_hidden_fields any additional hidden fields required in the form header
3305
-     * @return void
3306
-     */
3307
-    protected function _set_add_edit_form_tags($route = '', $additional_hidden_fields = [])
3308
-    {
3309
-        if (empty($route)) {
3310
-            $user_msg = esc_html__(
3311
-                'An error occurred. No action was set for this page\'s form.',
3312
-                'event_espresso'
3313
-            );
3314
-            $dev_msg  = $user_msg . "\n"
3315
-                        . sprintf(
3316
-                            esc_html__('The $route argument is required for the %s->%s method.', 'event_espresso'),
3317
-                            __FUNCTION__,
3318
-                            __CLASS__
3319
-                        );
3320
-            EE_Error::add_error($user_msg . '||' . $dev_msg, __FILE__, __FUNCTION__, __LINE__);
3321
-        }
3322
-        // open form
3323
-        $action = $this->_admin_base_url;
3324
-        $this->_template_args['before_admin_page_content'] = "
2955
+		$this->_template_args['list_table_hidden_fields']        = $hidden_form_fields;
2956
+		// display message about search results?
2957
+		$search = $this->request->getRequestParam('s');
2958
+		$this->_template_args['before_list_table'] .= ! empty($search)
2959
+			? '<p class="ee-search-results">' . sprintf(
2960
+				esc_html__('Displaying search results for the search string: %1$s', 'event_espresso'),
2961
+				trim($search, '%')
2962
+			) . '</p>'
2963
+			: '';
2964
+		// filter before_list_table template arg
2965
+		$this->_template_args['before_list_table'] = apply_filters(
2966
+			'FHEE__EE_Admin_Page___display_admin_list_table_page__before_list_table__template_arg',
2967
+			$this->_template_args['before_list_table'],
2968
+			$this->page_slug,
2969
+			$this->request->requestParams(),
2970
+			$this->_req_action
2971
+		);
2972
+		// convert to array and filter again
2973
+		// arrays are easier to inject new items in a specific location,
2974
+		// but would not be backwards compatible, so we have to add a new filter
2975
+		$this->_template_args['before_list_table'] = implode(
2976
+			" \n",
2977
+			(array) apply_filters(
2978
+				'FHEE__EE_Admin_Page___display_admin_list_table_page__before_list_table__template_args_array',
2979
+				(array) $this->_template_args['before_list_table'],
2980
+				$this->page_slug,
2981
+				$this->request->requestParams(),
2982
+				$this->_req_action
2983
+			)
2984
+		);
2985
+		// filter after_list_table template arg
2986
+		$this->_template_args['after_list_table'] = apply_filters(
2987
+			'FHEE__EE_Admin_Page___display_admin_list_table_page__after_list_table__template_arg',
2988
+			$this->_template_args['after_list_table'],
2989
+			$this->page_slug,
2990
+			$this->request->requestParams(),
2991
+			$this->_req_action
2992
+		);
2993
+		// convert to array and filter again
2994
+		// arrays are easier to inject new items in a specific location,
2995
+		// but would not be backwards compatible, so we have to add a new filter
2996
+		$this->_template_args['after_list_table']   = implode(
2997
+			" \n",
2998
+			(array) apply_filters(
2999
+				'FHEE__EE_Admin_Page___display_admin_list_table_page__after_list_table__template_args_array',
3000
+				(array) $this->_template_args['after_list_table'],
3001
+				$this->page_slug,
3002
+				$this->request->requestParams(),
3003
+				$this->_req_action
3004
+			)
3005
+		);
3006
+		$this->_template_args['admin_page_content'] = EEH_Template::display_template(
3007
+			$template_path,
3008
+			$this->_template_args,
3009
+			true
3010
+		);
3011
+		// the final template wrapper
3012
+		if ($sidebar) {
3013
+			$this->display_admin_page_with_sidebar();
3014
+		} else {
3015
+			$this->display_admin_page_with_no_sidebar();
3016
+		}
3017
+	}
3018
+
3019
+
3020
+	/**
3021
+	 * This just prepares a legend using the given items and the admin_details_legend.template.php file and returns the
3022
+	 * html string for the legend.
3023
+	 * $items are expected in an array in the following format:
3024
+	 * $legend_items = array(
3025
+	 *        'item_id' => array(
3026
+	 *            'icon' => 'http://url_to_icon_being_described.png',
3027
+	 *            'desc' => esc_html__('localized description of item');
3028
+	 *        )
3029
+	 * );
3030
+	 *
3031
+	 * @param array $items see above for format of array
3032
+	 * @return string html string of legend
3033
+	 * @throws DomainException
3034
+	 */
3035
+	protected function _display_legend($items)
3036
+	{
3037
+		$this->_template_args['items'] = apply_filters(
3038
+			'FHEE__EE_Admin_Page___display_legend__items',
3039
+			(array) $items,
3040
+			$this
3041
+		);
3042
+		/** @var StatusChangeNotice $status_change_notice */
3043
+		$status_change_notice = $this->loader->getShared(
3044
+			'EventEspresso\core\domain\services\admin\notices\status_change\StatusChangeNotice'
3045
+		);
3046
+		$this->_template_args['status_change_notice'] = $status_change_notice->display(
3047
+			'__admin-legend',
3048
+			$this->page_slug
3049
+		);
3050
+		return EEH_Template::display_template(
3051
+			EE_ADMIN_TEMPLATE . 'admin_details_legend.template.php',
3052
+			$this->_template_args,
3053
+			true
3054
+		);
3055
+	}
3056
+
3057
+
3058
+	/**
3059
+	 * This is used whenever we're DOING_AJAX to return a formatted json array that our calling javascript can expect
3060
+	 * The returned json object is created from an array in the following format:
3061
+	 * array(
3062
+	 *  'error' => FALSE, //(default FALSE), contains any errors and/or exceptions (exceptions return json early),
3063
+	 *  'success' => FALSE, //(default FALSE) - contains any special success message.
3064
+	 *  'notices' => '', // - contains any EE_Error formatted notices
3065
+	 *  'content' => 'string can be html', //this is a string of formatted content (can be html)
3066
+	 *  'data' => array() //this can be any key/value pairs that a method returns for later json parsing by the js.
3067
+	 *  We're also going to include the template args with every package (so js can pick out any specific template args
3068
+	 *  that might be included in here)
3069
+	 * )
3070
+	 * The json object is populated by whatever is set in the $_template_args property.
3071
+	 *
3072
+	 * @param bool  $sticky_notices    Used to indicate whether you want to ensure notices are added to a transient
3073
+	 *                                 instead of displayed.
3074
+	 * @param array $notices_arguments Use this to pass any additional args on to the _process_notices.
3075
+	 * @return void
3076
+	 * @throws EE_Error
3077
+	 * @throws InvalidArgumentException
3078
+	 * @throws InvalidDataTypeException
3079
+	 * @throws InvalidInterfaceException
3080
+	 */
3081
+	protected function _return_json($sticky_notices = false, $notices_arguments = [])
3082
+	{
3083
+		// make sure any EE_Error notices have been handled.
3084
+		$this->_process_notices($notices_arguments, true, $sticky_notices);
3085
+		$data = isset($this->_template_args['data']) ? $this->_template_args['data'] : [];
3086
+		unset($this->_template_args['data']);
3087
+		$json = [
3088
+			'error'     => isset($this->_template_args['error']) ? $this->_template_args['error'] : false,
3089
+			'success'   => isset($this->_template_args['success']) ? $this->_template_args['success'] : false,
3090
+			'errors'    => isset($this->_template_args['errors']) ? $this->_template_args['errors'] : false,
3091
+			'attention' => isset($this->_template_args['attention']) ? $this->_template_args['attention'] : false,
3092
+			'notices'   => EE_Error::get_notices(),
3093
+			'content'   => isset($this->_template_args['admin_page_content'])
3094
+				? $this->_template_args['admin_page_content'] : '',
3095
+			'data'      => array_merge($data, ['template_args' => $this->_template_args]),
3096
+			'isEEajax'  => true
3097
+			// special flag so any ajax.Success methods in js can identify this return package as a EEajax package.
3098
+		];
3099
+		// make sure there are no php errors or headers_sent.  Then we can set correct json header.
3100
+		if (null === error_get_last() || ! headers_sent()) {
3101
+			header('Content-Type: application/json; charset=UTF-8');
3102
+		}
3103
+		echo wp_json_encode($json);
3104
+		exit();
3105
+	}
3106
+
3107
+
3108
+	/**
3109
+	 * Simply a wrapper for the protected method so we can call this outside the class (ONLY when doing ajax)
3110
+	 *
3111
+	 * @return void
3112
+	 * @throws EE_Error
3113
+	 * @throws InvalidArgumentException
3114
+	 * @throws InvalidDataTypeException
3115
+	 * @throws InvalidInterfaceException
3116
+	 */
3117
+	public function return_json()
3118
+	{
3119
+		if ($this->request->isAjax()) {
3120
+			$this->_return_json();
3121
+		} else {
3122
+			throw new EE_Error(
3123
+				sprintf(
3124
+					esc_html__('The public %s method can only be called when DOING_AJAX = TRUE', 'event_espresso'),
3125
+					__FUNCTION__
3126
+				)
3127
+			);
3128
+		}
3129
+	}
3130
+
3131
+
3132
+	/**
3133
+	 * This provides a way for child hook classes to send along themselves by reference so methods/properties within
3134
+	 * them can be accessed by EE_Admin_child pages. This is assigned to the $_hook_obj property.
3135
+	 *
3136
+	 * @param EE_Admin_Hooks $hook_obj This will be the object for the EE_Admin_Hooks child
3137
+	 */
3138
+	public function set_hook_object(EE_Admin_Hooks $hook_obj)
3139
+	{
3140
+		$this->_hook_obj = $hook_obj;
3141
+	}
3142
+
3143
+
3144
+	/**
3145
+	 *        generates  HTML wrapper with Tabbed nav for an admin page
3146
+	 *
3147
+	 * @param boolean $about whether to use the special about page wrapper or default.
3148
+	 * @return void
3149
+	 * @throws DomainException
3150
+	 * @throws EE_Error
3151
+	 * @throws InvalidArgumentException
3152
+	 * @throws InvalidDataTypeException
3153
+	 * @throws InvalidInterfaceException
3154
+	 */
3155
+	public function admin_page_wrapper($about = false)
3156
+	{
3157
+		do_action('AHEE_log', __FILE__, __FUNCTION__, '');
3158
+		$this->_nav_tabs                                   = $this->_get_main_nav_tabs();
3159
+		$this->_template_args['nav_tabs']                  = $this->_nav_tabs;
3160
+		$this->_template_args['admin_page_title']          = $this->_admin_page_title;
3161
+
3162
+		$this->_template_args['before_admin_page_content'] = apply_filters(
3163
+			"FHEE_before_admin_page_content{$this->_current_page}{$this->_current_view}",
3164
+			$this->_template_args['before_admin_page_content'] ?? ''
3165
+		);
3166
+
3167
+		$this->_template_args['after_admin_page_content']  = apply_filters(
3168
+			"FHEE_after_admin_page_content{$this->_current_page}{$this->_current_view}",
3169
+			$this->_template_args['after_admin_page_content'] ?? ''
3170
+		);
3171
+		$this->_template_args['after_admin_page_content']  .= $this->_set_help_popup_content();
3172
+
3173
+		if ($this->request->isAjax()) {
3174
+			$this->_template_args['admin_page_content'] = EEH_Template::display_template(
3175
+				// $template_path,
3176
+				EE_ADMIN_TEMPLATE . 'admin_wrapper_ajax.template.php',
3177
+				$this->_template_args,
3178
+				true
3179
+			);
3180
+			$this->_return_json();
3181
+		}
3182
+		// load settings page wrapper template
3183
+		$template_path = $about
3184
+			? EE_ADMIN_TEMPLATE . 'about_admin_wrapper.template.php'
3185
+			: EE_ADMIN_TEMPLATE . 'admin_wrapper.template.php';
3186
+
3187
+		EEH_Template::display_template($template_path, $this->_template_args);
3188
+	}
3189
+
3190
+
3191
+	/**
3192
+	 * This returns the admin_nav tabs html using the configuration in the _nav_tabs property
3193
+	 *
3194
+	 * @return string html
3195
+	 * @throws EE_Error
3196
+	 */
3197
+	protected function _get_main_nav_tabs()
3198
+	{
3199
+		// let's generate the html using the EEH_Tabbed_Content helper.
3200
+		// We do this here so that it's possible for child classes to add in nav tabs dynamically at the last minute
3201
+		// (rather than setting in the page_routes array)
3202
+		return EEH_Tabbed_Content::display_admin_nav_tabs($this->_nav_tabs, $this->page_slug);
3203
+	}
3204
+
3205
+
3206
+	/**
3207
+	 *        sort nav tabs
3208
+	 *
3209
+	 * @param $a
3210
+	 * @param $b
3211
+	 * @return int
3212
+	 */
3213
+	private function _sort_nav_tabs($a, $b)
3214
+	{
3215
+		if ($a['order'] === $b['order']) {
3216
+			return 0;
3217
+		}
3218
+		return ($a['order'] < $b['order']) ? -1 : 1;
3219
+	}
3220
+
3221
+
3222
+	/**
3223
+	 * generates HTML for the forms used on admin pages
3224
+	 *
3225
+	 * @param array  $input_vars - array of input field details
3226
+	 * @param string $generator  indicates which generator to use: options are 'string' or 'array'
3227
+	 * @param bool   $id
3228
+	 * @return array|string
3229
+	 * @uses   EEH_Form_Fields::get_form_fields (/helper/EEH_Form_Fields.helper.php)
3230
+	 * @uses   EEH_Form_Fields::get_form_fields_array (/helper/EEH_Form_Fields.helper.php)
3231
+	 */
3232
+	protected function _generate_admin_form_fields($input_vars = [], $generator = 'string', $id = false)
3233
+	{
3234
+		return $generator === 'string'
3235
+			? EEH_Form_Fields::get_form_fields($input_vars, $id)
3236
+			: EEH_Form_Fields::get_form_fields_array($input_vars);
3237
+	}
3238
+
3239
+
3240
+	/**
3241
+	 * generates the "Save" and "Save & Close" buttons for edit forms
3242
+	 *
3243
+	 * @param bool             $both     if true then both buttons will be generated.  If false then just the "Save &
3244
+	 *                                   Close" button.
3245
+	 * @param array            $text     if included, generator will use the given text for the buttons ( array([0] =>
3246
+	 *                                   'Save', [1] => 'save & close')
3247
+	 * @param array            $actions  if included allows us to set the actions that each button will carry out (i.e.
3248
+	 *                                   via the "name" value in the button).  We can also use this to just dump
3249
+	 *                                   default actions by submitting some other value.
3250
+	 * @param bool|string|null $referrer if false then we just do the default action on save and close.  Other wise it
3251
+	 *                                   will use the $referrer string. IF null, then we don't do ANYTHING on save and
3252
+	 *                                   close (normal form handling).
3253
+	 */
3254
+	protected function _set_save_buttons($both = true, $text = [], $actions = [], $referrer = null)
3255
+	{
3256
+		// make sure $text and $actions are in an array
3257
+		$text          = (array) $text;
3258
+		$actions       = (array) $actions;
3259
+		$referrer_url  = ! empty($referrer) ? $referrer : $this->request->getServerParam('REQUEST_URI');
3260
+		$button_text   = ! empty($text)
3261
+			? $text
3262
+			: [
3263
+				esc_html__('Save', 'event_espresso'),
3264
+				esc_html__('Save and Close', 'event_espresso'),
3265
+			];
3266
+		$default_names = ['save', 'save_and_close'];
3267
+		$buttons = '';
3268
+		foreach ($button_text as $key => $button) {
3269
+			$ref     = $default_names[ $key ];
3270
+			$name    = ! empty($actions) ? $actions[ $key ] : $ref;
3271
+			$buttons .= '<input type="submit" class="button button--primary ' . $ref . '" '
3272
+						. 'value="' . $button . '" name="' . $name . '" '
3273
+						. 'id="' . $this->_current_view . '_' . $ref . '" />';
3274
+			if (! $both) {
3275
+				break;
3276
+			}
3277
+		}
3278
+		// add in a hidden index for the current page (so save and close redirects properly)
3279
+		$buttons .= '<input type="hidden" id="save_and_close_referrer" name="save_and_close_referrer" value="'
3280
+				   . $referrer_url
3281
+				   . '" />';
3282
+		$this->_template_args['save_buttons'] = $buttons;
3283
+	}
3284
+
3285
+
3286
+	/**
3287
+	 * Wrapper for the protected function.  Allows plugins/addons to call this to set the form tags.
3288
+	 *
3289
+	 * @param string $route
3290
+	 * @param array  $additional_hidden_fields
3291
+	 * @see   $this->_set_add_edit_form_tags() for details on params
3292
+	 * @since 4.6.0
3293
+	 */
3294
+	public function set_add_edit_form_tags($route = '', $additional_hidden_fields = [])
3295
+	{
3296
+		$this->_set_add_edit_form_tags($route, $additional_hidden_fields);
3297
+	}
3298
+
3299
+
3300
+	/**
3301
+	 * set form open and close tags on add/edit pages.
3302
+	 *
3303
+	 * @param string $route                    the route you want the form to direct to
3304
+	 * @param array  $additional_hidden_fields any additional hidden fields required in the form header
3305
+	 * @return void
3306
+	 */
3307
+	protected function _set_add_edit_form_tags($route = '', $additional_hidden_fields = [])
3308
+	{
3309
+		if (empty($route)) {
3310
+			$user_msg = esc_html__(
3311
+				'An error occurred. No action was set for this page\'s form.',
3312
+				'event_espresso'
3313
+			);
3314
+			$dev_msg  = $user_msg . "\n"
3315
+						. sprintf(
3316
+							esc_html__('The $route argument is required for the %s->%s method.', 'event_espresso'),
3317
+							__FUNCTION__,
3318
+							__CLASS__
3319
+						);
3320
+			EE_Error::add_error($user_msg . '||' . $dev_msg, __FILE__, __FUNCTION__, __LINE__);
3321
+		}
3322
+		// open form
3323
+		$action = $this->_admin_base_url;
3324
+		$this->_template_args['before_admin_page_content'] = "
3325 3325
             <form name='form' method='post' action='{$action}' id='{$route}_event_form' class='ee-admin-page-form' >
3326 3326
             ";
3327
-        // add nonce
3328
-        $nonce                                             =
3329
-            wp_nonce_field($route . '_nonce', $route . '_nonce', false, false);
3330
-        $this->_template_args['before_admin_page_content'] .= "\n\t" . $nonce;
3331
-        // add REQUIRED form action
3332
-        $hidden_fields = [
3333
-            'action' => ['type' => 'hidden', 'value' => $route],
3334
-        ];
3335
-        // merge arrays
3336
-        $hidden_fields = is_array($additional_hidden_fields)
3337
-            ? array_merge($hidden_fields, $additional_hidden_fields)
3338
-            : $hidden_fields;
3339
-        // generate form fields
3340
-        $form_fields = $this->_generate_admin_form_fields($hidden_fields, 'array');
3341
-        // add fields to form
3342
-        foreach ((array) $form_fields as $form_field) {
3343
-            $this->_template_args['before_admin_page_content'] .= "\n\t" . $form_field['field'];
3344
-        }
3345
-        // close form
3346
-        $this->_template_args['after_admin_page_content'] = '</form>';
3347
-    }
3348
-
3349
-
3350
-    /**
3351
-     * Public Wrapper for _redirect_after_action() method since its
3352
-     * discovered it would be useful for external code to have access.
3353
-     *
3354
-     * @param bool   $success
3355
-     * @param string $what
3356
-     * @param string $action_desc
3357
-     * @param array  $query_args
3358
-     * @param bool   $override_overwrite
3359
-     * @throws EE_Error
3360
-     * @see   EE_Admin_Page::_redirect_after_action() for params.
3361
-     * @since 4.5.0
3362
-     */
3363
-    public function redirect_after_action(
3364
-        $success = false,
3365
-        $what = 'item',
3366
-        $action_desc = 'processed',
3367
-        $query_args = [],
3368
-        $override_overwrite = false
3369
-    ) {
3370
-        $this->_redirect_after_action(
3371
-            $success,
3372
-            $what,
3373
-            $action_desc,
3374
-            $query_args,
3375
-            $override_overwrite
3376
-        );
3377
-    }
3378
-
3379
-
3380
-    /**
3381
-     * Helper method for merging existing request data with the returned redirect url.
3382
-     *
3383
-     * This is typically used for redirects after an action so that if the original view was a filtered view those
3384
-     * filters are still applied.
3385
-     *
3386
-     * @param array $new_route_data
3387
-     * @return array
3388
-     */
3389
-    protected function mergeExistingRequestParamsWithRedirectArgs(array $new_route_data)
3390
-    {
3391
-        foreach ($this->request->requestParams() as $ref => $value) {
3392
-            // unset nonces
3393
-            if (strpos($ref, 'nonce') !== false) {
3394
-                $this->request->unSetRequestParam($ref);
3395
-                continue;
3396
-            }
3397
-            // urlencode values.
3398
-            $value = is_array($value) ? array_map('urlencode', $value) : urlencode($value);
3399
-            $this->request->setRequestParam($ref, $value);
3400
-        }
3401
-        return array_merge($this->request->requestParams(), $new_route_data);
3402
-    }
3403
-
3404
-
3405
-    /**
3406
-     * @param int|float|string $success      - whether success was for two or more records, or just one, or none
3407
-     * @param string           $what         - what the action was performed on
3408
-     * @param string           $action_desc  - what was done ie: updated, deleted, etc
3409
-     * @param array $query_args              - an array of query_args to be added to the URL to redirect to
3410
-     * @param BOOL $override_overwrite       - by default all EE_Error::success messages are overwritten,
3411
-     *                                         this allows you to override this so that they show.
3412
-     * @return void
3413
-     * @throws EE_Error
3414
-     * @throws InvalidArgumentException
3415
-     * @throws InvalidDataTypeException
3416
-     * @throws InvalidInterfaceException
3417
-     */
3418
-    protected function _redirect_after_action(
3419
-        $success = 0,
3420
-        string $what = 'item',
3421
-        string $action_desc = 'processed',
3422
-        array $query_args = [],
3423
-        bool $override_overwrite = false
3424
-    ) {
3425
-        do_action('AHEE_log', __FILE__, __FUNCTION__, '');
3426
-        $notices      = EE_Error::get_notices(false);
3427
-        // overwrite default success messages //BUT ONLY if overwrite not overridden
3428
-        if (! $override_overwrite || ! empty($notices['errors'])) {
3429
-            EE_Error::overwrite_success();
3430
-        }
3431
-        if (! $override_overwrite && ! empty($what) && ! empty($action_desc) && empty($notices['errors'])) {
3432
-            // how many records affected ? more than one record ? or just one ?
3433
-            EE_Error::add_success(
3434
-                sprintf(
3435
-                    esc_html(
3436
-                        _n(
3437
-                            'The "%1$s" has been successfully %2$s.',
3438
-                            'The "%1$s" have been successfully %2$s.',
3439
-                            $success,
3440
-                            'event_espresso'
3441
-                        )
3442
-                    ),
3443
-                    $what,
3444
-                    $action_desc
3445
-                ),
3446
-                __FILE__,
3447
-                __FUNCTION__,
3448
-                __LINE__
3449
-            );
3450
-        }
3451
-        // check that $query_args isn't something crazy
3452
-        if (! is_array($query_args)) {
3453
-            $query_args = [];
3454
-        }
3455
-        /**
3456
-         * Allow injecting actions before the query_args are modified for possible different
3457
-         * redirections on save and close actions
3458
-         *
3459
-         * @param array $query_args       The original query_args array coming into the
3460
-         *                                method.
3461
-         * @since 4.2.0
3462
-         */
3463
-        do_action(
3464
-            "AHEE__{$this->class_name}___redirect_after_action__before_redirect_modification_{$this->_req_action}",
3465
-            $query_args
3466
-        );
3467
-        // set redirect url.
3468
-        // Note if there is a "page" index in the $query_args then we go with vanilla admin.php route,
3469
-        // otherwise we go with whatever is set as the _admin_base_url
3470
-        $redirect_url = isset($query_args['page']) ? admin_url('admin.php') : $this->_admin_base_url;
3471
-        // calculate where we're going (if we have a "save and close" button pushed)
3472
-        if (
3473
-            $this->request->requestParamIsSet('save_and_close')
3474
-            && $this->request->requestParamIsSet('save_and_close_referrer')
3475
-        ) {
3476
-            // even though we have the save_and_close referrer, we need to parse the url for the action in order to generate a nonce
3477
-            $parsed_url = parse_url($this->request->getRequestParam('save_and_close_referrer', '', 'url'));
3478
-            // regenerate query args array from referrer URL
3479
-            parse_str($parsed_url['query'], $query_args);
3480
-            // correct page and action will be in the query args now
3481
-            $redirect_url = admin_url('admin.php');
3482
-        }
3483
-        // merge any default query_args set in _default_route_query_args property
3484
-        if (! empty($this->_default_route_query_args) && ! $this->_is_UI_request) {
3485
-            $args_to_merge = [];
3486
-            foreach ($this->_default_route_query_args as $query_param => $query_value) {
3487
-                // is there a wp_referer array in our _default_route_query_args property?
3488
-                if ($query_param === 'wp_referer') {
3489
-                    $query_value = (array) $query_value;
3490
-                    foreach ($query_value as $reference => $value) {
3491
-                        if (strpos($reference, 'nonce') !== false) {
3492
-                            continue;
3493
-                        }
3494
-                        // finally we will override any arguments in the referer with
3495
-                        // what might be set on the _default_route_query_args array.
3496
-                        if (isset($this->_default_route_query_args[ $reference ])) {
3497
-                            $args_to_merge[ $reference ] = urlencode($this->_default_route_query_args[ $reference ]);
3498
-                        } else {
3499
-                            $args_to_merge[ $reference ] = urlencode($value);
3500
-                        }
3501
-                    }
3502
-                    continue;
3503
-                }
3504
-                $args_to_merge[ $query_param ] = $query_value;
3505
-            }
3506
-            // now let's merge these arguments but override with what was specifically sent in to the
3507
-            // redirect.
3508
-            $query_args = array_merge($args_to_merge, $query_args);
3509
-        }
3510
-        $this->_process_notices($query_args);
3511
-        // generate redirect url
3512
-        // if redirecting to anything other than the main page, add a nonce
3513
-        if (isset($query_args['action'])) {
3514
-            // manually generate wp_nonce and merge that with the query vars
3515
-            // becuz the wp_nonce_url function wrecks havoc on some vars
3516
-            $query_args['_wpnonce'] = wp_create_nonce($query_args['action'] . '_nonce');
3517
-        }
3518
-        // we're adding some hooks and filters in here for processing any things just before redirects
3519
-        // (example: an admin page has done an insert or update and we want to run something after that).
3520
-        do_action('AHEE_redirect_' . $this->class_name . $this->_req_action, $query_args);
3521
-        $redirect_url = apply_filters(
3522
-            'FHEE_redirect_' . $this->class_name . $this->_req_action,
3523
-            EE_Admin_Page::add_query_args_and_nonce($query_args, $redirect_url),
3524
-            $query_args
3525
-        );
3526
-        // check if we're doing ajax.  If we are then lets just return the results and js can handle how it wants.
3527
-        if ($this->request->isAjax()) {
3528
-            $default_data                    = [
3529
-                'close'        => true,
3530
-                'redirect_url' => $redirect_url,
3531
-                'where'        => 'main',
3532
-                'what'         => 'append',
3533
-            ];
3534
-            $this->_template_args['success'] = $success;
3535
-            $this->_template_args['data']    = ! empty($this->_template_args['data']) ? array_merge(
3536
-                $default_data,
3537
-                $this->_template_args['data']
3538
-            ) : $default_data;
3539
-            $this->_return_json();
3540
-        }
3541
-        wp_safe_redirect($redirect_url);
3542
-        exit();
3543
-    }
3544
-
3545
-
3546
-    /**
3547
-     * process any notices before redirecting (or returning ajax request)
3548
-     * This method sets the $this->_template_args['notices'] attribute;
3549
-     *
3550
-     * @param array $query_args         any query args that need to be used for notice transient ('action')
3551
-     * @param bool  $skip_route_verify  This is typically used when we are processing notices REALLY early and
3552
-     *                                  page_routes haven't been defined yet.
3553
-     * @param bool  $sticky_notices     This is used to flag that regardless of whether this is doing_ajax or not, we
3554
-     *                                  still save a transient for the notice.
3555
-     * @return void
3556
-     * @throws EE_Error
3557
-     * @throws InvalidArgumentException
3558
-     * @throws InvalidDataTypeException
3559
-     * @throws InvalidInterfaceException
3560
-     */
3561
-    protected function _process_notices($query_args = [], $skip_route_verify = false, $sticky_notices = true)
3562
-    {
3563
-        // first let's set individual error properties if doing_ajax and the properties aren't already set.
3564
-        if ($this->request->isAjax()) {
3565
-            $notices = EE_Error::get_notices(false);
3566
-            if (empty($this->_template_args['success'])) {
3567
-                $this->_template_args['success'] = isset($notices['success']) ? $notices['success'] : false;
3568
-            }
3569
-            if (empty($this->_template_args['errors'])) {
3570
-                $this->_template_args['errors'] = isset($notices['errors']) ? $notices['errors'] : false;
3571
-            }
3572
-            if (empty($this->_template_args['attention'])) {
3573
-                $this->_template_args['attention'] = isset($notices['attention']) ? $notices['attention'] : false;
3574
-            }
3575
-        }
3576
-        $this->_template_args['notices'] = EE_Error::get_notices();
3577
-        // IF this isn't ajax we need to create a transient for the notices using the route (however, overridden if $sticky_notices == true)
3578
-        if (! $this->request->isAjax() || $sticky_notices) {
3579
-            $route = isset($query_args['action']) ? $query_args['action'] : 'default';
3580
-            $this->_add_transient(
3581
-                $route,
3582
-                $this->_template_args['notices'],
3583
-                true,
3584
-                $skip_route_verify
3585
-            );
3586
-        }
3587
-    }
3588
-
3589
-
3590
-    /**
3591
-     * get_action_link_or_button
3592
-     * returns the button html for adding, editing, or deleting an item (depending on given type)
3593
-     *
3594
-     * @param string $action        use this to indicate which action the url is generated with.
3595
-     * @param string $type          accepted strings must be defined in the $_labels['button'] array(as the key)
3596
-     *                              property.
3597
-     * @param array  $extra_request if the button requires extra params you can include them in $key=>$value pairs.
3598
-     * @param string $class         Use this to give the class for the button. Defaults to 'button--primary'
3599
-     * @param string $base_url      If this is not provided
3600
-     *                              the _admin_base_url will be used as the default for the button base_url.
3601
-     *                              Otherwise this value will be used.
3602
-     * @param bool   $exclude_nonce If true then no nonce will be in the generated button link.
3603
-     * @return string
3604
-     * @throws InvalidArgumentException
3605
-     * @throws InvalidInterfaceException
3606
-     * @throws InvalidDataTypeException
3607
-     * @throws EE_Error
3608
-     */
3609
-    public function get_action_link_or_button(
3610
-        $action,
3611
-        $type = 'add',
3612
-        $extra_request = [],
3613
-        $class = 'button button--primary',
3614
-        $base_url = '',
3615
-        $exclude_nonce = false
3616
-    ) {
3617
-        // first let's validate the action (if $base_url is FALSE otherwise validation will happen further along)
3618
-        if (empty($base_url) && ! isset($this->_page_routes[ $action ])) {
3619
-            throw new EE_Error(
3620
-                sprintf(
3621
-                    esc_html__(
3622
-                        'There is no page route for given action for the button.  This action was given: %s',
3623
-                        'event_espresso'
3624
-                    ),
3625
-                    $action
3626
-                )
3627
-            );
3628
-        }
3629
-        if (! isset($this->_labels['buttons'][ $type ])) {
3630
-            throw new EE_Error(
3631
-                sprintf(
3632
-                    esc_html__(
3633
-                        'There is no label for the given button type (%s). Labels are set in the <code>_page_config</code> property.',
3634
-                        'event_espresso'
3635
-                    ),
3636
-                    $type
3637
-                )
3638
-            );
3639
-        }
3640
-        // finally check user access for this button.
3641
-        $has_access = $this->check_user_access($action, true);
3642
-        if (! $has_access) {
3643
-            return '';
3644
-        }
3645
-        $_base_url  = ! $base_url ? $this->_admin_base_url : $base_url;
3646
-        $query_args = [
3647
-            'action' => $action,
3648
-        ];
3649
-        // merge extra_request args but make sure our original action takes precedence and doesn't get overwritten.
3650
-        if (! empty($extra_request)) {
3651
-            $query_args = array_merge($extra_request, $query_args);
3652
-        }
3653
-        $url = EE_Admin_Page::add_query_args_and_nonce($query_args, $_base_url, false, $exclude_nonce);
3654
-        return EEH_Template::get_button_or_link($url, $this->_labels['buttons'][ $type ], $class);
3655
-    }
3656
-
3657
-
3658
-    /**
3659
-     * _per_page_screen_option
3660
-     * Utility function for adding in a per_page_option in the screen_options_dropdown.
3661
-     *
3662
-     * @return void
3663
-     * @throws InvalidArgumentException
3664
-     * @throws InvalidInterfaceException
3665
-     * @throws InvalidDataTypeException
3666
-     */
3667
-    protected function _per_page_screen_option()
3668
-    {
3669
-        $option = 'per_page';
3670
-        $args   = [
3671
-            'label'   => apply_filters(
3672
-                'FHEE__EE_Admin_Page___per_page_screen_options___label',
3673
-                $this->_admin_page_title,
3674
-                $this
3675
-            ),
3676
-            'default' => (int) apply_filters(
3677
-                'FHEE__EE_Admin_Page___per_page_screen_options__default',
3678
-                20
3679
-            ),
3680
-            'option'  => $this->_current_page . '_' . $this->_current_view . '_per_page',
3681
-        ];
3682
-        // ONLY add the screen option if the user has access to it.
3683
-        if ($this->check_user_access($this->_current_view, true)) {
3684
-            add_screen_option($option, $args);
3685
-        }
3686
-    }
3687
-
3688
-
3689
-    /**
3690
-     * set_per_page_screen_option
3691
-     * All this does is make sure that WordPress saves any per_page screen options (if set) for the current page.
3692
-     * we have to do this rather than running inside the 'set-screen-options' hook because it runs earlier than
3693
-     * admin_menu.
3694
-     *
3695
-     * @return void
3696
-     */
3697
-    private function _set_per_page_screen_options()
3698
-    {
3699
-        if ($this->request->requestParamIsSet('wp_screen_options')) {
3700
-            check_admin_referer('screen-options-nonce', 'screenoptionnonce');
3701
-            if (! $user = wp_get_current_user()) {
3702
-                return;
3703
-            }
3704
-            $option = $this->request->getRequestParam('wp_screen_options[option]', '', 'key');
3705
-            if (! $option) {
3706
-                return;
3707
-            }
3708
-            $value  = $this->request->getRequestParam('wp_screen_options[value]', 0, 'int');
3709
-            $map_option = $option;
3710
-            $option     = str_replace('-', '_', $option);
3711
-            switch ($map_option) {
3712
-                case $this->_current_page . '_' . $this->_current_view . '_per_page':
3713
-                    $max_value = apply_filters(
3714
-                        'FHEE__EE_Admin_Page___set_per_page_screen_options__max_value',
3715
-                        999,
3716
-                        $this->_current_page,
3717
-                        $this->_current_view
3718
-                    );
3719
-                    if ($value < 1) {
3720
-                        return;
3721
-                    }
3722
-                    $value = min($value, $max_value);
3723
-                    break;
3724
-                default:
3725
-                    $value = apply_filters(
3726
-                        'FHEE__EE_Admin_Page___set_per_page_screen_options__value',
3727
-                        false,
3728
-                        $option,
3729
-                        $value
3730
-                    );
3731
-                    if (false === $value) {
3732
-                        return;
3733
-                    }
3734
-                    break;
3735
-            }
3736
-            update_user_meta($user->ID, $option, $value);
3737
-            wp_safe_redirect(remove_query_arg(['pagenum', 'apage', 'paged'], wp_get_referer()));
3738
-            exit;
3739
-        }
3740
-    }
3741
-
3742
-
3743
-    /**
3744
-     * This just allows for setting the $_template_args property if it needs to be set outside the object
3745
-     *
3746
-     * @param array $data array that will be assigned to template args.
3747
-     */
3748
-    public function set_template_args($data)
3749
-    {
3750
-        $this->_template_args = array_merge($this->_template_args, (array) $data);
3751
-    }
3752
-
3753
-
3754
-    /**
3755
-     * This makes available the WP transient system for temporarily moving data between routes
3756
-     *
3757
-     * @param string $route             the route that should receive the transient
3758
-     * @param array  $data              the data that gets sent
3759
-     * @param bool   $notices           If this is for notices then we use this to indicate so, otherwise its just a
3760
-     *                                  normal route transient.
3761
-     * @param bool   $skip_route_verify Used to indicate we want to skip route verification.  This is usually ONLY used
3762
-     *                                  when we are adding a transient before page_routes have been defined.
3763
-     * @return void
3764
-     * @throws EE_Error
3765
-     */
3766
-    protected function _add_transient($route, $data, $notices = false, $skip_route_verify = false)
3767
-    {
3768
-        $user_id = get_current_user_id();
3769
-        if (! $skip_route_verify) {
3770
-            $this->_verify_route($route);
3771
-        }
3772
-        // now let's set the string for what kind of transient we're setting
3773
-        $transient = $notices
3774
-            ? 'ee_rte_n_tx_' . $route . '_' . $user_id
3775
-            : 'rte_tx_' . $route . '_' . $user_id;
3776
-        $data      = $notices ? ['notices' => $data] : $data;
3777
-        // is there already a transient for this route?  If there is then let's ADD to that transient
3778
-        $existing = is_multisite() && is_network_admin()
3779
-            ? get_site_transient($transient)
3780
-            : get_transient($transient);
3781
-        if ($existing) {
3782
-            $data = array_merge((array) $data, (array) $existing);
3783
-        }
3784
-        if (is_multisite() && is_network_admin()) {
3785
-            set_site_transient($transient, $data, 8);
3786
-        } else {
3787
-            set_transient($transient, $data, 8);
3788
-        }
3789
-    }
3790
-
3791
-
3792
-    /**
3793
-     * this retrieves the temporary transient that has been set for moving data between routes.
3794
-     *
3795
-     * @param bool   $notices true we get notices transient. False we just return normal route transient
3796
-     * @param string $route
3797
-     * @return mixed data
3798
-     */
3799
-    protected function _get_transient($notices = false, $route = '')
3800
-    {
3801
-        $user_id   = get_current_user_id();
3802
-        $route     = ! $route ? $this->_req_action : $route;
3803
-        $transient = $notices
3804
-            ? 'ee_rte_n_tx_' . $route . '_' . $user_id
3805
-            : 'rte_tx_' . $route . '_' . $user_id;
3806
-        $data      = is_multisite() && is_network_admin()
3807
-            ? get_site_transient($transient)
3808
-            : get_transient($transient);
3809
-        // delete transient after retrieval (just in case it hasn't expired);
3810
-        if (is_multisite() && is_network_admin()) {
3811
-            delete_site_transient($transient);
3812
-        } else {
3813
-            delete_transient($transient);
3814
-        }
3815
-        return $notices && isset($data['notices']) ? $data['notices'] : $data;
3816
-    }
3817
-
3818
-
3819
-    /**
3820
-     * The purpose of this method is just to run garbage collection on any EE transients that might have expired but
3821
-     * would not be called later. This will be assigned to run on a specific EE Admin page. (place the method in the
3822
-     * default route callback on the EE_Admin page you want it run.)
3823
-     *
3824
-     * @return void
3825
-     */
3826
-    protected function _transient_garbage_collection()
3827
-    {
3828
-        global $wpdb;
3829
-        // retrieve all existing transients
3830
-        $query =
3831
-            "SELECT option_name FROM {$wpdb->options} WHERE option_name LIKE '%rte_tx_%' OR option_name LIKE '%rte_n_tx_%'";
3832
-        if ($results = $wpdb->get_results($query)) {
3833
-            foreach ($results as $result) {
3834
-                $transient = str_replace('_transient_', '', $result->option_name);
3835
-                get_transient($transient);
3836
-                if (is_multisite() && is_network_admin()) {
3837
-                    get_site_transient($transient);
3838
-                }
3839
-            }
3840
-        }
3841
-    }
3842
-
3843
-
3844
-    /**
3845
-     * get_view
3846
-     *
3847
-     * @return string content of _view property
3848
-     */
3849
-    public function get_view()
3850
-    {
3851
-        return $this->_view;
3852
-    }
3853
-
3854
-
3855
-    /**
3856
-     * getter for the protected $_views property
3857
-     *
3858
-     * @return array
3859
-     */
3860
-    public function get_views()
3861
-    {
3862
-        return $this->_views;
3863
-    }
3864
-
3865
-
3866
-    /**
3867
-     * get_current_page
3868
-     *
3869
-     * @return string _current_page property value
3870
-     */
3871
-    public function get_current_page()
3872
-    {
3873
-        return $this->_current_page;
3874
-    }
3875
-
3876
-
3877
-    /**
3878
-     * get_current_view
3879
-     *
3880
-     * @return string _current_view property value
3881
-     */
3882
-    public function get_current_view()
3883
-    {
3884
-        return $this->_current_view;
3885
-    }
3886
-
3887
-
3888
-    /**
3889
-     * get_current_screen
3890
-     *
3891
-     * @return object The current WP_Screen object
3892
-     */
3893
-    public function get_current_screen()
3894
-    {
3895
-        return $this->_current_screen;
3896
-    }
3897
-
3898
-
3899
-    /**
3900
-     * get_current_page_view_url
3901
-     *
3902
-     * @return string This returns the url for the current_page_view.
3903
-     */
3904
-    public function get_current_page_view_url()
3905
-    {
3906
-        return $this->_current_page_view_url;
3907
-    }
3908
-
3909
-
3910
-    /**
3911
-     * just returns the Request
3912
-     *
3913
-     * @return RequestInterface
3914
-     */
3915
-    public function get_request()
3916
-    {
3917
-        return $this->request;
3918
-    }
3919
-
3920
-
3921
-    /**
3922
-     * just returns the _req_data property
3923
-     *
3924
-     * @return array
3925
-     */
3926
-    public function get_request_data()
3927
-    {
3928
-        return $this->request->requestParams();
3929
-    }
3930
-
3931
-
3932
-    /**
3933
-     * returns the _req_data protected property
3934
-     *
3935
-     * @return string
3936
-     */
3937
-    public function get_req_action()
3938
-    {
3939
-        return $this->_req_action;
3940
-    }
3941
-
3942
-
3943
-    /**
3944
-     * @return bool  value of $_is_caf property
3945
-     */
3946
-    public function is_caf()
3947
-    {
3948
-        return $this->_is_caf;
3949
-    }
3950
-
3951
-
3952
-    /**
3953
-     * @return mixed
3954
-     */
3955
-    public function default_espresso_metaboxes()
3956
-    {
3957
-        return $this->_default_espresso_metaboxes;
3958
-    }
3959
-
3960
-
3961
-    /**
3962
-     * @return mixed
3963
-     */
3964
-    public function admin_base_url()
3965
-    {
3966
-        return $this->_admin_base_url;
3967
-    }
3968
-
3969
-
3970
-    /**
3971
-     * @return mixed
3972
-     */
3973
-    public function wp_page_slug()
3974
-    {
3975
-        return $this->_wp_page_slug;
3976
-    }
3977
-
3978
-
3979
-    /**
3980
-     * updates  espresso configuration settings
3981
-     *
3982
-     * @param string                   $tab
3983
-     * @param EE_Config_Base|EE_Config $config
3984
-     * @param string                   $file file where error occurred
3985
-     * @param string                   $func function  where error occurred
3986
-     * @param string                   $line line no where error occurred
3987
-     * @return boolean
3988
-     */
3989
-    protected function _update_espresso_configuration($tab, $config, $file = '', $func = '', $line = '')
3990
-    {
3991
-        // remove any options that are NOT going to be saved with the config settings.
3992
-        if (isset($config->core->ee_ueip_optin)) {
3993
-            // TODO: remove the following two lines and make sure values are migrated from 3.1
3994
-            update_option('ee_ueip_optin', $config->core->ee_ueip_optin);
3995
-            update_option('ee_ueip_has_notified', true);
3996
-        }
3997
-        // and save it (note we're also doing the network save here)
3998
-        $net_saved    = ! is_main_site() || EE_Network_Config::instance()->update_config(false, false);
3999
-        $config_saved = EE_Config::instance()->update_espresso_config(false, false);
4000
-        if ($config_saved && $net_saved) {
4001
-            EE_Error::add_success(sprintf(esc_html__('"%s" have been successfully updated.', 'event_espresso'), $tab));
4002
-            return true;
4003
-        }
4004
-        EE_Error::add_error(sprintf(esc_html__('The "%s" were not updated.', 'event_espresso'), $tab), $file, $func, $line);
4005
-        return false;
4006
-    }
4007
-
4008
-
4009
-    /**
4010
-     * Returns an array to be used for EE_FOrm_Fields.helper.php's select_input as the $values argument.
4011
-     *
4012
-     * @return array
4013
-     */
4014
-    public function get_yes_no_values()
4015
-    {
4016
-        return $this->_yes_no_values;
4017
-    }
4018
-
4019
-
4020
-    /**
4021
-     * @return string
4022
-     * @throws ReflectionException
4023
-     * @since $VID:$
4024
-     */
4025
-    protected function _get_dir()
4026
-    {
4027
-        $reflector = new ReflectionClass($this->class_name);
4028
-        return dirname($reflector->getFileName());
4029
-    }
4030
-
4031
-
4032
-    /**
4033
-     * A helper for getting a "next link".
4034
-     *
4035
-     * @param string $url   The url to link to
4036
-     * @param string $class The class to use.
4037
-     * @return string
4038
-     */
4039
-    protected function _next_link($url, $class = 'dashicons dashicons-arrow-right')
4040
-    {
4041
-        return '<a class="' . $class . '" href="' . $url . '"></a>';
4042
-    }
4043
-
4044
-
4045
-    /**
4046
-     * A helper for getting a "previous link".
4047
-     *
4048
-     * @param string $url   The url to link to
4049
-     * @param string $class The class to use.
4050
-     * @return string
4051
-     */
4052
-    protected function _previous_link($url, $class = 'dashicons dashicons-arrow-left')
4053
-    {
4054
-        return '<a class="' . $class . '" href="' . $url . '"></a>';
4055
-    }
4056
-
4057
-
4058
-
4059
-
4060
-
4061
-
4062
-
4063
-    // below are some messages related methods that should be available across the EE_Admin system.  Note, these methods are NOT page specific
4064
-
4065
-
4066
-    /**
4067
-     * This processes an request to resend a registration and assumes we have a _REG_ID for doing so. So if the caller
4068
-     * knows that the _REG_ID isn't in the req_data array but CAN obtain it, the caller should ADD the _REG_ID to the
4069
-     * _req_data array.
4070
-     *
4071
-     * @return bool success/fail
4072
-     * @throws EE_Error
4073
-     * @throws InvalidArgumentException
4074
-     * @throws ReflectionException
4075
-     * @throws InvalidDataTypeException
4076
-     * @throws InvalidInterfaceException
4077
-     */
4078
-    protected function _process_resend_registration()
4079
-    {
4080
-        $this->_template_args['success'] = EED_Messages::process_resend($this->_req_data);
4081
-        do_action(
4082
-            'AHEE__EE_Admin_Page___process_resend_registration',
4083
-            $this->_template_args['success'],
4084
-            $this->request->requestParams()
4085
-        );
4086
-        return $this->_template_args['success'];
4087
-    }
4088
-
4089
-
4090
-    /**
4091
-     * This automatically processes any payment message notifications when manual payment has been applied.
4092
-     *
4093
-     * @param EE_Payment $payment
4094
-     * @return bool success/fail
4095
-     */
4096
-    protected function _process_payment_notification(EE_Payment $payment)
4097
-    {
4098
-        add_filter('FHEE__EE_Payment_Processor__process_registration_payments__display_notifications', '__return_true');
4099
-        do_action('AHEE__EE_Admin_Page___process_admin_payment_notification', $payment);
4100
-        $this->_template_args['success'] = apply_filters(
4101
-            'FHEE__EE_Admin_Page___process_admin_payment_notification__success',
4102
-            false,
4103
-            $payment
4104
-        );
4105
-        return $this->_template_args['success'];
4106
-    }
4107
-
4108
-
4109
-    /**
4110
-     * @param EEM_Base      $entity_model
4111
-     * @param string        $entity_PK_name name of the primary key field used as a request param, ie: id, ID, etc
4112
-     * @param string        $action         one of the EE_Admin_List_Table::ACTION_* constants: delete, restore, trash
4113
-     * @param string        $delete_column  name of the field that denotes whether entity is trashed
4114
-     * @param callable|null $callback       called after entity is trashed, restored, or deleted
4115
-     * @return int|float
4116
-     * @throws EE_Error
4117
-     */
4118
-    protected function trashRestoreDeleteEntities(
4119
-        EEM_Base $entity_model,
4120
-        $entity_PK_name,
4121
-        $action = EE_Admin_List_Table::ACTION_DELETE,
4122
-        $delete_column = '',
4123
-        callable $callback = null
4124
-    ) {
4125
-        $entity_PK      = $entity_model->get_primary_key_field();
4126
-        $entity_PK_name = $entity_PK_name ?: $entity_PK->get_name();
4127
-        $entity_PK_type = $this->resolveEntityFieldDataType($entity_PK);
4128
-        // grab ID if deleting a single entity
4129
-        if ($this->request->requestParamIsSet($entity_PK_name)) {
4130
-            $ID = $this->request->getRequestParam($entity_PK_name, 0, $entity_PK_type);
4131
-            return $this->trashRestoreDeleteEntity($entity_model, $ID, $action, $delete_column, $callback) ? 1 : 0;
4132
-        }
4133
-        // or grab checkbox array if bulk deleting
4134
-        $checkboxes = $this->request->getRequestParam('checkbox', [], $entity_PK_type, true);
4135
-        if (empty($checkboxes)) {
4136
-            return 0;
4137
-        }
4138
-        $success = 0;
4139
-        $IDs     = array_keys($checkboxes);
4140
-        // cycle thru bulk action checkboxes
4141
-        foreach ($IDs as $ID) {
4142
-            // increment $success
4143
-            if ($this->trashRestoreDeleteEntity($entity_model, $ID, $action, $delete_column, $callback)) {
4144
-                $success++;
4145
-            }
4146
-        }
4147
-        $count = (int) count($checkboxes);
4148
-        // if multiple entities were deleted successfully, then $deleted will be full count of deletions,
4149
-        // otherwise it will be a fraction of ( actual deletions / total entities to be deleted )
4150
-        return $success === $count ? $count : $success / $count;
4151
-    }
4152
-
4153
-
4154
-    /**
4155
-     * @param EE_Primary_Key_Field_Base $entity_PK
4156
-     * @return string
4157
-     * @throws EE_Error
4158
-     * @since   4.10.30.p
4159
-     */
4160
-    private function resolveEntityFieldDataType(EE_Primary_Key_Field_Base $entity_PK)
4161
-    {
4162
-        $entity_PK_type = $entity_PK->getSchemaType();
4163
-        switch ($entity_PK_type) {
4164
-            case 'boolean':
4165
-                return 'bool';
4166
-            case 'integer':
4167
-                return 'int';
4168
-            case 'number':
4169
-                return 'float';
4170
-            case 'string':
4171
-                return 'string';
4172
-        }
4173
-        throw new RuntimeException(
4174
-            sprintf(
4175
-                esc_html__(
4176
-                    '"%1$s" is an invalid schema type for the %2$s primary key.',
4177
-                    'event_espresso'
4178
-                ),
4179
-                $entity_PK_type,
4180
-                $entity_PK->get_name()
4181
-            )
4182
-        );
4183
-    }
4184
-
4185
-
4186
-    /**
4187
-     * @param EEM_Base      $entity_model
4188
-     * @param int|string    $entity_ID
4189
-     * @param string        $action        one of the EE_Admin_List_Table::ACTION_* constants: delete, restore, trash
4190
-     * @param string        $delete_column name of the field that denotes whether entity is trashed
4191
-     * @param callable|null $callback      called after entity is trashed, restored, or deleted
4192
-     * @return bool
4193
-     */
4194
-    protected function trashRestoreDeleteEntity(
4195
-        EEM_Base $entity_model,
4196
-        $entity_ID,
4197
-        string $action,
4198
-        string $delete_column,
4199
-        ?callable $callback = null
4200
-    ): bool {
4201
-        $entity_ID = absint($entity_ID);
4202
-        if (! $entity_ID) {
4203
-            $this->trashRestoreDeleteError($action, $entity_model);
4204
-        }
4205
-        $result = 0;
4206
-        try {
4207
-            $entity = $entity_model->get_one_by_ID($entity_ID);
4208
-            if (! $entity instanceof EE_Base_Class) {
4209
-                throw new DomainException(
4210
-                    sprintf(
4211
-                        esc_html__(
4212
-                            'Missing or invalid %1$s entity with ID of "%2$s" returned from db.',
4213
-                            'event_espresso'
4214
-                        ),
4215
-                        str_replace('EEM_', '', $entity_model->get_this_model_name()),
4216
-                        $entity_ID
4217
-                    )
4218
-                );
4219
-            }
4220
-            switch ($action) {
4221
-                case EE_Admin_List_Table::ACTION_DELETE:
4222
-                    $result = (bool) $entity->delete_permanently();
4223
-                    break;
4224
-                case EE_Admin_List_Table::ACTION_RESTORE:
4225
-                    $result = $entity->delete_or_restore(false);
4226
-                    break;
4227
-                case EE_Admin_List_Table::ACTION_TRASH:
4228
-                    $result = $entity->delete_or_restore();
4229
-                    break;
4230
-            }
4231
-        } catch (Exception $exception) {
4232
-            $this->trashRestoreDeleteError($action, $entity_model, $exception);
4233
-        }
4234
-        if (is_callable($callback)) {
4235
-            call_user_func_array($callback, [$entity_model, $entity_ID, $action, $result, $delete_column]);
4236
-        }
4237
-        return $result;
4238
-    }
4239
-
4240
-
4241
-    /**
4242
-     * @param EEM_Base $entity_model
4243
-     * @param string   $delete_column
4244
-     * @since 4.10.30.p
4245
-     */
4246
-    private function validateDeleteColumn(EEM_Base $entity_model, $delete_column)
4247
-    {
4248
-        if (empty($delete_column)) {
4249
-            throw new DomainException(
4250
-                sprintf(
4251
-                    esc_html__(
4252
-                        'You need to specify the name of the "delete column" on the %2$s model, in order to trash or restore an entity.',
4253
-                        'event_espresso'
4254
-                    ),
4255
-                    $entity_model->get_this_model_name()
4256
-                )
4257
-            );
4258
-        }
4259
-        if (! $entity_model->has_field($delete_column)) {
4260
-            throw new DomainException(
4261
-                sprintf(
4262
-                    esc_html__(
4263
-                        'The %1$s field does not exist on the %2$s model.',
4264
-                        'event_espresso'
4265
-                    ),
4266
-                    $delete_column,
4267
-                    $entity_model->get_this_model_name()
4268
-                )
4269
-            );
4270
-        }
4271
-    }
4272
-
4273
-
4274
-    /**
4275
-     * @param EEM_Base       $entity_model
4276
-     * @param Exception|null $exception
4277
-     * @param string         $action
4278
-     * @since 4.10.30.p
4279
-     */
4280
-    private function trashRestoreDeleteError($action, EEM_Base $entity_model, Exception $exception = null)
4281
-    {
4282
-        if ($exception instanceof Exception) {
4283
-            throw new RuntimeException(
4284
-                sprintf(
4285
-                    esc_html__(
4286
-                        'Could not %1$s the %2$s because the following error occurred: %3$s',
4287
-                        'event_espresso'
4288
-                    ),
4289
-                    $action,
4290
-                    $entity_model->get_this_model_name(),
4291
-                    $exception->getMessage()
4292
-                )
4293
-            );
4294
-        }
4295
-        throw new RuntimeException(
4296
-            sprintf(
4297
-                esc_html__(
4298
-                    'Could not %1$s the %2$s because an invalid ID was received.',
4299
-                    'event_espresso'
4300
-                ),
4301
-                $action,
4302
-                $entity_model->get_this_model_name()
4303
-            )
4304
-        );
4305
-    }
3327
+		// add nonce
3328
+		$nonce                                             =
3329
+			wp_nonce_field($route . '_nonce', $route . '_nonce', false, false);
3330
+		$this->_template_args['before_admin_page_content'] .= "\n\t" . $nonce;
3331
+		// add REQUIRED form action
3332
+		$hidden_fields = [
3333
+			'action' => ['type' => 'hidden', 'value' => $route],
3334
+		];
3335
+		// merge arrays
3336
+		$hidden_fields = is_array($additional_hidden_fields)
3337
+			? array_merge($hidden_fields, $additional_hidden_fields)
3338
+			: $hidden_fields;
3339
+		// generate form fields
3340
+		$form_fields = $this->_generate_admin_form_fields($hidden_fields, 'array');
3341
+		// add fields to form
3342
+		foreach ((array) $form_fields as $form_field) {
3343
+			$this->_template_args['before_admin_page_content'] .= "\n\t" . $form_field['field'];
3344
+		}
3345
+		// close form
3346
+		$this->_template_args['after_admin_page_content'] = '</form>';
3347
+	}
3348
+
3349
+
3350
+	/**
3351
+	 * Public Wrapper for _redirect_after_action() method since its
3352
+	 * discovered it would be useful for external code to have access.
3353
+	 *
3354
+	 * @param bool   $success
3355
+	 * @param string $what
3356
+	 * @param string $action_desc
3357
+	 * @param array  $query_args
3358
+	 * @param bool   $override_overwrite
3359
+	 * @throws EE_Error
3360
+	 * @see   EE_Admin_Page::_redirect_after_action() for params.
3361
+	 * @since 4.5.0
3362
+	 */
3363
+	public function redirect_after_action(
3364
+		$success = false,
3365
+		$what = 'item',
3366
+		$action_desc = 'processed',
3367
+		$query_args = [],
3368
+		$override_overwrite = false
3369
+	) {
3370
+		$this->_redirect_after_action(
3371
+			$success,
3372
+			$what,
3373
+			$action_desc,
3374
+			$query_args,
3375
+			$override_overwrite
3376
+		);
3377
+	}
3378
+
3379
+
3380
+	/**
3381
+	 * Helper method for merging existing request data with the returned redirect url.
3382
+	 *
3383
+	 * This is typically used for redirects after an action so that if the original view was a filtered view those
3384
+	 * filters are still applied.
3385
+	 *
3386
+	 * @param array $new_route_data
3387
+	 * @return array
3388
+	 */
3389
+	protected function mergeExistingRequestParamsWithRedirectArgs(array $new_route_data)
3390
+	{
3391
+		foreach ($this->request->requestParams() as $ref => $value) {
3392
+			// unset nonces
3393
+			if (strpos($ref, 'nonce') !== false) {
3394
+				$this->request->unSetRequestParam($ref);
3395
+				continue;
3396
+			}
3397
+			// urlencode values.
3398
+			$value = is_array($value) ? array_map('urlencode', $value) : urlencode($value);
3399
+			$this->request->setRequestParam($ref, $value);
3400
+		}
3401
+		return array_merge($this->request->requestParams(), $new_route_data);
3402
+	}
3403
+
3404
+
3405
+	/**
3406
+	 * @param int|float|string $success      - whether success was for two or more records, or just one, or none
3407
+	 * @param string           $what         - what the action was performed on
3408
+	 * @param string           $action_desc  - what was done ie: updated, deleted, etc
3409
+	 * @param array $query_args              - an array of query_args to be added to the URL to redirect to
3410
+	 * @param BOOL $override_overwrite       - by default all EE_Error::success messages are overwritten,
3411
+	 *                                         this allows you to override this so that they show.
3412
+	 * @return void
3413
+	 * @throws EE_Error
3414
+	 * @throws InvalidArgumentException
3415
+	 * @throws InvalidDataTypeException
3416
+	 * @throws InvalidInterfaceException
3417
+	 */
3418
+	protected function _redirect_after_action(
3419
+		$success = 0,
3420
+		string $what = 'item',
3421
+		string $action_desc = 'processed',
3422
+		array $query_args = [],
3423
+		bool $override_overwrite = false
3424
+	) {
3425
+		do_action('AHEE_log', __FILE__, __FUNCTION__, '');
3426
+		$notices      = EE_Error::get_notices(false);
3427
+		// overwrite default success messages //BUT ONLY if overwrite not overridden
3428
+		if (! $override_overwrite || ! empty($notices['errors'])) {
3429
+			EE_Error::overwrite_success();
3430
+		}
3431
+		if (! $override_overwrite && ! empty($what) && ! empty($action_desc) && empty($notices['errors'])) {
3432
+			// how many records affected ? more than one record ? or just one ?
3433
+			EE_Error::add_success(
3434
+				sprintf(
3435
+					esc_html(
3436
+						_n(
3437
+							'The "%1$s" has been successfully %2$s.',
3438
+							'The "%1$s" have been successfully %2$s.',
3439
+							$success,
3440
+							'event_espresso'
3441
+						)
3442
+					),
3443
+					$what,
3444
+					$action_desc
3445
+				),
3446
+				__FILE__,
3447
+				__FUNCTION__,
3448
+				__LINE__
3449
+			);
3450
+		}
3451
+		// check that $query_args isn't something crazy
3452
+		if (! is_array($query_args)) {
3453
+			$query_args = [];
3454
+		}
3455
+		/**
3456
+		 * Allow injecting actions before the query_args are modified for possible different
3457
+		 * redirections on save and close actions
3458
+		 *
3459
+		 * @param array $query_args       The original query_args array coming into the
3460
+		 *                                method.
3461
+		 * @since 4.2.0
3462
+		 */
3463
+		do_action(
3464
+			"AHEE__{$this->class_name}___redirect_after_action__before_redirect_modification_{$this->_req_action}",
3465
+			$query_args
3466
+		);
3467
+		// set redirect url.
3468
+		// Note if there is a "page" index in the $query_args then we go with vanilla admin.php route,
3469
+		// otherwise we go with whatever is set as the _admin_base_url
3470
+		$redirect_url = isset($query_args['page']) ? admin_url('admin.php') : $this->_admin_base_url;
3471
+		// calculate where we're going (if we have a "save and close" button pushed)
3472
+		if (
3473
+			$this->request->requestParamIsSet('save_and_close')
3474
+			&& $this->request->requestParamIsSet('save_and_close_referrer')
3475
+		) {
3476
+			// even though we have the save_and_close referrer, we need to parse the url for the action in order to generate a nonce
3477
+			$parsed_url = parse_url($this->request->getRequestParam('save_and_close_referrer', '', 'url'));
3478
+			// regenerate query args array from referrer URL
3479
+			parse_str($parsed_url['query'], $query_args);
3480
+			// correct page and action will be in the query args now
3481
+			$redirect_url = admin_url('admin.php');
3482
+		}
3483
+		// merge any default query_args set in _default_route_query_args property
3484
+		if (! empty($this->_default_route_query_args) && ! $this->_is_UI_request) {
3485
+			$args_to_merge = [];
3486
+			foreach ($this->_default_route_query_args as $query_param => $query_value) {
3487
+				// is there a wp_referer array in our _default_route_query_args property?
3488
+				if ($query_param === 'wp_referer') {
3489
+					$query_value = (array) $query_value;
3490
+					foreach ($query_value as $reference => $value) {
3491
+						if (strpos($reference, 'nonce') !== false) {
3492
+							continue;
3493
+						}
3494
+						// finally we will override any arguments in the referer with
3495
+						// what might be set on the _default_route_query_args array.
3496
+						if (isset($this->_default_route_query_args[ $reference ])) {
3497
+							$args_to_merge[ $reference ] = urlencode($this->_default_route_query_args[ $reference ]);
3498
+						} else {
3499
+							$args_to_merge[ $reference ] = urlencode($value);
3500
+						}
3501
+					}
3502
+					continue;
3503
+				}
3504
+				$args_to_merge[ $query_param ] = $query_value;
3505
+			}
3506
+			// now let's merge these arguments but override with what was specifically sent in to the
3507
+			// redirect.
3508
+			$query_args = array_merge($args_to_merge, $query_args);
3509
+		}
3510
+		$this->_process_notices($query_args);
3511
+		// generate redirect url
3512
+		// if redirecting to anything other than the main page, add a nonce
3513
+		if (isset($query_args['action'])) {
3514
+			// manually generate wp_nonce and merge that with the query vars
3515
+			// becuz the wp_nonce_url function wrecks havoc on some vars
3516
+			$query_args['_wpnonce'] = wp_create_nonce($query_args['action'] . '_nonce');
3517
+		}
3518
+		// we're adding some hooks and filters in here for processing any things just before redirects
3519
+		// (example: an admin page has done an insert or update and we want to run something after that).
3520
+		do_action('AHEE_redirect_' . $this->class_name . $this->_req_action, $query_args);
3521
+		$redirect_url = apply_filters(
3522
+			'FHEE_redirect_' . $this->class_name . $this->_req_action,
3523
+			EE_Admin_Page::add_query_args_and_nonce($query_args, $redirect_url),
3524
+			$query_args
3525
+		);
3526
+		// check if we're doing ajax.  If we are then lets just return the results and js can handle how it wants.
3527
+		if ($this->request->isAjax()) {
3528
+			$default_data                    = [
3529
+				'close'        => true,
3530
+				'redirect_url' => $redirect_url,
3531
+				'where'        => 'main',
3532
+				'what'         => 'append',
3533
+			];
3534
+			$this->_template_args['success'] = $success;
3535
+			$this->_template_args['data']    = ! empty($this->_template_args['data']) ? array_merge(
3536
+				$default_data,
3537
+				$this->_template_args['data']
3538
+			) : $default_data;
3539
+			$this->_return_json();
3540
+		}
3541
+		wp_safe_redirect($redirect_url);
3542
+		exit();
3543
+	}
3544
+
3545
+
3546
+	/**
3547
+	 * process any notices before redirecting (or returning ajax request)
3548
+	 * This method sets the $this->_template_args['notices'] attribute;
3549
+	 *
3550
+	 * @param array $query_args         any query args that need to be used for notice transient ('action')
3551
+	 * @param bool  $skip_route_verify  This is typically used when we are processing notices REALLY early and
3552
+	 *                                  page_routes haven't been defined yet.
3553
+	 * @param bool  $sticky_notices     This is used to flag that regardless of whether this is doing_ajax or not, we
3554
+	 *                                  still save a transient for the notice.
3555
+	 * @return void
3556
+	 * @throws EE_Error
3557
+	 * @throws InvalidArgumentException
3558
+	 * @throws InvalidDataTypeException
3559
+	 * @throws InvalidInterfaceException
3560
+	 */
3561
+	protected function _process_notices($query_args = [], $skip_route_verify = false, $sticky_notices = true)
3562
+	{
3563
+		// first let's set individual error properties if doing_ajax and the properties aren't already set.
3564
+		if ($this->request->isAjax()) {
3565
+			$notices = EE_Error::get_notices(false);
3566
+			if (empty($this->_template_args['success'])) {
3567
+				$this->_template_args['success'] = isset($notices['success']) ? $notices['success'] : false;
3568
+			}
3569
+			if (empty($this->_template_args['errors'])) {
3570
+				$this->_template_args['errors'] = isset($notices['errors']) ? $notices['errors'] : false;
3571
+			}
3572
+			if (empty($this->_template_args['attention'])) {
3573
+				$this->_template_args['attention'] = isset($notices['attention']) ? $notices['attention'] : false;
3574
+			}
3575
+		}
3576
+		$this->_template_args['notices'] = EE_Error::get_notices();
3577
+		// IF this isn't ajax we need to create a transient for the notices using the route (however, overridden if $sticky_notices == true)
3578
+		if (! $this->request->isAjax() || $sticky_notices) {
3579
+			$route = isset($query_args['action']) ? $query_args['action'] : 'default';
3580
+			$this->_add_transient(
3581
+				$route,
3582
+				$this->_template_args['notices'],
3583
+				true,
3584
+				$skip_route_verify
3585
+			);
3586
+		}
3587
+	}
3588
+
3589
+
3590
+	/**
3591
+	 * get_action_link_or_button
3592
+	 * returns the button html for adding, editing, or deleting an item (depending on given type)
3593
+	 *
3594
+	 * @param string $action        use this to indicate which action the url is generated with.
3595
+	 * @param string $type          accepted strings must be defined in the $_labels['button'] array(as the key)
3596
+	 *                              property.
3597
+	 * @param array  $extra_request if the button requires extra params you can include them in $key=>$value pairs.
3598
+	 * @param string $class         Use this to give the class for the button. Defaults to 'button--primary'
3599
+	 * @param string $base_url      If this is not provided
3600
+	 *                              the _admin_base_url will be used as the default for the button base_url.
3601
+	 *                              Otherwise this value will be used.
3602
+	 * @param bool   $exclude_nonce If true then no nonce will be in the generated button link.
3603
+	 * @return string
3604
+	 * @throws InvalidArgumentException
3605
+	 * @throws InvalidInterfaceException
3606
+	 * @throws InvalidDataTypeException
3607
+	 * @throws EE_Error
3608
+	 */
3609
+	public function get_action_link_or_button(
3610
+		$action,
3611
+		$type = 'add',
3612
+		$extra_request = [],
3613
+		$class = 'button button--primary',
3614
+		$base_url = '',
3615
+		$exclude_nonce = false
3616
+	) {
3617
+		// first let's validate the action (if $base_url is FALSE otherwise validation will happen further along)
3618
+		if (empty($base_url) && ! isset($this->_page_routes[ $action ])) {
3619
+			throw new EE_Error(
3620
+				sprintf(
3621
+					esc_html__(
3622
+						'There is no page route for given action for the button.  This action was given: %s',
3623
+						'event_espresso'
3624
+					),
3625
+					$action
3626
+				)
3627
+			);
3628
+		}
3629
+		if (! isset($this->_labels['buttons'][ $type ])) {
3630
+			throw new EE_Error(
3631
+				sprintf(
3632
+					esc_html__(
3633
+						'There is no label for the given button type (%s). Labels are set in the <code>_page_config</code> property.',
3634
+						'event_espresso'
3635
+					),
3636
+					$type
3637
+				)
3638
+			);
3639
+		}
3640
+		// finally check user access for this button.
3641
+		$has_access = $this->check_user_access($action, true);
3642
+		if (! $has_access) {
3643
+			return '';
3644
+		}
3645
+		$_base_url  = ! $base_url ? $this->_admin_base_url : $base_url;
3646
+		$query_args = [
3647
+			'action' => $action,
3648
+		];
3649
+		// merge extra_request args but make sure our original action takes precedence and doesn't get overwritten.
3650
+		if (! empty($extra_request)) {
3651
+			$query_args = array_merge($extra_request, $query_args);
3652
+		}
3653
+		$url = EE_Admin_Page::add_query_args_and_nonce($query_args, $_base_url, false, $exclude_nonce);
3654
+		return EEH_Template::get_button_or_link($url, $this->_labels['buttons'][ $type ], $class);
3655
+	}
3656
+
3657
+
3658
+	/**
3659
+	 * _per_page_screen_option
3660
+	 * Utility function for adding in a per_page_option in the screen_options_dropdown.
3661
+	 *
3662
+	 * @return void
3663
+	 * @throws InvalidArgumentException
3664
+	 * @throws InvalidInterfaceException
3665
+	 * @throws InvalidDataTypeException
3666
+	 */
3667
+	protected function _per_page_screen_option()
3668
+	{
3669
+		$option = 'per_page';
3670
+		$args   = [
3671
+			'label'   => apply_filters(
3672
+				'FHEE__EE_Admin_Page___per_page_screen_options___label',
3673
+				$this->_admin_page_title,
3674
+				$this
3675
+			),
3676
+			'default' => (int) apply_filters(
3677
+				'FHEE__EE_Admin_Page___per_page_screen_options__default',
3678
+				20
3679
+			),
3680
+			'option'  => $this->_current_page . '_' . $this->_current_view . '_per_page',
3681
+		];
3682
+		// ONLY add the screen option if the user has access to it.
3683
+		if ($this->check_user_access($this->_current_view, true)) {
3684
+			add_screen_option($option, $args);
3685
+		}
3686
+	}
3687
+
3688
+
3689
+	/**
3690
+	 * set_per_page_screen_option
3691
+	 * All this does is make sure that WordPress saves any per_page screen options (if set) for the current page.
3692
+	 * we have to do this rather than running inside the 'set-screen-options' hook because it runs earlier than
3693
+	 * admin_menu.
3694
+	 *
3695
+	 * @return void
3696
+	 */
3697
+	private function _set_per_page_screen_options()
3698
+	{
3699
+		if ($this->request->requestParamIsSet('wp_screen_options')) {
3700
+			check_admin_referer('screen-options-nonce', 'screenoptionnonce');
3701
+			if (! $user = wp_get_current_user()) {
3702
+				return;
3703
+			}
3704
+			$option = $this->request->getRequestParam('wp_screen_options[option]', '', 'key');
3705
+			if (! $option) {
3706
+				return;
3707
+			}
3708
+			$value  = $this->request->getRequestParam('wp_screen_options[value]', 0, 'int');
3709
+			$map_option = $option;
3710
+			$option     = str_replace('-', '_', $option);
3711
+			switch ($map_option) {
3712
+				case $this->_current_page . '_' . $this->_current_view . '_per_page':
3713
+					$max_value = apply_filters(
3714
+						'FHEE__EE_Admin_Page___set_per_page_screen_options__max_value',
3715
+						999,
3716
+						$this->_current_page,
3717
+						$this->_current_view
3718
+					);
3719
+					if ($value < 1) {
3720
+						return;
3721
+					}
3722
+					$value = min($value, $max_value);
3723
+					break;
3724
+				default:
3725
+					$value = apply_filters(
3726
+						'FHEE__EE_Admin_Page___set_per_page_screen_options__value',
3727
+						false,
3728
+						$option,
3729
+						$value
3730
+					);
3731
+					if (false === $value) {
3732
+						return;
3733
+					}
3734
+					break;
3735
+			}
3736
+			update_user_meta($user->ID, $option, $value);
3737
+			wp_safe_redirect(remove_query_arg(['pagenum', 'apage', 'paged'], wp_get_referer()));
3738
+			exit;
3739
+		}
3740
+	}
3741
+
3742
+
3743
+	/**
3744
+	 * This just allows for setting the $_template_args property if it needs to be set outside the object
3745
+	 *
3746
+	 * @param array $data array that will be assigned to template args.
3747
+	 */
3748
+	public function set_template_args($data)
3749
+	{
3750
+		$this->_template_args = array_merge($this->_template_args, (array) $data);
3751
+	}
3752
+
3753
+
3754
+	/**
3755
+	 * This makes available the WP transient system for temporarily moving data between routes
3756
+	 *
3757
+	 * @param string $route             the route that should receive the transient
3758
+	 * @param array  $data              the data that gets sent
3759
+	 * @param bool   $notices           If this is for notices then we use this to indicate so, otherwise its just a
3760
+	 *                                  normal route transient.
3761
+	 * @param bool   $skip_route_verify Used to indicate we want to skip route verification.  This is usually ONLY used
3762
+	 *                                  when we are adding a transient before page_routes have been defined.
3763
+	 * @return void
3764
+	 * @throws EE_Error
3765
+	 */
3766
+	protected function _add_transient($route, $data, $notices = false, $skip_route_verify = false)
3767
+	{
3768
+		$user_id = get_current_user_id();
3769
+		if (! $skip_route_verify) {
3770
+			$this->_verify_route($route);
3771
+		}
3772
+		// now let's set the string for what kind of transient we're setting
3773
+		$transient = $notices
3774
+			? 'ee_rte_n_tx_' . $route . '_' . $user_id
3775
+			: 'rte_tx_' . $route . '_' . $user_id;
3776
+		$data      = $notices ? ['notices' => $data] : $data;
3777
+		// is there already a transient for this route?  If there is then let's ADD to that transient
3778
+		$existing = is_multisite() && is_network_admin()
3779
+			? get_site_transient($transient)
3780
+			: get_transient($transient);
3781
+		if ($existing) {
3782
+			$data = array_merge((array) $data, (array) $existing);
3783
+		}
3784
+		if (is_multisite() && is_network_admin()) {
3785
+			set_site_transient($transient, $data, 8);
3786
+		} else {
3787
+			set_transient($transient, $data, 8);
3788
+		}
3789
+	}
3790
+
3791
+
3792
+	/**
3793
+	 * this retrieves the temporary transient that has been set for moving data between routes.
3794
+	 *
3795
+	 * @param bool   $notices true we get notices transient. False we just return normal route transient
3796
+	 * @param string $route
3797
+	 * @return mixed data
3798
+	 */
3799
+	protected function _get_transient($notices = false, $route = '')
3800
+	{
3801
+		$user_id   = get_current_user_id();
3802
+		$route     = ! $route ? $this->_req_action : $route;
3803
+		$transient = $notices
3804
+			? 'ee_rte_n_tx_' . $route . '_' . $user_id
3805
+			: 'rte_tx_' . $route . '_' . $user_id;
3806
+		$data      = is_multisite() && is_network_admin()
3807
+			? get_site_transient($transient)
3808
+			: get_transient($transient);
3809
+		// delete transient after retrieval (just in case it hasn't expired);
3810
+		if (is_multisite() && is_network_admin()) {
3811
+			delete_site_transient($transient);
3812
+		} else {
3813
+			delete_transient($transient);
3814
+		}
3815
+		return $notices && isset($data['notices']) ? $data['notices'] : $data;
3816
+	}
3817
+
3818
+
3819
+	/**
3820
+	 * The purpose of this method is just to run garbage collection on any EE transients that might have expired but
3821
+	 * would not be called later. This will be assigned to run on a specific EE Admin page. (place the method in the
3822
+	 * default route callback on the EE_Admin page you want it run.)
3823
+	 *
3824
+	 * @return void
3825
+	 */
3826
+	protected function _transient_garbage_collection()
3827
+	{
3828
+		global $wpdb;
3829
+		// retrieve all existing transients
3830
+		$query =
3831
+			"SELECT option_name FROM {$wpdb->options} WHERE option_name LIKE '%rte_tx_%' OR option_name LIKE '%rte_n_tx_%'";
3832
+		if ($results = $wpdb->get_results($query)) {
3833
+			foreach ($results as $result) {
3834
+				$transient = str_replace('_transient_', '', $result->option_name);
3835
+				get_transient($transient);
3836
+				if (is_multisite() && is_network_admin()) {
3837
+					get_site_transient($transient);
3838
+				}
3839
+			}
3840
+		}
3841
+	}
3842
+
3843
+
3844
+	/**
3845
+	 * get_view
3846
+	 *
3847
+	 * @return string content of _view property
3848
+	 */
3849
+	public function get_view()
3850
+	{
3851
+		return $this->_view;
3852
+	}
3853
+
3854
+
3855
+	/**
3856
+	 * getter for the protected $_views property
3857
+	 *
3858
+	 * @return array
3859
+	 */
3860
+	public function get_views()
3861
+	{
3862
+		return $this->_views;
3863
+	}
3864
+
3865
+
3866
+	/**
3867
+	 * get_current_page
3868
+	 *
3869
+	 * @return string _current_page property value
3870
+	 */
3871
+	public function get_current_page()
3872
+	{
3873
+		return $this->_current_page;
3874
+	}
3875
+
3876
+
3877
+	/**
3878
+	 * get_current_view
3879
+	 *
3880
+	 * @return string _current_view property value
3881
+	 */
3882
+	public function get_current_view()
3883
+	{
3884
+		return $this->_current_view;
3885
+	}
3886
+
3887
+
3888
+	/**
3889
+	 * get_current_screen
3890
+	 *
3891
+	 * @return object The current WP_Screen object
3892
+	 */
3893
+	public function get_current_screen()
3894
+	{
3895
+		return $this->_current_screen;
3896
+	}
3897
+
3898
+
3899
+	/**
3900
+	 * get_current_page_view_url
3901
+	 *
3902
+	 * @return string This returns the url for the current_page_view.
3903
+	 */
3904
+	public function get_current_page_view_url()
3905
+	{
3906
+		return $this->_current_page_view_url;
3907
+	}
3908
+
3909
+
3910
+	/**
3911
+	 * just returns the Request
3912
+	 *
3913
+	 * @return RequestInterface
3914
+	 */
3915
+	public function get_request()
3916
+	{
3917
+		return $this->request;
3918
+	}
3919
+
3920
+
3921
+	/**
3922
+	 * just returns the _req_data property
3923
+	 *
3924
+	 * @return array
3925
+	 */
3926
+	public function get_request_data()
3927
+	{
3928
+		return $this->request->requestParams();
3929
+	}
3930
+
3931
+
3932
+	/**
3933
+	 * returns the _req_data protected property
3934
+	 *
3935
+	 * @return string
3936
+	 */
3937
+	public function get_req_action()
3938
+	{
3939
+		return $this->_req_action;
3940
+	}
3941
+
3942
+
3943
+	/**
3944
+	 * @return bool  value of $_is_caf property
3945
+	 */
3946
+	public function is_caf()
3947
+	{
3948
+		return $this->_is_caf;
3949
+	}
3950
+
3951
+
3952
+	/**
3953
+	 * @return mixed
3954
+	 */
3955
+	public function default_espresso_metaboxes()
3956
+	{
3957
+		return $this->_default_espresso_metaboxes;
3958
+	}
3959
+
3960
+
3961
+	/**
3962
+	 * @return mixed
3963
+	 */
3964
+	public function admin_base_url()
3965
+	{
3966
+		return $this->_admin_base_url;
3967
+	}
3968
+
3969
+
3970
+	/**
3971
+	 * @return mixed
3972
+	 */
3973
+	public function wp_page_slug()
3974
+	{
3975
+		return $this->_wp_page_slug;
3976
+	}
3977
+
3978
+
3979
+	/**
3980
+	 * updates  espresso configuration settings
3981
+	 *
3982
+	 * @param string                   $tab
3983
+	 * @param EE_Config_Base|EE_Config $config
3984
+	 * @param string                   $file file where error occurred
3985
+	 * @param string                   $func function  where error occurred
3986
+	 * @param string                   $line line no where error occurred
3987
+	 * @return boolean
3988
+	 */
3989
+	protected function _update_espresso_configuration($tab, $config, $file = '', $func = '', $line = '')
3990
+	{
3991
+		// remove any options that are NOT going to be saved with the config settings.
3992
+		if (isset($config->core->ee_ueip_optin)) {
3993
+			// TODO: remove the following two lines and make sure values are migrated from 3.1
3994
+			update_option('ee_ueip_optin', $config->core->ee_ueip_optin);
3995
+			update_option('ee_ueip_has_notified', true);
3996
+		}
3997
+		// and save it (note we're also doing the network save here)
3998
+		$net_saved    = ! is_main_site() || EE_Network_Config::instance()->update_config(false, false);
3999
+		$config_saved = EE_Config::instance()->update_espresso_config(false, false);
4000
+		if ($config_saved && $net_saved) {
4001
+			EE_Error::add_success(sprintf(esc_html__('"%s" have been successfully updated.', 'event_espresso'), $tab));
4002
+			return true;
4003
+		}
4004
+		EE_Error::add_error(sprintf(esc_html__('The "%s" were not updated.', 'event_espresso'), $tab), $file, $func, $line);
4005
+		return false;
4006
+	}
4007
+
4008
+
4009
+	/**
4010
+	 * Returns an array to be used for EE_FOrm_Fields.helper.php's select_input as the $values argument.
4011
+	 *
4012
+	 * @return array
4013
+	 */
4014
+	public function get_yes_no_values()
4015
+	{
4016
+		return $this->_yes_no_values;
4017
+	}
4018
+
4019
+
4020
+	/**
4021
+	 * @return string
4022
+	 * @throws ReflectionException
4023
+	 * @since $VID:$
4024
+	 */
4025
+	protected function _get_dir()
4026
+	{
4027
+		$reflector = new ReflectionClass($this->class_name);
4028
+		return dirname($reflector->getFileName());
4029
+	}
4030
+
4031
+
4032
+	/**
4033
+	 * A helper for getting a "next link".
4034
+	 *
4035
+	 * @param string $url   The url to link to
4036
+	 * @param string $class The class to use.
4037
+	 * @return string
4038
+	 */
4039
+	protected function _next_link($url, $class = 'dashicons dashicons-arrow-right')
4040
+	{
4041
+		return '<a class="' . $class . '" href="' . $url . '"></a>';
4042
+	}
4043
+
4044
+
4045
+	/**
4046
+	 * A helper for getting a "previous link".
4047
+	 *
4048
+	 * @param string $url   The url to link to
4049
+	 * @param string $class The class to use.
4050
+	 * @return string
4051
+	 */
4052
+	protected function _previous_link($url, $class = 'dashicons dashicons-arrow-left')
4053
+	{
4054
+		return '<a class="' . $class . '" href="' . $url . '"></a>';
4055
+	}
4056
+
4057
+
4058
+
4059
+
4060
+
4061
+
4062
+
4063
+	// below are some messages related methods that should be available across the EE_Admin system.  Note, these methods are NOT page specific
4064
+
4065
+
4066
+	/**
4067
+	 * This processes an request to resend a registration and assumes we have a _REG_ID for doing so. So if the caller
4068
+	 * knows that the _REG_ID isn't in the req_data array but CAN obtain it, the caller should ADD the _REG_ID to the
4069
+	 * _req_data array.
4070
+	 *
4071
+	 * @return bool success/fail
4072
+	 * @throws EE_Error
4073
+	 * @throws InvalidArgumentException
4074
+	 * @throws ReflectionException
4075
+	 * @throws InvalidDataTypeException
4076
+	 * @throws InvalidInterfaceException
4077
+	 */
4078
+	protected function _process_resend_registration()
4079
+	{
4080
+		$this->_template_args['success'] = EED_Messages::process_resend($this->_req_data);
4081
+		do_action(
4082
+			'AHEE__EE_Admin_Page___process_resend_registration',
4083
+			$this->_template_args['success'],
4084
+			$this->request->requestParams()
4085
+		);
4086
+		return $this->_template_args['success'];
4087
+	}
4088
+
4089
+
4090
+	/**
4091
+	 * This automatically processes any payment message notifications when manual payment has been applied.
4092
+	 *
4093
+	 * @param EE_Payment $payment
4094
+	 * @return bool success/fail
4095
+	 */
4096
+	protected function _process_payment_notification(EE_Payment $payment)
4097
+	{
4098
+		add_filter('FHEE__EE_Payment_Processor__process_registration_payments__display_notifications', '__return_true');
4099
+		do_action('AHEE__EE_Admin_Page___process_admin_payment_notification', $payment);
4100
+		$this->_template_args['success'] = apply_filters(
4101
+			'FHEE__EE_Admin_Page___process_admin_payment_notification__success',
4102
+			false,
4103
+			$payment
4104
+		);
4105
+		return $this->_template_args['success'];
4106
+	}
4107
+
4108
+
4109
+	/**
4110
+	 * @param EEM_Base      $entity_model
4111
+	 * @param string        $entity_PK_name name of the primary key field used as a request param, ie: id, ID, etc
4112
+	 * @param string        $action         one of the EE_Admin_List_Table::ACTION_* constants: delete, restore, trash
4113
+	 * @param string        $delete_column  name of the field that denotes whether entity is trashed
4114
+	 * @param callable|null $callback       called after entity is trashed, restored, or deleted
4115
+	 * @return int|float
4116
+	 * @throws EE_Error
4117
+	 */
4118
+	protected function trashRestoreDeleteEntities(
4119
+		EEM_Base $entity_model,
4120
+		$entity_PK_name,
4121
+		$action = EE_Admin_List_Table::ACTION_DELETE,
4122
+		$delete_column = '',
4123
+		callable $callback = null
4124
+	) {
4125
+		$entity_PK      = $entity_model->get_primary_key_field();
4126
+		$entity_PK_name = $entity_PK_name ?: $entity_PK->get_name();
4127
+		$entity_PK_type = $this->resolveEntityFieldDataType($entity_PK);
4128
+		// grab ID if deleting a single entity
4129
+		if ($this->request->requestParamIsSet($entity_PK_name)) {
4130
+			$ID = $this->request->getRequestParam($entity_PK_name, 0, $entity_PK_type);
4131
+			return $this->trashRestoreDeleteEntity($entity_model, $ID, $action, $delete_column, $callback) ? 1 : 0;
4132
+		}
4133
+		// or grab checkbox array if bulk deleting
4134
+		$checkboxes = $this->request->getRequestParam('checkbox', [], $entity_PK_type, true);
4135
+		if (empty($checkboxes)) {
4136
+			return 0;
4137
+		}
4138
+		$success = 0;
4139
+		$IDs     = array_keys($checkboxes);
4140
+		// cycle thru bulk action checkboxes
4141
+		foreach ($IDs as $ID) {
4142
+			// increment $success
4143
+			if ($this->trashRestoreDeleteEntity($entity_model, $ID, $action, $delete_column, $callback)) {
4144
+				$success++;
4145
+			}
4146
+		}
4147
+		$count = (int) count($checkboxes);
4148
+		// if multiple entities were deleted successfully, then $deleted will be full count of deletions,
4149
+		// otherwise it will be a fraction of ( actual deletions / total entities to be deleted )
4150
+		return $success === $count ? $count : $success / $count;
4151
+	}
4152
+
4153
+
4154
+	/**
4155
+	 * @param EE_Primary_Key_Field_Base $entity_PK
4156
+	 * @return string
4157
+	 * @throws EE_Error
4158
+	 * @since   4.10.30.p
4159
+	 */
4160
+	private function resolveEntityFieldDataType(EE_Primary_Key_Field_Base $entity_PK)
4161
+	{
4162
+		$entity_PK_type = $entity_PK->getSchemaType();
4163
+		switch ($entity_PK_type) {
4164
+			case 'boolean':
4165
+				return 'bool';
4166
+			case 'integer':
4167
+				return 'int';
4168
+			case 'number':
4169
+				return 'float';
4170
+			case 'string':
4171
+				return 'string';
4172
+		}
4173
+		throw new RuntimeException(
4174
+			sprintf(
4175
+				esc_html__(
4176
+					'"%1$s" is an invalid schema type for the %2$s primary key.',
4177
+					'event_espresso'
4178
+				),
4179
+				$entity_PK_type,
4180
+				$entity_PK->get_name()
4181
+			)
4182
+		);
4183
+	}
4184
+
4185
+
4186
+	/**
4187
+	 * @param EEM_Base      $entity_model
4188
+	 * @param int|string    $entity_ID
4189
+	 * @param string        $action        one of the EE_Admin_List_Table::ACTION_* constants: delete, restore, trash
4190
+	 * @param string        $delete_column name of the field that denotes whether entity is trashed
4191
+	 * @param callable|null $callback      called after entity is trashed, restored, or deleted
4192
+	 * @return bool
4193
+	 */
4194
+	protected function trashRestoreDeleteEntity(
4195
+		EEM_Base $entity_model,
4196
+		$entity_ID,
4197
+		string $action,
4198
+		string $delete_column,
4199
+		?callable $callback = null
4200
+	): bool {
4201
+		$entity_ID = absint($entity_ID);
4202
+		if (! $entity_ID) {
4203
+			$this->trashRestoreDeleteError($action, $entity_model);
4204
+		}
4205
+		$result = 0;
4206
+		try {
4207
+			$entity = $entity_model->get_one_by_ID($entity_ID);
4208
+			if (! $entity instanceof EE_Base_Class) {
4209
+				throw new DomainException(
4210
+					sprintf(
4211
+						esc_html__(
4212
+							'Missing or invalid %1$s entity with ID of "%2$s" returned from db.',
4213
+							'event_espresso'
4214
+						),
4215
+						str_replace('EEM_', '', $entity_model->get_this_model_name()),
4216
+						$entity_ID
4217
+					)
4218
+				);
4219
+			}
4220
+			switch ($action) {
4221
+				case EE_Admin_List_Table::ACTION_DELETE:
4222
+					$result = (bool) $entity->delete_permanently();
4223
+					break;
4224
+				case EE_Admin_List_Table::ACTION_RESTORE:
4225
+					$result = $entity->delete_or_restore(false);
4226
+					break;
4227
+				case EE_Admin_List_Table::ACTION_TRASH:
4228
+					$result = $entity->delete_or_restore();
4229
+					break;
4230
+			}
4231
+		} catch (Exception $exception) {
4232
+			$this->trashRestoreDeleteError($action, $entity_model, $exception);
4233
+		}
4234
+		if (is_callable($callback)) {
4235
+			call_user_func_array($callback, [$entity_model, $entity_ID, $action, $result, $delete_column]);
4236
+		}
4237
+		return $result;
4238
+	}
4239
+
4240
+
4241
+	/**
4242
+	 * @param EEM_Base $entity_model
4243
+	 * @param string   $delete_column
4244
+	 * @since 4.10.30.p
4245
+	 */
4246
+	private function validateDeleteColumn(EEM_Base $entity_model, $delete_column)
4247
+	{
4248
+		if (empty($delete_column)) {
4249
+			throw new DomainException(
4250
+				sprintf(
4251
+					esc_html__(
4252
+						'You need to specify the name of the "delete column" on the %2$s model, in order to trash or restore an entity.',
4253
+						'event_espresso'
4254
+					),
4255
+					$entity_model->get_this_model_name()
4256
+				)
4257
+			);
4258
+		}
4259
+		if (! $entity_model->has_field($delete_column)) {
4260
+			throw new DomainException(
4261
+				sprintf(
4262
+					esc_html__(
4263
+						'The %1$s field does not exist on the %2$s model.',
4264
+						'event_espresso'
4265
+					),
4266
+					$delete_column,
4267
+					$entity_model->get_this_model_name()
4268
+				)
4269
+			);
4270
+		}
4271
+	}
4272
+
4273
+
4274
+	/**
4275
+	 * @param EEM_Base       $entity_model
4276
+	 * @param Exception|null $exception
4277
+	 * @param string         $action
4278
+	 * @since 4.10.30.p
4279
+	 */
4280
+	private function trashRestoreDeleteError($action, EEM_Base $entity_model, Exception $exception = null)
4281
+	{
4282
+		if ($exception instanceof Exception) {
4283
+			throw new RuntimeException(
4284
+				sprintf(
4285
+					esc_html__(
4286
+						'Could not %1$s the %2$s because the following error occurred: %3$s',
4287
+						'event_espresso'
4288
+					),
4289
+					$action,
4290
+					$entity_model->get_this_model_name(),
4291
+					$exception->getMessage()
4292
+				)
4293
+			);
4294
+		}
4295
+		throw new RuntimeException(
4296
+			sprintf(
4297
+				esc_html__(
4298
+					'Could not %1$s the %2$s because an invalid ID was received.',
4299
+					'event_espresso'
4300
+				),
4301
+				$action,
4302
+				$entity_model->get_this_model_name()
4303
+			)
4304
+		);
4305
+	}
4306 4306
 }
Please login to merge, or discard this patch.
Spacing   +180 added lines, -180 removed lines patch added patch discarded remove patch
@@ -629,7 +629,7 @@  discard block
 block discarded – undo
629 629
         $ee_menu_slugs = (array) $ee_menu_slugs;
630 630
         if (
631 631
             ! $this->request->isAjax()
632
-            && (! $this->_current_page || ! isset($ee_menu_slugs[ $this->_current_page ]))
632
+            && ( ! $this->_current_page || ! isset($ee_menu_slugs[$this->_current_page]))
633 633
         ) {
634 634
             return;
635 635
         }
@@ -649,7 +649,7 @@  discard block
 block discarded – undo
649 649
             : $req_action;
650 650
 
651 651
         $this->_current_view = $this->_req_action;
652
-        $this->_req_nonce    = $this->_req_action . '_nonce';
652
+        $this->_req_nonce    = $this->_req_action.'_nonce';
653 653
         $this->_define_page_props();
654 654
         $this->_current_page_view_url = add_query_arg(
655 655
             ['page' => $this->_current_page, 'action' => $this->_current_view],
@@ -679,33 +679,33 @@  discard block
 block discarded – undo
679 679
         }
680 680
         // filter routes and page_config so addons can add their stuff. Filtering done per class
681 681
         $this->_page_routes = apply_filters(
682
-            'FHEE__' . $this->class_name . '__page_setup__page_routes',
682
+            'FHEE__'.$this->class_name.'__page_setup__page_routes',
683 683
             $this->_page_routes,
684 684
             $this
685 685
         );
686 686
         $this->_page_config = apply_filters(
687
-            'FHEE__' . $this->class_name . '__page_setup__page_config',
687
+            'FHEE__'.$this->class_name.'__page_setup__page_config',
688 688
             $this->_page_config,
689 689
             $this
690 690
         );
691 691
         if ($this->base_class_name !== '') {
692 692
             $this->_page_routes = apply_filters(
693
-                'FHEE__' . $this->base_class_name . '__page_setup__page_routes',
693
+                'FHEE__'.$this->base_class_name.'__page_setup__page_routes',
694 694
                 $this->_page_routes,
695 695
                 $this
696 696
             );
697 697
             $this->_page_config = apply_filters(
698
-                'FHEE__' . $this->base_class_name . '__page_setup__page_config',
698
+                'FHEE__'.$this->base_class_name.'__page_setup__page_config',
699 699
                 $this->_page_config,
700 700
                 $this
701 701
             );
702 702
         }
703 703
         // if AHEE__EE_Admin_Page__route_admin_request_$this->_current_view method is present
704 704
         // then we call it hooked into the AHEE__EE_Admin_Page__route_admin_request action
705
-        if (method_exists($this, 'AHEE__EE_Admin_Page__route_admin_request_' . $this->_current_view)) {
705
+        if (method_exists($this, 'AHEE__EE_Admin_Page__route_admin_request_'.$this->_current_view)) {
706 706
             add_action(
707 707
                 'AHEE__EE_Admin_Page__route_admin_request',
708
-                [$this, 'AHEE__EE_Admin_Page__route_admin_request_' . $this->_current_view],
708
+                [$this, 'AHEE__EE_Admin_Page__route_admin_request_'.$this->_current_view],
709 709
                 10,
710 710
                 2
711 711
             );
@@ -718,8 +718,8 @@  discard block
 block discarded – undo
718 718
             if ($this->_is_UI_request) {
719 719
                 // admin_init stuff - global, all views for this page class, specific view
720 720
                 add_action('admin_init', [$this, 'admin_init'], 10);
721
-                if (method_exists($this, 'admin_init_' . $this->_current_view)) {
722
-                    add_action('admin_init', [$this, 'admin_init_' . $this->_current_view], 15);
721
+                if (method_exists($this, 'admin_init_'.$this->_current_view)) {
722
+                    add_action('admin_init', [$this, 'admin_init_'.$this->_current_view], 15);
723 723
                 }
724 724
             } else {
725 725
                 // hijack regular WP loading and route admin request immediately
@@ -738,12 +738,12 @@  discard block
 block discarded – undo
738 738
      */
739 739
     private function _do_other_page_hooks()
740 740
     {
741
-        $registered_pages = apply_filters('FHEE_do_other_page_hooks_' . $this->page_slug, []);
741
+        $registered_pages = apply_filters('FHEE_do_other_page_hooks_'.$this->page_slug, []);
742 742
         foreach ($registered_pages as $page) {
743 743
             // now let's setup the file name and class that should be present
744 744
             $classname = str_replace('.class.php', '', $page);
745 745
             // autoloaders should take care of loading file
746
-            if (! class_exists($classname)) {
746
+            if ( ! class_exists($classname)) {
747 747
                 $error_msg[] = sprintf(
748 748
                     esc_html__(
749 749
                         'Something went wrong with loading the %s admin hooks page.',
@@ -760,7 +760,7 @@  discard block
 block discarded – undo
760 760
                                    ),
761 761
                                    $page,
762 762
                                    '<br />',
763
-                                   '<strong>' . $classname . '</strong>'
763
+                                   '<strong>'.$classname.'</strong>'
764 764
                                );
765 765
                 throw new EE_Error(implode('||', $error_msg));
766 766
             }
@@ -802,13 +802,13 @@  discard block
 block discarded – undo
802 802
         // load admin_notices - global, page class, and view specific
803 803
         add_action('admin_notices', [$this, 'admin_notices_global'], 5);
804 804
         add_action('admin_notices', [$this, 'admin_notices'], 10);
805
-        if (method_exists($this, 'admin_notices_' . $this->_current_view)) {
806
-            add_action('admin_notices', [$this, 'admin_notices_' . $this->_current_view], 15);
805
+        if (method_exists($this, 'admin_notices_'.$this->_current_view)) {
806
+            add_action('admin_notices', [$this, 'admin_notices_'.$this->_current_view], 15);
807 807
         }
808 808
         // load network admin_notices - global, page class, and view specific
809 809
         add_action('network_admin_notices', [$this, 'network_admin_notices_global'], 5);
810
-        if (method_exists($this, 'network_admin_notices_' . $this->_current_view)) {
811
-            add_action('network_admin_notices', [$this, 'network_admin_notices_' . $this->_current_view]);
810
+        if (method_exists($this, 'network_admin_notices_'.$this->_current_view)) {
811
+            add_action('network_admin_notices', [$this, 'network_admin_notices_'.$this->_current_view]);
812 812
         }
813 813
         // this will save any per_page screen options if they are present
814 814
         $this->_set_per_page_screen_options();
@@ -929,7 +929,7 @@  discard block
 block discarded – undo
929 929
     protected function _verify_routes()
930 930
     {
931 931
         do_action('AHEE_log', __FILE__, __FUNCTION__, '');
932
-        if (! $this->_current_page && ! $this->request->isAjax()) {
932
+        if ( ! $this->_current_page && ! $this->request->isAjax()) {
933 933
             return false;
934 934
         }
935 935
         $this->_route = false;
@@ -941,7 +941,7 @@  discard block
 block discarded – undo
941 941
                 $this->_admin_page_title
942 942
             );
943 943
             // developer error msg
944
-            $error_msg .= '||' . $error_msg
944
+            $error_msg .= '||'.$error_msg
945 945
                           . esc_html__(
946 946
                               ' Make sure the "set_page_routes()" method exists, and is setting the "_page_routes" array properly.',
947 947
                               'event_espresso'
@@ -950,8 +950,8 @@  discard block
 block discarded – undo
950 950
         }
951 951
         // and that the requested page route exists
952 952
         if (array_key_exists($this->_req_action, $this->_page_routes)) {
953
-            $this->_route        = $this->_page_routes[ $this->_req_action ];
954
-            $this->_route_config = $this->_page_config[ $this->_req_action ] ?? [];
953
+            $this->_route        = $this->_page_routes[$this->_req_action];
954
+            $this->_route_config = $this->_page_config[$this->_req_action] ?? [];
955 955
         } else {
956 956
             // user error msg
957 957
             $error_msg = sprintf(
@@ -962,7 +962,7 @@  discard block
 block discarded – undo
962 962
                 $this->_admin_page_title
963 963
             );
964 964
             // developer error msg
965
-            $error_msg .= '||' . $error_msg
965
+            $error_msg .= '||'.$error_msg
966 966
                           . sprintf(
967 967
                               esc_html__(
968 968
                                   ' Create a key in the "_page_routes" array named "%s" and set its value to the appropriate method.',
@@ -973,7 +973,7 @@  discard block
 block discarded – undo
973 973
             throw new EE_Error($error_msg);
974 974
         }
975 975
         // and that a default route exists
976
-        if (! array_key_exists('default', $this->_page_routes)) {
976
+        if ( ! array_key_exists('default', $this->_page_routes)) {
977 977
             // user error msg
978 978
             $error_msg = sprintf(
979 979
                 esc_html__(
@@ -983,7 +983,7 @@  discard block
 block discarded – undo
983 983
                 $this->_admin_page_title
984 984
             );
985 985
             // developer error msg
986
-            $error_msg .= '||' . $error_msg
986
+            $error_msg .= '||'.$error_msg
987 987
                           . esc_html__(
988 988
                               ' Create a key in the "_page_routes" array named "default" and set its value to your default page method.',
989 989
                               'event_espresso'
@@ -1025,7 +1025,7 @@  discard block
 block discarded – undo
1025 1025
             $this->_admin_page_title
1026 1026
         );
1027 1027
         // developer error msg
1028
-        $error_msg .= '||' . $error_msg
1028
+        $error_msg .= '||'.$error_msg
1029 1029
                       . sprintf(
1030 1030
                           esc_html__(
1031 1031
                               ' Check the route you are using in your method (%s) and make sure it matches a route set in your "_page_routes" array property',
@@ -1053,7 +1053,7 @@  discard block
 block discarded – undo
1053 1053
     protected function _verify_nonce($nonce, $nonce_ref)
1054 1054
     {
1055 1055
         // verify nonce against expected value
1056
-        if (! wp_verify_nonce($nonce, $nonce_ref)) {
1056
+        if ( ! wp_verify_nonce($nonce, $nonce_ref)) {
1057 1057
             // these are not the droids you are looking for !!!
1058 1058
             $msg = sprintf(
1059 1059
                 esc_html__('%sNonce Fail.%s', 'event_espresso'),
@@ -1070,7 +1070,7 @@  discard block
 block discarded – undo
1070 1070
                     __CLASS__
1071 1071
                 );
1072 1072
             }
1073
-            if (! $this->request->isAjax()) {
1073
+            if ( ! $this->request->isAjax()) {
1074 1074
                 wp_die($msg);
1075 1075
             }
1076 1076
             EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
@@ -1094,7 +1094,7 @@  discard block
 block discarded – undo
1094 1094
      */
1095 1095
     protected function _route_admin_request()
1096 1096
     {
1097
-        if (! $this->_is_UI_request) {
1097
+        if ( ! $this->_is_UI_request) {
1098 1098
             $this->_verify_routes();
1099 1099
         }
1100 1100
         $nonce_check = ! isset($this->_route_config['require_nonce']) || $this->_route_config['require_nonce'];
@@ -1114,7 +1114,7 @@  discard block
 block discarded – undo
1114 1114
         $error_msg = '';
1115 1115
         // action right before calling route
1116 1116
         // (hook is something like 'AHEE__Registrations_Admin_Page__route_admin_request')
1117
-        if (! did_action('AHEE__EE_Admin_Page__route_admin_request')) {
1117
+        if ( ! did_action('AHEE__EE_Admin_Page__route_admin_request')) {
1118 1118
             do_action('AHEE__EE_Admin_Page__route_admin_request', $this->_current_view, $this);
1119 1119
         }
1120 1120
         // strip _wp_http_referer from the server REQUEST_URI
@@ -1128,7 +1128,7 @@  discard block
 block discarded – undo
1128 1128
         );
1129 1129
         $this->request->setRequestParam('_wp_http_referer', $cleaner_request_uri, true);
1130 1130
         $this->request->setServerParam('REQUEST_URI', $cleaner_request_uri, true);
1131
-        if (! empty($func)) {
1131
+        if ( ! empty($func)) {
1132 1132
             if (is_array($func)) {
1133 1133
                 [$class, $method] = $func;
1134 1134
             } elseif (strpos($func, '::') !== false) {
@@ -1137,7 +1137,7 @@  discard block
 block discarded – undo
1137 1137
                 $class  = $this;
1138 1138
                 $method = $func;
1139 1139
             }
1140
-            if (! (is_object($class) && $class === $this)) {
1140
+            if ( ! (is_object($class) && $class === $this)) {
1141 1141
                 // send along this admin page object for access by addons.
1142 1142
                 $args['admin_page_object'] = $this;
1143 1143
             }
@@ -1178,7 +1178,7 @@  discard block
 block discarded – undo
1178 1178
                     $method
1179 1179
                 );
1180 1180
             }
1181
-            if (! empty($error_msg)) {
1181
+            if ( ! empty($error_msg)) {
1182 1182
                 throw new EE_Error($error_msg);
1183 1183
             }
1184 1184
         }
@@ -1264,7 +1264,7 @@  discard block
 block discarded – undo
1264 1264
                 if (strpos($key, 'nonce') !== false) {
1265 1265
                     continue;
1266 1266
                 }
1267
-                $args[ 'wp_referer[' . $key . ']' ] = is_string($value) ? htmlspecialchars($value) : $value;
1267
+                $args['wp_referer['.$key.']'] = is_string($value) ? htmlspecialchars($value) : $value;
1268 1268
             }
1269 1269
         }
1270 1270
         return EEH_URL::add_query_args_and_nonce($args, $url, $exclude_nonce);
@@ -1304,12 +1304,12 @@  discard block
 block discarded – undo
1304 1304
      */
1305 1305
     protected function _add_help_tabs()
1306 1306
     {
1307
-        if (isset($this->_page_config[ $this->_req_action ])) {
1308
-            $config = $this->_page_config[ $this->_req_action ];
1307
+        if (isset($this->_page_config[$this->_req_action])) {
1308
+            $config = $this->_page_config[$this->_req_action];
1309 1309
             // let's see if there is a help_sidebar set for the current route and we'll set that up for usage as well.
1310 1310
             if (is_array($config) && isset($config['help_sidebar'])) {
1311 1311
                 // check that the callback given is valid
1312
-                if (! method_exists($this, $config['help_sidebar'])) {
1312
+                if ( ! method_exists($this, $config['help_sidebar'])) {
1313 1313
                     throw new EE_Error(
1314 1314
                         sprintf(
1315 1315
                             esc_html__(
@@ -1322,18 +1322,18 @@  discard block
 block discarded – undo
1322 1322
                     );
1323 1323
                 }
1324 1324
                 $content = apply_filters(
1325
-                    'FHEE__' . $this->class_name . '__add_help_tabs__help_sidebar',
1325
+                    'FHEE__'.$this->class_name.'__add_help_tabs__help_sidebar',
1326 1326
                     $this->{$config['help_sidebar']}()
1327 1327
                 );
1328 1328
                 $this->_current_screen->set_help_sidebar($content);
1329 1329
             }
1330
-            if (! isset($config['help_tabs'])) {
1330
+            if ( ! isset($config['help_tabs'])) {
1331 1331
                 return;
1332 1332
             } //no help tabs for this route
1333 1333
             foreach ((array) $config['help_tabs'] as $tab_id => $cfg) {
1334 1334
                 // we're here so there ARE help tabs!
1335 1335
                 // make sure we've got what we need
1336
-                if (! isset($cfg['title'])) {
1336
+                if ( ! isset($cfg['title'])) {
1337 1337
                     throw new EE_Error(
1338 1338
                         esc_html__(
1339 1339
                             'The _page_config array is not set up properly for help tabs.  It is missing a title',
@@ -1341,7 +1341,7 @@  discard block
 block discarded – undo
1341 1341
                         )
1342 1342
                     );
1343 1343
                 }
1344
-                if (! isset($cfg['filename']) && ! isset($cfg['callback']) && ! isset($cfg['content'])) {
1344
+                if ( ! isset($cfg['filename']) && ! isset($cfg['callback']) && ! isset($cfg['content'])) {
1345 1345
                     throw new EE_Error(
1346 1346
                         esc_html__(
1347 1347
                             'The _page_config array is not setup properly for help tabs. It is missing a either a filename reference, or a callback reference or a content reference so there is no way to know the content for the help tab',
@@ -1350,11 +1350,11 @@  discard block
 block discarded – undo
1350 1350
                     );
1351 1351
                 }
1352 1352
                 // first priority goes to content.
1353
-                if (! empty($cfg['content'])) {
1353
+                if ( ! empty($cfg['content'])) {
1354 1354
                     $content = ! empty($cfg['content']) ? $cfg['content'] : null;
1355 1355
                     // second priority goes to filename
1356
-                } elseif (! empty($cfg['filename'])) {
1357
-                    $file_path = $this->_get_dir() . '/help_tabs/' . $cfg['filename'] . '.help_tab.php';
1356
+                } elseif ( ! empty($cfg['filename'])) {
1357
+                    $file_path = $this->_get_dir().'/help_tabs/'.$cfg['filename'].'.help_tab.php';
1358 1358
                     // it's possible that the file is located on decaf route (and above sets up for caf route, if this is the case then lets check decaf route too)
1359 1359
                     $file_path = ! is_readable($file_path) ? EE_ADMIN_PAGES
1360 1360
                                                              . basename($this->_get_dir())
@@ -1362,7 +1362,7 @@  discard block
 block discarded – undo
1362 1362
                                                              . $cfg['filename']
1363 1363
                                                              . '.help_tab.php' : $file_path;
1364 1364
                     // if file is STILL not readable then let's do a EE_Error so its more graceful than a fatal error.
1365
-                    if (! isset($cfg['callback']) && ! is_readable($file_path)) {
1365
+                    if ( ! isset($cfg['callback']) && ! is_readable($file_path)) {
1366 1366
                         EE_Error::add_error(
1367 1367
                             sprintf(
1368 1368
                                 esc_html__(
@@ -1410,7 +1410,7 @@  discard block
 block discarded – undo
1410 1410
                     return;
1411 1411
                 }
1412 1412
                 // setup config array for help tab method
1413
-                $id  = $this->page_slug . '-' . $this->_req_action . '-' . $tab_id;
1413
+                $id  = $this->page_slug.'-'.$this->_req_action.'-'.$tab_id;
1414 1414
                 $_ht = [
1415 1415
                     'id'       => $id,
1416 1416
                     'title'    => $cfg['title'],
@@ -1436,8 +1436,8 @@  discard block
 block discarded – undo
1436 1436
             $qtips = (array) $this->_route_config['qtips'];
1437 1437
             // load qtip loader
1438 1438
             $path = [
1439
-                $this->_get_dir() . '/qtips/',
1440
-                EE_ADMIN_PAGES . basename($this->_get_dir()) . '/qtips/',
1439
+                $this->_get_dir().'/qtips/',
1440
+                EE_ADMIN_PAGES.basename($this->_get_dir()).'/qtips/',
1441 1441
             ];
1442 1442
             EEH_Qtip_Loader::instance()->register($qtips, $path);
1443 1443
         }
@@ -1459,7 +1459,7 @@  discard block
 block discarded – undo
1459 1459
         $i = 0;
1460 1460
         $only_tab = count($this->_page_config) < 2;
1461 1461
         foreach ($this->_page_config as $slug => $config) {
1462
-            if (! is_array($config) || empty($config['nav'])) {
1462
+            if ( ! is_array($config) || empty($config['nav'])) {
1463 1463
                 continue;
1464 1464
             }
1465 1465
             // no nav tab for this config
@@ -1468,27 +1468,27 @@  discard block
 block discarded – undo
1468 1468
                 // nav tab is only to appear when route requested.
1469 1469
                 continue;
1470 1470
             }
1471
-            if (! $this->check_user_access($slug, true)) {
1471
+            if ( ! $this->check_user_access($slug, true)) {
1472 1472
                 // no nav tab because current user does not have access.
1473 1473
                 continue;
1474 1474
             }
1475
-            $css_class = isset($config['css_class']) ? $config['css_class'] . ' ' : '';
1475
+            $css_class = isset($config['css_class']) ? $config['css_class'].' ' : '';
1476 1476
             $css_class .= $only_tab ? ' ee-only-tab' : '';
1477 1477
 
1478
-            $this->_nav_tabs[ $slug ] = [
1478
+            $this->_nav_tabs[$slug] = [
1479 1479
                 'url'       => $config['nav']['url'] ?? EE_Admin_Page::add_query_args_and_nonce(
1480 1480
                     ['action' => $slug],
1481 1481
                     $this->_admin_base_url
1482 1482
                 ),
1483 1483
                 'link_text' => $this->navTabLabel($config['nav'], $slug),
1484
-                'css_class' => $this->_req_action === $slug ? $css_class . ' nav-tab-active' : $css_class,
1484
+                'css_class' => $this->_req_action === $slug ? $css_class.' nav-tab-active' : $css_class,
1485 1485
                 'order'     => $config['nav']['order'] ?? $i,
1486 1486
             ];
1487 1487
             $i++;
1488 1488
         }
1489 1489
         // if $this->_nav_tabs is empty then lets set the default
1490 1490
         if (empty($this->_nav_tabs)) {
1491
-            $this->_nav_tabs[ $this->_default_nav_tab_name ] = [
1491
+            $this->_nav_tabs[$this->_default_nav_tab_name] = [
1492 1492
                 'url'       => $this->_admin_base_url,
1493 1493
                 'link_text' => ucwords(str_replace('_', ' ', $this->_default_nav_tab_name)),
1494 1494
                 'css_class' => 'nav-tab-active',
@@ -1504,11 +1504,11 @@  discard block
 block discarded – undo
1504 1504
     {
1505 1505
         $label = $nav_tab['label'] ?? ucwords(str_replace('_', ' ', $slug));
1506 1506
         $icon = $nav_tab['icon'] ?? null;
1507
-        $icon = $icon ? '<span class="dashicons ' . $icon . '"></span>' : '';
1507
+        $icon = $icon ? '<span class="dashicons '.$icon.'"></span>' : '';
1508 1508
         return '
1509 1509
             <span class="ee-admin-screen-tab__label">
1510
-                ' . $icon . '
1511
-                <span class="ee-nav-label__text">' . $label . '</span>
1510
+                ' . $icon.'
1511
+                <span class="ee-nav-label__text">' . $label.'</span>
1512 1512
             </span>';
1513 1513
     }
1514 1514
 
@@ -1525,10 +1525,10 @@  discard block
 block discarded – undo
1525 1525
             foreach ($this->_route_config['labels'] as $label => $text) {
1526 1526
                 if (is_array($text)) {
1527 1527
                     foreach ($text as $sublabel => $subtext) {
1528
-                        $this->_labels[ $label ][ $sublabel ] = $subtext;
1528
+                        $this->_labels[$label][$sublabel] = $subtext;
1529 1529
                     }
1530 1530
                 } else {
1531
-                    $this->_labels[ $label ] = $text;
1531
+                    $this->_labels[$label] = $text;
1532 1532
                 }
1533 1533
             }
1534 1534
         }
@@ -1550,10 +1550,10 @@  discard block
 block discarded – undo
1550 1550
     {
1551 1551
         do_action('AHEE_log', __FILE__, __FUNCTION__, '');
1552 1552
         $route_to_check = empty($route_to_check) ? $this->_req_action : $route_to_check;
1553
-        $capability = ! empty($route_to_check) && isset($this->_page_routes[ $route_to_check ])
1554
-                                      && is_array($this->_page_routes[ $route_to_check ])
1555
-                        && ! empty($this->_page_routes[ $route_to_check ]['capability'])
1556
-            ? $this->_page_routes[ $route_to_check ]['capability']
1553
+        $capability = ! empty($route_to_check) && isset($this->_page_routes[$route_to_check])
1554
+                                      && is_array($this->_page_routes[$route_to_check])
1555
+                        && ! empty($this->_page_routes[$route_to_check]['capability'])
1556
+            ? $this->_page_routes[$route_to_check]['capability']
1557 1557
             : null;
1558 1558
 
1559 1559
         if (empty($capability) && empty($route_to_check)) {
@@ -1607,14 +1607,14 @@  discard block
 block discarded – undo
1607 1607
         string $priority = 'default',
1608 1608
         ?array $callback_args = null
1609 1609
     ) {
1610
-        if (! is_callable($callback)) {
1610
+        if ( ! is_callable($callback)) {
1611 1611
             return;
1612 1612
         }
1613 1613
 
1614 1614
         add_meta_box($box_id, $title, $callback, $screen, $context, $priority, $callback_args);
1615 1615
         add_filter(
1616 1616
             "postbox_classes_{$this->_wp_page_slug}_{$box_id}",
1617
-            function ($classes) {
1617
+            function($classes) {
1618 1618
                 array_push($classes, 'ee-admin-container');
1619 1619
                 return $classes;
1620 1620
             }
@@ -1708,7 +1708,7 @@  discard block
 block discarded – undo
1708 1708
         ';
1709 1709
 
1710 1710
         // current set timezone for timezone js
1711
-        echo '<span id="current_timezone" class="hidden">' . esc_html(EEH_DTT_Helper::get_timezone()) . '</span>';
1711
+        echo '<span id="current_timezone" class="hidden">'.esc_html(EEH_DTT_Helper::get_timezone()).'</span>';
1712 1712
     }
1713 1713
 
1714 1714
 
@@ -1742,7 +1742,7 @@  discard block
 block discarded – undo
1742 1742
         // loop through the array and setup content
1743 1743
         foreach ($help_array as $trigger => $help) {
1744 1744
             // make sure the array is setup properly
1745
-            if (! isset($help['title'], $help['content'])) {
1745
+            if ( ! isset($help['title'], $help['content'])) {
1746 1746
                 throw new EE_Error(
1747 1747
                     esc_html__(
1748 1748
                         'Does not look like the popup content array has been setup correctly.  Might want to double check that.  Read the comments for the _get_help_popup_content method found in "EE_Admin_Page" class',
@@ -1756,8 +1756,8 @@  discard block
 block discarded – undo
1756 1756
                 'help_popup_title'   => $help['title'],
1757 1757
                 'help_popup_content' => $help['content'],
1758 1758
             ];
1759
-            $content       .= EEH_Template::display_template(
1760
-                EE_ADMIN_TEMPLATE . 'admin_help_popup.template.php',
1759
+            $content .= EEH_Template::display_template(
1760
+                EE_ADMIN_TEMPLATE.'admin_help_popup.template.php',
1761 1761
                 $template_args,
1762 1762
                 true
1763 1763
             );
@@ -1779,15 +1779,15 @@  discard block
 block discarded – undo
1779 1779
     private function _get_help_content()
1780 1780
     {
1781 1781
         // what is the method we're looking for?
1782
-        $method_name = '_help_popup_content_' . $this->_req_action;
1782
+        $method_name = '_help_popup_content_'.$this->_req_action;
1783 1783
         // if method doesn't exist let's get out.
1784
-        if (! method_exists($this, $method_name)) {
1784
+        if ( ! method_exists($this, $method_name)) {
1785 1785
             return [];
1786 1786
         }
1787 1787
         // k we're good to go let's retrieve the help array
1788 1788
         $help_array = $this->{$method_name}();
1789 1789
         // make sure we've got an array!
1790
-        if (! is_array($help_array)) {
1790
+        if ( ! is_array($help_array)) {
1791 1791
             throw new EE_Error(
1792 1792
                 esc_html__(
1793 1793
                     'Something went wrong with help popup content generation. Expecting an array and well, this ain\'t no array bub.',
@@ -1819,8 +1819,8 @@  discard block
 block discarded – undo
1819 1819
         // let's check and see if there is any content set for this popup.  If there isn't then we'll include a default title and content so that developers know something needs to be corrected
1820 1820
         $help_array   = $this->_get_help_content();
1821 1821
         $help_content = '';
1822
-        if (empty($help_array) || ! isset($help_array[ $trigger_id ])) {
1823
-            $help_array[ $trigger_id ] = [
1822
+        if (empty($help_array) || ! isset($help_array[$trigger_id])) {
1823
+            $help_array[$trigger_id] = [
1824 1824
                 'title'   => esc_html__('Missing Content', 'event_espresso'),
1825 1825
                 'content' => esc_html__(
1826 1826
                     'A trigger has been set that doesn\'t have any corresponding content. Make sure you have set the help content. (see the "_set_help_popup_content" method in the EE_Admin_Page for instructions.)',
@@ -1915,7 +1915,7 @@  discard block
 block discarded – undo
1915 1915
 
1916 1916
         add_filter(
1917 1917
             'admin_body_class',
1918
-            function ($classes) {
1918
+            function($classes) {
1919 1919
                 if (strpos($classes, 'espresso-admin') === false) {
1920 1920
                     $classes .= ' espresso-admin';
1921 1921
                 }
@@ -2006,12 +2006,12 @@  discard block
 block discarded – undo
2006 2006
     protected function _set_list_table()
2007 2007
     {
2008 2008
         // first is this a list_table view?
2009
-        if (! isset($this->_route_config['list_table'])) {
2009
+        if ( ! isset($this->_route_config['list_table'])) {
2010 2010
             return;
2011 2011
         } //not a list_table view so get out.
2012 2012
         // list table functions are per view specific (because some admin pages might have more than one list table!)
2013
-        $list_table_view = '_set_list_table_views_' . $this->_req_action;
2014
-        if (! method_exists($this, $list_table_view) || $this->{$list_table_view}() === false) {
2013
+        $list_table_view = '_set_list_table_views_'.$this->_req_action;
2014
+        if ( ! method_exists($this, $list_table_view) || $this->{$list_table_view}() === false) {
2015 2015
             // user error msg
2016 2016
             $error_msg = esc_html__(
2017 2017
                 'An error occurred. The requested list table views could not be found.',
@@ -2031,10 +2031,10 @@  discard block
 block discarded – undo
2031 2031
         }
2032 2032
         // let's provide the ability to filter the views per PAGE AND ROUTE, per PAGE, and globally
2033 2033
         $this->_views = apply_filters(
2034
-            'FHEE_list_table_views_' . $this->page_slug . '_' . $this->_req_action,
2034
+            'FHEE_list_table_views_'.$this->page_slug.'_'.$this->_req_action,
2035 2035
             $this->_views
2036 2036
         );
2037
-        $this->_views = apply_filters('FHEE_list_table_views_' . $this->page_slug, $this->_views);
2037
+        $this->_views = apply_filters('FHEE_list_table_views_'.$this->page_slug, $this->_views);
2038 2038
         $this->_views = apply_filters('FHEE_list_table_views', $this->_views);
2039 2039
         $this->_set_list_table_view();
2040 2040
         $this->_set_list_table_object();
@@ -2069,7 +2069,7 @@  discard block
 block discarded – undo
2069 2069
     protected function _set_list_table_object()
2070 2070
     {
2071 2071
         if (isset($this->_route_config['list_table'])) {
2072
-            if (! class_exists($this->_route_config['list_table'])) {
2072
+            if ( ! class_exists($this->_route_config['list_table'])) {
2073 2073
                 throw new EE_Error(
2074 2074
                     sprintf(
2075 2075
                         esc_html__(
@@ -2107,17 +2107,17 @@  discard block
 block discarded – undo
2107 2107
         foreach ($this->_views as $key => $view) {
2108 2108
             $query_args = [];
2109 2109
             // check for current view
2110
-            $this->_views[ $key ]['class']               = $this->_view === $view['slug'] ? 'current' : '';
2110
+            $this->_views[$key]['class']               = $this->_view === $view['slug'] ? 'current' : '';
2111 2111
             $query_args['action']                        = $this->_req_action;
2112
-            $query_args[ $this->_req_action . '_nonce' ] = wp_create_nonce($query_args['action'] . '_nonce');
2112
+            $query_args[$this->_req_action.'_nonce'] = wp_create_nonce($query_args['action'].'_nonce');
2113 2113
             $query_args['status']                        = $view['slug'];
2114 2114
             // merge any other arguments sent in.
2115
-            if (isset($extra_query_args[ $view['slug'] ])) {
2116
-                foreach ($extra_query_args[ $view['slug'] ] as $extra_query_arg) {
2115
+            if (isset($extra_query_args[$view['slug']])) {
2116
+                foreach ($extra_query_args[$view['slug']] as $extra_query_arg) {
2117 2117
                     $query_args[] = $extra_query_arg;
2118 2118
                 }
2119 2119
             }
2120
-            $this->_views[ $key ]['url'] = EE_Admin_Page::add_query_args_and_nonce($query_args, $this->_admin_base_url);
2120
+            $this->_views[$key]['url'] = EE_Admin_Page::add_query_args_and_nonce($query_args, $this->_admin_base_url);
2121 2121
         }
2122 2122
         return $this->_views;
2123 2123
     }
@@ -2148,14 +2148,14 @@  discard block
 block discarded – undo
2148 2148
 					<select id="entries-per-page-slct" name="entries-per-page-slct">';
2149 2149
         foreach ($values as $value) {
2150 2150
             if ($value < $max_entries) {
2151
-                $selected                  = $value === $per_page ? ' selected="' . $per_page . '"' : '';
2151
+                $selected = $value === $per_page ? ' selected="'.$per_page.'"' : '';
2152 2152
                 $entries_per_page_dropdown .= '
2153
-						<option value="' . $value . '"' . $selected . '>' . $value . '&nbsp;&nbsp;</option>';
2153
+						<option value="' . $value.'"'.$selected.'>'.$value.'&nbsp;&nbsp;</option>';
2154 2154
             }
2155 2155
         }
2156
-        $selected                  = $max_entries === $per_page ? ' selected="' . $per_page . '"' : '';
2156
+        $selected = $max_entries === $per_page ? ' selected="'.$per_page.'"' : '';
2157 2157
         $entries_per_page_dropdown .= '
2158
-						<option value="' . $max_entries . '"' . $selected . '>All&nbsp;&nbsp;</option>';
2158
+						<option value="' . $max_entries.'"'.$selected.'>All&nbsp;&nbsp;</option>';
2159 2159
         $entries_per_page_dropdown .= '
2160 2160
 					</select>
2161 2161
 					entries
@@ -2179,7 +2179,7 @@  discard block
 block discarded – undo
2179 2179
             empty($this->_search_btn_label) ? $this->page_label
2180 2180
                 : $this->_search_btn_label
2181 2181
         );
2182
-        $this->_template_args['search']['callback']  = 'search_' . $this->page_slug;
2182
+        $this->_template_args['search']['callback'] = 'search_'.$this->page_slug;
2183 2183
     }
2184 2184
 
2185 2185
 
@@ -2267,7 +2267,7 @@  discard block
 block discarded – undo
2267 2267
             $total_columns                                       = ! empty($screen_columns)
2268 2268
                 ? $screen_columns
2269 2269
                 : $this->_route_config['columns'][1];
2270
-            $this->_template_args['current_screen_widget_class'] = 'columns-' . $total_columns;
2270
+            $this->_template_args['current_screen_widget_class'] = 'columns-'.$total_columns;
2271 2271
             $this->_template_args['current_page']                = $this->_wp_page_slug;
2272 2272
             $this->_template_args['screen']                      = $this->_current_screen;
2273 2273
             $this->_column_template_path                         = EE_ADMIN_TEMPLATE
@@ -2313,7 +2313,7 @@  discard block
 block discarded – undo
2313 2313
      */
2314 2314
     protected function _espresso_ratings_request()
2315 2315
     {
2316
-        if (! apply_filters('FHEE_show_ratings_request_meta_box', true)) {
2316
+        if ( ! apply_filters('FHEE_show_ratings_request_meta_box', true)) {
2317 2317
             return;
2318 2318
         }
2319 2319
         $ratings_box_title = apply_filters(
@@ -2340,28 +2340,28 @@  discard block
 block discarded – undo
2340 2340
      */
2341 2341
     public function espresso_ratings_request()
2342 2342
     {
2343
-        EEH_Template::display_template(EE_ADMIN_TEMPLATE . 'espresso_ratings_request_content.template.php');
2343
+        EEH_Template::display_template(EE_ADMIN_TEMPLATE.'espresso_ratings_request_content.template.php');
2344 2344
     }
2345 2345
 
2346 2346
 
2347 2347
     public static function cached_rss_display($rss_id, $url)
2348 2348
     {
2349
-        $loading   = '<p class="widget-loading hide-if-no-js">'
2349
+        $loading = '<p class="widget-loading hide-if-no-js">'
2350 2350
                      . esc_html__('Loading&#8230;', 'event_espresso')
2351 2351
                      . '</p><p class="hide-if-js">'
2352 2352
                      . esc_html__('This widget requires JavaScript.', 'event_espresso')
2353 2353
                      . '</p>';
2354
-        $pre       = '<div class="espresso-rss-display">' . "\n\t";
2355
-        $pre       .= '<span id="' . esc_attr($rss_id) . '_url" class="hidden">' . esc_url_raw($url) . '</span>';
2356
-        $post      = '</div>' . "\n";
2357
-        $cache_key = 'ee_rss_' . md5($rss_id);
2354
+        $pre       = '<div class="espresso-rss-display">'."\n\t";
2355
+        $pre .= '<span id="'.esc_attr($rss_id).'_url" class="hidden">'.esc_url_raw($url).'</span>';
2356
+        $post      = '</div>'."\n";
2357
+        $cache_key = 'ee_rss_'.md5($rss_id);
2358 2358
         $output    = get_transient($cache_key);
2359 2359
         if ($output !== false) {
2360
-            echo wp_kses($pre . $output . $post, AllowedTags::getWithFormTags());
2360
+            echo wp_kses($pre.$output.$post, AllowedTags::getWithFormTags());
2361 2361
             return true;
2362 2362
         }
2363
-        if (! (defined('DOING_AJAX') && DOING_AJAX)) {
2364
-            echo wp_kses($pre . $loading . $post, AllowedTags::getWithFormTags());
2363
+        if ( ! (defined('DOING_AJAX') && DOING_AJAX)) {
2364
+            echo wp_kses($pre.$loading.$post, AllowedTags::getWithFormTags());
2365 2365
             return false;
2366 2366
         }
2367 2367
         ob_start();
@@ -2428,19 +2428,19 @@  discard block
 block discarded – undo
2428 2428
     public function espresso_sponsors_post_box()
2429 2429
     {
2430 2430
         EEH_Template::display_template(
2431
-            EE_ADMIN_TEMPLATE . 'admin_general_metabox_contents_espresso_sponsors.template.php'
2431
+            EE_ADMIN_TEMPLATE.'admin_general_metabox_contents_espresso_sponsors.template.php'
2432 2432
         );
2433 2433
     }
2434 2434
 
2435 2435
 
2436 2436
     private function _publish_post_box()
2437 2437
     {
2438
-        $meta_box_ref = 'espresso_' . $this->page_slug . '_editor_overview';
2438
+        $meta_box_ref = 'espresso_'.$this->page_slug.'_editor_overview';
2439 2439
         // if there is a array('label' => array('publishbox' => 'some title') ) present in the _page_config array
2440 2440
         // then we'll use that for the metabox label.
2441 2441
         // Otherwise we'll just use publish (publishbox itself could be an array of labels indexed by routes)
2442
-        if (! empty($this->_labels['publishbox'])) {
2443
-            $box_label = is_array($this->_labels['publishbox']) ? $this->_labels['publishbox'][ $this->_req_action ]
2442
+        if ( ! empty($this->_labels['publishbox'])) {
2443
+            $box_label = is_array($this->_labels['publishbox']) ? $this->_labels['publishbox'][$this->_req_action]
2444 2444
                 : $this->_labels['publishbox'];
2445 2445
         } else {
2446 2446
             $box_label = esc_html__('Publish', 'event_espresso');
@@ -2469,7 +2469,7 @@  discard block
 block discarded – undo
2469 2469
             ? $this->_template_args['publish_box_extra_content']
2470 2470
             : '';
2471 2471
         echo EEH_Template::display_template(
2472
-            EE_ADMIN_TEMPLATE . 'admin_details_publish_metabox.template.php',
2472
+            EE_ADMIN_TEMPLATE.'admin_details_publish_metabox.template.php',
2473 2473
             $this->_template_args,
2474 2474
             true
2475 2475
         );
@@ -2557,18 +2557,18 @@  discard block
 block discarded – undo
2557 2557
             );
2558 2558
         }
2559 2559
         $this->_template_args['publish_delete_link'] = $delete_link;
2560
-        if (! empty($name) && ! empty($id)) {
2561
-            $hidden_field_arr[ $name ] = [
2560
+        if ( ! empty($name) && ! empty($id)) {
2561
+            $hidden_field_arr[$name] = [
2562 2562
                 'type'  => 'hidden',
2563 2563
                 'value' => $id,
2564 2564
             ];
2565
-            $hf                        = $this->_generate_admin_form_fields($hidden_field_arr, 'array');
2565
+            $hf = $this->_generate_admin_form_fields($hidden_field_arr, 'array');
2566 2566
         } else {
2567 2567
             $hf = '';
2568 2568
         }
2569 2569
         // add hidden field
2570 2570
         $this->_template_args['publish_hidden_fields'] = is_array($hf) && ! empty($name)
2571
-            ? $hf[ $name ]['field']
2571
+            ? $hf[$name]['field']
2572 2572
             : $hf;
2573 2573
     }
2574 2574
 
@@ -2670,7 +2670,7 @@  discard block
 block discarded – undo
2670 2670
         }
2671 2671
         // if $create_func is true (default) then we automatically create the function for displaying the actual meta box.  If false then we take the $callback reference passed through and use it instead (so callers can define their own callback function/method if they wish)
2672 2672
         $call_back_func = $create_func
2673
-            ? static function ($post, $metabox) {
2673
+            ? static function($post, $metabox) {
2674 2674
                 do_action('AHEE_log', __FILE__, __FUNCTION__, '');
2675 2675
                 echo EEH_Template::display_template(
2676 2676
                     $metabox['args']['template_path'],
@@ -2680,7 +2680,7 @@  discard block
 block discarded – undo
2680 2680
             }
2681 2681
             : $callback;
2682 2682
         $this->addMetaBox(
2683
-            str_replace('_', '-', $action) . '-mbox',
2683
+            str_replace('_', '-', $action).'-mbox',
2684 2684
             $title,
2685 2685
             $call_back_func,
2686 2686
             $this->_wp_page_slug,
@@ -2797,13 +2797,13 @@  discard block
 block discarded – undo
2797 2797
             'event-espresso_page_espresso_',
2798 2798
             '',
2799 2799
             $this->_wp_page_slug
2800
-        ) . ' ' . $this->_req_action . '-route';
2800
+        ).' '.$this->_req_action.'-route';
2801 2801
 
2802 2802
         $template_path = $sidebar
2803 2803
             ? EE_ADMIN_TEMPLATE . 'admin_details_wrapper.template.php'
2804
-            : EE_ADMIN_TEMPLATE . 'admin_details_wrapper_no_sidebar.template.php';
2804
+            : EE_ADMIN_TEMPLATE.'admin_details_wrapper_no_sidebar.template.php';
2805 2805
         if ($this->request->isAjax()) {
2806
-            $template_path = EE_ADMIN_TEMPLATE . 'admin_details_wrapper_no_sidebar_ajax.template.php';
2806
+            $template_path = EE_ADMIN_TEMPLATE.'admin_details_wrapper_no_sidebar_ajax.template.php';
2807 2807
         }
2808 2808
         $template_path = ! empty($this->_column_template_path) ? $this->_column_template_path : $template_path;
2809 2809
 
@@ -2837,11 +2837,11 @@  discard block
 block discarded – undo
2837 2837
     public function display_admin_caf_preview_page($utm_campaign_source = '', $display_sidebar = true)
2838 2838
     {
2839 2839
         // let's generate a default preview action button if there isn't one already present.
2840
-        $this->_labels['buttons']['buy_now']           = esc_html__(
2840
+        $this->_labels['buttons']['buy_now'] = esc_html__(
2841 2841
             'Upgrade to Event Espresso 4 Right Now',
2842 2842
             'event_espresso'
2843 2843
         );
2844
-        $buy_now_url                                   = add_query_arg(
2844
+        $buy_now_url = add_query_arg(
2845 2845
             [
2846 2846
                 'ee_ver'       => 'ee4',
2847 2847
                 'utm_source'   => 'ee4_plugin_admin',
@@ -2861,8 +2861,8 @@  discard block
 block discarded – undo
2861 2861
                 true
2862 2862
             )
2863 2863
             : $this->_template_args['preview_action_button'];
2864
-        $this->_template_args['admin_page_content']    = EEH_Template::display_template(
2865
-            EE_ADMIN_TEMPLATE . 'admin_caf_full_page_preview.template.php',
2864
+        $this->_template_args['admin_page_content'] = EEH_Template::display_template(
2865
+            EE_ADMIN_TEMPLATE.'admin_caf_full_page_preview.template.php',
2866 2866
             $this->_template_args,
2867 2867
             true
2868 2868
         );
@@ -2920,7 +2920,7 @@  discard block
 block discarded – undo
2920 2920
         // setup search attributes
2921 2921
         $this->_set_search_attributes();
2922 2922
         $this->_template_args['current_page']     = $this->_wp_page_slug;
2923
-        $template_path                            = EE_ADMIN_TEMPLATE . 'admin_list_wrapper.template.php';
2923
+        $template_path                            = EE_ADMIN_TEMPLATE.'admin_list_wrapper.template.php';
2924 2924
         $this->_template_args['table_url']        = $this->request->isAjax()
2925 2925
             ? add_query_arg(['noheader' => 'true', 'route' => $this->_req_action], $this->_admin_base_url)
2926 2926
             : add_query_arg(['route' => $this->_req_action], $this->_admin_base_url);
@@ -2928,10 +2928,10 @@  discard block
 block discarded – undo
2928 2928
         $this->_template_args['current_route']    = $this->_req_action;
2929 2929
         $this->_template_args['list_table_class'] = get_class($this->_list_table_object);
2930 2930
         $ajax_sorting_callback                    = $this->_list_table_object->get_ajax_sorting_callback();
2931
-        if (! empty($ajax_sorting_callback)) {
2931
+        if ( ! empty($ajax_sorting_callback)) {
2932 2932
             $sortable_list_table_form_fields = wp_nonce_field(
2933
-                $ajax_sorting_callback . '_nonce',
2934
-                $ajax_sorting_callback . '_nonce',
2933
+                $ajax_sorting_callback.'_nonce',
2934
+                $ajax_sorting_callback.'_nonce',
2935 2935
                 false,
2936 2936
                 false
2937 2937
             );
@@ -2948,18 +2948,18 @@  discard block
 block discarded – undo
2948 2948
 
2949 2949
         $hidden_form_fields = $this->_template_args['list_table_hidden_fields'] ?? '';
2950 2950
 
2951
-        $nonce_ref          = $this->_req_action . '_nonce';
2951
+        $nonce_ref          = $this->_req_action.'_nonce';
2952 2952
         $hidden_form_fields .= '
2953
-            <input type="hidden" name="' . $nonce_ref . '" value="' . wp_create_nonce($nonce_ref) . '">';
2953
+            <input type="hidden" name="' . $nonce_ref.'" value="'.wp_create_nonce($nonce_ref).'">';
2954 2954
 
2955
-        $this->_template_args['list_table_hidden_fields']        = $hidden_form_fields;
2955
+        $this->_template_args['list_table_hidden_fields'] = $hidden_form_fields;
2956 2956
         // display message about search results?
2957 2957
         $search = $this->request->getRequestParam('s');
2958 2958
         $this->_template_args['before_list_table'] .= ! empty($search)
2959
-            ? '<p class="ee-search-results">' . sprintf(
2959
+            ? '<p class="ee-search-results">'.sprintf(
2960 2960
                 esc_html__('Displaying search results for the search string: %1$s', 'event_espresso'),
2961 2961
                 trim($search, '%')
2962
-            ) . '</p>'
2962
+            ).'</p>'
2963 2963
             : '';
2964 2964
         // filter before_list_table template arg
2965 2965
         $this->_template_args['before_list_table'] = apply_filters(
@@ -2993,7 +2993,7 @@  discard block
 block discarded – undo
2993 2993
         // convert to array and filter again
2994 2994
         // arrays are easier to inject new items in a specific location,
2995 2995
         // but would not be backwards compatible, so we have to add a new filter
2996
-        $this->_template_args['after_list_table']   = implode(
2996
+        $this->_template_args['after_list_table'] = implode(
2997 2997
             " \n",
2998 2998
             (array) apply_filters(
2999 2999
                 'FHEE__EE_Admin_Page___display_admin_list_table_page__after_list_table__template_args_array',
@@ -3048,7 +3048,7 @@  discard block
 block discarded – undo
3048 3048
             $this->page_slug
3049 3049
         );
3050 3050
         return EEH_Template::display_template(
3051
-            EE_ADMIN_TEMPLATE . 'admin_details_legend.template.php',
3051
+            EE_ADMIN_TEMPLATE.'admin_details_legend.template.php',
3052 3052
             $this->_template_args,
3053 3053
             true
3054 3054
         );
@@ -3164,16 +3164,16 @@  discard block
 block discarded – undo
3164 3164
             $this->_template_args['before_admin_page_content'] ?? ''
3165 3165
         );
3166 3166
 
3167
-        $this->_template_args['after_admin_page_content']  = apply_filters(
3167
+        $this->_template_args['after_admin_page_content'] = apply_filters(
3168 3168
             "FHEE_after_admin_page_content{$this->_current_page}{$this->_current_view}",
3169 3169
             $this->_template_args['after_admin_page_content'] ?? ''
3170 3170
         );
3171
-        $this->_template_args['after_admin_page_content']  .= $this->_set_help_popup_content();
3171
+        $this->_template_args['after_admin_page_content'] .= $this->_set_help_popup_content();
3172 3172
 
3173 3173
         if ($this->request->isAjax()) {
3174 3174
             $this->_template_args['admin_page_content'] = EEH_Template::display_template(
3175 3175
                 // $template_path,
3176
-                EE_ADMIN_TEMPLATE . 'admin_wrapper_ajax.template.php',
3176
+                EE_ADMIN_TEMPLATE.'admin_wrapper_ajax.template.php',
3177 3177
                 $this->_template_args,
3178 3178
                 true
3179 3179
             );
@@ -3182,7 +3182,7 @@  discard block
 block discarded – undo
3182 3182
         // load settings page wrapper template
3183 3183
         $template_path = $about
3184 3184
             ? EE_ADMIN_TEMPLATE . 'about_admin_wrapper.template.php'
3185
-            : EE_ADMIN_TEMPLATE . 'admin_wrapper.template.php';
3185
+            : EE_ADMIN_TEMPLATE.'admin_wrapper.template.php';
3186 3186
 
3187 3187
         EEH_Template::display_template($template_path, $this->_template_args);
3188 3188
     }
@@ -3266,12 +3266,12 @@  discard block
 block discarded – undo
3266 3266
         $default_names = ['save', 'save_and_close'];
3267 3267
         $buttons = '';
3268 3268
         foreach ($button_text as $key => $button) {
3269
-            $ref     = $default_names[ $key ];
3270
-            $name    = ! empty($actions) ? $actions[ $key ] : $ref;
3271
-            $buttons .= '<input type="submit" class="button button--primary ' . $ref . '" '
3272
-                        . 'value="' . $button . '" name="' . $name . '" '
3273
-                        . 'id="' . $this->_current_view . '_' . $ref . '" />';
3274
-            if (! $both) {
3269
+            $ref     = $default_names[$key];
3270
+            $name    = ! empty($actions) ? $actions[$key] : $ref;
3271
+            $buttons .= '<input type="submit" class="button button--primary '.$ref.'" '
3272
+                        . 'value="'.$button.'" name="'.$name.'" '
3273
+                        . 'id="'.$this->_current_view.'_'.$ref.'" />';
3274
+            if ( ! $both) {
3275 3275
                 break;
3276 3276
             }
3277 3277
         }
@@ -3311,13 +3311,13 @@  discard block
 block discarded – undo
3311 3311
                 'An error occurred. No action was set for this page\'s form.',
3312 3312
                 'event_espresso'
3313 3313
             );
3314
-            $dev_msg  = $user_msg . "\n"
3314
+            $dev_msg = $user_msg."\n"
3315 3315
                         . sprintf(
3316 3316
                             esc_html__('The $route argument is required for the %s->%s method.', 'event_espresso'),
3317 3317
                             __FUNCTION__,
3318 3318
                             __CLASS__
3319 3319
                         );
3320
-            EE_Error::add_error($user_msg . '||' . $dev_msg, __FILE__, __FUNCTION__, __LINE__);
3320
+            EE_Error::add_error($user_msg.'||'.$dev_msg, __FILE__, __FUNCTION__, __LINE__);
3321 3321
         }
3322 3322
         // open form
3323 3323
         $action = $this->_admin_base_url;
@@ -3325,9 +3325,9 @@  discard block
 block discarded – undo
3325 3325
             <form name='form' method='post' action='{$action}' id='{$route}_event_form' class='ee-admin-page-form' >
3326 3326
             ";
3327 3327
         // add nonce
3328
-        $nonce                                             =
3329
-            wp_nonce_field($route . '_nonce', $route . '_nonce', false, false);
3330
-        $this->_template_args['before_admin_page_content'] .= "\n\t" . $nonce;
3328
+        $nonce =
3329
+            wp_nonce_field($route.'_nonce', $route.'_nonce', false, false);
3330
+        $this->_template_args['before_admin_page_content'] .= "\n\t".$nonce;
3331 3331
         // add REQUIRED form action
3332 3332
         $hidden_fields = [
3333 3333
             'action' => ['type' => 'hidden', 'value' => $route],
@@ -3340,7 +3340,7 @@  discard block
 block discarded – undo
3340 3340
         $form_fields = $this->_generate_admin_form_fields($hidden_fields, 'array');
3341 3341
         // add fields to form
3342 3342
         foreach ((array) $form_fields as $form_field) {
3343
-            $this->_template_args['before_admin_page_content'] .= "\n\t" . $form_field['field'];
3343
+            $this->_template_args['before_admin_page_content'] .= "\n\t".$form_field['field'];
3344 3344
         }
3345 3345
         // close form
3346 3346
         $this->_template_args['after_admin_page_content'] = '</form>';
@@ -3423,12 +3423,12 @@  discard block
 block discarded – undo
3423 3423
         bool $override_overwrite = false
3424 3424
     ) {
3425 3425
         do_action('AHEE_log', __FILE__, __FUNCTION__, '');
3426
-        $notices      = EE_Error::get_notices(false);
3426
+        $notices = EE_Error::get_notices(false);
3427 3427
         // overwrite default success messages //BUT ONLY if overwrite not overridden
3428
-        if (! $override_overwrite || ! empty($notices['errors'])) {
3428
+        if ( ! $override_overwrite || ! empty($notices['errors'])) {
3429 3429
             EE_Error::overwrite_success();
3430 3430
         }
3431
-        if (! $override_overwrite && ! empty($what) && ! empty($action_desc) && empty($notices['errors'])) {
3431
+        if ( ! $override_overwrite && ! empty($what) && ! empty($action_desc) && empty($notices['errors'])) {
3432 3432
             // how many records affected ? more than one record ? or just one ?
3433 3433
             EE_Error::add_success(
3434 3434
                 sprintf(
@@ -3449,7 +3449,7 @@  discard block
 block discarded – undo
3449 3449
             );
3450 3450
         }
3451 3451
         // check that $query_args isn't something crazy
3452
-        if (! is_array($query_args)) {
3452
+        if ( ! is_array($query_args)) {
3453 3453
             $query_args = [];
3454 3454
         }
3455 3455
         /**
@@ -3481,7 +3481,7 @@  discard block
 block discarded – undo
3481 3481
             $redirect_url = admin_url('admin.php');
3482 3482
         }
3483 3483
         // merge any default query_args set in _default_route_query_args property
3484
-        if (! empty($this->_default_route_query_args) && ! $this->_is_UI_request) {
3484
+        if ( ! empty($this->_default_route_query_args) && ! $this->_is_UI_request) {
3485 3485
             $args_to_merge = [];
3486 3486
             foreach ($this->_default_route_query_args as $query_param => $query_value) {
3487 3487
                 // is there a wp_referer array in our _default_route_query_args property?
@@ -3493,15 +3493,15 @@  discard block
 block discarded – undo
3493 3493
                         }
3494 3494
                         // finally we will override any arguments in the referer with
3495 3495
                         // what might be set on the _default_route_query_args array.
3496
-                        if (isset($this->_default_route_query_args[ $reference ])) {
3497
-                            $args_to_merge[ $reference ] = urlencode($this->_default_route_query_args[ $reference ]);
3496
+                        if (isset($this->_default_route_query_args[$reference])) {
3497
+                            $args_to_merge[$reference] = urlencode($this->_default_route_query_args[$reference]);
3498 3498
                         } else {
3499
-                            $args_to_merge[ $reference ] = urlencode($value);
3499
+                            $args_to_merge[$reference] = urlencode($value);
3500 3500
                         }
3501 3501
                     }
3502 3502
                     continue;
3503 3503
                 }
3504
-                $args_to_merge[ $query_param ] = $query_value;
3504
+                $args_to_merge[$query_param] = $query_value;
3505 3505
             }
3506 3506
             // now let's merge these arguments but override with what was specifically sent in to the
3507 3507
             // redirect.
@@ -3513,19 +3513,19 @@  discard block
 block discarded – undo
3513 3513
         if (isset($query_args['action'])) {
3514 3514
             // manually generate wp_nonce and merge that with the query vars
3515 3515
             // becuz the wp_nonce_url function wrecks havoc on some vars
3516
-            $query_args['_wpnonce'] = wp_create_nonce($query_args['action'] . '_nonce');
3516
+            $query_args['_wpnonce'] = wp_create_nonce($query_args['action'].'_nonce');
3517 3517
         }
3518 3518
         // we're adding some hooks and filters in here for processing any things just before redirects
3519 3519
         // (example: an admin page has done an insert or update and we want to run something after that).
3520
-        do_action('AHEE_redirect_' . $this->class_name . $this->_req_action, $query_args);
3520
+        do_action('AHEE_redirect_'.$this->class_name.$this->_req_action, $query_args);
3521 3521
         $redirect_url = apply_filters(
3522
-            'FHEE_redirect_' . $this->class_name . $this->_req_action,
3522
+            'FHEE_redirect_'.$this->class_name.$this->_req_action,
3523 3523
             EE_Admin_Page::add_query_args_and_nonce($query_args, $redirect_url),
3524 3524
             $query_args
3525 3525
         );
3526 3526
         // check if we're doing ajax.  If we are then lets just return the results and js can handle how it wants.
3527 3527
         if ($this->request->isAjax()) {
3528
-            $default_data                    = [
3528
+            $default_data = [
3529 3529
                 'close'        => true,
3530 3530
                 'redirect_url' => $redirect_url,
3531 3531
                 'where'        => 'main',
@@ -3575,7 +3575,7 @@  discard block
 block discarded – undo
3575 3575
         }
3576 3576
         $this->_template_args['notices'] = EE_Error::get_notices();
3577 3577
         // IF this isn't ajax we need to create a transient for the notices using the route (however, overridden if $sticky_notices == true)
3578
-        if (! $this->request->isAjax() || $sticky_notices) {
3578
+        if ( ! $this->request->isAjax() || $sticky_notices) {
3579 3579
             $route = isset($query_args['action']) ? $query_args['action'] : 'default';
3580 3580
             $this->_add_transient(
3581 3581
                 $route,
@@ -3615,7 +3615,7 @@  discard block
 block discarded – undo
3615 3615
         $exclude_nonce = false
3616 3616
     ) {
3617 3617
         // first let's validate the action (if $base_url is FALSE otherwise validation will happen further along)
3618
-        if (empty($base_url) && ! isset($this->_page_routes[ $action ])) {
3618
+        if (empty($base_url) && ! isset($this->_page_routes[$action])) {
3619 3619
             throw new EE_Error(
3620 3620
                 sprintf(
3621 3621
                     esc_html__(
@@ -3626,7 +3626,7 @@  discard block
 block discarded – undo
3626 3626
                 )
3627 3627
             );
3628 3628
         }
3629
-        if (! isset($this->_labels['buttons'][ $type ])) {
3629
+        if ( ! isset($this->_labels['buttons'][$type])) {
3630 3630
             throw new EE_Error(
3631 3631
                 sprintf(
3632 3632
                     esc_html__(
@@ -3639,7 +3639,7 @@  discard block
 block discarded – undo
3639 3639
         }
3640 3640
         // finally check user access for this button.
3641 3641
         $has_access = $this->check_user_access($action, true);
3642
-        if (! $has_access) {
3642
+        if ( ! $has_access) {
3643 3643
             return '';
3644 3644
         }
3645 3645
         $_base_url  = ! $base_url ? $this->_admin_base_url : $base_url;
@@ -3647,11 +3647,11 @@  discard block
 block discarded – undo
3647 3647
             'action' => $action,
3648 3648
         ];
3649 3649
         // merge extra_request args but make sure our original action takes precedence and doesn't get overwritten.
3650
-        if (! empty($extra_request)) {
3650
+        if ( ! empty($extra_request)) {
3651 3651
             $query_args = array_merge($extra_request, $query_args);
3652 3652
         }
3653 3653
         $url = EE_Admin_Page::add_query_args_and_nonce($query_args, $_base_url, false, $exclude_nonce);
3654
-        return EEH_Template::get_button_or_link($url, $this->_labels['buttons'][ $type ], $class);
3654
+        return EEH_Template::get_button_or_link($url, $this->_labels['buttons'][$type], $class);
3655 3655
     }
3656 3656
 
3657 3657
 
@@ -3677,7 +3677,7 @@  discard block
 block discarded – undo
3677 3677
                 'FHEE__EE_Admin_Page___per_page_screen_options__default',
3678 3678
                 20
3679 3679
             ),
3680
-            'option'  => $this->_current_page . '_' . $this->_current_view . '_per_page',
3680
+            'option'  => $this->_current_page.'_'.$this->_current_view.'_per_page',
3681 3681
         ];
3682 3682
         // ONLY add the screen option if the user has access to it.
3683 3683
         if ($this->check_user_access($this->_current_view, true)) {
@@ -3698,18 +3698,18 @@  discard block
 block discarded – undo
3698 3698
     {
3699 3699
         if ($this->request->requestParamIsSet('wp_screen_options')) {
3700 3700
             check_admin_referer('screen-options-nonce', 'screenoptionnonce');
3701
-            if (! $user = wp_get_current_user()) {
3701
+            if ( ! $user = wp_get_current_user()) {
3702 3702
                 return;
3703 3703
             }
3704 3704
             $option = $this->request->getRequestParam('wp_screen_options[option]', '', 'key');
3705
-            if (! $option) {
3705
+            if ( ! $option) {
3706 3706
                 return;
3707 3707
             }
3708
-            $value  = $this->request->getRequestParam('wp_screen_options[value]', 0, 'int');
3708
+            $value = $this->request->getRequestParam('wp_screen_options[value]', 0, 'int');
3709 3709
             $map_option = $option;
3710 3710
             $option     = str_replace('-', '_', $option);
3711 3711
             switch ($map_option) {
3712
-                case $this->_current_page . '_' . $this->_current_view . '_per_page':
3712
+                case $this->_current_page.'_'.$this->_current_view.'_per_page':
3713 3713
                     $max_value = apply_filters(
3714 3714
                         'FHEE__EE_Admin_Page___set_per_page_screen_options__max_value',
3715 3715
                         999,
@@ -3766,13 +3766,13 @@  discard block
 block discarded – undo
3766 3766
     protected function _add_transient($route, $data, $notices = false, $skip_route_verify = false)
3767 3767
     {
3768 3768
         $user_id = get_current_user_id();
3769
-        if (! $skip_route_verify) {
3769
+        if ( ! $skip_route_verify) {
3770 3770
             $this->_verify_route($route);
3771 3771
         }
3772 3772
         // now let's set the string for what kind of transient we're setting
3773 3773
         $transient = $notices
3774
-            ? 'ee_rte_n_tx_' . $route . '_' . $user_id
3775
-            : 'rte_tx_' . $route . '_' . $user_id;
3774
+            ? 'ee_rte_n_tx_'.$route.'_'.$user_id
3775
+            : 'rte_tx_'.$route.'_'.$user_id;
3776 3776
         $data      = $notices ? ['notices' => $data] : $data;
3777 3777
         // is there already a transient for this route?  If there is then let's ADD to that transient
3778 3778
         $existing = is_multisite() && is_network_admin()
@@ -3801,8 +3801,8 @@  discard block
 block discarded – undo
3801 3801
         $user_id   = get_current_user_id();
3802 3802
         $route     = ! $route ? $this->_req_action : $route;
3803 3803
         $transient = $notices
3804
-            ? 'ee_rte_n_tx_' . $route . '_' . $user_id
3805
-            : 'rte_tx_' . $route . '_' . $user_id;
3804
+            ? 'ee_rte_n_tx_'.$route.'_'.$user_id
3805
+            : 'rte_tx_'.$route.'_'.$user_id;
3806 3806
         $data      = is_multisite() && is_network_admin()
3807 3807
             ? get_site_transient($transient)
3808 3808
             : get_transient($transient);
@@ -4038,7 +4038,7 @@  discard block
 block discarded – undo
4038 4038
      */
4039 4039
     protected function _next_link($url, $class = 'dashicons dashicons-arrow-right')
4040 4040
     {
4041
-        return '<a class="' . $class . '" href="' . $url . '"></a>';
4041
+        return '<a class="'.$class.'" href="'.$url.'"></a>';
4042 4042
     }
4043 4043
 
4044 4044
 
@@ -4051,7 +4051,7 @@  discard block
 block discarded – undo
4051 4051
      */
4052 4052
     protected function _previous_link($url, $class = 'dashicons dashicons-arrow-left')
4053 4053
     {
4054
-        return '<a class="' . $class . '" href="' . $url . '"></a>';
4054
+        return '<a class="'.$class.'" href="'.$url.'"></a>';
4055 4055
     }
4056 4056
 
4057 4057
 
@@ -4199,13 +4199,13 @@  discard block
 block discarded – undo
4199 4199
         ?callable $callback = null
4200 4200
     ): bool {
4201 4201
         $entity_ID = absint($entity_ID);
4202
-        if (! $entity_ID) {
4202
+        if ( ! $entity_ID) {
4203 4203
             $this->trashRestoreDeleteError($action, $entity_model);
4204 4204
         }
4205 4205
         $result = 0;
4206 4206
         try {
4207 4207
             $entity = $entity_model->get_one_by_ID($entity_ID);
4208
-            if (! $entity instanceof EE_Base_Class) {
4208
+            if ( ! $entity instanceof EE_Base_Class) {
4209 4209
                 throw new DomainException(
4210 4210
                     sprintf(
4211 4211
                         esc_html__(
@@ -4256,7 +4256,7 @@  discard block
 block discarded – undo
4256 4256
                 )
4257 4257
             );
4258 4258
         }
4259
-        if (! $entity_model->has_field($delete_column)) {
4259
+        if ( ! $entity_model->has_field($delete_column)) {
4260 4260
             throw new DomainException(
4261 4261
                 sprintf(
4262 4262
                     esc_html__(
Please login to merge, or discard this patch.
core/libraries/batch/JobHandlers/ExecuteBatchDeletion.php 2 patches
Indentation   +169 added lines, -169 removed lines patch added patch discarded remove patch
@@ -26,185 +26,185 @@
 block discarded – undo
26 26
  */
27 27
 class ExecuteBatchDeletion extends JobHandler
28 28
 {
29
-    /**
30
-     * @var NodeGroupDao
31
-     */
32
-    protected $model_obj_node_group_persister;
29
+	/**
30
+	 * @var NodeGroupDao
31
+	 */
32
+	protected $model_obj_node_group_persister;
33 33
 
34 34
 
35
-    /**
36
-     * @param NodeGroupDao $model_obj_node_group_persister
37
-     */
38
-    public function __construct(NodeGroupDao $model_obj_node_group_persister)
39
-    {
40
-        $this->model_obj_node_group_persister = $model_obj_node_group_persister;
41
-    }
35
+	/**
36
+	 * @param NodeGroupDao $model_obj_node_group_persister
37
+	 */
38
+	public function __construct(NodeGroupDao $model_obj_node_group_persister)
39
+	{
40
+		$this->model_obj_node_group_persister = $model_obj_node_group_persister;
41
+	}
42 42
 
43 43
 
44
-    // phpcs:disable PSR1.Methods.CamelCapsMethodName.NotCamelCaps
44
+	// phpcs:disable PSR1.Methods.CamelCapsMethodName.NotCamelCaps
45 45
 
46 46
 
47
-    /**
48
-     *
49
-     * @param JobParameters $job_parameters
50
-     * @return JobStepResponse
51
-     * @throws EE_Error
52
-     * @throws ReflectionException
53
-     * @throws Exception
54
-     */
55
-    public function create_job(JobParameters $job_parameters)
56
-    {
57
-        $deletion_job_code = $job_parameters->request_datum('deletion_job_code', null);
58
-        $roots             = $this->model_obj_node_group_persister->getModelObjNodesInGroup($deletion_job_code);
59
-        if ($roots === null) {
60
-            throw new UnexpectedEntityException(
61
-                $roots,
62
-                'array',
63
-                esc_html__(
64
-                    'The job seems to be stale. Please press the back button in your browser twice.',
65
-                    'event_espresso'
66
-                )
67
-            );
68
-        }
69
-        $models_and_ids_to_delete = [];
70
-        foreach ($roots as $root) {
71
-            if (! $root instanceof ModelObjNode) {
72
-                throw new UnexpectedEntityException($root, 'ModelObjNode');
73
-            }
74
-            $models_and_ids_to_delete = array_replace_recursive($models_and_ids_to_delete, $root->getIds());
75
-        }
76
-        $job_parameters->set_extra_data(
77
-            [
78
-                'models_and_ids_to_delete' => $models_and_ids_to_delete,
79
-            ]
80
-        );
81
-        // Find the job's actual size.
82
-        $job_size = 0;
83
-        foreach ($models_and_ids_to_delete as $ids) {
84
-            $job_size += count($ids);
85
-        }
86
-        $job_parameters->set_job_size($job_size);
87
-        $this->updateTextHeader(esc_html__('Beginning to delete items...', 'event_espresso'));
88
-        return new JobStepResponse($job_parameters, $this->feedback);
89
-    }
47
+	/**
48
+	 *
49
+	 * @param JobParameters $job_parameters
50
+	 * @return JobStepResponse
51
+	 * @throws EE_Error
52
+	 * @throws ReflectionException
53
+	 * @throws Exception
54
+	 */
55
+	public function create_job(JobParameters $job_parameters)
56
+	{
57
+		$deletion_job_code = $job_parameters->request_datum('deletion_job_code', null);
58
+		$roots             = $this->model_obj_node_group_persister->getModelObjNodesInGroup($deletion_job_code);
59
+		if ($roots === null) {
60
+			throw new UnexpectedEntityException(
61
+				$roots,
62
+				'array',
63
+				esc_html__(
64
+					'The job seems to be stale. Please press the back button in your browser twice.',
65
+					'event_espresso'
66
+				)
67
+			);
68
+		}
69
+		$models_and_ids_to_delete = [];
70
+		foreach ($roots as $root) {
71
+			if (! $root instanceof ModelObjNode) {
72
+				throw new UnexpectedEntityException($root, 'ModelObjNode');
73
+			}
74
+			$models_and_ids_to_delete = array_replace_recursive($models_and_ids_to_delete, $root->getIds());
75
+		}
76
+		$job_parameters->set_extra_data(
77
+			[
78
+				'models_and_ids_to_delete' => $models_and_ids_to_delete,
79
+			]
80
+		);
81
+		// Find the job's actual size.
82
+		$job_size = 0;
83
+		foreach ($models_and_ids_to_delete as $ids) {
84
+			$job_size += count($ids);
85
+		}
86
+		$job_parameters->set_job_size($job_size);
87
+		$this->updateTextHeader(esc_html__('Beginning to delete items...', 'event_espresso'));
88
+		return new JobStepResponse($job_parameters, $this->feedback);
89
+	}
90 90
 
91 91
 
92
-    /**
93
-     * Performs another step of the job
94
-     *
95
-     * @param JobParameters $job_parameters
96
-     * @param int           $batch_size
97
-     * @return JobStepResponse
98
-     * @throws EE_Error
99
-     * @throws ReflectionException
100
-     */
101
-    public function continue_job(JobParameters $job_parameters, $batch_size = 50)
102
-    {
103
-        // We already have the items IDs. So deleting is really fast. Let's speed it up.
104
-        $batch_size               *= 10;
105
-        $units_processed          = 0;
106
-        $models_and_ids_to_delete = $job_parameters->extra_datum('models_and_ids_to_delete', []);
107
-        // Build a new list of everything leftover after this request's of deletions.
108
-        $models_and_ids_remaining = [];
109
-        foreach ($models_and_ids_to_delete as $model_name => $ids_to_delete) {
110
-            // don't delete logs
111
-            if ($model_name === 'Change_Log') {
112
-                continue;
113
-            }
114
-            if ($units_processed < $batch_size) {
115
-                /** @var EEM_Base $model */
116
-                $model                    = EE_Registry::instance()->load_model($model_name);
117
-                $ids_to_delete_this_query = array_slice($ids_to_delete, 0, $batch_size - $units_processed, true);
118
-                if ($model->has_primary_key_field()) {
119
-                    $where_conditions = [
120
-                        $model->primary_key_name() => [
121
-                            'IN',
122
-                            $ids_to_delete_this_query,
123
-                        ],
124
-                    ];
125
-                } else {
126
-                    $where_conditions = [
127
-                        'OR' => [],
128
-                    ];
129
-                    foreach ($ids_to_delete_this_query as $index_primary_key_string) {
130
-                        $keys_n_values                                                =
131
-                            $model->parse_index_primary_key_string($index_primary_key_string);
132
-                        $where_conditions['OR'][ 'AND*' . $index_primary_key_string ] = $keys_n_values;
133
-                    }
134
-                }
135
-                // Deleting time!
136
-                // The model's deletion method reports every ROW deleted, and in the case of CPT models that will be
137
-                // two rows deleted for event CPT item. So don't rely on it for the count of items deleted.
138
-                $model->delete_permanently([ $where_conditions ], false);
139
-                $units_processed += count($ids_to_delete_this_query);
140
-                $remaining_ids   = array_diff_key($ids_to_delete, $ids_to_delete_this_query);
141
-                // If there's any more from this model, we'll do them next time.
142
-                if (count($remaining_ids) > 0) {
143
-                    $models_and_ids_remaining[ $model_name ] = $remaining_ids;
144
-                }
145
-            } else {
146
-                $models_and_ids_remaining[ $model_name ] = $ids_to_delete;
147
-            }
148
-        }
149
-        $job_parameters->mark_processed($units_processed);
150
-        // All done deleting for this request. Is there anything to do next time?
151
-        if (empty($models_and_ids_remaining)) {
152
-            $job_parameters->set_status(JobParameters::status_complete);
153
-            return new JobStepResponse($job_parameters, $this->feedback);
154
-        }
155
-        $job_parameters->add_extra_data('models_and_ids_to_delete', $models_and_ids_remaining);
156
-        $this->displayJobStepResults(
157
-            $units_processed,
158
-            esc_html__('Deleted %d items.', 'event_espresso')
159
-        );
160
-        return new JobStepResponse($job_parameters, $this->feedback);
161
-    }
92
+	/**
93
+	 * Performs another step of the job
94
+	 *
95
+	 * @param JobParameters $job_parameters
96
+	 * @param int           $batch_size
97
+	 * @return JobStepResponse
98
+	 * @throws EE_Error
99
+	 * @throws ReflectionException
100
+	 */
101
+	public function continue_job(JobParameters $job_parameters, $batch_size = 50)
102
+	{
103
+		// We already have the items IDs. So deleting is really fast. Let's speed it up.
104
+		$batch_size               *= 10;
105
+		$units_processed          = 0;
106
+		$models_and_ids_to_delete = $job_parameters->extra_datum('models_and_ids_to_delete', []);
107
+		// Build a new list of everything leftover after this request's of deletions.
108
+		$models_and_ids_remaining = [];
109
+		foreach ($models_and_ids_to_delete as $model_name => $ids_to_delete) {
110
+			// don't delete logs
111
+			if ($model_name === 'Change_Log') {
112
+				continue;
113
+			}
114
+			if ($units_processed < $batch_size) {
115
+				/** @var EEM_Base $model */
116
+				$model                    = EE_Registry::instance()->load_model($model_name);
117
+				$ids_to_delete_this_query = array_slice($ids_to_delete, 0, $batch_size - $units_processed, true);
118
+				if ($model->has_primary_key_field()) {
119
+					$where_conditions = [
120
+						$model->primary_key_name() => [
121
+							'IN',
122
+							$ids_to_delete_this_query,
123
+						],
124
+					];
125
+				} else {
126
+					$where_conditions = [
127
+						'OR' => [],
128
+					];
129
+					foreach ($ids_to_delete_this_query as $index_primary_key_string) {
130
+						$keys_n_values                                                =
131
+							$model->parse_index_primary_key_string($index_primary_key_string);
132
+						$where_conditions['OR'][ 'AND*' . $index_primary_key_string ] = $keys_n_values;
133
+					}
134
+				}
135
+				// Deleting time!
136
+				// The model's deletion method reports every ROW deleted, and in the case of CPT models that will be
137
+				// two rows deleted for event CPT item. So don't rely on it for the count of items deleted.
138
+				$model->delete_permanently([ $where_conditions ], false);
139
+				$units_processed += count($ids_to_delete_this_query);
140
+				$remaining_ids   = array_diff_key($ids_to_delete, $ids_to_delete_this_query);
141
+				// If there's any more from this model, we'll do them next time.
142
+				if (count($remaining_ids) > 0) {
143
+					$models_and_ids_remaining[ $model_name ] = $remaining_ids;
144
+				}
145
+			} else {
146
+				$models_and_ids_remaining[ $model_name ] = $ids_to_delete;
147
+			}
148
+		}
149
+		$job_parameters->mark_processed($units_processed);
150
+		// All done deleting for this request. Is there anything to do next time?
151
+		if (empty($models_and_ids_remaining)) {
152
+			$job_parameters->set_status(JobParameters::status_complete);
153
+			return new JobStepResponse($job_parameters, $this->feedback);
154
+		}
155
+		$job_parameters->add_extra_data('models_and_ids_to_delete', $models_and_ids_remaining);
156
+		$this->displayJobStepResults(
157
+			$units_processed,
158
+			esc_html__('Deleted %d items.', 'event_espresso')
159
+		);
160
+		return new JobStepResponse($job_parameters, $this->feedback);
161
+	}
162 162
 
163 163
 
164
-    /**
165
-     * Performs any clean-up logic when we know the job is completed
166
-     *
167
-     * @param JobParameters $job_parameters
168
-     * @return JobStepResponse
169
-     * @throws EE_Error
170
-     * @throws ReflectionException
171
-     */
172
-    public function cleanup_job(JobParameters $job_parameters)
173
-    {
174
-        $this->model_obj_node_group_persister->deleteModelObjNodesInGroup(
175
-            $job_parameters->request_datum('deletion_job_code')
176
-        );
177
-        // For backwards compatibility with how we used to delete events, make sure we still trigger the old action.
178
-        $models_and_ids_to_delete = $job_parameters->extra_datum('models_and_ids_to_delete', []);
179
-        foreach ($models_and_ids_to_delete['Event'] as $event_id) {
180
-            // TrashLogger hooks into the following to create a log entry
181
-            // so we know when and who permanently deleted this event.
182
-            do_action(
183
-                'AHEE__Events_Admin_Page___permanently_delete_event__after_event_deleted',
184
-                $event_id,
185
-                'Event',
186
-                $job_parameters
187
-            );
188
-        }
189
-        $this->displayJobFinalResults(
190
-            $job_parameters,
191
-            esc_html__('All Done. Deleted a total of %d items.', 'event_espresso')
192
-        );
193
-        $this->updateText(
194
-            $this->infoWrapper(
195
-                sprintf(
196
-                    esc_html__(
197
-                        'If not automatically redirected in %1$s seconds, click here to return to the %2$sprevious admin screen%3$s',
198
-                        'event_espresso'
199
-                    ),
200
-                    '<span id="ee-redirect-timer">10</span>',
201
-                    '<a href="' . $job_parameters->request_datum('return_url') . '">',
202
-                    '</a>'
203
-                )
204
-            )
205
-        );
206
-        return new JobStepResponse($job_parameters, $this->feedback);
207
-    }
164
+	/**
165
+	 * Performs any clean-up logic when we know the job is completed
166
+	 *
167
+	 * @param JobParameters $job_parameters
168
+	 * @return JobStepResponse
169
+	 * @throws EE_Error
170
+	 * @throws ReflectionException
171
+	 */
172
+	public function cleanup_job(JobParameters $job_parameters)
173
+	{
174
+		$this->model_obj_node_group_persister->deleteModelObjNodesInGroup(
175
+			$job_parameters->request_datum('deletion_job_code')
176
+		);
177
+		// For backwards compatibility with how we used to delete events, make sure we still trigger the old action.
178
+		$models_and_ids_to_delete = $job_parameters->extra_datum('models_and_ids_to_delete', []);
179
+		foreach ($models_and_ids_to_delete['Event'] as $event_id) {
180
+			// TrashLogger hooks into the following to create a log entry
181
+			// so we know when and who permanently deleted this event.
182
+			do_action(
183
+				'AHEE__Events_Admin_Page___permanently_delete_event__after_event_deleted',
184
+				$event_id,
185
+				'Event',
186
+				$job_parameters
187
+			);
188
+		}
189
+		$this->displayJobFinalResults(
190
+			$job_parameters,
191
+			esc_html__('All Done. Deleted a total of %d items.', 'event_espresso')
192
+		);
193
+		$this->updateText(
194
+			$this->infoWrapper(
195
+				sprintf(
196
+					esc_html__(
197
+						'If not automatically redirected in %1$s seconds, click here to return to the %2$sprevious admin screen%3$s',
198
+						'event_espresso'
199
+					),
200
+					'<span id="ee-redirect-timer">10</span>',
201
+					'<a href="' . $job_parameters->request_datum('return_url') . '">',
202
+					'</a>'
203
+				)
204
+			)
205
+		);
206
+		return new JobStepResponse($job_parameters, $this->feedback);
207
+	}
208 208
 }
209 209
 // End of file EventDeletion.php
210 210
 // Location: EventEspressoBatchRequest\JobHandlers/EventDeletion.php
Please login to merge, or discard this patch.
Spacing   +8 added lines, -8 removed lines patch added patch discarded remove patch
@@ -68,7 +68,7 @@  discard block
 block discarded – undo
68 68
         }
69 69
         $models_and_ids_to_delete = [];
70 70
         foreach ($roots as $root) {
71
-            if (! $root instanceof ModelObjNode) {
71
+            if ( ! $root instanceof ModelObjNode) {
72 72
                 throw new UnexpectedEntityException($root, 'ModelObjNode');
73 73
             }
74 74
             $models_and_ids_to_delete = array_replace_recursive($models_and_ids_to_delete, $root->getIds());
@@ -101,7 +101,7 @@  discard block
 block discarded – undo
101 101
     public function continue_job(JobParameters $job_parameters, $batch_size = 50)
102 102
     {
103 103
         // We already have the items IDs. So deleting is really fast. Let's speed it up.
104
-        $batch_size               *= 10;
104
+        $batch_size *= 10;
105 105
         $units_processed          = 0;
106 106
         $models_and_ids_to_delete = $job_parameters->extra_datum('models_and_ids_to_delete', []);
107 107
         // Build a new list of everything leftover after this request's of deletions.
@@ -129,21 +129,21 @@  discard block
 block discarded – undo
129 129
                     foreach ($ids_to_delete_this_query as $index_primary_key_string) {
130 130
                         $keys_n_values                                                =
131 131
                             $model->parse_index_primary_key_string($index_primary_key_string);
132
-                        $where_conditions['OR'][ 'AND*' . $index_primary_key_string ] = $keys_n_values;
132
+                        $where_conditions['OR']['AND*'.$index_primary_key_string] = $keys_n_values;
133 133
                     }
134 134
                 }
135 135
                 // Deleting time!
136 136
                 // The model's deletion method reports every ROW deleted, and in the case of CPT models that will be
137 137
                 // two rows deleted for event CPT item. So don't rely on it for the count of items deleted.
138
-                $model->delete_permanently([ $where_conditions ], false);
138
+                $model->delete_permanently([$where_conditions], false);
139 139
                 $units_processed += count($ids_to_delete_this_query);
140
-                $remaining_ids   = array_diff_key($ids_to_delete, $ids_to_delete_this_query);
140
+                $remaining_ids = array_diff_key($ids_to_delete, $ids_to_delete_this_query);
141 141
                 // If there's any more from this model, we'll do them next time.
142 142
                 if (count($remaining_ids) > 0) {
143
-                    $models_and_ids_remaining[ $model_name ] = $remaining_ids;
143
+                    $models_and_ids_remaining[$model_name] = $remaining_ids;
144 144
                 }
145 145
             } else {
146
-                $models_and_ids_remaining[ $model_name ] = $ids_to_delete;
146
+                $models_and_ids_remaining[$model_name] = $ids_to_delete;
147 147
             }
148 148
         }
149 149
         $job_parameters->mark_processed($units_processed);
@@ -198,7 +198,7 @@  discard block
 block discarded – undo
198 198
                         'event_espresso'
199 199
                     ),
200 200
                     '<span id="ee-redirect-timer">10</span>',
201
-                    '<a href="' . $job_parameters->request_datum('return_url') . '">',
201
+                    '<a href="'.$job_parameters->request_datum('return_url').'">',
202 202
                     '</a>'
203 203
                 )
204 204
             )
Please login to merge, or discard this patch.
core/EE_System.core.php 1 patch
Indentation   +1216 added lines, -1216 removed lines patch added patch discarded remove patch
@@ -24,1220 +24,1220 @@
 block discarded – undo
24 24
  */
25 25
 final class EE_System implements ResettableInterface
26 26
 {
27
-    /**
28
-     * indicates this is a 'normal' request. Ie, not activation, nor upgrade, nor activation.
29
-     * So examples of this would be a normal GET request on the frontend or backend, or a POST, etc
30
-     */
31
-    const req_type_normal = 0;
32
-
33
-    /**
34
-     * Indicates this is a brand new installation of EE so we should install
35
-     * tables and default data etc
36
-     */
37
-    const req_type_new_activation = 1;
38
-
39
-    /**
40
-     * we've detected that EE has been reactivated (or EE was activated during maintenance mode,
41
-     * and we just exited maintenance mode). We MUST check the database is setup properly
42
-     * and that default data is setup too
43
-     */
44
-    const req_type_reactivation = 2;
45
-
46
-    /**
47
-     * indicates that EE has been upgraded since its previous request.
48
-     * We may have data migration scripts to call and will want to trigger maintenance mode
49
-     */
50
-    const req_type_upgrade = 3;
51
-
52
-    /**
53
-     * TODO  will detect that EE has been DOWNGRADED. We probably don't want to run in this case...
54
-     */
55
-    const req_type_downgrade = 4;
56
-
57
-    /**
58
-     * @deprecated since version 4.6.0.dev.006
59
-     * Now whenever a new_activation is detected the request type is still just
60
-     * new_activation (same for reactivation, upgrade, downgrade etc), but if we're in maintenance mode
61
-     * EE_System::initialize_db_if_no_migrations_required and EE_Addon::initialize_db_if_no_migrations_required
62
-     * will instead enqueue that EE plugin's db initialization for when we're taken out of maintenance mode.
63
-     * (Specifically, when the migration manager indicates migrations are finished
64
-     * EE_Data_Migration_Manager::initialize_db_for_enqueued_ee_plugins() will be called)
65
-     */
66
-    const req_type_activation_but_not_installed = 5;
67
-
68
-    /**
69
-     * option prefix for recording the activation history (like core's "espresso_db_update") of addons
70
-     */
71
-    const addon_activation_history_option_prefix = 'ee_addon_activation_history_';
72
-
73
-    /**
74
-     * @var AddonManager $addon_manager
75
-     */
76
-    private $addon_manager;
77
-
78
-    /**
79
-     * @var EE_System $_instance
80
-     */
81
-    private static $_instance;
82
-
83
-    /**
84
-     * @var EE_Registry $registry
85
-     */
86
-    private $registry;
87
-
88
-    /**
89
-     * @var LoaderInterface $loader
90
-     */
91
-    private $loader;
92
-
93
-    /**
94
-     * @var EE_Capabilities $capabilities
95
-     */
96
-    private $capabilities;
97
-
98
-    /**
99
-     * @var EE_Maintenance_Mode $maintenance_mode
100
-     */
101
-    private $maintenance_mode;
102
-
103
-    /**
104
-     * @var RequestInterface $request
105
-     */
106
-    private $request;
107
-
108
-    /**
109
-     * Stores which type of request this is, options being one of the constants on EE_System starting with req_type_*.
110
-     * It can be a brand-new activation, a reactivation, an upgrade, a downgrade, or a normal request.
111
-     *
112
-     * @var int $_req_type
113
-     */
114
-    private $_req_type;
115
-
116
-    /**
117
-     * Whether or not there was a non-micro version change in EE core version during this request
118
-     *
119
-     * @var boolean $_major_version_change
120
-     */
121
-    private $_major_version_change = false;
122
-
123
-    /**
124
-     * @var Router $router
125
-     */
126
-    private $router;
127
-
128
-    /**
129
-     * @param EventEspresso\core\domain\services\custom_post_types\RegisterCustomPostTypes
130
-     */
131
-    private $register_custom_post_types;
132
-
133
-    /**
134
-     * @param EventEspresso\core\domain\services\custom_post_types\RegisterCustomTaxonomies
135
-     */
136
-    private $register_custom_taxonomies;
137
-
138
-    /**
139
-     * @param EventEspresso\core\domain\services\custom_post_types\RegisterCustomTaxonomyTerms
140
-     */
141
-    private $register_custom_taxonomy_terms;
142
-
143
-    /**
144
-     * @singleton method used to instantiate class object
145
-     * @param LoaderInterface|null     $loader
146
-     * @param EE_Maintenance_Mode|null $maintenance_mode
147
-     * @param EE_Registry|null         $registry
148
-     * @param RequestInterface|null    $request
149
-     * @param Router|null              $router
150
-     * @return EE_System
151
-     */
152
-    public static function instance(
153
-        LoaderInterface $loader = null,
154
-        EE_Maintenance_Mode $maintenance_mode = null,
155
-        EE_Registry $registry = null,
156
-        RequestInterface $request = null,
157
-        Router $router = null
158
-    ): EE_System {
159
-        // check if class object is instantiated
160
-        if (! self::$_instance instanceof EE_System) {
161
-            self::$_instance = new self($loader, $maintenance_mode, $registry, $request, $router);
162
-        }
163
-        return self::$_instance;
164
-    }
165
-
166
-
167
-    /**
168
-     * resets the instance and returns it
169
-     *
170
-     * @return EE_System
171
-     */
172
-    public static function reset(): EE_System
173
-    {
174
-        self::$_instance->_req_type = null;
175
-        // make sure none of the old hooks are left hanging around
176
-        remove_all_actions('AHEE__EE_System__perform_activations_upgrades_and_migrations');
177
-        // we need to reset the migration manager in order for it to detect DMSs properly
178
-        EE_Data_Migration_Manager::reset();
179
-        self::instance()->detect_activations_or_upgrades();
180
-        self::instance()->perform_activations_upgrades_and_migrations();
181
-        return self::instance();
182
-    }
183
-
184
-
185
-    /**
186
-     * sets hooks for running rest of system
187
-     * provides "AHEE__EE_System__construct__complete" hook for EE Addons to use as their starting point
188
-     * starting EE Addons from any other point may lead to problems
189
-     *
190
-     * @param LoaderInterface     $loader
191
-     * @param EE_Maintenance_Mode $maintenance_mode
192
-     * @param EE_Registry         $registry
193
-     * @param RequestInterface    $request
194
-     * @param Router              $router
195
-     */
196
-    private function __construct(
197
-        LoaderInterface $loader,
198
-        EE_Maintenance_Mode $maintenance_mode,
199
-        EE_Registry $registry,
200
-        RequestInterface $request,
201
-        Router $router
202
-    ) {
203
-        $this->registry         = $registry;
204
-        $this->loader           = $loader;
205
-        $this->request          = $request;
206
-        $this->router           = $router;
207
-        $this->maintenance_mode = $maintenance_mode;
208
-        do_action('AHEE__EE_System__construct__begin', $this);
209
-        add_action(
210
-            'AHEE__EE_Bootstrap__load_espresso_addons',
211
-            [$this, 'loadCapabilities'],
212
-            5
213
-        );
214
-        add_action(
215
-            'AHEE__EE_Bootstrap__load_espresso_addons',
216
-            [$this, 'loadCommandBus'],
217
-            7
218
-        );
219
-        add_action(
220
-            'AHEE__EE_Bootstrap__load_espresso_addons',
221
-            [$this, 'loadPluginApi'],
222
-            9
223
-        );
224
-        // allow addons to load first so that they can register autoloaders, set hooks for running DMS's, etc
225
-        add_action(
226
-            'AHEE__EE_Bootstrap__load_espresso_addons',
227
-            [$this, 'load_espresso_addons']
228
-        );
229
-        // when an ee addon is activated, we want to call the core hook(s) again
230
-        // because the newly-activated addon didn't get a chance to run at all
231
-        add_action('activate_plugin', [$this, 'load_espresso_addons'], 1);
232
-        // detect whether install or upgrade
233
-        add_action(
234
-            'AHEE__EE_Bootstrap__detect_activations_or_upgrades',
235
-            [$this, 'detect_activations_or_upgrades'],
236
-            3
237
-        );
238
-        // load EE_Config, EE_Textdomain, etc
239
-        add_action(
240
-            'AHEE__EE_Bootstrap__load_core_configuration',
241
-            [$this, 'load_core_configuration'],
242
-            5
243
-        );
244
-        // load specifications for matching routes to current request
245
-        add_action(
246
-            'AHEE__EE_Bootstrap__load_core_configuration',
247
-            [$this, 'loadRouteMatchSpecifications']
248
-        );
249
-        // load specifications for custom post types
250
-        add_action(
251
-            'AHEE__EE_Bootstrap__load_core_configuration',
252
-            array($this, 'loadCustomPostTypes')
253
-        );
254
-        // load specifications for custom post types
255
-        add_action(
256
-            'AHEE__EE_Bootstrap__load_core_configuration',
257
-            array($this, 'loadCustomPostTypes')
258
-        );
259
-        // load EE_Config, EE_Textdomain, etc
260
-        add_action(
261
-            'AHEE__EE_Bootstrap__register_shortcodes_modules_and_widgets',
262
-            [$this, 'register_shortcodes_modules_and_widgets'],
263
-            7
264
-        );
265
-        // you wanna get going? I wanna get going... let's get going!
266
-        add_action(
267
-            'AHEE__EE_Bootstrap__brew_espresso',
268
-            [$this, 'brew_espresso'],
269
-            9
270
-        );
271
-        // other housekeeping
272
-        // exclude EE critical pages from wp_list_pages
273
-        add_filter(
274
-            'wp_list_pages_excludes',
275
-            [$this, 'remove_pages_from_wp_list_pages'],
276
-            10
277
-        );
278
-        // ALL EE Addons should use the following hook point to attach their initial setup too
279
-        // it's extremely important for EE Addons to register any class autoloaders so that they can be available when the EE_Config loads
280
-        do_action('AHEE__EE_System__construct__complete', $this);
281
-    }
282
-
283
-
284
-    /**
285
-     * load and setup EE_Capabilities
286
-     *
287
-     * @return void
288
-     */
289
-    public function loadCapabilities()
290
-    {
291
-        $this->capabilities = $this->loader->getShared('EE_Capabilities');
292
-        add_action(
293
-            'AHEE__EE_Capabilities__init_caps__before_initialization',
294
-            function () {
295
-                LoaderFactory::getLoader()->getShared('EE_Payment_Method_Manager');
296
-            }
297
-        );
298
-    }
299
-
300
-
301
-    /**
302
-     * create and cache the CommandBus, and also add middleware
303
-     * The CapChecker middleware requires the use of EE_Capabilities
304
-     * which is why we need to load the CommandBus after Caps are set up
305
-     * CommandBus middleware operate FIFO - First In First Out
306
-     * so LocateMovedCommands will run first in order to return any new commands
307
-     *
308
-     * @return void
309
-     */
310
-    public function loadCommandBus()
311
-    {
312
-        $this->loader->getShared(
313
-            'CommandBusInterface',
314
-            [
315
-                null,
316
-                apply_filters(
317
-                    'FHEE__EE_Load_Espresso_Core__handle_request__CommandBus_middleware',
318
-                    [
319
-                        $this->loader->getShared('EventEspresso\core\services\commands\middleware\LocateMovedCommands'),
320
-                        $this->loader->getShared('EventEspresso\core\services\commands\middleware\CapChecker'),
321
-                        $this->loader->getShared('EventEspresso\core\services\commands\middleware\AddActionHook'),
322
-                    ]
323
-                ),
324
-            ]
325
-        );
326
-    }
327
-
328
-
329
-    /**
330
-     * @return void
331
-     * @throws Exception
332
-     */
333
-    public function loadPluginApi()
334
-    {
335
-        $this->addon_manager = $this->loader->getShared(AddonManager::class);
336
-        $this->addon_manager->initialize();
337
-        $this->loader->getShared('EE_Request_Handler');
338
-    }
339
-
340
-
341
-    /**
342
-     * load_espresso_addons
343
-     * allow addons to load first so that they can set hooks for running DMS's, etc
344
-     * this is hooked into both:
345
-     *    'AHEE__EE_Bootstrap__load_core_configuration'
346
-     *        which runs during the WP 'plugins_loaded' action at priority 5
347
-     *    and the WP 'activate_plugin' hook point
348
-     *
349
-     * @return void
350
-     * @throws Exception
351
-     */
352
-    public function load_espresso_addons()
353
-    {
354
-        // looking for hooks? they've been moved into the AddonManager to maintain compatibility
355
-        $this->addon_manager->loadAddons();
356
-    }
357
-
358
-
359
-    /**
360
-     * detect_activations_or_upgrades
361
-     * Checks for activation or upgrade of core first;
362
-     * then also checks if any registered addons have been activated or upgraded
363
-     * This is hooked into 'AHEE__EE_Bootstrap__detect_activations_or_upgrades'
364
-     * which runs during the WP 'plugins_loaded' action at priority 3
365
-     *
366
-     * @access public
367
-     * @return void
368
-     */
369
-    public function detect_activations_or_upgrades()
370
-    {
371
-        // first off: let's make sure to handle core
372
-        $this->detect_if_activation_or_upgrade();
373
-        foreach ($this->registry->addons as $addon) {
374
-            if ($addon instanceof EE_Addon) {
375
-                // detect teh request type for that addon
376
-                $addon->detect_req_type();
377
-            }
378
-        }
379
-    }
380
-
381
-
382
-    /**
383
-     * detect_if_activation_or_upgrade
384
-     * Takes care of detecting whether this is a brand new install or code upgrade,
385
-     * and either setting up the DB or setting up maintenance mode etc.
386
-     *
387
-     * @access public
388
-     * @return void
389
-     */
390
-    public function detect_if_activation_or_upgrade()
391
-    {
392
-        do_action('AHEE__EE_System___detect_if_activation_or_upgrade__begin');
393
-        // check if db has been updated, or if its a brand-new installation
394
-        $espresso_db_update = $this->fix_espresso_db_upgrade_option();
395
-        $request_type       = $this->detect_req_type($espresso_db_update);
396
-        // EEH_Debug_Tools::printr( $request_type, '$request_type', __FILE__, __LINE__ );
397
-        switch ($request_type) {
398
-            case EE_System::req_type_new_activation:
399
-                do_action('AHEE__EE_System__detect_if_activation_or_upgrade__new_activation');
400
-                $this->_handle_core_version_change($espresso_db_update);
401
-                break;
402
-            case EE_System::req_type_reactivation:
403
-                do_action('AHEE__EE_System__detect_if_activation_or_upgrade__reactivation');
404
-                $this->_handle_core_version_change($espresso_db_update);
405
-                break;
406
-            case EE_System::req_type_upgrade:
407
-                do_action('AHEE__EE_System__detect_if_activation_or_upgrade__upgrade');
408
-                // migrations may be required now that we've upgraded
409
-                $this->maintenance_mode->set_maintenance_mode_if_db_old();
410
-                $this->_handle_core_version_change($espresso_db_update);
411
-                break;
412
-            case EE_System::req_type_downgrade:
413
-                do_action('AHEE__EE_System__detect_if_activation_or_upgrade__downgrade');
414
-                // its possible migrations are no longer required
415
-                $this->maintenance_mode->set_maintenance_mode_if_db_old();
416
-                $this->_handle_core_version_change($espresso_db_update);
417
-                break;
418
-            case EE_System::req_type_normal:
419
-            default:
420
-                break;
421
-        }
422
-        do_action('AHEE__EE_System__detect_if_activation_or_upgrade__complete');
423
-    }
424
-
425
-
426
-    /**
427
-     * Updates the list of installed versions and sets hooks for
428
-     * initializing the database later during the request
429
-     *
430
-     * @param array $espresso_db_update
431
-     */
432
-    private function _handle_core_version_change(array $espresso_db_update)
433
-    {
434
-        $this->update_list_of_installed_versions($espresso_db_update);
435
-        // get ready to verify the DB is ok (provided we aren't in maintenance mode, of course)
436
-        add_action(
437
-            'AHEE__EE_System__perform_activations_upgrades_and_migrations',
438
-            [$this, 'initialize_db_if_no_migrations_required']
439
-        );
440
-    }
441
-
442
-
443
-    /**
444
-     * standardizes the wp option 'espresso_db_upgrade' which actually stores
445
-     * information about what versions of EE have been installed and activated,
446
-     * NOT necessarily the state of the database
447
-     *
448
-     * @param mixed $espresso_db_update           the value of the WordPress option.
449
-     *                                            If not supplied, fetches it from the options table
450
-     * @return array the correct value of 'espresso_db_upgrade', after saving it, if it needed correction
451
-     */
452
-    private function fix_espresso_db_upgrade_option($espresso_db_update = null): array
453
-    {
454
-        do_action('FHEE__EE_System__manage_fix_espresso_db_upgrade_option__begin', $espresso_db_update);
455
-        if (! $espresso_db_update) {
456
-            $espresso_db_update = get_option('espresso_db_update');
457
-        }
458
-        // check that option is an array
459
-        if (! is_array($espresso_db_update)) {
460
-            // if option is FALSE, then it never existed
461
-            if ($espresso_db_update === false) {
462
-                // make $espresso_db_update an array and save option with autoload OFF
463
-                $espresso_db_update = [];
464
-                add_option('espresso_db_update', $espresso_db_update, '', 'no');
465
-            } else {
466
-                // option is NOT FALSE but also is NOT an array, so make it an array and save it
467
-                $espresso_db_update = [$espresso_db_update => []];
468
-                update_option('espresso_db_update', $espresso_db_update);
469
-            }
470
-        } else {
471
-            $corrected_db_update = [];
472
-            // if IS an array, but is it an array where KEYS are version numbers, and values are arrays?
473
-            foreach ($espresso_db_update as $should_be_version_string => $should_be_array) {
474
-                if (is_int($should_be_version_string) && ! is_array($should_be_array)) {
475
-                    // the key is an int, and the value IS NOT an array
476
-                    // so it must be numerically-indexed, where values are versions installed...
477
-                    // fix it!
478
-                    $version_string                         = $should_be_array;
479
-                    $corrected_db_update[ $version_string ] = ['unknown-date'];
480
-                } else {
481
-                    // ok it checks out
482
-                    $corrected_db_update[ $should_be_version_string ] = $should_be_array;
483
-                }
484
-            }
485
-            $espresso_db_update = $corrected_db_update;
486
-            update_option('espresso_db_update', $espresso_db_update);
487
-        }
488
-        do_action('FHEE__EE_System__manage_fix_espresso_db_upgrade_option__complete', $espresso_db_update);
489
-        return ! empty($espresso_db_update) ? $espresso_db_update : [];
490
-    }
491
-
492
-
493
-    /**
494
-     * Does the traditional work of setting up the plugin's database and adding default data.
495
-     * If migration script/process did not exist, this is what would happen on every activation/reactivation/upgrade.
496
-     * NOTE: if we're in maintenance mode (which would be the case if we detect there are data
497
-     * migration scripts that need to be run and a version change happens), enqueues core for database initialization,
498
-     * so that it will be done when migrations are finished
499
-     *
500
-     * @param boolean $initialize_addons_too if true, we double-check addons' database tables etc too;
501
-     * @param boolean $verify_schema         if true will re-check the database tables have the correct schema.
502
-     *                                       This is a resource-intensive job
503
-     *                                       so we prefer to only do it when necessary
504
-     * @return void
505
-     * @throws EE_Error
506
-     * @throws ReflectionException
507
-     */
508
-    public function initialize_db_if_no_migrations_required($initialize_addons_too = false, $verify_schema = true)
509
-    {
510
-        $request_type = $this->detect_req_type();
511
-        // only initialize system if we're not in maintenance mode.
512
-        if ($this->maintenance_mode->level() !== EE_Maintenance_Mode::level_2_complete_maintenance) {
513
-            /** @var EventEspresso\core\domain\services\custom_post_types\RewriteRules $rewrite_rules */
514
-            $rewrite_rules = $this->loader->getShared(
515
-                'EventEspresso\core\domain\services\custom_post_types\RewriteRules'
516
-            );
517
-            $rewrite_rules->flush();
518
-            if ($verify_schema) {
519
-                EEH_Activation::initialize_db_and_folders();
520
-            }
521
-            EEH_Activation::initialize_db_content();
522
-            EEH_Activation::system_initialization();
523
-            if ($initialize_addons_too) {
524
-                $this->initialize_addons();
525
-            }
526
-        } else {
527
-            EE_Data_Migration_Manager::instance()->enqueue_db_initialization_for('Core');
528
-        }
529
-        if (
530
-            $request_type === EE_System::req_type_new_activation
531
-            || $request_type === EE_System::req_type_reactivation
532
-            || (
533
-                $request_type === EE_System::req_type_upgrade
534
-                && $this->is_major_version_change()
535
-            )
536
-        ) {
537
-            add_action('AHEE__EE_System__initialize_last', [$this, 'redirect_to_about_ee'], 9);
538
-        }
539
-    }
540
-
541
-
542
-    /**
543
-     * Initializes the db for all registered addons
544
-     *
545
-     * @throws EE_Error
546
-     * @throws ReflectionException
547
-     */
548
-    public function initialize_addons()
549
-    {
550
-        // foreach registered addon, make sure its db is up-to-date too
551
-        foreach ($this->registry->addons as $addon) {
552
-            if ($addon instanceof EE_Addon) {
553
-                $addon->initialize_db_if_no_migrations_required();
554
-            }
555
-        }
556
-    }
557
-
558
-
559
-    /**
560
-     * Adds the current code version to the saved wp option which stores a list of all ee versions ever installed.
561
-     *
562
-     * @param array  $version_history
563
-     * @param string $current_version_to_add version to be added to the version history
564
-     * @return    boolean success as to whether or not this option was changed
565
-     */
566
-    public function update_list_of_installed_versions($version_history = null, $current_version_to_add = null): bool
567
-    {
568
-        if (! $version_history) {
569
-            $version_history = $this->fix_espresso_db_upgrade_option($version_history);
570
-        }
571
-        if ($current_version_to_add === null) {
572
-            $current_version_to_add = espresso_version();
573
-        }
574
-        $version_history[ $current_version_to_add ][] = date('Y-m-d H:i:s', time());
575
-        // re-save
576
-        return update_option('espresso_db_update', $version_history);
577
-    }
578
-
579
-
580
-    /**
581
-     * Detects if the current version indicated in the has existed in the list of
582
-     * previously-installed versions of EE (espresso_db_update). Does NOT modify it (ie, no side-effect)
583
-     *
584
-     * @param array $espresso_db_update array from the wp option stored under the name 'espresso_db_update'.
585
-     *                                  If not supplied, fetches it from the options table.
586
-     *                                  Also, caches its result so later parts of the code can also know whether
587
-     *                                  there's been an update or not. This way we can add the current version to
588
-     *                                  espresso_db_update, but still know if this is a new install or not
589
-     * @return int one of the constants on EE_System::req_type_
590
-     */
591
-    public function detect_req_type($espresso_db_update = null): int
592
-    {
593
-        if ($this->_req_type === null) {
594
-            $espresso_db_update          = ! empty($espresso_db_update)
595
-                ? $espresso_db_update
596
-                : $this->fix_espresso_db_upgrade_option();
597
-            $this->_req_type             = EE_System::detect_req_type_given_activation_history(
598
-                $espresso_db_update,
599
-                'ee_espresso_activation',
600
-                espresso_version()
601
-            );
602
-            $this->_major_version_change = $this->_detect_major_version_change($espresso_db_update);
603
-            $this->request->setIsActivation($this->_req_type !== EE_System::req_type_normal);
604
-        }
605
-        return $this->_req_type;
606
-    }
607
-
608
-
609
-    /**
610
-     * Returns whether or not there was a non-micro version change (ie, change in either
611
-     * the first or second number in the version. Eg 4.9.0.rc.001 to 4.10.0.rc.000,
612
-     * but not 4.9.0.rc.0001 to 4.9.1.rc.0001
613
-     *
614
-     * @param $activation_history
615
-     * @return bool
616
-     */
617
-    private function _detect_major_version_change($activation_history): bool
618
-    {
619
-        $previous_version       = EE_System::getMostRecentlyActiveVersion($activation_history);
620
-        $previous_version_parts = explode('.', $previous_version);
621
-        $current_version_parts  = explode('.', espresso_version());
622
-        return isset(
623
-            $previous_version_parts[0],
624
-            $previous_version_parts[1],
625
-            $current_version_parts[0],
626
-            $current_version_parts[1]
627
-        ) && (
628
-            $previous_version_parts[0] !== $current_version_parts[0]
629
-            || $previous_version_parts[1] !== $current_version_parts[1]
630
-        );
631
-    }
632
-
633
-
634
-    /**
635
-     * Returns true if either the major or minor version of EE changed during this request.
636
-     * Eg 4.9.0.rc.001 to 4.10.0.rc.000, but not 4.9.0.rc.0001 to 4.9.1.rc.0001
637
-     *
638
-     * @return bool
639
-     */
640
-    public function is_major_version_change(): bool
641
-    {
642
-        return $this->_major_version_change;
643
-    }
644
-
645
-
646
-    /**
647
-     * Determines the request type for any ee addon, given three piece of info: the current array of activation
648
-     * histories (for core that' 'espresso_db_update' wp option); the name of the WordPress option which is temporarily
649
-     * set upon activation of the plugin (for core it's 'ee_espresso_activation'); and the version that this plugin was
650
-     * just activated to (for core that will always be espresso_version())
651
-     *
652
-     * @param array|null $activation_history             the option's value which stores the activation history for
653
-     *                                                 this
654
-     *                                                 ee plugin. for core that's 'espresso_db_update'
655
-     * @param string $activation_indicator_option_name the name of the WordPress option that is temporarily set to
656
-     *                                                 indicate that this plugin was just activated
657
-     * @param string $current_version                  the version that was just upgraded to (for core that will be
658
-     *                                                 espresso_version())
659
-     * @return int one of the constants on EE_System::req_type_
660
-     */
661
-    public static function detect_req_type_given_activation_history(
662
-        array $activation_history,
663
-        string $activation_indicator_option_name,
664
-        string $current_version
665
-    ): int {
666
-        $version_change = self::compareVersionWithPrevious($activation_history, $current_version);
667
-        $is_activation  = get_option($activation_indicator_option_name, false);
668
-        $req_type       = self::getRequestType($activation_history, $version_change, $is_activation);
669
-        if ($is_activation) {
670
-            // cleanup in aisle 6
671
-            delete_option($activation_indicator_option_name);
672
-        }
673
-        return $req_type;
674
-    }
675
-
676
-
677
-    /**
678
-     * @param array  $activation_history
679
-     * @param int    $version_change
680
-     * @param bool   $is_activation
681
-     * @return int
682
-     * @since $VID:$
683
-     */
684
-    private static function getRequestType(array $activation_history, int $version_change, bool $is_activation): int
685
-    {
686
-        // if no previous activation history exists, then this is a brand new install
687
-        if (empty($activation_history)) {
688
-            return EE_System::req_type_new_activation;
689
-        }
690
-        // current version is higher than previous version, so it's an upgrade
691
-        if ($version_change === 1) {
692
-            return EE_System::req_type_upgrade;
693
-        }
694
-        // current version is lower than previous version, so it's a downgrade
695
-        if ($version_change === -1) {
696
-            return EE_System::req_type_downgrade;
697
-        }
698
-        // version hasn't changed since last version so check if the activation indicator is set
699
-        // to determine if it's a reactivation, or just a normal request
700
-        return $is_activation
701
-            ? EE_System::req_type_reactivation
702
-            : EE_System::req_type_normal;
703
-    }
704
-
705
-
706
-    /**
707
-     * Detects if the $version_to_upgrade_to is higher than the most recent version in
708
-     * the $activation_history_for_addon
709
-     *
710
-     * @param array  $activation_history    array where keys are versions,
711
-     *                                      values are arrays of times activated (sometimes 'unknown-date')
712
-     * @param string $current_version
713
-     * @return int results of version_compare( $version_to_upgrade_to, $most_recently_active_version ).
714
-     *                                      -1 if $version_to_upgrade_to is LOWER (downgrade);
715
-     *                                      0 if $version_to_upgrade_to MATCHES (reactivation or normal request);
716
-     *                                      1 if $version_to_upgrade_to is HIGHER (upgrade) ;
717
-     */
718
-    private static function compareVersionWithPrevious(array $activation_history, string $current_version): int
719
-    {
720
-        // find the most recently-activated version
721
-        $most_recently_active_version = EE_System::getMostRecentlyActiveVersion($activation_history);
722
-        return version_compare($current_version, $most_recently_active_version);
723
-    }
724
-
725
-
726
-    /**
727
-     * Gets the most recently active version listed in the activation history,
728
-     * and if none are found (ie, it's a brand new install) returns '0.0.0.dev.000'.
729
-     *
730
-     * @param array $activation_history  (keys are versions, values are arrays of times activated,
731
-     *                                   sometimes containing 'unknown-date'
732
-     * @return string
733
-     */
734
-    private static function getMostRecentlyActiveVersion(array $activation_history): string
735
-    {
736
-        $most_recent_activation_date  = '1970-01-01 00:00:00';
737
-        $most_recently_active_version = '0.0.0.dev.000';
738
-        if (is_array($activation_history)) {
739
-            foreach ($activation_history as $version => $activation_dates) {
740
-                // check there is a record of when this version was activated.
741
-                // Otherwise, mark it as unknown
742
-                if (! $activation_dates) {
743
-                    $activation_dates = ['unknown-date'];
744
-                }
745
-                $activation_dates = is_string($activation_dates) ? [$activation_dates] : $activation_dates;
746
-                foreach ($activation_dates as $activation_date) {
747
-                    if ($activation_date !== 'unknown-date' && $activation_date > $most_recent_activation_date) {
748
-                        $most_recently_active_version = $version;
749
-                        $most_recent_activation_date  = $activation_date;
750
-                    }
751
-                }
752
-            }
753
-        }
754
-        return $most_recently_active_version;
755
-    }
756
-
757
-
758
-    /**
759
-     * This redirects to the about EE page after activation
760
-     *
761
-     * @return void
762
-     */
763
-    public function redirect_to_about_ee()
764
-    {
765
-        $notices = EE_Error::get_notices(false);
766
-        // if current user is an admin and it's not an ajax or rest request
767
-        if (
768
-            ! isset($notices['errors'])
769
-            && $this->request->isAdmin()
770
-            && apply_filters(
771
-                'FHEE__EE_System__redirect_to_about_ee__do_redirect',
772
-                $this->capabilities->current_user_can('manage_options', 'espresso_about_default')
773
-            )
774
-        ) {
775
-            $query_params = ['page' => 'espresso_about'];
776
-            if (EE_System::instance()->detect_req_type() === EE_System::req_type_new_activation) {
777
-                $query_params['new_activation'] = true;
778
-            }
779
-            if (EE_System::instance()->detect_req_type() === EE_System::req_type_reactivation) {
780
-                $query_params['reactivation'] = true;
781
-            }
782
-            $url = add_query_arg($query_params, admin_url('admin.php'));
783
-            EEH_URL::safeRedirectAndExit($url);
784
-        }
785
-    }
786
-
787
-
788
-    /**
789
-     * load_core_configuration
790
-     * this is hooked into 'AHEE__EE_Bootstrap__load_core_configuration'
791
-     * which runs during the WP 'plugins_loaded' action at priority 5
792
-     *
793
-     * @return void
794
-     * @throws ReflectionException
795
-     * @throws Exception
796
-     */
797
-    public function load_core_configuration()
798
-    {
799
-        do_action('AHEE__EE_System__load_core_configuration__begin', $this);
800
-        $this->loader->getShared('EE_Load_Textdomain');
801
-        // load textdomain
802
-        EE_Load_Textdomain::load_textdomain();
803
-        // load caf stuff a chance to play during the activation process too.
804
-        $this->_maybe_brew_regular();
805
-        // load and setup EE_Config and EE_Network_Config
806
-        $config = $this->loader->getShared('EE_Config');
807
-        $this->loader->getShared('EE_Network_Config');
808
-        // setup autoloaders
809
-        // enable logging?
810
-        $this->loader->getShared('EventEspresso\core\services\orm\TrashLogger');
811
-        if ($config->admin->use_remote_logging) {
812
-            $this->loader->getShared('EE_Log');
813
-        }
814
-        // check for activation errors
815
-        $activation_errors = get_option('ee_plugin_activation_errors', false);
816
-        if ($activation_errors) {
817
-            EE_Error::add_error($activation_errors, __FILE__, __FUNCTION__, __LINE__);
818
-            update_option('ee_plugin_activation_errors', false);
819
-        }
820
-        // get model names
821
-        $this->_parse_model_names();
822
-        // configure custom post type definitions
823
-        $this->loader->getShared('EventEspresso\core\domain\entities\custom_post_types\CustomTaxonomyDefinitions');
824
-        $this->loader->getShared('EventEspresso\core\domain\entities\custom_post_types\CustomPostTypeDefinitions');
825
-        do_action('AHEE__EE_System__load_core_configuration__complete', $this);
826
-    }
827
-
828
-
829
-    /**
830
-     * cycles through all of the models/*.model.php files, and assembles an array of model names
831
-     *
832
-     * @return void
833
-     * @throws ReflectionException
834
-     */
835
-    private function _parse_model_names()
836
-    {
837
-        // get all the files in the EE_MODELS folder that end in .model.php
838
-        $models                 = glob(EE_MODELS . '*.model.php');
839
-        $model_names            = [];
840
-        $non_abstract_db_models = [];
841
-        foreach ($models as $model) {
842
-            // get model classname
843
-            $classname       = EEH_File::get_classname_from_filepath_with_standard_filename($model);
844
-            $short_name      = str_replace('EEM_', '', $classname);
845
-            $reflectionClass = new ReflectionClass($classname);
846
-            if ($reflectionClass->isSubclassOf('EEM_Base') && ! $reflectionClass->isAbstract()) {
847
-                $non_abstract_db_models[ $short_name ] = $classname;
848
-            }
849
-            $model_names[ $short_name ] = $classname;
850
-        }
851
-        $this->registry->models                 = apply_filters('FHEE__EE_System__parse_model_names', $model_names);
852
-        $this->registry->non_abstract_db_models = apply_filters(
853
-            'FHEE__EE_System__parse_implemented_model_names',
854
-            $non_abstract_db_models
855
-        );
856
-    }
857
-
858
-
859
-    /**
860
-     * The purpose of this method is to simply check for a file named "caffeinated/brewing_regular.php" for any hooks
861
-     * that need to be setup before our EE_System launches.
862
-     *
863
-     * @return void
864
-     * @throws DomainException
865
-     * @throws InvalidArgumentException
866
-     * @throws InvalidDataTypeException
867
-     * @throws InvalidInterfaceException
868
-     * @throws InvalidClassException
869
-     * @throws InvalidFilePathException
870
-     */
871
-    private function _maybe_brew_regular()
872
-    {
873
-        /** @var Domain $domain */
874
-        $domain = DomainFactory::getEventEspressoCoreDomain();
875
-        if ($domain->isCaffeinated()) {
876
-            require_once EE_CAFF_PATH . 'brewing_regular.php';
877
-        }
878
-    }
879
-
880
-
881
-    /**
882
-     * @throws Exception
883
-     * @since 4.9.71.p
884
-     */
885
-    public function loadRouteMatchSpecifications()
886
-    {
887
-        try {
888
-            $this->loader->getShared('EventEspresso\core\services\routing\RouteMatchSpecificationManager');
889
-            $this->loader->getShared('EventEspresso\core\services\routing\RouteCollection');
890
-            $this->router->loadPrimaryRoutes();
891
-        } catch (Exception $exception) {
892
-            new ExceptionStackTraceDisplay($exception);
893
-        }
894
-        do_action('AHEE__EE_System__loadRouteMatchSpecifications');
895
-    }
896
-
897
-
898
-    /**
899
-     * loading CPT related classes earlier so that their definitions are available
900
-     * but not performing any actual registration with WP core until load_CPTs_and_session() is called
901
-     *
902
-     * @since   4.10.21.p
903
-     */
904
-    public function loadCustomPostTypes()
905
-    {
906
-        $this->register_custom_taxonomies = $this->loader->getShared(
907
-            'EventEspresso\core\domain\services\custom_post_types\RegisterCustomTaxonomies'
908
-        );
909
-        $this->register_custom_post_types = $this->loader->getShared(
910
-            'EventEspresso\core\domain\services\custom_post_types\RegisterCustomPostTypes'
911
-        );
912
-        $this->register_custom_taxonomy_terms = $this->loader->getShared(
913
-            'EventEspresso\core\domain\services\custom_post_types\RegisterCustomTaxonomyTerms'
914
-        );
915
-        // integrate WP_Query with the EE models
916
-        $this->loader->getShared('EE_CPT_Strategy');
917
-        // load legacy EE_Request_Handler in case add-ons still need it
918
-        $this->loader->getShared('EE_Request_Handler');
919
-    }
920
-
921
-
922
-    /**
923
-     * register_shortcodes_modules_and_widgets
924
-     * generate lists of shortcodes and modules, then verify paths and classes
925
-     * This is hooked into 'AHEE__EE_Bootstrap__register_shortcodes_modules_and_widgets'
926
-     * which runs during the WP 'plugins_loaded' action at priority 7
927
-     *
928
-     * @access public
929
-     * @return void
930
-     * @throws Exception
931
-     */
932
-    public function register_shortcodes_modules_and_widgets()
933
-    {
934
-        $this->router->registerShortcodesModulesAndWidgets();
935
-        do_action('AHEE__EE_System__register_shortcodes_modules_and_widgets');
936
-        // check for addons using old hook point
937
-        if (has_action('AHEE__EE_System__register_shortcodes_modules_and_addons')) {
938
-            $this->_incompatible_addon_error();
939
-        }
940
-    }
941
-
942
-
943
-    /**
944
-     * _incompatible_addon_error
945
-     *
946
-     * @access public
947
-     * @return void
948
-     */
949
-    private function _incompatible_addon_error()
950
-    {
951
-        // get array of classes hooking into here
952
-        $class_names = EEH_Class_Tools::get_class_names_for_all_callbacks_on_hook(
953
-            'AHEE__EE_System__register_shortcodes_modules_and_addons'
954
-        );
955
-        if (! empty($class_names)) {
956
-            $msg = esc_html__(
957
-                'The following plugins, addons, or modules appear to be incompatible with this version of Event Espresso and were automatically deactivated to avoid fatal errors:',
958
-                'event_espresso'
959
-            );
960
-            $msg .= '<ul>';
961
-            foreach ($class_names as $class_name) {
962
-                $msg .= '<li><b>Event Espresso - '
963
-                        . str_replace(
964
-                            ['EE_', 'EEM_', 'EED_', 'EES_', 'EEW_'],
965
-                            '',
966
-                            $class_name
967
-                        ) . '</b></li>';
968
-            }
969
-            $msg .= '</ul>';
970
-            $msg .= esc_html__(
971
-                'Compatibility issues can be avoided and/or resolved by keeping addons and plugins updated to the latest version.',
972
-                'event_espresso'
973
-            );
974
-            // save list of incompatible addons to wp-options for later use
975
-            add_option('ee_incompatible_addons', $class_names, '', 'no');
976
-            if (is_admin()) {
977
-                EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
978
-            }
979
-        }
980
-    }
981
-
982
-
983
-    /**
984
-     * brew_espresso
985
-     * begins the process of setting hooks for initializing EE in the correct order
986
-     * This is happening on the 'AHEE__EE_Bootstrap__brew_espresso' hook point
987
-     * which runs during the WP 'plugins_loaded' action at priority 9
988
-     *
989
-     * @return void
990
-     * @throws Exception
991
-     */
992
-    public function brew_espresso()
993
-    {
994
-        do_action('AHEE__EE_System__brew_espresso__begin', $this);
995
-        // load some final core systems
996
-        add_action('init', [$this, 'set_hooks_for_core'], 1);
997
-        add_action('init', [$this, 'perform_activations_upgrades_and_migrations'], 3);
998
-        add_action('init', [$this, 'load_CPTs_and_session'], 5);
999
-        add_action('init', [$this, 'load_controllers'], 7);
1000
-        add_action('init', [$this, 'core_loaded_and_ready'], 9);
1001
-        add_action('init', [$this, 'initialize'], 10);
1002
-        add_action('init', [$this, 'initialize_last'], 100);
1003
-        $this->router->brewEspresso();
1004
-        do_action('AHEE__EE_System__brew_espresso__complete', $this);
1005
-    }
1006
-
1007
-
1008
-    /**
1009
-     *    set_hooks_for_core
1010
-     *
1011
-     * @access public
1012
-     * @return    void
1013
-     * @throws EE_Error
1014
-     */
1015
-    public function set_hooks_for_core()
1016
-    {
1017
-        $this->_deactivate_incompatible_addons();
1018
-        do_action('AHEE__EE_System__set_hooks_for_core');
1019
-        $this->loader->getShared('EventEspresso\core\domain\values\session\SessionLifespan');
1020
-        // caps need to be initialized on every request so that capability maps are set.
1021
-        // @see https://events.codebasehq.com/projects/event-espresso/tickets/8674
1022
-        $this->registry->CAP->init_caps();
1023
-    }
1024
-
1025
-
1026
-    /**
1027
-     * Using the information gathered in EE_System::_incompatible_addon_error,
1028
-     * deactivates any addons considered incompatible with the current version of EE
1029
-     */
1030
-    private function _deactivate_incompatible_addons()
1031
-    {
1032
-        $incompatible_addons = get_option('ee_incompatible_addons', []);
1033
-        if (! empty($incompatible_addons)) {
1034
-            $active_plugins = get_option('active_plugins', []);
1035
-            foreach ($active_plugins as $active_plugin) {
1036
-                foreach ($incompatible_addons as $incompatible_addon) {
1037
-                    if (strpos($active_plugin, $incompatible_addon) !== false) {
1038
-                        $this->request->unSetRequestParams(['activate'], true);
1039
-                        espresso_deactivate_plugin($active_plugin);
1040
-                    }
1041
-                }
1042
-            }
1043
-        }
1044
-    }
1045
-
1046
-
1047
-    /**
1048
-     *    perform_activations_upgrades_and_migrations
1049
-     *
1050
-     * @access public
1051
-     * @return    void
1052
-     */
1053
-    public function perform_activations_upgrades_and_migrations()
1054
-    {
1055
-        do_action('AHEE__EE_System__perform_activations_upgrades_and_migrations');
1056
-    }
1057
-
1058
-
1059
-    /**
1060
-     * @return void
1061
-     * @throws DomainException
1062
-     */
1063
-    public function load_CPTs_and_session()
1064
-    {
1065
-        do_action('AHEE__EE_System__load_CPTs_and_session__start');
1066
-        $this->register_custom_taxonomies->registerCustomTaxonomies();
1067
-        $this->register_custom_post_types->registerCustomPostTypes();
1068
-        $this->register_custom_taxonomy_terms->registerCustomTaxonomyTerms();
1069
-        // load legacy Custom Post Types and Taxonomies
1070
-        $this->loader->getShared('EE_Register_CPTs');
1071
-        do_action('AHEE__EE_System__load_CPTs_and_session__complete');
1072
-    }
1073
-
1074
-
1075
-    /**
1076
-     * load_controllers
1077
-     * this is the best place to load any additional controllers that needs access to EE core.
1078
-     * it is expected that all basic core EE systems, that are not dependant on the current request are loaded at this
1079
-     * time
1080
-     *
1081
-     * @access public
1082
-     * @return void
1083
-     * @throws Exception
1084
-     */
1085
-    public function load_controllers()
1086
-    {
1087
-        do_action('AHEE__EE_System__load_controllers__start');
1088
-        $this->router->loadControllers();
1089
-        do_action('AHEE__EE_System__load_controllers__complete');
1090
-    }
1091
-
1092
-
1093
-    /**
1094
-     * core_loaded_and_ready
1095
-     * all of the basic EE core should be loaded at this point and available regardless of M-Mode
1096
-     *
1097
-     * @access public
1098
-     * @return void
1099
-     * @throws Exception
1100
-     */
1101
-    public function core_loaded_and_ready()
1102
-    {
1103
-        $this->router->coreLoadedAndReady();
1104
-        do_action('AHEE__EE_System__core_loaded_and_ready');
1105
-        // always load template tags, because it's faster than checking if it's a front-end request, and many page
1106
-        // builders require these even on the front-end
1107
-        require_once EE_PUBLIC . 'template_tags.php';
1108
-        do_action('AHEE__EE_System__set_hooks_for_shortcodes_modules_and_addons');
1109
-    }
1110
-
1111
-
1112
-    /**
1113
-     * initialize
1114
-     * this is the best place to begin initializing client code
1115
-     *
1116
-     * @access public
1117
-     * @return void
1118
-     */
1119
-    public function initialize()
1120
-    {
1121
-        do_action('AHEE__EE_System__initialize');
1122
-        add_filter(
1123
-            'safe_style_css',
1124
-            function ($styles) {
1125
-                $styles[] = 'display';
1126
-                $styles[] = 'visibility';
1127
-                $styles[] = 'position';
1128
-                $styles[] = 'top';
1129
-                $styles[] = 'right';
1130
-                $styles[] = 'bottom';
1131
-                $styles[] = 'left';
1132
-                $styles[] = 'resize';
1133
-                $styles[] = 'max-width';
1134
-                $styles[] = 'max-height';
1135
-                return $styles;
1136
-            }
1137
-        );
1138
-    }
1139
-
1140
-
1141
-    /**
1142
-     * initialize_last
1143
-     * this is run really late during the WP init hook point, and ensures that mostly everything else that needs to
1144
-     * initialize has done so
1145
-     *
1146
-     * @access public
1147
-     * @return void
1148
-     * @throws Exception
1149
-     */
1150
-    public function initialize_last()
1151
-    {
1152
-        $this->router->initializeLast();
1153
-        do_action('AHEE__EE_System__initialize_last');
1154
-        /** @var EventEspresso\core\domain\services\custom_post_types\RewriteRules $rewrite_rules */
1155
-        $rewrite_rules = $this->loader->getShared(
1156
-            'EventEspresso\core\domain\services\custom_post_types\RewriteRules'
1157
-        );
1158
-        $rewrite_rules->flushRewriteRules();
1159
-        add_action('admin_bar_init', [$this, 'addEspressoToolbar']);
1160
-    }
1161
-
1162
-
1163
-    /**
1164
-     * @return void
1165
-     */
1166
-    public function addEspressoToolbar()
1167
-    {
1168
-        $this->loader->getShared(
1169
-            'EventEspresso\core\domain\services\admin\AdminToolBar',
1170
-            [$this->registry->CAP]
1171
-        );
1172
-    }
1173
-
1174
-
1175
-    /**
1176
-     * do_not_cache
1177
-     * sets no cache headers and defines no cache constants for WP plugins
1178
-     *
1179
-     * @access public
1180
-     * @return void
1181
-     */
1182
-    public static function do_not_cache()
1183
-    {
1184
-        // set no cache constants
1185
-        if (! defined('DONOTCACHEPAGE')) {
1186
-            define('DONOTCACHEPAGE', true);
1187
-        }
1188
-        if (! defined('DONOTCACHCEOBJECT')) {
1189
-            define('DONOTCACHCEOBJECT', true);
1190
-        }
1191
-        if (! defined('DONOTCACHEDB')) {
1192
-            define('DONOTCACHEDB', true);
1193
-        }
1194
-        // add no cache headers
1195
-        add_action('send_headers', ['EE_System', 'nocache_headers'], 10);
1196
-        // plus a little extra for nginx and Google Chrome
1197
-        add_filter('nocache_headers', ['EE_System', 'extra_nocache_headers'], 10, 1);
1198
-        // prevent browsers from prefetching of the rel='next' link, because it may contain content that interferes with the registration process
1199
-        remove_action('wp_head', 'adjacent_posts_rel_link_wp_head');
1200
-    }
1201
-
1202
-
1203
-    /**
1204
-     *    extra_nocache_headers
1205
-     *
1206
-     * @access    public
1207
-     * @param $headers
1208
-     * @return    array
1209
-     */
1210
-    public static function extra_nocache_headers($headers): array
1211
-    {
1212
-        // for NGINX
1213
-        $headers['X-Accel-Expires'] = 0;
1214
-        // plus extra for Google Chrome since it doesn't seem to respect "no-cache", but WILL respect "no-store"
1215
-        $headers['Cache-Control'] = 'no-store, no-cache, must-revalidate, max-age=0';
1216
-        return $headers;
1217
-    }
1218
-
1219
-
1220
-    /**
1221
-     *    nocache_headers
1222
-     *
1223
-     * @access    public
1224
-     * @return    void
1225
-     */
1226
-    public static function nocache_headers()
1227
-    {
1228
-        nocache_headers();
1229
-    }
1230
-
1231
-
1232
-    /**
1233
-     * simply hooks into "wp_list_pages_exclude" filter (for wp_list_pages method) and makes sure EE critical pages are
1234
-     * never returned with the function.
1235
-     *
1236
-     * @param array $exclude_array any existing pages being excluded are in this array.
1237
-     * @return array
1238
-     */
1239
-    public function remove_pages_from_wp_list_pages(array $exclude_array): array
1240
-    {
1241
-        return array_merge($exclude_array, $this->registry->CFG->core->get_critical_pages_array());
1242
-    }
27
+	/**
28
+	 * indicates this is a 'normal' request. Ie, not activation, nor upgrade, nor activation.
29
+	 * So examples of this would be a normal GET request on the frontend or backend, or a POST, etc
30
+	 */
31
+	const req_type_normal = 0;
32
+
33
+	/**
34
+	 * Indicates this is a brand new installation of EE so we should install
35
+	 * tables and default data etc
36
+	 */
37
+	const req_type_new_activation = 1;
38
+
39
+	/**
40
+	 * we've detected that EE has been reactivated (or EE was activated during maintenance mode,
41
+	 * and we just exited maintenance mode). We MUST check the database is setup properly
42
+	 * and that default data is setup too
43
+	 */
44
+	const req_type_reactivation = 2;
45
+
46
+	/**
47
+	 * indicates that EE has been upgraded since its previous request.
48
+	 * We may have data migration scripts to call and will want to trigger maintenance mode
49
+	 */
50
+	const req_type_upgrade = 3;
51
+
52
+	/**
53
+	 * TODO  will detect that EE has been DOWNGRADED. We probably don't want to run in this case...
54
+	 */
55
+	const req_type_downgrade = 4;
56
+
57
+	/**
58
+	 * @deprecated since version 4.6.0.dev.006
59
+	 * Now whenever a new_activation is detected the request type is still just
60
+	 * new_activation (same for reactivation, upgrade, downgrade etc), but if we're in maintenance mode
61
+	 * EE_System::initialize_db_if_no_migrations_required and EE_Addon::initialize_db_if_no_migrations_required
62
+	 * will instead enqueue that EE plugin's db initialization for when we're taken out of maintenance mode.
63
+	 * (Specifically, when the migration manager indicates migrations are finished
64
+	 * EE_Data_Migration_Manager::initialize_db_for_enqueued_ee_plugins() will be called)
65
+	 */
66
+	const req_type_activation_but_not_installed = 5;
67
+
68
+	/**
69
+	 * option prefix for recording the activation history (like core's "espresso_db_update") of addons
70
+	 */
71
+	const addon_activation_history_option_prefix = 'ee_addon_activation_history_';
72
+
73
+	/**
74
+	 * @var AddonManager $addon_manager
75
+	 */
76
+	private $addon_manager;
77
+
78
+	/**
79
+	 * @var EE_System $_instance
80
+	 */
81
+	private static $_instance;
82
+
83
+	/**
84
+	 * @var EE_Registry $registry
85
+	 */
86
+	private $registry;
87
+
88
+	/**
89
+	 * @var LoaderInterface $loader
90
+	 */
91
+	private $loader;
92
+
93
+	/**
94
+	 * @var EE_Capabilities $capabilities
95
+	 */
96
+	private $capabilities;
97
+
98
+	/**
99
+	 * @var EE_Maintenance_Mode $maintenance_mode
100
+	 */
101
+	private $maintenance_mode;
102
+
103
+	/**
104
+	 * @var RequestInterface $request
105
+	 */
106
+	private $request;
107
+
108
+	/**
109
+	 * Stores which type of request this is, options being one of the constants on EE_System starting with req_type_*.
110
+	 * It can be a brand-new activation, a reactivation, an upgrade, a downgrade, or a normal request.
111
+	 *
112
+	 * @var int $_req_type
113
+	 */
114
+	private $_req_type;
115
+
116
+	/**
117
+	 * Whether or not there was a non-micro version change in EE core version during this request
118
+	 *
119
+	 * @var boolean $_major_version_change
120
+	 */
121
+	private $_major_version_change = false;
122
+
123
+	/**
124
+	 * @var Router $router
125
+	 */
126
+	private $router;
127
+
128
+	/**
129
+	 * @param EventEspresso\core\domain\services\custom_post_types\RegisterCustomPostTypes
130
+	 */
131
+	private $register_custom_post_types;
132
+
133
+	/**
134
+	 * @param EventEspresso\core\domain\services\custom_post_types\RegisterCustomTaxonomies
135
+	 */
136
+	private $register_custom_taxonomies;
137
+
138
+	/**
139
+	 * @param EventEspresso\core\domain\services\custom_post_types\RegisterCustomTaxonomyTerms
140
+	 */
141
+	private $register_custom_taxonomy_terms;
142
+
143
+	/**
144
+	 * @singleton method used to instantiate class object
145
+	 * @param LoaderInterface|null     $loader
146
+	 * @param EE_Maintenance_Mode|null $maintenance_mode
147
+	 * @param EE_Registry|null         $registry
148
+	 * @param RequestInterface|null    $request
149
+	 * @param Router|null              $router
150
+	 * @return EE_System
151
+	 */
152
+	public static function instance(
153
+		LoaderInterface $loader = null,
154
+		EE_Maintenance_Mode $maintenance_mode = null,
155
+		EE_Registry $registry = null,
156
+		RequestInterface $request = null,
157
+		Router $router = null
158
+	): EE_System {
159
+		// check if class object is instantiated
160
+		if (! self::$_instance instanceof EE_System) {
161
+			self::$_instance = new self($loader, $maintenance_mode, $registry, $request, $router);
162
+		}
163
+		return self::$_instance;
164
+	}
165
+
166
+
167
+	/**
168
+	 * resets the instance and returns it
169
+	 *
170
+	 * @return EE_System
171
+	 */
172
+	public static function reset(): EE_System
173
+	{
174
+		self::$_instance->_req_type = null;
175
+		// make sure none of the old hooks are left hanging around
176
+		remove_all_actions('AHEE__EE_System__perform_activations_upgrades_and_migrations');
177
+		// we need to reset the migration manager in order for it to detect DMSs properly
178
+		EE_Data_Migration_Manager::reset();
179
+		self::instance()->detect_activations_or_upgrades();
180
+		self::instance()->perform_activations_upgrades_and_migrations();
181
+		return self::instance();
182
+	}
183
+
184
+
185
+	/**
186
+	 * sets hooks for running rest of system
187
+	 * provides "AHEE__EE_System__construct__complete" hook for EE Addons to use as their starting point
188
+	 * starting EE Addons from any other point may lead to problems
189
+	 *
190
+	 * @param LoaderInterface     $loader
191
+	 * @param EE_Maintenance_Mode $maintenance_mode
192
+	 * @param EE_Registry         $registry
193
+	 * @param RequestInterface    $request
194
+	 * @param Router              $router
195
+	 */
196
+	private function __construct(
197
+		LoaderInterface $loader,
198
+		EE_Maintenance_Mode $maintenance_mode,
199
+		EE_Registry $registry,
200
+		RequestInterface $request,
201
+		Router $router
202
+	) {
203
+		$this->registry         = $registry;
204
+		$this->loader           = $loader;
205
+		$this->request          = $request;
206
+		$this->router           = $router;
207
+		$this->maintenance_mode = $maintenance_mode;
208
+		do_action('AHEE__EE_System__construct__begin', $this);
209
+		add_action(
210
+			'AHEE__EE_Bootstrap__load_espresso_addons',
211
+			[$this, 'loadCapabilities'],
212
+			5
213
+		);
214
+		add_action(
215
+			'AHEE__EE_Bootstrap__load_espresso_addons',
216
+			[$this, 'loadCommandBus'],
217
+			7
218
+		);
219
+		add_action(
220
+			'AHEE__EE_Bootstrap__load_espresso_addons',
221
+			[$this, 'loadPluginApi'],
222
+			9
223
+		);
224
+		// allow addons to load first so that they can register autoloaders, set hooks for running DMS's, etc
225
+		add_action(
226
+			'AHEE__EE_Bootstrap__load_espresso_addons',
227
+			[$this, 'load_espresso_addons']
228
+		);
229
+		// when an ee addon is activated, we want to call the core hook(s) again
230
+		// because the newly-activated addon didn't get a chance to run at all
231
+		add_action('activate_plugin', [$this, 'load_espresso_addons'], 1);
232
+		// detect whether install or upgrade
233
+		add_action(
234
+			'AHEE__EE_Bootstrap__detect_activations_or_upgrades',
235
+			[$this, 'detect_activations_or_upgrades'],
236
+			3
237
+		);
238
+		// load EE_Config, EE_Textdomain, etc
239
+		add_action(
240
+			'AHEE__EE_Bootstrap__load_core_configuration',
241
+			[$this, 'load_core_configuration'],
242
+			5
243
+		);
244
+		// load specifications for matching routes to current request
245
+		add_action(
246
+			'AHEE__EE_Bootstrap__load_core_configuration',
247
+			[$this, 'loadRouteMatchSpecifications']
248
+		);
249
+		// load specifications for custom post types
250
+		add_action(
251
+			'AHEE__EE_Bootstrap__load_core_configuration',
252
+			array($this, 'loadCustomPostTypes')
253
+		);
254
+		// load specifications for custom post types
255
+		add_action(
256
+			'AHEE__EE_Bootstrap__load_core_configuration',
257
+			array($this, 'loadCustomPostTypes')
258
+		);
259
+		// load EE_Config, EE_Textdomain, etc
260
+		add_action(
261
+			'AHEE__EE_Bootstrap__register_shortcodes_modules_and_widgets',
262
+			[$this, 'register_shortcodes_modules_and_widgets'],
263
+			7
264
+		);
265
+		// you wanna get going? I wanna get going... let's get going!
266
+		add_action(
267
+			'AHEE__EE_Bootstrap__brew_espresso',
268
+			[$this, 'brew_espresso'],
269
+			9
270
+		);
271
+		// other housekeeping
272
+		// exclude EE critical pages from wp_list_pages
273
+		add_filter(
274
+			'wp_list_pages_excludes',
275
+			[$this, 'remove_pages_from_wp_list_pages'],
276
+			10
277
+		);
278
+		// ALL EE Addons should use the following hook point to attach their initial setup too
279
+		// it's extremely important for EE Addons to register any class autoloaders so that they can be available when the EE_Config loads
280
+		do_action('AHEE__EE_System__construct__complete', $this);
281
+	}
282
+
283
+
284
+	/**
285
+	 * load and setup EE_Capabilities
286
+	 *
287
+	 * @return void
288
+	 */
289
+	public function loadCapabilities()
290
+	{
291
+		$this->capabilities = $this->loader->getShared('EE_Capabilities');
292
+		add_action(
293
+			'AHEE__EE_Capabilities__init_caps__before_initialization',
294
+			function () {
295
+				LoaderFactory::getLoader()->getShared('EE_Payment_Method_Manager');
296
+			}
297
+		);
298
+	}
299
+
300
+
301
+	/**
302
+	 * create and cache the CommandBus, and also add middleware
303
+	 * The CapChecker middleware requires the use of EE_Capabilities
304
+	 * which is why we need to load the CommandBus after Caps are set up
305
+	 * CommandBus middleware operate FIFO - First In First Out
306
+	 * so LocateMovedCommands will run first in order to return any new commands
307
+	 *
308
+	 * @return void
309
+	 */
310
+	public function loadCommandBus()
311
+	{
312
+		$this->loader->getShared(
313
+			'CommandBusInterface',
314
+			[
315
+				null,
316
+				apply_filters(
317
+					'FHEE__EE_Load_Espresso_Core__handle_request__CommandBus_middleware',
318
+					[
319
+						$this->loader->getShared('EventEspresso\core\services\commands\middleware\LocateMovedCommands'),
320
+						$this->loader->getShared('EventEspresso\core\services\commands\middleware\CapChecker'),
321
+						$this->loader->getShared('EventEspresso\core\services\commands\middleware\AddActionHook'),
322
+					]
323
+				),
324
+			]
325
+		);
326
+	}
327
+
328
+
329
+	/**
330
+	 * @return void
331
+	 * @throws Exception
332
+	 */
333
+	public function loadPluginApi()
334
+	{
335
+		$this->addon_manager = $this->loader->getShared(AddonManager::class);
336
+		$this->addon_manager->initialize();
337
+		$this->loader->getShared('EE_Request_Handler');
338
+	}
339
+
340
+
341
+	/**
342
+	 * load_espresso_addons
343
+	 * allow addons to load first so that they can set hooks for running DMS's, etc
344
+	 * this is hooked into both:
345
+	 *    'AHEE__EE_Bootstrap__load_core_configuration'
346
+	 *        which runs during the WP 'plugins_loaded' action at priority 5
347
+	 *    and the WP 'activate_plugin' hook point
348
+	 *
349
+	 * @return void
350
+	 * @throws Exception
351
+	 */
352
+	public function load_espresso_addons()
353
+	{
354
+		// looking for hooks? they've been moved into the AddonManager to maintain compatibility
355
+		$this->addon_manager->loadAddons();
356
+	}
357
+
358
+
359
+	/**
360
+	 * detect_activations_or_upgrades
361
+	 * Checks for activation or upgrade of core first;
362
+	 * then also checks if any registered addons have been activated or upgraded
363
+	 * This is hooked into 'AHEE__EE_Bootstrap__detect_activations_or_upgrades'
364
+	 * which runs during the WP 'plugins_loaded' action at priority 3
365
+	 *
366
+	 * @access public
367
+	 * @return void
368
+	 */
369
+	public function detect_activations_or_upgrades()
370
+	{
371
+		// first off: let's make sure to handle core
372
+		$this->detect_if_activation_or_upgrade();
373
+		foreach ($this->registry->addons as $addon) {
374
+			if ($addon instanceof EE_Addon) {
375
+				// detect teh request type for that addon
376
+				$addon->detect_req_type();
377
+			}
378
+		}
379
+	}
380
+
381
+
382
+	/**
383
+	 * detect_if_activation_or_upgrade
384
+	 * Takes care of detecting whether this is a brand new install or code upgrade,
385
+	 * and either setting up the DB or setting up maintenance mode etc.
386
+	 *
387
+	 * @access public
388
+	 * @return void
389
+	 */
390
+	public function detect_if_activation_or_upgrade()
391
+	{
392
+		do_action('AHEE__EE_System___detect_if_activation_or_upgrade__begin');
393
+		// check if db has been updated, or if its a brand-new installation
394
+		$espresso_db_update = $this->fix_espresso_db_upgrade_option();
395
+		$request_type       = $this->detect_req_type($espresso_db_update);
396
+		// EEH_Debug_Tools::printr( $request_type, '$request_type', __FILE__, __LINE__ );
397
+		switch ($request_type) {
398
+			case EE_System::req_type_new_activation:
399
+				do_action('AHEE__EE_System__detect_if_activation_or_upgrade__new_activation');
400
+				$this->_handle_core_version_change($espresso_db_update);
401
+				break;
402
+			case EE_System::req_type_reactivation:
403
+				do_action('AHEE__EE_System__detect_if_activation_or_upgrade__reactivation');
404
+				$this->_handle_core_version_change($espresso_db_update);
405
+				break;
406
+			case EE_System::req_type_upgrade:
407
+				do_action('AHEE__EE_System__detect_if_activation_or_upgrade__upgrade');
408
+				// migrations may be required now that we've upgraded
409
+				$this->maintenance_mode->set_maintenance_mode_if_db_old();
410
+				$this->_handle_core_version_change($espresso_db_update);
411
+				break;
412
+			case EE_System::req_type_downgrade:
413
+				do_action('AHEE__EE_System__detect_if_activation_or_upgrade__downgrade');
414
+				// its possible migrations are no longer required
415
+				$this->maintenance_mode->set_maintenance_mode_if_db_old();
416
+				$this->_handle_core_version_change($espresso_db_update);
417
+				break;
418
+			case EE_System::req_type_normal:
419
+			default:
420
+				break;
421
+		}
422
+		do_action('AHEE__EE_System__detect_if_activation_or_upgrade__complete');
423
+	}
424
+
425
+
426
+	/**
427
+	 * Updates the list of installed versions and sets hooks for
428
+	 * initializing the database later during the request
429
+	 *
430
+	 * @param array $espresso_db_update
431
+	 */
432
+	private function _handle_core_version_change(array $espresso_db_update)
433
+	{
434
+		$this->update_list_of_installed_versions($espresso_db_update);
435
+		// get ready to verify the DB is ok (provided we aren't in maintenance mode, of course)
436
+		add_action(
437
+			'AHEE__EE_System__perform_activations_upgrades_and_migrations',
438
+			[$this, 'initialize_db_if_no_migrations_required']
439
+		);
440
+	}
441
+
442
+
443
+	/**
444
+	 * standardizes the wp option 'espresso_db_upgrade' which actually stores
445
+	 * information about what versions of EE have been installed and activated,
446
+	 * NOT necessarily the state of the database
447
+	 *
448
+	 * @param mixed $espresso_db_update           the value of the WordPress option.
449
+	 *                                            If not supplied, fetches it from the options table
450
+	 * @return array the correct value of 'espresso_db_upgrade', after saving it, if it needed correction
451
+	 */
452
+	private function fix_espresso_db_upgrade_option($espresso_db_update = null): array
453
+	{
454
+		do_action('FHEE__EE_System__manage_fix_espresso_db_upgrade_option__begin', $espresso_db_update);
455
+		if (! $espresso_db_update) {
456
+			$espresso_db_update = get_option('espresso_db_update');
457
+		}
458
+		// check that option is an array
459
+		if (! is_array($espresso_db_update)) {
460
+			// if option is FALSE, then it never existed
461
+			if ($espresso_db_update === false) {
462
+				// make $espresso_db_update an array and save option with autoload OFF
463
+				$espresso_db_update = [];
464
+				add_option('espresso_db_update', $espresso_db_update, '', 'no');
465
+			} else {
466
+				// option is NOT FALSE but also is NOT an array, so make it an array and save it
467
+				$espresso_db_update = [$espresso_db_update => []];
468
+				update_option('espresso_db_update', $espresso_db_update);
469
+			}
470
+		} else {
471
+			$corrected_db_update = [];
472
+			// if IS an array, but is it an array where KEYS are version numbers, and values are arrays?
473
+			foreach ($espresso_db_update as $should_be_version_string => $should_be_array) {
474
+				if (is_int($should_be_version_string) && ! is_array($should_be_array)) {
475
+					// the key is an int, and the value IS NOT an array
476
+					// so it must be numerically-indexed, where values are versions installed...
477
+					// fix it!
478
+					$version_string                         = $should_be_array;
479
+					$corrected_db_update[ $version_string ] = ['unknown-date'];
480
+				} else {
481
+					// ok it checks out
482
+					$corrected_db_update[ $should_be_version_string ] = $should_be_array;
483
+				}
484
+			}
485
+			$espresso_db_update = $corrected_db_update;
486
+			update_option('espresso_db_update', $espresso_db_update);
487
+		}
488
+		do_action('FHEE__EE_System__manage_fix_espresso_db_upgrade_option__complete', $espresso_db_update);
489
+		return ! empty($espresso_db_update) ? $espresso_db_update : [];
490
+	}
491
+
492
+
493
+	/**
494
+	 * Does the traditional work of setting up the plugin's database and adding default data.
495
+	 * If migration script/process did not exist, this is what would happen on every activation/reactivation/upgrade.
496
+	 * NOTE: if we're in maintenance mode (which would be the case if we detect there are data
497
+	 * migration scripts that need to be run and a version change happens), enqueues core for database initialization,
498
+	 * so that it will be done when migrations are finished
499
+	 *
500
+	 * @param boolean $initialize_addons_too if true, we double-check addons' database tables etc too;
501
+	 * @param boolean $verify_schema         if true will re-check the database tables have the correct schema.
502
+	 *                                       This is a resource-intensive job
503
+	 *                                       so we prefer to only do it when necessary
504
+	 * @return void
505
+	 * @throws EE_Error
506
+	 * @throws ReflectionException
507
+	 */
508
+	public function initialize_db_if_no_migrations_required($initialize_addons_too = false, $verify_schema = true)
509
+	{
510
+		$request_type = $this->detect_req_type();
511
+		// only initialize system if we're not in maintenance mode.
512
+		if ($this->maintenance_mode->level() !== EE_Maintenance_Mode::level_2_complete_maintenance) {
513
+			/** @var EventEspresso\core\domain\services\custom_post_types\RewriteRules $rewrite_rules */
514
+			$rewrite_rules = $this->loader->getShared(
515
+				'EventEspresso\core\domain\services\custom_post_types\RewriteRules'
516
+			);
517
+			$rewrite_rules->flush();
518
+			if ($verify_schema) {
519
+				EEH_Activation::initialize_db_and_folders();
520
+			}
521
+			EEH_Activation::initialize_db_content();
522
+			EEH_Activation::system_initialization();
523
+			if ($initialize_addons_too) {
524
+				$this->initialize_addons();
525
+			}
526
+		} else {
527
+			EE_Data_Migration_Manager::instance()->enqueue_db_initialization_for('Core');
528
+		}
529
+		if (
530
+			$request_type === EE_System::req_type_new_activation
531
+			|| $request_type === EE_System::req_type_reactivation
532
+			|| (
533
+				$request_type === EE_System::req_type_upgrade
534
+				&& $this->is_major_version_change()
535
+			)
536
+		) {
537
+			add_action('AHEE__EE_System__initialize_last', [$this, 'redirect_to_about_ee'], 9);
538
+		}
539
+	}
540
+
541
+
542
+	/**
543
+	 * Initializes the db for all registered addons
544
+	 *
545
+	 * @throws EE_Error
546
+	 * @throws ReflectionException
547
+	 */
548
+	public function initialize_addons()
549
+	{
550
+		// foreach registered addon, make sure its db is up-to-date too
551
+		foreach ($this->registry->addons as $addon) {
552
+			if ($addon instanceof EE_Addon) {
553
+				$addon->initialize_db_if_no_migrations_required();
554
+			}
555
+		}
556
+	}
557
+
558
+
559
+	/**
560
+	 * Adds the current code version to the saved wp option which stores a list of all ee versions ever installed.
561
+	 *
562
+	 * @param array  $version_history
563
+	 * @param string $current_version_to_add version to be added to the version history
564
+	 * @return    boolean success as to whether or not this option was changed
565
+	 */
566
+	public function update_list_of_installed_versions($version_history = null, $current_version_to_add = null): bool
567
+	{
568
+		if (! $version_history) {
569
+			$version_history = $this->fix_espresso_db_upgrade_option($version_history);
570
+		}
571
+		if ($current_version_to_add === null) {
572
+			$current_version_to_add = espresso_version();
573
+		}
574
+		$version_history[ $current_version_to_add ][] = date('Y-m-d H:i:s', time());
575
+		// re-save
576
+		return update_option('espresso_db_update', $version_history);
577
+	}
578
+
579
+
580
+	/**
581
+	 * Detects if the current version indicated in the has existed in the list of
582
+	 * previously-installed versions of EE (espresso_db_update). Does NOT modify it (ie, no side-effect)
583
+	 *
584
+	 * @param array $espresso_db_update array from the wp option stored under the name 'espresso_db_update'.
585
+	 *                                  If not supplied, fetches it from the options table.
586
+	 *                                  Also, caches its result so later parts of the code can also know whether
587
+	 *                                  there's been an update or not. This way we can add the current version to
588
+	 *                                  espresso_db_update, but still know if this is a new install or not
589
+	 * @return int one of the constants on EE_System::req_type_
590
+	 */
591
+	public function detect_req_type($espresso_db_update = null): int
592
+	{
593
+		if ($this->_req_type === null) {
594
+			$espresso_db_update          = ! empty($espresso_db_update)
595
+				? $espresso_db_update
596
+				: $this->fix_espresso_db_upgrade_option();
597
+			$this->_req_type             = EE_System::detect_req_type_given_activation_history(
598
+				$espresso_db_update,
599
+				'ee_espresso_activation',
600
+				espresso_version()
601
+			);
602
+			$this->_major_version_change = $this->_detect_major_version_change($espresso_db_update);
603
+			$this->request->setIsActivation($this->_req_type !== EE_System::req_type_normal);
604
+		}
605
+		return $this->_req_type;
606
+	}
607
+
608
+
609
+	/**
610
+	 * Returns whether or not there was a non-micro version change (ie, change in either
611
+	 * the first or second number in the version. Eg 4.9.0.rc.001 to 4.10.0.rc.000,
612
+	 * but not 4.9.0.rc.0001 to 4.9.1.rc.0001
613
+	 *
614
+	 * @param $activation_history
615
+	 * @return bool
616
+	 */
617
+	private function _detect_major_version_change($activation_history): bool
618
+	{
619
+		$previous_version       = EE_System::getMostRecentlyActiveVersion($activation_history);
620
+		$previous_version_parts = explode('.', $previous_version);
621
+		$current_version_parts  = explode('.', espresso_version());
622
+		return isset(
623
+			$previous_version_parts[0],
624
+			$previous_version_parts[1],
625
+			$current_version_parts[0],
626
+			$current_version_parts[1]
627
+		) && (
628
+			$previous_version_parts[0] !== $current_version_parts[0]
629
+			|| $previous_version_parts[1] !== $current_version_parts[1]
630
+		);
631
+	}
632
+
633
+
634
+	/**
635
+	 * Returns true if either the major or minor version of EE changed during this request.
636
+	 * Eg 4.9.0.rc.001 to 4.10.0.rc.000, but not 4.9.0.rc.0001 to 4.9.1.rc.0001
637
+	 *
638
+	 * @return bool
639
+	 */
640
+	public function is_major_version_change(): bool
641
+	{
642
+		return $this->_major_version_change;
643
+	}
644
+
645
+
646
+	/**
647
+	 * Determines the request type for any ee addon, given three piece of info: the current array of activation
648
+	 * histories (for core that' 'espresso_db_update' wp option); the name of the WordPress option which is temporarily
649
+	 * set upon activation of the plugin (for core it's 'ee_espresso_activation'); and the version that this plugin was
650
+	 * just activated to (for core that will always be espresso_version())
651
+	 *
652
+	 * @param array|null $activation_history             the option's value which stores the activation history for
653
+	 *                                                 this
654
+	 *                                                 ee plugin. for core that's 'espresso_db_update'
655
+	 * @param string $activation_indicator_option_name the name of the WordPress option that is temporarily set to
656
+	 *                                                 indicate that this plugin was just activated
657
+	 * @param string $current_version                  the version that was just upgraded to (for core that will be
658
+	 *                                                 espresso_version())
659
+	 * @return int one of the constants on EE_System::req_type_
660
+	 */
661
+	public static function detect_req_type_given_activation_history(
662
+		array $activation_history,
663
+		string $activation_indicator_option_name,
664
+		string $current_version
665
+	): int {
666
+		$version_change = self::compareVersionWithPrevious($activation_history, $current_version);
667
+		$is_activation  = get_option($activation_indicator_option_name, false);
668
+		$req_type       = self::getRequestType($activation_history, $version_change, $is_activation);
669
+		if ($is_activation) {
670
+			// cleanup in aisle 6
671
+			delete_option($activation_indicator_option_name);
672
+		}
673
+		return $req_type;
674
+	}
675
+
676
+
677
+	/**
678
+	 * @param array  $activation_history
679
+	 * @param int    $version_change
680
+	 * @param bool   $is_activation
681
+	 * @return int
682
+	 * @since $VID:$
683
+	 */
684
+	private static function getRequestType(array $activation_history, int $version_change, bool $is_activation): int
685
+	{
686
+		// if no previous activation history exists, then this is a brand new install
687
+		if (empty($activation_history)) {
688
+			return EE_System::req_type_new_activation;
689
+		}
690
+		// current version is higher than previous version, so it's an upgrade
691
+		if ($version_change === 1) {
692
+			return EE_System::req_type_upgrade;
693
+		}
694
+		// current version is lower than previous version, so it's a downgrade
695
+		if ($version_change === -1) {
696
+			return EE_System::req_type_downgrade;
697
+		}
698
+		// version hasn't changed since last version so check if the activation indicator is set
699
+		// to determine if it's a reactivation, or just a normal request
700
+		return $is_activation
701
+			? EE_System::req_type_reactivation
702
+			: EE_System::req_type_normal;
703
+	}
704
+
705
+
706
+	/**
707
+	 * Detects if the $version_to_upgrade_to is higher than the most recent version in
708
+	 * the $activation_history_for_addon
709
+	 *
710
+	 * @param array  $activation_history    array where keys are versions,
711
+	 *                                      values are arrays of times activated (sometimes 'unknown-date')
712
+	 * @param string $current_version
713
+	 * @return int results of version_compare( $version_to_upgrade_to, $most_recently_active_version ).
714
+	 *                                      -1 if $version_to_upgrade_to is LOWER (downgrade);
715
+	 *                                      0 if $version_to_upgrade_to MATCHES (reactivation or normal request);
716
+	 *                                      1 if $version_to_upgrade_to is HIGHER (upgrade) ;
717
+	 */
718
+	private static function compareVersionWithPrevious(array $activation_history, string $current_version): int
719
+	{
720
+		// find the most recently-activated version
721
+		$most_recently_active_version = EE_System::getMostRecentlyActiveVersion($activation_history);
722
+		return version_compare($current_version, $most_recently_active_version);
723
+	}
724
+
725
+
726
+	/**
727
+	 * Gets the most recently active version listed in the activation history,
728
+	 * and if none are found (ie, it's a brand new install) returns '0.0.0.dev.000'.
729
+	 *
730
+	 * @param array $activation_history  (keys are versions, values are arrays of times activated,
731
+	 *                                   sometimes containing 'unknown-date'
732
+	 * @return string
733
+	 */
734
+	private static function getMostRecentlyActiveVersion(array $activation_history): string
735
+	{
736
+		$most_recent_activation_date  = '1970-01-01 00:00:00';
737
+		$most_recently_active_version = '0.0.0.dev.000';
738
+		if (is_array($activation_history)) {
739
+			foreach ($activation_history as $version => $activation_dates) {
740
+				// check there is a record of when this version was activated.
741
+				// Otherwise, mark it as unknown
742
+				if (! $activation_dates) {
743
+					$activation_dates = ['unknown-date'];
744
+				}
745
+				$activation_dates = is_string($activation_dates) ? [$activation_dates] : $activation_dates;
746
+				foreach ($activation_dates as $activation_date) {
747
+					if ($activation_date !== 'unknown-date' && $activation_date > $most_recent_activation_date) {
748
+						$most_recently_active_version = $version;
749
+						$most_recent_activation_date  = $activation_date;
750
+					}
751
+				}
752
+			}
753
+		}
754
+		return $most_recently_active_version;
755
+	}
756
+
757
+
758
+	/**
759
+	 * This redirects to the about EE page after activation
760
+	 *
761
+	 * @return void
762
+	 */
763
+	public function redirect_to_about_ee()
764
+	{
765
+		$notices = EE_Error::get_notices(false);
766
+		// if current user is an admin and it's not an ajax or rest request
767
+		if (
768
+			! isset($notices['errors'])
769
+			&& $this->request->isAdmin()
770
+			&& apply_filters(
771
+				'FHEE__EE_System__redirect_to_about_ee__do_redirect',
772
+				$this->capabilities->current_user_can('manage_options', 'espresso_about_default')
773
+			)
774
+		) {
775
+			$query_params = ['page' => 'espresso_about'];
776
+			if (EE_System::instance()->detect_req_type() === EE_System::req_type_new_activation) {
777
+				$query_params['new_activation'] = true;
778
+			}
779
+			if (EE_System::instance()->detect_req_type() === EE_System::req_type_reactivation) {
780
+				$query_params['reactivation'] = true;
781
+			}
782
+			$url = add_query_arg($query_params, admin_url('admin.php'));
783
+			EEH_URL::safeRedirectAndExit($url);
784
+		}
785
+	}
786
+
787
+
788
+	/**
789
+	 * load_core_configuration
790
+	 * this is hooked into 'AHEE__EE_Bootstrap__load_core_configuration'
791
+	 * which runs during the WP 'plugins_loaded' action at priority 5
792
+	 *
793
+	 * @return void
794
+	 * @throws ReflectionException
795
+	 * @throws Exception
796
+	 */
797
+	public function load_core_configuration()
798
+	{
799
+		do_action('AHEE__EE_System__load_core_configuration__begin', $this);
800
+		$this->loader->getShared('EE_Load_Textdomain');
801
+		// load textdomain
802
+		EE_Load_Textdomain::load_textdomain();
803
+		// load caf stuff a chance to play during the activation process too.
804
+		$this->_maybe_brew_regular();
805
+		// load and setup EE_Config and EE_Network_Config
806
+		$config = $this->loader->getShared('EE_Config');
807
+		$this->loader->getShared('EE_Network_Config');
808
+		// setup autoloaders
809
+		// enable logging?
810
+		$this->loader->getShared('EventEspresso\core\services\orm\TrashLogger');
811
+		if ($config->admin->use_remote_logging) {
812
+			$this->loader->getShared('EE_Log');
813
+		}
814
+		// check for activation errors
815
+		$activation_errors = get_option('ee_plugin_activation_errors', false);
816
+		if ($activation_errors) {
817
+			EE_Error::add_error($activation_errors, __FILE__, __FUNCTION__, __LINE__);
818
+			update_option('ee_plugin_activation_errors', false);
819
+		}
820
+		// get model names
821
+		$this->_parse_model_names();
822
+		// configure custom post type definitions
823
+		$this->loader->getShared('EventEspresso\core\domain\entities\custom_post_types\CustomTaxonomyDefinitions');
824
+		$this->loader->getShared('EventEspresso\core\domain\entities\custom_post_types\CustomPostTypeDefinitions');
825
+		do_action('AHEE__EE_System__load_core_configuration__complete', $this);
826
+	}
827
+
828
+
829
+	/**
830
+	 * cycles through all of the models/*.model.php files, and assembles an array of model names
831
+	 *
832
+	 * @return void
833
+	 * @throws ReflectionException
834
+	 */
835
+	private function _parse_model_names()
836
+	{
837
+		// get all the files in the EE_MODELS folder that end in .model.php
838
+		$models                 = glob(EE_MODELS . '*.model.php');
839
+		$model_names            = [];
840
+		$non_abstract_db_models = [];
841
+		foreach ($models as $model) {
842
+			// get model classname
843
+			$classname       = EEH_File::get_classname_from_filepath_with_standard_filename($model);
844
+			$short_name      = str_replace('EEM_', '', $classname);
845
+			$reflectionClass = new ReflectionClass($classname);
846
+			if ($reflectionClass->isSubclassOf('EEM_Base') && ! $reflectionClass->isAbstract()) {
847
+				$non_abstract_db_models[ $short_name ] = $classname;
848
+			}
849
+			$model_names[ $short_name ] = $classname;
850
+		}
851
+		$this->registry->models                 = apply_filters('FHEE__EE_System__parse_model_names', $model_names);
852
+		$this->registry->non_abstract_db_models = apply_filters(
853
+			'FHEE__EE_System__parse_implemented_model_names',
854
+			$non_abstract_db_models
855
+		);
856
+	}
857
+
858
+
859
+	/**
860
+	 * The purpose of this method is to simply check for a file named "caffeinated/brewing_regular.php" for any hooks
861
+	 * that need to be setup before our EE_System launches.
862
+	 *
863
+	 * @return void
864
+	 * @throws DomainException
865
+	 * @throws InvalidArgumentException
866
+	 * @throws InvalidDataTypeException
867
+	 * @throws InvalidInterfaceException
868
+	 * @throws InvalidClassException
869
+	 * @throws InvalidFilePathException
870
+	 */
871
+	private function _maybe_brew_regular()
872
+	{
873
+		/** @var Domain $domain */
874
+		$domain = DomainFactory::getEventEspressoCoreDomain();
875
+		if ($domain->isCaffeinated()) {
876
+			require_once EE_CAFF_PATH . 'brewing_regular.php';
877
+		}
878
+	}
879
+
880
+
881
+	/**
882
+	 * @throws Exception
883
+	 * @since 4.9.71.p
884
+	 */
885
+	public function loadRouteMatchSpecifications()
886
+	{
887
+		try {
888
+			$this->loader->getShared('EventEspresso\core\services\routing\RouteMatchSpecificationManager');
889
+			$this->loader->getShared('EventEspresso\core\services\routing\RouteCollection');
890
+			$this->router->loadPrimaryRoutes();
891
+		} catch (Exception $exception) {
892
+			new ExceptionStackTraceDisplay($exception);
893
+		}
894
+		do_action('AHEE__EE_System__loadRouteMatchSpecifications');
895
+	}
896
+
897
+
898
+	/**
899
+	 * loading CPT related classes earlier so that their definitions are available
900
+	 * but not performing any actual registration with WP core until load_CPTs_and_session() is called
901
+	 *
902
+	 * @since   4.10.21.p
903
+	 */
904
+	public function loadCustomPostTypes()
905
+	{
906
+		$this->register_custom_taxonomies = $this->loader->getShared(
907
+			'EventEspresso\core\domain\services\custom_post_types\RegisterCustomTaxonomies'
908
+		);
909
+		$this->register_custom_post_types = $this->loader->getShared(
910
+			'EventEspresso\core\domain\services\custom_post_types\RegisterCustomPostTypes'
911
+		);
912
+		$this->register_custom_taxonomy_terms = $this->loader->getShared(
913
+			'EventEspresso\core\domain\services\custom_post_types\RegisterCustomTaxonomyTerms'
914
+		);
915
+		// integrate WP_Query with the EE models
916
+		$this->loader->getShared('EE_CPT_Strategy');
917
+		// load legacy EE_Request_Handler in case add-ons still need it
918
+		$this->loader->getShared('EE_Request_Handler');
919
+	}
920
+
921
+
922
+	/**
923
+	 * register_shortcodes_modules_and_widgets
924
+	 * generate lists of shortcodes and modules, then verify paths and classes
925
+	 * This is hooked into 'AHEE__EE_Bootstrap__register_shortcodes_modules_and_widgets'
926
+	 * which runs during the WP 'plugins_loaded' action at priority 7
927
+	 *
928
+	 * @access public
929
+	 * @return void
930
+	 * @throws Exception
931
+	 */
932
+	public function register_shortcodes_modules_and_widgets()
933
+	{
934
+		$this->router->registerShortcodesModulesAndWidgets();
935
+		do_action('AHEE__EE_System__register_shortcodes_modules_and_widgets');
936
+		// check for addons using old hook point
937
+		if (has_action('AHEE__EE_System__register_shortcodes_modules_and_addons')) {
938
+			$this->_incompatible_addon_error();
939
+		}
940
+	}
941
+
942
+
943
+	/**
944
+	 * _incompatible_addon_error
945
+	 *
946
+	 * @access public
947
+	 * @return void
948
+	 */
949
+	private function _incompatible_addon_error()
950
+	{
951
+		// get array of classes hooking into here
952
+		$class_names = EEH_Class_Tools::get_class_names_for_all_callbacks_on_hook(
953
+			'AHEE__EE_System__register_shortcodes_modules_and_addons'
954
+		);
955
+		if (! empty($class_names)) {
956
+			$msg = esc_html__(
957
+				'The following plugins, addons, or modules appear to be incompatible with this version of Event Espresso and were automatically deactivated to avoid fatal errors:',
958
+				'event_espresso'
959
+			);
960
+			$msg .= '<ul>';
961
+			foreach ($class_names as $class_name) {
962
+				$msg .= '<li><b>Event Espresso - '
963
+						. str_replace(
964
+							['EE_', 'EEM_', 'EED_', 'EES_', 'EEW_'],
965
+							'',
966
+							$class_name
967
+						) . '</b></li>';
968
+			}
969
+			$msg .= '</ul>';
970
+			$msg .= esc_html__(
971
+				'Compatibility issues can be avoided and/or resolved by keeping addons and plugins updated to the latest version.',
972
+				'event_espresso'
973
+			);
974
+			// save list of incompatible addons to wp-options for later use
975
+			add_option('ee_incompatible_addons', $class_names, '', 'no');
976
+			if (is_admin()) {
977
+				EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
978
+			}
979
+		}
980
+	}
981
+
982
+
983
+	/**
984
+	 * brew_espresso
985
+	 * begins the process of setting hooks for initializing EE in the correct order
986
+	 * This is happening on the 'AHEE__EE_Bootstrap__brew_espresso' hook point
987
+	 * which runs during the WP 'plugins_loaded' action at priority 9
988
+	 *
989
+	 * @return void
990
+	 * @throws Exception
991
+	 */
992
+	public function brew_espresso()
993
+	{
994
+		do_action('AHEE__EE_System__brew_espresso__begin', $this);
995
+		// load some final core systems
996
+		add_action('init', [$this, 'set_hooks_for_core'], 1);
997
+		add_action('init', [$this, 'perform_activations_upgrades_and_migrations'], 3);
998
+		add_action('init', [$this, 'load_CPTs_and_session'], 5);
999
+		add_action('init', [$this, 'load_controllers'], 7);
1000
+		add_action('init', [$this, 'core_loaded_and_ready'], 9);
1001
+		add_action('init', [$this, 'initialize'], 10);
1002
+		add_action('init', [$this, 'initialize_last'], 100);
1003
+		$this->router->brewEspresso();
1004
+		do_action('AHEE__EE_System__brew_espresso__complete', $this);
1005
+	}
1006
+
1007
+
1008
+	/**
1009
+	 *    set_hooks_for_core
1010
+	 *
1011
+	 * @access public
1012
+	 * @return    void
1013
+	 * @throws EE_Error
1014
+	 */
1015
+	public function set_hooks_for_core()
1016
+	{
1017
+		$this->_deactivate_incompatible_addons();
1018
+		do_action('AHEE__EE_System__set_hooks_for_core');
1019
+		$this->loader->getShared('EventEspresso\core\domain\values\session\SessionLifespan');
1020
+		// caps need to be initialized on every request so that capability maps are set.
1021
+		// @see https://events.codebasehq.com/projects/event-espresso/tickets/8674
1022
+		$this->registry->CAP->init_caps();
1023
+	}
1024
+
1025
+
1026
+	/**
1027
+	 * Using the information gathered in EE_System::_incompatible_addon_error,
1028
+	 * deactivates any addons considered incompatible with the current version of EE
1029
+	 */
1030
+	private function _deactivate_incompatible_addons()
1031
+	{
1032
+		$incompatible_addons = get_option('ee_incompatible_addons', []);
1033
+		if (! empty($incompatible_addons)) {
1034
+			$active_plugins = get_option('active_plugins', []);
1035
+			foreach ($active_plugins as $active_plugin) {
1036
+				foreach ($incompatible_addons as $incompatible_addon) {
1037
+					if (strpos($active_plugin, $incompatible_addon) !== false) {
1038
+						$this->request->unSetRequestParams(['activate'], true);
1039
+						espresso_deactivate_plugin($active_plugin);
1040
+					}
1041
+				}
1042
+			}
1043
+		}
1044
+	}
1045
+
1046
+
1047
+	/**
1048
+	 *    perform_activations_upgrades_and_migrations
1049
+	 *
1050
+	 * @access public
1051
+	 * @return    void
1052
+	 */
1053
+	public function perform_activations_upgrades_and_migrations()
1054
+	{
1055
+		do_action('AHEE__EE_System__perform_activations_upgrades_and_migrations');
1056
+	}
1057
+
1058
+
1059
+	/**
1060
+	 * @return void
1061
+	 * @throws DomainException
1062
+	 */
1063
+	public function load_CPTs_and_session()
1064
+	{
1065
+		do_action('AHEE__EE_System__load_CPTs_and_session__start');
1066
+		$this->register_custom_taxonomies->registerCustomTaxonomies();
1067
+		$this->register_custom_post_types->registerCustomPostTypes();
1068
+		$this->register_custom_taxonomy_terms->registerCustomTaxonomyTerms();
1069
+		// load legacy Custom Post Types and Taxonomies
1070
+		$this->loader->getShared('EE_Register_CPTs');
1071
+		do_action('AHEE__EE_System__load_CPTs_and_session__complete');
1072
+	}
1073
+
1074
+
1075
+	/**
1076
+	 * load_controllers
1077
+	 * this is the best place to load any additional controllers that needs access to EE core.
1078
+	 * it is expected that all basic core EE systems, that are not dependant on the current request are loaded at this
1079
+	 * time
1080
+	 *
1081
+	 * @access public
1082
+	 * @return void
1083
+	 * @throws Exception
1084
+	 */
1085
+	public function load_controllers()
1086
+	{
1087
+		do_action('AHEE__EE_System__load_controllers__start');
1088
+		$this->router->loadControllers();
1089
+		do_action('AHEE__EE_System__load_controllers__complete');
1090
+	}
1091
+
1092
+
1093
+	/**
1094
+	 * core_loaded_and_ready
1095
+	 * all of the basic EE core should be loaded at this point and available regardless of M-Mode
1096
+	 *
1097
+	 * @access public
1098
+	 * @return void
1099
+	 * @throws Exception
1100
+	 */
1101
+	public function core_loaded_and_ready()
1102
+	{
1103
+		$this->router->coreLoadedAndReady();
1104
+		do_action('AHEE__EE_System__core_loaded_and_ready');
1105
+		// always load template tags, because it's faster than checking if it's a front-end request, and many page
1106
+		// builders require these even on the front-end
1107
+		require_once EE_PUBLIC . 'template_tags.php';
1108
+		do_action('AHEE__EE_System__set_hooks_for_shortcodes_modules_and_addons');
1109
+	}
1110
+
1111
+
1112
+	/**
1113
+	 * initialize
1114
+	 * this is the best place to begin initializing client code
1115
+	 *
1116
+	 * @access public
1117
+	 * @return void
1118
+	 */
1119
+	public function initialize()
1120
+	{
1121
+		do_action('AHEE__EE_System__initialize');
1122
+		add_filter(
1123
+			'safe_style_css',
1124
+			function ($styles) {
1125
+				$styles[] = 'display';
1126
+				$styles[] = 'visibility';
1127
+				$styles[] = 'position';
1128
+				$styles[] = 'top';
1129
+				$styles[] = 'right';
1130
+				$styles[] = 'bottom';
1131
+				$styles[] = 'left';
1132
+				$styles[] = 'resize';
1133
+				$styles[] = 'max-width';
1134
+				$styles[] = 'max-height';
1135
+				return $styles;
1136
+			}
1137
+		);
1138
+	}
1139
+
1140
+
1141
+	/**
1142
+	 * initialize_last
1143
+	 * this is run really late during the WP init hook point, and ensures that mostly everything else that needs to
1144
+	 * initialize has done so
1145
+	 *
1146
+	 * @access public
1147
+	 * @return void
1148
+	 * @throws Exception
1149
+	 */
1150
+	public function initialize_last()
1151
+	{
1152
+		$this->router->initializeLast();
1153
+		do_action('AHEE__EE_System__initialize_last');
1154
+		/** @var EventEspresso\core\domain\services\custom_post_types\RewriteRules $rewrite_rules */
1155
+		$rewrite_rules = $this->loader->getShared(
1156
+			'EventEspresso\core\domain\services\custom_post_types\RewriteRules'
1157
+		);
1158
+		$rewrite_rules->flushRewriteRules();
1159
+		add_action('admin_bar_init', [$this, 'addEspressoToolbar']);
1160
+	}
1161
+
1162
+
1163
+	/**
1164
+	 * @return void
1165
+	 */
1166
+	public function addEspressoToolbar()
1167
+	{
1168
+		$this->loader->getShared(
1169
+			'EventEspresso\core\domain\services\admin\AdminToolBar',
1170
+			[$this->registry->CAP]
1171
+		);
1172
+	}
1173
+
1174
+
1175
+	/**
1176
+	 * do_not_cache
1177
+	 * sets no cache headers and defines no cache constants for WP plugins
1178
+	 *
1179
+	 * @access public
1180
+	 * @return void
1181
+	 */
1182
+	public static function do_not_cache()
1183
+	{
1184
+		// set no cache constants
1185
+		if (! defined('DONOTCACHEPAGE')) {
1186
+			define('DONOTCACHEPAGE', true);
1187
+		}
1188
+		if (! defined('DONOTCACHCEOBJECT')) {
1189
+			define('DONOTCACHCEOBJECT', true);
1190
+		}
1191
+		if (! defined('DONOTCACHEDB')) {
1192
+			define('DONOTCACHEDB', true);
1193
+		}
1194
+		// add no cache headers
1195
+		add_action('send_headers', ['EE_System', 'nocache_headers'], 10);
1196
+		// plus a little extra for nginx and Google Chrome
1197
+		add_filter('nocache_headers', ['EE_System', 'extra_nocache_headers'], 10, 1);
1198
+		// prevent browsers from prefetching of the rel='next' link, because it may contain content that interferes with the registration process
1199
+		remove_action('wp_head', 'adjacent_posts_rel_link_wp_head');
1200
+	}
1201
+
1202
+
1203
+	/**
1204
+	 *    extra_nocache_headers
1205
+	 *
1206
+	 * @access    public
1207
+	 * @param $headers
1208
+	 * @return    array
1209
+	 */
1210
+	public static function extra_nocache_headers($headers): array
1211
+	{
1212
+		// for NGINX
1213
+		$headers['X-Accel-Expires'] = 0;
1214
+		// plus extra for Google Chrome since it doesn't seem to respect "no-cache", but WILL respect "no-store"
1215
+		$headers['Cache-Control'] = 'no-store, no-cache, must-revalidate, max-age=0';
1216
+		return $headers;
1217
+	}
1218
+
1219
+
1220
+	/**
1221
+	 *    nocache_headers
1222
+	 *
1223
+	 * @access    public
1224
+	 * @return    void
1225
+	 */
1226
+	public static function nocache_headers()
1227
+	{
1228
+		nocache_headers();
1229
+	}
1230
+
1231
+
1232
+	/**
1233
+	 * simply hooks into "wp_list_pages_exclude" filter (for wp_list_pages method) and makes sure EE critical pages are
1234
+	 * never returned with the function.
1235
+	 *
1236
+	 * @param array $exclude_array any existing pages being excluded are in this array.
1237
+	 * @return array
1238
+	 */
1239
+	public function remove_pages_from_wp_list_pages(array $exclude_array): array
1240
+	{
1241
+		return array_merge($exclude_array, $this->registry->CFG->core->get_critical_pages_array());
1242
+	}
1243 1243
 }
Please login to merge, or discard this patch.
core/db_classes/EE_Transaction.class.php 2 patches
Indentation   +1706 added lines, -1706 removed lines patch added patch discarded remove patch
@@ -13,1710 +13,1710 @@
 block discarded – undo
13 13
  */
14 14
 class EE_Transaction extends EE_Base_Class implements EEI_Transaction
15 15
 {
16
-    /**
17
-     * The length of time in seconds that a lock is applied before being considered expired.
18
-     * It is not long because a transaction should only be locked for the duration of the request that locked it
19
-     */
20
-    const LOCK_EXPIRATION = 2;
21
-
22
-    /**
23
-     * txn status upon initial construction.
24
-     *
25
-     * @var string
26
-     */
27
-    protected $_old_txn_status;
28
-
29
-
30
-    /**
31
-     * @param array  $props_n_values          incoming values
32
-     * @param string $timezone                incoming timezone
33
-     *                                        (if not set the timezone set for the website will be used.)
34
-     * @param array  $date_formats            incoming date_formats in an array where the first value is the
35
-     *                                        date_format and the second value is the time format
36
-     * @return EE_Transaction
37
-     * @throws EE_Error
38
-     * @throws InvalidArgumentException
39
-     * @throws InvalidDataTypeException
40
-     * @throws InvalidInterfaceException
41
-     * @throws ReflectionException
42
-     */
43
-    public static function new_instance($props_n_values = [], $timezone = null, $date_formats = [])
44
-    {
45
-        $has_object = parent::_check_for_object($props_n_values, __CLASS__, $timezone, $date_formats);
46
-        $txn        = $has_object
47
-            ? $has_object
48
-            : new self($props_n_values, false, $timezone, $date_formats);
49
-        if (! $has_object) {
50
-            $txn->set_old_txn_status($txn->status_ID());
51
-        }
52
-        return $txn;
53
-    }
54
-
55
-
56
-    /**
57
-     * @param array  $props_n_values  incoming values from the database
58
-     * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
59
-     *                                the website will be used.
60
-     * @return EE_Transaction
61
-     * @throws EE_Error
62
-     * @throws InvalidArgumentException
63
-     * @throws InvalidDataTypeException
64
-     * @throws InvalidInterfaceException
65
-     * @throws ReflectionException
66
-     */
67
-    public static function new_instance_from_db($props_n_values = [], $timezone = null)
68
-    {
69
-        $txn = new self($props_n_values, true, $timezone);
70
-        $txn->set_old_txn_status($txn->status_ID());
71
-        return $txn;
72
-    }
73
-
74
-
75
-    /**
76
-     * Sets a meta field indicating that this TXN is locked and should not be updated in the db.
77
-     * If a lock has already been set, then we will attempt to remove it in case it has expired.
78
-     * If that also fails, then an exception is thrown.
79
-     *
80
-     * @throws EE_Error
81
-     * @throws InvalidArgumentException
82
-     * @throws InvalidDataTypeException
83
-     * @throws InvalidInterfaceException
84
-     * @throws ReflectionException
85
-     */
86
-    public function lock()
87
-    {
88
-        // attempt to set lock, but if that fails...
89
-        if (! $this->add_extra_meta('lock', time(), true)) {
90
-            // then attempt to remove the lock in case it is expired
91
-            if ($this->_remove_expired_lock()) {
92
-                // if removal was successful, then try setting lock again
93
-                $this->lock();
94
-            } else {
95
-                // but if the lock can not be removed, then throw an exception
96
-                throw new EE_Error(
97
-                    sprintf(
98
-                        esc_html__(
99
-                            'Could not lock Transaction %1$d because it is already locked, meaning another part of the system is currently editing it. It should already be unlocked by the time you read this, so please refresh the page and try again.',
100
-                            'event_espresso'
101
-                        ),
102
-                        $this->ID()
103
-                    )
104
-                );
105
-            }
106
-        }
107
-    }
108
-
109
-
110
-    /**
111
-     * removes transaction lock applied in EE_Transaction::lock()
112
-     *
113
-     * @return int
114
-     * @throws EE_Error
115
-     * @throws InvalidArgumentException
116
-     * @throws InvalidDataTypeException
117
-     * @throws InvalidInterfaceException
118
-     * @throws ReflectionException
119
-     */
120
-    public function unlock()
121
-    {
122
-        return $this->delete_extra_meta('lock');
123
-    }
124
-
125
-
126
-    /**
127
-     * Decides whether or not now is the right time to update the transaction.
128
-     * This is useful because we don't always know if it is safe to update the transaction
129
-     * and its related data. why?
130
-     * because it's possible that the transaction is being used in another
131
-     * request and could overwrite anything we save.
132
-     * So we want to only update the txn once we know that won't happen.
133
-     * We also check that the lock isn't expired, and remove it if it is
134
-     *
135
-     * @return boolean
136
-     * @throws EE_Error
137
-     * @throws InvalidArgumentException
138
-     * @throws InvalidDataTypeException
139
-     * @throws InvalidInterfaceException
140
-     * @throws ReflectionException
141
-     */
142
-    public function is_locked()
143
-    {
144
-        // if TXN is not locked, then return false immediately
145
-        if (! $this->_get_lock()) {
146
-            return false;
147
-        }
148
-        // if not, then let's try and remove the lock in case it's expired...
149
-        // _remove_expired_lock() returns 0 when lock is valid (ie: removed = false)
150
-        // and a positive number if the lock was removed (ie: number of locks deleted),
151
-        // so we need to return the opposite
152
-        return ! $this->_remove_expired_lock() ? true : false;
153
-    }
154
-
155
-
156
-    /**
157
-     * Gets the meta field indicating that this TXN is locked
158
-     *
159
-     * @return int
160
-     * @throws EE_Error
161
-     * @throws InvalidArgumentException
162
-     * @throws InvalidDataTypeException
163
-     * @throws InvalidInterfaceException
164
-     * @throws ReflectionException
165
-     */
166
-    protected function _get_lock()
167
-    {
168
-        return (int) $this->get_extra_meta('lock', true, 0);
169
-    }
170
-
171
-
172
-    /**
173
-     * If the lock on this transaction is expired, then we want to remove it so that the transaction can be updated
174
-     *
175
-     * @return int
176
-     * @throws EE_Error
177
-     * @throws InvalidArgumentException
178
-     * @throws InvalidDataTypeException
179
-     * @throws InvalidInterfaceException
180
-     * @throws ReflectionException
181
-     */
182
-    protected function _remove_expired_lock()
183
-    {
184
-        $locked = $this->_get_lock();
185
-        if ($locked && time() - EE_Transaction::LOCK_EXPIRATION > $locked) {
186
-            return $this->unlock();
187
-        }
188
-        return 0;
189
-    }
190
-
191
-
192
-    /**
193
-     * Set transaction total
194
-     *
195
-     * @param float $total total value of transaction
196
-     * @throws EE_Error
197
-     * @throws InvalidArgumentException
198
-     * @throws InvalidDataTypeException
199
-     * @throws InvalidInterfaceException
200
-     * @throws ReflectionException
201
-     */
202
-    public function set_total($total = 0.00)
203
-    {
204
-        $this->set('TXN_total', (float) $total);
205
-    }
206
-
207
-
208
-    /**
209
-     * Set Total Amount Paid to Date
210
-     *
211
-     * @param float $total_paid total amount paid to date (sum of all payments)
212
-     * @throws EE_Error
213
-     * @throws InvalidArgumentException
214
-     * @throws InvalidDataTypeException
215
-     * @throws InvalidInterfaceException
216
-     * @throws ReflectionException
217
-     */
218
-    public function set_paid($total_paid = 0.00)
219
-    {
220
-        $this->set('TXN_paid', (float) $total_paid);
221
-    }
222
-
223
-
224
-    /**
225
-     * Set transaction status
226
-     *
227
-     * @param string $status        whether the transaction is open, declined, accepted,
228
-     *                              or any number of custom values that can be set
229
-     * @throws EE_Error
230
-     * @throws InvalidArgumentException
231
-     * @throws InvalidDataTypeException
232
-     * @throws InvalidInterfaceException
233
-     * @throws ReflectionException
234
-     */
235
-    public function set_status($status = '')
236
-    {
237
-        $this->set('STS_ID', $status);
238
-    }
239
-
240
-
241
-    /**
242
-     * Set hash salt
243
-     *
244
-     * @param string $hash_salt required for some payment gateways
245
-     * @throws EE_Error
246
-     * @throws InvalidArgumentException
247
-     * @throws InvalidDataTypeException
248
-     * @throws InvalidInterfaceException
249
-     * @throws ReflectionException
250
-     */
251
-    public function set_hash_salt($hash_salt = '')
252
-    {
253
-        $this->set('TXN_hash_salt', $hash_salt);
254
-    }
255
-
256
-
257
-    /**
258
-     * Sets TXN_reg_steps array
259
-     *
260
-     * @param array $txn_reg_steps
261
-     * @throws EE_Error
262
-     * @throws InvalidArgumentException
263
-     * @throws InvalidDataTypeException
264
-     * @throws InvalidInterfaceException
265
-     * @throws ReflectionException
266
-     */
267
-    public function set_reg_steps(array $txn_reg_steps)
268
-    {
269
-        $this->set('TXN_reg_steps', $txn_reg_steps);
270
-    }
271
-
272
-
273
-    /**
274
-     * Gets TXN_reg_steps
275
-     *
276
-     * @return array
277
-     * @throws EE_Error
278
-     * @throws InvalidArgumentException
279
-     * @throws InvalidDataTypeException
280
-     * @throws InvalidInterfaceException
281
-     * @throws ReflectionException
282
-     */
283
-    public function reg_steps()
284
-    {
285
-        $TXN_reg_steps = $this->get('TXN_reg_steps');
286
-        return is_array($TXN_reg_steps) ? $TXN_reg_steps : [];
287
-    }
288
-
289
-
290
-    /**
291
-     * @return string of transaction's total cost, with currency symbol and decimal
292
-     * @throws EE_Error
293
-     * @throws InvalidArgumentException
294
-     * @throws InvalidDataTypeException
295
-     * @throws InvalidInterfaceException
296
-     * @throws ReflectionException
297
-     */
298
-    public function pretty_total()
299
-    {
300
-        return $this->get_pretty('TXN_total');
301
-    }
302
-
303
-
304
-    /**
305
-     * Gets the amount paid in a pretty string (formatted and with currency symbol)
306
-     *
307
-     * @return string
308
-     * @throws EE_Error
309
-     * @throws InvalidArgumentException
310
-     * @throws InvalidDataTypeException
311
-     * @throws InvalidInterfaceException
312
-     * @throws ReflectionException
313
-     */
314
-    public function pretty_paid()
315
-    {
316
-        return $this->get_pretty('TXN_paid');
317
-    }
318
-
319
-
320
-    /**
321
-     * calculate the amount remaining for this transaction and return;
322
-     *
323
-     * @return float amount remaining
324
-     * @throws EE_Error
325
-     * @throws InvalidArgumentException
326
-     * @throws InvalidDataTypeException
327
-     * @throws InvalidInterfaceException
328
-     * @throws ReflectionException
329
-     */
330
-    public function remaining()
331
-    {
332
-        return $this->total() - $this->paid();
333
-    }
334
-
335
-
336
-    /**
337
-     * get Transaction Total
338
-     *
339
-     * @return float
340
-     * @throws EE_Error
341
-     * @throws InvalidArgumentException
342
-     * @throws InvalidDataTypeException
343
-     * @throws InvalidInterfaceException
344
-     * @throws ReflectionException
345
-     */
346
-    public function total()
347
-    {
348
-        return (float) $this->get('TXN_total');
349
-    }
350
-
351
-
352
-    /**
353
-     * get Total Amount Paid to Date
354
-     *
355
-     * @return float
356
-     * @throws EE_Error
357
-     * @throws InvalidArgumentException
358
-     * @throws InvalidDataTypeException
359
-     * @throws InvalidInterfaceException
360
-     * @throws ReflectionException
361
-     */
362
-    public function paid()
363
-    {
364
-        return (float) $this->get('TXN_paid');
365
-    }
366
-
367
-
368
-    /**
369
-     * @return mixed|null
370
-     * @throws EE_Error
371
-     * @throws InvalidArgumentException
372
-     * @throws InvalidDataTypeException
373
-     * @throws InvalidInterfaceException
374
-     * @throws ReflectionException
375
-     */
376
-    public function get_cart_session()
377
-    {
378
-        $session_data = (array) $this->get('TXN_session_data');
379
-        return isset($session_data['cart']) && $session_data['cart'] instanceof EE_Cart
380
-            ? $session_data['cart']
381
-            : null;
382
-    }
383
-
384
-
385
-    /**
386
-     * get Transaction session data
387
-     *
388
-     * @return array|mixed
389
-     * @throws EE_Error
390
-     * @throws InvalidArgumentException
391
-     * @throws InvalidDataTypeException
392
-     * @throws InvalidInterfaceException
393
-     * @throws ReflectionException
394
-     */
395
-    public function session_data()
396
-    {
397
-        $session_data = $this->get('TXN_session_data');
398
-        if (empty($session_data)) {
399
-            $session_data = [
400
-                'id'            => null,
401
-                'user_id'       => null,
402
-                'ip_address'    => null,
403
-                'user_agent'    => null,
404
-                'init_access'   => null,
405
-                'last_access'   => null,
406
-                'pages_visited' => [],
407
-            ];
408
-        }
409
-        return $session_data;
410
-    }
411
-
412
-
413
-    /**
414
-     * Set session data within the TXN object
415
-     *
416
-     * @param EE_Session|array $session_data
417
-     * @throws EE_Error
418
-     * @throws InvalidArgumentException
419
-     * @throws InvalidDataTypeException
420
-     * @throws InvalidInterfaceException
421
-     * @throws ReflectionException
422
-     */
423
-    public function set_txn_session_data($session_data)
424
-    {
425
-        if ($session_data instanceof EE_Session) {
426
-            $this->set('TXN_session_data', $session_data->get_session_data(null, true));
427
-        } else {
428
-            $this->set('TXN_session_data', $session_data);
429
-        }
430
-    }
431
-
432
-
433
-    /**
434
-     * get Transaction hash salt
435
-     *
436
-     * @return mixed
437
-     * @throws EE_Error
438
-     * @throws InvalidArgumentException
439
-     * @throws InvalidDataTypeException
440
-     * @throws InvalidInterfaceException
441
-     * @throws ReflectionException
442
-     */
443
-    public function hash_salt_()
444
-    {
445
-        return $this->get('TXN_hash_salt');
446
-    }
447
-
448
-
449
-    /**
450
-     * Returns the transaction datetime as either:
451
-     *            - unix timestamp format ($format = false, $gmt = true)
452
-     *            - formatted date string including the UTC (timezone) offset ($format = true ($gmt
453
-     *              has no affect with this option)), this also may include a timezone abbreviation if the
454
-     *              set timezone in this class differs from what the timezone is on the blog.
455
-     *            - formatted date string including the UTC (timezone) offset (default).
456
-     *
457
-     * @param boolean $format   - whether to return a unix timestamp (default) or formatted date string
458
-     * @param boolean $gmt      - whether to return a unix timestamp with UTC offset applied (default)
459
-     *                          or no UTC offset applied
460
-     * @return string | int
461
-     * @throws EE_Error
462
-     * @throws InvalidArgumentException
463
-     * @throws InvalidDataTypeException
464
-     * @throws InvalidInterfaceException
465
-     * @throws ReflectionException
466
-     */
467
-    public function datetime($format = false, $gmt = false)
468
-    {
469
-        if ($format) {
470
-            return $this->get_pretty('TXN_timestamp');
471
-        }
472
-        if ($gmt) {
473
-            return $this->get_raw('TXN_timestamp');
474
-        }
475
-        return $this->get('TXN_timestamp');
476
-    }
477
-
478
-
479
-    /**
480
-     * Gets registrations on this transaction
481
-     *
482
-     * @param array   $query_params array of query parameters
483
-     * @param boolean $get_cached   TRUE to retrieve cached registrations or FALSE to pull from the db
484
-     * @return EE_Base_Class[]|EE_Registration[]
485
-     * @throws EE_Error
486
-     * @throws InvalidArgumentException
487
-     * @throws InvalidDataTypeException
488
-     * @throws InvalidInterfaceException
489
-     * @throws ReflectionException
490
-     */
491
-    public function registrations($query_params = [], $get_cached = false)
492
-    {
493
-        $query_params = (empty($query_params) || ! is_array($query_params))
494
-            ? [
495
-                'order_by' => [
496
-                    'Event.EVT_name'     => 'ASC',
497
-                    'Attendee.ATT_lname' => 'ASC',
498
-                    'Attendee.ATT_fname' => 'ASC',
499
-                ],
500
-            ]
501
-            : $query_params;
502
-        $query_params = $get_cached ? [] : $query_params;
503
-        return $this->get_many_related('Registration', $query_params);
504
-    }
505
-
506
-
507
-    /**
508
-     * Gets all the attendees for this transaction (handy for use with EE_Attendee's get_registrations_for_event
509
-     * function for getting attendees and how many registrations they each have for an event)
510
-     *
511
-     * @return mixed EE_Attendee[] by default, int if $output is set to 'COUNT'
512
-     * @throws EE_Error
513
-     * @throws InvalidArgumentException
514
-     * @throws InvalidDataTypeException
515
-     * @throws InvalidInterfaceException
516
-     * @throws ReflectionException
517
-     */
518
-    public function attendees()
519
-    {
520
-        return $this->get_many_related('Attendee', [['Registration.Transaction.TXN_ID' => $this->ID()]]);
521
-    }
522
-
523
-
524
-    /**
525
-     * Gets payments for this transaction. Unlike other such functions, order by 'DESC' by default
526
-     *
527
-     * @param array $query_params @see
528
-     *                            https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
529
-     * @return EE_Base_Class[]|EE_Payment[]
530
-     * @throws EE_Error
531
-     * @throws InvalidArgumentException
532
-     * @throws InvalidDataTypeException
533
-     * @throws InvalidInterfaceException
534
-     * @throws ReflectionException
535
-     */
536
-    public function payments($query_params = [])
537
-    {
538
-        return $this->get_many_related('Payment', $query_params);
539
-    }
540
-
541
-
542
-    /**
543
-     * gets only approved payments for this transaction
544
-     *
545
-     * @return EE_Base_Class[]|EE_Payment[]
546
-     * @throws EE_Error
547
-     * @throws InvalidArgumentException
548
-     * @throws ReflectionException
549
-     * @throws InvalidDataTypeException
550
-     * @throws InvalidInterfaceException
551
-     */
552
-    public function approved_payments()
553
-    {
554
-        EE_Registry::instance()->load_model('Payment');
555
-        return $this->get_many_related(
556
-            'Payment',
557
-            [
558
-                ['STS_ID' => EEM_Payment::status_id_approved],
559
-                'order_by' => ['PAY_timestamp' => 'DESC'],
560
-            ]
561
-        );
562
-    }
563
-
564
-
565
-    /**
566
-     * Gets all payments which have not been approved
567
-     *
568
-     * @return EE_Base_Class[]|EEI_Payment[]
569
-     * @throws EE_Error if a model is misconfigured somehow
570
-     * @throws InvalidArgumentException
571
-     * @throws InvalidDataTypeException
572
-     * @throws InvalidInterfaceException
573
-     * @throws ReflectionException
574
-     */
575
-    public function pending_payments()
576
-    {
577
-        return $this->get_many_related(
578
-            'Payment',
579
-            [
580
-                [
581
-                    'STS_ID' => EEM_Payment::status_id_pending,
582
-                ],
583
-                'order_by' => [
584
-                    'PAY_timestamp' => 'DESC',
585
-                ],
586
-            ]
587
-        );
588
-    }
589
-
590
-
591
-    /**
592
-     * echoes $this->pretty_status()
593
-     *
594
-     * @param bool $show_icons
595
-     * @throws EE_Error
596
-     * @throws InvalidArgumentException
597
-     * @throws InvalidDataTypeException
598
-     * @throws InvalidInterfaceException
599
-     * @throws ReflectionException
600
-     */
601
-    public function e_pretty_status($show_icons = false)
602
-    {
603
-        echo wp_kses($this->pretty_status($show_icons), AllowedTags::getAllowedTags());
604
-    }
605
-
606
-
607
-    /**
608
-     * returns a pretty version of the status, good for displaying to users
609
-     *
610
-     * @param bool $show_icons
611
-     * @return string
612
-     * @throws EE_Error
613
-     * @throws InvalidArgumentException
614
-     * @throws InvalidDataTypeException
615
-     * @throws InvalidInterfaceException
616
-     * @throws ReflectionException
617
-     */
618
-    public function pretty_status($show_icons = false)
619
-    {
620
-        $status = EEM_Status::instance()->localized_status(
621
-            [$this->status_ID() => esc_html__('unknown', 'event_espresso')],
622
-            false,
623
-            'sentence'
624
-        );
625
-        $icon   = '';
626
-        switch ($this->status_ID()) {
627
-            case EEM_Transaction::complete_status_code:
628
-                $icon = $show_icons ? '<span class="dashicons dashicons-yes ee-icon-size-24 green-text"></span>' : '';
629
-                break;
630
-            case EEM_Transaction::incomplete_status_code:
631
-                $icon = $show_icons ? '<span class="dashicons dashicons-marker ee-icon-size-16 lt-blue-text"></span>'
632
-                    : '';
633
-                break;
634
-            case EEM_Transaction::abandoned_status_code:
635
-                $icon = $show_icons ? '<span class="dashicons dashicons-marker ee-icon-size-16 red-text"></span>' : '';
636
-                break;
637
-            case EEM_Transaction::failed_status_code:
638
-                $icon = $show_icons ? '<span class="dashicons dashicons-no ee-icon-size-16 red-text"></span>' : '';
639
-                break;
640
-            case EEM_Transaction::overpaid_status_code:
641
-                $icon = $show_icons ? '<span class="dashicons dashicons-plus ee-icon-size-16 orange-text"></span>' : '';
642
-                break;
643
-        }
644
-        return $icon . $status[ $this->status_ID() ];
645
-    }
646
-
647
-
648
-    /**
649
-     * get Transaction Status
650
-     *
651
-     * @return mixed
652
-     * @throws EE_Error
653
-     * @throws InvalidArgumentException
654
-     * @throws InvalidDataTypeException
655
-     * @throws InvalidInterfaceException
656
-     * @throws ReflectionException
657
-     */
658
-    public function status_ID()
659
-    {
660
-        return $this->get('STS_ID');
661
-    }
662
-
663
-
664
-    /**
665
-     * Returns TRUE or FALSE for whether or not this transaction cost any money
666
-     *
667
-     * @return boolean
668
-     * @throws EE_Error
669
-     * @throws InvalidArgumentException
670
-     * @throws InvalidDataTypeException
671
-     * @throws InvalidInterfaceException
672
-     * @throws ReflectionException
673
-     */
674
-    public function is_free()
675
-    {
676
-        return EEH_Money::compare_floats($this->get('TXN_total'), 0, '==');
677
-    }
678
-
679
-
680
-    /**
681
-     * Returns whether this transaction is complete
682
-     * Useful in templates and other logic for deciding if we should ask for another payment...
683
-     *
684
-     * @return boolean
685
-     * @throws EE_Error
686
-     * @throws InvalidArgumentException
687
-     * @throws InvalidDataTypeException
688
-     * @throws InvalidInterfaceException
689
-     * @throws ReflectionException
690
-     */
691
-    public function is_completed()
692
-    {
693
-        return $this->status_ID() === EEM_Transaction::complete_status_code;
694
-    }
695
-
696
-
697
-    /**
698
-     * Returns whether this transaction is incomplete
699
-     * Useful in templates and other logic for deciding if we should ask for another payment...
700
-     *
701
-     * @return boolean
702
-     * @throws EE_Error
703
-     * @throws InvalidArgumentException
704
-     * @throws InvalidDataTypeException
705
-     * @throws InvalidInterfaceException
706
-     * @throws ReflectionException
707
-     */
708
-    public function is_incomplete()
709
-    {
710
-        return $this->status_ID() === EEM_Transaction::incomplete_status_code;
711
-    }
712
-
713
-
714
-    /**
715
-     * Returns whether this transaction is overpaid
716
-     * Useful in templates and other logic for deciding if monies need to be refunded
717
-     *
718
-     * @return boolean
719
-     * @throws EE_Error
720
-     * @throws InvalidArgumentException
721
-     * @throws InvalidDataTypeException
722
-     * @throws InvalidInterfaceException
723
-     * @throws ReflectionException
724
-     */
725
-    public function is_overpaid()
726
-    {
727
-        return $this->status_ID() === EEM_Transaction::overpaid_status_code;
728
-    }
729
-
730
-
731
-    /**
732
-     * Returns whether this transaction was abandoned
733
-     * meaning that the transaction/registration process was somehow interrupted and never completed
734
-     * but that contact information exists for at least one registrant
735
-     *
736
-     * @return boolean
737
-     * @throws EE_Error
738
-     * @throws InvalidArgumentException
739
-     * @throws InvalidDataTypeException
740
-     * @throws InvalidInterfaceException
741
-     * @throws ReflectionException
742
-     */
743
-    public function is_abandoned()
744
-    {
745
-        return $this->status_ID() === EEM_Transaction::abandoned_status_code;
746
-    }
747
-
748
-
749
-    /**
750
-     * Returns whether this transaction failed
751
-     * meaning that the transaction/registration process was somehow interrupted and never completed
752
-     * and that NO contact information exists for any registrants
753
-     *
754
-     * @return boolean
755
-     * @throws EE_Error
756
-     * @throws InvalidArgumentException
757
-     * @throws InvalidDataTypeException
758
-     * @throws InvalidInterfaceException
759
-     * @throws ReflectionException
760
-     */
761
-    public function failed()
762
-    {
763
-        return $this->status_ID() === EEM_Transaction::failed_status_code;
764
-    }
765
-
766
-
767
-    /**
768
-     * This returns the url for the invoice of this transaction
769
-     *
770
-     * @param string $type 'html' or 'pdf' (default is pdf)
771
-     * @return string
772
-     * @throws DomainException
773
-     * @throws EE_Error
774
-     * @throws InvalidArgumentException
775
-     * @throws InvalidDataTypeException
776
-     * @throws InvalidInterfaceException
777
-     * @throws ReflectionException
778
-     */
779
-    public function invoice_url($type = 'html')
780
-    {
781
-        $REG = $this->primary_registration();
782
-        if (! $REG instanceof EE_Registration) {
783
-            return '';
784
-        }
785
-        return $REG->invoice_url($type);
786
-    }
787
-
788
-
789
-    /**
790
-     * Gets the primary registration only
791
-     *
792
-     * @return EE_Base_Class|EE_Registration
793
-     * @throws EE_Error
794
-     * @throws InvalidArgumentException
795
-     * @throws InvalidDataTypeException
796
-     * @throws InvalidInterfaceException
797
-     * @throws ReflectionException
798
-     */
799
-    public function primary_registration()
800
-    {
801
-        $registrations = (array) $this->get_many_related(
802
-            'Registration',
803
-            [['REG_count' => EEM_Registration::PRIMARY_REGISTRANT_COUNT]]
804
-        );
805
-        foreach ($registrations as $registration) {
806
-            // valid registration that is NOT cancelled or declined ?
807
-            if (
808
-                $registration instanceof EE_Registration
809
-                && ! in_array($registration->status_ID(), EEM_Registration::closed_reg_statuses(), true)
810
-            ) {
811
-                return $registration;
812
-            }
813
-        }
814
-        // nothing valid found, so just return first thing from array of results
815
-        return reset($registrations);
816
-    }
817
-
818
-
819
-    /**
820
-     * Gets the URL for viewing the receipt
821
-     *
822
-     * @param string $type 'pdf' or 'html' (default is 'html')
823
-     * @return string
824
-     * @throws DomainException
825
-     * @throws EE_Error
826
-     * @throws InvalidArgumentException
827
-     * @throws InvalidDataTypeException
828
-     * @throws InvalidInterfaceException
829
-     * @throws ReflectionException
830
-     */
831
-    public function receipt_url($type = 'html')
832
-    {
833
-        $REG = $this->primary_registration();
834
-        if (! $REG instanceof EE_Registration) {
835
-            return '';
836
-        }
837
-        return $REG->receipt_url($type);
838
-    }
839
-
840
-
841
-    /**
842
-     * Gets the URL of the thank you page with this registration REG_url_link added as
843
-     * a query parameter
844
-     *
845
-     * @return string
846
-     * @throws EE_Error
847
-     * @throws InvalidArgumentException
848
-     * @throws InvalidDataTypeException
849
-     * @throws InvalidInterfaceException
850
-     * @throws ReflectionException
851
-     */
852
-    public function payment_overview_url()
853
-    {
854
-        $primary_registration = $this->primary_registration();
855
-        return $primary_registration instanceof EE_Registration ? $primary_registration->payment_overview_url() : false;
856
-    }
857
-
858
-
859
-    /**
860
-     * @return string
861
-     * @throws EE_Error
862
-     * @throws InvalidArgumentException
863
-     * @throws InvalidDataTypeException
864
-     * @throws InvalidInterfaceException
865
-     * @throws ReflectionException
866
-     */
867
-    public function gateway_response_on_transaction()
868
-    {
869
-        $payment = $this->get_first_related('Payment');
870
-        return $payment instanceof EE_Payment ? $payment->gateway_response() : '';
871
-    }
872
-
873
-
874
-    /**
875
-     * Get the status object of this object
876
-     *
877
-     * @return EE_Base_Class|EE_Status
878
-     * @throws EE_Error
879
-     * @throws InvalidArgumentException
880
-     * @throws InvalidDataTypeException
881
-     * @throws InvalidInterfaceException
882
-     * @throws ReflectionException
883
-     */
884
-    public function status_obj()
885
-    {
886
-        return $this->get_first_related('Status');
887
-    }
888
-
889
-
890
-    /**
891
-     * Gets all the extra meta info on this payment
892
-     *
893
-     * @param array $query_params
894
-     * @return EE_Base_Class[]|EE_Extra_Meta
895
-     * @throws EE_Error
896
-     * @throws InvalidArgumentException
897
-     * @throws InvalidDataTypeException
898
-     * @throws InvalidInterfaceException
899
-     * @throws ReflectionException
900
-     * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
901
-     */
902
-    public function extra_meta($query_params = [])
903
-    {
904
-        return $this->get_many_related('Extra_Meta', $query_params);
905
-    }
906
-
907
-
908
-    /**
909
-     * Wrapper for _add_relation_to
910
-     *
911
-     * @param EE_Registration $registration
912
-     * @return EE_Base_Class the relation was added to
913
-     * @throws EE_Error
914
-     * @throws InvalidArgumentException
915
-     * @throws InvalidDataTypeException
916
-     * @throws InvalidInterfaceException
917
-     * @throws ReflectionException
918
-     */
919
-    public function add_registration(EE_Registration $registration)
920
-    {
921
-        return $this->_add_relation_to($registration, 'Registration');
922
-    }
923
-
924
-
925
-    /**
926
-     * Removes the given registration from being related (even before saving this transaction).
927
-     * If an ID/index is provided and this transaction isn't saved yet, removes it from list of cached relations
928
-     *
929
-     * @param int $registration_or_id
930
-     * @return EE_Base_Class that was removed from being related
931
-     * @throws EE_Error
932
-     * @throws InvalidArgumentException
933
-     * @throws InvalidDataTypeException
934
-     * @throws InvalidInterfaceException
935
-     * @throws ReflectionException
936
-     */
937
-    public function remove_registration_with_id($registration_or_id)
938
-    {
939
-        return $this->_remove_relation_to($registration_or_id, 'Registration');
940
-    }
941
-
942
-
943
-    /**
944
-     * Gets all the line items which are for ACTUAL items
945
-     *
946
-     * @return EE_Line_Item[]
947
-     * @throws EE_Error
948
-     * @throws InvalidArgumentException
949
-     * @throws InvalidDataTypeException
950
-     * @throws InvalidInterfaceException
951
-     * @throws ReflectionException
952
-     */
953
-    public function items_purchased()
954
-    {
955
-        return $this->line_items([['LIN_type' => EEM_Line_Item::type_line_item]]);
956
-    }
957
-
958
-
959
-    /**
960
-     * Wrapper for _add_relation_to
961
-     *
962
-     * @param EE_Line_Item $line_item
963
-     * @return EE_Base_Class the relation was added to
964
-     * @throws EE_Error
965
-     * @throws InvalidArgumentException
966
-     * @throws InvalidDataTypeException
967
-     * @throws InvalidInterfaceException
968
-     * @throws ReflectionException
969
-     */
970
-    public function add_line_item(EE_Line_Item $line_item)
971
-    {
972
-        return $this->_add_relation_to($line_item, 'Line_Item');
973
-    }
974
-
975
-
976
-    /**
977
-     * Gets ALL the line items related to this transaction (unstructured)
978
-     *
979
-     * @param array $query_params
980
-     * @return EE_Base_Class[]|EE_Line_Item[]
981
-     * @throws EE_Error
982
-     * @throws InvalidArgumentException
983
-     * @throws InvalidDataTypeException
984
-     * @throws InvalidInterfaceException
985
-     * @throws ReflectionException
986
-     */
987
-    public function line_items($query_params = [])
988
-    {
989
-        return $this->get_many_related('Line_Item', $query_params);
990
-    }
991
-
992
-
993
-    /**
994
-     * Gets all the line items which are taxes on the total
995
-     *
996
-     * @return EE_Line_Item[]
997
-     * @throws EE_Error
998
-     * @throws InvalidArgumentException
999
-     * @throws InvalidDataTypeException
1000
-     * @throws InvalidInterfaceException
1001
-     * @throws ReflectionException
1002
-     */
1003
-    public function tax_items()
1004
-    {
1005
-        return $this->line_items([['LIN_type' => EEM_Line_Item::type_tax]]);
1006
-    }
1007
-
1008
-
1009
-    /**
1010
-     * Gets the total line item (which is a parent of all other related line items,
1011
-     * meaning it takes them all into account on its total)
1012
-     *
1013
-     * @param bool $create_if_not_found
1014
-     * @return EE_Line_Item|null
1015
-     * @throws EE_Error
1016
-     * @throws InvalidArgumentException
1017
-     * @throws InvalidDataTypeException
1018
-     * @throws InvalidInterfaceException
1019
-     * @throws ReflectionException
1020
-     */
1021
-    public function total_line_item(bool $create_if_not_found = true): ?EE_Line_Item
1022
-    {
1023
-        $item = $this->get_first_related('Line_Item', [['LIN_type' => EEM_Line_Item::type_total]]);
1024
-        if ($item instanceof EE_Line_Item) {
1025
-            return $item;
1026
-        }
1027
-        return $create_if_not_found ? EEH_Line_Item::create_total_line_item($this) : null;
1028
-    }
1029
-
1030
-
1031
-    /**
1032
-     * Returns the total amount of tax on this transaction
1033
-     * (assumes there's only one tax subtotal line item)
1034
-     *
1035
-     * @return float
1036
-     * @throws EE_Error
1037
-     * @throws InvalidArgumentException
1038
-     * @throws InvalidDataTypeException
1039
-     * @throws InvalidInterfaceException
1040
-     * @throws ReflectionException
1041
-     */
1042
-    public function tax_total()
1043
-    {
1044
-        $tax_line_item = $this->tax_total_line_item();
1045
-        if ($tax_line_item) {
1046
-            return (float) $tax_line_item->total();
1047
-        }
1048
-        return (float) 0;
1049
-    }
1050
-
1051
-
1052
-    /**
1053
-     * Gets the tax subtotal line item (assumes there's only one)
1054
-     *
1055
-     * @return EE_Line_Item
1056
-     * @throws EE_Error
1057
-     * @throws InvalidArgumentException
1058
-     * @throws InvalidDataTypeException
1059
-     * @throws InvalidInterfaceException
1060
-     * @throws ReflectionException
1061
-     */
1062
-    public function tax_total_line_item()
1063
-    {
1064
-        return EEH_Line_Item::get_taxes_subtotal($this->total_line_item());
1065
-    }
1066
-
1067
-
1068
-    /**
1069
-     * Gets the array of billing info for the gateway and for this transaction's primary registration's attendee.
1070
-     *
1071
-     * @return EE_Form_Section_Proper
1072
-     * @throws EE_Error
1073
-     * @throws InvalidArgumentException
1074
-     * @throws InvalidDataTypeException
1075
-     * @throws InvalidInterfaceException
1076
-     * @throws ReflectionException
1077
-     */
1078
-    public function billing_info()
1079
-    {
1080
-        $payment_method = $this->payment_method();
1081
-        if (! $payment_method) {
1082
-            EE_Error::add_error(
1083
-                esc_html__(
1084
-                    'Could not find billing info for transaction because no gateway has been used for it yet',
1085
-                    'event_espresso'
1086
-                ),
1087
-                __FILE__,
1088
-                __FUNCTION__,
1089
-                __LINE__
1090
-            );
1091
-            return null;
1092
-        }
1093
-        $primary_reg = $this->primary_registration();
1094
-        if (! $primary_reg) {
1095
-            EE_Error::add_error(
1096
-                esc_html__(
1097
-                    'Cannot get billing info for gateway %s on transaction because no primary registration exists',
1098
-                    'event_espresso'
1099
-                ),
1100
-                __FILE__,
1101
-                __FUNCTION__,
1102
-                __LINE__
1103
-            );
1104
-            return null;
1105
-        }
1106
-        $attendee = $primary_reg->attendee();
1107
-        if (! $attendee) {
1108
-            EE_Error::add_error(
1109
-                esc_html__(
1110
-                    'Cannot get billing info for gateway %s on transaction because the primary registration has no attendee exists',
1111
-                    'event_espresso'
1112
-                ),
1113
-                __FILE__,
1114
-                __FUNCTION__,
1115
-                __LINE__
1116
-            );
1117
-            return null;
1118
-        }
1119
-        return $attendee->billing_info_for_payment_method($payment_method);
1120
-    }
1121
-
1122
-
1123
-    /**
1124
-     * Gets PMD_ID
1125
-     *
1126
-     * @return int
1127
-     * @throws EE_Error
1128
-     * @throws InvalidArgumentException
1129
-     * @throws InvalidDataTypeException
1130
-     * @throws InvalidInterfaceException
1131
-     * @throws ReflectionException
1132
-     */
1133
-    public function payment_method_ID()
1134
-    {
1135
-        return $this->get('PMD_ID');
1136
-    }
1137
-
1138
-
1139
-    /**
1140
-     * Sets PMD_ID
1141
-     *
1142
-     * @param int $PMD_ID
1143
-     * @throws EE_Error
1144
-     * @throws InvalidArgumentException
1145
-     * @throws InvalidDataTypeException
1146
-     * @throws InvalidInterfaceException
1147
-     * @throws ReflectionException
1148
-     */
1149
-    public function set_payment_method_ID($PMD_ID)
1150
-    {
1151
-        $this->set('PMD_ID', $PMD_ID);
1152
-    }
1153
-
1154
-
1155
-    /**
1156
-     * Gets the last-used payment method on this transaction
1157
-     * (we COULD just use the last-made payment, but some payment methods, namely
1158
-     * offline ones, dont' create payments)
1159
-     *
1160
-     * @return EE_Payment_Method
1161
-     * @throws EE_Error
1162
-     * @throws InvalidArgumentException
1163
-     * @throws InvalidDataTypeException
1164
-     * @throws InvalidInterfaceException
1165
-     * @throws ReflectionException
1166
-     */
1167
-    public function payment_method()
1168
-    {
1169
-        $pm = $this->get_first_related('Payment_Method');
1170
-        if ($pm instanceof EE_Payment_Method) {
1171
-            return $pm;
1172
-        }
1173
-        $last_payment = $this->last_payment();
1174
-        if ($last_payment instanceof EE_Payment && $last_payment->payment_method()) {
1175
-            return $last_payment->payment_method();
1176
-        }
1177
-        return null;
1178
-    }
1179
-
1180
-
1181
-    /**
1182
-     * Gets the last payment made
1183
-     *
1184
-     * @return EE_Base_Class|EE_Payment
1185
-     * @throws EE_Error
1186
-     * @throws InvalidArgumentException
1187
-     * @throws InvalidDataTypeException
1188
-     * @throws InvalidInterfaceException
1189
-     * @throws ReflectionException
1190
-     */
1191
-    public function last_payment()
1192
-    {
1193
-        return $this->get_first_related('Payment', ['order_by' => ['PAY_ID' => 'desc']]);
1194
-    }
1195
-
1196
-
1197
-    /**
1198
-     * Gets all the line items which are unrelated to tickets on this transaction
1199
-     *
1200
-     * @return EE_Line_Item[]
1201
-     * @throws EE_Error
1202
-     * @throws InvalidArgumentException
1203
-     * @throws InvalidDataTypeException
1204
-     * @throws InvalidInterfaceException
1205
-     * @throws ReflectionException
1206
-     */
1207
-    public function non_ticket_line_items()
1208
-    {
1209
-        return EEM_Line_Item::instance()->get_all_non_ticket_line_items_for_transaction($this->ID());
1210
-    }
1211
-
1212
-
1213
-    /**
1214
-     * possibly toggles TXN status
1215
-     *
1216
-     * @param boolean $update whether to save the TXN
1217
-     * @return bool whether the TXN was saved
1218
-     * @throws EE_Error
1219
-     * @throws InvalidArgumentException
1220
-     * @throws InvalidDataTypeException
1221
-     * @throws InvalidInterfaceException
1222
-     * @throws ReflectionException
1223
-     * @throws RuntimeException
1224
-     */
1225
-    public function update_status_based_on_total_paid($update = true)
1226
-    {
1227
-        // set transaction status based on comparison of TXN_paid vs TXN_total
1228
-        if (EEH_Money::compare_floats($this->paid(), $this->total(), '>')) {
1229
-            $new_txn_status = EEM_Transaction::overpaid_status_code;
1230
-        } elseif (EEH_Money::compare_floats($this->paid(), $this->total())) {
1231
-            $new_txn_status = EEM_Transaction::complete_status_code;
1232
-        } elseif (EEH_Money::compare_floats($this->paid(), $this->total(), '<')) {
1233
-            $new_txn_status = EEM_Transaction::incomplete_status_code;
1234
-        } else {
1235
-            throw new RuntimeException(
1236
-                esc_html__('The total paid calculation for this transaction is inaccurate.', 'event_espresso')
1237
-            );
1238
-        }
1239
-        if ($new_txn_status !== $this->status_ID()) {
1240
-            $this->set_status($new_txn_status);
1241
-            if ($update) {
1242
-                return (bool) $this->save();
1243
-            }
1244
-        }
1245
-        return false;
1246
-    }
1247
-
1248
-
1249
-    /**
1250
-     * Updates the transaction's status and total_paid based on all the payments
1251
-     * that apply to it
1252
-     *
1253
-     * @return array|bool
1254
-     * @throws EE_Error
1255
-     * @throws InvalidArgumentException
1256
-     * @throws ReflectionException
1257
-     * @throws InvalidDataTypeException
1258
-     * @throws InvalidInterfaceException
1259
-     * @deprecated
1260
-     */
1261
-    public function update_based_on_payments()
1262
-    {
1263
-        EE_Error::doing_it_wrong(
1264
-            __CLASS__ . '::' . __FUNCTION__,
1265
-            sprintf(
1266
-                esc_html__('This method is deprecated. Please use "%s" instead', 'event_espresso'),
1267
-                'EE_Transaction_Processor::update_transaction_and_registrations_after_checkout_or_payment()'
1268
-            ),
1269
-            '4.6.0'
1270
-        );
1271
-        /** @type EE_Transaction_Processor $transaction_processor */
1272
-        $transaction_processor = EE_Registry::instance()->load_class('Transaction_Processor');
1273
-        return $transaction_processor->update_transaction_and_registrations_after_checkout_or_payment($this);
1274
-    }
1275
-
1276
-
1277
-    /**
1278
-     * @return string
1279
-     */
1280
-    public function old_txn_status()
1281
-    {
1282
-        return $this->_old_txn_status;
1283
-    }
1284
-
1285
-
1286
-    /**
1287
-     * @param string $old_txn_status
1288
-     */
1289
-    public function set_old_txn_status($old_txn_status)
1290
-    {
1291
-        // only set the first time
1292
-        if ($this->_old_txn_status === null) {
1293
-            $this->_old_txn_status = $old_txn_status;
1294
-        }
1295
-    }
1296
-
1297
-
1298
-    /**
1299
-     * reg_status_updated
1300
-     *
1301
-     * @return bool
1302
-     * @throws EE_Error
1303
-     * @throws InvalidArgumentException
1304
-     * @throws InvalidDataTypeException
1305
-     * @throws InvalidInterfaceException
1306
-     * @throws ReflectionException
1307
-     */
1308
-    public function txn_status_updated()
1309
-    {
1310
-        return $this->status_ID() !== $this->_old_txn_status && $this->_old_txn_status !== null;
1311
-    }
1312
-
1313
-
1314
-    /**
1315
-     * _reg_steps_completed
1316
-     * if $check_all is TRUE, then returns TRUE if ALL reg steps have been marked as completed,
1317
-     * if a $reg_step_slug is provided, then this step will be skipped when testing for completion
1318
-     * if $check_all is FALSE and a $reg_step_slug is provided, then ONLY that reg step will be tested for completion
1319
-     *
1320
-     * @param string $reg_step_slug
1321
-     * @param bool   $check_all
1322
-     * @return bool|int
1323
-     * @throws EE_Error
1324
-     * @throws InvalidArgumentException
1325
-     * @throws InvalidDataTypeException
1326
-     * @throws InvalidInterfaceException
1327
-     * @throws ReflectionException
1328
-     */
1329
-    private function _reg_steps_completed($reg_step_slug = '', $check_all = true)
1330
-    {
1331
-        $reg_steps = $this->reg_steps();
1332
-        if (! is_array($reg_steps) || empty($reg_steps)) {
1333
-            return false;
1334
-        }
1335
-        // loop thru reg steps array)
1336
-        foreach ($reg_steps as $slug => $reg_step_completed) {
1337
-            // if NOT checking ALL steps (only checking one step)
1338
-            if (! $check_all) {
1339
-                // and this is the one
1340
-                if ($slug === $reg_step_slug) {
1341
-                    return $reg_step_completed;
1342
-                }
1343
-                // skip to next reg step in loop
1344
-                continue;
1345
-            }
1346
-            // $check_all must be true, else we would never have gotten to this point
1347
-            if ($slug === $reg_step_slug) {
1348
-                // if we reach this point, then we are testing either:
1349
-                // all_reg_steps_completed_except() or
1350
-                // all_reg_steps_completed_except_final_step(),
1351
-                // and since this is the reg step EXCEPTION being tested
1352
-                // we want to return true (yes true) if this reg step is NOT completed
1353
-                // ie: "is everything completed except the final step?"
1354
-                // "that is correct... the final step is not completed, but all others are."
1355
-                return $reg_step_completed !== true;
1356
-            }
1357
-            if ($reg_step_completed !== true) {
1358
-                // if any reg step is NOT completed, then ALL steps are not completed
1359
-                return false;
1360
-            }
1361
-        }
1362
-        return true;
1363
-    }
1364
-
1365
-
1366
-    /**
1367
-     * all_reg_steps_completed
1368
-     * returns:
1369
-     *    true if ALL reg steps have been marked as completed
1370
-     *        or false if any step is not completed
1371
-     *
1372
-     * @return bool
1373
-     * @throws EE_Error
1374
-     * @throws InvalidArgumentException
1375
-     * @throws InvalidDataTypeException
1376
-     * @throws InvalidInterfaceException
1377
-     * @throws ReflectionException
1378
-     */
1379
-    public function all_reg_steps_completed()
1380
-    {
1381
-        return $this->_reg_steps_completed();
1382
-    }
1383
-
1384
-
1385
-    /**
1386
-     * all_reg_steps_completed_except
1387
-     * returns:
1388
-     *        true if ALL reg steps, except a particular step that you wish to skip over, have been marked as completed
1389
-     *        or false if any other step is not completed
1390
-     *        or false if ALL steps are completed including the exception you are testing !!!
1391
-     *
1392
-     * @param string $exception
1393
-     * @return bool
1394
-     * @throws EE_Error
1395
-     * @throws InvalidArgumentException
1396
-     * @throws InvalidDataTypeException
1397
-     * @throws InvalidInterfaceException
1398
-     * @throws ReflectionException
1399
-     */
1400
-    public function all_reg_steps_completed_except($exception = '')
1401
-    {
1402
-        return $this->_reg_steps_completed($exception);
1403
-    }
1404
-
1405
-
1406
-    /**
1407
-     * all_reg_steps_completed_except
1408
-     * returns:
1409
-     *        true if ALL reg steps, except the final step, have been marked as completed
1410
-     *        or false if any step is not completed
1411
-     *    or false if ALL steps are completed including the final step !!!
1412
-     *
1413
-     * @return bool
1414
-     * @throws EE_Error
1415
-     * @throws InvalidArgumentException
1416
-     * @throws InvalidDataTypeException
1417
-     * @throws InvalidInterfaceException
1418
-     * @throws ReflectionException
1419
-     */
1420
-    public function all_reg_steps_completed_except_final_step()
1421
-    {
1422
-        return $this->_reg_steps_completed('finalize_registration');
1423
-    }
1424
-
1425
-
1426
-    /**
1427
-     * reg_step_completed
1428
-     * returns:
1429
-     *    true if a specific reg step has been marked as completed
1430
-     *    a Unix timestamp if it has been initialized but not yet completed,
1431
-     *    or false if it has not yet been initialized
1432
-     *
1433
-     * @param string $reg_step_slug
1434
-     * @return bool|int
1435
-     * @throws EE_Error
1436
-     * @throws InvalidArgumentException
1437
-     * @throws InvalidDataTypeException
1438
-     * @throws InvalidInterfaceException
1439
-     * @throws ReflectionException
1440
-     */
1441
-    public function reg_step_completed($reg_step_slug)
1442
-    {
1443
-        return $this->_reg_steps_completed($reg_step_slug, false);
1444
-    }
1445
-
1446
-
1447
-    /**
1448
-     * completed_final_reg_step
1449
-     * returns:
1450
-     *    true if the finalize_registration reg step has been marked as completed
1451
-     *    a Unix timestamp if it has been initialized but not yet completed,
1452
-     *    or false if it has not yet been initialized
1453
-     *
1454
-     * @return bool|int
1455
-     * @throws EE_Error
1456
-     * @throws InvalidArgumentException
1457
-     * @throws InvalidDataTypeException
1458
-     * @throws InvalidInterfaceException
1459
-     * @throws ReflectionException
1460
-     */
1461
-    public function final_reg_step_completed()
1462
-    {
1463
-        return $this->_reg_steps_completed('finalize_registration', false);
1464
-    }
1465
-
1466
-
1467
-    /**
1468
-     * set_reg_step_initiated
1469
-     * given a valid TXN_reg_step, this sets it's value to a unix timestamp
1470
-     *
1471
-     * @param string $reg_step_slug
1472
-     * @return boolean
1473
-     * @throws EE_Error
1474
-     * @throws InvalidArgumentException
1475
-     * @throws InvalidDataTypeException
1476
-     * @throws InvalidInterfaceException
1477
-     * @throws ReflectionException
1478
-     */
1479
-    public function set_reg_step_initiated($reg_step_slug)
1480
-    {
1481
-        return $this->_set_reg_step_completed_status($reg_step_slug, time());
1482
-    }
1483
-
1484
-
1485
-    /**
1486
-     * set_reg_step_completed
1487
-     * given a valid TXN_reg_step, this sets the step as completed
1488
-     *
1489
-     * @param string $reg_step_slug
1490
-     * @return boolean
1491
-     * @throws EE_Error
1492
-     * @throws InvalidArgumentException
1493
-     * @throws InvalidDataTypeException
1494
-     * @throws InvalidInterfaceException
1495
-     * @throws ReflectionException
1496
-     */
1497
-    public function set_reg_step_completed($reg_step_slug)
1498
-    {
1499
-        return $this->_set_reg_step_completed_status($reg_step_slug, true);
1500
-    }
1501
-
1502
-
1503
-    /**
1504
-     * set_reg_step_completed
1505
-     * given a valid TXN_reg_step slug, this sets the step as NOT completed
1506
-     *
1507
-     * @param string $reg_step_slug
1508
-     * @return boolean
1509
-     * @throws EE_Error
1510
-     * @throws InvalidArgumentException
1511
-     * @throws InvalidDataTypeException
1512
-     * @throws InvalidInterfaceException
1513
-     * @throws ReflectionException
1514
-     */
1515
-    public function set_reg_step_not_completed($reg_step_slug)
1516
-    {
1517
-        return $this->_set_reg_step_completed_status($reg_step_slug, false);
1518
-    }
1519
-
1520
-
1521
-    /**
1522
-     * set_reg_step_completed
1523
-     * given a valid reg step slug, this sets the TXN_reg_step completed status which is either:
1524
-     *
1525
-     * @param string      $reg_step_slug
1526
-     * @param boolean|int $status
1527
-     * @return boolean
1528
-     * @throws EE_Error
1529
-     * @throws InvalidArgumentException
1530
-     * @throws InvalidDataTypeException
1531
-     * @throws InvalidInterfaceException
1532
-     * @throws ReflectionException
1533
-     */
1534
-    private function _set_reg_step_completed_status($reg_step_slug, $status)
1535
-    {
1536
-        // validate status
1537
-        $status = is_bool($status) || is_int($status) ? $status : false;
1538
-        // get reg steps array
1539
-        $txn_reg_steps = $this->reg_steps();
1540
-        // if reg step does NOT exist
1541
-        if (! isset($txn_reg_steps[ $reg_step_slug ])) {
1542
-            return false;
1543
-        }
1544
-        // if  we're trying to complete a step that is already completed
1545
-        if ($txn_reg_steps[ $reg_step_slug ] === true) {
1546
-            return true;
1547
-        }
1548
-        // if  we're trying to complete a step that hasn't even started
1549
-        if ($status === true && $txn_reg_steps[ $reg_step_slug ] === false) {
1550
-            return false;
1551
-        }
1552
-        // if current status value matches the incoming value (no change)
1553
-        // type casting as int means values should collapse to either 0, 1, or a timestamp like 1234567890
1554
-        if ((int) $txn_reg_steps[ $reg_step_slug ] === (int) $status) {
1555
-            // this will happen in cases where multiple AJAX requests occur during the same step
1556
-            return true;
1557
-        }
1558
-        // if we're trying to set a start time, but it has already been set...
1559
-        if (is_numeric($status) && is_numeric($txn_reg_steps[ $reg_step_slug ])) {
1560
-            // skip the update below, but don't return FALSE so that errors won't be displayed
1561
-            return true;
1562
-        }
1563
-        // update completed status
1564
-        $txn_reg_steps[ $reg_step_slug ] = $status;
1565
-        $this->set_reg_steps($txn_reg_steps);
1566
-        $this->save();
1567
-        return true;
1568
-    }
1569
-
1570
-
1571
-    /**
1572
-     * remove_reg_step
1573
-     * given a valid TXN_reg_step slug, this will remove (unset)
1574
-     * the reg step from the TXN reg step array
1575
-     *
1576
-     * @param string $reg_step_slug
1577
-     * @return void
1578
-     * @throws EE_Error
1579
-     * @throws InvalidArgumentException
1580
-     * @throws InvalidDataTypeException
1581
-     * @throws InvalidInterfaceException
1582
-     * @throws ReflectionException
1583
-     */
1584
-    public function remove_reg_step($reg_step_slug)
1585
-    {
1586
-        // get reg steps array
1587
-        $txn_reg_steps = $this->reg_steps();
1588
-        unset($txn_reg_steps[ $reg_step_slug ]);
1589
-        $this->set_reg_steps($txn_reg_steps);
1590
-    }
1591
-
1592
-
1593
-    /**
1594
-     * toggle_failed_transaction_status
1595
-     * upgrades a TXNs status from failed to abandoned,
1596
-     * meaning that contact information has been captured for at least one registrant
1597
-     *
1598
-     * @param bool $save
1599
-     * @return bool
1600
-     * @throws EE_Error
1601
-     * @throws InvalidArgumentException
1602
-     * @throws InvalidDataTypeException
1603
-     * @throws InvalidInterfaceException
1604
-     * @throws ReflectionException
1605
-     */
1606
-    public function toggle_failed_transaction_status($save = true)
1607
-    {
1608
-        // if TXN status is still set as "failed"...
1609
-        if ($this->status_ID() === EEM_Transaction::failed_status_code) {
1610
-            $this->set_status(EEM_Transaction::abandoned_status_code);
1611
-            if ($save) {
1612
-                $this->save();
1613
-            }
1614
-            return true;
1615
-        }
1616
-        return false;
1617
-    }
1618
-
1619
-
1620
-    /**
1621
-     * toggle_abandoned_transaction_status
1622
-     * upgrades a TXNs status from failed or abandoned to incomplete
1623
-     *
1624
-     * @return bool
1625
-     * @throws EE_Error
1626
-     * @throws InvalidArgumentException
1627
-     * @throws InvalidDataTypeException
1628
-     * @throws InvalidInterfaceException
1629
-     * @throws ReflectionException
1630
-     */
1631
-    public function toggle_abandoned_transaction_status()
1632
-    {
1633
-        // if TXN status has not been updated already due to a payment, and is still set as "failed" or "abandoned"...
1634
-        $txn_status = $this->status_ID();
1635
-        if (
1636
-            $txn_status === EEM_Transaction::failed_status_code
1637
-            || $txn_status === EEM_Transaction::abandoned_status_code
1638
-        ) {
1639
-            // if a contact record for the primary registrant has been created
1640
-            if (
1641
-                $this->primary_registration() instanceof EE_Registration
1642
-                && $this->primary_registration()->attendee() instanceof EE_Attendee
1643
-            ) {
1644
-                $this->set_status(EEM_Transaction::incomplete_status_code);
1645
-            } else {
1646
-                // no contact record? yer abandoned!
1647
-                $this->set_status(EEM_Transaction::abandoned_status_code);
1648
-            }
1649
-            return true;
1650
-        }
1651
-        return false;
1652
-    }
1653
-
1654
-
1655
-    /**
1656
-     * checks if an Abandoned TXN has any related payments, and if so,
1657
-     * updates the TXN status based on the amount paid
1658
-     *
1659
-     * @throws EE_Error
1660
-     * @throws InvalidArgumentException
1661
-     * @throws InvalidDataTypeException
1662
-     * @throws InvalidInterfaceException
1663
-     * @throws ReflectionException
1664
-     * @throws RuntimeException
1665
-     * @throws ReflectionException
1666
-     */
1667
-    public function verify_abandoned_transaction_status()
1668
-    {
1669
-        if ($this->status_ID() !== EEM_Transaction::abandoned_status_code) {
1670
-            return;
1671
-        }
1672
-        $payments = $this->get_many_related('Payment');
1673
-        if (! empty($payments)) {
1674
-            foreach ($payments as $payment) {
1675
-                if ($payment instanceof EE_Payment) {
1676
-                    // kk this TXN should NOT be abandoned
1677
-                    $this->update_status_based_on_total_paid();
1678
-                    if (! (defined('DOING_AJAX') && DOING_AJAX) && is_admin()) {
1679
-                        EE_Error::add_attention(
1680
-                            sprintf(
1681
-                                esc_html__(
1682
-                                    'The status for Transaction #%1$d has been updated from "Abandoned" to "%2$s", because at least one payment has been made towards it. If the payment appears in the "Payment Details" table below, you may need to edit its status and/or other details as well.',
1683
-                                    'event_espresso'
1684
-                                ),
1685
-                                $this->ID(),
1686
-                                $this->pretty_status()
1687
-                            )
1688
-                        );
1689
-                    }
1690
-                    // get final reg step status
1691
-                    $finalized = $this->final_reg_step_completed();
1692
-                    // if the 'finalize_registration' step has been initiated (has a timestamp)
1693
-                    // but has not yet been fully completed (TRUE)
1694
-                    if (is_int($finalized) && $finalized !== false && $finalized !== true) {
1695
-                        $this->set_reg_step_completed('finalize_registration');
1696
-                        $this->save();
1697
-                    }
1698
-                }
1699
-            }
1700
-        }
1701
-    }
1702
-
1703
-
1704
-    /**
1705
-     * @throws EE_Error
1706
-     * @throws InvalidArgumentException
1707
-     * @throws InvalidDataTypeException
1708
-     * @throws InvalidInterfaceException
1709
-     * @throws ReflectionException
1710
-     * @throws RuntimeException
1711
-     * @since 4.10.4.p
1712
-     */
1713
-    public function recalculateLineItems()
1714
-    {
1715
-        $total_line_item = $this->total_line_item(false);
1716
-        if ($total_line_item instanceof EE_Line_Item) {
1717
-            EEH_Line_Item::resetIsTaxableForTickets($total_line_item);
1718
-            return EEH_Line_Item::apply_taxes($total_line_item, true);
1719
-        }
1720
-        return false;
1721
-    }
16
+	/**
17
+	 * The length of time in seconds that a lock is applied before being considered expired.
18
+	 * It is not long because a transaction should only be locked for the duration of the request that locked it
19
+	 */
20
+	const LOCK_EXPIRATION = 2;
21
+
22
+	/**
23
+	 * txn status upon initial construction.
24
+	 *
25
+	 * @var string
26
+	 */
27
+	protected $_old_txn_status;
28
+
29
+
30
+	/**
31
+	 * @param array  $props_n_values          incoming values
32
+	 * @param string $timezone                incoming timezone
33
+	 *                                        (if not set the timezone set for the website will be used.)
34
+	 * @param array  $date_formats            incoming date_formats in an array where the first value is the
35
+	 *                                        date_format and the second value is the time format
36
+	 * @return EE_Transaction
37
+	 * @throws EE_Error
38
+	 * @throws InvalidArgumentException
39
+	 * @throws InvalidDataTypeException
40
+	 * @throws InvalidInterfaceException
41
+	 * @throws ReflectionException
42
+	 */
43
+	public static function new_instance($props_n_values = [], $timezone = null, $date_formats = [])
44
+	{
45
+		$has_object = parent::_check_for_object($props_n_values, __CLASS__, $timezone, $date_formats);
46
+		$txn        = $has_object
47
+			? $has_object
48
+			: new self($props_n_values, false, $timezone, $date_formats);
49
+		if (! $has_object) {
50
+			$txn->set_old_txn_status($txn->status_ID());
51
+		}
52
+		return $txn;
53
+	}
54
+
55
+
56
+	/**
57
+	 * @param array  $props_n_values  incoming values from the database
58
+	 * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
59
+	 *                                the website will be used.
60
+	 * @return EE_Transaction
61
+	 * @throws EE_Error
62
+	 * @throws InvalidArgumentException
63
+	 * @throws InvalidDataTypeException
64
+	 * @throws InvalidInterfaceException
65
+	 * @throws ReflectionException
66
+	 */
67
+	public static function new_instance_from_db($props_n_values = [], $timezone = null)
68
+	{
69
+		$txn = new self($props_n_values, true, $timezone);
70
+		$txn->set_old_txn_status($txn->status_ID());
71
+		return $txn;
72
+	}
73
+
74
+
75
+	/**
76
+	 * Sets a meta field indicating that this TXN is locked and should not be updated in the db.
77
+	 * If a lock has already been set, then we will attempt to remove it in case it has expired.
78
+	 * If that also fails, then an exception is thrown.
79
+	 *
80
+	 * @throws EE_Error
81
+	 * @throws InvalidArgumentException
82
+	 * @throws InvalidDataTypeException
83
+	 * @throws InvalidInterfaceException
84
+	 * @throws ReflectionException
85
+	 */
86
+	public function lock()
87
+	{
88
+		// attempt to set lock, but if that fails...
89
+		if (! $this->add_extra_meta('lock', time(), true)) {
90
+			// then attempt to remove the lock in case it is expired
91
+			if ($this->_remove_expired_lock()) {
92
+				// if removal was successful, then try setting lock again
93
+				$this->lock();
94
+			} else {
95
+				// but if the lock can not be removed, then throw an exception
96
+				throw new EE_Error(
97
+					sprintf(
98
+						esc_html__(
99
+							'Could not lock Transaction %1$d because it is already locked, meaning another part of the system is currently editing it. It should already be unlocked by the time you read this, so please refresh the page and try again.',
100
+							'event_espresso'
101
+						),
102
+						$this->ID()
103
+					)
104
+				);
105
+			}
106
+		}
107
+	}
108
+
109
+
110
+	/**
111
+	 * removes transaction lock applied in EE_Transaction::lock()
112
+	 *
113
+	 * @return int
114
+	 * @throws EE_Error
115
+	 * @throws InvalidArgumentException
116
+	 * @throws InvalidDataTypeException
117
+	 * @throws InvalidInterfaceException
118
+	 * @throws ReflectionException
119
+	 */
120
+	public function unlock()
121
+	{
122
+		return $this->delete_extra_meta('lock');
123
+	}
124
+
125
+
126
+	/**
127
+	 * Decides whether or not now is the right time to update the transaction.
128
+	 * This is useful because we don't always know if it is safe to update the transaction
129
+	 * and its related data. why?
130
+	 * because it's possible that the transaction is being used in another
131
+	 * request and could overwrite anything we save.
132
+	 * So we want to only update the txn once we know that won't happen.
133
+	 * We also check that the lock isn't expired, and remove it if it is
134
+	 *
135
+	 * @return boolean
136
+	 * @throws EE_Error
137
+	 * @throws InvalidArgumentException
138
+	 * @throws InvalidDataTypeException
139
+	 * @throws InvalidInterfaceException
140
+	 * @throws ReflectionException
141
+	 */
142
+	public function is_locked()
143
+	{
144
+		// if TXN is not locked, then return false immediately
145
+		if (! $this->_get_lock()) {
146
+			return false;
147
+		}
148
+		// if not, then let's try and remove the lock in case it's expired...
149
+		// _remove_expired_lock() returns 0 when lock is valid (ie: removed = false)
150
+		// and a positive number if the lock was removed (ie: number of locks deleted),
151
+		// so we need to return the opposite
152
+		return ! $this->_remove_expired_lock() ? true : false;
153
+	}
154
+
155
+
156
+	/**
157
+	 * Gets the meta field indicating that this TXN is locked
158
+	 *
159
+	 * @return int
160
+	 * @throws EE_Error
161
+	 * @throws InvalidArgumentException
162
+	 * @throws InvalidDataTypeException
163
+	 * @throws InvalidInterfaceException
164
+	 * @throws ReflectionException
165
+	 */
166
+	protected function _get_lock()
167
+	{
168
+		return (int) $this->get_extra_meta('lock', true, 0);
169
+	}
170
+
171
+
172
+	/**
173
+	 * If the lock on this transaction is expired, then we want to remove it so that the transaction can be updated
174
+	 *
175
+	 * @return int
176
+	 * @throws EE_Error
177
+	 * @throws InvalidArgumentException
178
+	 * @throws InvalidDataTypeException
179
+	 * @throws InvalidInterfaceException
180
+	 * @throws ReflectionException
181
+	 */
182
+	protected function _remove_expired_lock()
183
+	{
184
+		$locked = $this->_get_lock();
185
+		if ($locked && time() - EE_Transaction::LOCK_EXPIRATION > $locked) {
186
+			return $this->unlock();
187
+		}
188
+		return 0;
189
+	}
190
+
191
+
192
+	/**
193
+	 * Set transaction total
194
+	 *
195
+	 * @param float $total total value of transaction
196
+	 * @throws EE_Error
197
+	 * @throws InvalidArgumentException
198
+	 * @throws InvalidDataTypeException
199
+	 * @throws InvalidInterfaceException
200
+	 * @throws ReflectionException
201
+	 */
202
+	public function set_total($total = 0.00)
203
+	{
204
+		$this->set('TXN_total', (float) $total);
205
+	}
206
+
207
+
208
+	/**
209
+	 * Set Total Amount Paid to Date
210
+	 *
211
+	 * @param float $total_paid total amount paid to date (sum of all payments)
212
+	 * @throws EE_Error
213
+	 * @throws InvalidArgumentException
214
+	 * @throws InvalidDataTypeException
215
+	 * @throws InvalidInterfaceException
216
+	 * @throws ReflectionException
217
+	 */
218
+	public function set_paid($total_paid = 0.00)
219
+	{
220
+		$this->set('TXN_paid', (float) $total_paid);
221
+	}
222
+
223
+
224
+	/**
225
+	 * Set transaction status
226
+	 *
227
+	 * @param string $status        whether the transaction is open, declined, accepted,
228
+	 *                              or any number of custom values that can be set
229
+	 * @throws EE_Error
230
+	 * @throws InvalidArgumentException
231
+	 * @throws InvalidDataTypeException
232
+	 * @throws InvalidInterfaceException
233
+	 * @throws ReflectionException
234
+	 */
235
+	public function set_status($status = '')
236
+	{
237
+		$this->set('STS_ID', $status);
238
+	}
239
+
240
+
241
+	/**
242
+	 * Set hash salt
243
+	 *
244
+	 * @param string $hash_salt required for some payment gateways
245
+	 * @throws EE_Error
246
+	 * @throws InvalidArgumentException
247
+	 * @throws InvalidDataTypeException
248
+	 * @throws InvalidInterfaceException
249
+	 * @throws ReflectionException
250
+	 */
251
+	public function set_hash_salt($hash_salt = '')
252
+	{
253
+		$this->set('TXN_hash_salt', $hash_salt);
254
+	}
255
+
256
+
257
+	/**
258
+	 * Sets TXN_reg_steps array
259
+	 *
260
+	 * @param array $txn_reg_steps
261
+	 * @throws EE_Error
262
+	 * @throws InvalidArgumentException
263
+	 * @throws InvalidDataTypeException
264
+	 * @throws InvalidInterfaceException
265
+	 * @throws ReflectionException
266
+	 */
267
+	public function set_reg_steps(array $txn_reg_steps)
268
+	{
269
+		$this->set('TXN_reg_steps', $txn_reg_steps);
270
+	}
271
+
272
+
273
+	/**
274
+	 * Gets TXN_reg_steps
275
+	 *
276
+	 * @return array
277
+	 * @throws EE_Error
278
+	 * @throws InvalidArgumentException
279
+	 * @throws InvalidDataTypeException
280
+	 * @throws InvalidInterfaceException
281
+	 * @throws ReflectionException
282
+	 */
283
+	public function reg_steps()
284
+	{
285
+		$TXN_reg_steps = $this->get('TXN_reg_steps');
286
+		return is_array($TXN_reg_steps) ? $TXN_reg_steps : [];
287
+	}
288
+
289
+
290
+	/**
291
+	 * @return string of transaction's total cost, with currency symbol and decimal
292
+	 * @throws EE_Error
293
+	 * @throws InvalidArgumentException
294
+	 * @throws InvalidDataTypeException
295
+	 * @throws InvalidInterfaceException
296
+	 * @throws ReflectionException
297
+	 */
298
+	public function pretty_total()
299
+	{
300
+		return $this->get_pretty('TXN_total');
301
+	}
302
+
303
+
304
+	/**
305
+	 * Gets the amount paid in a pretty string (formatted and with currency symbol)
306
+	 *
307
+	 * @return string
308
+	 * @throws EE_Error
309
+	 * @throws InvalidArgumentException
310
+	 * @throws InvalidDataTypeException
311
+	 * @throws InvalidInterfaceException
312
+	 * @throws ReflectionException
313
+	 */
314
+	public function pretty_paid()
315
+	{
316
+		return $this->get_pretty('TXN_paid');
317
+	}
318
+
319
+
320
+	/**
321
+	 * calculate the amount remaining for this transaction and return;
322
+	 *
323
+	 * @return float amount remaining
324
+	 * @throws EE_Error
325
+	 * @throws InvalidArgumentException
326
+	 * @throws InvalidDataTypeException
327
+	 * @throws InvalidInterfaceException
328
+	 * @throws ReflectionException
329
+	 */
330
+	public function remaining()
331
+	{
332
+		return $this->total() - $this->paid();
333
+	}
334
+
335
+
336
+	/**
337
+	 * get Transaction Total
338
+	 *
339
+	 * @return float
340
+	 * @throws EE_Error
341
+	 * @throws InvalidArgumentException
342
+	 * @throws InvalidDataTypeException
343
+	 * @throws InvalidInterfaceException
344
+	 * @throws ReflectionException
345
+	 */
346
+	public function total()
347
+	{
348
+		return (float) $this->get('TXN_total');
349
+	}
350
+
351
+
352
+	/**
353
+	 * get Total Amount Paid to Date
354
+	 *
355
+	 * @return float
356
+	 * @throws EE_Error
357
+	 * @throws InvalidArgumentException
358
+	 * @throws InvalidDataTypeException
359
+	 * @throws InvalidInterfaceException
360
+	 * @throws ReflectionException
361
+	 */
362
+	public function paid()
363
+	{
364
+		return (float) $this->get('TXN_paid');
365
+	}
366
+
367
+
368
+	/**
369
+	 * @return mixed|null
370
+	 * @throws EE_Error
371
+	 * @throws InvalidArgumentException
372
+	 * @throws InvalidDataTypeException
373
+	 * @throws InvalidInterfaceException
374
+	 * @throws ReflectionException
375
+	 */
376
+	public function get_cart_session()
377
+	{
378
+		$session_data = (array) $this->get('TXN_session_data');
379
+		return isset($session_data['cart']) && $session_data['cart'] instanceof EE_Cart
380
+			? $session_data['cart']
381
+			: null;
382
+	}
383
+
384
+
385
+	/**
386
+	 * get Transaction session data
387
+	 *
388
+	 * @return array|mixed
389
+	 * @throws EE_Error
390
+	 * @throws InvalidArgumentException
391
+	 * @throws InvalidDataTypeException
392
+	 * @throws InvalidInterfaceException
393
+	 * @throws ReflectionException
394
+	 */
395
+	public function session_data()
396
+	{
397
+		$session_data = $this->get('TXN_session_data');
398
+		if (empty($session_data)) {
399
+			$session_data = [
400
+				'id'            => null,
401
+				'user_id'       => null,
402
+				'ip_address'    => null,
403
+				'user_agent'    => null,
404
+				'init_access'   => null,
405
+				'last_access'   => null,
406
+				'pages_visited' => [],
407
+			];
408
+		}
409
+		return $session_data;
410
+	}
411
+
412
+
413
+	/**
414
+	 * Set session data within the TXN object
415
+	 *
416
+	 * @param EE_Session|array $session_data
417
+	 * @throws EE_Error
418
+	 * @throws InvalidArgumentException
419
+	 * @throws InvalidDataTypeException
420
+	 * @throws InvalidInterfaceException
421
+	 * @throws ReflectionException
422
+	 */
423
+	public function set_txn_session_data($session_data)
424
+	{
425
+		if ($session_data instanceof EE_Session) {
426
+			$this->set('TXN_session_data', $session_data->get_session_data(null, true));
427
+		} else {
428
+			$this->set('TXN_session_data', $session_data);
429
+		}
430
+	}
431
+
432
+
433
+	/**
434
+	 * get Transaction hash salt
435
+	 *
436
+	 * @return mixed
437
+	 * @throws EE_Error
438
+	 * @throws InvalidArgumentException
439
+	 * @throws InvalidDataTypeException
440
+	 * @throws InvalidInterfaceException
441
+	 * @throws ReflectionException
442
+	 */
443
+	public function hash_salt_()
444
+	{
445
+		return $this->get('TXN_hash_salt');
446
+	}
447
+
448
+
449
+	/**
450
+	 * Returns the transaction datetime as either:
451
+	 *            - unix timestamp format ($format = false, $gmt = true)
452
+	 *            - formatted date string including the UTC (timezone) offset ($format = true ($gmt
453
+	 *              has no affect with this option)), this also may include a timezone abbreviation if the
454
+	 *              set timezone in this class differs from what the timezone is on the blog.
455
+	 *            - formatted date string including the UTC (timezone) offset (default).
456
+	 *
457
+	 * @param boolean $format   - whether to return a unix timestamp (default) or formatted date string
458
+	 * @param boolean $gmt      - whether to return a unix timestamp with UTC offset applied (default)
459
+	 *                          or no UTC offset applied
460
+	 * @return string | int
461
+	 * @throws EE_Error
462
+	 * @throws InvalidArgumentException
463
+	 * @throws InvalidDataTypeException
464
+	 * @throws InvalidInterfaceException
465
+	 * @throws ReflectionException
466
+	 */
467
+	public function datetime($format = false, $gmt = false)
468
+	{
469
+		if ($format) {
470
+			return $this->get_pretty('TXN_timestamp');
471
+		}
472
+		if ($gmt) {
473
+			return $this->get_raw('TXN_timestamp');
474
+		}
475
+		return $this->get('TXN_timestamp');
476
+	}
477
+
478
+
479
+	/**
480
+	 * Gets registrations on this transaction
481
+	 *
482
+	 * @param array   $query_params array of query parameters
483
+	 * @param boolean $get_cached   TRUE to retrieve cached registrations or FALSE to pull from the db
484
+	 * @return EE_Base_Class[]|EE_Registration[]
485
+	 * @throws EE_Error
486
+	 * @throws InvalidArgumentException
487
+	 * @throws InvalidDataTypeException
488
+	 * @throws InvalidInterfaceException
489
+	 * @throws ReflectionException
490
+	 */
491
+	public function registrations($query_params = [], $get_cached = false)
492
+	{
493
+		$query_params = (empty($query_params) || ! is_array($query_params))
494
+			? [
495
+				'order_by' => [
496
+					'Event.EVT_name'     => 'ASC',
497
+					'Attendee.ATT_lname' => 'ASC',
498
+					'Attendee.ATT_fname' => 'ASC',
499
+				],
500
+			]
501
+			: $query_params;
502
+		$query_params = $get_cached ? [] : $query_params;
503
+		return $this->get_many_related('Registration', $query_params);
504
+	}
505
+
506
+
507
+	/**
508
+	 * Gets all the attendees for this transaction (handy for use with EE_Attendee's get_registrations_for_event
509
+	 * function for getting attendees and how many registrations they each have for an event)
510
+	 *
511
+	 * @return mixed EE_Attendee[] by default, int if $output is set to 'COUNT'
512
+	 * @throws EE_Error
513
+	 * @throws InvalidArgumentException
514
+	 * @throws InvalidDataTypeException
515
+	 * @throws InvalidInterfaceException
516
+	 * @throws ReflectionException
517
+	 */
518
+	public function attendees()
519
+	{
520
+		return $this->get_many_related('Attendee', [['Registration.Transaction.TXN_ID' => $this->ID()]]);
521
+	}
522
+
523
+
524
+	/**
525
+	 * Gets payments for this transaction. Unlike other such functions, order by 'DESC' by default
526
+	 *
527
+	 * @param array $query_params @see
528
+	 *                            https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
529
+	 * @return EE_Base_Class[]|EE_Payment[]
530
+	 * @throws EE_Error
531
+	 * @throws InvalidArgumentException
532
+	 * @throws InvalidDataTypeException
533
+	 * @throws InvalidInterfaceException
534
+	 * @throws ReflectionException
535
+	 */
536
+	public function payments($query_params = [])
537
+	{
538
+		return $this->get_many_related('Payment', $query_params);
539
+	}
540
+
541
+
542
+	/**
543
+	 * gets only approved payments for this transaction
544
+	 *
545
+	 * @return EE_Base_Class[]|EE_Payment[]
546
+	 * @throws EE_Error
547
+	 * @throws InvalidArgumentException
548
+	 * @throws ReflectionException
549
+	 * @throws InvalidDataTypeException
550
+	 * @throws InvalidInterfaceException
551
+	 */
552
+	public function approved_payments()
553
+	{
554
+		EE_Registry::instance()->load_model('Payment');
555
+		return $this->get_many_related(
556
+			'Payment',
557
+			[
558
+				['STS_ID' => EEM_Payment::status_id_approved],
559
+				'order_by' => ['PAY_timestamp' => 'DESC'],
560
+			]
561
+		);
562
+	}
563
+
564
+
565
+	/**
566
+	 * Gets all payments which have not been approved
567
+	 *
568
+	 * @return EE_Base_Class[]|EEI_Payment[]
569
+	 * @throws EE_Error if a model is misconfigured somehow
570
+	 * @throws InvalidArgumentException
571
+	 * @throws InvalidDataTypeException
572
+	 * @throws InvalidInterfaceException
573
+	 * @throws ReflectionException
574
+	 */
575
+	public function pending_payments()
576
+	{
577
+		return $this->get_many_related(
578
+			'Payment',
579
+			[
580
+				[
581
+					'STS_ID' => EEM_Payment::status_id_pending,
582
+				],
583
+				'order_by' => [
584
+					'PAY_timestamp' => 'DESC',
585
+				],
586
+			]
587
+		);
588
+	}
589
+
590
+
591
+	/**
592
+	 * echoes $this->pretty_status()
593
+	 *
594
+	 * @param bool $show_icons
595
+	 * @throws EE_Error
596
+	 * @throws InvalidArgumentException
597
+	 * @throws InvalidDataTypeException
598
+	 * @throws InvalidInterfaceException
599
+	 * @throws ReflectionException
600
+	 */
601
+	public function e_pretty_status($show_icons = false)
602
+	{
603
+		echo wp_kses($this->pretty_status($show_icons), AllowedTags::getAllowedTags());
604
+	}
605
+
606
+
607
+	/**
608
+	 * returns a pretty version of the status, good for displaying to users
609
+	 *
610
+	 * @param bool $show_icons
611
+	 * @return string
612
+	 * @throws EE_Error
613
+	 * @throws InvalidArgumentException
614
+	 * @throws InvalidDataTypeException
615
+	 * @throws InvalidInterfaceException
616
+	 * @throws ReflectionException
617
+	 */
618
+	public function pretty_status($show_icons = false)
619
+	{
620
+		$status = EEM_Status::instance()->localized_status(
621
+			[$this->status_ID() => esc_html__('unknown', 'event_espresso')],
622
+			false,
623
+			'sentence'
624
+		);
625
+		$icon   = '';
626
+		switch ($this->status_ID()) {
627
+			case EEM_Transaction::complete_status_code:
628
+				$icon = $show_icons ? '<span class="dashicons dashicons-yes ee-icon-size-24 green-text"></span>' : '';
629
+				break;
630
+			case EEM_Transaction::incomplete_status_code:
631
+				$icon = $show_icons ? '<span class="dashicons dashicons-marker ee-icon-size-16 lt-blue-text"></span>'
632
+					: '';
633
+				break;
634
+			case EEM_Transaction::abandoned_status_code:
635
+				$icon = $show_icons ? '<span class="dashicons dashicons-marker ee-icon-size-16 red-text"></span>' : '';
636
+				break;
637
+			case EEM_Transaction::failed_status_code:
638
+				$icon = $show_icons ? '<span class="dashicons dashicons-no ee-icon-size-16 red-text"></span>' : '';
639
+				break;
640
+			case EEM_Transaction::overpaid_status_code:
641
+				$icon = $show_icons ? '<span class="dashicons dashicons-plus ee-icon-size-16 orange-text"></span>' : '';
642
+				break;
643
+		}
644
+		return $icon . $status[ $this->status_ID() ];
645
+	}
646
+
647
+
648
+	/**
649
+	 * get Transaction Status
650
+	 *
651
+	 * @return mixed
652
+	 * @throws EE_Error
653
+	 * @throws InvalidArgumentException
654
+	 * @throws InvalidDataTypeException
655
+	 * @throws InvalidInterfaceException
656
+	 * @throws ReflectionException
657
+	 */
658
+	public function status_ID()
659
+	{
660
+		return $this->get('STS_ID');
661
+	}
662
+
663
+
664
+	/**
665
+	 * Returns TRUE or FALSE for whether or not this transaction cost any money
666
+	 *
667
+	 * @return boolean
668
+	 * @throws EE_Error
669
+	 * @throws InvalidArgumentException
670
+	 * @throws InvalidDataTypeException
671
+	 * @throws InvalidInterfaceException
672
+	 * @throws ReflectionException
673
+	 */
674
+	public function is_free()
675
+	{
676
+		return EEH_Money::compare_floats($this->get('TXN_total'), 0, '==');
677
+	}
678
+
679
+
680
+	/**
681
+	 * Returns whether this transaction is complete
682
+	 * Useful in templates and other logic for deciding if we should ask for another payment...
683
+	 *
684
+	 * @return boolean
685
+	 * @throws EE_Error
686
+	 * @throws InvalidArgumentException
687
+	 * @throws InvalidDataTypeException
688
+	 * @throws InvalidInterfaceException
689
+	 * @throws ReflectionException
690
+	 */
691
+	public function is_completed()
692
+	{
693
+		return $this->status_ID() === EEM_Transaction::complete_status_code;
694
+	}
695
+
696
+
697
+	/**
698
+	 * Returns whether this transaction is incomplete
699
+	 * Useful in templates and other logic for deciding if we should ask for another payment...
700
+	 *
701
+	 * @return boolean
702
+	 * @throws EE_Error
703
+	 * @throws InvalidArgumentException
704
+	 * @throws InvalidDataTypeException
705
+	 * @throws InvalidInterfaceException
706
+	 * @throws ReflectionException
707
+	 */
708
+	public function is_incomplete()
709
+	{
710
+		return $this->status_ID() === EEM_Transaction::incomplete_status_code;
711
+	}
712
+
713
+
714
+	/**
715
+	 * Returns whether this transaction is overpaid
716
+	 * Useful in templates and other logic for deciding if monies need to be refunded
717
+	 *
718
+	 * @return boolean
719
+	 * @throws EE_Error
720
+	 * @throws InvalidArgumentException
721
+	 * @throws InvalidDataTypeException
722
+	 * @throws InvalidInterfaceException
723
+	 * @throws ReflectionException
724
+	 */
725
+	public function is_overpaid()
726
+	{
727
+		return $this->status_ID() === EEM_Transaction::overpaid_status_code;
728
+	}
729
+
730
+
731
+	/**
732
+	 * Returns whether this transaction was abandoned
733
+	 * meaning that the transaction/registration process was somehow interrupted and never completed
734
+	 * but that contact information exists for at least one registrant
735
+	 *
736
+	 * @return boolean
737
+	 * @throws EE_Error
738
+	 * @throws InvalidArgumentException
739
+	 * @throws InvalidDataTypeException
740
+	 * @throws InvalidInterfaceException
741
+	 * @throws ReflectionException
742
+	 */
743
+	public function is_abandoned()
744
+	{
745
+		return $this->status_ID() === EEM_Transaction::abandoned_status_code;
746
+	}
747
+
748
+
749
+	/**
750
+	 * Returns whether this transaction failed
751
+	 * meaning that the transaction/registration process was somehow interrupted and never completed
752
+	 * and that NO contact information exists for any registrants
753
+	 *
754
+	 * @return boolean
755
+	 * @throws EE_Error
756
+	 * @throws InvalidArgumentException
757
+	 * @throws InvalidDataTypeException
758
+	 * @throws InvalidInterfaceException
759
+	 * @throws ReflectionException
760
+	 */
761
+	public function failed()
762
+	{
763
+		return $this->status_ID() === EEM_Transaction::failed_status_code;
764
+	}
765
+
766
+
767
+	/**
768
+	 * This returns the url for the invoice of this transaction
769
+	 *
770
+	 * @param string $type 'html' or 'pdf' (default is pdf)
771
+	 * @return string
772
+	 * @throws DomainException
773
+	 * @throws EE_Error
774
+	 * @throws InvalidArgumentException
775
+	 * @throws InvalidDataTypeException
776
+	 * @throws InvalidInterfaceException
777
+	 * @throws ReflectionException
778
+	 */
779
+	public function invoice_url($type = 'html')
780
+	{
781
+		$REG = $this->primary_registration();
782
+		if (! $REG instanceof EE_Registration) {
783
+			return '';
784
+		}
785
+		return $REG->invoice_url($type);
786
+	}
787
+
788
+
789
+	/**
790
+	 * Gets the primary registration only
791
+	 *
792
+	 * @return EE_Base_Class|EE_Registration
793
+	 * @throws EE_Error
794
+	 * @throws InvalidArgumentException
795
+	 * @throws InvalidDataTypeException
796
+	 * @throws InvalidInterfaceException
797
+	 * @throws ReflectionException
798
+	 */
799
+	public function primary_registration()
800
+	{
801
+		$registrations = (array) $this->get_many_related(
802
+			'Registration',
803
+			[['REG_count' => EEM_Registration::PRIMARY_REGISTRANT_COUNT]]
804
+		);
805
+		foreach ($registrations as $registration) {
806
+			// valid registration that is NOT cancelled or declined ?
807
+			if (
808
+				$registration instanceof EE_Registration
809
+				&& ! in_array($registration->status_ID(), EEM_Registration::closed_reg_statuses(), true)
810
+			) {
811
+				return $registration;
812
+			}
813
+		}
814
+		// nothing valid found, so just return first thing from array of results
815
+		return reset($registrations);
816
+	}
817
+
818
+
819
+	/**
820
+	 * Gets the URL for viewing the receipt
821
+	 *
822
+	 * @param string $type 'pdf' or 'html' (default is 'html')
823
+	 * @return string
824
+	 * @throws DomainException
825
+	 * @throws EE_Error
826
+	 * @throws InvalidArgumentException
827
+	 * @throws InvalidDataTypeException
828
+	 * @throws InvalidInterfaceException
829
+	 * @throws ReflectionException
830
+	 */
831
+	public function receipt_url($type = 'html')
832
+	{
833
+		$REG = $this->primary_registration();
834
+		if (! $REG instanceof EE_Registration) {
835
+			return '';
836
+		}
837
+		return $REG->receipt_url($type);
838
+	}
839
+
840
+
841
+	/**
842
+	 * Gets the URL of the thank you page with this registration REG_url_link added as
843
+	 * a query parameter
844
+	 *
845
+	 * @return string
846
+	 * @throws EE_Error
847
+	 * @throws InvalidArgumentException
848
+	 * @throws InvalidDataTypeException
849
+	 * @throws InvalidInterfaceException
850
+	 * @throws ReflectionException
851
+	 */
852
+	public function payment_overview_url()
853
+	{
854
+		$primary_registration = $this->primary_registration();
855
+		return $primary_registration instanceof EE_Registration ? $primary_registration->payment_overview_url() : false;
856
+	}
857
+
858
+
859
+	/**
860
+	 * @return string
861
+	 * @throws EE_Error
862
+	 * @throws InvalidArgumentException
863
+	 * @throws InvalidDataTypeException
864
+	 * @throws InvalidInterfaceException
865
+	 * @throws ReflectionException
866
+	 */
867
+	public function gateway_response_on_transaction()
868
+	{
869
+		$payment = $this->get_first_related('Payment');
870
+		return $payment instanceof EE_Payment ? $payment->gateway_response() : '';
871
+	}
872
+
873
+
874
+	/**
875
+	 * Get the status object of this object
876
+	 *
877
+	 * @return EE_Base_Class|EE_Status
878
+	 * @throws EE_Error
879
+	 * @throws InvalidArgumentException
880
+	 * @throws InvalidDataTypeException
881
+	 * @throws InvalidInterfaceException
882
+	 * @throws ReflectionException
883
+	 */
884
+	public function status_obj()
885
+	{
886
+		return $this->get_first_related('Status');
887
+	}
888
+
889
+
890
+	/**
891
+	 * Gets all the extra meta info on this payment
892
+	 *
893
+	 * @param array $query_params
894
+	 * @return EE_Base_Class[]|EE_Extra_Meta
895
+	 * @throws EE_Error
896
+	 * @throws InvalidArgumentException
897
+	 * @throws InvalidDataTypeException
898
+	 * @throws InvalidInterfaceException
899
+	 * @throws ReflectionException
900
+	 * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
901
+	 */
902
+	public function extra_meta($query_params = [])
903
+	{
904
+		return $this->get_many_related('Extra_Meta', $query_params);
905
+	}
906
+
907
+
908
+	/**
909
+	 * Wrapper for _add_relation_to
910
+	 *
911
+	 * @param EE_Registration $registration
912
+	 * @return EE_Base_Class the relation was added to
913
+	 * @throws EE_Error
914
+	 * @throws InvalidArgumentException
915
+	 * @throws InvalidDataTypeException
916
+	 * @throws InvalidInterfaceException
917
+	 * @throws ReflectionException
918
+	 */
919
+	public function add_registration(EE_Registration $registration)
920
+	{
921
+		return $this->_add_relation_to($registration, 'Registration');
922
+	}
923
+
924
+
925
+	/**
926
+	 * Removes the given registration from being related (even before saving this transaction).
927
+	 * If an ID/index is provided and this transaction isn't saved yet, removes it from list of cached relations
928
+	 *
929
+	 * @param int $registration_or_id
930
+	 * @return EE_Base_Class that was removed from being related
931
+	 * @throws EE_Error
932
+	 * @throws InvalidArgumentException
933
+	 * @throws InvalidDataTypeException
934
+	 * @throws InvalidInterfaceException
935
+	 * @throws ReflectionException
936
+	 */
937
+	public function remove_registration_with_id($registration_or_id)
938
+	{
939
+		return $this->_remove_relation_to($registration_or_id, 'Registration');
940
+	}
941
+
942
+
943
+	/**
944
+	 * Gets all the line items which are for ACTUAL items
945
+	 *
946
+	 * @return EE_Line_Item[]
947
+	 * @throws EE_Error
948
+	 * @throws InvalidArgumentException
949
+	 * @throws InvalidDataTypeException
950
+	 * @throws InvalidInterfaceException
951
+	 * @throws ReflectionException
952
+	 */
953
+	public function items_purchased()
954
+	{
955
+		return $this->line_items([['LIN_type' => EEM_Line_Item::type_line_item]]);
956
+	}
957
+
958
+
959
+	/**
960
+	 * Wrapper for _add_relation_to
961
+	 *
962
+	 * @param EE_Line_Item $line_item
963
+	 * @return EE_Base_Class the relation was added to
964
+	 * @throws EE_Error
965
+	 * @throws InvalidArgumentException
966
+	 * @throws InvalidDataTypeException
967
+	 * @throws InvalidInterfaceException
968
+	 * @throws ReflectionException
969
+	 */
970
+	public function add_line_item(EE_Line_Item $line_item)
971
+	{
972
+		return $this->_add_relation_to($line_item, 'Line_Item');
973
+	}
974
+
975
+
976
+	/**
977
+	 * Gets ALL the line items related to this transaction (unstructured)
978
+	 *
979
+	 * @param array $query_params
980
+	 * @return EE_Base_Class[]|EE_Line_Item[]
981
+	 * @throws EE_Error
982
+	 * @throws InvalidArgumentException
983
+	 * @throws InvalidDataTypeException
984
+	 * @throws InvalidInterfaceException
985
+	 * @throws ReflectionException
986
+	 */
987
+	public function line_items($query_params = [])
988
+	{
989
+		return $this->get_many_related('Line_Item', $query_params);
990
+	}
991
+
992
+
993
+	/**
994
+	 * Gets all the line items which are taxes on the total
995
+	 *
996
+	 * @return EE_Line_Item[]
997
+	 * @throws EE_Error
998
+	 * @throws InvalidArgumentException
999
+	 * @throws InvalidDataTypeException
1000
+	 * @throws InvalidInterfaceException
1001
+	 * @throws ReflectionException
1002
+	 */
1003
+	public function tax_items()
1004
+	{
1005
+		return $this->line_items([['LIN_type' => EEM_Line_Item::type_tax]]);
1006
+	}
1007
+
1008
+
1009
+	/**
1010
+	 * Gets the total line item (which is a parent of all other related line items,
1011
+	 * meaning it takes them all into account on its total)
1012
+	 *
1013
+	 * @param bool $create_if_not_found
1014
+	 * @return EE_Line_Item|null
1015
+	 * @throws EE_Error
1016
+	 * @throws InvalidArgumentException
1017
+	 * @throws InvalidDataTypeException
1018
+	 * @throws InvalidInterfaceException
1019
+	 * @throws ReflectionException
1020
+	 */
1021
+	public function total_line_item(bool $create_if_not_found = true): ?EE_Line_Item
1022
+	{
1023
+		$item = $this->get_first_related('Line_Item', [['LIN_type' => EEM_Line_Item::type_total]]);
1024
+		if ($item instanceof EE_Line_Item) {
1025
+			return $item;
1026
+		}
1027
+		return $create_if_not_found ? EEH_Line_Item::create_total_line_item($this) : null;
1028
+	}
1029
+
1030
+
1031
+	/**
1032
+	 * Returns the total amount of tax on this transaction
1033
+	 * (assumes there's only one tax subtotal line item)
1034
+	 *
1035
+	 * @return float
1036
+	 * @throws EE_Error
1037
+	 * @throws InvalidArgumentException
1038
+	 * @throws InvalidDataTypeException
1039
+	 * @throws InvalidInterfaceException
1040
+	 * @throws ReflectionException
1041
+	 */
1042
+	public function tax_total()
1043
+	{
1044
+		$tax_line_item = $this->tax_total_line_item();
1045
+		if ($tax_line_item) {
1046
+			return (float) $tax_line_item->total();
1047
+		}
1048
+		return (float) 0;
1049
+	}
1050
+
1051
+
1052
+	/**
1053
+	 * Gets the tax subtotal line item (assumes there's only one)
1054
+	 *
1055
+	 * @return EE_Line_Item
1056
+	 * @throws EE_Error
1057
+	 * @throws InvalidArgumentException
1058
+	 * @throws InvalidDataTypeException
1059
+	 * @throws InvalidInterfaceException
1060
+	 * @throws ReflectionException
1061
+	 */
1062
+	public function tax_total_line_item()
1063
+	{
1064
+		return EEH_Line_Item::get_taxes_subtotal($this->total_line_item());
1065
+	}
1066
+
1067
+
1068
+	/**
1069
+	 * Gets the array of billing info for the gateway and for this transaction's primary registration's attendee.
1070
+	 *
1071
+	 * @return EE_Form_Section_Proper
1072
+	 * @throws EE_Error
1073
+	 * @throws InvalidArgumentException
1074
+	 * @throws InvalidDataTypeException
1075
+	 * @throws InvalidInterfaceException
1076
+	 * @throws ReflectionException
1077
+	 */
1078
+	public function billing_info()
1079
+	{
1080
+		$payment_method = $this->payment_method();
1081
+		if (! $payment_method) {
1082
+			EE_Error::add_error(
1083
+				esc_html__(
1084
+					'Could not find billing info for transaction because no gateway has been used for it yet',
1085
+					'event_espresso'
1086
+				),
1087
+				__FILE__,
1088
+				__FUNCTION__,
1089
+				__LINE__
1090
+			);
1091
+			return null;
1092
+		}
1093
+		$primary_reg = $this->primary_registration();
1094
+		if (! $primary_reg) {
1095
+			EE_Error::add_error(
1096
+				esc_html__(
1097
+					'Cannot get billing info for gateway %s on transaction because no primary registration exists',
1098
+					'event_espresso'
1099
+				),
1100
+				__FILE__,
1101
+				__FUNCTION__,
1102
+				__LINE__
1103
+			);
1104
+			return null;
1105
+		}
1106
+		$attendee = $primary_reg->attendee();
1107
+		if (! $attendee) {
1108
+			EE_Error::add_error(
1109
+				esc_html__(
1110
+					'Cannot get billing info for gateway %s on transaction because the primary registration has no attendee exists',
1111
+					'event_espresso'
1112
+				),
1113
+				__FILE__,
1114
+				__FUNCTION__,
1115
+				__LINE__
1116
+			);
1117
+			return null;
1118
+		}
1119
+		return $attendee->billing_info_for_payment_method($payment_method);
1120
+	}
1121
+
1122
+
1123
+	/**
1124
+	 * Gets PMD_ID
1125
+	 *
1126
+	 * @return int
1127
+	 * @throws EE_Error
1128
+	 * @throws InvalidArgumentException
1129
+	 * @throws InvalidDataTypeException
1130
+	 * @throws InvalidInterfaceException
1131
+	 * @throws ReflectionException
1132
+	 */
1133
+	public function payment_method_ID()
1134
+	{
1135
+		return $this->get('PMD_ID');
1136
+	}
1137
+
1138
+
1139
+	/**
1140
+	 * Sets PMD_ID
1141
+	 *
1142
+	 * @param int $PMD_ID
1143
+	 * @throws EE_Error
1144
+	 * @throws InvalidArgumentException
1145
+	 * @throws InvalidDataTypeException
1146
+	 * @throws InvalidInterfaceException
1147
+	 * @throws ReflectionException
1148
+	 */
1149
+	public function set_payment_method_ID($PMD_ID)
1150
+	{
1151
+		$this->set('PMD_ID', $PMD_ID);
1152
+	}
1153
+
1154
+
1155
+	/**
1156
+	 * Gets the last-used payment method on this transaction
1157
+	 * (we COULD just use the last-made payment, but some payment methods, namely
1158
+	 * offline ones, dont' create payments)
1159
+	 *
1160
+	 * @return EE_Payment_Method
1161
+	 * @throws EE_Error
1162
+	 * @throws InvalidArgumentException
1163
+	 * @throws InvalidDataTypeException
1164
+	 * @throws InvalidInterfaceException
1165
+	 * @throws ReflectionException
1166
+	 */
1167
+	public function payment_method()
1168
+	{
1169
+		$pm = $this->get_first_related('Payment_Method');
1170
+		if ($pm instanceof EE_Payment_Method) {
1171
+			return $pm;
1172
+		}
1173
+		$last_payment = $this->last_payment();
1174
+		if ($last_payment instanceof EE_Payment && $last_payment->payment_method()) {
1175
+			return $last_payment->payment_method();
1176
+		}
1177
+		return null;
1178
+	}
1179
+
1180
+
1181
+	/**
1182
+	 * Gets the last payment made
1183
+	 *
1184
+	 * @return EE_Base_Class|EE_Payment
1185
+	 * @throws EE_Error
1186
+	 * @throws InvalidArgumentException
1187
+	 * @throws InvalidDataTypeException
1188
+	 * @throws InvalidInterfaceException
1189
+	 * @throws ReflectionException
1190
+	 */
1191
+	public function last_payment()
1192
+	{
1193
+		return $this->get_first_related('Payment', ['order_by' => ['PAY_ID' => 'desc']]);
1194
+	}
1195
+
1196
+
1197
+	/**
1198
+	 * Gets all the line items which are unrelated to tickets on this transaction
1199
+	 *
1200
+	 * @return EE_Line_Item[]
1201
+	 * @throws EE_Error
1202
+	 * @throws InvalidArgumentException
1203
+	 * @throws InvalidDataTypeException
1204
+	 * @throws InvalidInterfaceException
1205
+	 * @throws ReflectionException
1206
+	 */
1207
+	public function non_ticket_line_items()
1208
+	{
1209
+		return EEM_Line_Item::instance()->get_all_non_ticket_line_items_for_transaction($this->ID());
1210
+	}
1211
+
1212
+
1213
+	/**
1214
+	 * possibly toggles TXN status
1215
+	 *
1216
+	 * @param boolean $update whether to save the TXN
1217
+	 * @return bool whether the TXN was saved
1218
+	 * @throws EE_Error
1219
+	 * @throws InvalidArgumentException
1220
+	 * @throws InvalidDataTypeException
1221
+	 * @throws InvalidInterfaceException
1222
+	 * @throws ReflectionException
1223
+	 * @throws RuntimeException
1224
+	 */
1225
+	public function update_status_based_on_total_paid($update = true)
1226
+	{
1227
+		// set transaction status based on comparison of TXN_paid vs TXN_total
1228
+		if (EEH_Money::compare_floats($this->paid(), $this->total(), '>')) {
1229
+			$new_txn_status = EEM_Transaction::overpaid_status_code;
1230
+		} elseif (EEH_Money::compare_floats($this->paid(), $this->total())) {
1231
+			$new_txn_status = EEM_Transaction::complete_status_code;
1232
+		} elseif (EEH_Money::compare_floats($this->paid(), $this->total(), '<')) {
1233
+			$new_txn_status = EEM_Transaction::incomplete_status_code;
1234
+		} else {
1235
+			throw new RuntimeException(
1236
+				esc_html__('The total paid calculation for this transaction is inaccurate.', 'event_espresso')
1237
+			);
1238
+		}
1239
+		if ($new_txn_status !== $this->status_ID()) {
1240
+			$this->set_status($new_txn_status);
1241
+			if ($update) {
1242
+				return (bool) $this->save();
1243
+			}
1244
+		}
1245
+		return false;
1246
+	}
1247
+
1248
+
1249
+	/**
1250
+	 * Updates the transaction's status and total_paid based on all the payments
1251
+	 * that apply to it
1252
+	 *
1253
+	 * @return array|bool
1254
+	 * @throws EE_Error
1255
+	 * @throws InvalidArgumentException
1256
+	 * @throws ReflectionException
1257
+	 * @throws InvalidDataTypeException
1258
+	 * @throws InvalidInterfaceException
1259
+	 * @deprecated
1260
+	 */
1261
+	public function update_based_on_payments()
1262
+	{
1263
+		EE_Error::doing_it_wrong(
1264
+			__CLASS__ . '::' . __FUNCTION__,
1265
+			sprintf(
1266
+				esc_html__('This method is deprecated. Please use "%s" instead', 'event_espresso'),
1267
+				'EE_Transaction_Processor::update_transaction_and_registrations_after_checkout_or_payment()'
1268
+			),
1269
+			'4.6.0'
1270
+		);
1271
+		/** @type EE_Transaction_Processor $transaction_processor */
1272
+		$transaction_processor = EE_Registry::instance()->load_class('Transaction_Processor');
1273
+		return $transaction_processor->update_transaction_and_registrations_after_checkout_or_payment($this);
1274
+	}
1275
+
1276
+
1277
+	/**
1278
+	 * @return string
1279
+	 */
1280
+	public function old_txn_status()
1281
+	{
1282
+		return $this->_old_txn_status;
1283
+	}
1284
+
1285
+
1286
+	/**
1287
+	 * @param string $old_txn_status
1288
+	 */
1289
+	public function set_old_txn_status($old_txn_status)
1290
+	{
1291
+		// only set the first time
1292
+		if ($this->_old_txn_status === null) {
1293
+			$this->_old_txn_status = $old_txn_status;
1294
+		}
1295
+	}
1296
+
1297
+
1298
+	/**
1299
+	 * reg_status_updated
1300
+	 *
1301
+	 * @return bool
1302
+	 * @throws EE_Error
1303
+	 * @throws InvalidArgumentException
1304
+	 * @throws InvalidDataTypeException
1305
+	 * @throws InvalidInterfaceException
1306
+	 * @throws ReflectionException
1307
+	 */
1308
+	public function txn_status_updated()
1309
+	{
1310
+		return $this->status_ID() !== $this->_old_txn_status && $this->_old_txn_status !== null;
1311
+	}
1312
+
1313
+
1314
+	/**
1315
+	 * _reg_steps_completed
1316
+	 * if $check_all is TRUE, then returns TRUE if ALL reg steps have been marked as completed,
1317
+	 * if a $reg_step_slug is provided, then this step will be skipped when testing for completion
1318
+	 * if $check_all is FALSE and a $reg_step_slug is provided, then ONLY that reg step will be tested for completion
1319
+	 *
1320
+	 * @param string $reg_step_slug
1321
+	 * @param bool   $check_all
1322
+	 * @return bool|int
1323
+	 * @throws EE_Error
1324
+	 * @throws InvalidArgumentException
1325
+	 * @throws InvalidDataTypeException
1326
+	 * @throws InvalidInterfaceException
1327
+	 * @throws ReflectionException
1328
+	 */
1329
+	private function _reg_steps_completed($reg_step_slug = '', $check_all = true)
1330
+	{
1331
+		$reg_steps = $this->reg_steps();
1332
+		if (! is_array($reg_steps) || empty($reg_steps)) {
1333
+			return false;
1334
+		}
1335
+		// loop thru reg steps array)
1336
+		foreach ($reg_steps as $slug => $reg_step_completed) {
1337
+			// if NOT checking ALL steps (only checking one step)
1338
+			if (! $check_all) {
1339
+				// and this is the one
1340
+				if ($slug === $reg_step_slug) {
1341
+					return $reg_step_completed;
1342
+				}
1343
+				// skip to next reg step in loop
1344
+				continue;
1345
+			}
1346
+			// $check_all must be true, else we would never have gotten to this point
1347
+			if ($slug === $reg_step_slug) {
1348
+				// if we reach this point, then we are testing either:
1349
+				// all_reg_steps_completed_except() or
1350
+				// all_reg_steps_completed_except_final_step(),
1351
+				// and since this is the reg step EXCEPTION being tested
1352
+				// we want to return true (yes true) if this reg step is NOT completed
1353
+				// ie: "is everything completed except the final step?"
1354
+				// "that is correct... the final step is not completed, but all others are."
1355
+				return $reg_step_completed !== true;
1356
+			}
1357
+			if ($reg_step_completed !== true) {
1358
+				// if any reg step is NOT completed, then ALL steps are not completed
1359
+				return false;
1360
+			}
1361
+		}
1362
+		return true;
1363
+	}
1364
+
1365
+
1366
+	/**
1367
+	 * all_reg_steps_completed
1368
+	 * returns:
1369
+	 *    true if ALL reg steps have been marked as completed
1370
+	 *        or false if any step is not completed
1371
+	 *
1372
+	 * @return bool
1373
+	 * @throws EE_Error
1374
+	 * @throws InvalidArgumentException
1375
+	 * @throws InvalidDataTypeException
1376
+	 * @throws InvalidInterfaceException
1377
+	 * @throws ReflectionException
1378
+	 */
1379
+	public function all_reg_steps_completed()
1380
+	{
1381
+		return $this->_reg_steps_completed();
1382
+	}
1383
+
1384
+
1385
+	/**
1386
+	 * all_reg_steps_completed_except
1387
+	 * returns:
1388
+	 *        true if ALL reg steps, except a particular step that you wish to skip over, have been marked as completed
1389
+	 *        or false if any other step is not completed
1390
+	 *        or false if ALL steps are completed including the exception you are testing !!!
1391
+	 *
1392
+	 * @param string $exception
1393
+	 * @return bool
1394
+	 * @throws EE_Error
1395
+	 * @throws InvalidArgumentException
1396
+	 * @throws InvalidDataTypeException
1397
+	 * @throws InvalidInterfaceException
1398
+	 * @throws ReflectionException
1399
+	 */
1400
+	public function all_reg_steps_completed_except($exception = '')
1401
+	{
1402
+		return $this->_reg_steps_completed($exception);
1403
+	}
1404
+
1405
+
1406
+	/**
1407
+	 * all_reg_steps_completed_except
1408
+	 * returns:
1409
+	 *        true if ALL reg steps, except the final step, have been marked as completed
1410
+	 *        or false if any step is not completed
1411
+	 *    or false if ALL steps are completed including the final step !!!
1412
+	 *
1413
+	 * @return bool
1414
+	 * @throws EE_Error
1415
+	 * @throws InvalidArgumentException
1416
+	 * @throws InvalidDataTypeException
1417
+	 * @throws InvalidInterfaceException
1418
+	 * @throws ReflectionException
1419
+	 */
1420
+	public function all_reg_steps_completed_except_final_step()
1421
+	{
1422
+		return $this->_reg_steps_completed('finalize_registration');
1423
+	}
1424
+
1425
+
1426
+	/**
1427
+	 * reg_step_completed
1428
+	 * returns:
1429
+	 *    true if a specific reg step has been marked as completed
1430
+	 *    a Unix timestamp if it has been initialized but not yet completed,
1431
+	 *    or false if it has not yet been initialized
1432
+	 *
1433
+	 * @param string $reg_step_slug
1434
+	 * @return bool|int
1435
+	 * @throws EE_Error
1436
+	 * @throws InvalidArgumentException
1437
+	 * @throws InvalidDataTypeException
1438
+	 * @throws InvalidInterfaceException
1439
+	 * @throws ReflectionException
1440
+	 */
1441
+	public function reg_step_completed($reg_step_slug)
1442
+	{
1443
+		return $this->_reg_steps_completed($reg_step_slug, false);
1444
+	}
1445
+
1446
+
1447
+	/**
1448
+	 * completed_final_reg_step
1449
+	 * returns:
1450
+	 *    true if the finalize_registration reg step has been marked as completed
1451
+	 *    a Unix timestamp if it has been initialized but not yet completed,
1452
+	 *    or false if it has not yet been initialized
1453
+	 *
1454
+	 * @return bool|int
1455
+	 * @throws EE_Error
1456
+	 * @throws InvalidArgumentException
1457
+	 * @throws InvalidDataTypeException
1458
+	 * @throws InvalidInterfaceException
1459
+	 * @throws ReflectionException
1460
+	 */
1461
+	public function final_reg_step_completed()
1462
+	{
1463
+		return $this->_reg_steps_completed('finalize_registration', false);
1464
+	}
1465
+
1466
+
1467
+	/**
1468
+	 * set_reg_step_initiated
1469
+	 * given a valid TXN_reg_step, this sets it's value to a unix timestamp
1470
+	 *
1471
+	 * @param string $reg_step_slug
1472
+	 * @return boolean
1473
+	 * @throws EE_Error
1474
+	 * @throws InvalidArgumentException
1475
+	 * @throws InvalidDataTypeException
1476
+	 * @throws InvalidInterfaceException
1477
+	 * @throws ReflectionException
1478
+	 */
1479
+	public function set_reg_step_initiated($reg_step_slug)
1480
+	{
1481
+		return $this->_set_reg_step_completed_status($reg_step_slug, time());
1482
+	}
1483
+
1484
+
1485
+	/**
1486
+	 * set_reg_step_completed
1487
+	 * given a valid TXN_reg_step, this sets the step as completed
1488
+	 *
1489
+	 * @param string $reg_step_slug
1490
+	 * @return boolean
1491
+	 * @throws EE_Error
1492
+	 * @throws InvalidArgumentException
1493
+	 * @throws InvalidDataTypeException
1494
+	 * @throws InvalidInterfaceException
1495
+	 * @throws ReflectionException
1496
+	 */
1497
+	public function set_reg_step_completed($reg_step_slug)
1498
+	{
1499
+		return $this->_set_reg_step_completed_status($reg_step_slug, true);
1500
+	}
1501
+
1502
+
1503
+	/**
1504
+	 * set_reg_step_completed
1505
+	 * given a valid TXN_reg_step slug, this sets the step as NOT completed
1506
+	 *
1507
+	 * @param string $reg_step_slug
1508
+	 * @return boolean
1509
+	 * @throws EE_Error
1510
+	 * @throws InvalidArgumentException
1511
+	 * @throws InvalidDataTypeException
1512
+	 * @throws InvalidInterfaceException
1513
+	 * @throws ReflectionException
1514
+	 */
1515
+	public function set_reg_step_not_completed($reg_step_slug)
1516
+	{
1517
+		return $this->_set_reg_step_completed_status($reg_step_slug, false);
1518
+	}
1519
+
1520
+
1521
+	/**
1522
+	 * set_reg_step_completed
1523
+	 * given a valid reg step slug, this sets the TXN_reg_step completed status which is either:
1524
+	 *
1525
+	 * @param string      $reg_step_slug
1526
+	 * @param boolean|int $status
1527
+	 * @return boolean
1528
+	 * @throws EE_Error
1529
+	 * @throws InvalidArgumentException
1530
+	 * @throws InvalidDataTypeException
1531
+	 * @throws InvalidInterfaceException
1532
+	 * @throws ReflectionException
1533
+	 */
1534
+	private function _set_reg_step_completed_status($reg_step_slug, $status)
1535
+	{
1536
+		// validate status
1537
+		$status = is_bool($status) || is_int($status) ? $status : false;
1538
+		// get reg steps array
1539
+		$txn_reg_steps = $this->reg_steps();
1540
+		// if reg step does NOT exist
1541
+		if (! isset($txn_reg_steps[ $reg_step_slug ])) {
1542
+			return false;
1543
+		}
1544
+		// if  we're trying to complete a step that is already completed
1545
+		if ($txn_reg_steps[ $reg_step_slug ] === true) {
1546
+			return true;
1547
+		}
1548
+		// if  we're trying to complete a step that hasn't even started
1549
+		if ($status === true && $txn_reg_steps[ $reg_step_slug ] === false) {
1550
+			return false;
1551
+		}
1552
+		// if current status value matches the incoming value (no change)
1553
+		// type casting as int means values should collapse to either 0, 1, or a timestamp like 1234567890
1554
+		if ((int) $txn_reg_steps[ $reg_step_slug ] === (int) $status) {
1555
+			// this will happen in cases where multiple AJAX requests occur during the same step
1556
+			return true;
1557
+		}
1558
+		// if we're trying to set a start time, but it has already been set...
1559
+		if (is_numeric($status) && is_numeric($txn_reg_steps[ $reg_step_slug ])) {
1560
+			// skip the update below, but don't return FALSE so that errors won't be displayed
1561
+			return true;
1562
+		}
1563
+		// update completed status
1564
+		$txn_reg_steps[ $reg_step_slug ] = $status;
1565
+		$this->set_reg_steps($txn_reg_steps);
1566
+		$this->save();
1567
+		return true;
1568
+	}
1569
+
1570
+
1571
+	/**
1572
+	 * remove_reg_step
1573
+	 * given a valid TXN_reg_step slug, this will remove (unset)
1574
+	 * the reg step from the TXN reg step array
1575
+	 *
1576
+	 * @param string $reg_step_slug
1577
+	 * @return void
1578
+	 * @throws EE_Error
1579
+	 * @throws InvalidArgumentException
1580
+	 * @throws InvalidDataTypeException
1581
+	 * @throws InvalidInterfaceException
1582
+	 * @throws ReflectionException
1583
+	 */
1584
+	public function remove_reg_step($reg_step_slug)
1585
+	{
1586
+		// get reg steps array
1587
+		$txn_reg_steps = $this->reg_steps();
1588
+		unset($txn_reg_steps[ $reg_step_slug ]);
1589
+		$this->set_reg_steps($txn_reg_steps);
1590
+	}
1591
+
1592
+
1593
+	/**
1594
+	 * toggle_failed_transaction_status
1595
+	 * upgrades a TXNs status from failed to abandoned,
1596
+	 * meaning that contact information has been captured for at least one registrant
1597
+	 *
1598
+	 * @param bool $save
1599
+	 * @return bool
1600
+	 * @throws EE_Error
1601
+	 * @throws InvalidArgumentException
1602
+	 * @throws InvalidDataTypeException
1603
+	 * @throws InvalidInterfaceException
1604
+	 * @throws ReflectionException
1605
+	 */
1606
+	public function toggle_failed_transaction_status($save = true)
1607
+	{
1608
+		// if TXN status is still set as "failed"...
1609
+		if ($this->status_ID() === EEM_Transaction::failed_status_code) {
1610
+			$this->set_status(EEM_Transaction::abandoned_status_code);
1611
+			if ($save) {
1612
+				$this->save();
1613
+			}
1614
+			return true;
1615
+		}
1616
+		return false;
1617
+	}
1618
+
1619
+
1620
+	/**
1621
+	 * toggle_abandoned_transaction_status
1622
+	 * upgrades a TXNs status from failed or abandoned to incomplete
1623
+	 *
1624
+	 * @return bool
1625
+	 * @throws EE_Error
1626
+	 * @throws InvalidArgumentException
1627
+	 * @throws InvalidDataTypeException
1628
+	 * @throws InvalidInterfaceException
1629
+	 * @throws ReflectionException
1630
+	 */
1631
+	public function toggle_abandoned_transaction_status()
1632
+	{
1633
+		// if TXN status has not been updated already due to a payment, and is still set as "failed" or "abandoned"...
1634
+		$txn_status = $this->status_ID();
1635
+		if (
1636
+			$txn_status === EEM_Transaction::failed_status_code
1637
+			|| $txn_status === EEM_Transaction::abandoned_status_code
1638
+		) {
1639
+			// if a contact record for the primary registrant has been created
1640
+			if (
1641
+				$this->primary_registration() instanceof EE_Registration
1642
+				&& $this->primary_registration()->attendee() instanceof EE_Attendee
1643
+			) {
1644
+				$this->set_status(EEM_Transaction::incomplete_status_code);
1645
+			} else {
1646
+				// no contact record? yer abandoned!
1647
+				$this->set_status(EEM_Transaction::abandoned_status_code);
1648
+			}
1649
+			return true;
1650
+		}
1651
+		return false;
1652
+	}
1653
+
1654
+
1655
+	/**
1656
+	 * checks if an Abandoned TXN has any related payments, and if so,
1657
+	 * updates the TXN status based on the amount paid
1658
+	 *
1659
+	 * @throws EE_Error
1660
+	 * @throws InvalidArgumentException
1661
+	 * @throws InvalidDataTypeException
1662
+	 * @throws InvalidInterfaceException
1663
+	 * @throws ReflectionException
1664
+	 * @throws RuntimeException
1665
+	 * @throws ReflectionException
1666
+	 */
1667
+	public function verify_abandoned_transaction_status()
1668
+	{
1669
+		if ($this->status_ID() !== EEM_Transaction::abandoned_status_code) {
1670
+			return;
1671
+		}
1672
+		$payments = $this->get_many_related('Payment');
1673
+		if (! empty($payments)) {
1674
+			foreach ($payments as $payment) {
1675
+				if ($payment instanceof EE_Payment) {
1676
+					// kk this TXN should NOT be abandoned
1677
+					$this->update_status_based_on_total_paid();
1678
+					if (! (defined('DOING_AJAX') && DOING_AJAX) && is_admin()) {
1679
+						EE_Error::add_attention(
1680
+							sprintf(
1681
+								esc_html__(
1682
+									'The status for Transaction #%1$d has been updated from "Abandoned" to "%2$s", because at least one payment has been made towards it. If the payment appears in the "Payment Details" table below, you may need to edit its status and/or other details as well.',
1683
+									'event_espresso'
1684
+								),
1685
+								$this->ID(),
1686
+								$this->pretty_status()
1687
+							)
1688
+						);
1689
+					}
1690
+					// get final reg step status
1691
+					$finalized = $this->final_reg_step_completed();
1692
+					// if the 'finalize_registration' step has been initiated (has a timestamp)
1693
+					// but has not yet been fully completed (TRUE)
1694
+					if (is_int($finalized) && $finalized !== false && $finalized !== true) {
1695
+						$this->set_reg_step_completed('finalize_registration');
1696
+						$this->save();
1697
+					}
1698
+				}
1699
+			}
1700
+		}
1701
+	}
1702
+
1703
+
1704
+	/**
1705
+	 * @throws EE_Error
1706
+	 * @throws InvalidArgumentException
1707
+	 * @throws InvalidDataTypeException
1708
+	 * @throws InvalidInterfaceException
1709
+	 * @throws ReflectionException
1710
+	 * @throws RuntimeException
1711
+	 * @since 4.10.4.p
1712
+	 */
1713
+	public function recalculateLineItems()
1714
+	{
1715
+		$total_line_item = $this->total_line_item(false);
1716
+		if ($total_line_item instanceof EE_Line_Item) {
1717
+			EEH_Line_Item::resetIsTaxableForTickets($total_line_item);
1718
+			return EEH_Line_Item::apply_taxes($total_line_item, true);
1719
+		}
1720
+		return false;
1721
+	}
1722 1722
 }
Please login to merge, or discard this patch.
Spacing   +22 added lines, -22 removed lines patch added patch discarded remove patch
@@ -46,7 +46,7 @@  discard block
 block discarded – undo
46 46
         $txn        = $has_object
47 47
             ? $has_object
48 48
             : new self($props_n_values, false, $timezone, $date_formats);
49
-        if (! $has_object) {
49
+        if ( ! $has_object) {
50 50
             $txn->set_old_txn_status($txn->status_ID());
51 51
         }
52 52
         return $txn;
@@ -86,7 +86,7 @@  discard block
 block discarded – undo
86 86
     public function lock()
87 87
     {
88 88
         // attempt to set lock, but if that fails...
89
-        if (! $this->add_extra_meta('lock', time(), true)) {
89
+        if ( ! $this->add_extra_meta('lock', time(), true)) {
90 90
             // then attempt to remove the lock in case it is expired
91 91
             if ($this->_remove_expired_lock()) {
92 92
                 // if removal was successful, then try setting lock again
@@ -142,7 +142,7 @@  discard block
 block discarded – undo
142 142
     public function is_locked()
143 143
     {
144 144
         // if TXN is not locked, then return false immediately
145
-        if (! $this->_get_lock()) {
145
+        if ( ! $this->_get_lock()) {
146 146
             return false;
147 147
         }
148 148
         // if not, then let's try and remove the lock in case it's expired...
@@ -622,7 +622,7 @@  discard block
 block discarded – undo
622 622
             false,
623 623
             'sentence'
624 624
         );
625
-        $icon   = '';
625
+        $icon = '';
626 626
         switch ($this->status_ID()) {
627 627
             case EEM_Transaction::complete_status_code:
628 628
                 $icon = $show_icons ? '<span class="dashicons dashicons-yes ee-icon-size-24 green-text"></span>' : '';
@@ -641,7 +641,7 @@  discard block
 block discarded – undo
641 641
                 $icon = $show_icons ? '<span class="dashicons dashicons-plus ee-icon-size-16 orange-text"></span>' : '';
642 642
                 break;
643 643
         }
644
-        return $icon . $status[ $this->status_ID() ];
644
+        return $icon.$status[$this->status_ID()];
645 645
     }
646 646
 
647 647
 
@@ -779,7 +779,7 @@  discard block
 block discarded – undo
779 779
     public function invoice_url($type = 'html')
780 780
     {
781 781
         $REG = $this->primary_registration();
782
-        if (! $REG instanceof EE_Registration) {
782
+        if ( ! $REG instanceof EE_Registration) {
783 783
             return '';
784 784
         }
785 785
         return $REG->invoice_url($type);
@@ -831,7 +831,7 @@  discard block
 block discarded – undo
831 831
     public function receipt_url($type = 'html')
832 832
     {
833 833
         $REG = $this->primary_registration();
834
-        if (! $REG instanceof EE_Registration) {
834
+        if ( ! $REG instanceof EE_Registration) {
835 835
             return '';
836 836
         }
837 837
         return $REG->receipt_url($type);
@@ -1078,7 +1078,7 @@  discard block
 block discarded – undo
1078 1078
     public function billing_info()
1079 1079
     {
1080 1080
         $payment_method = $this->payment_method();
1081
-        if (! $payment_method) {
1081
+        if ( ! $payment_method) {
1082 1082
             EE_Error::add_error(
1083 1083
                 esc_html__(
1084 1084
                     'Could not find billing info for transaction because no gateway has been used for it yet',
@@ -1091,7 +1091,7 @@  discard block
 block discarded – undo
1091 1091
             return null;
1092 1092
         }
1093 1093
         $primary_reg = $this->primary_registration();
1094
-        if (! $primary_reg) {
1094
+        if ( ! $primary_reg) {
1095 1095
             EE_Error::add_error(
1096 1096
                 esc_html__(
1097 1097
                     'Cannot get billing info for gateway %s on transaction because no primary registration exists',
@@ -1104,7 +1104,7 @@  discard block
 block discarded – undo
1104 1104
             return null;
1105 1105
         }
1106 1106
         $attendee = $primary_reg->attendee();
1107
-        if (! $attendee) {
1107
+        if ( ! $attendee) {
1108 1108
             EE_Error::add_error(
1109 1109
                 esc_html__(
1110 1110
                     'Cannot get billing info for gateway %s on transaction because the primary registration has no attendee exists',
@@ -1261,7 +1261,7 @@  discard block
 block discarded – undo
1261 1261
     public function update_based_on_payments()
1262 1262
     {
1263 1263
         EE_Error::doing_it_wrong(
1264
-            __CLASS__ . '::' . __FUNCTION__,
1264
+            __CLASS__.'::'.__FUNCTION__,
1265 1265
             sprintf(
1266 1266
                 esc_html__('This method is deprecated. Please use "%s" instead', 'event_espresso'),
1267 1267
                 'EE_Transaction_Processor::update_transaction_and_registrations_after_checkout_or_payment()'
@@ -1329,13 +1329,13 @@  discard block
 block discarded – undo
1329 1329
     private function _reg_steps_completed($reg_step_slug = '', $check_all = true)
1330 1330
     {
1331 1331
         $reg_steps = $this->reg_steps();
1332
-        if (! is_array($reg_steps) || empty($reg_steps)) {
1332
+        if ( ! is_array($reg_steps) || empty($reg_steps)) {
1333 1333
             return false;
1334 1334
         }
1335 1335
         // loop thru reg steps array)
1336 1336
         foreach ($reg_steps as $slug => $reg_step_completed) {
1337 1337
             // if NOT checking ALL steps (only checking one step)
1338
-            if (! $check_all) {
1338
+            if ( ! $check_all) {
1339 1339
                 // and this is the one
1340 1340
                 if ($slug === $reg_step_slug) {
1341 1341
                     return $reg_step_completed;
@@ -1538,30 +1538,30 @@  discard block
 block discarded – undo
1538 1538
         // get reg steps array
1539 1539
         $txn_reg_steps = $this->reg_steps();
1540 1540
         // if reg step does NOT exist
1541
-        if (! isset($txn_reg_steps[ $reg_step_slug ])) {
1541
+        if ( ! isset($txn_reg_steps[$reg_step_slug])) {
1542 1542
             return false;
1543 1543
         }
1544 1544
         // if  we're trying to complete a step that is already completed
1545
-        if ($txn_reg_steps[ $reg_step_slug ] === true) {
1545
+        if ($txn_reg_steps[$reg_step_slug] === true) {
1546 1546
             return true;
1547 1547
         }
1548 1548
         // if  we're trying to complete a step that hasn't even started
1549
-        if ($status === true && $txn_reg_steps[ $reg_step_slug ] === false) {
1549
+        if ($status === true && $txn_reg_steps[$reg_step_slug] === false) {
1550 1550
             return false;
1551 1551
         }
1552 1552
         // if current status value matches the incoming value (no change)
1553 1553
         // type casting as int means values should collapse to either 0, 1, or a timestamp like 1234567890
1554
-        if ((int) $txn_reg_steps[ $reg_step_slug ] === (int) $status) {
1554
+        if ((int) $txn_reg_steps[$reg_step_slug] === (int) $status) {
1555 1555
             // this will happen in cases where multiple AJAX requests occur during the same step
1556 1556
             return true;
1557 1557
         }
1558 1558
         // if we're trying to set a start time, but it has already been set...
1559
-        if (is_numeric($status) && is_numeric($txn_reg_steps[ $reg_step_slug ])) {
1559
+        if (is_numeric($status) && is_numeric($txn_reg_steps[$reg_step_slug])) {
1560 1560
             // skip the update below, but don't return FALSE so that errors won't be displayed
1561 1561
             return true;
1562 1562
         }
1563 1563
         // update completed status
1564
-        $txn_reg_steps[ $reg_step_slug ] = $status;
1564
+        $txn_reg_steps[$reg_step_slug] = $status;
1565 1565
         $this->set_reg_steps($txn_reg_steps);
1566 1566
         $this->save();
1567 1567
         return true;
@@ -1585,7 +1585,7 @@  discard block
 block discarded – undo
1585 1585
     {
1586 1586
         // get reg steps array
1587 1587
         $txn_reg_steps = $this->reg_steps();
1588
-        unset($txn_reg_steps[ $reg_step_slug ]);
1588
+        unset($txn_reg_steps[$reg_step_slug]);
1589 1589
         $this->set_reg_steps($txn_reg_steps);
1590 1590
     }
1591 1591
 
@@ -1670,12 +1670,12 @@  discard block
 block discarded – undo
1670 1670
             return;
1671 1671
         }
1672 1672
         $payments = $this->get_many_related('Payment');
1673
-        if (! empty($payments)) {
1673
+        if ( ! empty($payments)) {
1674 1674
             foreach ($payments as $payment) {
1675 1675
                 if ($payment instanceof EE_Payment) {
1676 1676
                     // kk this TXN should NOT be abandoned
1677 1677
                     $this->update_status_based_on_total_paid();
1678
-                    if (! (defined('DOING_AJAX') && DOING_AJAX) && is_admin()) {
1678
+                    if ( ! (defined('DOING_AJAX') && DOING_AJAX) && is_admin()) {
1679 1679
                         EE_Error::add_attention(
1680 1680
                             sprintf(
1681 1681
                                 esc_html__(
Please login to merge, or discard this patch.
core/db_classes/EE_Registration.class.php 2 patches
Indentation   +2484 added lines, -2484 removed lines patch added patch discarded remove patch
@@ -16,2488 +16,2488 @@
 block discarded – undo
16 16
  */
17 17
 class EE_Registration extends EE_Soft_Delete_Base_Class implements EEI_Registration, EEI_Admin_Links
18 18
 {
19
-    /**
20
-     * Used to reference when a registration has never been checked in.
21
-     *
22
-     * @deprecated use \EE_Checkin::status_checked_never instead
23
-     * @type int
24
-     */
25
-    const checkin_status_never = 2;
26
-
27
-    /**
28
-     * Used to reference when a registration has been checked in.
29
-     *
30
-     * @deprecated use \EE_Checkin::status_checked_in instead
31
-     * @type int
32
-     */
33
-    const checkin_status_in = 1;
34
-
35
-    /**
36
-     * Used to reference when a registration has been checked out.
37
-     *
38
-     * @deprecated use \EE_Checkin::status_checked_out instead
39
-     * @type int
40
-     */
41
-    const checkin_status_out = 0;
42
-
43
-    /**
44
-     * extra meta key for tracking reg status os trashed registrations
45
-     *
46
-     * @type string
47
-     */
48
-    const PRE_TRASH_REG_STATUS_KEY = 'pre_trash_registration_status';
49
-
50
-    /**
51
-     * extra meta key for tracking if registration has reserved ticket
52
-     *
53
-     * @type string
54
-     */
55
-    const HAS_RESERVED_TICKET_KEY = 'has_reserved_ticket';
56
-
57
-
58
-    /**
59
-     * @param array  $props_n_values          incoming values
60
-     * @param string $timezone                incoming timezone (if not set the timezone set for the website will be
61
-     *                                        used.)
62
-     * @param array  $date_formats            incoming date_formats in an array where the first value is the
63
-     *                                        date_format and the second value is the time format
64
-     * @return EE_Registration
65
-     * @throws EE_Error
66
-     * @throws InvalidArgumentException
67
-     * @throws InvalidDataTypeException
68
-     * @throws InvalidInterfaceException
69
-     * @throws ReflectionException
70
-     */
71
-    public static function new_instance($props_n_values = array(), $timezone = null, $date_formats = array())
72
-    {
73
-        $has_object = parent::_check_for_object($props_n_values, __CLASS__, $timezone, $date_formats);
74
-        return $has_object ? $has_object : new self($props_n_values, false, $timezone, $date_formats);
75
-    }
76
-
77
-
78
-    /**
79
-     * @param array  $props_n_values  incoming values from the database
80
-     * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
81
-     *                                the website will be used.
82
-     * @return EE_Registration
83
-     * @throws EE_Error
84
-     * @throws InvalidArgumentException
85
-     * @throws InvalidDataTypeException
86
-     * @throws InvalidInterfaceException
87
-     * @throws ReflectionException
88
-     */
89
-    public static function new_instance_from_db($props_n_values = array(), $timezone = null)
90
-    {
91
-        return new self($props_n_values, true, $timezone);
92
-    }
93
-
94
-
95
-    /**
96
-     *        Set Event ID
97
-     *
98
-     * @param        int $EVT_ID Event ID
99
-     * @throws DomainException
100
-     * @throws EE_Error
101
-     * @throws EntityNotFoundException
102
-     * @throws InvalidArgumentException
103
-     * @throws InvalidDataTypeException
104
-     * @throws InvalidInterfaceException
105
-     * @throws ReflectionException
106
-     * @throws RuntimeException
107
-     * @throws UnexpectedEntityException
108
-     */
109
-    public function set_event($EVT_ID = 0)
110
-    {
111
-        $this->set('EVT_ID', $EVT_ID);
112
-    }
113
-
114
-
115
-    /**
116
-     * Overrides parent set() method so that all calls to set( 'REG_code', $REG_code ) OR set( 'STS_ID', $STS_ID ) can
117
-     * be routed to internal methods
118
-     *
119
-     * @param string $field_name
120
-     * @param mixed  $field_value
121
-     * @param bool   $use_default
122
-     * @throws DomainException
123
-     * @throws EE_Error
124
-     * @throws EntityNotFoundException
125
-     * @throws InvalidArgumentException
126
-     * @throws InvalidDataTypeException
127
-     * @throws InvalidInterfaceException
128
-     * @throws ReflectionException
129
-     * @throws RuntimeException
130
-     * @throws UnexpectedEntityException
131
-     */
132
-    public function set($field_name, $field_value, $use_default = false)
133
-    {
134
-        switch ($field_name) {
135
-            case 'REG_code':
136
-                if (! empty($field_value) && $this->reg_code() === null) {
137
-                    $this->set_reg_code($field_value, $use_default);
138
-                }
139
-                break;
140
-            case 'STS_ID':
141
-                $this->set_status($field_value, $use_default);
142
-                break;
143
-            default:
144
-                parent::set($field_name, $field_value, $use_default);
145
-        }
146
-    }
147
-
148
-
149
-    /**
150
-     * Set Status ID
151
-     * updates the registration status and ALSO...
152
-     * calls reserve_registration_space() if the reg status changes TO approved from any other reg status
153
-     * calls release_registration_space() if the reg status changes FROM approved to any other reg status
154
-     *
155
-     * @param string                $new_STS_ID
156
-     * @param boolean               $use_default
157
-     * @param ContextInterface|null $context
158
-     * @return bool
159
-     * @throws DomainException
160
-     * @throws EE_Error
161
-     * @throws EntityNotFoundException
162
-     * @throws InvalidArgumentException
163
-     * @throws InvalidDataTypeException
164
-     * @throws InvalidInterfaceException
165
-     * @throws ReflectionException
166
-     * @throws RuntimeException
167
-     * @throws UnexpectedEntityException
168
-     */
169
-    public function set_status($new_STS_ID = null, $use_default = false, ContextInterface $context = null)
170
-    {
171
-        // get current REG_Status
172
-        $old_STS_ID = $this->status_ID();
173
-        // if status has changed
174
-        if (
175
-            $old_STS_ID !== $new_STS_ID // and that status has actually changed
176
-            && ! empty($old_STS_ID) // and that old status is actually set
177
-            && ! empty($new_STS_ID) // as well as the new status
178
-            && $this->ID() // ensure registration is in the db
179
-        ) {
180
-            // update internal status first
181
-            parent::set('STS_ID', $new_STS_ID, $use_default);
182
-            // THEN handle other changes that occur when reg status changes
183
-            // TO approved
184
-            if ($new_STS_ID === EEM_Registration::status_id_approved) {
185
-                // reserve a space by incrementing ticket and datetime sold values
186
-                $this->reserveRegistrationSpace();
187
-                do_action('AHEE__EE_Registration__set_status__to_approved', $this, $old_STS_ID, $new_STS_ID, $context);
188
-                // OR FROM  approved
189
-            } elseif ($old_STS_ID === EEM_Registration::status_id_approved) {
190
-                // release a space by decrementing ticket and datetime sold values
191
-                $this->releaseRegistrationSpace();
192
-                do_action(
193
-                    'AHEE__EE_Registration__set_status__from_approved',
194
-                    $this,
195
-                    $old_STS_ID,
196
-                    $new_STS_ID,
197
-                    $context
198
-                );
199
-            }
200
-            // update status
201
-            parent::set('STS_ID', $new_STS_ID, $use_default);
202
-            $this->updateIfCanceledOrReinstated($new_STS_ID, $old_STS_ID, $context);
203
-            if ($this->statusChangeUpdatesTransaction($context)) {
204
-                $this->updateTransactionAfterStatusChange();
205
-            }
206
-            do_action('AHEE__EE_Registration__set_status__after_update', $this, $old_STS_ID, $new_STS_ID, $context);
207
-            return true;
208
-        }
209
-        // even though the old value matches the new value, it's still good to
210
-        // allow the parent set method to have a say
211
-        parent::set('STS_ID', $new_STS_ID, $use_default);
212
-        return true;
213
-    }
214
-
215
-
216
-    /**
217
-     * update REGs and TXN when cancelled or declined registrations involved
218
-     *
219
-     * @param string                $new_STS_ID
220
-     * @param string                $old_STS_ID
221
-     * @param ContextInterface|null $context
222
-     * @throws EE_Error
223
-     * @throws InvalidArgumentException
224
-     * @throws InvalidDataTypeException
225
-     * @throws InvalidInterfaceException
226
-     * @throws ReflectionException
227
-     * @throws RuntimeException
228
-     */
229
-    private function updateIfCanceledOrReinstated($new_STS_ID, $old_STS_ID, ContextInterface $context = null)
230
-    {
231
-        // these reg statuses should not be considered in any calculations involving monies owing
232
-        $closed_reg_statuses = EEM_Registration::closed_reg_statuses();
233
-        // true if registration has been cancelled or declined
234
-        $this->updateIfCanceled(
235
-            $closed_reg_statuses,
236
-            $new_STS_ID,
237
-            $old_STS_ID,
238
-            $context
239
-        );
240
-        $this->updateIfReinstated(
241
-            $closed_reg_statuses,
242
-            $new_STS_ID,
243
-            $old_STS_ID,
244
-            $context
245
-        );
246
-    }
247
-
248
-
249
-    /**
250
-     * update REGs and TXN when cancelled or declined registrations involved
251
-     *
252
-     * @param array                 $closed_reg_statuses
253
-     * @param string                $new_STS_ID
254
-     * @param string                $old_STS_ID
255
-     * @param ContextInterface|null $context
256
-     * @throws EE_Error
257
-     * @throws InvalidArgumentException
258
-     * @throws InvalidDataTypeException
259
-     * @throws InvalidInterfaceException
260
-     * @throws ReflectionException
261
-     * @throws RuntimeException
262
-     */
263
-    private function updateIfCanceled(
264
-        array $closed_reg_statuses,
265
-        $new_STS_ID,
266
-        $old_STS_ID,
267
-        ContextInterface $context = null
268
-    ) {
269
-        // true if registration has been cancelled or declined
270
-        if (
271
-            in_array($new_STS_ID, $closed_reg_statuses, true)
272
-            && ! in_array($old_STS_ID, $closed_reg_statuses, true)
273
-        ) {
274
-            /** @type EE_Registration_Processor $registration_processor */
275
-            $registration_processor = EE_Registry::instance()->load_class('Registration_Processor');
276
-            /** @type EE_Transaction_Processor $transaction_processor */
277
-            $transaction_processor = EE_Registry::instance()->load_class('Transaction_Processor');
278
-            // cancelled or declined registration
279
-            $registration_processor->update_registration_after_being_canceled_or_declined(
280
-                $this,
281
-                $closed_reg_statuses
282
-            );
283
-            $transaction_processor->update_transaction_after_canceled_or_declined_registration(
284
-                $this,
285
-                $closed_reg_statuses,
286
-                false
287
-            );
288
-            do_action(
289
-                'AHEE__EE_Registration__set_status__canceled_or_declined',
290
-                $this,
291
-                $old_STS_ID,
292
-                $new_STS_ID,
293
-                $context
294
-            );
295
-            return;
296
-        }
297
-    }
298
-
299
-
300
-    /**
301
-     * update REGs and TXN when cancelled or declined registrations involved
302
-     *
303
-     * @param array                 $closed_reg_statuses
304
-     * @param string                $new_STS_ID
305
-     * @param string                $old_STS_ID
306
-     * @param ContextInterface|null $context
307
-     * @throws EE_Error
308
-     * @throws InvalidArgumentException
309
-     * @throws InvalidDataTypeException
310
-     * @throws InvalidInterfaceException
311
-     * @throws ReflectionException
312
-     * @throws RuntimeException
313
-     */
314
-    private function updateIfReinstated(
315
-        array $closed_reg_statuses,
316
-        $new_STS_ID,
317
-        $old_STS_ID,
318
-        ContextInterface $context = null
319
-    ) {
320
-        // true if reinstating cancelled or declined registration
321
-        if (
322
-            in_array($old_STS_ID, $closed_reg_statuses, true)
323
-            && ! in_array($new_STS_ID, $closed_reg_statuses, true)
324
-        ) {
325
-            /** @type EE_Registration_Processor $registration_processor */
326
-            $registration_processor = EE_Registry::instance()->load_class('Registration_Processor');
327
-            /** @type EE_Transaction_Processor $transaction_processor */
328
-            $transaction_processor = EE_Registry::instance()->load_class('Transaction_Processor');
329
-            // reinstating cancelled or declined registration
330
-            $registration_processor->update_canceled_or_declined_registration_after_being_reinstated(
331
-                $this,
332
-                $closed_reg_statuses
333
-            );
334
-            $transaction_processor->update_transaction_after_reinstating_canceled_registration(
335
-                $this,
336
-                $closed_reg_statuses,
337
-                false
338
-            );
339
-            do_action(
340
-                'AHEE__EE_Registration__set_status__after_reinstated',
341
-                $this,
342
-                $old_STS_ID,
343
-                $new_STS_ID,
344
-                $context
345
-            );
346
-        }
347
-    }
348
-
349
-
350
-    /**
351
-     * @param ContextInterface|null $context
352
-     * @return bool
353
-     */
354
-    private function statusChangeUpdatesTransaction(ContextInterface $context = null)
355
-    {
356
-        $contexts_that_do_not_update_transaction = (array) apply_filters(
357
-            'AHEE__EE_Registration__statusChangeUpdatesTransaction__contexts_that_do_not_update_transaction',
358
-            array('spco_reg_step_attendee_information_process_registrations'),
359
-            $context,
360
-            $this
361
-        );
362
-        return ! (
363
-            $context instanceof ContextInterface
364
-            && in_array($context->slug(), $contexts_that_do_not_update_transaction, true)
365
-        );
366
-    }
367
-
368
-
369
-    /**
370
-     * @throws EE_Error
371
-     * @throws EntityNotFoundException
372
-     * @throws InvalidArgumentException
373
-     * @throws InvalidDataTypeException
374
-     * @throws InvalidInterfaceException
375
-     * @throws ReflectionException
376
-     * @throws RuntimeException
377
-     */
378
-    private function updateTransactionAfterStatusChange()
379
-    {
380
-        /** @type EE_Transaction_Payments $transaction_payments */
381
-        $transaction_payments = EE_Registry::instance()->load_class('Transaction_Payments');
382
-        $transaction_payments->recalculate_transaction_total($this->transaction(), false);
383
-        $this->transaction()->update_status_based_on_total_paid();
384
-    }
385
-
386
-
387
-    /**
388
-     * get Status ID
389
-     *
390
-     * @throws EE_Error
391
-     * @throws InvalidArgumentException
392
-     * @throws InvalidDataTypeException
393
-     * @throws InvalidInterfaceException
394
-     * @throws ReflectionException
395
-     */
396
-    public function status_ID()
397
-    {
398
-        return $this->get('STS_ID');
399
-    }
400
-
401
-
402
-    /**
403
-     * Gets the ticket this registration is for
404
-     *
405
-     * @param boolean $include_archived whether to include archived tickets or not.
406
-     * @return EE_Ticket|EE_Base_Class
407
-     * @throws EE_Error
408
-     * @throws InvalidArgumentException
409
-     * @throws InvalidDataTypeException
410
-     * @throws InvalidInterfaceException
411
-     * @throws ReflectionException
412
-     */
413
-    public function ticket($include_archived = true)
414
-    {
415
-        $query_params = array();
416
-        if ($include_archived) {
417
-            $query_params['default_where_conditions'] = 'none';
418
-        }
419
-        return $this->get_first_related('Ticket', $query_params);
420
-    }
421
-
422
-
423
-    /**
424
-     * Gets the event this registration is for
425
-     *
426
-     * @return EE_Event
427
-     * @throws EE_Error
428
-     * @throws EntityNotFoundException
429
-     * @throws InvalidArgumentException
430
-     * @throws InvalidDataTypeException
431
-     * @throws InvalidInterfaceException
432
-     * @throws ReflectionException
433
-     */
434
-    public function event()
435
-    {
436
-        $event = $this->get_first_related('Event');
437
-        if (! $event instanceof \EE_Event) {
438
-            throw new EntityNotFoundException('Event ID', $this->event_ID());
439
-        }
440
-        return $event;
441
-    }
442
-
443
-
444
-    /**
445
-     * Gets the "author" of the registration.  Note that for the purposes of registrations, the author will correspond
446
-     * with the author of the event this registration is for.
447
-     *
448
-     * @since 4.5.0
449
-     * @return int
450
-     * @throws EE_Error
451
-     * @throws EntityNotFoundException
452
-     * @throws InvalidArgumentException
453
-     * @throws InvalidDataTypeException
454
-     * @throws InvalidInterfaceException
455
-     * @throws ReflectionException
456
-     */
457
-    public function wp_user()
458
-    {
459
-        $event = $this->event();
460
-        if ($event instanceof EE_Event) {
461
-            return $event->wp_user();
462
-        }
463
-        return 0;
464
-    }
465
-
466
-
467
-    /**
468
-     * increments this registration's related ticket sold and corresponding datetime sold values
469
-     *
470
-     * @return void
471
-     * @throws DomainException
472
-     * @throws EE_Error
473
-     * @throws EntityNotFoundException
474
-     * @throws InvalidArgumentException
475
-     * @throws InvalidDataTypeException
476
-     * @throws InvalidInterfaceException
477
-     * @throws ReflectionException
478
-     * @throws UnexpectedEntityException
479
-     */
480
-    private function reserveRegistrationSpace()
481
-    {
482
-        // reserved ticket and datetime counts will be decremented as sold counts are incremented
483
-        // so stop tracking that this reg has a ticket reserved
484
-        $this->release_reserved_ticket(false, "REG: {$this->ID()} (ln:" . __LINE__ . ')');
485
-        $ticket = $this->ticket();
486
-        $ticket->increaseSold();
487
-        // possibly set event status to sold out
488
-        $this->event()->perform_sold_out_status_check();
489
-    }
490
-
491
-
492
-    /**
493
-     * decrements (subtracts) this registration's related ticket sold and corresponding datetime sold values
494
-     *
495
-     * @return void
496
-     * @throws DomainException
497
-     * @throws EE_Error
498
-     * @throws EntityNotFoundException
499
-     * @throws InvalidArgumentException
500
-     * @throws InvalidDataTypeException
501
-     * @throws InvalidInterfaceException
502
-     * @throws ReflectionException
503
-     * @throws UnexpectedEntityException
504
-     */
505
-    private function releaseRegistrationSpace()
506
-    {
507
-        $ticket = $this->ticket();
508
-        $ticket->decreaseSold();
509
-        // possibly change event status from sold out back to previous status
510
-        $this->event()->perform_sold_out_status_check();
511
-    }
512
-
513
-
514
-    /**
515
-     * tracks this registration's ticket reservation in extra meta
516
-     * and can increment related ticket reserved and corresponding datetime reserved values
517
-     *
518
-     * @param bool   $update_ticket if true, will increment ticket and datetime reserved count
519
-     * @param string $source
520
-     * @return void
521
-     * @throws EE_Error
522
-     * @throws InvalidArgumentException
523
-     * @throws InvalidDataTypeException
524
-     * @throws InvalidInterfaceException
525
-     * @throws ReflectionException
526
-     */
527
-    public function reserve_ticket($update_ticket = false, $source = 'unknown')
528
-    {
529
-        // only reserve ticket if space is not currently reserved
530
-        if ((bool) $this->get_extra_meta(EE_Registration::HAS_RESERVED_TICKET_KEY, true) !== true) {
531
-            $reserved = $this->update_extra_meta(EE_Registration::HAS_RESERVED_TICKET_KEY, true);
532
-            if ($reserved && $update_ticket) {
533
-                $ticket = $this->ticket();
534
-                $ticket->increaseReserved(1, "REG: {$this->ID()} (ln:" . __LINE__ . ')');
535
-                $this->update_extra_meta('reserve_ticket', "{$this->ticket_ID()} from {$source}");
536
-                $ticket->save();
537
-            }
538
-        }
539
-    }
540
-
541
-
542
-    /**
543
-     * stops tracking this registration's ticket reservation in extra meta
544
-     * decrements (subtracts) related ticket reserved and corresponding datetime reserved values
545
-     *
546
-     * @param bool   $update_ticket if true, will decrement ticket and datetime reserved count
547
-     * @param string $source
548
-     * @return void
549
-     * @throws EE_Error
550
-     * @throws InvalidArgumentException
551
-     * @throws InvalidDataTypeException
552
-     * @throws InvalidInterfaceException
553
-     * @throws ReflectionException
554
-     */
555
-    public function release_reserved_ticket($update_ticket = false, $source = 'unknown')
556
-    {
557
-        // only release ticket if space is currently reserved
558
-        if ((bool) $this->get_extra_meta(EE_Registration::HAS_RESERVED_TICKET_KEY, true) === true) {
559
-            $reserved = $this->update_extra_meta(EE_Registration::HAS_RESERVED_TICKET_KEY, false);
560
-            if ($reserved && $update_ticket) {
561
-                $ticket = $this->ticket();
562
-                $ticket->decreaseReserved(1, true, "REG: {$this->ID()} (ln:" . __LINE__ . ')');
563
-                $this->update_extra_meta('release_reserved_ticket', "{$this->ticket_ID()} from {$source}");
564
-            }
565
-        }
566
-    }
567
-
568
-
569
-    /**
570
-     * Set Attendee ID
571
-     *
572
-     * @param        int $ATT_ID Attendee ID
573
-     * @throws DomainException
574
-     * @throws EE_Error
575
-     * @throws EntityNotFoundException
576
-     * @throws InvalidArgumentException
577
-     * @throws InvalidDataTypeException
578
-     * @throws InvalidInterfaceException
579
-     * @throws ReflectionException
580
-     * @throws RuntimeException
581
-     * @throws UnexpectedEntityException
582
-     */
583
-    public function set_attendee_id($ATT_ID = 0)
584
-    {
585
-        $this->set('ATT_ID', $ATT_ID);
586
-    }
587
-
588
-
589
-    /**
590
-     *        Set Transaction ID
591
-     *
592
-     * @param        int $TXN_ID Transaction ID
593
-     * @throws DomainException
594
-     * @throws EE_Error
595
-     * @throws EntityNotFoundException
596
-     * @throws InvalidArgumentException
597
-     * @throws InvalidDataTypeException
598
-     * @throws InvalidInterfaceException
599
-     * @throws ReflectionException
600
-     * @throws RuntimeException
601
-     * @throws UnexpectedEntityException
602
-     */
603
-    public function set_transaction_id($TXN_ID = 0)
604
-    {
605
-        $this->set('TXN_ID', $TXN_ID);
606
-    }
607
-
608
-
609
-    /**
610
-     *        Set Session
611
-     *
612
-     * @param    string $REG_session PHP Session ID
613
-     * @throws DomainException
614
-     * @throws EE_Error
615
-     * @throws EntityNotFoundException
616
-     * @throws InvalidArgumentException
617
-     * @throws InvalidDataTypeException
618
-     * @throws InvalidInterfaceException
619
-     * @throws ReflectionException
620
-     * @throws RuntimeException
621
-     * @throws UnexpectedEntityException
622
-     */
623
-    public function set_session($REG_session = '')
624
-    {
625
-        $this->set('REG_session', $REG_session);
626
-    }
627
-
628
-
629
-    /**
630
-     *        Set Registration URL Link
631
-     *
632
-     * @param    string $REG_url_link Registration URL Link
633
-     * @throws DomainException
634
-     * @throws EE_Error
635
-     * @throws EntityNotFoundException
636
-     * @throws InvalidArgumentException
637
-     * @throws InvalidDataTypeException
638
-     * @throws InvalidInterfaceException
639
-     * @throws ReflectionException
640
-     * @throws RuntimeException
641
-     * @throws UnexpectedEntityException
642
-     */
643
-    public function set_reg_url_link($REG_url_link = '')
644
-    {
645
-        $this->set('REG_url_link', $REG_url_link);
646
-    }
647
-
648
-
649
-    /**
650
-     *        Set Attendee Counter
651
-     *
652
-     * @param        int $REG_count Primary Attendee
653
-     * @throws DomainException
654
-     * @throws EE_Error
655
-     * @throws EntityNotFoundException
656
-     * @throws InvalidArgumentException
657
-     * @throws InvalidDataTypeException
658
-     * @throws InvalidInterfaceException
659
-     * @throws ReflectionException
660
-     * @throws RuntimeException
661
-     * @throws UnexpectedEntityException
662
-     */
663
-    public function set_count($REG_count = 1)
664
-    {
665
-        $this->set('REG_count', $REG_count);
666
-    }
667
-
668
-
669
-    /**
670
-     *        Set Group Size
671
-     *
672
-     * @param        boolean $REG_group_size Group Registration
673
-     * @throws DomainException
674
-     * @throws EE_Error
675
-     * @throws EntityNotFoundException
676
-     * @throws InvalidArgumentException
677
-     * @throws InvalidDataTypeException
678
-     * @throws InvalidInterfaceException
679
-     * @throws ReflectionException
680
-     * @throws RuntimeException
681
-     * @throws UnexpectedEntityException
682
-     */
683
-    public function set_group_size($REG_group_size = false)
684
-    {
685
-        $this->set('REG_group_size', $REG_group_size);
686
-    }
687
-
688
-
689
-    /**
690
-     *    is_not_approved -  convenience method that returns TRUE if REG status ID ==
691
-     *    EEM_Registration::status_id_not_approved
692
-     *
693
-     * @return        boolean
694
-     * @throws EE_Error
695
-     * @throws InvalidArgumentException
696
-     * @throws InvalidDataTypeException
697
-     * @throws InvalidInterfaceException
698
-     * @throws ReflectionException
699
-     */
700
-    public function is_not_approved()
701
-    {
702
-        return $this->status_ID() === EEM_Registration::status_id_not_approved;
703
-    }
704
-
705
-
706
-    /**
707
-     *    is_pending_payment -  convenience method that returns TRUE if REG status ID ==
708
-     *    EEM_Registration::status_id_pending_payment
709
-     *
710
-     * @return        boolean
711
-     * @throws EE_Error
712
-     * @throws InvalidArgumentException
713
-     * @throws InvalidDataTypeException
714
-     * @throws InvalidInterfaceException
715
-     * @throws ReflectionException
716
-     */
717
-    public function is_pending_payment()
718
-    {
719
-        return $this->status_ID() === EEM_Registration::status_id_pending_payment;
720
-    }
721
-
722
-
723
-    /**
724
-     *    is_approved -  convenience method that returns TRUE if REG status ID == EEM_Registration::status_id_approved
725
-     *
726
-     * @return        boolean
727
-     * @throws EE_Error
728
-     * @throws InvalidArgumentException
729
-     * @throws InvalidDataTypeException
730
-     * @throws InvalidInterfaceException
731
-     * @throws ReflectionException
732
-     */
733
-    public function is_approved()
734
-    {
735
-        return $this->status_ID() === EEM_Registration::status_id_approved;
736
-    }
737
-
738
-
739
-    /**
740
-     *    is_cancelled -  convenience method that returns TRUE if REG status ID == EEM_Registration::status_id_cancelled
741
-     *
742
-     * @return        boolean
743
-     * @throws EE_Error
744
-     * @throws InvalidArgumentException
745
-     * @throws InvalidDataTypeException
746
-     * @throws InvalidInterfaceException
747
-     * @throws ReflectionException
748
-     */
749
-    public function is_cancelled()
750
-    {
751
-        return $this->status_ID() === EEM_Registration::status_id_cancelled;
752
-    }
753
-
754
-
755
-    /**
756
-     *    is_declined -  convenience method that returns TRUE if REG status ID == EEM_Registration::status_id_declined
757
-     *
758
-     * @return        boolean
759
-     * @throws EE_Error
760
-     * @throws InvalidArgumentException
761
-     * @throws InvalidDataTypeException
762
-     * @throws InvalidInterfaceException
763
-     * @throws ReflectionException
764
-     */
765
-    public function is_declined()
766
-    {
767
-        return $this->status_ID() === EEM_Registration::status_id_declined;
768
-    }
769
-
770
-
771
-    /**
772
-     *    is_incomplete -  convenience method that returns TRUE if REG status ID ==
773
-     *    EEM_Registration::status_id_incomplete
774
-     *
775
-     * @return        boolean
776
-     * @throws EE_Error
777
-     * @throws InvalidArgumentException
778
-     * @throws InvalidDataTypeException
779
-     * @throws InvalidInterfaceException
780
-     * @throws ReflectionException
781
-     */
782
-    public function is_incomplete()
783
-    {
784
-        return $this->status_ID() === EEM_Registration::status_id_incomplete;
785
-    }
786
-
787
-
788
-    /**
789
-     *        Set Registration Date
790
-     *
791
-     * @param        mixed ( int or string ) $REG_date Registration Date - Unix timestamp or string representation of
792
-     *                                                 Date
793
-     * @throws DomainException
794
-     * @throws EE_Error
795
-     * @throws EntityNotFoundException
796
-     * @throws InvalidArgumentException
797
-     * @throws InvalidDataTypeException
798
-     * @throws InvalidInterfaceException
799
-     * @throws ReflectionException
800
-     * @throws RuntimeException
801
-     * @throws UnexpectedEntityException
802
-     */
803
-    public function set_reg_date($REG_date = false)
804
-    {
805
-        $this->set('REG_date', $REG_date);
806
-    }
807
-
808
-
809
-    /**
810
-     *    Set final price owing for this registration after all ticket/price modifications
811
-     *
812
-     * @param    float $REG_final_price
813
-     * @throws DomainException
814
-     * @throws EE_Error
815
-     * @throws EntityNotFoundException
816
-     * @throws InvalidArgumentException
817
-     * @throws InvalidDataTypeException
818
-     * @throws InvalidInterfaceException
819
-     * @throws ReflectionException
820
-     * @throws RuntimeException
821
-     * @throws UnexpectedEntityException
822
-     */
823
-    public function set_final_price($REG_final_price = 0.00)
824
-    {
825
-        $this->set('REG_final_price', $REG_final_price);
826
-    }
827
-
828
-
829
-    /**
830
-     *    Set amount paid towards this registration's final price
831
-     *
832
-     * @param    float $REG_paid
833
-     * @throws DomainException
834
-     * @throws EE_Error
835
-     * @throws EntityNotFoundException
836
-     * @throws InvalidArgumentException
837
-     * @throws InvalidDataTypeException
838
-     * @throws InvalidInterfaceException
839
-     * @throws ReflectionException
840
-     * @throws RuntimeException
841
-     * @throws UnexpectedEntityException
842
-     */
843
-    public function set_paid($REG_paid = 0.00)
844
-    {
845
-        $this->set('REG_paid', $REG_paid);
846
-    }
847
-
848
-
849
-    /**
850
-     *        Attendee Is Going
851
-     *
852
-     * @param        boolean $REG_att_is_going Attendee Is Going
853
-     * @throws DomainException
854
-     * @throws EE_Error
855
-     * @throws EntityNotFoundException
856
-     * @throws InvalidArgumentException
857
-     * @throws InvalidDataTypeException
858
-     * @throws InvalidInterfaceException
859
-     * @throws ReflectionException
860
-     * @throws RuntimeException
861
-     * @throws UnexpectedEntityException
862
-     */
863
-    public function set_att_is_going($REG_att_is_going = false)
864
-    {
865
-        $this->set('REG_att_is_going', $REG_att_is_going);
866
-    }
867
-
868
-
869
-    /**
870
-     * Gets the related attendee
871
-     *
872
-     * @return EE_Attendee|EE_Base_Class
873
-     * @throws EE_Error
874
-     * @throws InvalidArgumentException
875
-     * @throws InvalidDataTypeException
876
-     * @throws InvalidInterfaceException
877
-     * @throws ReflectionException
878
-     */
879
-    public function attendee()
880
-    {
881
-        return $this->get_first_related('Attendee');
882
-    }
883
-
884
-    /**
885
-     * Gets the name of the attendee.
886
-     * @since 4.10.12.p
887
-     * @param bool $apply_html_entities set to true if you want to use HTML entities.
888
-     * @return string
889
-     * @throws EE_Error
890
-     * @throws InvalidArgumentException
891
-     * @throws InvalidDataTypeException
892
-     * @throws InvalidInterfaceException
893
-     * @throws ReflectionException
894
-     */
895
-    public function attendeeName($apply_html_entities = false)
896
-    {
897
-        $attendee = $this->get_first_related('Attendee');
898
-        if ($attendee instanceof EE_Attendee) {
899
-            $attendee_name = $attendee->full_name($apply_html_entities);
900
-        } else {
901
-            $attendee_name = esc_html__('Unknown', 'event_espresso');
902
-        }
903
-        return $attendee_name;
904
-    }
905
-
906
-
907
-    /**
908
-     *        get Event ID
909
-     */
910
-    public function event_ID()
911
-    {
912
-        return $this->get('EVT_ID');
913
-    }
914
-
915
-
916
-    /**
917
-     *        get Event ID
918
-     */
919
-    public function event_name()
920
-    {
921
-        $event = $this->event_obj();
922
-        if ($event) {
923
-            return $event->name();
924
-        } else {
925
-            return null;
926
-        }
927
-    }
928
-
929
-
930
-    /**
931
-     * Fetches the event this registration is for
932
-     *
933
-     * @return EE_Base_Class|EE_Event
934
-     * @throws EE_Error
935
-     * @throws InvalidArgumentException
936
-     * @throws InvalidDataTypeException
937
-     * @throws InvalidInterfaceException
938
-     * @throws ReflectionException
939
-     */
940
-    public function event_obj()
941
-    {
942
-        return $this->get_first_related('Event');
943
-    }
944
-
945
-
946
-    /**
947
-     *        get Attendee ID
948
-     */
949
-    public function attendee_ID()
950
-    {
951
-        return $this->get('ATT_ID');
952
-    }
953
-
954
-
955
-    /**
956
-     *        get PHP Session ID
957
-     */
958
-    public function session_ID()
959
-    {
960
-        return $this->get('REG_session');
961
-    }
962
-
963
-
964
-    /**
965
-     * Gets the string which represents the URL trigger for the receipt template in the message template system.
966
-     *
967
-     * @param string $messenger 'pdf' or 'html'.  Default 'html'.
968
-     * @return string
969
-     * @throws DomainException
970
-     * @throws EE_Error
971
-     * @throws InvalidArgumentException
972
-     * @throws InvalidDataTypeException
973
-     * @throws InvalidInterfaceException
974
-     * @throws ReflectionException
975
-     */
976
-    public function receipt_url($messenger = 'html')
977
-    {
978
-
979
-        /**
980
-         * The below will be deprecated one version after this.  We check first if there is a custom receipt template
981
-         * already in use on old system.  If there is then we just return the standard url for it.
982
-         *
983
-         * @since 4.5.0
984
-         */
985
-        $template_relative_path = 'modules/gateways/Invoice/lib/templates/receipt_body.template.php';
986
-        $has_custom = EEH_Template::locate_template(
987
-            $template_relative_path,
988
-            array(),
989
-            true,
990
-            true,
991
-            true
992
-        );
993
-
994
-        if ($has_custom) {
995
-            return add_query_arg(array('receipt' => 'true'), $this->invoice_url('launch'));
996
-        }
997
-        return apply_filters('FHEE__EE_Registration__receipt_url__receipt_url', '', $this, $messenger, 'receipt');
998
-    }
999
-
1000
-
1001
-    /**
1002
-     * Gets the string which represents the URL trigger for the invoice template in the message template system.
1003
-     *
1004
-     * @param string $messenger 'pdf' or 'html'.  Default 'html'.
1005
-     * @return string
1006
-     * @throws DomainException
1007
-     * @throws EE_Error
1008
-     * @throws InvalidArgumentException
1009
-     * @throws InvalidDataTypeException
1010
-     * @throws InvalidInterfaceException
1011
-     * @throws ReflectionException
1012
-     */
1013
-    public function invoice_url($messenger = 'html')
1014
-    {
1015
-        /**
1016
-         * The below will be deprecated one version after this.  We check first if there is a custom invoice template
1017
-         * already in use on old system.  If there is then we just return the standard url for it.
1018
-         *
1019
-         * @since 4.5.0
1020
-         */
1021
-        $template_relative_path = 'modules/gateways/Invoice/lib/templates/invoice_body.template.php';
1022
-        $has_custom = EEH_Template::locate_template(
1023
-            $template_relative_path,
1024
-            array(),
1025
-            true,
1026
-            true,
1027
-            true
1028
-        );
1029
-
1030
-        if ($has_custom) {
1031
-            if ($messenger == 'html') {
1032
-                return $this->invoice_url('launch');
1033
-            }
1034
-            $route = $messenger == 'download' || $messenger == 'pdf' ? 'download_invoice' : 'launch_invoice';
1035
-
1036
-            $query_args = array('ee' => $route, 'id' => $this->reg_url_link());
1037
-            if ($messenger == 'html') {
1038
-                $query_args['html'] = true;
1039
-            }
1040
-            return add_query_arg($query_args, get_permalink(EE_Registry::instance()->CFG->core->thank_you_page_id));
1041
-        }
1042
-        return apply_filters('FHEE__EE_Registration__invoice_url__invoice_url', '', $this, $messenger, 'invoice');
1043
-    }
1044
-
1045
-
1046
-    /**
1047
-     * get Registration URL Link
1048
-     *
1049
-     * @return string
1050
-     * @throws EE_Error
1051
-     * @throws InvalidArgumentException
1052
-     * @throws InvalidDataTypeException
1053
-     * @throws InvalidInterfaceException
1054
-     * @throws ReflectionException
1055
-     */
1056
-    public function reg_url_link()
1057
-    {
1058
-        return (string) $this->get('REG_url_link');
1059
-    }
1060
-
1061
-
1062
-    /**
1063
-     * Echoes out invoice_url()
1064
-     *
1065
-     * @param string $type 'download','launch', or 'html' (default is 'launch')
1066
-     * @return void
1067
-     * @throws DomainException
1068
-     * @throws EE_Error
1069
-     * @throws InvalidArgumentException
1070
-     * @throws InvalidDataTypeException
1071
-     * @throws InvalidInterfaceException
1072
-     * @throws ReflectionException
1073
-     */
1074
-    public function e_invoice_url($type = 'launch')
1075
-    {
1076
-        echo esc_url_raw($this->invoice_url($type));
1077
-    }
1078
-
1079
-
1080
-    /**
1081
-     * Echoes out payment_overview_url
1082
-     */
1083
-    public function e_payment_overview_url()
1084
-    {
1085
-        echo esc_url_raw($this->payment_overview_url());
1086
-    }
1087
-
1088
-
1089
-    /**
1090
-     * Gets the URL for the checkout payment options reg step
1091
-     * with this registration's REG_url_link added as a query parameter
1092
-     *
1093
-     * @param bool $clear_session Set to true when you want to clear the session on revisiting the
1094
-     *                            payment overview url.
1095
-     * @return string
1096
-     * @throws EE_Error
1097
-     * @throws InvalidArgumentException
1098
-     * @throws InvalidDataTypeException
1099
-     * @throws InvalidInterfaceException
1100
-     * @throws ReflectionException
1101
-     */
1102
-    public function payment_overview_url($clear_session = false)
1103
-    {
1104
-        return add_query_arg(
1105
-            (array) apply_filters(
1106
-                'FHEE__EE_Registration__payment_overview_url__query_args',
1107
-                array(
1108
-                    'e_reg_url_link' => $this->reg_url_link(),
1109
-                    'step'           => 'payment_options',
1110
-                    'revisit'        => true,
1111
-                    'clear_session'  => (bool) $clear_session,
1112
-                ),
1113
-                $this
1114
-            ),
1115
-            EE_Registry::instance()->CFG->core->reg_page_url()
1116
-        );
1117
-    }
1118
-
1119
-
1120
-    /**
1121
-     * Gets the URL for the checkout attendee information reg step
1122
-     * with this registration's REG_url_link added as a query parameter
1123
-     *
1124
-     * @return string
1125
-     * @throws EE_Error
1126
-     * @throws InvalidArgumentException
1127
-     * @throws InvalidDataTypeException
1128
-     * @throws InvalidInterfaceException
1129
-     * @throws ReflectionException
1130
-     */
1131
-    public function edit_attendee_information_url()
1132
-    {
1133
-        return add_query_arg(
1134
-            (array) apply_filters(
1135
-                'FHEE__EE_Registration__edit_attendee_information_url__query_args',
1136
-                array(
1137
-                    'e_reg_url_link' => $this->reg_url_link(),
1138
-                    'step'           => 'attendee_information',
1139
-                    'revisit'        => true,
1140
-                ),
1141
-                $this
1142
-            ),
1143
-            EE_Registry::instance()->CFG->core->reg_page_url()
1144
-        );
1145
-    }
1146
-
1147
-
1148
-    /**
1149
-     * Simply generates and returns the appropriate admin_url link to edit this registration
1150
-     *
1151
-     * @return string
1152
-     * @throws EE_Error
1153
-     * @throws InvalidArgumentException
1154
-     * @throws InvalidDataTypeException
1155
-     * @throws InvalidInterfaceException
1156
-     * @throws ReflectionException
1157
-     */
1158
-    public function get_admin_edit_url()
1159
-    {
1160
-        return EEH_URL::add_query_args_and_nonce(
1161
-            array(
1162
-                'page'    => 'espresso_registrations',
1163
-                'action'  => 'view_registration',
1164
-                '_REG_ID' => $this->ID(),
1165
-            ),
1166
-            admin_url('admin.php')
1167
-        );
1168
-    }
1169
-
1170
-
1171
-    /**
1172
-     * is_primary_registrant?
1173
-     *
1174
-     * @throws EE_Error
1175
-     * @throws InvalidArgumentException
1176
-     * @throws InvalidDataTypeException
1177
-     * @throws InvalidInterfaceException
1178
-     * @throws ReflectionException
1179
-     */
1180
-    public function is_primary_registrant()
1181
-    {
1182
-        return (int) $this->get('REG_count') === 1;
1183
-    }
1184
-
1185
-
1186
-    /**
1187
-     * This returns the primary registration object for this registration group (which may be this object).
1188
-     *
1189
-     * @return EE_Registration
1190
-     * @throws EE_Error
1191
-     * @throws InvalidArgumentException
1192
-     * @throws InvalidDataTypeException
1193
-     * @throws InvalidInterfaceException
1194
-     * @throws ReflectionException
1195
-     */
1196
-    public function get_primary_registration()
1197
-    {
1198
-        if ($this->is_primary_registrant()) {
1199
-            return $this;
1200
-        }
1201
-
1202
-        // k reg_count !== 1 so let's get the EE_Registration object matching this txn_id and reg_count == 1
1203
-        /** @var EE_Registration $primary_registrant */
1204
-        $primary_registrant = EEM_Registration::instance()->get_one(
1205
-            array(
1206
-                array(
1207
-                    'TXN_ID'    => $this->transaction_ID(),
1208
-                    'REG_count' => 1,
1209
-                ),
1210
-            )
1211
-        );
1212
-        return $primary_registrant;
1213
-    }
1214
-
1215
-
1216
-    /**
1217
-     * get  Attendee Number
1218
-     *
1219
-     * @throws EE_Error
1220
-     * @throws InvalidArgumentException
1221
-     * @throws InvalidDataTypeException
1222
-     * @throws InvalidInterfaceException
1223
-     * @throws ReflectionException
1224
-     */
1225
-    public function count()
1226
-    {
1227
-        return $this->get('REG_count');
1228
-    }
1229
-
1230
-
1231
-    /**
1232
-     * get Group Size
1233
-     *
1234
-     * @throws EE_Error
1235
-     * @throws InvalidArgumentException
1236
-     * @throws InvalidDataTypeException
1237
-     * @throws InvalidInterfaceException
1238
-     * @throws ReflectionException
1239
-     */
1240
-    public function group_size()
1241
-    {
1242
-        return $this->get('REG_group_size');
1243
-    }
1244
-
1245
-
1246
-    /**
1247
-     * get Registration Date
1248
-     *
1249
-     * @throws EE_Error
1250
-     * @throws InvalidArgumentException
1251
-     * @throws InvalidDataTypeException
1252
-     * @throws InvalidInterfaceException
1253
-     * @throws ReflectionException
1254
-     */
1255
-    public function date()
1256
-    {
1257
-        return $this->get('REG_date');
1258
-    }
1259
-
1260
-
1261
-    /**
1262
-     * gets a pretty date
1263
-     *
1264
-     * @param string $date_format
1265
-     * @param string $time_format
1266
-     * @return string
1267
-     * @throws EE_Error
1268
-     * @throws InvalidArgumentException
1269
-     * @throws InvalidDataTypeException
1270
-     * @throws InvalidInterfaceException
1271
-     * @throws ReflectionException
1272
-     */
1273
-    public function pretty_date($date_format = null, $time_format = null)
1274
-    {
1275
-        return $this->get_datetime('REG_date', $date_format, $time_format);
1276
-    }
1277
-
1278
-
1279
-    /**
1280
-     * final_price
1281
-     * the registration's share of the transaction total, so that the
1282
-     * sum of all the transaction's REG_final_prices equal the transaction's total
1283
-     *
1284
-     * @return float
1285
-     * @throws EE_Error
1286
-     * @throws InvalidArgumentException
1287
-     * @throws InvalidDataTypeException
1288
-     * @throws InvalidInterfaceException
1289
-     * @throws ReflectionException
1290
-     */
1291
-    public function final_price()
1292
-    {
1293
-        return $this->get('REG_final_price');
1294
-    }
1295
-
1296
-
1297
-    /**
1298
-     * pretty_final_price
1299
-     *  final price as formatted string, with correct decimal places and currency symbol
1300
-     *
1301
-     * @return string
1302
-     * @throws EE_Error
1303
-     * @throws InvalidArgumentException
1304
-     * @throws InvalidDataTypeException
1305
-     * @throws InvalidInterfaceException
1306
-     * @throws ReflectionException
1307
-     */
1308
-    public function pretty_final_price()
1309
-    {
1310
-        return $this->get_pretty('REG_final_price');
1311
-    }
1312
-
1313
-
1314
-    /**
1315
-     * get paid (yeah)
1316
-     *
1317
-     * @return float
1318
-     * @throws EE_Error
1319
-     * @throws InvalidArgumentException
1320
-     * @throws InvalidDataTypeException
1321
-     * @throws InvalidInterfaceException
1322
-     * @throws ReflectionException
1323
-     */
1324
-    public function paid()
1325
-    {
1326
-        return $this->get('REG_paid');
1327
-    }
1328
-
1329
-
1330
-    /**
1331
-     * pretty_paid
1332
-     *
1333
-     * @return float
1334
-     * @throws EE_Error
1335
-     * @throws InvalidArgumentException
1336
-     * @throws InvalidDataTypeException
1337
-     * @throws InvalidInterfaceException
1338
-     * @throws ReflectionException
1339
-     */
1340
-    public function pretty_paid()
1341
-    {
1342
-        return $this->get_pretty('REG_paid');
1343
-    }
1344
-
1345
-
1346
-    /**
1347
-     * owes_monies_and_can_pay
1348
-     * whether or not this registration has monies owing and it's' status allows payment
1349
-     *
1350
-     * @param array $requires_payment
1351
-     * @return bool
1352
-     * @throws EE_Error
1353
-     * @throws InvalidArgumentException
1354
-     * @throws InvalidDataTypeException
1355
-     * @throws InvalidInterfaceException
1356
-     * @throws ReflectionException
1357
-     */
1358
-    public function owes_monies_and_can_pay($requires_payment = array())
1359
-    {
1360
-        // these reg statuses require payment (if event is not free)
1361
-        $requires_payment = ! empty($requires_payment)
1362
-            ? $requires_payment
1363
-            : EEM_Registration::reg_statuses_that_allow_payment();
1364
-        if (
1365
-            $this->final_price() !== 0 &&
1366
-            $this->final_price() !== $this->paid() &&
1367
-            in_array($this->status_ID(), $requires_payment)
1368
-        ) {
1369
-            return true;
1370
-        }
1371
-        return false;
1372
-    }
1373
-
1374
-
1375
-    /**
1376
-     * Prints out the return value of $this->pretty_status()
1377
-     *
1378
-     * @param bool $show_icons
1379
-     * @return void
1380
-     * @throws EE_Error
1381
-     * @throws InvalidArgumentException
1382
-     * @throws InvalidDataTypeException
1383
-     * @throws InvalidInterfaceException
1384
-     * @throws ReflectionException
1385
-     */
1386
-    public function e_pretty_status($show_icons = false)
1387
-    {
1388
-        echo wp_kses($this->pretty_status($show_icons), AllowedTags::getAllowedTags());
1389
-    }
1390
-
1391
-
1392
-    /**
1393
-     * Returns a nice version of the status for displaying to customers
1394
-     *
1395
-     * @param bool $show_icons
1396
-     * @return string
1397
-     * @throws EE_Error
1398
-     * @throws InvalidArgumentException
1399
-     * @throws InvalidDataTypeException
1400
-     * @throws InvalidInterfaceException
1401
-     * @throws ReflectionException
1402
-     */
1403
-    public function pretty_status($show_icons = false)
1404
-    {
1405
-        $status = EEM_Status::instance()->localized_status(
1406
-            array($this->status_ID() => esc_html__('unknown', 'event_espresso')),
1407
-            false,
1408
-            'sentence'
1409
-        );
1410
-        $icon = '';
1411
-        switch ($this->status_ID()) {
1412
-            case EEM_Registration::status_id_approved:
1413
-                $icon = $show_icons
1414
-                    ? '<span class="dashicons dashicons-star-filled ee-icon-size-16 green-text"></span>'
1415
-                    : '';
1416
-                break;
1417
-            case EEM_Registration::status_id_pending_payment:
1418
-                $icon = $show_icons
1419
-                    ? '<span class="dashicons dashicons-star-half ee-icon-size-16 orange-text"></span>'
1420
-                    : '';
1421
-                break;
1422
-            case EEM_Registration::status_id_not_approved:
1423
-                $icon = $show_icons
1424
-                    ? '<span class="dashicons dashicons-marker ee-icon-size-16 orange-text"></span>'
1425
-                    : '';
1426
-                break;
1427
-            case EEM_Registration::status_id_cancelled:
1428
-                $icon = $show_icons
1429
-                    ? '<span class="dashicons dashicons-no ee-icon-size-16 lt-grey-text"></span>'
1430
-                    : '';
1431
-                break;
1432
-            case EEM_Registration::status_id_incomplete:
1433
-                $icon = $show_icons
1434
-                    ? '<span class="dashicons dashicons-no ee-icon-size-16 lt-orange-text"></span>'
1435
-                    : '';
1436
-                break;
1437
-            case EEM_Registration::status_id_declined:
1438
-                $icon = $show_icons
1439
-                    ? '<span class="dashicons dashicons-no ee-icon-size-16 red-text"></span>'
1440
-                    : '';
1441
-                break;
1442
-            case EEM_Registration::status_id_wait_list:
1443
-                $icon = $show_icons
1444
-                    ? '<span class="dashicons dashicons-clipboard ee-icon-size-16 purple-text"></span>'
1445
-                    : '';
1446
-                break;
1447
-        }
1448
-        return $icon . $status[ $this->status_ID() ];
1449
-    }
1450
-
1451
-
1452
-    /**
1453
-     *        get Attendee Is Going
1454
-     */
1455
-    public function att_is_going()
1456
-    {
1457
-        return $this->get('REG_att_is_going');
1458
-    }
1459
-
1460
-
1461
-    /**
1462
-     * Gets related answers
1463
-     *
1464
-     * @param array $query_params @see
1465
-     *                            https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1466
-     * @return EE_Answer[]|EE_Base_Class[]
1467
-     * @throws EE_Error
1468
-     * @throws InvalidArgumentException
1469
-     * @throws InvalidDataTypeException
1470
-     * @throws InvalidInterfaceException
1471
-     * @throws ReflectionException
1472
-     */
1473
-    public function answers($query_params = null)
1474
-    {
1475
-        return $this->get_many_related('Answer', $query_params);
1476
-    }
1477
-
1478
-
1479
-    /**
1480
-     * Gets the registration's answer value to the specified question
1481
-     * (either the question's ID or a question object)
1482
-     *
1483
-     * @param EE_Question|int $question
1484
-     * @param bool            $pretty_value
1485
-     * @return array|string if pretty_value= true, the result will always be a string
1486
-     * (because the answer might be an array of answer values, so passing pretty_value=true
1487
-     * will convert it into some kind of string)
1488
-     * @throws EE_Error
1489
-     * @throws InvalidArgumentException
1490
-     * @throws InvalidDataTypeException
1491
-     * @throws InvalidInterfaceException
1492
-     */
1493
-    public function answer_value_to_question($question, $pretty_value = true)
1494
-    {
1495
-        $question_id = EEM_Question::instance()->ensure_is_ID($question);
1496
-        return EEM_Answer::instance()->get_answer_value_to_question($this, $question_id, $pretty_value);
1497
-    }
1498
-
1499
-
1500
-    /**
1501
-     * question_groups
1502
-     * returns an array of EE_Question_Group objects for this registration
1503
-     *
1504
-     * @return EE_Question_Group[]
1505
-     * @throws EE_Error
1506
-     * @throws InvalidArgumentException
1507
-     * @throws InvalidDataTypeException
1508
-     * @throws InvalidInterfaceException
1509
-     * @throws ReflectionException
1510
-     */
1511
-    public function question_groups()
1512
-    {
1513
-        return EEM_Event::instance()->get_question_groups_for_event($this->event_ID(), $this);
1514
-    }
1515
-
1516
-
1517
-    /**
1518
-     * count_question_groups
1519
-     * returns a count of the number of EE_Question_Group objects for this registration
1520
-     *
1521
-     * @return int
1522
-     * @throws EE_Error
1523
-     * @throws EntityNotFoundException
1524
-     * @throws InvalidArgumentException
1525
-     * @throws InvalidDataTypeException
1526
-     * @throws InvalidInterfaceException
1527
-     * @throws ReflectionException
1528
-     */
1529
-    public function count_question_groups()
1530
-    {
1531
-        return EEM_Event::instance()->count_related(
1532
-            $this->event_ID(),
1533
-            'Question_Group',
1534
-            [
1535
-                [
1536
-                    'Event_Question_Group.'
1537
-                    . EEM_Event_Question_Group::instance()->fieldNameForContext($this->is_primary_registrant()) => true,
1538
-                ]
1539
-            ]
1540
-        );
1541
-    }
1542
-
1543
-
1544
-    /**
1545
-     * Returns the registration date in the 'standard' string format
1546
-     * (function may be improved in the future to allow for different formats and timezones)
1547
-     *
1548
-     * @return string
1549
-     * @throws EE_Error
1550
-     * @throws InvalidArgumentException
1551
-     * @throws InvalidDataTypeException
1552
-     * @throws InvalidInterfaceException
1553
-     * @throws ReflectionException
1554
-     */
1555
-    public function reg_date()
1556
-    {
1557
-        return $this->get_datetime('REG_date');
1558
-    }
1559
-
1560
-
1561
-    /**
1562
-     * Gets the datetime-ticket for this registration (ie, it can be used to isolate
1563
-     * the ticket this registration purchased, or the datetime they have registered
1564
-     * to attend)
1565
-     *
1566
-     * @return EE_Base_Class|EE_Datetime_Ticket
1567
-     * @throws EE_Error
1568
-     * @throws InvalidArgumentException
1569
-     * @throws InvalidDataTypeException
1570
-     * @throws InvalidInterfaceException
1571
-     * @throws ReflectionException
1572
-     */
1573
-    public function datetime_ticket()
1574
-    {
1575
-        return $this->get_first_related('Datetime_Ticket');
1576
-    }
1577
-
1578
-
1579
-    /**
1580
-     * Sets the registration's datetime_ticket.
1581
-     *
1582
-     * @param EE_Datetime_Ticket $datetime_ticket
1583
-     * @return EE_Base_Class|EE_Datetime_Ticket
1584
-     * @throws EE_Error
1585
-     * @throws InvalidArgumentException
1586
-     * @throws InvalidDataTypeException
1587
-     * @throws InvalidInterfaceException
1588
-     * @throws ReflectionException
1589
-     */
1590
-    public function set_datetime_ticket($datetime_ticket)
1591
-    {
1592
-        return $this->_add_relation_to($datetime_ticket, 'Datetime_Ticket');
1593
-    }
1594
-
1595
-
1596
-    /**
1597
-     * Gets deleted
1598
-     *
1599
-     * @return bool
1600
-     * @throws EE_Error
1601
-     * @throws InvalidArgumentException
1602
-     * @throws InvalidDataTypeException
1603
-     * @throws InvalidInterfaceException
1604
-     * @throws ReflectionException
1605
-     */
1606
-    public function deleted()
1607
-    {
1608
-        return $this->get('REG_deleted');
1609
-    }
1610
-
1611
-
1612
-    /**
1613
-     * Sets deleted
1614
-     *
1615
-     * @param boolean $deleted
1616
-     * @return void
1617
-     * @throws DomainException
1618
-     * @throws EE_Error
1619
-     * @throws EntityNotFoundException
1620
-     * @throws InvalidArgumentException
1621
-     * @throws InvalidDataTypeException
1622
-     * @throws InvalidInterfaceException
1623
-     * @throws ReflectionException
1624
-     * @throws RuntimeException
1625
-     * @throws UnexpectedEntityException
1626
-     */
1627
-    public function set_deleted($deleted)
1628
-    {
1629
-        if ($deleted) {
1630
-            $this->delete();
1631
-        } else {
1632
-            $this->restore();
1633
-        }
1634
-    }
1635
-
1636
-
1637
-    /**
1638
-     * Get the status object of this object
1639
-     *
1640
-     * @return EE_Base_Class|EE_Status
1641
-     * @throws EE_Error
1642
-     * @throws InvalidArgumentException
1643
-     * @throws InvalidDataTypeException
1644
-     * @throws InvalidInterfaceException
1645
-     * @throws ReflectionException
1646
-     */
1647
-    public function status_obj()
1648
-    {
1649
-        return $this->get_first_related('Status');
1650
-    }
1651
-
1652
-
1653
-    /**
1654
-     * Returns the number of times this registration has checked into any of the datetimes
1655
-     * its available for
1656
-     *
1657
-     * @return int
1658
-     * @throws EE_Error
1659
-     * @throws InvalidArgumentException
1660
-     * @throws InvalidDataTypeException
1661
-     * @throws InvalidInterfaceException
1662
-     * @throws ReflectionException
1663
-     */
1664
-    public function count_checkins()
1665
-    {
1666
-        return $this->get_model()->count_related($this, 'Checkin');
1667
-    }
1668
-
1669
-
1670
-    /**
1671
-     * Returns the number of current Check-ins this registration is checked into for any of the datetimes the
1672
-     * registration is for.  Note, this is ONLY checked in (does not include checkedout)
1673
-     *
1674
-     * @return int
1675
-     * @throws EE_Error
1676
-     * @throws InvalidArgumentException
1677
-     * @throws InvalidDataTypeException
1678
-     * @throws InvalidInterfaceException
1679
-     * @throws ReflectionException
1680
-     */
1681
-    public function count_checkins_not_checkedout()
1682
-    {
1683
-        return $this->get_model()->count_related($this, 'Checkin', array(array('CHK_in' => 1)));
1684
-    }
1685
-
1686
-
1687
-    /**
1688
-     * The purpose of this method is simply to check whether this registration can checkin to the given datetime.
1689
-     *
1690
-     * @param int | EE_Datetime $DTT_OR_ID      The datetime the registration is being checked against
1691
-     * @param bool              $check_approved This is used to indicate whether the caller wants can_checkin to also
1692
-     *                                          consider registration status as well as datetime access.
1693
-     * @return bool
1694
-     * @throws EE_Error
1695
-     * @throws InvalidArgumentException
1696
-     * @throws InvalidDataTypeException
1697
-     * @throws InvalidInterfaceException
1698
-     * @throws ReflectionException
1699
-     */
1700
-    public function can_checkin($DTT_OR_ID, $check_approved = true)
1701
-    {
1702
-        $DTT_ID = EEM_Datetime::instance()->ensure_is_ID($DTT_OR_ID);
1703
-        // first check registration status
1704
-        if (! $DTT_ID || ($check_approved && ! $this->is_approved())) {
1705
-            return false;
1706
-        }
1707
-        // is there a datetime ticket that matches this dtt_ID?
1708
-        if (
1709
-            ! (EEM_Datetime_Ticket::instance()->exists(
1710
-                array(
1711
-                    array(
1712
-                        'TKT_ID' => $this->get('TKT_ID'),
1713
-                        'DTT_ID' => $DTT_ID,
1714
-                    ),
1715
-                )
1716
-            ))
1717
-        ) {
1718
-            return false;
1719
-        }
1720
-
1721
-        // final check is against TKT_uses
1722
-        return $this->verify_can_checkin_against_TKT_uses($DTT_ID);
1723
-    }
1724
-
1725
-
1726
-    /**
1727
-     * This method verifies whether the user can checkin for the given datetime considering the max uses value set on
1728
-     * the ticket. To do this,  a query is done to get the count of the datetime records already checked into.  If the
1729
-     * datetime given does not have a check-in record and checking in for that datetime will exceed the allowed uses,
1730
-     * then return false.  Otherwise return true.
1731
-     *
1732
-     * @param int | EE_Datetime $DTT_OR_ID The datetime the registration is being checked against
1733
-     * @return bool true means can checkin.  false means cannot checkin.
1734
-     * @throws EE_Error
1735
-     * @throws InvalidArgumentException
1736
-     * @throws InvalidDataTypeException
1737
-     * @throws InvalidInterfaceException
1738
-     * @throws ReflectionException
1739
-     */
1740
-    public function verify_can_checkin_against_TKT_uses($DTT_OR_ID)
1741
-    {
1742
-        $DTT_ID = EEM_Datetime::instance()->ensure_is_ID($DTT_OR_ID);
1743
-
1744
-        if (! $DTT_ID) {
1745
-            return false;
1746
-        }
1747
-
1748
-        $max_uses = $this->ticket() instanceof EE_Ticket ? $this->ticket()->uses() : EE_INF;
1749
-
1750
-        // if max uses is not set or equals infinity then return true cause its not a factor for whether user can
1751
-        // check-in or not.
1752
-        if (! $max_uses || $max_uses === EE_INF) {
1753
-            return true;
1754
-        }
1755
-
1756
-        // does this datetime have a checkin record?  If so, then the dtt count has already been verified so we can just
1757
-        // go ahead and toggle.
1758
-        if (EEM_Checkin::instance()->exists(array(array('REG_ID' => $this->ID(), 'DTT_ID' => $DTT_ID)))) {
1759
-            return true;
1760
-        }
1761
-
1762
-        // made it here so the last check is whether the number of checkins per unique datetime on this registration
1763
-        // disallows further check-ins.
1764
-        $count_unique_dtt_checkins = EEM_Checkin::instance()->count(
1765
-            array(
1766
-                array(
1767
-                    'REG_ID' => $this->ID(),
1768
-                    'CHK_in' => true,
1769
-                ),
1770
-            ),
1771
-            'DTT_ID',
1772
-            true
1773
-        );
1774
-        // checkins have already reached their max number of uses
1775
-        // so registrant can NOT checkin
1776
-        if ($count_unique_dtt_checkins >= $max_uses) {
1777
-            EE_Error::add_error(
1778
-                esc_html__(
1779
-                    'Check-in denied because number of datetime uses for the ticket has been reached or exceeded.',
1780
-                    'event_espresso'
1781
-                ),
1782
-                __FILE__,
1783
-                __FUNCTION__,
1784
-                __LINE__
1785
-            );
1786
-            return false;
1787
-        }
1788
-        return true;
1789
-    }
1790
-
1791
-
1792
-    /**
1793
-     * toggle Check-in status for this registration
1794
-     * Check-ins are toggled in the following order:
1795
-     * never checked in -> checked in
1796
-     * checked in -> checked out
1797
-     * checked out -> checked in
1798
-     *
1799
-     * @param  int $DTT_ID  include specific datetime to toggle Check-in for.
1800
-     *                      If not included or null, then it is assumed latest datetime is being toggled.
1801
-     * @param bool $verify  If true then can_checkin() is used to verify whether the person
1802
-     *                      can be checked in or not.  Otherwise this forces change in checkin status.
1803
-     * @return bool|int     the chk_in status toggled to OR false if nothing got changed.
1804
-     * @throws EE_Error
1805
-     * @throws InvalidArgumentException
1806
-     * @throws InvalidDataTypeException
1807
-     * @throws InvalidInterfaceException
1808
-     * @throws ReflectionException
1809
-     */
1810
-    public function toggle_checkin_status($DTT_ID = null, $verify = false)
1811
-    {
1812
-        if (empty($DTT_ID)) {
1813
-            $datetime = $this->get_latest_related_datetime();
1814
-            $DTT_ID = $datetime instanceof EE_Datetime ? $datetime->ID() : 0;
1815
-            // verify the registration can checkin for the given DTT_ID
1816
-        } elseif (! $this->can_checkin($DTT_ID, $verify)) {
1817
-            EE_Error::add_error(
1818
-                sprintf(
1819
-                    esc_html__(
1820
-                        'The given registration (ID:%1$d) can not be checked in to the given DTT_ID (%2$d), because the registration does not have access',
1821
-                        'event_espresso'
1822
-                    ),
1823
-                    $this->ID(),
1824
-                    $DTT_ID
1825
-                ),
1826
-                __FILE__,
1827
-                __FUNCTION__,
1828
-                __LINE__
1829
-            );
1830
-            return false;
1831
-        }
1832
-        $status_paths = array(
1833
-            EE_Checkin::status_checked_never => EE_Checkin::status_checked_in,
1834
-            EE_Checkin::status_checked_in    => EE_Checkin::status_checked_out,
1835
-            EE_Checkin::status_checked_out   => EE_Checkin::status_checked_in,
1836
-        );
1837
-        // start by getting the current status so we know what status we'll be changing to.
1838
-        $cur_status = $this->check_in_status_for_datetime($DTT_ID);
1839
-        $status_to = $status_paths[ $cur_status ];
1840
-        // database only records true for checked IN or false for checked OUT
1841
-        // no record ( null ) means checked in NEVER, but we obviously don't save that
1842
-        $new_status = $status_to === EE_Checkin::status_checked_in;
1843
-        // add relation - note Check-ins are always creating new rows
1844
-        // because we are keeping track of Check-ins over time.
1845
-        // Eventually we'll probably want to show a list table
1846
-        // for the individual Check-ins so that they can be managed.
1847
-        $checkin = EE_Checkin::new_instance(
1848
-            array(
1849
-                'REG_ID' => $this->ID(),
1850
-                'DTT_ID' => $DTT_ID,
1851
-                'CHK_in' => $new_status,
1852
-            )
1853
-        );
1854
-        // if the record could not be saved then return false
1855
-        if ($checkin->save() === 0) {
1856
-            if (WP_DEBUG) {
1857
-                global $wpdb;
1858
-                $error = sprintf(
1859
-                    esc_html__(
1860
-                        'Registration check in update failed because of the following database error: %1$s%2$s',
1861
-                        'event_espresso'
1862
-                    ),
1863
-                    '<br />',
1864
-                    $wpdb->last_error
1865
-                );
1866
-            } else {
1867
-                $error = esc_html__(
1868
-                    'Registration check in update failed because of an unknown database error',
1869
-                    'event_espresso'
1870
-                );
1871
-            }
1872
-            EE_Error::add_error($error, __FILE__, __FUNCTION__, __LINE__);
1873
-            return false;
1874
-        }
1875
-        // Fire a checked_in and checkout_out action.
1876
-        $checked_status = $status_to === EE_Checkin::status_checked_in ? 'checked_in' : 'checked_out';
1877
-        do_action("AHEE__EE_Registration__toggle_checkin_status__{$checked_status}", $this, $DTT_ID);
1878
-        return $status_to;
1879
-    }
1880
-
1881
-
1882
-    /**
1883
-     * Returns the latest datetime related to this registration (via the ticket attached to the registration).
1884
-     * "Latest" is defined by the `DTT_EVT_start` column.
1885
-     *
1886
-     * @return EE_Datetime|null
1887
-     * @throws EE_Error
1888
-     * @throws InvalidArgumentException
1889
-     * @throws InvalidDataTypeException
1890
-     * @throws InvalidInterfaceException
1891
-     * @throws ReflectionException
1892
-     */
1893
-    public function get_latest_related_datetime()
1894
-    {
1895
-        return EEM_Datetime::instance()->get_one(
1896
-            array(
1897
-                array(
1898
-                    'Ticket.Registration.REG_ID' => $this->ID(),
1899
-                ),
1900
-                'order_by' => array('DTT_EVT_start' => 'DESC'),
1901
-            )
1902
-        );
1903
-    }
1904
-
1905
-
1906
-    /**
1907
-     * Returns the earliest datetime related to this registration (via the ticket attached to the registration).
1908
-     * "Earliest" is defined by the `DTT_EVT_start` column.
1909
-     *
1910
-     * @return EE_Base_Class|EE_Soft_Delete_Base_Class|NULL
1911
-     * @throws EE_Error
1912
-     * @throws InvalidArgumentException
1913
-     * @throws InvalidDataTypeException
1914
-     * @throws InvalidInterfaceException
1915
-     * @throws ReflectionException
1916
-     */
1917
-    public function get_earliest_related_datetime()
1918
-    {
1919
-        return EEM_Datetime::instance()->get_one(
1920
-            array(
1921
-                array(
1922
-                    'Ticket.Registration.REG_ID' => $this->ID(),
1923
-                ),
1924
-                'order_by' => array('DTT_EVT_start' => 'ASC'),
1925
-            )
1926
-        );
1927
-    }
1928
-
1929
-
1930
-    /**
1931
-     * This method simply returns the check-in status for this registration and the given datetime.
1932
-     * If neither the datetime nor the checkin values are provided as arguments,
1933
-     * then this will return the LATEST check-in status for the registration across all datetimes it belongs to.
1934
-     *
1935
-     * @param int|null        $DTT_ID  The ID of the datetime we're checking against
1936
-     *                                 (if empty we'll get the primary datetime for
1937
-     *                                 this registration (via event) and use it's ID);
1938
-     * @param EE_Checkin|null $checkin If present, we use the given checkin object rather than the dtt_id.
1939
-     * @return int                     Integer representing Check-in status.
1940
-     * @throws EE_Error
1941
-     * @throws ReflectionException
1942
-     */
1943
-    public function check_in_status_for_datetime(?int $DTT_ID = 0, ?EE_Checkin $checkin = null): int
1944
-    {
1945
-        if ($checkin instanceof EE_Checkin) {
1946
-            return $checkin->status();
1947
-        }
1948
-        // can't query checkin for a specific date if no ID was supplied
1949
-        if (empty($DTT_ID)) {
1950
-            return EE_Checkin::status_invalid;
1951
-        }
1952
-
1953
-        $checkin = $this->get_first_related(
1954
-            'Checkin',
1955
-            [
1956
-                ['DTT_ID' => $DTT_ID],
1957
-                'order_by' => ['CHK_timestamp' => 'DESC'],
1958
-            ]
1959
-        );
1960
-        return $checkin instanceof EE_Checkin ? $checkin->status() :  EE_Checkin::status_checked_never;
1961
-    }
1962
-
1963
-
1964
-    /**
1965
-     * This method returns a localized message for the toggled Check-in message.
1966
-     *
1967
-     * @param int|null $DTT_ID include specific datetime to get the correct Check-in message.  If not included or null,
1968
-     *                         then it is assumed Check-in for primary datetime was toggled.
1969
-     * @param bool     $error  This just flags that you want an error message returned. This is put in so that the error
1970
-     *                         message can be customized with the attendee name.
1971
-     * @return string internationalized message
1972
-     * @throws EE_Error
1973
-     * @throws ReflectionException
1974
-     */
1975
-    public function get_checkin_msg(?int $DTT_ID, bool $error = false): string
1976
-    {
1977
-        // let's get the attendee first so we can include the name of the attendee
1978
-        $attendee = $this->get_first_related('Attendee');
1979
-        if ($attendee instanceof EE_Attendee) {
1980
-            if ($error) {
1981
-                return sprintf(
1982
-                    esc_html__("%s's check-in status was not changed.", "event_espresso"),
1983
-                    $attendee->full_name()
1984
-                );
1985
-            }
1986
-            $cur_status = $this->check_in_status_for_datetime($DTT_ID);
1987
-            // what is the status message going to be?
1988
-            switch ($cur_status) {
1989
-                case EE_Checkin::status_checked_never:
1990
-                    return sprintf(
1991
-                        esc_html__('%s has been removed from Check-in records', 'event_espresso'),
1992
-                        $attendee->full_name()
1993
-                    );
1994
-                case EE_Checkin::status_checked_in:
1995
-                    return sprintf(esc_html__('%s has been checked in', 'event_espresso'), $attendee->full_name());
1996
-                case EE_Checkin::status_checked_out:
1997
-                    return sprintf(esc_html__('%s has been checked out', 'event_espresso'), $attendee->full_name());
1998
-            }
1999
-        }
2000
-        return esc_html__('The check-in status could not be determined.', 'event_espresso');
2001
-    }
2002
-
2003
-
2004
-    /**
2005
-     * Returns the related EE_Transaction to this registration
2006
-     *
2007
-     * @return EE_Transaction
2008
-     * @throws EE_Error
2009
-     * @throws EntityNotFoundException
2010
-     * @throws ReflectionException
2011
-     */
2012
-    public function transaction(): EE_Transaction
2013
-    {
2014
-        $transaction = $this->get_first_related('Transaction');
2015
-        if (! $transaction instanceof \EE_Transaction) {
2016
-            throw new EntityNotFoundException('Transaction ID', $this->transaction_ID());
2017
-        }
2018
-        return $transaction;
2019
-    }
2020
-
2021
-
2022
-    /**
2023
-     * get Registration Code
2024
-     *
2025
-     * @return mixed
2026
-     * @throws EE_Error
2027
-     * @throws InvalidArgumentException
2028
-     * @throws InvalidDataTypeException
2029
-     * @throws InvalidInterfaceException
2030
-     * @throws ReflectionException
2031
-     */
2032
-    public function reg_code()
2033
-    {
2034
-        return $this->get('REG_code');
2035
-    }
2036
-
2037
-
2038
-    /**
2039
-     * @return mixed
2040
-     * @throws EE_Error
2041
-     * @throws InvalidArgumentException
2042
-     * @throws InvalidDataTypeException
2043
-     * @throws InvalidInterfaceException
2044
-     * @throws ReflectionException
2045
-     */
2046
-    public function transaction_ID()
2047
-    {
2048
-        return $this->get('TXN_ID');
2049
-    }
2050
-
2051
-
2052
-    /**
2053
-     * @return int
2054
-     * @throws EE_Error
2055
-     * @throws InvalidArgumentException
2056
-     * @throws InvalidDataTypeException
2057
-     * @throws InvalidInterfaceException
2058
-     * @throws ReflectionException
2059
-     */
2060
-    public function ticket_ID()
2061
-    {
2062
-        return $this->get('TKT_ID');
2063
-    }
2064
-
2065
-
2066
-    /**
2067
-     * Set Registration Code
2068
-     *
2069
-     * @param    string  $REG_code Registration Code
2070
-     * @param    boolean $use_default
2071
-     * @throws EE_Error
2072
-     * @throws InvalidArgumentException
2073
-     * @throws InvalidDataTypeException
2074
-     * @throws InvalidInterfaceException
2075
-     * @throws ReflectionException
2076
-     */
2077
-    public function set_reg_code($REG_code, $use_default = false)
2078
-    {
2079
-        if (empty($REG_code)) {
2080
-            EE_Error::add_error(
2081
-                esc_html__('REG_code can not be empty.', 'event_espresso'),
2082
-                __FILE__,
2083
-                __FUNCTION__,
2084
-                __LINE__
2085
-            );
2086
-            return;
2087
-        }
2088
-        if (! $this->reg_code()) {
2089
-            parent::set('REG_code', $REG_code, $use_default);
2090
-        } else {
2091
-            EE_Error::doing_it_wrong(
2092
-                __CLASS__ . '::' . __FUNCTION__,
2093
-                esc_html__('Can not change a registration REG_code once it has been set.', 'event_espresso'),
2094
-                '4.6.0'
2095
-            );
2096
-        }
2097
-    }
2098
-
2099
-
2100
-    /**
2101
-     * Returns all other registrations in the same group as this registrant who have the same ticket option.
2102
-     * Note, if you want to just get all registrations in the same transaction (group), use:
2103
-     *    $registration->transaction()->registrations();
2104
-     *
2105
-     * @since 4.5.0
2106
-     * @return EE_Registration[] or empty array if this isn't a group registration.
2107
-     * @throws EE_Error
2108
-     * @throws InvalidArgumentException
2109
-     * @throws InvalidDataTypeException
2110
-     * @throws InvalidInterfaceException
2111
-     * @throws ReflectionException
2112
-     */
2113
-    public function get_all_other_registrations_in_group()
2114
-    {
2115
-        if ($this->group_size() < 2) {
2116
-            return array();
2117
-        }
2118
-
2119
-        $query[0] = array(
2120
-            'TXN_ID' => $this->transaction_ID(),
2121
-            'REG_ID' => array('!=', $this->ID()),
2122
-            'TKT_ID' => $this->ticket_ID(),
2123
-        );
2124
-        /** @var EE_Registration[] $registrations */
2125
-        $registrations = $this->get_model()->get_all($query);
2126
-        return $registrations;
2127
-    }
2128
-
2129
-
2130
-    /**
2131
-     * Return the link to the admin details for the object.
2132
-     *
2133
-     * @return string
2134
-     * @throws EE_Error
2135
-     * @throws InvalidArgumentException
2136
-     * @throws InvalidDataTypeException
2137
-     * @throws InvalidInterfaceException
2138
-     * @throws ReflectionException
2139
-     */
2140
-    public function get_admin_details_link()
2141
-    {
2142
-        EE_Registry::instance()->load_helper('URL');
2143
-        return EEH_URL::add_query_args_and_nonce(
2144
-            array(
2145
-                'page'    => 'espresso_registrations',
2146
-                'action'  => 'view_registration',
2147
-                '_REG_ID' => $this->ID(),
2148
-            ),
2149
-            admin_url('admin.php')
2150
-        );
2151
-    }
2152
-
2153
-
2154
-    /**
2155
-     * Returns the link to the editor for the object.  Sometimes this is the same as the details.
2156
-     *
2157
-     * @return string
2158
-     * @throws EE_Error
2159
-     * @throws InvalidArgumentException
2160
-     * @throws InvalidDataTypeException
2161
-     * @throws InvalidInterfaceException
2162
-     * @throws ReflectionException
2163
-     */
2164
-    public function get_admin_edit_link()
2165
-    {
2166
-        return $this->get_admin_details_link();
2167
-    }
2168
-
2169
-
2170
-    /**
2171
-     * Returns the link to a settings page for the object.
2172
-     *
2173
-     * @return string
2174
-     * @throws EE_Error
2175
-     * @throws InvalidArgumentException
2176
-     * @throws InvalidDataTypeException
2177
-     * @throws InvalidInterfaceException
2178
-     * @throws ReflectionException
2179
-     */
2180
-    public function get_admin_settings_link()
2181
-    {
2182
-        return $this->get_admin_details_link();
2183
-    }
2184
-
2185
-
2186
-    /**
2187
-     * Returns the link to the "overview" for the object (typically the "list table" view).
2188
-     *
2189
-     * @return string
2190
-     * @throws EE_Error
2191
-     * @throws InvalidArgumentException
2192
-     * @throws InvalidDataTypeException
2193
-     * @throws InvalidInterfaceException
2194
-     * @throws ReflectionException
2195
-     */
2196
-    public function get_admin_overview_link()
2197
-    {
2198
-        EE_Registry::instance()->load_helper('URL');
2199
-        return EEH_URL::add_query_args_and_nonce(
2200
-            array(
2201
-                'page' => 'espresso_registrations',
2202
-            ),
2203
-            admin_url('admin.php')
2204
-        );
2205
-    }
2206
-
2207
-
2208
-    /**
2209
-     * @param array $query_params
2210
-     * @return EE_Base_Class[]|EE_Registration[]
2211
-     * @throws EE_Error
2212
-     * @throws InvalidArgumentException
2213
-     * @throws InvalidDataTypeException
2214
-     * @throws InvalidInterfaceException
2215
-     * @throws ReflectionException
2216
-     */
2217
-    public function payments($query_params = array())
2218
-    {
2219
-        return $this->get_many_related('Payment', $query_params);
2220
-    }
2221
-
2222
-
2223
-    /**
2224
-     * @param array $query_params
2225
-     * @return EE_Base_Class[]|EE_Registration_Payment[]
2226
-     * @throws EE_Error
2227
-     * @throws InvalidArgumentException
2228
-     * @throws InvalidDataTypeException
2229
-     * @throws InvalidInterfaceException
2230
-     * @throws ReflectionException
2231
-     */
2232
-    public function registration_payments($query_params = array())
2233
-    {
2234
-        return $this->get_many_related('Registration_Payment', $query_params);
2235
-    }
2236
-
2237
-
2238
-    /**
2239
-     * This grabs the payment method corresponding to the last payment made for the amount owing on the registration.
2240
-     * Note: if there are no payments on the registration there will be no payment method returned.
2241
-     *
2242
-     * @return EE_Payment|EE_Payment_Method|null
2243
-     * @throws EE_Error
2244
-     * @throws InvalidArgumentException
2245
-     * @throws InvalidDataTypeException
2246
-     * @throws InvalidInterfaceException
2247
-     */
2248
-    public function payment_method()
2249
-    {
2250
-        return EEM_Payment_Method::instance()->get_last_used_for_registration($this);
2251
-    }
2252
-
2253
-
2254
-    /**
2255
-     * @return \EE_Line_Item
2256
-     * @throws EE_Error
2257
-     * @throws EntityNotFoundException
2258
-     * @throws InvalidArgumentException
2259
-     * @throws InvalidDataTypeException
2260
-     * @throws InvalidInterfaceException
2261
-     * @throws ReflectionException
2262
-     */
2263
-    public function ticket_line_item()
2264
-    {
2265
-        $ticket = $this->ticket();
2266
-        $transaction = $this->transaction();
2267
-        $line_item = null;
2268
-        $ticket_line_items = \EEH_Line_Item::get_line_items_by_object_type_and_IDs(
2269
-            $transaction->total_line_item(),
2270
-            'Ticket',
2271
-            array($ticket->ID())
2272
-        );
2273
-        foreach ($ticket_line_items as $ticket_line_item) {
2274
-            if (
2275
-                $ticket_line_item instanceof \EE_Line_Item
2276
-                && $ticket_line_item->OBJ_type() === 'Ticket'
2277
-                && $ticket_line_item->OBJ_ID() === $ticket->ID()
2278
-            ) {
2279
-                $line_item = $ticket_line_item;
2280
-                break;
2281
-            }
2282
-        }
2283
-        if (! ($line_item instanceof \EE_Line_Item && $line_item->OBJ_type() === 'Ticket')) {
2284
-            throw new EntityNotFoundException('Line Item Ticket ID', $ticket->ID());
2285
-        }
2286
-        return $line_item;
2287
-    }
2288
-
2289
-
2290
-    /**
2291
-     * Soft Deletes this model object.
2292
-     *
2293
-     * @param string $source function name that called this method
2294
-     * @return boolean | int
2295
-     * @throws DomainException
2296
-     * @throws EE_Error
2297
-     * @throws EntityNotFoundException
2298
-     * @throws InvalidArgumentException
2299
-     * @throws InvalidDataTypeException
2300
-     * @throws InvalidInterfaceException
2301
-     * @throws ReflectionException
2302
-     * @throws RuntimeException
2303
-     * @throws UnexpectedEntityException
2304
-     */
2305
-    public function delete()
2306
-    {
2307
-        if ($this->update_extra_meta(EE_Registration::PRE_TRASH_REG_STATUS_KEY, $this->status_ID()) === true) {
2308
-            $this->set_status(EEM_Registration::status_id_cancelled);
2309
-        }
2310
-        return parent::delete();
2311
-    }
2312
-
2313
-
2314
-    /**
2315
-     * Restores whatever the previous status was on a registration before it was trashed (if possible)
2316
-     *
2317
-     * @param string $source function name that called this method
2318
-     * @return bool|int
2319
-     * @throws DomainException
2320
-     * @throws EE_Error
2321
-     * @throws EntityNotFoundException
2322
-     * @throws InvalidArgumentException
2323
-     * @throws InvalidDataTypeException
2324
-     * @throws InvalidInterfaceException
2325
-     * @throws ReflectionException
2326
-     * @throws RuntimeException
2327
-     * @throws UnexpectedEntityException
2328
-     */
2329
-    public function restore()
2330
-    {
2331
-        $previous_status = $this->get_extra_meta(
2332
-            EE_Registration::PRE_TRASH_REG_STATUS_KEY,
2333
-            true,
2334
-            EEM_Registration::status_id_cancelled
2335
-        );
2336
-        if ($previous_status) {
2337
-            $this->delete_extra_meta(EE_Registration::PRE_TRASH_REG_STATUS_KEY);
2338
-            $this->set_status($previous_status);
2339
-        }
2340
-        return parent::restore();
2341
-    }
2342
-
2343
-
2344
-    /**
2345
-     * possibly toggle Registration status based on comparison of REG_paid vs REG_final_price
2346
-     *
2347
-     * @param  boolean $trigger_set_status_logic EE_Registration::set_status() can trigger additional logic
2348
-     *                                           depending on whether the reg status changes to or from "Approved"
2349
-     * @return boolean whether the Registration status was updated
2350
-     * @throws DomainException
2351
-     * @throws EE_Error
2352
-     * @throws EntityNotFoundException
2353
-     * @throws InvalidArgumentException
2354
-     * @throws InvalidDataTypeException
2355
-     * @throws InvalidInterfaceException
2356
-     * @throws ReflectionException
2357
-     * @throws RuntimeException
2358
-     * @throws UnexpectedEntityException
2359
-     */
2360
-    public function updateStatusBasedOnTotalPaid($trigger_set_status_logic = true)
2361
-    {
2362
-        $paid = $this->paid();
2363
-        $price = $this->final_price();
2364
-        switch (true) {
2365
-            // overpaid or paid
2366
-            case EEH_Money::compare_floats($paid, $price, '>'):
2367
-            case EEH_Money::compare_floats($paid, $price):
2368
-                $new_status = EEM_Registration::status_id_approved;
2369
-                break;
2370
-            //  underpaid
2371
-            case EEH_Money::compare_floats($paid, $price, '<'):
2372
-                $new_status = EEM_Registration::status_id_pending_payment;
2373
-                break;
2374
-            // uhhh Houston...
2375
-            default:
2376
-                throw new RuntimeException(
2377
-                    esc_html__('The total paid calculation for this registration is inaccurate.', 'event_espresso')
2378
-                );
2379
-        }
2380
-        if ($new_status !== $this->status_ID()) {
2381
-            if ($trigger_set_status_logic) {
2382
-                return $this->set_status($new_status);
2383
-            }
2384
-            parent::set('STS_ID', $new_status);
2385
-            return true;
2386
-        }
2387
-        return false;
2388
-    }
2389
-
2390
-
2391
-    /*************************** DEPRECATED ***************************/
2392
-
2393
-
2394
-    /**
2395
-     * @deprecated
2396
-     * @since     4.7.0
2397
-     */
2398
-    public function price_paid()
2399
-    {
2400
-        EE_Error::doing_it_wrong(
2401
-            'EE_Registration::price_paid()',
2402
-            esc_html__(
2403
-                'This method is deprecated, please use EE_Registration::final_price() instead.',
2404
-                'event_espresso'
2405
-            ),
2406
-            '4.7.0'
2407
-        );
2408
-        return $this->final_price();
2409
-    }
2410
-
2411
-
2412
-    /**
2413
-     * @deprecated
2414
-     * @since     4.7.0
2415
-     * @param    float $REG_final_price
2416
-     * @throws EE_Error
2417
-     * @throws EntityNotFoundException
2418
-     * @throws InvalidArgumentException
2419
-     * @throws InvalidDataTypeException
2420
-     * @throws InvalidInterfaceException
2421
-     * @throws ReflectionException
2422
-     * @throws RuntimeException
2423
-     * @throws DomainException
2424
-     */
2425
-    public function set_price_paid($REG_final_price = 0.00)
2426
-    {
2427
-        EE_Error::doing_it_wrong(
2428
-            'EE_Registration::set_price_paid()',
2429
-            esc_html__(
2430
-                'This method is deprecated, please use EE_Registration::set_final_price() instead.',
2431
-                'event_espresso'
2432
-            ),
2433
-            '4.7.0'
2434
-        );
2435
-        $this->set_final_price($REG_final_price);
2436
-    }
2437
-
2438
-
2439
-    /**
2440
-     * @deprecated
2441
-     * @since 4.7.0
2442
-     * @return string
2443
-     * @throws EE_Error
2444
-     * @throws InvalidArgumentException
2445
-     * @throws InvalidDataTypeException
2446
-     * @throws InvalidInterfaceException
2447
-     * @throws ReflectionException
2448
-     */
2449
-    public function pretty_price_paid()
2450
-    {
2451
-        EE_Error::doing_it_wrong(
2452
-            'EE_Registration::pretty_price_paid()',
2453
-            esc_html__(
2454
-                'This method is deprecated, please use EE_Registration::pretty_final_price() instead.',
2455
-                'event_espresso'
2456
-            ),
2457
-            '4.7.0'
2458
-        );
2459
-        return $this->pretty_final_price();
2460
-    }
2461
-
2462
-
2463
-    /**
2464
-     * Gets the primary datetime related to this registration via the related Event to this registration
2465
-     *
2466
-     * @deprecated 4.9.17
2467
-     * @return EE_Datetime
2468
-     * @throws EE_Error
2469
-     * @throws EntityNotFoundException
2470
-     * @throws InvalidArgumentException
2471
-     * @throws InvalidDataTypeException
2472
-     * @throws InvalidInterfaceException
2473
-     * @throws ReflectionException
2474
-     */
2475
-    public function get_related_primary_datetime()
2476
-    {
2477
-        EE_Error::doing_it_wrong(
2478
-            __METHOD__,
2479
-            esc_html__(
2480
-                'Use EE_Registration::get_latest_related_datetime() or EE_Registration::get_earliest_related_datetime()',
2481
-                'event_espresso'
2482
-            ),
2483
-            '4.9.17',
2484
-            '5.0.0'
2485
-        );
2486
-        return $this->event()->primary_datetime();
2487
-    }
2488
-
2489
-    /**
2490
-     * Returns the contact's name (or "Unknown" if there is no contact.)
2491
-     * @since 4.10.12.p
2492
-     * @return string
2493
-     * @throws EE_Error
2494
-     * @throws InvalidArgumentException
2495
-     * @throws InvalidDataTypeException
2496
-     * @throws InvalidInterfaceException
2497
-     * @throws ReflectionException
2498
-     */
2499
-    public function name()
2500
-    {
2501
-        return $this->attendeeName();
2502
-    }
19
+	/**
20
+	 * Used to reference when a registration has never been checked in.
21
+	 *
22
+	 * @deprecated use \EE_Checkin::status_checked_never instead
23
+	 * @type int
24
+	 */
25
+	const checkin_status_never = 2;
26
+
27
+	/**
28
+	 * Used to reference when a registration has been checked in.
29
+	 *
30
+	 * @deprecated use \EE_Checkin::status_checked_in instead
31
+	 * @type int
32
+	 */
33
+	const checkin_status_in = 1;
34
+
35
+	/**
36
+	 * Used to reference when a registration has been checked out.
37
+	 *
38
+	 * @deprecated use \EE_Checkin::status_checked_out instead
39
+	 * @type int
40
+	 */
41
+	const checkin_status_out = 0;
42
+
43
+	/**
44
+	 * extra meta key for tracking reg status os trashed registrations
45
+	 *
46
+	 * @type string
47
+	 */
48
+	const PRE_TRASH_REG_STATUS_KEY = 'pre_trash_registration_status';
49
+
50
+	/**
51
+	 * extra meta key for tracking if registration has reserved ticket
52
+	 *
53
+	 * @type string
54
+	 */
55
+	const HAS_RESERVED_TICKET_KEY = 'has_reserved_ticket';
56
+
57
+
58
+	/**
59
+	 * @param array  $props_n_values          incoming values
60
+	 * @param string $timezone                incoming timezone (if not set the timezone set for the website will be
61
+	 *                                        used.)
62
+	 * @param array  $date_formats            incoming date_formats in an array where the first value is the
63
+	 *                                        date_format and the second value is the time format
64
+	 * @return EE_Registration
65
+	 * @throws EE_Error
66
+	 * @throws InvalidArgumentException
67
+	 * @throws InvalidDataTypeException
68
+	 * @throws InvalidInterfaceException
69
+	 * @throws ReflectionException
70
+	 */
71
+	public static function new_instance($props_n_values = array(), $timezone = null, $date_formats = array())
72
+	{
73
+		$has_object = parent::_check_for_object($props_n_values, __CLASS__, $timezone, $date_formats);
74
+		return $has_object ? $has_object : new self($props_n_values, false, $timezone, $date_formats);
75
+	}
76
+
77
+
78
+	/**
79
+	 * @param array  $props_n_values  incoming values from the database
80
+	 * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
81
+	 *                                the website will be used.
82
+	 * @return EE_Registration
83
+	 * @throws EE_Error
84
+	 * @throws InvalidArgumentException
85
+	 * @throws InvalidDataTypeException
86
+	 * @throws InvalidInterfaceException
87
+	 * @throws ReflectionException
88
+	 */
89
+	public static function new_instance_from_db($props_n_values = array(), $timezone = null)
90
+	{
91
+		return new self($props_n_values, true, $timezone);
92
+	}
93
+
94
+
95
+	/**
96
+	 *        Set Event ID
97
+	 *
98
+	 * @param        int $EVT_ID Event ID
99
+	 * @throws DomainException
100
+	 * @throws EE_Error
101
+	 * @throws EntityNotFoundException
102
+	 * @throws InvalidArgumentException
103
+	 * @throws InvalidDataTypeException
104
+	 * @throws InvalidInterfaceException
105
+	 * @throws ReflectionException
106
+	 * @throws RuntimeException
107
+	 * @throws UnexpectedEntityException
108
+	 */
109
+	public function set_event($EVT_ID = 0)
110
+	{
111
+		$this->set('EVT_ID', $EVT_ID);
112
+	}
113
+
114
+
115
+	/**
116
+	 * Overrides parent set() method so that all calls to set( 'REG_code', $REG_code ) OR set( 'STS_ID', $STS_ID ) can
117
+	 * be routed to internal methods
118
+	 *
119
+	 * @param string $field_name
120
+	 * @param mixed  $field_value
121
+	 * @param bool   $use_default
122
+	 * @throws DomainException
123
+	 * @throws EE_Error
124
+	 * @throws EntityNotFoundException
125
+	 * @throws InvalidArgumentException
126
+	 * @throws InvalidDataTypeException
127
+	 * @throws InvalidInterfaceException
128
+	 * @throws ReflectionException
129
+	 * @throws RuntimeException
130
+	 * @throws UnexpectedEntityException
131
+	 */
132
+	public function set($field_name, $field_value, $use_default = false)
133
+	{
134
+		switch ($field_name) {
135
+			case 'REG_code':
136
+				if (! empty($field_value) && $this->reg_code() === null) {
137
+					$this->set_reg_code($field_value, $use_default);
138
+				}
139
+				break;
140
+			case 'STS_ID':
141
+				$this->set_status($field_value, $use_default);
142
+				break;
143
+			default:
144
+				parent::set($field_name, $field_value, $use_default);
145
+		}
146
+	}
147
+
148
+
149
+	/**
150
+	 * Set Status ID
151
+	 * updates the registration status and ALSO...
152
+	 * calls reserve_registration_space() if the reg status changes TO approved from any other reg status
153
+	 * calls release_registration_space() if the reg status changes FROM approved to any other reg status
154
+	 *
155
+	 * @param string                $new_STS_ID
156
+	 * @param boolean               $use_default
157
+	 * @param ContextInterface|null $context
158
+	 * @return bool
159
+	 * @throws DomainException
160
+	 * @throws EE_Error
161
+	 * @throws EntityNotFoundException
162
+	 * @throws InvalidArgumentException
163
+	 * @throws InvalidDataTypeException
164
+	 * @throws InvalidInterfaceException
165
+	 * @throws ReflectionException
166
+	 * @throws RuntimeException
167
+	 * @throws UnexpectedEntityException
168
+	 */
169
+	public function set_status($new_STS_ID = null, $use_default = false, ContextInterface $context = null)
170
+	{
171
+		// get current REG_Status
172
+		$old_STS_ID = $this->status_ID();
173
+		// if status has changed
174
+		if (
175
+			$old_STS_ID !== $new_STS_ID // and that status has actually changed
176
+			&& ! empty($old_STS_ID) // and that old status is actually set
177
+			&& ! empty($new_STS_ID) // as well as the new status
178
+			&& $this->ID() // ensure registration is in the db
179
+		) {
180
+			// update internal status first
181
+			parent::set('STS_ID', $new_STS_ID, $use_default);
182
+			// THEN handle other changes that occur when reg status changes
183
+			// TO approved
184
+			if ($new_STS_ID === EEM_Registration::status_id_approved) {
185
+				// reserve a space by incrementing ticket and datetime sold values
186
+				$this->reserveRegistrationSpace();
187
+				do_action('AHEE__EE_Registration__set_status__to_approved', $this, $old_STS_ID, $new_STS_ID, $context);
188
+				// OR FROM  approved
189
+			} elseif ($old_STS_ID === EEM_Registration::status_id_approved) {
190
+				// release a space by decrementing ticket and datetime sold values
191
+				$this->releaseRegistrationSpace();
192
+				do_action(
193
+					'AHEE__EE_Registration__set_status__from_approved',
194
+					$this,
195
+					$old_STS_ID,
196
+					$new_STS_ID,
197
+					$context
198
+				);
199
+			}
200
+			// update status
201
+			parent::set('STS_ID', $new_STS_ID, $use_default);
202
+			$this->updateIfCanceledOrReinstated($new_STS_ID, $old_STS_ID, $context);
203
+			if ($this->statusChangeUpdatesTransaction($context)) {
204
+				$this->updateTransactionAfterStatusChange();
205
+			}
206
+			do_action('AHEE__EE_Registration__set_status__after_update', $this, $old_STS_ID, $new_STS_ID, $context);
207
+			return true;
208
+		}
209
+		// even though the old value matches the new value, it's still good to
210
+		// allow the parent set method to have a say
211
+		parent::set('STS_ID', $new_STS_ID, $use_default);
212
+		return true;
213
+	}
214
+
215
+
216
+	/**
217
+	 * update REGs and TXN when cancelled or declined registrations involved
218
+	 *
219
+	 * @param string                $new_STS_ID
220
+	 * @param string                $old_STS_ID
221
+	 * @param ContextInterface|null $context
222
+	 * @throws EE_Error
223
+	 * @throws InvalidArgumentException
224
+	 * @throws InvalidDataTypeException
225
+	 * @throws InvalidInterfaceException
226
+	 * @throws ReflectionException
227
+	 * @throws RuntimeException
228
+	 */
229
+	private function updateIfCanceledOrReinstated($new_STS_ID, $old_STS_ID, ContextInterface $context = null)
230
+	{
231
+		// these reg statuses should not be considered in any calculations involving monies owing
232
+		$closed_reg_statuses = EEM_Registration::closed_reg_statuses();
233
+		// true if registration has been cancelled or declined
234
+		$this->updateIfCanceled(
235
+			$closed_reg_statuses,
236
+			$new_STS_ID,
237
+			$old_STS_ID,
238
+			$context
239
+		);
240
+		$this->updateIfReinstated(
241
+			$closed_reg_statuses,
242
+			$new_STS_ID,
243
+			$old_STS_ID,
244
+			$context
245
+		);
246
+	}
247
+
248
+
249
+	/**
250
+	 * update REGs and TXN when cancelled or declined registrations involved
251
+	 *
252
+	 * @param array                 $closed_reg_statuses
253
+	 * @param string                $new_STS_ID
254
+	 * @param string                $old_STS_ID
255
+	 * @param ContextInterface|null $context
256
+	 * @throws EE_Error
257
+	 * @throws InvalidArgumentException
258
+	 * @throws InvalidDataTypeException
259
+	 * @throws InvalidInterfaceException
260
+	 * @throws ReflectionException
261
+	 * @throws RuntimeException
262
+	 */
263
+	private function updateIfCanceled(
264
+		array $closed_reg_statuses,
265
+		$new_STS_ID,
266
+		$old_STS_ID,
267
+		ContextInterface $context = null
268
+	) {
269
+		// true if registration has been cancelled or declined
270
+		if (
271
+			in_array($new_STS_ID, $closed_reg_statuses, true)
272
+			&& ! in_array($old_STS_ID, $closed_reg_statuses, true)
273
+		) {
274
+			/** @type EE_Registration_Processor $registration_processor */
275
+			$registration_processor = EE_Registry::instance()->load_class('Registration_Processor');
276
+			/** @type EE_Transaction_Processor $transaction_processor */
277
+			$transaction_processor = EE_Registry::instance()->load_class('Transaction_Processor');
278
+			// cancelled or declined registration
279
+			$registration_processor->update_registration_after_being_canceled_or_declined(
280
+				$this,
281
+				$closed_reg_statuses
282
+			);
283
+			$transaction_processor->update_transaction_after_canceled_or_declined_registration(
284
+				$this,
285
+				$closed_reg_statuses,
286
+				false
287
+			);
288
+			do_action(
289
+				'AHEE__EE_Registration__set_status__canceled_or_declined',
290
+				$this,
291
+				$old_STS_ID,
292
+				$new_STS_ID,
293
+				$context
294
+			);
295
+			return;
296
+		}
297
+	}
298
+
299
+
300
+	/**
301
+	 * update REGs and TXN when cancelled or declined registrations involved
302
+	 *
303
+	 * @param array                 $closed_reg_statuses
304
+	 * @param string                $new_STS_ID
305
+	 * @param string                $old_STS_ID
306
+	 * @param ContextInterface|null $context
307
+	 * @throws EE_Error
308
+	 * @throws InvalidArgumentException
309
+	 * @throws InvalidDataTypeException
310
+	 * @throws InvalidInterfaceException
311
+	 * @throws ReflectionException
312
+	 * @throws RuntimeException
313
+	 */
314
+	private function updateIfReinstated(
315
+		array $closed_reg_statuses,
316
+		$new_STS_ID,
317
+		$old_STS_ID,
318
+		ContextInterface $context = null
319
+	) {
320
+		// true if reinstating cancelled or declined registration
321
+		if (
322
+			in_array($old_STS_ID, $closed_reg_statuses, true)
323
+			&& ! in_array($new_STS_ID, $closed_reg_statuses, true)
324
+		) {
325
+			/** @type EE_Registration_Processor $registration_processor */
326
+			$registration_processor = EE_Registry::instance()->load_class('Registration_Processor');
327
+			/** @type EE_Transaction_Processor $transaction_processor */
328
+			$transaction_processor = EE_Registry::instance()->load_class('Transaction_Processor');
329
+			// reinstating cancelled or declined registration
330
+			$registration_processor->update_canceled_or_declined_registration_after_being_reinstated(
331
+				$this,
332
+				$closed_reg_statuses
333
+			);
334
+			$transaction_processor->update_transaction_after_reinstating_canceled_registration(
335
+				$this,
336
+				$closed_reg_statuses,
337
+				false
338
+			);
339
+			do_action(
340
+				'AHEE__EE_Registration__set_status__after_reinstated',
341
+				$this,
342
+				$old_STS_ID,
343
+				$new_STS_ID,
344
+				$context
345
+			);
346
+		}
347
+	}
348
+
349
+
350
+	/**
351
+	 * @param ContextInterface|null $context
352
+	 * @return bool
353
+	 */
354
+	private function statusChangeUpdatesTransaction(ContextInterface $context = null)
355
+	{
356
+		$contexts_that_do_not_update_transaction = (array) apply_filters(
357
+			'AHEE__EE_Registration__statusChangeUpdatesTransaction__contexts_that_do_not_update_transaction',
358
+			array('spco_reg_step_attendee_information_process_registrations'),
359
+			$context,
360
+			$this
361
+		);
362
+		return ! (
363
+			$context instanceof ContextInterface
364
+			&& in_array($context->slug(), $contexts_that_do_not_update_transaction, true)
365
+		);
366
+	}
367
+
368
+
369
+	/**
370
+	 * @throws EE_Error
371
+	 * @throws EntityNotFoundException
372
+	 * @throws InvalidArgumentException
373
+	 * @throws InvalidDataTypeException
374
+	 * @throws InvalidInterfaceException
375
+	 * @throws ReflectionException
376
+	 * @throws RuntimeException
377
+	 */
378
+	private function updateTransactionAfterStatusChange()
379
+	{
380
+		/** @type EE_Transaction_Payments $transaction_payments */
381
+		$transaction_payments = EE_Registry::instance()->load_class('Transaction_Payments');
382
+		$transaction_payments->recalculate_transaction_total($this->transaction(), false);
383
+		$this->transaction()->update_status_based_on_total_paid();
384
+	}
385
+
386
+
387
+	/**
388
+	 * get Status ID
389
+	 *
390
+	 * @throws EE_Error
391
+	 * @throws InvalidArgumentException
392
+	 * @throws InvalidDataTypeException
393
+	 * @throws InvalidInterfaceException
394
+	 * @throws ReflectionException
395
+	 */
396
+	public function status_ID()
397
+	{
398
+		return $this->get('STS_ID');
399
+	}
400
+
401
+
402
+	/**
403
+	 * Gets the ticket this registration is for
404
+	 *
405
+	 * @param boolean $include_archived whether to include archived tickets or not.
406
+	 * @return EE_Ticket|EE_Base_Class
407
+	 * @throws EE_Error
408
+	 * @throws InvalidArgumentException
409
+	 * @throws InvalidDataTypeException
410
+	 * @throws InvalidInterfaceException
411
+	 * @throws ReflectionException
412
+	 */
413
+	public function ticket($include_archived = true)
414
+	{
415
+		$query_params = array();
416
+		if ($include_archived) {
417
+			$query_params['default_where_conditions'] = 'none';
418
+		}
419
+		return $this->get_first_related('Ticket', $query_params);
420
+	}
421
+
422
+
423
+	/**
424
+	 * Gets the event this registration is for
425
+	 *
426
+	 * @return EE_Event
427
+	 * @throws EE_Error
428
+	 * @throws EntityNotFoundException
429
+	 * @throws InvalidArgumentException
430
+	 * @throws InvalidDataTypeException
431
+	 * @throws InvalidInterfaceException
432
+	 * @throws ReflectionException
433
+	 */
434
+	public function event()
435
+	{
436
+		$event = $this->get_first_related('Event');
437
+		if (! $event instanceof \EE_Event) {
438
+			throw new EntityNotFoundException('Event ID', $this->event_ID());
439
+		}
440
+		return $event;
441
+	}
442
+
443
+
444
+	/**
445
+	 * Gets the "author" of the registration.  Note that for the purposes of registrations, the author will correspond
446
+	 * with the author of the event this registration is for.
447
+	 *
448
+	 * @since 4.5.0
449
+	 * @return int
450
+	 * @throws EE_Error
451
+	 * @throws EntityNotFoundException
452
+	 * @throws InvalidArgumentException
453
+	 * @throws InvalidDataTypeException
454
+	 * @throws InvalidInterfaceException
455
+	 * @throws ReflectionException
456
+	 */
457
+	public function wp_user()
458
+	{
459
+		$event = $this->event();
460
+		if ($event instanceof EE_Event) {
461
+			return $event->wp_user();
462
+		}
463
+		return 0;
464
+	}
465
+
466
+
467
+	/**
468
+	 * increments this registration's related ticket sold and corresponding datetime sold values
469
+	 *
470
+	 * @return void
471
+	 * @throws DomainException
472
+	 * @throws EE_Error
473
+	 * @throws EntityNotFoundException
474
+	 * @throws InvalidArgumentException
475
+	 * @throws InvalidDataTypeException
476
+	 * @throws InvalidInterfaceException
477
+	 * @throws ReflectionException
478
+	 * @throws UnexpectedEntityException
479
+	 */
480
+	private function reserveRegistrationSpace()
481
+	{
482
+		// reserved ticket and datetime counts will be decremented as sold counts are incremented
483
+		// so stop tracking that this reg has a ticket reserved
484
+		$this->release_reserved_ticket(false, "REG: {$this->ID()} (ln:" . __LINE__ . ')');
485
+		$ticket = $this->ticket();
486
+		$ticket->increaseSold();
487
+		// possibly set event status to sold out
488
+		$this->event()->perform_sold_out_status_check();
489
+	}
490
+
491
+
492
+	/**
493
+	 * decrements (subtracts) this registration's related ticket sold and corresponding datetime sold values
494
+	 *
495
+	 * @return void
496
+	 * @throws DomainException
497
+	 * @throws EE_Error
498
+	 * @throws EntityNotFoundException
499
+	 * @throws InvalidArgumentException
500
+	 * @throws InvalidDataTypeException
501
+	 * @throws InvalidInterfaceException
502
+	 * @throws ReflectionException
503
+	 * @throws UnexpectedEntityException
504
+	 */
505
+	private function releaseRegistrationSpace()
506
+	{
507
+		$ticket = $this->ticket();
508
+		$ticket->decreaseSold();
509
+		// possibly change event status from sold out back to previous status
510
+		$this->event()->perform_sold_out_status_check();
511
+	}
512
+
513
+
514
+	/**
515
+	 * tracks this registration's ticket reservation in extra meta
516
+	 * and can increment related ticket reserved and corresponding datetime reserved values
517
+	 *
518
+	 * @param bool   $update_ticket if true, will increment ticket and datetime reserved count
519
+	 * @param string $source
520
+	 * @return void
521
+	 * @throws EE_Error
522
+	 * @throws InvalidArgumentException
523
+	 * @throws InvalidDataTypeException
524
+	 * @throws InvalidInterfaceException
525
+	 * @throws ReflectionException
526
+	 */
527
+	public function reserve_ticket($update_ticket = false, $source = 'unknown')
528
+	{
529
+		// only reserve ticket if space is not currently reserved
530
+		if ((bool) $this->get_extra_meta(EE_Registration::HAS_RESERVED_TICKET_KEY, true) !== true) {
531
+			$reserved = $this->update_extra_meta(EE_Registration::HAS_RESERVED_TICKET_KEY, true);
532
+			if ($reserved && $update_ticket) {
533
+				$ticket = $this->ticket();
534
+				$ticket->increaseReserved(1, "REG: {$this->ID()} (ln:" . __LINE__ . ')');
535
+				$this->update_extra_meta('reserve_ticket', "{$this->ticket_ID()} from {$source}");
536
+				$ticket->save();
537
+			}
538
+		}
539
+	}
540
+
541
+
542
+	/**
543
+	 * stops tracking this registration's ticket reservation in extra meta
544
+	 * decrements (subtracts) related ticket reserved and corresponding datetime reserved values
545
+	 *
546
+	 * @param bool   $update_ticket if true, will decrement ticket and datetime reserved count
547
+	 * @param string $source
548
+	 * @return void
549
+	 * @throws EE_Error
550
+	 * @throws InvalidArgumentException
551
+	 * @throws InvalidDataTypeException
552
+	 * @throws InvalidInterfaceException
553
+	 * @throws ReflectionException
554
+	 */
555
+	public function release_reserved_ticket($update_ticket = false, $source = 'unknown')
556
+	{
557
+		// only release ticket if space is currently reserved
558
+		if ((bool) $this->get_extra_meta(EE_Registration::HAS_RESERVED_TICKET_KEY, true) === true) {
559
+			$reserved = $this->update_extra_meta(EE_Registration::HAS_RESERVED_TICKET_KEY, false);
560
+			if ($reserved && $update_ticket) {
561
+				$ticket = $this->ticket();
562
+				$ticket->decreaseReserved(1, true, "REG: {$this->ID()} (ln:" . __LINE__ . ')');
563
+				$this->update_extra_meta('release_reserved_ticket', "{$this->ticket_ID()} from {$source}");
564
+			}
565
+		}
566
+	}
567
+
568
+
569
+	/**
570
+	 * Set Attendee ID
571
+	 *
572
+	 * @param        int $ATT_ID Attendee ID
573
+	 * @throws DomainException
574
+	 * @throws EE_Error
575
+	 * @throws EntityNotFoundException
576
+	 * @throws InvalidArgumentException
577
+	 * @throws InvalidDataTypeException
578
+	 * @throws InvalidInterfaceException
579
+	 * @throws ReflectionException
580
+	 * @throws RuntimeException
581
+	 * @throws UnexpectedEntityException
582
+	 */
583
+	public function set_attendee_id($ATT_ID = 0)
584
+	{
585
+		$this->set('ATT_ID', $ATT_ID);
586
+	}
587
+
588
+
589
+	/**
590
+	 *        Set Transaction ID
591
+	 *
592
+	 * @param        int $TXN_ID Transaction ID
593
+	 * @throws DomainException
594
+	 * @throws EE_Error
595
+	 * @throws EntityNotFoundException
596
+	 * @throws InvalidArgumentException
597
+	 * @throws InvalidDataTypeException
598
+	 * @throws InvalidInterfaceException
599
+	 * @throws ReflectionException
600
+	 * @throws RuntimeException
601
+	 * @throws UnexpectedEntityException
602
+	 */
603
+	public function set_transaction_id($TXN_ID = 0)
604
+	{
605
+		$this->set('TXN_ID', $TXN_ID);
606
+	}
607
+
608
+
609
+	/**
610
+	 *        Set Session
611
+	 *
612
+	 * @param    string $REG_session PHP Session ID
613
+	 * @throws DomainException
614
+	 * @throws EE_Error
615
+	 * @throws EntityNotFoundException
616
+	 * @throws InvalidArgumentException
617
+	 * @throws InvalidDataTypeException
618
+	 * @throws InvalidInterfaceException
619
+	 * @throws ReflectionException
620
+	 * @throws RuntimeException
621
+	 * @throws UnexpectedEntityException
622
+	 */
623
+	public function set_session($REG_session = '')
624
+	{
625
+		$this->set('REG_session', $REG_session);
626
+	}
627
+
628
+
629
+	/**
630
+	 *        Set Registration URL Link
631
+	 *
632
+	 * @param    string $REG_url_link Registration URL Link
633
+	 * @throws DomainException
634
+	 * @throws EE_Error
635
+	 * @throws EntityNotFoundException
636
+	 * @throws InvalidArgumentException
637
+	 * @throws InvalidDataTypeException
638
+	 * @throws InvalidInterfaceException
639
+	 * @throws ReflectionException
640
+	 * @throws RuntimeException
641
+	 * @throws UnexpectedEntityException
642
+	 */
643
+	public function set_reg_url_link($REG_url_link = '')
644
+	{
645
+		$this->set('REG_url_link', $REG_url_link);
646
+	}
647
+
648
+
649
+	/**
650
+	 *        Set Attendee Counter
651
+	 *
652
+	 * @param        int $REG_count Primary Attendee
653
+	 * @throws DomainException
654
+	 * @throws EE_Error
655
+	 * @throws EntityNotFoundException
656
+	 * @throws InvalidArgumentException
657
+	 * @throws InvalidDataTypeException
658
+	 * @throws InvalidInterfaceException
659
+	 * @throws ReflectionException
660
+	 * @throws RuntimeException
661
+	 * @throws UnexpectedEntityException
662
+	 */
663
+	public function set_count($REG_count = 1)
664
+	{
665
+		$this->set('REG_count', $REG_count);
666
+	}
667
+
668
+
669
+	/**
670
+	 *        Set Group Size
671
+	 *
672
+	 * @param        boolean $REG_group_size Group Registration
673
+	 * @throws DomainException
674
+	 * @throws EE_Error
675
+	 * @throws EntityNotFoundException
676
+	 * @throws InvalidArgumentException
677
+	 * @throws InvalidDataTypeException
678
+	 * @throws InvalidInterfaceException
679
+	 * @throws ReflectionException
680
+	 * @throws RuntimeException
681
+	 * @throws UnexpectedEntityException
682
+	 */
683
+	public function set_group_size($REG_group_size = false)
684
+	{
685
+		$this->set('REG_group_size', $REG_group_size);
686
+	}
687
+
688
+
689
+	/**
690
+	 *    is_not_approved -  convenience method that returns TRUE if REG status ID ==
691
+	 *    EEM_Registration::status_id_not_approved
692
+	 *
693
+	 * @return        boolean
694
+	 * @throws EE_Error
695
+	 * @throws InvalidArgumentException
696
+	 * @throws InvalidDataTypeException
697
+	 * @throws InvalidInterfaceException
698
+	 * @throws ReflectionException
699
+	 */
700
+	public function is_not_approved()
701
+	{
702
+		return $this->status_ID() === EEM_Registration::status_id_not_approved;
703
+	}
704
+
705
+
706
+	/**
707
+	 *    is_pending_payment -  convenience method that returns TRUE if REG status ID ==
708
+	 *    EEM_Registration::status_id_pending_payment
709
+	 *
710
+	 * @return        boolean
711
+	 * @throws EE_Error
712
+	 * @throws InvalidArgumentException
713
+	 * @throws InvalidDataTypeException
714
+	 * @throws InvalidInterfaceException
715
+	 * @throws ReflectionException
716
+	 */
717
+	public function is_pending_payment()
718
+	{
719
+		return $this->status_ID() === EEM_Registration::status_id_pending_payment;
720
+	}
721
+
722
+
723
+	/**
724
+	 *    is_approved -  convenience method that returns TRUE if REG status ID == EEM_Registration::status_id_approved
725
+	 *
726
+	 * @return        boolean
727
+	 * @throws EE_Error
728
+	 * @throws InvalidArgumentException
729
+	 * @throws InvalidDataTypeException
730
+	 * @throws InvalidInterfaceException
731
+	 * @throws ReflectionException
732
+	 */
733
+	public function is_approved()
734
+	{
735
+		return $this->status_ID() === EEM_Registration::status_id_approved;
736
+	}
737
+
738
+
739
+	/**
740
+	 *    is_cancelled -  convenience method that returns TRUE if REG status ID == EEM_Registration::status_id_cancelled
741
+	 *
742
+	 * @return        boolean
743
+	 * @throws EE_Error
744
+	 * @throws InvalidArgumentException
745
+	 * @throws InvalidDataTypeException
746
+	 * @throws InvalidInterfaceException
747
+	 * @throws ReflectionException
748
+	 */
749
+	public function is_cancelled()
750
+	{
751
+		return $this->status_ID() === EEM_Registration::status_id_cancelled;
752
+	}
753
+
754
+
755
+	/**
756
+	 *    is_declined -  convenience method that returns TRUE if REG status ID == EEM_Registration::status_id_declined
757
+	 *
758
+	 * @return        boolean
759
+	 * @throws EE_Error
760
+	 * @throws InvalidArgumentException
761
+	 * @throws InvalidDataTypeException
762
+	 * @throws InvalidInterfaceException
763
+	 * @throws ReflectionException
764
+	 */
765
+	public function is_declined()
766
+	{
767
+		return $this->status_ID() === EEM_Registration::status_id_declined;
768
+	}
769
+
770
+
771
+	/**
772
+	 *    is_incomplete -  convenience method that returns TRUE if REG status ID ==
773
+	 *    EEM_Registration::status_id_incomplete
774
+	 *
775
+	 * @return        boolean
776
+	 * @throws EE_Error
777
+	 * @throws InvalidArgumentException
778
+	 * @throws InvalidDataTypeException
779
+	 * @throws InvalidInterfaceException
780
+	 * @throws ReflectionException
781
+	 */
782
+	public function is_incomplete()
783
+	{
784
+		return $this->status_ID() === EEM_Registration::status_id_incomplete;
785
+	}
786
+
787
+
788
+	/**
789
+	 *        Set Registration Date
790
+	 *
791
+	 * @param        mixed ( int or string ) $REG_date Registration Date - Unix timestamp or string representation of
792
+	 *                                                 Date
793
+	 * @throws DomainException
794
+	 * @throws EE_Error
795
+	 * @throws EntityNotFoundException
796
+	 * @throws InvalidArgumentException
797
+	 * @throws InvalidDataTypeException
798
+	 * @throws InvalidInterfaceException
799
+	 * @throws ReflectionException
800
+	 * @throws RuntimeException
801
+	 * @throws UnexpectedEntityException
802
+	 */
803
+	public function set_reg_date($REG_date = false)
804
+	{
805
+		$this->set('REG_date', $REG_date);
806
+	}
807
+
808
+
809
+	/**
810
+	 *    Set final price owing for this registration after all ticket/price modifications
811
+	 *
812
+	 * @param    float $REG_final_price
813
+	 * @throws DomainException
814
+	 * @throws EE_Error
815
+	 * @throws EntityNotFoundException
816
+	 * @throws InvalidArgumentException
817
+	 * @throws InvalidDataTypeException
818
+	 * @throws InvalidInterfaceException
819
+	 * @throws ReflectionException
820
+	 * @throws RuntimeException
821
+	 * @throws UnexpectedEntityException
822
+	 */
823
+	public function set_final_price($REG_final_price = 0.00)
824
+	{
825
+		$this->set('REG_final_price', $REG_final_price);
826
+	}
827
+
828
+
829
+	/**
830
+	 *    Set amount paid towards this registration's final price
831
+	 *
832
+	 * @param    float $REG_paid
833
+	 * @throws DomainException
834
+	 * @throws EE_Error
835
+	 * @throws EntityNotFoundException
836
+	 * @throws InvalidArgumentException
837
+	 * @throws InvalidDataTypeException
838
+	 * @throws InvalidInterfaceException
839
+	 * @throws ReflectionException
840
+	 * @throws RuntimeException
841
+	 * @throws UnexpectedEntityException
842
+	 */
843
+	public function set_paid($REG_paid = 0.00)
844
+	{
845
+		$this->set('REG_paid', $REG_paid);
846
+	}
847
+
848
+
849
+	/**
850
+	 *        Attendee Is Going
851
+	 *
852
+	 * @param        boolean $REG_att_is_going Attendee Is Going
853
+	 * @throws DomainException
854
+	 * @throws EE_Error
855
+	 * @throws EntityNotFoundException
856
+	 * @throws InvalidArgumentException
857
+	 * @throws InvalidDataTypeException
858
+	 * @throws InvalidInterfaceException
859
+	 * @throws ReflectionException
860
+	 * @throws RuntimeException
861
+	 * @throws UnexpectedEntityException
862
+	 */
863
+	public function set_att_is_going($REG_att_is_going = false)
864
+	{
865
+		$this->set('REG_att_is_going', $REG_att_is_going);
866
+	}
867
+
868
+
869
+	/**
870
+	 * Gets the related attendee
871
+	 *
872
+	 * @return EE_Attendee|EE_Base_Class
873
+	 * @throws EE_Error
874
+	 * @throws InvalidArgumentException
875
+	 * @throws InvalidDataTypeException
876
+	 * @throws InvalidInterfaceException
877
+	 * @throws ReflectionException
878
+	 */
879
+	public function attendee()
880
+	{
881
+		return $this->get_first_related('Attendee');
882
+	}
883
+
884
+	/**
885
+	 * Gets the name of the attendee.
886
+	 * @since 4.10.12.p
887
+	 * @param bool $apply_html_entities set to true if you want to use HTML entities.
888
+	 * @return string
889
+	 * @throws EE_Error
890
+	 * @throws InvalidArgumentException
891
+	 * @throws InvalidDataTypeException
892
+	 * @throws InvalidInterfaceException
893
+	 * @throws ReflectionException
894
+	 */
895
+	public function attendeeName($apply_html_entities = false)
896
+	{
897
+		$attendee = $this->get_first_related('Attendee');
898
+		if ($attendee instanceof EE_Attendee) {
899
+			$attendee_name = $attendee->full_name($apply_html_entities);
900
+		} else {
901
+			$attendee_name = esc_html__('Unknown', 'event_espresso');
902
+		}
903
+		return $attendee_name;
904
+	}
905
+
906
+
907
+	/**
908
+	 *        get Event ID
909
+	 */
910
+	public function event_ID()
911
+	{
912
+		return $this->get('EVT_ID');
913
+	}
914
+
915
+
916
+	/**
917
+	 *        get Event ID
918
+	 */
919
+	public function event_name()
920
+	{
921
+		$event = $this->event_obj();
922
+		if ($event) {
923
+			return $event->name();
924
+		} else {
925
+			return null;
926
+		}
927
+	}
928
+
929
+
930
+	/**
931
+	 * Fetches the event this registration is for
932
+	 *
933
+	 * @return EE_Base_Class|EE_Event
934
+	 * @throws EE_Error
935
+	 * @throws InvalidArgumentException
936
+	 * @throws InvalidDataTypeException
937
+	 * @throws InvalidInterfaceException
938
+	 * @throws ReflectionException
939
+	 */
940
+	public function event_obj()
941
+	{
942
+		return $this->get_first_related('Event');
943
+	}
944
+
945
+
946
+	/**
947
+	 *        get Attendee ID
948
+	 */
949
+	public function attendee_ID()
950
+	{
951
+		return $this->get('ATT_ID');
952
+	}
953
+
954
+
955
+	/**
956
+	 *        get PHP Session ID
957
+	 */
958
+	public function session_ID()
959
+	{
960
+		return $this->get('REG_session');
961
+	}
962
+
963
+
964
+	/**
965
+	 * Gets the string which represents the URL trigger for the receipt template in the message template system.
966
+	 *
967
+	 * @param string $messenger 'pdf' or 'html'.  Default 'html'.
968
+	 * @return string
969
+	 * @throws DomainException
970
+	 * @throws EE_Error
971
+	 * @throws InvalidArgumentException
972
+	 * @throws InvalidDataTypeException
973
+	 * @throws InvalidInterfaceException
974
+	 * @throws ReflectionException
975
+	 */
976
+	public function receipt_url($messenger = 'html')
977
+	{
978
+
979
+		/**
980
+		 * The below will be deprecated one version after this.  We check first if there is a custom receipt template
981
+		 * already in use on old system.  If there is then we just return the standard url for it.
982
+		 *
983
+		 * @since 4.5.0
984
+		 */
985
+		$template_relative_path = 'modules/gateways/Invoice/lib/templates/receipt_body.template.php';
986
+		$has_custom = EEH_Template::locate_template(
987
+			$template_relative_path,
988
+			array(),
989
+			true,
990
+			true,
991
+			true
992
+		);
993
+
994
+		if ($has_custom) {
995
+			return add_query_arg(array('receipt' => 'true'), $this->invoice_url('launch'));
996
+		}
997
+		return apply_filters('FHEE__EE_Registration__receipt_url__receipt_url', '', $this, $messenger, 'receipt');
998
+	}
999
+
1000
+
1001
+	/**
1002
+	 * Gets the string which represents the URL trigger for the invoice template in the message template system.
1003
+	 *
1004
+	 * @param string $messenger 'pdf' or 'html'.  Default 'html'.
1005
+	 * @return string
1006
+	 * @throws DomainException
1007
+	 * @throws EE_Error
1008
+	 * @throws InvalidArgumentException
1009
+	 * @throws InvalidDataTypeException
1010
+	 * @throws InvalidInterfaceException
1011
+	 * @throws ReflectionException
1012
+	 */
1013
+	public function invoice_url($messenger = 'html')
1014
+	{
1015
+		/**
1016
+		 * The below will be deprecated one version after this.  We check first if there is a custom invoice template
1017
+		 * already in use on old system.  If there is then we just return the standard url for it.
1018
+		 *
1019
+		 * @since 4.5.0
1020
+		 */
1021
+		$template_relative_path = 'modules/gateways/Invoice/lib/templates/invoice_body.template.php';
1022
+		$has_custom = EEH_Template::locate_template(
1023
+			$template_relative_path,
1024
+			array(),
1025
+			true,
1026
+			true,
1027
+			true
1028
+		);
1029
+
1030
+		if ($has_custom) {
1031
+			if ($messenger == 'html') {
1032
+				return $this->invoice_url('launch');
1033
+			}
1034
+			$route = $messenger == 'download' || $messenger == 'pdf' ? 'download_invoice' : 'launch_invoice';
1035
+
1036
+			$query_args = array('ee' => $route, 'id' => $this->reg_url_link());
1037
+			if ($messenger == 'html') {
1038
+				$query_args['html'] = true;
1039
+			}
1040
+			return add_query_arg($query_args, get_permalink(EE_Registry::instance()->CFG->core->thank_you_page_id));
1041
+		}
1042
+		return apply_filters('FHEE__EE_Registration__invoice_url__invoice_url', '', $this, $messenger, 'invoice');
1043
+	}
1044
+
1045
+
1046
+	/**
1047
+	 * get Registration URL Link
1048
+	 *
1049
+	 * @return string
1050
+	 * @throws EE_Error
1051
+	 * @throws InvalidArgumentException
1052
+	 * @throws InvalidDataTypeException
1053
+	 * @throws InvalidInterfaceException
1054
+	 * @throws ReflectionException
1055
+	 */
1056
+	public function reg_url_link()
1057
+	{
1058
+		return (string) $this->get('REG_url_link');
1059
+	}
1060
+
1061
+
1062
+	/**
1063
+	 * Echoes out invoice_url()
1064
+	 *
1065
+	 * @param string $type 'download','launch', or 'html' (default is 'launch')
1066
+	 * @return void
1067
+	 * @throws DomainException
1068
+	 * @throws EE_Error
1069
+	 * @throws InvalidArgumentException
1070
+	 * @throws InvalidDataTypeException
1071
+	 * @throws InvalidInterfaceException
1072
+	 * @throws ReflectionException
1073
+	 */
1074
+	public function e_invoice_url($type = 'launch')
1075
+	{
1076
+		echo esc_url_raw($this->invoice_url($type));
1077
+	}
1078
+
1079
+
1080
+	/**
1081
+	 * Echoes out payment_overview_url
1082
+	 */
1083
+	public function e_payment_overview_url()
1084
+	{
1085
+		echo esc_url_raw($this->payment_overview_url());
1086
+	}
1087
+
1088
+
1089
+	/**
1090
+	 * Gets the URL for the checkout payment options reg step
1091
+	 * with this registration's REG_url_link added as a query parameter
1092
+	 *
1093
+	 * @param bool $clear_session Set to true when you want to clear the session on revisiting the
1094
+	 *                            payment overview url.
1095
+	 * @return string
1096
+	 * @throws EE_Error
1097
+	 * @throws InvalidArgumentException
1098
+	 * @throws InvalidDataTypeException
1099
+	 * @throws InvalidInterfaceException
1100
+	 * @throws ReflectionException
1101
+	 */
1102
+	public function payment_overview_url($clear_session = false)
1103
+	{
1104
+		return add_query_arg(
1105
+			(array) apply_filters(
1106
+				'FHEE__EE_Registration__payment_overview_url__query_args',
1107
+				array(
1108
+					'e_reg_url_link' => $this->reg_url_link(),
1109
+					'step'           => 'payment_options',
1110
+					'revisit'        => true,
1111
+					'clear_session'  => (bool) $clear_session,
1112
+				),
1113
+				$this
1114
+			),
1115
+			EE_Registry::instance()->CFG->core->reg_page_url()
1116
+		);
1117
+	}
1118
+
1119
+
1120
+	/**
1121
+	 * Gets the URL for the checkout attendee information reg step
1122
+	 * with this registration's REG_url_link added as a query parameter
1123
+	 *
1124
+	 * @return string
1125
+	 * @throws EE_Error
1126
+	 * @throws InvalidArgumentException
1127
+	 * @throws InvalidDataTypeException
1128
+	 * @throws InvalidInterfaceException
1129
+	 * @throws ReflectionException
1130
+	 */
1131
+	public function edit_attendee_information_url()
1132
+	{
1133
+		return add_query_arg(
1134
+			(array) apply_filters(
1135
+				'FHEE__EE_Registration__edit_attendee_information_url__query_args',
1136
+				array(
1137
+					'e_reg_url_link' => $this->reg_url_link(),
1138
+					'step'           => 'attendee_information',
1139
+					'revisit'        => true,
1140
+				),
1141
+				$this
1142
+			),
1143
+			EE_Registry::instance()->CFG->core->reg_page_url()
1144
+		);
1145
+	}
1146
+
1147
+
1148
+	/**
1149
+	 * Simply generates and returns the appropriate admin_url link to edit this registration
1150
+	 *
1151
+	 * @return string
1152
+	 * @throws EE_Error
1153
+	 * @throws InvalidArgumentException
1154
+	 * @throws InvalidDataTypeException
1155
+	 * @throws InvalidInterfaceException
1156
+	 * @throws ReflectionException
1157
+	 */
1158
+	public function get_admin_edit_url()
1159
+	{
1160
+		return EEH_URL::add_query_args_and_nonce(
1161
+			array(
1162
+				'page'    => 'espresso_registrations',
1163
+				'action'  => 'view_registration',
1164
+				'_REG_ID' => $this->ID(),
1165
+			),
1166
+			admin_url('admin.php')
1167
+		);
1168
+	}
1169
+
1170
+
1171
+	/**
1172
+	 * is_primary_registrant?
1173
+	 *
1174
+	 * @throws EE_Error
1175
+	 * @throws InvalidArgumentException
1176
+	 * @throws InvalidDataTypeException
1177
+	 * @throws InvalidInterfaceException
1178
+	 * @throws ReflectionException
1179
+	 */
1180
+	public function is_primary_registrant()
1181
+	{
1182
+		return (int) $this->get('REG_count') === 1;
1183
+	}
1184
+
1185
+
1186
+	/**
1187
+	 * This returns the primary registration object for this registration group (which may be this object).
1188
+	 *
1189
+	 * @return EE_Registration
1190
+	 * @throws EE_Error
1191
+	 * @throws InvalidArgumentException
1192
+	 * @throws InvalidDataTypeException
1193
+	 * @throws InvalidInterfaceException
1194
+	 * @throws ReflectionException
1195
+	 */
1196
+	public function get_primary_registration()
1197
+	{
1198
+		if ($this->is_primary_registrant()) {
1199
+			return $this;
1200
+		}
1201
+
1202
+		// k reg_count !== 1 so let's get the EE_Registration object matching this txn_id and reg_count == 1
1203
+		/** @var EE_Registration $primary_registrant */
1204
+		$primary_registrant = EEM_Registration::instance()->get_one(
1205
+			array(
1206
+				array(
1207
+					'TXN_ID'    => $this->transaction_ID(),
1208
+					'REG_count' => 1,
1209
+				),
1210
+			)
1211
+		);
1212
+		return $primary_registrant;
1213
+	}
1214
+
1215
+
1216
+	/**
1217
+	 * get  Attendee Number
1218
+	 *
1219
+	 * @throws EE_Error
1220
+	 * @throws InvalidArgumentException
1221
+	 * @throws InvalidDataTypeException
1222
+	 * @throws InvalidInterfaceException
1223
+	 * @throws ReflectionException
1224
+	 */
1225
+	public function count()
1226
+	{
1227
+		return $this->get('REG_count');
1228
+	}
1229
+
1230
+
1231
+	/**
1232
+	 * get Group Size
1233
+	 *
1234
+	 * @throws EE_Error
1235
+	 * @throws InvalidArgumentException
1236
+	 * @throws InvalidDataTypeException
1237
+	 * @throws InvalidInterfaceException
1238
+	 * @throws ReflectionException
1239
+	 */
1240
+	public function group_size()
1241
+	{
1242
+		return $this->get('REG_group_size');
1243
+	}
1244
+
1245
+
1246
+	/**
1247
+	 * get Registration Date
1248
+	 *
1249
+	 * @throws EE_Error
1250
+	 * @throws InvalidArgumentException
1251
+	 * @throws InvalidDataTypeException
1252
+	 * @throws InvalidInterfaceException
1253
+	 * @throws ReflectionException
1254
+	 */
1255
+	public function date()
1256
+	{
1257
+		return $this->get('REG_date');
1258
+	}
1259
+
1260
+
1261
+	/**
1262
+	 * gets a pretty date
1263
+	 *
1264
+	 * @param string $date_format
1265
+	 * @param string $time_format
1266
+	 * @return string
1267
+	 * @throws EE_Error
1268
+	 * @throws InvalidArgumentException
1269
+	 * @throws InvalidDataTypeException
1270
+	 * @throws InvalidInterfaceException
1271
+	 * @throws ReflectionException
1272
+	 */
1273
+	public function pretty_date($date_format = null, $time_format = null)
1274
+	{
1275
+		return $this->get_datetime('REG_date', $date_format, $time_format);
1276
+	}
1277
+
1278
+
1279
+	/**
1280
+	 * final_price
1281
+	 * the registration's share of the transaction total, so that the
1282
+	 * sum of all the transaction's REG_final_prices equal the transaction's total
1283
+	 *
1284
+	 * @return float
1285
+	 * @throws EE_Error
1286
+	 * @throws InvalidArgumentException
1287
+	 * @throws InvalidDataTypeException
1288
+	 * @throws InvalidInterfaceException
1289
+	 * @throws ReflectionException
1290
+	 */
1291
+	public function final_price()
1292
+	{
1293
+		return $this->get('REG_final_price');
1294
+	}
1295
+
1296
+
1297
+	/**
1298
+	 * pretty_final_price
1299
+	 *  final price as formatted string, with correct decimal places and currency symbol
1300
+	 *
1301
+	 * @return string
1302
+	 * @throws EE_Error
1303
+	 * @throws InvalidArgumentException
1304
+	 * @throws InvalidDataTypeException
1305
+	 * @throws InvalidInterfaceException
1306
+	 * @throws ReflectionException
1307
+	 */
1308
+	public function pretty_final_price()
1309
+	{
1310
+		return $this->get_pretty('REG_final_price');
1311
+	}
1312
+
1313
+
1314
+	/**
1315
+	 * get paid (yeah)
1316
+	 *
1317
+	 * @return float
1318
+	 * @throws EE_Error
1319
+	 * @throws InvalidArgumentException
1320
+	 * @throws InvalidDataTypeException
1321
+	 * @throws InvalidInterfaceException
1322
+	 * @throws ReflectionException
1323
+	 */
1324
+	public function paid()
1325
+	{
1326
+		return $this->get('REG_paid');
1327
+	}
1328
+
1329
+
1330
+	/**
1331
+	 * pretty_paid
1332
+	 *
1333
+	 * @return float
1334
+	 * @throws EE_Error
1335
+	 * @throws InvalidArgumentException
1336
+	 * @throws InvalidDataTypeException
1337
+	 * @throws InvalidInterfaceException
1338
+	 * @throws ReflectionException
1339
+	 */
1340
+	public function pretty_paid()
1341
+	{
1342
+		return $this->get_pretty('REG_paid');
1343
+	}
1344
+
1345
+
1346
+	/**
1347
+	 * owes_monies_and_can_pay
1348
+	 * whether or not this registration has monies owing and it's' status allows payment
1349
+	 *
1350
+	 * @param array $requires_payment
1351
+	 * @return bool
1352
+	 * @throws EE_Error
1353
+	 * @throws InvalidArgumentException
1354
+	 * @throws InvalidDataTypeException
1355
+	 * @throws InvalidInterfaceException
1356
+	 * @throws ReflectionException
1357
+	 */
1358
+	public function owes_monies_and_can_pay($requires_payment = array())
1359
+	{
1360
+		// these reg statuses require payment (if event is not free)
1361
+		$requires_payment = ! empty($requires_payment)
1362
+			? $requires_payment
1363
+			: EEM_Registration::reg_statuses_that_allow_payment();
1364
+		if (
1365
+			$this->final_price() !== 0 &&
1366
+			$this->final_price() !== $this->paid() &&
1367
+			in_array($this->status_ID(), $requires_payment)
1368
+		) {
1369
+			return true;
1370
+		}
1371
+		return false;
1372
+	}
1373
+
1374
+
1375
+	/**
1376
+	 * Prints out the return value of $this->pretty_status()
1377
+	 *
1378
+	 * @param bool $show_icons
1379
+	 * @return void
1380
+	 * @throws EE_Error
1381
+	 * @throws InvalidArgumentException
1382
+	 * @throws InvalidDataTypeException
1383
+	 * @throws InvalidInterfaceException
1384
+	 * @throws ReflectionException
1385
+	 */
1386
+	public function e_pretty_status($show_icons = false)
1387
+	{
1388
+		echo wp_kses($this->pretty_status($show_icons), AllowedTags::getAllowedTags());
1389
+	}
1390
+
1391
+
1392
+	/**
1393
+	 * Returns a nice version of the status for displaying to customers
1394
+	 *
1395
+	 * @param bool $show_icons
1396
+	 * @return string
1397
+	 * @throws EE_Error
1398
+	 * @throws InvalidArgumentException
1399
+	 * @throws InvalidDataTypeException
1400
+	 * @throws InvalidInterfaceException
1401
+	 * @throws ReflectionException
1402
+	 */
1403
+	public function pretty_status($show_icons = false)
1404
+	{
1405
+		$status = EEM_Status::instance()->localized_status(
1406
+			array($this->status_ID() => esc_html__('unknown', 'event_espresso')),
1407
+			false,
1408
+			'sentence'
1409
+		);
1410
+		$icon = '';
1411
+		switch ($this->status_ID()) {
1412
+			case EEM_Registration::status_id_approved:
1413
+				$icon = $show_icons
1414
+					? '<span class="dashicons dashicons-star-filled ee-icon-size-16 green-text"></span>'
1415
+					: '';
1416
+				break;
1417
+			case EEM_Registration::status_id_pending_payment:
1418
+				$icon = $show_icons
1419
+					? '<span class="dashicons dashicons-star-half ee-icon-size-16 orange-text"></span>'
1420
+					: '';
1421
+				break;
1422
+			case EEM_Registration::status_id_not_approved:
1423
+				$icon = $show_icons
1424
+					? '<span class="dashicons dashicons-marker ee-icon-size-16 orange-text"></span>'
1425
+					: '';
1426
+				break;
1427
+			case EEM_Registration::status_id_cancelled:
1428
+				$icon = $show_icons
1429
+					? '<span class="dashicons dashicons-no ee-icon-size-16 lt-grey-text"></span>'
1430
+					: '';
1431
+				break;
1432
+			case EEM_Registration::status_id_incomplete:
1433
+				$icon = $show_icons
1434
+					? '<span class="dashicons dashicons-no ee-icon-size-16 lt-orange-text"></span>'
1435
+					: '';
1436
+				break;
1437
+			case EEM_Registration::status_id_declined:
1438
+				$icon = $show_icons
1439
+					? '<span class="dashicons dashicons-no ee-icon-size-16 red-text"></span>'
1440
+					: '';
1441
+				break;
1442
+			case EEM_Registration::status_id_wait_list:
1443
+				$icon = $show_icons
1444
+					? '<span class="dashicons dashicons-clipboard ee-icon-size-16 purple-text"></span>'
1445
+					: '';
1446
+				break;
1447
+		}
1448
+		return $icon . $status[ $this->status_ID() ];
1449
+	}
1450
+
1451
+
1452
+	/**
1453
+	 *        get Attendee Is Going
1454
+	 */
1455
+	public function att_is_going()
1456
+	{
1457
+		return $this->get('REG_att_is_going');
1458
+	}
1459
+
1460
+
1461
+	/**
1462
+	 * Gets related answers
1463
+	 *
1464
+	 * @param array $query_params @see
1465
+	 *                            https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1466
+	 * @return EE_Answer[]|EE_Base_Class[]
1467
+	 * @throws EE_Error
1468
+	 * @throws InvalidArgumentException
1469
+	 * @throws InvalidDataTypeException
1470
+	 * @throws InvalidInterfaceException
1471
+	 * @throws ReflectionException
1472
+	 */
1473
+	public function answers($query_params = null)
1474
+	{
1475
+		return $this->get_many_related('Answer', $query_params);
1476
+	}
1477
+
1478
+
1479
+	/**
1480
+	 * Gets the registration's answer value to the specified question
1481
+	 * (either the question's ID or a question object)
1482
+	 *
1483
+	 * @param EE_Question|int $question
1484
+	 * @param bool            $pretty_value
1485
+	 * @return array|string if pretty_value= true, the result will always be a string
1486
+	 * (because the answer might be an array of answer values, so passing pretty_value=true
1487
+	 * will convert it into some kind of string)
1488
+	 * @throws EE_Error
1489
+	 * @throws InvalidArgumentException
1490
+	 * @throws InvalidDataTypeException
1491
+	 * @throws InvalidInterfaceException
1492
+	 */
1493
+	public function answer_value_to_question($question, $pretty_value = true)
1494
+	{
1495
+		$question_id = EEM_Question::instance()->ensure_is_ID($question);
1496
+		return EEM_Answer::instance()->get_answer_value_to_question($this, $question_id, $pretty_value);
1497
+	}
1498
+
1499
+
1500
+	/**
1501
+	 * question_groups
1502
+	 * returns an array of EE_Question_Group objects for this registration
1503
+	 *
1504
+	 * @return EE_Question_Group[]
1505
+	 * @throws EE_Error
1506
+	 * @throws InvalidArgumentException
1507
+	 * @throws InvalidDataTypeException
1508
+	 * @throws InvalidInterfaceException
1509
+	 * @throws ReflectionException
1510
+	 */
1511
+	public function question_groups()
1512
+	{
1513
+		return EEM_Event::instance()->get_question_groups_for_event($this->event_ID(), $this);
1514
+	}
1515
+
1516
+
1517
+	/**
1518
+	 * count_question_groups
1519
+	 * returns a count of the number of EE_Question_Group objects for this registration
1520
+	 *
1521
+	 * @return int
1522
+	 * @throws EE_Error
1523
+	 * @throws EntityNotFoundException
1524
+	 * @throws InvalidArgumentException
1525
+	 * @throws InvalidDataTypeException
1526
+	 * @throws InvalidInterfaceException
1527
+	 * @throws ReflectionException
1528
+	 */
1529
+	public function count_question_groups()
1530
+	{
1531
+		return EEM_Event::instance()->count_related(
1532
+			$this->event_ID(),
1533
+			'Question_Group',
1534
+			[
1535
+				[
1536
+					'Event_Question_Group.'
1537
+					. EEM_Event_Question_Group::instance()->fieldNameForContext($this->is_primary_registrant()) => true,
1538
+				]
1539
+			]
1540
+		);
1541
+	}
1542
+
1543
+
1544
+	/**
1545
+	 * Returns the registration date in the 'standard' string format
1546
+	 * (function may be improved in the future to allow for different formats and timezones)
1547
+	 *
1548
+	 * @return string
1549
+	 * @throws EE_Error
1550
+	 * @throws InvalidArgumentException
1551
+	 * @throws InvalidDataTypeException
1552
+	 * @throws InvalidInterfaceException
1553
+	 * @throws ReflectionException
1554
+	 */
1555
+	public function reg_date()
1556
+	{
1557
+		return $this->get_datetime('REG_date');
1558
+	}
1559
+
1560
+
1561
+	/**
1562
+	 * Gets the datetime-ticket for this registration (ie, it can be used to isolate
1563
+	 * the ticket this registration purchased, or the datetime they have registered
1564
+	 * to attend)
1565
+	 *
1566
+	 * @return EE_Base_Class|EE_Datetime_Ticket
1567
+	 * @throws EE_Error
1568
+	 * @throws InvalidArgumentException
1569
+	 * @throws InvalidDataTypeException
1570
+	 * @throws InvalidInterfaceException
1571
+	 * @throws ReflectionException
1572
+	 */
1573
+	public function datetime_ticket()
1574
+	{
1575
+		return $this->get_first_related('Datetime_Ticket');
1576
+	}
1577
+
1578
+
1579
+	/**
1580
+	 * Sets the registration's datetime_ticket.
1581
+	 *
1582
+	 * @param EE_Datetime_Ticket $datetime_ticket
1583
+	 * @return EE_Base_Class|EE_Datetime_Ticket
1584
+	 * @throws EE_Error
1585
+	 * @throws InvalidArgumentException
1586
+	 * @throws InvalidDataTypeException
1587
+	 * @throws InvalidInterfaceException
1588
+	 * @throws ReflectionException
1589
+	 */
1590
+	public function set_datetime_ticket($datetime_ticket)
1591
+	{
1592
+		return $this->_add_relation_to($datetime_ticket, 'Datetime_Ticket');
1593
+	}
1594
+
1595
+
1596
+	/**
1597
+	 * Gets deleted
1598
+	 *
1599
+	 * @return bool
1600
+	 * @throws EE_Error
1601
+	 * @throws InvalidArgumentException
1602
+	 * @throws InvalidDataTypeException
1603
+	 * @throws InvalidInterfaceException
1604
+	 * @throws ReflectionException
1605
+	 */
1606
+	public function deleted()
1607
+	{
1608
+		return $this->get('REG_deleted');
1609
+	}
1610
+
1611
+
1612
+	/**
1613
+	 * Sets deleted
1614
+	 *
1615
+	 * @param boolean $deleted
1616
+	 * @return void
1617
+	 * @throws DomainException
1618
+	 * @throws EE_Error
1619
+	 * @throws EntityNotFoundException
1620
+	 * @throws InvalidArgumentException
1621
+	 * @throws InvalidDataTypeException
1622
+	 * @throws InvalidInterfaceException
1623
+	 * @throws ReflectionException
1624
+	 * @throws RuntimeException
1625
+	 * @throws UnexpectedEntityException
1626
+	 */
1627
+	public function set_deleted($deleted)
1628
+	{
1629
+		if ($deleted) {
1630
+			$this->delete();
1631
+		} else {
1632
+			$this->restore();
1633
+		}
1634
+	}
1635
+
1636
+
1637
+	/**
1638
+	 * Get the status object of this object
1639
+	 *
1640
+	 * @return EE_Base_Class|EE_Status
1641
+	 * @throws EE_Error
1642
+	 * @throws InvalidArgumentException
1643
+	 * @throws InvalidDataTypeException
1644
+	 * @throws InvalidInterfaceException
1645
+	 * @throws ReflectionException
1646
+	 */
1647
+	public function status_obj()
1648
+	{
1649
+		return $this->get_first_related('Status');
1650
+	}
1651
+
1652
+
1653
+	/**
1654
+	 * Returns the number of times this registration has checked into any of the datetimes
1655
+	 * its available for
1656
+	 *
1657
+	 * @return int
1658
+	 * @throws EE_Error
1659
+	 * @throws InvalidArgumentException
1660
+	 * @throws InvalidDataTypeException
1661
+	 * @throws InvalidInterfaceException
1662
+	 * @throws ReflectionException
1663
+	 */
1664
+	public function count_checkins()
1665
+	{
1666
+		return $this->get_model()->count_related($this, 'Checkin');
1667
+	}
1668
+
1669
+
1670
+	/**
1671
+	 * Returns the number of current Check-ins this registration is checked into for any of the datetimes the
1672
+	 * registration is for.  Note, this is ONLY checked in (does not include checkedout)
1673
+	 *
1674
+	 * @return int
1675
+	 * @throws EE_Error
1676
+	 * @throws InvalidArgumentException
1677
+	 * @throws InvalidDataTypeException
1678
+	 * @throws InvalidInterfaceException
1679
+	 * @throws ReflectionException
1680
+	 */
1681
+	public function count_checkins_not_checkedout()
1682
+	{
1683
+		return $this->get_model()->count_related($this, 'Checkin', array(array('CHK_in' => 1)));
1684
+	}
1685
+
1686
+
1687
+	/**
1688
+	 * The purpose of this method is simply to check whether this registration can checkin to the given datetime.
1689
+	 *
1690
+	 * @param int | EE_Datetime $DTT_OR_ID      The datetime the registration is being checked against
1691
+	 * @param bool              $check_approved This is used to indicate whether the caller wants can_checkin to also
1692
+	 *                                          consider registration status as well as datetime access.
1693
+	 * @return bool
1694
+	 * @throws EE_Error
1695
+	 * @throws InvalidArgumentException
1696
+	 * @throws InvalidDataTypeException
1697
+	 * @throws InvalidInterfaceException
1698
+	 * @throws ReflectionException
1699
+	 */
1700
+	public function can_checkin($DTT_OR_ID, $check_approved = true)
1701
+	{
1702
+		$DTT_ID = EEM_Datetime::instance()->ensure_is_ID($DTT_OR_ID);
1703
+		// first check registration status
1704
+		if (! $DTT_ID || ($check_approved && ! $this->is_approved())) {
1705
+			return false;
1706
+		}
1707
+		// is there a datetime ticket that matches this dtt_ID?
1708
+		if (
1709
+			! (EEM_Datetime_Ticket::instance()->exists(
1710
+				array(
1711
+					array(
1712
+						'TKT_ID' => $this->get('TKT_ID'),
1713
+						'DTT_ID' => $DTT_ID,
1714
+					),
1715
+				)
1716
+			))
1717
+		) {
1718
+			return false;
1719
+		}
1720
+
1721
+		// final check is against TKT_uses
1722
+		return $this->verify_can_checkin_against_TKT_uses($DTT_ID);
1723
+	}
1724
+
1725
+
1726
+	/**
1727
+	 * This method verifies whether the user can checkin for the given datetime considering the max uses value set on
1728
+	 * the ticket. To do this,  a query is done to get the count of the datetime records already checked into.  If the
1729
+	 * datetime given does not have a check-in record and checking in for that datetime will exceed the allowed uses,
1730
+	 * then return false.  Otherwise return true.
1731
+	 *
1732
+	 * @param int | EE_Datetime $DTT_OR_ID The datetime the registration is being checked against
1733
+	 * @return bool true means can checkin.  false means cannot checkin.
1734
+	 * @throws EE_Error
1735
+	 * @throws InvalidArgumentException
1736
+	 * @throws InvalidDataTypeException
1737
+	 * @throws InvalidInterfaceException
1738
+	 * @throws ReflectionException
1739
+	 */
1740
+	public function verify_can_checkin_against_TKT_uses($DTT_OR_ID)
1741
+	{
1742
+		$DTT_ID = EEM_Datetime::instance()->ensure_is_ID($DTT_OR_ID);
1743
+
1744
+		if (! $DTT_ID) {
1745
+			return false;
1746
+		}
1747
+
1748
+		$max_uses = $this->ticket() instanceof EE_Ticket ? $this->ticket()->uses() : EE_INF;
1749
+
1750
+		// if max uses is not set or equals infinity then return true cause its not a factor for whether user can
1751
+		// check-in or not.
1752
+		if (! $max_uses || $max_uses === EE_INF) {
1753
+			return true;
1754
+		}
1755
+
1756
+		// does this datetime have a checkin record?  If so, then the dtt count has already been verified so we can just
1757
+		// go ahead and toggle.
1758
+		if (EEM_Checkin::instance()->exists(array(array('REG_ID' => $this->ID(), 'DTT_ID' => $DTT_ID)))) {
1759
+			return true;
1760
+		}
1761
+
1762
+		// made it here so the last check is whether the number of checkins per unique datetime on this registration
1763
+		// disallows further check-ins.
1764
+		$count_unique_dtt_checkins = EEM_Checkin::instance()->count(
1765
+			array(
1766
+				array(
1767
+					'REG_ID' => $this->ID(),
1768
+					'CHK_in' => true,
1769
+				),
1770
+			),
1771
+			'DTT_ID',
1772
+			true
1773
+		);
1774
+		// checkins have already reached their max number of uses
1775
+		// so registrant can NOT checkin
1776
+		if ($count_unique_dtt_checkins >= $max_uses) {
1777
+			EE_Error::add_error(
1778
+				esc_html__(
1779
+					'Check-in denied because number of datetime uses for the ticket has been reached or exceeded.',
1780
+					'event_espresso'
1781
+				),
1782
+				__FILE__,
1783
+				__FUNCTION__,
1784
+				__LINE__
1785
+			);
1786
+			return false;
1787
+		}
1788
+		return true;
1789
+	}
1790
+
1791
+
1792
+	/**
1793
+	 * toggle Check-in status for this registration
1794
+	 * Check-ins are toggled in the following order:
1795
+	 * never checked in -> checked in
1796
+	 * checked in -> checked out
1797
+	 * checked out -> checked in
1798
+	 *
1799
+	 * @param  int $DTT_ID  include specific datetime to toggle Check-in for.
1800
+	 *                      If not included or null, then it is assumed latest datetime is being toggled.
1801
+	 * @param bool $verify  If true then can_checkin() is used to verify whether the person
1802
+	 *                      can be checked in or not.  Otherwise this forces change in checkin status.
1803
+	 * @return bool|int     the chk_in status toggled to OR false if nothing got changed.
1804
+	 * @throws EE_Error
1805
+	 * @throws InvalidArgumentException
1806
+	 * @throws InvalidDataTypeException
1807
+	 * @throws InvalidInterfaceException
1808
+	 * @throws ReflectionException
1809
+	 */
1810
+	public function toggle_checkin_status($DTT_ID = null, $verify = false)
1811
+	{
1812
+		if (empty($DTT_ID)) {
1813
+			$datetime = $this->get_latest_related_datetime();
1814
+			$DTT_ID = $datetime instanceof EE_Datetime ? $datetime->ID() : 0;
1815
+			// verify the registration can checkin for the given DTT_ID
1816
+		} elseif (! $this->can_checkin($DTT_ID, $verify)) {
1817
+			EE_Error::add_error(
1818
+				sprintf(
1819
+					esc_html__(
1820
+						'The given registration (ID:%1$d) can not be checked in to the given DTT_ID (%2$d), because the registration does not have access',
1821
+						'event_espresso'
1822
+					),
1823
+					$this->ID(),
1824
+					$DTT_ID
1825
+				),
1826
+				__FILE__,
1827
+				__FUNCTION__,
1828
+				__LINE__
1829
+			);
1830
+			return false;
1831
+		}
1832
+		$status_paths = array(
1833
+			EE_Checkin::status_checked_never => EE_Checkin::status_checked_in,
1834
+			EE_Checkin::status_checked_in    => EE_Checkin::status_checked_out,
1835
+			EE_Checkin::status_checked_out   => EE_Checkin::status_checked_in,
1836
+		);
1837
+		// start by getting the current status so we know what status we'll be changing to.
1838
+		$cur_status = $this->check_in_status_for_datetime($DTT_ID);
1839
+		$status_to = $status_paths[ $cur_status ];
1840
+		// database only records true for checked IN or false for checked OUT
1841
+		// no record ( null ) means checked in NEVER, but we obviously don't save that
1842
+		$new_status = $status_to === EE_Checkin::status_checked_in;
1843
+		// add relation - note Check-ins are always creating new rows
1844
+		// because we are keeping track of Check-ins over time.
1845
+		// Eventually we'll probably want to show a list table
1846
+		// for the individual Check-ins so that they can be managed.
1847
+		$checkin = EE_Checkin::new_instance(
1848
+			array(
1849
+				'REG_ID' => $this->ID(),
1850
+				'DTT_ID' => $DTT_ID,
1851
+				'CHK_in' => $new_status,
1852
+			)
1853
+		);
1854
+		// if the record could not be saved then return false
1855
+		if ($checkin->save() === 0) {
1856
+			if (WP_DEBUG) {
1857
+				global $wpdb;
1858
+				$error = sprintf(
1859
+					esc_html__(
1860
+						'Registration check in update failed because of the following database error: %1$s%2$s',
1861
+						'event_espresso'
1862
+					),
1863
+					'<br />',
1864
+					$wpdb->last_error
1865
+				);
1866
+			} else {
1867
+				$error = esc_html__(
1868
+					'Registration check in update failed because of an unknown database error',
1869
+					'event_espresso'
1870
+				);
1871
+			}
1872
+			EE_Error::add_error($error, __FILE__, __FUNCTION__, __LINE__);
1873
+			return false;
1874
+		}
1875
+		// Fire a checked_in and checkout_out action.
1876
+		$checked_status = $status_to === EE_Checkin::status_checked_in ? 'checked_in' : 'checked_out';
1877
+		do_action("AHEE__EE_Registration__toggle_checkin_status__{$checked_status}", $this, $DTT_ID);
1878
+		return $status_to;
1879
+	}
1880
+
1881
+
1882
+	/**
1883
+	 * Returns the latest datetime related to this registration (via the ticket attached to the registration).
1884
+	 * "Latest" is defined by the `DTT_EVT_start` column.
1885
+	 *
1886
+	 * @return EE_Datetime|null
1887
+	 * @throws EE_Error
1888
+	 * @throws InvalidArgumentException
1889
+	 * @throws InvalidDataTypeException
1890
+	 * @throws InvalidInterfaceException
1891
+	 * @throws ReflectionException
1892
+	 */
1893
+	public function get_latest_related_datetime()
1894
+	{
1895
+		return EEM_Datetime::instance()->get_one(
1896
+			array(
1897
+				array(
1898
+					'Ticket.Registration.REG_ID' => $this->ID(),
1899
+				),
1900
+				'order_by' => array('DTT_EVT_start' => 'DESC'),
1901
+			)
1902
+		);
1903
+	}
1904
+
1905
+
1906
+	/**
1907
+	 * Returns the earliest datetime related to this registration (via the ticket attached to the registration).
1908
+	 * "Earliest" is defined by the `DTT_EVT_start` column.
1909
+	 *
1910
+	 * @return EE_Base_Class|EE_Soft_Delete_Base_Class|NULL
1911
+	 * @throws EE_Error
1912
+	 * @throws InvalidArgumentException
1913
+	 * @throws InvalidDataTypeException
1914
+	 * @throws InvalidInterfaceException
1915
+	 * @throws ReflectionException
1916
+	 */
1917
+	public function get_earliest_related_datetime()
1918
+	{
1919
+		return EEM_Datetime::instance()->get_one(
1920
+			array(
1921
+				array(
1922
+					'Ticket.Registration.REG_ID' => $this->ID(),
1923
+				),
1924
+				'order_by' => array('DTT_EVT_start' => 'ASC'),
1925
+			)
1926
+		);
1927
+	}
1928
+
1929
+
1930
+	/**
1931
+	 * This method simply returns the check-in status for this registration and the given datetime.
1932
+	 * If neither the datetime nor the checkin values are provided as arguments,
1933
+	 * then this will return the LATEST check-in status for the registration across all datetimes it belongs to.
1934
+	 *
1935
+	 * @param int|null        $DTT_ID  The ID of the datetime we're checking against
1936
+	 *                                 (if empty we'll get the primary datetime for
1937
+	 *                                 this registration (via event) and use it's ID);
1938
+	 * @param EE_Checkin|null $checkin If present, we use the given checkin object rather than the dtt_id.
1939
+	 * @return int                     Integer representing Check-in status.
1940
+	 * @throws EE_Error
1941
+	 * @throws ReflectionException
1942
+	 */
1943
+	public function check_in_status_for_datetime(?int $DTT_ID = 0, ?EE_Checkin $checkin = null): int
1944
+	{
1945
+		if ($checkin instanceof EE_Checkin) {
1946
+			return $checkin->status();
1947
+		}
1948
+		// can't query checkin for a specific date if no ID was supplied
1949
+		if (empty($DTT_ID)) {
1950
+			return EE_Checkin::status_invalid;
1951
+		}
1952
+
1953
+		$checkin = $this->get_first_related(
1954
+			'Checkin',
1955
+			[
1956
+				['DTT_ID' => $DTT_ID],
1957
+				'order_by' => ['CHK_timestamp' => 'DESC'],
1958
+			]
1959
+		);
1960
+		return $checkin instanceof EE_Checkin ? $checkin->status() :  EE_Checkin::status_checked_never;
1961
+	}
1962
+
1963
+
1964
+	/**
1965
+	 * This method returns a localized message for the toggled Check-in message.
1966
+	 *
1967
+	 * @param int|null $DTT_ID include specific datetime to get the correct Check-in message.  If not included or null,
1968
+	 *                         then it is assumed Check-in for primary datetime was toggled.
1969
+	 * @param bool     $error  This just flags that you want an error message returned. This is put in so that the error
1970
+	 *                         message can be customized with the attendee name.
1971
+	 * @return string internationalized message
1972
+	 * @throws EE_Error
1973
+	 * @throws ReflectionException
1974
+	 */
1975
+	public function get_checkin_msg(?int $DTT_ID, bool $error = false): string
1976
+	{
1977
+		// let's get the attendee first so we can include the name of the attendee
1978
+		$attendee = $this->get_first_related('Attendee');
1979
+		if ($attendee instanceof EE_Attendee) {
1980
+			if ($error) {
1981
+				return sprintf(
1982
+					esc_html__("%s's check-in status was not changed.", "event_espresso"),
1983
+					$attendee->full_name()
1984
+				);
1985
+			}
1986
+			$cur_status = $this->check_in_status_for_datetime($DTT_ID);
1987
+			// what is the status message going to be?
1988
+			switch ($cur_status) {
1989
+				case EE_Checkin::status_checked_never:
1990
+					return sprintf(
1991
+						esc_html__('%s has been removed from Check-in records', 'event_espresso'),
1992
+						$attendee->full_name()
1993
+					);
1994
+				case EE_Checkin::status_checked_in:
1995
+					return sprintf(esc_html__('%s has been checked in', 'event_espresso'), $attendee->full_name());
1996
+				case EE_Checkin::status_checked_out:
1997
+					return sprintf(esc_html__('%s has been checked out', 'event_espresso'), $attendee->full_name());
1998
+			}
1999
+		}
2000
+		return esc_html__('The check-in status could not be determined.', 'event_espresso');
2001
+	}
2002
+
2003
+
2004
+	/**
2005
+	 * Returns the related EE_Transaction to this registration
2006
+	 *
2007
+	 * @return EE_Transaction
2008
+	 * @throws EE_Error
2009
+	 * @throws EntityNotFoundException
2010
+	 * @throws ReflectionException
2011
+	 */
2012
+	public function transaction(): EE_Transaction
2013
+	{
2014
+		$transaction = $this->get_first_related('Transaction');
2015
+		if (! $transaction instanceof \EE_Transaction) {
2016
+			throw new EntityNotFoundException('Transaction ID', $this->transaction_ID());
2017
+		}
2018
+		return $transaction;
2019
+	}
2020
+
2021
+
2022
+	/**
2023
+	 * get Registration Code
2024
+	 *
2025
+	 * @return mixed
2026
+	 * @throws EE_Error
2027
+	 * @throws InvalidArgumentException
2028
+	 * @throws InvalidDataTypeException
2029
+	 * @throws InvalidInterfaceException
2030
+	 * @throws ReflectionException
2031
+	 */
2032
+	public function reg_code()
2033
+	{
2034
+		return $this->get('REG_code');
2035
+	}
2036
+
2037
+
2038
+	/**
2039
+	 * @return mixed
2040
+	 * @throws EE_Error
2041
+	 * @throws InvalidArgumentException
2042
+	 * @throws InvalidDataTypeException
2043
+	 * @throws InvalidInterfaceException
2044
+	 * @throws ReflectionException
2045
+	 */
2046
+	public function transaction_ID()
2047
+	{
2048
+		return $this->get('TXN_ID');
2049
+	}
2050
+
2051
+
2052
+	/**
2053
+	 * @return int
2054
+	 * @throws EE_Error
2055
+	 * @throws InvalidArgumentException
2056
+	 * @throws InvalidDataTypeException
2057
+	 * @throws InvalidInterfaceException
2058
+	 * @throws ReflectionException
2059
+	 */
2060
+	public function ticket_ID()
2061
+	{
2062
+		return $this->get('TKT_ID');
2063
+	}
2064
+
2065
+
2066
+	/**
2067
+	 * Set Registration Code
2068
+	 *
2069
+	 * @param    string  $REG_code Registration Code
2070
+	 * @param    boolean $use_default
2071
+	 * @throws EE_Error
2072
+	 * @throws InvalidArgumentException
2073
+	 * @throws InvalidDataTypeException
2074
+	 * @throws InvalidInterfaceException
2075
+	 * @throws ReflectionException
2076
+	 */
2077
+	public function set_reg_code($REG_code, $use_default = false)
2078
+	{
2079
+		if (empty($REG_code)) {
2080
+			EE_Error::add_error(
2081
+				esc_html__('REG_code can not be empty.', 'event_espresso'),
2082
+				__FILE__,
2083
+				__FUNCTION__,
2084
+				__LINE__
2085
+			);
2086
+			return;
2087
+		}
2088
+		if (! $this->reg_code()) {
2089
+			parent::set('REG_code', $REG_code, $use_default);
2090
+		} else {
2091
+			EE_Error::doing_it_wrong(
2092
+				__CLASS__ . '::' . __FUNCTION__,
2093
+				esc_html__('Can not change a registration REG_code once it has been set.', 'event_espresso'),
2094
+				'4.6.0'
2095
+			);
2096
+		}
2097
+	}
2098
+
2099
+
2100
+	/**
2101
+	 * Returns all other registrations in the same group as this registrant who have the same ticket option.
2102
+	 * Note, if you want to just get all registrations in the same transaction (group), use:
2103
+	 *    $registration->transaction()->registrations();
2104
+	 *
2105
+	 * @since 4.5.0
2106
+	 * @return EE_Registration[] or empty array if this isn't a group registration.
2107
+	 * @throws EE_Error
2108
+	 * @throws InvalidArgumentException
2109
+	 * @throws InvalidDataTypeException
2110
+	 * @throws InvalidInterfaceException
2111
+	 * @throws ReflectionException
2112
+	 */
2113
+	public function get_all_other_registrations_in_group()
2114
+	{
2115
+		if ($this->group_size() < 2) {
2116
+			return array();
2117
+		}
2118
+
2119
+		$query[0] = array(
2120
+			'TXN_ID' => $this->transaction_ID(),
2121
+			'REG_ID' => array('!=', $this->ID()),
2122
+			'TKT_ID' => $this->ticket_ID(),
2123
+		);
2124
+		/** @var EE_Registration[] $registrations */
2125
+		$registrations = $this->get_model()->get_all($query);
2126
+		return $registrations;
2127
+	}
2128
+
2129
+
2130
+	/**
2131
+	 * Return the link to the admin details for the object.
2132
+	 *
2133
+	 * @return string
2134
+	 * @throws EE_Error
2135
+	 * @throws InvalidArgumentException
2136
+	 * @throws InvalidDataTypeException
2137
+	 * @throws InvalidInterfaceException
2138
+	 * @throws ReflectionException
2139
+	 */
2140
+	public function get_admin_details_link()
2141
+	{
2142
+		EE_Registry::instance()->load_helper('URL');
2143
+		return EEH_URL::add_query_args_and_nonce(
2144
+			array(
2145
+				'page'    => 'espresso_registrations',
2146
+				'action'  => 'view_registration',
2147
+				'_REG_ID' => $this->ID(),
2148
+			),
2149
+			admin_url('admin.php')
2150
+		);
2151
+	}
2152
+
2153
+
2154
+	/**
2155
+	 * Returns the link to the editor for the object.  Sometimes this is the same as the details.
2156
+	 *
2157
+	 * @return string
2158
+	 * @throws EE_Error
2159
+	 * @throws InvalidArgumentException
2160
+	 * @throws InvalidDataTypeException
2161
+	 * @throws InvalidInterfaceException
2162
+	 * @throws ReflectionException
2163
+	 */
2164
+	public function get_admin_edit_link()
2165
+	{
2166
+		return $this->get_admin_details_link();
2167
+	}
2168
+
2169
+
2170
+	/**
2171
+	 * Returns the link to a settings page for the object.
2172
+	 *
2173
+	 * @return string
2174
+	 * @throws EE_Error
2175
+	 * @throws InvalidArgumentException
2176
+	 * @throws InvalidDataTypeException
2177
+	 * @throws InvalidInterfaceException
2178
+	 * @throws ReflectionException
2179
+	 */
2180
+	public function get_admin_settings_link()
2181
+	{
2182
+		return $this->get_admin_details_link();
2183
+	}
2184
+
2185
+
2186
+	/**
2187
+	 * Returns the link to the "overview" for the object (typically the "list table" view).
2188
+	 *
2189
+	 * @return string
2190
+	 * @throws EE_Error
2191
+	 * @throws InvalidArgumentException
2192
+	 * @throws InvalidDataTypeException
2193
+	 * @throws InvalidInterfaceException
2194
+	 * @throws ReflectionException
2195
+	 */
2196
+	public function get_admin_overview_link()
2197
+	{
2198
+		EE_Registry::instance()->load_helper('URL');
2199
+		return EEH_URL::add_query_args_and_nonce(
2200
+			array(
2201
+				'page' => 'espresso_registrations',
2202
+			),
2203
+			admin_url('admin.php')
2204
+		);
2205
+	}
2206
+
2207
+
2208
+	/**
2209
+	 * @param array $query_params
2210
+	 * @return EE_Base_Class[]|EE_Registration[]
2211
+	 * @throws EE_Error
2212
+	 * @throws InvalidArgumentException
2213
+	 * @throws InvalidDataTypeException
2214
+	 * @throws InvalidInterfaceException
2215
+	 * @throws ReflectionException
2216
+	 */
2217
+	public function payments($query_params = array())
2218
+	{
2219
+		return $this->get_many_related('Payment', $query_params);
2220
+	}
2221
+
2222
+
2223
+	/**
2224
+	 * @param array $query_params
2225
+	 * @return EE_Base_Class[]|EE_Registration_Payment[]
2226
+	 * @throws EE_Error
2227
+	 * @throws InvalidArgumentException
2228
+	 * @throws InvalidDataTypeException
2229
+	 * @throws InvalidInterfaceException
2230
+	 * @throws ReflectionException
2231
+	 */
2232
+	public function registration_payments($query_params = array())
2233
+	{
2234
+		return $this->get_many_related('Registration_Payment', $query_params);
2235
+	}
2236
+
2237
+
2238
+	/**
2239
+	 * This grabs the payment method corresponding to the last payment made for the amount owing on the registration.
2240
+	 * Note: if there are no payments on the registration there will be no payment method returned.
2241
+	 *
2242
+	 * @return EE_Payment|EE_Payment_Method|null
2243
+	 * @throws EE_Error
2244
+	 * @throws InvalidArgumentException
2245
+	 * @throws InvalidDataTypeException
2246
+	 * @throws InvalidInterfaceException
2247
+	 */
2248
+	public function payment_method()
2249
+	{
2250
+		return EEM_Payment_Method::instance()->get_last_used_for_registration($this);
2251
+	}
2252
+
2253
+
2254
+	/**
2255
+	 * @return \EE_Line_Item
2256
+	 * @throws EE_Error
2257
+	 * @throws EntityNotFoundException
2258
+	 * @throws InvalidArgumentException
2259
+	 * @throws InvalidDataTypeException
2260
+	 * @throws InvalidInterfaceException
2261
+	 * @throws ReflectionException
2262
+	 */
2263
+	public function ticket_line_item()
2264
+	{
2265
+		$ticket = $this->ticket();
2266
+		$transaction = $this->transaction();
2267
+		$line_item = null;
2268
+		$ticket_line_items = \EEH_Line_Item::get_line_items_by_object_type_and_IDs(
2269
+			$transaction->total_line_item(),
2270
+			'Ticket',
2271
+			array($ticket->ID())
2272
+		);
2273
+		foreach ($ticket_line_items as $ticket_line_item) {
2274
+			if (
2275
+				$ticket_line_item instanceof \EE_Line_Item
2276
+				&& $ticket_line_item->OBJ_type() === 'Ticket'
2277
+				&& $ticket_line_item->OBJ_ID() === $ticket->ID()
2278
+			) {
2279
+				$line_item = $ticket_line_item;
2280
+				break;
2281
+			}
2282
+		}
2283
+		if (! ($line_item instanceof \EE_Line_Item && $line_item->OBJ_type() === 'Ticket')) {
2284
+			throw new EntityNotFoundException('Line Item Ticket ID', $ticket->ID());
2285
+		}
2286
+		return $line_item;
2287
+	}
2288
+
2289
+
2290
+	/**
2291
+	 * Soft Deletes this model object.
2292
+	 *
2293
+	 * @param string $source function name that called this method
2294
+	 * @return boolean | int
2295
+	 * @throws DomainException
2296
+	 * @throws EE_Error
2297
+	 * @throws EntityNotFoundException
2298
+	 * @throws InvalidArgumentException
2299
+	 * @throws InvalidDataTypeException
2300
+	 * @throws InvalidInterfaceException
2301
+	 * @throws ReflectionException
2302
+	 * @throws RuntimeException
2303
+	 * @throws UnexpectedEntityException
2304
+	 */
2305
+	public function delete()
2306
+	{
2307
+		if ($this->update_extra_meta(EE_Registration::PRE_TRASH_REG_STATUS_KEY, $this->status_ID()) === true) {
2308
+			$this->set_status(EEM_Registration::status_id_cancelled);
2309
+		}
2310
+		return parent::delete();
2311
+	}
2312
+
2313
+
2314
+	/**
2315
+	 * Restores whatever the previous status was on a registration before it was trashed (if possible)
2316
+	 *
2317
+	 * @param string $source function name that called this method
2318
+	 * @return bool|int
2319
+	 * @throws DomainException
2320
+	 * @throws EE_Error
2321
+	 * @throws EntityNotFoundException
2322
+	 * @throws InvalidArgumentException
2323
+	 * @throws InvalidDataTypeException
2324
+	 * @throws InvalidInterfaceException
2325
+	 * @throws ReflectionException
2326
+	 * @throws RuntimeException
2327
+	 * @throws UnexpectedEntityException
2328
+	 */
2329
+	public function restore()
2330
+	{
2331
+		$previous_status = $this->get_extra_meta(
2332
+			EE_Registration::PRE_TRASH_REG_STATUS_KEY,
2333
+			true,
2334
+			EEM_Registration::status_id_cancelled
2335
+		);
2336
+		if ($previous_status) {
2337
+			$this->delete_extra_meta(EE_Registration::PRE_TRASH_REG_STATUS_KEY);
2338
+			$this->set_status($previous_status);
2339
+		}
2340
+		return parent::restore();
2341
+	}
2342
+
2343
+
2344
+	/**
2345
+	 * possibly toggle Registration status based on comparison of REG_paid vs REG_final_price
2346
+	 *
2347
+	 * @param  boolean $trigger_set_status_logic EE_Registration::set_status() can trigger additional logic
2348
+	 *                                           depending on whether the reg status changes to or from "Approved"
2349
+	 * @return boolean whether the Registration status was updated
2350
+	 * @throws DomainException
2351
+	 * @throws EE_Error
2352
+	 * @throws EntityNotFoundException
2353
+	 * @throws InvalidArgumentException
2354
+	 * @throws InvalidDataTypeException
2355
+	 * @throws InvalidInterfaceException
2356
+	 * @throws ReflectionException
2357
+	 * @throws RuntimeException
2358
+	 * @throws UnexpectedEntityException
2359
+	 */
2360
+	public function updateStatusBasedOnTotalPaid($trigger_set_status_logic = true)
2361
+	{
2362
+		$paid = $this->paid();
2363
+		$price = $this->final_price();
2364
+		switch (true) {
2365
+			// overpaid or paid
2366
+			case EEH_Money::compare_floats($paid, $price, '>'):
2367
+			case EEH_Money::compare_floats($paid, $price):
2368
+				$new_status = EEM_Registration::status_id_approved;
2369
+				break;
2370
+			//  underpaid
2371
+			case EEH_Money::compare_floats($paid, $price, '<'):
2372
+				$new_status = EEM_Registration::status_id_pending_payment;
2373
+				break;
2374
+			// uhhh Houston...
2375
+			default:
2376
+				throw new RuntimeException(
2377
+					esc_html__('The total paid calculation for this registration is inaccurate.', 'event_espresso')
2378
+				);
2379
+		}
2380
+		if ($new_status !== $this->status_ID()) {
2381
+			if ($trigger_set_status_logic) {
2382
+				return $this->set_status($new_status);
2383
+			}
2384
+			parent::set('STS_ID', $new_status);
2385
+			return true;
2386
+		}
2387
+		return false;
2388
+	}
2389
+
2390
+
2391
+	/*************************** DEPRECATED ***************************/
2392
+
2393
+
2394
+	/**
2395
+	 * @deprecated
2396
+	 * @since     4.7.0
2397
+	 */
2398
+	public function price_paid()
2399
+	{
2400
+		EE_Error::doing_it_wrong(
2401
+			'EE_Registration::price_paid()',
2402
+			esc_html__(
2403
+				'This method is deprecated, please use EE_Registration::final_price() instead.',
2404
+				'event_espresso'
2405
+			),
2406
+			'4.7.0'
2407
+		);
2408
+		return $this->final_price();
2409
+	}
2410
+
2411
+
2412
+	/**
2413
+	 * @deprecated
2414
+	 * @since     4.7.0
2415
+	 * @param    float $REG_final_price
2416
+	 * @throws EE_Error
2417
+	 * @throws EntityNotFoundException
2418
+	 * @throws InvalidArgumentException
2419
+	 * @throws InvalidDataTypeException
2420
+	 * @throws InvalidInterfaceException
2421
+	 * @throws ReflectionException
2422
+	 * @throws RuntimeException
2423
+	 * @throws DomainException
2424
+	 */
2425
+	public function set_price_paid($REG_final_price = 0.00)
2426
+	{
2427
+		EE_Error::doing_it_wrong(
2428
+			'EE_Registration::set_price_paid()',
2429
+			esc_html__(
2430
+				'This method is deprecated, please use EE_Registration::set_final_price() instead.',
2431
+				'event_espresso'
2432
+			),
2433
+			'4.7.0'
2434
+		);
2435
+		$this->set_final_price($REG_final_price);
2436
+	}
2437
+
2438
+
2439
+	/**
2440
+	 * @deprecated
2441
+	 * @since 4.7.0
2442
+	 * @return string
2443
+	 * @throws EE_Error
2444
+	 * @throws InvalidArgumentException
2445
+	 * @throws InvalidDataTypeException
2446
+	 * @throws InvalidInterfaceException
2447
+	 * @throws ReflectionException
2448
+	 */
2449
+	public function pretty_price_paid()
2450
+	{
2451
+		EE_Error::doing_it_wrong(
2452
+			'EE_Registration::pretty_price_paid()',
2453
+			esc_html__(
2454
+				'This method is deprecated, please use EE_Registration::pretty_final_price() instead.',
2455
+				'event_espresso'
2456
+			),
2457
+			'4.7.0'
2458
+		);
2459
+		return $this->pretty_final_price();
2460
+	}
2461
+
2462
+
2463
+	/**
2464
+	 * Gets the primary datetime related to this registration via the related Event to this registration
2465
+	 *
2466
+	 * @deprecated 4.9.17
2467
+	 * @return EE_Datetime
2468
+	 * @throws EE_Error
2469
+	 * @throws EntityNotFoundException
2470
+	 * @throws InvalidArgumentException
2471
+	 * @throws InvalidDataTypeException
2472
+	 * @throws InvalidInterfaceException
2473
+	 * @throws ReflectionException
2474
+	 */
2475
+	public function get_related_primary_datetime()
2476
+	{
2477
+		EE_Error::doing_it_wrong(
2478
+			__METHOD__,
2479
+			esc_html__(
2480
+				'Use EE_Registration::get_latest_related_datetime() or EE_Registration::get_earliest_related_datetime()',
2481
+				'event_espresso'
2482
+			),
2483
+			'4.9.17',
2484
+			'5.0.0'
2485
+		);
2486
+		return $this->event()->primary_datetime();
2487
+	}
2488
+
2489
+	/**
2490
+	 * Returns the contact's name (or "Unknown" if there is no contact.)
2491
+	 * @since 4.10.12.p
2492
+	 * @return string
2493
+	 * @throws EE_Error
2494
+	 * @throws InvalidArgumentException
2495
+	 * @throws InvalidDataTypeException
2496
+	 * @throws InvalidInterfaceException
2497
+	 * @throws ReflectionException
2498
+	 */
2499
+	public function name()
2500
+	{
2501
+		return $this->attendeeName();
2502
+	}
2503 2503
 }
Please login to merge, or discard this patch.
Spacing   +16 added lines, -16 removed lines patch added patch discarded remove patch
@@ -133,7 +133,7 @@  discard block
 block discarded – undo
133 133
     {
134 134
         switch ($field_name) {
135 135
             case 'REG_code':
136
-                if (! empty($field_value) && $this->reg_code() === null) {
136
+                if ( ! empty($field_value) && $this->reg_code() === null) {
137 137
                     $this->set_reg_code($field_value, $use_default);
138 138
                 }
139 139
                 break;
@@ -434,7 +434,7 @@  discard block
 block discarded – undo
434 434
     public function event()
435 435
     {
436 436
         $event = $this->get_first_related('Event');
437
-        if (! $event instanceof \EE_Event) {
437
+        if ( ! $event instanceof \EE_Event) {
438 438
             throw new EntityNotFoundException('Event ID', $this->event_ID());
439 439
         }
440 440
         return $event;
@@ -481,7 +481,7 @@  discard block
 block discarded – undo
481 481
     {
482 482
         // reserved ticket and datetime counts will be decremented as sold counts are incremented
483 483
         // so stop tracking that this reg has a ticket reserved
484
-        $this->release_reserved_ticket(false, "REG: {$this->ID()} (ln:" . __LINE__ . ')');
484
+        $this->release_reserved_ticket(false, "REG: {$this->ID()} (ln:".__LINE__.')');
485 485
         $ticket = $this->ticket();
486 486
         $ticket->increaseSold();
487 487
         // possibly set event status to sold out
@@ -531,7 +531,7 @@  discard block
 block discarded – undo
531 531
             $reserved = $this->update_extra_meta(EE_Registration::HAS_RESERVED_TICKET_KEY, true);
532 532
             if ($reserved && $update_ticket) {
533 533
                 $ticket = $this->ticket();
534
-                $ticket->increaseReserved(1, "REG: {$this->ID()} (ln:" . __LINE__ . ')');
534
+                $ticket->increaseReserved(1, "REG: {$this->ID()} (ln:".__LINE__.')');
535 535
                 $this->update_extra_meta('reserve_ticket', "{$this->ticket_ID()} from {$source}");
536 536
                 $ticket->save();
537 537
             }
@@ -559,7 +559,7 @@  discard block
 block discarded – undo
559 559
             $reserved = $this->update_extra_meta(EE_Registration::HAS_RESERVED_TICKET_KEY, false);
560 560
             if ($reserved && $update_ticket) {
561 561
                 $ticket = $this->ticket();
562
-                $ticket->decreaseReserved(1, true, "REG: {$this->ID()} (ln:" . __LINE__ . ')');
562
+                $ticket->decreaseReserved(1, true, "REG: {$this->ID()} (ln:".__LINE__.')');
563 563
                 $this->update_extra_meta('release_reserved_ticket', "{$this->ticket_ID()} from {$source}");
564 564
             }
565 565
         }
@@ -1445,7 +1445,7 @@  discard block
 block discarded – undo
1445 1445
                     : '';
1446 1446
                 break;
1447 1447
         }
1448
-        return $icon . $status[ $this->status_ID() ];
1448
+        return $icon.$status[$this->status_ID()];
1449 1449
     }
1450 1450
 
1451 1451
 
@@ -1701,7 +1701,7 @@  discard block
 block discarded – undo
1701 1701
     {
1702 1702
         $DTT_ID = EEM_Datetime::instance()->ensure_is_ID($DTT_OR_ID);
1703 1703
         // first check registration status
1704
-        if (! $DTT_ID || ($check_approved && ! $this->is_approved())) {
1704
+        if ( ! $DTT_ID || ($check_approved && ! $this->is_approved())) {
1705 1705
             return false;
1706 1706
         }
1707 1707
         // is there a datetime ticket that matches this dtt_ID?
@@ -1741,7 +1741,7 @@  discard block
 block discarded – undo
1741 1741
     {
1742 1742
         $DTT_ID = EEM_Datetime::instance()->ensure_is_ID($DTT_OR_ID);
1743 1743
 
1744
-        if (! $DTT_ID) {
1744
+        if ( ! $DTT_ID) {
1745 1745
             return false;
1746 1746
         }
1747 1747
 
@@ -1749,7 +1749,7 @@  discard block
 block discarded – undo
1749 1749
 
1750 1750
         // if max uses is not set or equals infinity then return true cause its not a factor for whether user can
1751 1751
         // check-in or not.
1752
-        if (! $max_uses || $max_uses === EE_INF) {
1752
+        if ( ! $max_uses || $max_uses === EE_INF) {
1753 1753
             return true;
1754 1754
         }
1755 1755
 
@@ -1813,7 +1813,7 @@  discard block
 block discarded – undo
1813 1813
             $datetime = $this->get_latest_related_datetime();
1814 1814
             $DTT_ID = $datetime instanceof EE_Datetime ? $datetime->ID() : 0;
1815 1815
             // verify the registration can checkin for the given DTT_ID
1816
-        } elseif (! $this->can_checkin($DTT_ID, $verify)) {
1816
+        } elseif ( ! $this->can_checkin($DTT_ID, $verify)) {
1817 1817
             EE_Error::add_error(
1818 1818
                 sprintf(
1819 1819
                     esc_html__(
@@ -1836,7 +1836,7 @@  discard block
 block discarded – undo
1836 1836
         );
1837 1837
         // start by getting the current status so we know what status we'll be changing to.
1838 1838
         $cur_status = $this->check_in_status_for_datetime($DTT_ID);
1839
-        $status_to = $status_paths[ $cur_status ];
1839
+        $status_to = $status_paths[$cur_status];
1840 1840
         // database only records true for checked IN or false for checked OUT
1841 1841
         // no record ( null ) means checked in NEVER, but we obviously don't save that
1842 1842
         $new_status = $status_to === EE_Checkin::status_checked_in;
@@ -1957,7 +1957,7 @@  discard block
 block discarded – undo
1957 1957
                 'order_by' => ['CHK_timestamp' => 'DESC'],
1958 1958
             ]
1959 1959
         );
1960
-        return $checkin instanceof EE_Checkin ? $checkin->status() :  EE_Checkin::status_checked_never;
1960
+        return $checkin instanceof EE_Checkin ? $checkin->status() : EE_Checkin::status_checked_never;
1961 1961
     }
1962 1962
 
1963 1963
 
@@ -2012,7 +2012,7 @@  discard block
 block discarded – undo
2012 2012
     public function transaction(): EE_Transaction
2013 2013
     {
2014 2014
         $transaction = $this->get_first_related('Transaction');
2015
-        if (! $transaction instanceof \EE_Transaction) {
2015
+        if ( ! $transaction instanceof \EE_Transaction) {
2016 2016
             throw new EntityNotFoundException('Transaction ID', $this->transaction_ID());
2017 2017
         }
2018 2018
         return $transaction;
@@ -2085,11 +2085,11 @@  discard block
 block discarded – undo
2085 2085
             );
2086 2086
             return;
2087 2087
         }
2088
-        if (! $this->reg_code()) {
2088
+        if ( ! $this->reg_code()) {
2089 2089
             parent::set('REG_code', $REG_code, $use_default);
2090 2090
         } else {
2091 2091
             EE_Error::doing_it_wrong(
2092
-                __CLASS__ . '::' . __FUNCTION__,
2092
+                __CLASS__.'::'.__FUNCTION__,
2093 2093
                 esc_html__('Can not change a registration REG_code once it has been set.', 'event_espresso'),
2094 2094
                 '4.6.0'
2095 2095
             );
@@ -2280,7 +2280,7 @@  discard block
 block discarded – undo
2280 2280
                 break;
2281 2281
             }
2282 2282
         }
2283
-        if (! ($line_item instanceof \EE_Line_Item && $line_item->OBJ_type() === 'Ticket')) {
2283
+        if ( ! ($line_item instanceof \EE_Line_Item && $line_item->OBJ_type() === 'Ticket')) {
2284 2284
             throw new EntityNotFoundException('Line Item Ticket ID', $ticket->ID());
2285 2285
         }
2286 2286
         return $line_item;
Please login to merge, or discard this patch.
caffeinated/admin/new/pricing/Pricing_Admin_Page.core.php 1 patch
Indentation   +1275 added lines, -1275 removed lines patch added patch discarded remove patch
@@ -11,1286 +11,1286 @@
 block discarded – undo
11 11
  */
12 12
 class Pricing_Admin_Page extends EE_Admin_Page
13 13
 {
14
-    protected function _init_page_props()
15
-    {
16
-        $this->page_slug        = PRICING_PG_SLUG;
17
-        $this->page_label       = PRICING_LABEL;
18
-        $this->_admin_base_url  = PRICING_ADMIN_URL;
19
-        $this->_admin_base_path = PRICING_ADMIN;
20
-    }
21
-
22
-
23
-    protected function _ajax_hooks()
24
-    {
25
-        add_action('wp_ajax_espresso_update_prices_order', [$this, 'update_price_order']);
26
-    }
27
-
28
-
29
-    protected function _define_page_props()
30
-    {
31
-        $this->_admin_page_title = PRICING_LABEL;
32
-        $this->_labels           = [
33
-            'buttons' => [
34
-                'add'         => esc_html__('Add New Default Price', 'event_espresso'),
35
-                'edit'        => esc_html__('Edit Default Price', 'event_espresso'),
36
-                'delete'      => esc_html__('Delete Default Price', 'event_espresso'),
37
-                'add_type'    => esc_html__('Add New Default Price Type', 'event_espresso'),
38
-                'edit_type'   => esc_html__('Edit Price Type', 'event_espresso'),
39
-                'delete_type' => esc_html__('Delete Price Type', 'event_espresso'),
40
-            ],
41
-        ];
42
-    }
43
-
44
-
45
-    /**
46
-     * an array for storing request actions and their corresponding methods
47
-     *
48
-     * @return void
49
-     */
50
-    protected function _set_page_routes()
51
-    {
52
-        $PRC_ID             = $this->request->getRequestParam('PRC_ID', 0, DataType::INTEGER);
53
-        $PRT_ID             = $this->request->getRequestParam('PRT_ID', 0, DataType::INTEGER);
54
-        $this->_page_routes = [
55
-            'default'                     => [
56
-                'func'       => [$this, '_price_overview_list_table'],
57
-                'capability' => 'ee_read_default_prices',
58
-            ],
59
-            'add_new_price'               => [
60
-                'func'       => [$this, '_edit_price_details'],
61
-                'capability' => 'ee_edit_default_prices',
62
-            ],
63
-            'edit_price'                  => [
64
-                'func'       => [$this, '_edit_price_details'],
65
-                'capability' => 'ee_edit_default_price',
66
-                'obj_id'     => $PRC_ID,
67
-            ],
68
-            'insert_price'                => [
69
-                'func'       => [$this, '_insert_or_update_price'],
70
-                'args'       => ['insert' => true],
71
-                'noheader'   => true,
72
-                'capability' => 'ee_edit_default_prices',
73
-            ],
74
-            'update_price'                => [
75
-                'func'       => [$this, '_insert_or_update_price'],
76
-                'args'       => ['insert' => false],
77
-                'noheader'   => true,
78
-                'capability' => 'ee_edit_default_price',
79
-                'obj_id'     => $PRC_ID,
80
-            ],
81
-            'trash_price'                 => [
82
-                'func'       => [$this, '_trash_or_restore_price'],
83
-                'args'       => ['trash' => true],
84
-                'noheader'   => true,
85
-                'capability' => 'ee_delete_default_price',
86
-                'obj_id'     => $PRC_ID,
87
-            ],
88
-            'restore_price'               => [
89
-                'func'       => [$this, '_trash_or_restore_price'],
90
-                'args'       => ['trash' => false],
91
-                'noheader'   => true,
92
-                'capability' => 'ee_delete_default_price',
93
-                'obj_id'     => $PRC_ID,
94
-            ],
95
-            'delete_price'                => [
96
-                'func'       => [$this, '_delete_price'],
97
-                'noheader'   => true,
98
-                'capability' => 'ee_delete_default_price',
99
-                'obj_id'     => $PRC_ID,
100
-            ],
101
-            'espresso_update_price_order' => [
102
-                'func'       => [$this, 'update_price_order'],
103
-                'noheader'   => true,
104
-                'capability' => 'ee_edit_default_prices',
105
-            ],
106
-            // price types
107
-            'price_types'                 => [
108
-                'func'       => [$this, '_price_types_overview_list_table'],
109
-                'capability' => 'ee_read_default_price_types',
110
-            ],
111
-            'add_new_price_type'          => [
112
-                'func'       => [$this, '_edit_price_type_details'],
113
-                'capability' => 'ee_edit_default_price_types',
114
-            ],
115
-            'edit_price_type'             => [
116
-                'func'       => [$this, '_edit_price_type_details'],
117
-                'capability' => 'ee_edit_default_price_type',
118
-                'obj_id'     => $PRT_ID,
119
-            ],
120
-            'insert_price_type'           => [
121
-                'func'       => [$this, '_insert_or_update_price_type'],
122
-                'args'       => ['new_price_type' => true],
123
-                'noheader'   => true,
124
-                'capability' => 'ee_edit_default_price_types',
125
-            ],
126
-            'update_price_type'           => [
127
-                'func'       => [$this, '_insert_or_update_price_type'],
128
-                'args'       => ['new_price_type' => false],
129
-                'noheader'   => true,
130
-                'capability' => 'ee_edit_default_price_type',
131
-                'obj_id'     => $PRT_ID,
132
-            ],
133
-            'trash_price_type'            => [
134
-                'func'       => [$this, '_trash_or_restore_price_type'],
135
-                'args'       => ['trash' => true],
136
-                'noheader'   => true,
137
-                'capability' => 'ee_delete_default_price_type',
138
-                'obj_id'     => $PRT_ID,
139
-            ],
140
-            'restore_price_type'          => [
141
-                'func'       => [$this, '_trash_or_restore_price_type'],
142
-                'args'       => ['trash' => false],
143
-                'noheader'   => true,
144
-                'capability' => 'ee_delete_default_price_type',
145
-                'obj_id'     => $PRT_ID,
146
-            ],
147
-            'delete_price_type'           => [
148
-                'func'       => [$this, '_delete_price_type'],
149
-                'noheader'   => true,
150
-                'capability' => 'ee_delete_default_price_type',
151
-                'obj_id'     => $PRT_ID,
152
-            ],
153
-            'tax_settings'                => [
154
-                'func'       => [$this, '_tax_settings'],
155
-                'capability' => 'manage_options',
156
-            ],
157
-            'update_tax_settings'         => [
158
-                'func'       => [$this, '_update_tax_settings'],
159
-                'capability' => 'manage_options',
160
-                'noheader'   => true,
161
-            ],
162
-        ];
163
-    }
164
-
165
-
166
-    protected function _set_page_config()
167
-    {
168
-        $PRC_ID             = $this->request->getRequestParam('id', 0, DataType::INTEGER);
169
-        $this->_page_config = [
170
-            'default'            => [
171
-                'nav'           => [
172
-                    'label' => esc_html__('Default Pricing', 'event_espresso'),
173
-                    'icon' => 'dashicons-money-alt',
174
-                    'order' => 10,
175
-                ],
176
-                'list_table'    => 'Prices_List_Table',
177
-                'metaboxes'     => $this->_default_espresso_metaboxes,
178
-                'help_tabs'     => [
179
-                    'pricing_default_pricing_help_tab'                           => [
180
-                        'title'    => esc_html__('Default Pricing', 'event_espresso'),
181
-                        'filename' => 'pricing_default_pricing',
182
-                    ],
183
-                    'pricing_default_pricing_table_column_headings_help_tab'     => [
184
-                        'title'    => esc_html__('Default Pricing Table Column Headings', 'event_espresso'),
185
-                        'filename' => 'pricing_default_pricing_table_column_headings',
186
-                    ],
187
-                    'pricing_default_pricing_views_bulk_actions_search_help_tab' => [
188
-                        'title'    => esc_html__('Default Pricing Views & Bulk Actions & Search', 'event_espresso'),
189
-                        'filename' => 'pricing_default_pricing_views_bulk_actions_search',
190
-                    ],
191
-                ],
192
-                'require_nonce' => false,
193
-            ],
194
-            'add_new_price'      => [
195
-                'nav'           => [
196
-                    'label'      => esc_html__('Add New Default Price', 'event_espresso'),
197
-                    'icon' => 'dashicons-plus-alt',
198
-                    'order'      => 20,
199
-                    'persistent' => false,
200
-                ],
201
-                'help_tabs'     => [
202
-                    'add_new_default_price_help_tab' => [
203
-                        'title'    => esc_html__('Add New Default Price', 'event_espresso'),
204
-                        'filename' => 'pricing_add_new_default_price',
205
-                    ],
206
-                ],
207
-                'metaboxes'     => array_merge(
208
-                    ['_publish_post_box'],
209
-                    $this->_default_espresso_metaboxes
210
-                ),
211
-                'require_nonce' => false,
212
-            ],
213
-            'edit_price'         => [
214
-                'nav'           => [
215
-                    'label'      => esc_html__('Edit Default Price', 'event_espresso'),
216
-                    'icon' => 'dashicons-edit-large',
217
-                    'order'      => 20,
218
-                    'url'        => $PRC_ID
219
-                        ? add_query_arg(['id' => $PRC_ID], $this->_current_page_view_url)
220
-                        : $this->_admin_base_url,
221
-                    'persistent' => false,
222
-                ],
223
-                'metaboxes'     => array_merge(
224
-                    ['_publish_post_box'],
225
-                    $this->_default_espresso_metaboxes
226
-                ),
227
-                'help_tabs'     => [
228
-                    'edit_default_price_help_tab' => [
229
-                        'title'    => esc_html__('Edit Default Price', 'event_espresso'),
230
-                        'filename' => 'pricing_edit_default_price',
231
-                    ],
232
-                ],
233
-                'require_nonce' => false,
234
-            ],
235
-            'price_types'        => [
236
-                'nav'           => [
237
-                    'label' => esc_html__('Price Types', 'event_espresso'),
238
-                    'icon' => 'dashicons-networking',
239
-                    'order' => 30,
240
-                ],
241
-                'list_table'    => 'Price_Types_List_Table',
242
-                'help_tabs'     => [
243
-                    'pricing_price_types_help_tab'                           => [
244
-                        'title'    => esc_html__('Price Types', 'event_espresso'),
245
-                        'filename' => 'pricing_price_types',
246
-                    ],
247
-                    'pricing_price_types_table_column_headings_help_tab'     => [
248
-                        'title'    => esc_html__('Price Types Table Column Headings', 'event_espresso'),
249
-                        'filename' => 'pricing_price_types_table_column_headings',
250
-                    ],
251
-                    'pricing_price_types_views_bulk_actions_search_help_tab' => [
252
-                        'title'    => esc_html__('Price Types Views & Bulk Actions & Search', 'event_espresso'),
253
-                        'filename' => 'pricing_price_types_views_bulk_actions_search',
254
-                    ],
255
-                ],
256
-                'metaboxes'     => $this->_default_espresso_metaboxes,
257
-                'require_nonce' => false,
258
-            ],
259
-            'add_new_price_type' => [
260
-                'nav'           => [
261
-                    'label'      => esc_html__('Add New Price Type', 'event_espresso'),
262
-                    'icon' => 'dashicons-plus-alt',
263
-                    'order'      => 40,
264
-                    'persistent' => false,
265
-                ],
266
-                'help_tabs'     => [
267
-                    'add_new_price_type_help_tab' => [
268
-                        'title'    => esc_html__('Add New Price Type', 'event_espresso'),
269
-                        'filename' => 'pricing_add_new_price_type',
270
-                    ],
271
-                ],
272
-                'metaboxes'     => array_merge(
273
-                    ['_publish_post_box'],
274
-                    $this->_default_espresso_metaboxes
275
-                ),
276
-                'require_nonce' => false,
277
-            ],
278
-            'edit_price_type'    => [
279
-                'nav'           => [
280
-                    'label'      => esc_html__('Edit Price Type', 'event_espresso'),
281
-                    'icon' => 'dashicons-edit-large',
282
-                    'order'      => 40,
283
-                    'persistent' => false,
284
-                ],
285
-                'help_tabs'     => [
286
-                    'edit_price_type_help_tab' => [
287
-                        'title'    => esc_html__('Edit Price Type', 'event_espresso'),
288
-                        'filename' => 'pricing_edit_price_type',
289
-                    ],
290
-                ],
291
-                'metaboxes'     => array_merge(
292
-                    ['_publish_post_box'],
293
-                    $this->_default_espresso_metaboxes
294
-                ),
295
-                'require_nonce' => false,
296
-            ],
297
-            'tax_settings'       => [
298
-                'nav' => [
299
-                    'label' => esc_html__('Tax Settings', 'event_espresso'),
300
-                    'icon' => 'dashicons-sticky',
301
-                    'order' => 50,
302
-                ],
303
-                'labels'        => [
304
-                    'publishbox' => esc_html__('Update Tax Settings', 'event_espresso'),
305
-                ],
306
-                'metaboxes'     => array_merge(
307
-                    ['_publish_post_box'],
308
-                    $this->_default_espresso_metaboxes
309
-                ),
310
-                'require_nonce' => true,
311
-            ],
312
-        ];
313
-    }
314
-
315
-
316
-    protected function _add_screen_options()
317
-    {
318
-        // todo
319
-    }
320
-
321
-
322
-    protected function _add_screen_options_default()
323
-    {
324
-        $this->_per_page_screen_option();
325
-    }
326
-
327
-
328
-    protected function _add_screen_options_price_types()
329
-    {
330
-        $page_title              = $this->_admin_page_title;
331
-        $this->_admin_page_title = esc_html__('Price Types', 'event_espresso');
332
-        $this->_per_page_screen_option();
333
-        $this->_admin_page_title = $page_title;
334
-    }
335
-
336
-
337
-    protected function _add_feature_pointers()
338
-    {
339
-    }
340
-
341
-
342
-    public function load_scripts_styles()
343
-    {
344
-        // styles
345
-        wp_enqueue_style('espresso-ui-theme');
346
-        wp_register_style(
347
-            'espresso_PRICING',
348
-            PRICING_ASSETS_URL . 'espresso_pricing_admin.css',
349
-            [],
350
-            EVENT_ESPRESSO_VERSION
351
-        );
352
-        wp_enqueue_style('espresso_PRICING');
353
-
354
-        // scripts
355
-        wp_enqueue_script('ee_admin_js');
356
-        wp_enqueue_script('jquery-ui-position');
357
-        wp_enqueue_script('jquery-ui-widget');
358
-        wp_register_script(
359
-            'espresso_PRICING',
360
-            PRICING_ASSETS_URL . 'espresso_pricing_admin.js',
361
-            ['jquery'],
362
-            EVENT_ESPRESSO_VERSION,
363
-            true
364
-        );
365
-        wp_enqueue_script('espresso_PRICING');
366
-    }
367
-
368
-
369
-    public function load_scripts_styles_default()
370
-    {
371
-        wp_enqueue_script('espresso_ajax_table_sorting');
372
-    }
373
-
374
-
375
-    public function admin_footer_scripts()
376
-    {
377
-    }
378
-
379
-
380
-    public function admin_init()
381
-    {
382
-    }
383
-
384
-
385
-    public function admin_notices()
386
-    {
387
-    }
388
-
389
-
390
-    protected function _set_list_table_views_default()
391
-    {
392
-        $this->_views = [
393
-            'all' => [
394
-                'slug'        => 'all',
395
-                'label'       => esc_html__('View All Default Pricing', 'event_espresso'),
396
-                'count'       => 0,
397
-                'bulk_action' => [
398
-                    'trash_price' => esc_html__('Move to Trash', 'event_espresso'),
399
-                ],
400
-            ],
401
-        ];
402
-
403
-        if (EE_Registry::instance()->CAP->current_user_can('ee_delete_default_prices', 'pricing_trash_price')) {
404
-            $this->_views['trashed'] = [
405
-                'slug'        => 'trashed',
406
-                'label'       => esc_html__('Trash', 'event_espresso'),
407
-                'count'       => 0,
408
-                'bulk_action' => [
409
-                    'restore_price' => esc_html__('Restore from Trash', 'event_espresso'),
410
-                    'delete_price'  => esc_html__('Delete Permanently', 'event_espresso'),
411
-                ],
412
-            ];
413
-        }
414
-    }
415
-
416
-
417
-    protected function _set_list_table_views_price_types()
418
-    {
419
-        $this->_views = [
420
-            'all' => [
421
-                'slug'        => 'all',
422
-                'label'       => esc_html__('All', 'event_espresso'),
423
-                'count'       => 0,
424
-                'bulk_action' => [
425
-                    'trash_price_type' => esc_html__('Move to Trash', 'event_espresso'),
426
-                ],
427
-            ],
428
-        ];
429
-
430
-        if (
431
-            EE_Registry::instance()->CAP->current_user_can(
432
-                'ee_delete_default_price_types',
433
-                'pricing_trash_price_type'
434
-            )
435
-        ) {
436
-            $this->_views['trashed'] = [
437
-                'slug'        => 'trashed',
438
-                'label'       => esc_html__('Trash', 'event_espresso'),
439
-                'count'       => 0,
440
-                'bulk_action' => [
441
-                    'restore_price_type' => esc_html__('Restore from Trash', 'event_espresso'),
442
-                    'delete_price_type'  => esc_html__('Delete Permanently', 'event_espresso'),
443
-                ],
444
-            ];
445
-        }
446
-    }
447
-
448
-
449
-    /**
450
-     * generates HTML for main Prices Admin page
451
-     *
452
-     * @return void
453
-     * @throws EE_Error
454
-     */
455
-    protected function _price_overview_list_table()
456
-    {
457
-        $this->_admin_page_title .= ' ' . $this->get_action_link_or_button(
458
-            'add_new_price',
459
-            'add',
460
-            [],
461
-            'add-new-h2'
462
-        );
463
-        $this->_admin_page_title .= $this->_learn_more_about_pricing_link();
464
-        $this->_search_btn_label = esc_html__('Default Prices', 'event_espresso');
465
-        $this->display_admin_list_table_page_with_sidebar();
466
-    }
467
-
468
-
469
-    /**
470
-     * retrieve data for Prices List table
471
-     *
472
-     * @param int  $per_page how many prices displayed per page
473
-     * @param bool $count    return the count or objects
474
-     * @param bool $trashed  whether the current view is of the trash can - eww yuck!
475
-     * @return EE_Soft_Delete_Base_Class[]|int int = count || array of price objects
476
-     * @throws EE_Error
477
-     * @throws ReflectionException
478
-     */
479
-    public function get_prices_overview_data(int $per_page = 10, bool $count = false, bool $trashed = false)
480
-    {
481
-        // start with an empty array
482
-        $event_pricing = [];
483
-
484
-        require_once(PRICING_ADMIN . 'Prices_List_Table.class.php');
485
-
486
-        $orderby = $this->request->getRequestParam('orderby', '');
487
-        $order   = $this->request->getRequestParam('order', 'ASC');
488
-
489
-        switch ($orderby) {
490
-            case 'name':
491
-                $orderby = ['PRC_name' => $order];
492
-                break;
493
-            case 'type':
494
-                $orderby = ['Price_Type.PRT_name' => $order];
495
-                break;
496
-            case 'amount':
497
-                $orderby = ['PRC_amount' => $order];
498
-                break;
499
-            default:
500
-                $orderby = ['PRC_order' => $order, 'Price_Type.PRT_order' => $order, 'PRC_ID' => $order];
501
-        }
502
-
503
-        $current_page = $this->request->getRequestParam('paged', 1, DataType::INTEGER);
504
-        $per_page     = $this->request->getRequestParam('perpage', $per_page, DataType::INTEGER);
505
-
506
-        $where = [
507
-            'PRC_is_default' => 1,
508
-            'PRC_deleted'    => $trashed,
509
-        ];
510
-
511
-        $offset = ($current_page - 1) * $per_page;
512
-        $limit  = [$offset, $per_page];
513
-
514
-        $search_term = $this->request->getRequestParam('s');
515
-        if ($search_term) {
516
-            $search_term = "%{$search_term}%";
517
-            $where['OR'] = [
518
-                'PRC_name'            => ['LIKE', $search_term],
519
-                'PRC_desc'            => ['LIKE', $search_term],
520
-                'PRC_amount'          => ['LIKE', $search_term],
521
-                'Price_Type.PRT_name' => ['LIKE', $search_term],
522
-            ];
523
-        }
524
-
525
-        $query_params = [
526
-            $where,
527
-            'order_by' => $orderby,
528
-            'limit'    => $limit,
529
-            'group_by' => 'PRC_ID',
530
-        ];
531
-
532
-        if ($count) {
533
-            return $trashed
534
-                ? EEM_Price::instance()->count([$where])
535
-                : EEM_Price::instance()->count_deleted_and_undeleted([$where]);
536
-        }
537
-        return EEM_Price::instance()->get_all_deleted_and_undeleted($query_params);
538
-    }
539
-
540
-
541
-    /**
542
-     * @return void
543
-     * @throws EE_Error
544
-     * @throws ReflectionException
545
-     */
546
-    protected function _edit_price_details()
547
-    {
548
-        // grab price ID
549
-        $PRC_ID = $this->request->getRequestParam('id', 0, DataType::INTEGER);
550
-        // change page title based on request action
551
-        switch ($this->_req_action) {
552
-            case 'add_new_price':
553
-                $this->_admin_page_title = esc_html__('Add New Price', 'event_espresso');
554
-                break;
555
-            case 'edit_price':
556
-                $this->_admin_page_title = esc_html__('Edit Price', 'event_espresso');
557
-                break;
558
-            default:
559
-                $this->_admin_page_title = ucwords(str_replace('_', ' ', $this->_req_action));
560
-        }
561
-        // add PRC_ID to title if editing
562
-        $this->_admin_page_title = $PRC_ID ? $this->_admin_page_title . ' # ' . $PRC_ID : $this->_admin_page_title;
563
-
564
-        if ($PRC_ID) {
565
-            $price                    = EEM_Price::instance()->get_one_by_ID($PRC_ID);
566
-            $additional_hidden_fields = [
567
-                'PRC_ID' => ['type' => 'hidden', 'value' => $PRC_ID],
568
-            ];
569
-            $this->_set_add_edit_form_tags('update_price', $additional_hidden_fields);
570
-        } else {
571
-            $price = EEM_Price::instance()->get_new_price();
572
-            $this->_set_add_edit_form_tags('insert_price');
573
-        }
574
-
575
-        if (! $price instanceof EE_Price) {
576
-            throw new RuntimeException(
577
-                sprintf(
578
-                    esc_html__(
579
-                        'A valid Price could not be retrieved from the database with ID: %1$s',
580
-                        'event_espresso'
581
-                    ),
582
-                    $PRC_ID
583
-                )
584
-            );
585
-        }
586
-
587
-        $this->_template_args['PRC_ID'] = $PRC_ID;
588
-        $this->_template_args['price']  = $price;
589
-
590
-        $default_base_price = $price->type_obj() && $price->type_obj()->base_type() === 1;
591
-
592
-        $this->_template_args['default_base_price'] = $default_base_price;
593
-
594
-        // get price types
595
-        $price_types = EEM_Price_Type::instance()->get_all([['PBT_ID' => ['!=', 1]]]);
596
-        if (empty($price_types)) {
597
-            $msg = esc_html__(
598
-                'You have no price types defined. Please add a price type before adding a price.',
599
-                'event_espresso'
600
-            );
601
-            EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
602
-            $this->display_admin_page_with_sidebar();
603
-        }
604
-        $attributes       = [];
605
-        $price_type_names = [];
606
-        $attributes[]     = 'id="PRT_ID"';
607
-        if ($default_base_price) {
608
-            $attributes[]       = 'disabled="disabled"';
609
-            $price_type_names[] = ['id' => 1, 'text' => esc_html__('Base Price', 'event_espresso')];
610
-        }
611
-        foreach ($price_types as $type) {
612
-            $price_type_names[] = ['id' => $type->ID(), 'text' => $type->name()];
613
-        }
614
-        $this->_template_args['attributes']  = implode(' ', $attributes);
615
-        $this->_template_args['price_types'] = $price_type_names;
616
-
617
-        $this->_template_args['learn_more_about_pricing_link'] = $this->_learn_more_about_pricing_link();
618
-        $this->_template_args['admin_page_content']            = $this->_edit_price_details_meta_box();
619
-
620
-        $this->_set_publish_post_box_vars('id', $PRC_ID);
621
-        // the details template wrapper
622
-        $this->display_admin_page_with_sidebar();
623
-    }
624
-
625
-
626
-    /**
627
-     *
628
-     * @return string
629
-     */
630
-    public function _edit_price_details_meta_box(): string
631
-    {
632
-        return EEH_Template::display_template(
633
-            PRICING_TEMPLATE_PATH . 'pricing_details_main_meta_box.template.php',
634
-            $this->_template_args,
635
-            true
636
-        );
637
-    }
638
-
639
-
640
-    /**
641
-     * @return array
642
-     * @throws EE_Error
643
-     * @throws ReflectionException
644
-     */
645
-    protected function set_price_column_values(): array
646
-    {
647
-        $PRC_order = 0;
648
-        $PRT_ID    = $this->request->getRequestParam('PRT_ID', 0, DataType::INTEGER);
649
-        if ($PRT_ID) {
650
-            /** @var EE_Price_Type $price_type */
651
-            $price_type = EEM_Price_Type::instance()->get_one_by_ID($PRT_ID);
652
-            if ($price_type instanceof EE_Price_Type) {
653
-                $PRC_order = $price_type->order();
654
-            }
655
-        }
656
-        return [
657
-            'PRT_ID'         => $PRT_ID,
658
-            'PRC_amount'     => $this->request->getRequestParam('PRC_amount', 0, DataType::FLOAT),
659
-            'PRC_name'       => $this->request->getRequestParam('PRC_name'),
660
-            'PRC_desc'       => $this->request->getRequestParam('PRC_desc'),
661
-            'PRC_is_default' => 1,
662
-            'PRC_overrides'  => null,
663
-            'PRC_order'      => $PRC_order,
664
-            'PRC_deleted'    => 0,
665
-            'PRC_parent'     => 0,
666
-        ];
667
-    }
668
-
669
-
670
-    /**
671
-     * @param bool $insert - whether to insert or update
672
-     * @return void
673
-     * @throws EE_Error
674
-     * @throws ReflectionException
675
-     */
676
-    protected function _insert_or_update_price(bool $insert = false)
677
-    {
678
-        // why be so pessimistic ???  : (
679
-        $updated = 0;
680
-
681
-        $set_column_values = $this->set_price_column_values();
682
-        // is this a new Price ?
683
-        if ($insert) {
684
-            // run the insert
685
-            $PRC_ID = EEM_Price::instance()->insert($set_column_values);
686
-            if ($PRC_ID) {
687
-                // make sure this new price modifier is attached to the ticket but ONLY if it is not a tax type
688
-                $price = EEM_price::instance()->get_one_by_ID($PRC_ID);
689
-                if (
690
-                    $price instanceof EE_Price
691
-                    && $price->type_obj() instanceof EE_Price_type
692
-                    && $price->type_obj()->base_type() !== EEM_Price_Type::base_type_tax
693
-                ) {
694
-                    $ticket = EEM_Ticket::instance()->get_one_by_ID(1);
695
-                    $ticket->_add_relation_to($price, 'Price');
696
-                    $ticket->save();
697
-                }
698
-                $updated = 1;
699
-            }
700
-            $action_desc = 'created';
701
-        } else {
702
-            $PRC_ID = $this->request->getRequestParam('PRC_ID', 0, DataType::INTEGER);
703
-            // run the update
704
-            $where_cols_n_values = ['PRC_ID' => $PRC_ID];
705
-            $updated             = EEM_Price::instance()->update($set_column_values, [$where_cols_n_values]);
706
-
707
-            $price = EEM_Price::instance()->get_one_by_ID($PRC_ID);
708
-            if ($price instanceof EE_Price && $price->type_obj()->base_type() !== EEM_Price_Type::base_type_tax) {
709
-                // if this is $PRC_ID == 1,
710
-                // then we need to update the default ticket attached to this price so the TKT_price value is updated.
711
-                if ($PRC_ID === 1) {
712
-                    $ticket = $price->get_first_related('Ticket');
713
-                    if ($ticket) {
714
-                        $ticket->set('TKT_price', $price->get('PRC_amount'));
715
-                        $ticket->set('TKT_name', $price->get('PRC_name'));
716
-                        $ticket->set('TKT_description', $price->get('PRC_desc'));
717
-                        $ticket->save();
718
-                    }
719
-                } else {
720
-                    // we make sure this price is attached to base ticket. but ONLY if its not a tax ticket type.
721
-                    $ticket = EEM_Ticket::instance()->get_one_by_ID(1);
722
-                    $ticket->_add_relation_to($PRC_ID, 'Price');
723
-                    $ticket->save();
724
-                }
725
-            }
726
-
727
-            $action_desc = 'updated';
728
-        }
729
-
730
-        $query_args = ['action' => 'edit_price', 'id' => $PRC_ID];
731
-
732
-        $this->_redirect_after_action($updated, 'Prices', $action_desc, $query_args);
733
-    }
734
-
735
-
736
-    /**
737
-     * @param bool $trash - whether to move item to trash (TRUE) or restore it (FALSE)
738
-     * @return void
739
-     * @throws EE_Error
740
-     */
741
-    protected function _trash_or_restore_price($trash = true)
742
-    {
743
-        $entity_model = EEM_Price::instance();
744
-        $action       = $trash ? EE_Admin_List_Table::ACTION_TRASH : EE_Admin_List_Table::ACTION_RESTORE;
745
-        $result       = $this->trashRestoreDeleteEntities(
746
-            $entity_model,
747
-            'id',
748
-            $action,
749
-            'PRC_deleted',
750
-            [$this, 'adjustTicketRelations']
751
-        );
752
-
753
-        if ($result) {
754
-            $msg = $trash
755
-                ? esc_html(
756
-                    _n(
757
-                        'The Price has been trashed',
758
-                        'The Prices have been trashed',
759
-                        $result,
760
-                        'event_espresso'
761
-                    )
762
-                )
763
-                : esc_html(
764
-                    _n(
765
-                        'The Price has been restored',
766
-                        'The Prices have been restored',
767
-                        $result,
768
-                        'event_espresso'
769
-                    )
770
-                );
771
-            EE_Error::add_success($msg);
772
-        }
773
-
774
-        $this->_redirect_after_action(
775
-            $result,
776
-            _n('Price', 'Prices', $result, 'event_espresso'),
777
-            $trash ? 'trashed' : 'restored',
778
-            ['action' => 'default'],
779
-            true
780
-        );
781
-    }
782
-
783
-
784
-    /**
785
-     * @param EEM_Base   $entity_model
786
-     * @param int|string $entity_ID
787
-     * @param string     $action
788
-     * @param int        $result
789
-     * @throws EE_Error
790
-     * @throws ReflectionException
791
-     * @since 4.10.30.p
792
-     */
793
-    public function adjustTicketRelations(
794
-        EEM_Base $entity_model,
795
-        $entity_ID,
796
-        string $action,
797
-        int $result
798
-    ) {
799
-        if (! $entity_ID || (float) $result < 1) {
800
-            return;
801
-        }
802
-
803
-        $entity = $entity_model->get_one_by_ID($entity_ID);
804
-        if (! $entity instanceof EE_Price || $entity->type_obj()->base_type() === EEM_Price_Type::base_type_tax) {
805
-            return;
806
-        }
807
-
808
-        // get default tickets for updating
809
-        $default_tickets = EEM_Ticket::instance()->get_all_default_tickets();
810
-        foreach ($default_tickets as $default_ticket) {
811
-            if (! $default_ticket instanceof EE_Ticket) {
812
-                continue;
813
-            }
814
-            switch ($action) {
815
-                case EE_Admin_List_Table::ACTION_DELETE:
816
-                case EE_Admin_List_Table::ACTION_TRASH:
817
-                    // if trashing then remove relations to base default ticket.
818
-                    $default_ticket->_remove_relation_to($entity_ID, 'Price');
819
-                    break;
820
-                case EE_Admin_List_Table::ACTION_RESTORE:
821
-                    // if restoring then add back to base default ticket
822
-                    $default_ticket->_add_relation_to($entity_ID, 'Price');
823
-                    break;
824
-            }
825
-            $default_ticket->save();
826
-        }
827
-    }
828
-
829
-
830
-    /**
831
-     * @return void
832
-     * @throws EE_Error
833
-     * @throws ReflectionException
834
-     */
835
-    protected function _delete_price()
836
-    {
837
-        $entity_model = EEM_Price::instance();
838
-        $deleted      = $this->trashRestoreDeleteEntities($entity_model, 'id');
839
-        $entity       = $entity_model->item_name($deleted);
840
-        $this->_redirect_after_action(
841
-            $deleted,
842
-            $entity,
843
-            'deleted',
844
-            ['action' => 'default']
845
-        );
846
-    }
847
-
848
-
849
-    /**
850
-     * @throws EE_Error
851
-     * @throws ReflectionException
852
-     */
853
-    public function update_price_order()
854
-    {
855
-        // grab our row IDs
856
-        $row_ids = $this->request->getRequestParam('row_ids', '');
857
-        $row_ids = explode(',', rtrim($row_ids, ','));
858
-
859
-        $all_updated = true;
860
-        foreach ($row_ids as $i => $row_id) {
861
-            // Update the prices when re-ordering
862
-            $fields_n_values = ['PRC_order' => $i + 1];
863
-            $query_params    = [['PRC_ID' => absint($row_id)]];
864
-            // any failure will toggle $all_updated to false
865
-            $all_updated = $row_id && EEM_Price::instance()->update($fields_n_values, $query_params) !== false
866
-                ? $all_updated
867
-                : false;
868
-        }
869
-        $success = $all_updated
870
-            ? esc_html__('Price order was updated successfully.', 'event_espresso')
871
-            : false;
872
-        $errors  = ! $all_updated
873
-            ? esc_html__('An error occurred. The price order was not updated.', 'event_espresso')
874
-            : false;
875
-
876
-        echo wp_json_encode(['return_data' => false, 'success' => $success, 'errors' => $errors]);
877
-        die();
878
-    }
879
-
880
-
881
-    /******************************************************************************************************************
14
+	protected function _init_page_props()
15
+	{
16
+		$this->page_slug        = PRICING_PG_SLUG;
17
+		$this->page_label       = PRICING_LABEL;
18
+		$this->_admin_base_url  = PRICING_ADMIN_URL;
19
+		$this->_admin_base_path = PRICING_ADMIN;
20
+	}
21
+
22
+
23
+	protected function _ajax_hooks()
24
+	{
25
+		add_action('wp_ajax_espresso_update_prices_order', [$this, 'update_price_order']);
26
+	}
27
+
28
+
29
+	protected function _define_page_props()
30
+	{
31
+		$this->_admin_page_title = PRICING_LABEL;
32
+		$this->_labels           = [
33
+			'buttons' => [
34
+				'add'         => esc_html__('Add New Default Price', 'event_espresso'),
35
+				'edit'        => esc_html__('Edit Default Price', 'event_espresso'),
36
+				'delete'      => esc_html__('Delete Default Price', 'event_espresso'),
37
+				'add_type'    => esc_html__('Add New Default Price Type', 'event_espresso'),
38
+				'edit_type'   => esc_html__('Edit Price Type', 'event_espresso'),
39
+				'delete_type' => esc_html__('Delete Price Type', 'event_espresso'),
40
+			],
41
+		];
42
+	}
43
+
44
+
45
+	/**
46
+	 * an array for storing request actions and their corresponding methods
47
+	 *
48
+	 * @return void
49
+	 */
50
+	protected function _set_page_routes()
51
+	{
52
+		$PRC_ID             = $this->request->getRequestParam('PRC_ID', 0, DataType::INTEGER);
53
+		$PRT_ID             = $this->request->getRequestParam('PRT_ID', 0, DataType::INTEGER);
54
+		$this->_page_routes = [
55
+			'default'                     => [
56
+				'func'       => [$this, '_price_overview_list_table'],
57
+				'capability' => 'ee_read_default_prices',
58
+			],
59
+			'add_new_price'               => [
60
+				'func'       => [$this, '_edit_price_details'],
61
+				'capability' => 'ee_edit_default_prices',
62
+			],
63
+			'edit_price'                  => [
64
+				'func'       => [$this, '_edit_price_details'],
65
+				'capability' => 'ee_edit_default_price',
66
+				'obj_id'     => $PRC_ID,
67
+			],
68
+			'insert_price'                => [
69
+				'func'       => [$this, '_insert_or_update_price'],
70
+				'args'       => ['insert' => true],
71
+				'noheader'   => true,
72
+				'capability' => 'ee_edit_default_prices',
73
+			],
74
+			'update_price'                => [
75
+				'func'       => [$this, '_insert_or_update_price'],
76
+				'args'       => ['insert' => false],
77
+				'noheader'   => true,
78
+				'capability' => 'ee_edit_default_price',
79
+				'obj_id'     => $PRC_ID,
80
+			],
81
+			'trash_price'                 => [
82
+				'func'       => [$this, '_trash_or_restore_price'],
83
+				'args'       => ['trash' => true],
84
+				'noheader'   => true,
85
+				'capability' => 'ee_delete_default_price',
86
+				'obj_id'     => $PRC_ID,
87
+			],
88
+			'restore_price'               => [
89
+				'func'       => [$this, '_trash_or_restore_price'],
90
+				'args'       => ['trash' => false],
91
+				'noheader'   => true,
92
+				'capability' => 'ee_delete_default_price',
93
+				'obj_id'     => $PRC_ID,
94
+			],
95
+			'delete_price'                => [
96
+				'func'       => [$this, '_delete_price'],
97
+				'noheader'   => true,
98
+				'capability' => 'ee_delete_default_price',
99
+				'obj_id'     => $PRC_ID,
100
+			],
101
+			'espresso_update_price_order' => [
102
+				'func'       => [$this, 'update_price_order'],
103
+				'noheader'   => true,
104
+				'capability' => 'ee_edit_default_prices',
105
+			],
106
+			// price types
107
+			'price_types'                 => [
108
+				'func'       => [$this, '_price_types_overview_list_table'],
109
+				'capability' => 'ee_read_default_price_types',
110
+			],
111
+			'add_new_price_type'          => [
112
+				'func'       => [$this, '_edit_price_type_details'],
113
+				'capability' => 'ee_edit_default_price_types',
114
+			],
115
+			'edit_price_type'             => [
116
+				'func'       => [$this, '_edit_price_type_details'],
117
+				'capability' => 'ee_edit_default_price_type',
118
+				'obj_id'     => $PRT_ID,
119
+			],
120
+			'insert_price_type'           => [
121
+				'func'       => [$this, '_insert_or_update_price_type'],
122
+				'args'       => ['new_price_type' => true],
123
+				'noheader'   => true,
124
+				'capability' => 'ee_edit_default_price_types',
125
+			],
126
+			'update_price_type'           => [
127
+				'func'       => [$this, '_insert_or_update_price_type'],
128
+				'args'       => ['new_price_type' => false],
129
+				'noheader'   => true,
130
+				'capability' => 'ee_edit_default_price_type',
131
+				'obj_id'     => $PRT_ID,
132
+			],
133
+			'trash_price_type'            => [
134
+				'func'       => [$this, '_trash_or_restore_price_type'],
135
+				'args'       => ['trash' => true],
136
+				'noheader'   => true,
137
+				'capability' => 'ee_delete_default_price_type',
138
+				'obj_id'     => $PRT_ID,
139
+			],
140
+			'restore_price_type'          => [
141
+				'func'       => [$this, '_trash_or_restore_price_type'],
142
+				'args'       => ['trash' => false],
143
+				'noheader'   => true,
144
+				'capability' => 'ee_delete_default_price_type',
145
+				'obj_id'     => $PRT_ID,
146
+			],
147
+			'delete_price_type'           => [
148
+				'func'       => [$this, '_delete_price_type'],
149
+				'noheader'   => true,
150
+				'capability' => 'ee_delete_default_price_type',
151
+				'obj_id'     => $PRT_ID,
152
+			],
153
+			'tax_settings'                => [
154
+				'func'       => [$this, '_tax_settings'],
155
+				'capability' => 'manage_options',
156
+			],
157
+			'update_tax_settings'         => [
158
+				'func'       => [$this, '_update_tax_settings'],
159
+				'capability' => 'manage_options',
160
+				'noheader'   => true,
161
+			],
162
+		];
163
+	}
164
+
165
+
166
+	protected function _set_page_config()
167
+	{
168
+		$PRC_ID             = $this->request->getRequestParam('id', 0, DataType::INTEGER);
169
+		$this->_page_config = [
170
+			'default'            => [
171
+				'nav'           => [
172
+					'label' => esc_html__('Default Pricing', 'event_espresso'),
173
+					'icon' => 'dashicons-money-alt',
174
+					'order' => 10,
175
+				],
176
+				'list_table'    => 'Prices_List_Table',
177
+				'metaboxes'     => $this->_default_espresso_metaboxes,
178
+				'help_tabs'     => [
179
+					'pricing_default_pricing_help_tab'                           => [
180
+						'title'    => esc_html__('Default Pricing', 'event_espresso'),
181
+						'filename' => 'pricing_default_pricing',
182
+					],
183
+					'pricing_default_pricing_table_column_headings_help_tab'     => [
184
+						'title'    => esc_html__('Default Pricing Table Column Headings', 'event_espresso'),
185
+						'filename' => 'pricing_default_pricing_table_column_headings',
186
+					],
187
+					'pricing_default_pricing_views_bulk_actions_search_help_tab' => [
188
+						'title'    => esc_html__('Default Pricing Views & Bulk Actions & Search', 'event_espresso'),
189
+						'filename' => 'pricing_default_pricing_views_bulk_actions_search',
190
+					],
191
+				],
192
+				'require_nonce' => false,
193
+			],
194
+			'add_new_price'      => [
195
+				'nav'           => [
196
+					'label'      => esc_html__('Add New Default Price', 'event_espresso'),
197
+					'icon' => 'dashicons-plus-alt',
198
+					'order'      => 20,
199
+					'persistent' => false,
200
+				],
201
+				'help_tabs'     => [
202
+					'add_new_default_price_help_tab' => [
203
+						'title'    => esc_html__('Add New Default Price', 'event_espresso'),
204
+						'filename' => 'pricing_add_new_default_price',
205
+					],
206
+				],
207
+				'metaboxes'     => array_merge(
208
+					['_publish_post_box'],
209
+					$this->_default_espresso_metaboxes
210
+				),
211
+				'require_nonce' => false,
212
+			],
213
+			'edit_price'         => [
214
+				'nav'           => [
215
+					'label'      => esc_html__('Edit Default Price', 'event_espresso'),
216
+					'icon' => 'dashicons-edit-large',
217
+					'order'      => 20,
218
+					'url'        => $PRC_ID
219
+						? add_query_arg(['id' => $PRC_ID], $this->_current_page_view_url)
220
+						: $this->_admin_base_url,
221
+					'persistent' => false,
222
+				],
223
+				'metaboxes'     => array_merge(
224
+					['_publish_post_box'],
225
+					$this->_default_espresso_metaboxes
226
+				),
227
+				'help_tabs'     => [
228
+					'edit_default_price_help_tab' => [
229
+						'title'    => esc_html__('Edit Default Price', 'event_espresso'),
230
+						'filename' => 'pricing_edit_default_price',
231
+					],
232
+				],
233
+				'require_nonce' => false,
234
+			],
235
+			'price_types'        => [
236
+				'nav'           => [
237
+					'label' => esc_html__('Price Types', 'event_espresso'),
238
+					'icon' => 'dashicons-networking',
239
+					'order' => 30,
240
+				],
241
+				'list_table'    => 'Price_Types_List_Table',
242
+				'help_tabs'     => [
243
+					'pricing_price_types_help_tab'                           => [
244
+						'title'    => esc_html__('Price Types', 'event_espresso'),
245
+						'filename' => 'pricing_price_types',
246
+					],
247
+					'pricing_price_types_table_column_headings_help_tab'     => [
248
+						'title'    => esc_html__('Price Types Table Column Headings', 'event_espresso'),
249
+						'filename' => 'pricing_price_types_table_column_headings',
250
+					],
251
+					'pricing_price_types_views_bulk_actions_search_help_tab' => [
252
+						'title'    => esc_html__('Price Types Views & Bulk Actions & Search', 'event_espresso'),
253
+						'filename' => 'pricing_price_types_views_bulk_actions_search',
254
+					],
255
+				],
256
+				'metaboxes'     => $this->_default_espresso_metaboxes,
257
+				'require_nonce' => false,
258
+			],
259
+			'add_new_price_type' => [
260
+				'nav'           => [
261
+					'label'      => esc_html__('Add New Price Type', 'event_espresso'),
262
+					'icon' => 'dashicons-plus-alt',
263
+					'order'      => 40,
264
+					'persistent' => false,
265
+				],
266
+				'help_tabs'     => [
267
+					'add_new_price_type_help_tab' => [
268
+						'title'    => esc_html__('Add New Price Type', 'event_espresso'),
269
+						'filename' => 'pricing_add_new_price_type',
270
+					],
271
+				],
272
+				'metaboxes'     => array_merge(
273
+					['_publish_post_box'],
274
+					$this->_default_espresso_metaboxes
275
+				),
276
+				'require_nonce' => false,
277
+			],
278
+			'edit_price_type'    => [
279
+				'nav'           => [
280
+					'label'      => esc_html__('Edit Price Type', 'event_espresso'),
281
+					'icon' => 'dashicons-edit-large',
282
+					'order'      => 40,
283
+					'persistent' => false,
284
+				],
285
+				'help_tabs'     => [
286
+					'edit_price_type_help_tab' => [
287
+						'title'    => esc_html__('Edit Price Type', 'event_espresso'),
288
+						'filename' => 'pricing_edit_price_type',
289
+					],
290
+				],
291
+				'metaboxes'     => array_merge(
292
+					['_publish_post_box'],
293
+					$this->_default_espresso_metaboxes
294
+				),
295
+				'require_nonce' => false,
296
+			],
297
+			'tax_settings'       => [
298
+				'nav' => [
299
+					'label' => esc_html__('Tax Settings', 'event_espresso'),
300
+					'icon' => 'dashicons-sticky',
301
+					'order' => 50,
302
+				],
303
+				'labels'        => [
304
+					'publishbox' => esc_html__('Update Tax Settings', 'event_espresso'),
305
+				],
306
+				'metaboxes'     => array_merge(
307
+					['_publish_post_box'],
308
+					$this->_default_espresso_metaboxes
309
+				),
310
+				'require_nonce' => true,
311
+			],
312
+		];
313
+	}
314
+
315
+
316
+	protected function _add_screen_options()
317
+	{
318
+		// todo
319
+	}
320
+
321
+
322
+	protected function _add_screen_options_default()
323
+	{
324
+		$this->_per_page_screen_option();
325
+	}
326
+
327
+
328
+	protected function _add_screen_options_price_types()
329
+	{
330
+		$page_title              = $this->_admin_page_title;
331
+		$this->_admin_page_title = esc_html__('Price Types', 'event_espresso');
332
+		$this->_per_page_screen_option();
333
+		$this->_admin_page_title = $page_title;
334
+	}
335
+
336
+
337
+	protected function _add_feature_pointers()
338
+	{
339
+	}
340
+
341
+
342
+	public function load_scripts_styles()
343
+	{
344
+		// styles
345
+		wp_enqueue_style('espresso-ui-theme');
346
+		wp_register_style(
347
+			'espresso_PRICING',
348
+			PRICING_ASSETS_URL . 'espresso_pricing_admin.css',
349
+			[],
350
+			EVENT_ESPRESSO_VERSION
351
+		);
352
+		wp_enqueue_style('espresso_PRICING');
353
+
354
+		// scripts
355
+		wp_enqueue_script('ee_admin_js');
356
+		wp_enqueue_script('jquery-ui-position');
357
+		wp_enqueue_script('jquery-ui-widget');
358
+		wp_register_script(
359
+			'espresso_PRICING',
360
+			PRICING_ASSETS_URL . 'espresso_pricing_admin.js',
361
+			['jquery'],
362
+			EVENT_ESPRESSO_VERSION,
363
+			true
364
+		);
365
+		wp_enqueue_script('espresso_PRICING');
366
+	}
367
+
368
+
369
+	public function load_scripts_styles_default()
370
+	{
371
+		wp_enqueue_script('espresso_ajax_table_sorting');
372
+	}
373
+
374
+
375
+	public function admin_footer_scripts()
376
+	{
377
+	}
378
+
379
+
380
+	public function admin_init()
381
+	{
382
+	}
383
+
384
+
385
+	public function admin_notices()
386
+	{
387
+	}
388
+
389
+
390
+	protected function _set_list_table_views_default()
391
+	{
392
+		$this->_views = [
393
+			'all' => [
394
+				'slug'        => 'all',
395
+				'label'       => esc_html__('View All Default Pricing', 'event_espresso'),
396
+				'count'       => 0,
397
+				'bulk_action' => [
398
+					'trash_price' => esc_html__('Move to Trash', 'event_espresso'),
399
+				],
400
+			],
401
+		];
402
+
403
+		if (EE_Registry::instance()->CAP->current_user_can('ee_delete_default_prices', 'pricing_trash_price')) {
404
+			$this->_views['trashed'] = [
405
+				'slug'        => 'trashed',
406
+				'label'       => esc_html__('Trash', 'event_espresso'),
407
+				'count'       => 0,
408
+				'bulk_action' => [
409
+					'restore_price' => esc_html__('Restore from Trash', 'event_espresso'),
410
+					'delete_price'  => esc_html__('Delete Permanently', 'event_espresso'),
411
+				],
412
+			];
413
+		}
414
+	}
415
+
416
+
417
+	protected function _set_list_table_views_price_types()
418
+	{
419
+		$this->_views = [
420
+			'all' => [
421
+				'slug'        => 'all',
422
+				'label'       => esc_html__('All', 'event_espresso'),
423
+				'count'       => 0,
424
+				'bulk_action' => [
425
+					'trash_price_type' => esc_html__('Move to Trash', 'event_espresso'),
426
+				],
427
+			],
428
+		];
429
+
430
+		if (
431
+			EE_Registry::instance()->CAP->current_user_can(
432
+				'ee_delete_default_price_types',
433
+				'pricing_trash_price_type'
434
+			)
435
+		) {
436
+			$this->_views['trashed'] = [
437
+				'slug'        => 'trashed',
438
+				'label'       => esc_html__('Trash', 'event_espresso'),
439
+				'count'       => 0,
440
+				'bulk_action' => [
441
+					'restore_price_type' => esc_html__('Restore from Trash', 'event_espresso'),
442
+					'delete_price_type'  => esc_html__('Delete Permanently', 'event_espresso'),
443
+				],
444
+			];
445
+		}
446
+	}
447
+
448
+
449
+	/**
450
+	 * generates HTML for main Prices Admin page
451
+	 *
452
+	 * @return void
453
+	 * @throws EE_Error
454
+	 */
455
+	protected function _price_overview_list_table()
456
+	{
457
+		$this->_admin_page_title .= ' ' . $this->get_action_link_or_button(
458
+			'add_new_price',
459
+			'add',
460
+			[],
461
+			'add-new-h2'
462
+		);
463
+		$this->_admin_page_title .= $this->_learn_more_about_pricing_link();
464
+		$this->_search_btn_label = esc_html__('Default Prices', 'event_espresso');
465
+		$this->display_admin_list_table_page_with_sidebar();
466
+	}
467
+
468
+
469
+	/**
470
+	 * retrieve data for Prices List table
471
+	 *
472
+	 * @param int  $per_page how many prices displayed per page
473
+	 * @param bool $count    return the count or objects
474
+	 * @param bool $trashed  whether the current view is of the trash can - eww yuck!
475
+	 * @return EE_Soft_Delete_Base_Class[]|int int = count || array of price objects
476
+	 * @throws EE_Error
477
+	 * @throws ReflectionException
478
+	 */
479
+	public function get_prices_overview_data(int $per_page = 10, bool $count = false, bool $trashed = false)
480
+	{
481
+		// start with an empty array
482
+		$event_pricing = [];
483
+
484
+		require_once(PRICING_ADMIN . 'Prices_List_Table.class.php');
485
+
486
+		$orderby = $this->request->getRequestParam('orderby', '');
487
+		$order   = $this->request->getRequestParam('order', 'ASC');
488
+
489
+		switch ($orderby) {
490
+			case 'name':
491
+				$orderby = ['PRC_name' => $order];
492
+				break;
493
+			case 'type':
494
+				$orderby = ['Price_Type.PRT_name' => $order];
495
+				break;
496
+			case 'amount':
497
+				$orderby = ['PRC_amount' => $order];
498
+				break;
499
+			default:
500
+				$orderby = ['PRC_order' => $order, 'Price_Type.PRT_order' => $order, 'PRC_ID' => $order];
501
+		}
502
+
503
+		$current_page = $this->request->getRequestParam('paged', 1, DataType::INTEGER);
504
+		$per_page     = $this->request->getRequestParam('perpage', $per_page, DataType::INTEGER);
505
+
506
+		$where = [
507
+			'PRC_is_default' => 1,
508
+			'PRC_deleted'    => $trashed,
509
+		];
510
+
511
+		$offset = ($current_page - 1) * $per_page;
512
+		$limit  = [$offset, $per_page];
513
+
514
+		$search_term = $this->request->getRequestParam('s');
515
+		if ($search_term) {
516
+			$search_term = "%{$search_term}%";
517
+			$where['OR'] = [
518
+				'PRC_name'            => ['LIKE', $search_term],
519
+				'PRC_desc'            => ['LIKE', $search_term],
520
+				'PRC_amount'          => ['LIKE', $search_term],
521
+				'Price_Type.PRT_name' => ['LIKE', $search_term],
522
+			];
523
+		}
524
+
525
+		$query_params = [
526
+			$where,
527
+			'order_by' => $orderby,
528
+			'limit'    => $limit,
529
+			'group_by' => 'PRC_ID',
530
+		];
531
+
532
+		if ($count) {
533
+			return $trashed
534
+				? EEM_Price::instance()->count([$where])
535
+				: EEM_Price::instance()->count_deleted_and_undeleted([$where]);
536
+		}
537
+		return EEM_Price::instance()->get_all_deleted_and_undeleted($query_params);
538
+	}
539
+
540
+
541
+	/**
542
+	 * @return void
543
+	 * @throws EE_Error
544
+	 * @throws ReflectionException
545
+	 */
546
+	protected function _edit_price_details()
547
+	{
548
+		// grab price ID
549
+		$PRC_ID = $this->request->getRequestParam('id', 0, DataType::INTEGER);
550
+		// change page title based on request action
551
+		switch ($this->_req_action) {
552
+			case 'add_new_price':
553
+				$this->_admin_page_title = esc_html__('Add New Price', 'event_espresso');
554
+				break;
555
+			case 'edit_price':
556
+				$this->_admin_page_title = esc_html__('Edit Price', 'event_espresso');
557
+				break;
558
+			default:
559
+				$this->_admin_page_title = ucwords(str_replace('_', ' ', $this->_req_action));
560
+		}
561
+		// add PRC_ID to title if editing
562
+		$this->_admin_page_title = $PRC_ID ? $this->_admin_page_title . ' # ' . $PRC_ID : $this->_admin_page_title;
563
+
564
+		if ($PRC_ID) {
565
+			$price                    = EEM_Price::instance()->get_one_by_ID($PRC_ID);
566
+			$additional_hidden_fields = [
567
+				'PRC_ID' => ['type' => 'hidden', 'value' => $PRC_ID],
568
+			];
569
+			$this->_set_add_edit_form_tags('update_price', $additional_hidden_fields);
570
+		} else {
571
+			$price = EEM_Price::instance()->get_new_price();
572
+			$this->_set_add_edit_form_tags('insert_price');
573
+		}
574
+
575
+		if (! $price instanceof EE_Price) {
576
+			throw new RuntimeException(
577
+				sprintf(
578
+					esc_html__(
579
+						'A valid Price could not be retrieved from the database with ID: %1$s',
580
+						'event_espresso'
581
+					),
582
+					$PRC_ID
583
+				)
584
+			);
585
+		}
586
+
587
+		$this->_template_args['PRC_ID'] = $PRC_ID;
588
+		$this->_template_args['price']  = $price;
589
+
590
+		$default_base_price = $price->type_obj() && $price->type_obj()->base_type() === 1;
591
+
592
+		$this->_template_args['default_base_price'] = $default_base_price;
593
+
594
+		// get price types
595
+		$price_types = EEM_Price_Type::instance()->get_all([['PBT_ID' => ['!=', 1]]]);
596
+		if (empty($price_types)) {
597
+			$msg = esc_html__(
598
+				'You have no price types defined. Please add a price type before adding a price.',
599
+				'event_espresso'
600
+			);
601
+			EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
602
+			$this->display_admin_page_with_sidebar();
603
+		}
604
+		$attributes       = [];
605
+		$price_type_names = [];
606
+		$attributes[]     = 'id="PRT_ID"';
607
+		if ($default_base_price) {
608
+			$attributes[]       = 'disabled="disabled"';
609
+			$price_type_names[] = ['id' => 1, 'text' => esc_html__('Base Price', 'event_espresso')];
610
+		}
611
+		foreach ($price_types as $type) {
612
+			$price_type_names[] = ['id' => $type->ID(), 'text' => $type->name()];
613
+		}
614
+		$this->_template_args['attributes']  = implode(' ', $attributes);
615
+		$this->_template_args['price_types'] = $price_type_names;
616
+
617
+		$this->_template_args['learn_more_about_pricing_link'] = $this->_learn_more_about_pricing_link();
618
+		$this->_template_args['admin_page_content']            = $this->_edit_price_details_meta_box();
619
+
620
+		$this->_set_publish_post_box_vars('id', $PRC_ID);
621
+		// the details template wrapper
622
+		$this->display_admin_page_with_sidebar();
623
+	}
624
+
625
+
626
+	/**
627
+	 *
628
+	 * @return string
629
+	 */
630
+	public function _edit_price_details_meta_box(): string
631
+	{
632
+		return EEH_Template::display_template(
633
+			PRICING_TEMPLATE_PATH . 'pricing_details_main_meta_box.template.php',
634
+			$this->_template_args,
635
+			true
636
+		);
637
+	}
638
+
639
+
640
+	/**
641
+	 * @return array
642
+	 * @throws EE_Error
643
+	 * @throws ReflectionException
644
+	 */
645
+	protected function set_price_column_values(): array
646
+	{
647
+		$PRC_order = 0;
648
+		$PRT_ID    = $this->request->getRequestParam('PRT_ID', 0, DataType::INTEGER);
649
+		if ($PRT_ID) {
650
+			/** @var EE_Price_Type $price_type */
651
+			$price_type = EEM_Price_Type::instance()->get_one_by_ID($PRT_ID);
652
+			if ($price_type instanceof EE_Price_Type) {
653
+				$PRC_order = $price_type->order();
654
+			}
655
+		}
656
+		return [
657
+			'PRT_ID'         => $PRT_ID,
658
+			'PRC_amount'     => $this->request->getRequestParam('PRC_amount', 0, DataType::FLOAT),
659
+			'PRC_name'       => $this->request->getRequestParam('PRC_name'),
660
+			'PRC_desc'       => $this->request->getRequestParam('PRC_desc'),
661
+			'PRC_is_default' => 1,
662
+			'PRC_overrides'  => null,
663
+			'PRC_order'      => $PRC_order,
664
+			'PRC_deleted'    => 0,
665
+			'PRC_parent'     => 0,
666
+		];
667
+	}
668
+
669
+
670
+	/**
671
+	 * @param bool $insert - whether to insert or update
672
+	 * @return void
673
+	 * @throws EE_Error
674
+	 * @throws ReflectionException
675
+	 */
676
+	protected function _insert_or_update_price(bool $insert = false)
677
+	{
678
+		// why be so pessimistic ???  : (
679
+		$updated = 0;
680
+
681
+		$set_column_values = $this->set_price_column_values();
682
+		// is this a new Price ?
683
+		if ($insert) {
684
+			// run the insert
685
+			$PRC_ID = EEM_Price::instance()->insert($set_column_values);
686
+			if ($PRC_ID) {
687
+				// make sure this new price modifier is attached to the ticket but ONLY if it is not a tax type
688
+				$price = EEM_price::instance()->get_one_by_ID($PRC_ID);
689
+				if (
690
+					$price instanceof EE_Price
691
+					&& $price->type_obj() instanceof EE_Price_type
692
+					&& $price->type_obj()->base_type() !== EEM_Price_Type::base_type_tax
693
+				) {
694
+					$ticket = EEM_Ticket::instance()->get_one_by_ID(1);
695
+					$ticket->_add_relation_to($price, 'Price');
696
+					$ticket->save();
697
+				}
698
+				$updated = 1;
699
+			}
700
+			$action_desc = 'created';
701
+		} else {
702
+			$PRC_ID = $this->request->getRequestParam('PRC_ID', 0, DataType::INTEGER);
703
+			// run the update
704
+			$where_cols_n_values = ['PRC_ID' => $PRC_ID];
705
+			$updated             = EEM_Price::instance()->update($set_column_values, [$where_cols_n_values]);
706
+
707
+			$price = EEM_Price::instance()->get_one_by_ID($PRC_ID);
708
+			if ($price instanceof EE_Price && $price->type_obj()->base_type() !== EEM_Price_Type::base_type_tax) {
709
+				// if this is $PRC_ID == 1,
710
+				// then we need to update the default ticket attached to this price so the TKT_price value is updated.
711
+				if ($PRC_ID === 1) {
712
+					$ticket = $price->get_first_related('Ticket');
713
+					if ($ticket) {
714
+						$ticket->set('TKT_price', $price->get('PRC_amount'));
715
+						$ticket->set('TKT_name', $price->get('PRC_name'));
716
+						$ticket->set('TKT_description', $price->get('PRC_desc'));
717
+						$ticket->save();
718
+					}
719
+				} else {
720
+					// we make sure this price is attached to base ticket. but ONLY if its not a tax ticket type.
721
+					$ticket = EEM_Ticket::instance()->get_one_by_ID(1);
722
+					$ticket->_add_relation_to($PRC_ID, 'Price');
723
+					$ticket->save();
724
+				}
725
+			}
726
+
727
+			$action_desc = 'updated';
728
+		}
729
+
730
+		$query_args = ['action' => 'edit_price', 'id' => $PRC_ID];
731
+
732
+		$this->_redirect_after_action($updated, 'Prices', $action_desc, $query_args);
733
+	}
734
+
735
+
736
+	/**
737
+	 * @param bool $trash - whether to move item to trash (TRUE) or restore it (FALSE)
738
+	 * @return void
739
+	 * @throws EE_Error
740
+	 */
741
+	protected function _trash_or_restore_price($trash = true)
742
+	{
743
+		$entity_model = EEM_Price::instance();
744
+		$action       = $trash ? EE_Admin_List_Table::ACTION_TRASH : EE_Admin_List_Table::ACTION_RESTORE;
745
+		$result       = $this->trashRestoreDeleteEntities(
746
+			$entity_model,
747
+			'id',
748
+			$action,
749
+			'PRC_deleted',
750
+			[$this, 'adjustTicketRelations']
751
+		);
752
+
753
+		if ($result) {
754
+			$msg = $trash
755
+				? esc_html(
756
+					_n(
757
+						'The Price has been trashed',
758
+						'The Prices have been trashed',
759
+						$result,
760
+						'event_espresso'
761
+					)
762
+				)
763
+				: esc_html(
764
+					_n(
765
+						'The Price has been restored',
766
+						'The Prices have been restored',
767
+						$result,
768
+						'event_espresso'
769
+					)
770
+				);
771
+			EE_Error::add_success($msg);
772
+		}
773
+
774
+		$this->_redirect_after_action(
775
+			$result,
776
+			_n('Price', 'Prices', $result, 'event_espresso'),
777
+			$trash ? 'trashed' : 'restored',
778
+			['action' => 'default'],
779
+			true
780
+		);
781
+	}
782
+
783
+
784
+	/**
785
+	 * @param EEM_Base   $entity_model
786
+	 * @param int|string $entity_ID
787
+	 * @param string     $action
788
+	 * @param int        $result
789
+	 * @throws EE_Error
790
+	 * @throws ReflectionException
791
+	 * @since 4.10.30.p
792
+	 */
793
+	public function adjustTicketRelations(
794
+		EEM_Base $entity_model,
795
+		$entity_ID,
796
+		string $action,
797
+		int $result
798
+	) {
799
+		if (! $entity_ID || (float) $result < 1) {
800
+			return;
801
+		}
802
+
803
+		$entity = $entity_model->get_one_by_ID($entity_ID);
804
+		if (! $entity instanceof EE_Price || $entity->type_obj()->base_type() === EEM_Price_Type::base_type_tax) {
805
+			return;
806
+		}
807
+
808
+		// get default tickets for updating
809
+		$default_tickets = EEM_Ticket::instance()->get_all_default_tickets();
810
+		foreach ($default_tickets as $default_ticket) {
811
+			if (! $default_ticket instanceof EE_Ticket) {
812
+				continue;
813
+			}
814
+			switch ($action) {
815
+				case EE_Admin_List_Table::ACTION_DELETE:
816
+				case EE_Admin_List_Table::ACTION_TRASH:
817
+					// if trashing then remove relations to base default ticket.
818
+					$default_ticket->_remove_relation_to($entity_ID, 'Price');
819
+					break;
820
+				case EE_Admin_List_Table::ACTION_RESTORE:
821
+					// if restoring then add back to base default ticket
822
+					$default_ticket->_add_relation_to($entity_ID, 'Price');
823
+					break;
824
+			}
825
+			$default_ticket->save();
826
+		}
827
+	}
828
+
829
+
830
+	/**
831
+	 * @return void
832
+	 * @throws EE_Error
833
+	 * @throws ReflectionException
834
+	 */
835
+	protected function _delete_price()
836
+	{
837
+		$entity_model = EEM_Price::instance();
838
+		$deleted      = $this->trashRestoreDeleteEntities($entity_model, 'id');
839
+		$entity       = $entity_model->item_name($deleted);
840
+		$this->_redirect_after_action(
841
+			$deleted,
842
+			$entity,
843
+			'deleted',
844
+			['action' => 'default']
845
+		);
846
+	}
847
+
848
+
849
+	/**
850
+	 * @throws EE_Error
851
+	 * @throws ReflectionException
852
+	 */
853
+	public function update_price_order()
854
+	{
855
+		// grab our row IDs
856
+		$row_ids = $this->request->getRequestParam('row_ids', '');
857
+		$row_ids = explode(',', rtrim($row_ids, ','));
858
+
859
+		$all_updated = true;
860
+		foreach ($row_ids as $i => $row_id) {
861
+			// Update the prices when re-ordering
862
+			$fields_n_values = ['PRC_order' => $i + 1];
863
+			$query_params    = [['PRC_ID' => absint($row_id)]];
864
+			// any failure will toggle $all_updated to false
865
+			$all_updated = $row_id && EEM_Price::instance()->update($fields_n_values, $query_params) !== false
866
+				? $all_updated
867
+				: false;
868
+		}
869
+		$success = $all_updated
870
+			? esc_html__('Price order was updated successfully.', 'event_espresso')
871
+			: false;
872
+		$errors  = ! $all_updated
873
+			? esc_html__('An error occurred. The price order was not updated.', 'event_espresso')
874
+			: false;
875
+
876
+		echo wp_json_encode(['return_data' => false, 'success' => $success, 'errors' => $errors]);
877
+		die();
878
+	}
879
+
880
+
881
+	/******************************************************************************************************************
882 882
      ***********************************************  TICKET PRICE TYPES  *********************************************
883 883
      ******************************************************************************************************************/
884 884
 
885 885
 
886
-    /**
887
-     * generates HTML for main Prices Admin page
888
-     *
889
-     * @return void
890
-     * @throws EE_Error
891
-     */
892
-    protected function _price_types_overview_list_table()
893
-    {
894
-        $this->_admin_page_title .= ' ' . $this->get_action_link_or_button(
895
-            'add_new_price_type',
896
-            'add_type',
897
-            [],
898
-            'add-new-h2'
899
-        );
900
-        $this->_admin_page_title .= $this->_learn_more_about_pricing_link();
901
-        $this->_search_btn_label = esc_html__('Price Types', 'event_espresso');
902
-        $this->display_admin_list_table_page_with_sidebar();
903
-    }
904
-
905
-
906
-    /**
907
-     * retrieve data for Price Types List table
908
-     *
909
-     * @param int  $per_page how many prices displayed per page
910
-     * @param bool $count    return the count or objects
911
-     * @param bool $trashed  whether the current view is of the trash can - eww yuck!
912
-     * @return EE_Soft_Delete_Base_Class[]|int int = count || array of price objects
913
-     * @throws EE_Error
914
-     * @throws ReflectionException
915
-     */
916
-    public function get_price_types_overview_data(int $per_page = 10, bool $count = false, bool $trashed = false)
917
-    {
918
-        // start with an empty array
919
-        require_once(PRICING_ADMIN . 'Price_Types_List_Table.class.php');
920
-
921
-        $orderby = $this->request->getRequestParam('orderby', '');
922
-        $order   = $this->request->getRequestParam('order', 'ASC');
923
-
924
-        switch ($orderby) {
925
-            case 'name':
926
-                $orderby = ['PRT_name' => $order];
927
-                break;
928
-            default:
929
-                $orderby = ['PRT_order' => $order];
930
-        }
931
-
932
-        $current_page = $this->request->getRequestParam('paged', 1, DataType::INTEGER);
933
-        $per_page     = $this->request->getRequestParam('perpage', $per_page, DataType::INTEGER);
934
-
935
-        $offset = ($current_page - 1) * $per_page;
936
-        $limit  = [$offset, $per_page];
937
-
938
-        $where = ['PRT_deleted' => $trashed, 'PBT_ID' => ['!=', 1]];
939
-
940
-        $search_term = $this->request->getRequestParam('s');
941
-        if ($search_term) {
942
-            $where['OR'] = [
943
-                'PRT_name' => ['LIKE', "%{$search_term}%"],
944
-            ];
945
-        }
946
-        $query_params = [
947
-            $where,
948
-            'order_by' => $orderby,
949
-            'limit'    => $limit,
950
-        ];
951
-        return $count
952
-            ? EEM_Price_Type::instance()->count_deleted_and_undeleted($query_params)
953
-            : EEM_Price_Type::instance()->get_all_deleted_and_undeleted($query_params);
954
-    }
955
-
956
-
957
-    /**
958
-     * _edit_price_type_details
959
-     *
960
-     * @return void
961
-     * @throws EE_Error
962
-     * @throws ReflectionException
963
-     */
964
-    protected function _edit_price_type_details()
965
-    {
966
-        // grab price type ID
967
-        $PRT_ID = $this->request->getRequestParam('id', 0, DataType::INTEGER);
968
-        // change page title based on request action
969
-        switch ($this->_req_action) {
970
-            case 'add_new_price_type':
971
-                $this->_admin_page_title = esc_html__('Add New Price Type', 'event_espresso');
972
-                break;
973
-            case 'edit_price_type':
974
-                $this->_admin_page_title = esc_html__('Edit Price Type', 'event_espresso');
975
-                break;
976
-            default:
977
-                $this->_admin_page_title = ucwords(str_replace('_', ' ', $this->_req_action));
978
-        }
979
-        // add PRT_ID to title if editing
980
-        $this->_admin_page_title = $PRT_ID ? $this->_admin_page_title . ' # ' . $PRT_ID : $this->_admin_page_title;
981
-
982
-        if ($PRT_ID) {
983
-            $price_type               = EEM_Price_Type::instance()->get_one_by_ID($PRT_ID);
984
-            $additional_hidden_fields = ['PRT_ID' => ['type' => 'hidden', 'value' => $PRT_ID]];
985
-            $this->_set_add_edit_form_tags('update_price_type', $additional_hidden_fields);
986
-        } else {
987
-            $price_type = EEM_Price_Type::instance()->get_new_price_type();
988
-            $this->_set_add_edit_form_tags('insert_price_type');
989
-        }
990
-
991
-        if (! $price_type instanceof EE_Price_Type) {
992
-            throw new RuntimeException(
993
-                sprintf(
994
-                    esc_html__(
995
-                        'A valid Price Type could not be retrieved from the database with ID: %1$s',
996
-                        'event_espresso'
997
-                    ),
998
-                    $PRT_ID
999
-                )
1000
-            );
1001
-        }
1002
-
1003
-        $this->_template_args['PRT_ID']     = $PRT_ID;
1004
-        $this->_template_args['price_type'] = $price_type;
1005
-
1006
-        $base_types    = EEM_Price_Type::instance()->get_base_types();
1007
-        $select_values = [];
1008
-        foreach ($base_types as $ref => $text) {
1009
-            if ($ref == EEM_Price_Type::base_type_base_price) {
1010
-                // do not allow creation of base_type_base_prices because that's a system only base type.
1011
-                continue;
1012
-            }
1013
-            $select_values[] = ['id' => $ref, 'text' => $text];
1014
-        }
1015
-
1016
-        $this->_template_args['base_type_select'] = EEH_Form_Fields::select_input(
1017
-            'base_type',
1018
-            $select_values,
1019
-            $price_type->base_type(),
1020
-            'id="price-type-base-type-slct"'
1021
-        );
1022
-
1023
-        $this->_template_args['learn_more_about_pricing_link'] = $this->_learn_more_about_pricing_link();
1024
-        $this->_template_args['admin_page_content']            = $this->_edit_price_type_details_meta_box();
1025
-
1026
-        $redirect_URL = add_query_arg(['action' => 'price_types'], $this->_admin_base_url);
1027
-        $this->_set_publish_post_box_vars('id', $PRT_ID, false, $redirect_URL);
1028
-        // the details template wrapper
1029
-        $this->display_admin_page_with_sidebar();
1030
-    }
1031
-
1032
-
1033
-    /**
1034
-     * _edit_price_type_details_meta_box
1035
-     *
1036
-     * @return string
1037
-     */
1038
-    public function _edit_price_type_details_meta_box(): string
1039
-    {
1040
-        return EEH_Template::display_template(
1041
-            PRICING_TEMPLATE_PATH . 'pricing_type_details_main_meta_box.template.php',
1042
-            $this->_template_args,
1043
-            true
1044
-        );
1045
-    }
1046
-
1047
-
1048
-    /**
1049
-     * @return array
1050
-     */
1051
-    protected function set_price_type_column_values(): array
1052
-    {
1053
-        $base_type  = $this->request->getRequestParam(
1054
-            'base_type',
1055
-            EEM_Price_Type::base_type_base_price,
1056
-            DataType::INTEGER
1057
-        );
1058
-        $is_percent = $this->request->getRequestParam('PRT_is_percent', 0, DataType::INTEGER);
1059
-        $order      = $this->request->getRequestParam('PRT_order', 0, DataType::INTEGER);
1060
-        switch ($base_type) {
1061
-            case EEM_Price_Type::base_type_base_price:
1062
-                $is_percent = 0;
1063
-                $order      = 0;
1064
-                break;
1065
-
1066
-            case EEM_Price_Type::base_type_discount:
1067
-            case EEM_Price_Type::base_type_surcharge:
1068
-                break;
1069
-
1070
-            case EEM_Price_Type::base_type_tax:
1071
-                $is_percent = 1;
1072
-                break;
1073
-        }
1074
-
1075
-        return [
1076
-            'PBT_ID'         => $base_type,
1077
-            'PRT_name'       => $this->request->getRequestParam('PRT_name', ''),
1078
-            'PRT_is_percent' => $is_percent,
1079
-            'PRT_order'      => $order,
1080
-            'PRT_deleted'    => 0,
1081
-        ];
1082
-    }
1083
-
1084
-
1085
-    /**
1086
-     * @param bool $new_price_type - whether to insert or update
1087
-     * @return void
1088
-     * @throws EE_Error
1089
-     * @throws ReflectionException
1090
-     */
1091
-    protected function _insert_or_update_price_type(bool $new_price_type = false)
1092
-    {
1093
-        // why be so pessimistic ???  : (
1094
-        $success = 0;
1095
-
1096
-        $set_column_values = $this->set_price_type_column_values();
1097
-        // is this a new Price ?
1098
-        if ($new_price_type) {
1099
-            // run the insert
1100
-            if ($PRT_ID = EEM_Price_Type::instance()->insert($set_column_values)) {
1101
-                $success = 1;
1102
-            }
1103
-            $action_desc = 'created';
1104
-        } else {
1105
-            $PRT_ID = $this->request->getRequestParam('PRT_ID', 0, DataType::INTEGER);
1106
-            // run the update
1107
-            $where_cols_n_values = ['PRT_ID' => $PRT_ID];
1108
-            if (EEM_Price_Type::instance()->update($set_column_values, [$where_cols_n_values])) {
1109
-                $success = 1;
1110
-            }
1111
-            $action_desc = 'updated';
1112
-        }
1113
-
1114
-        $query_args = ['action' => 'edit_price_type', 'id' => $PRT_ID];
1115
-        $this->_redirect_after_action($success, 'Price Type', $action_desc, $query_args);
1116
-    }
1117
-
1118
-
1119
-    /**
1120
-     * @param bool $trash - whether to move item to trash (TRUE) or restore it (FALSE)
1121
-     * @return void
1122
-     * @throws EE_Error
1123
-     * @throws ReflectionException
1124
-     */
1125
-    protected function _trash_or_restore_price_type(bool $trash = true)
1126
-    {
1127
-        $entity_model = EEM_Price_Type::instance();
1128
-        $action       = $trash ? EE_Admin_List_Table::ACTION_TRASH : EE_Admin_List_Table::ACTION_RESTORE;
1129
-        $success      = $this->trashRestoreDeleteEntities($entity_model, 'id', $action, 'PRT_deleted');
1130
-        if ($success) {
1131
-            $msg = $trash
1132
-                ? esc_html(
1133
-                    _n(
1134
-                        'The Price Type has been trashed',
1135
-                        'The Price Types have been trashed',
1136
-                        $success,
1137
-                        'event_espresso'
1138
-                    )
1139
-                )
1140
-                : esc_html(
1141
-                    _n(
1142
-                        'The Price Type has been restored',
1143
-                        'The Price Types have been restored',
1144
-                        $success,
1145
-                        'event_espresso'
1146
-                    )
1147
-                );
1148
-            EE_Error::add_success($msg);
1149
-        }
1150
-        $this->_redirect_after_action('', '', '', ['action' => 'price_types'], true);
1151
-    }
1152
-
1153
-
1154
-    /**
1155
-     * @return void
1156
-     * @throws EE_Error
1157
-     * @throws ReflectionException
1158
-     */
1159
-    protected function _delete_price_type()
1160
-    {
1161
-        $entity_model = EEM_Price_Type::instance();
1162
-        $deleted      = $this->trashRestoreDeleteEntities($entity_model, 'id');
1163
-        $this->_redirect_after_action(
1164
-            $deleted,
1165
-            $entity_model->item_name($deleted),
1166
-            'deleted',
1167
-            ['action' => 'price_types']
1168
-        );
1169
-    }
1170
-
1171
-
1172
-    /**
1173
-     * @return string
1174
-     */
1175
-    protected function _learn_more_about_pricing_link(): string
1176
-    {
1177
-        return '
886
+	/**
887
+	 * generates HTML for main Prices Admin page
888
+	 *
889
+	 * @return void
890
+	 * @throws EE_Error
891
+	 */
892
+	protected function _price_types_overview_list_table()
893
+	{
894
+		$this->_admin_page_title .= ' ' . $this->get_action_link_or_button(
895
+			'add_new_price_type',
896
+			'add_type',
897
+			[],
898
+			'add-new-h2'
899
+		);
900
+		$this->_admin_page_title .= $this->_learn_more_about_pricing_link();
901
+		$this->_search_btn_label = esc_html__('Price Types', 'event_espresso');
902
+		$this->display_admin_list_table_page_with_sidebar();
903
+	}
904
+
905
+
906
+	/**
907
+	 * retrieve data for Price Types List table
908
+	 *
909
+	 * @param int  $per_page how many prices displayed per page
910
+	 * @param bool $count    return the count or objects
911
+	 * @param bool $trashed  whether the current view is of the trash can - eww yuck!
912
+	 * @return EE_Soft_Delete_Base_Class[]|int int = count || array of price objects
913
+	 * @throws EE_Error
914
+	 * @throws ReflectionException
915
+	 */
916
+	public function get_price_types_overview_data(int $per_page = 10, bool $count = false, bool $trashed = false)
917
+	{
918
+		// start with an empty array
919
+		require_once(PRICING_ADMIN . 'Price_Types_List_Table.class.php');
920
+
921
+		$orderby = $this->request->getRequestParam('orderby', '');
922
+		$order   = $this->request->getRequestParam('order', 'ASC');
923
+
924
+		switch ($orderby) {
925
+			case 'name':
926
+				$orderby = ['PRT_name' => $order];
927
+				break;
928
+			default:
929
+				$orderby = ['PRT_order' => $order];
930
+		}
931
+
932
+		$current_page = $this->request->getRequestParam('paged', 1, DataType::INTEGER);
933
+		$per_page     = $this->request->getRequestParam('perpage', $per_page, DataType::INTEGER);
934
+
935
+		$offset = ($current_page - 1) * $per_page;
936
+		$limit  = [$offset, $per_page];
937
+
938
+		$where = ['PRT_deleted' => $trashed, 'PBT_ID' => ['!=', 1]];
939
+
940
+		$search_term = $this->request->getRequestParam('s');
941
+		if ($search_term) {
942
+			$where['OR'] = [
943
+				'PRT_name' => ['LIKE', "%{$search_term}%"],
944
+			];
945
+		}
946
+		$query_params = [
947
+			$where,
948
+			'order_by' => $orderby,
949
+			'limit'    => $limit,
950
+		];
951
+		return $count
952
+			? EEM_Price_Type::instance()->count_deleted_and_undeleted($query_params)
953
+			: EEM_Price_Type::instance()->get_all_deleted_and_undeleted($query_params);
954
+	}
955
+
956
+
957
+	/**
958
+	 * _edit_price_type_details
959
+	 *
960
+	 * @return void
961
+	 * @throws EE_Error
962
+	 * @throws ReflectionException
963
+	 */
964
+	protected function _edit_price_type_details()
965
+	{
966
+		// grab price type ID
967
+		$PRT_ID = $this->request->getRequestParam('id', 0, DataType::INTEGER);
968
+		// change page title based on request action
969
+		switch ($this->_req_action) {
970
+			case 'add_new_price_type':
971
+				$this->_admin_page_title = esc_html__('Add New Price Type', 'event_espresso');
972
+				break;
973
+			case 'edit_price_type':
974
+				$this->_admin_page_title = esc_html__('Edit Price Type', 'event_espresso');
975
+				break;
976
+			default:
977
+				$this->_admin_page_title = ucwords(str_replace('_', ' ', $this->_req_action));
978
+		}
979
+		// add PRT_ID to title if editing
980
+		$this->_admin_page_title = $PRT_ID ? $this->_admin_page_title . ' # ' . $PRT_ID : $this->_admin_page_title;
981
+
982
+		if ($PRT_ID) {
983
+			$price_type               = EEM_Price_Type::instance()->get_one_by_ID($PRT_ID);
984
+			$additional_hidden_fields = ['PRT_ID' => ['type' => 'hidden', 'value' => $PRT_ID]];
985
+			$this->_set_add_edit_form_tags('update_price_type', $additional_hidden_fields);
986
+		} else {
987
+			$price_type = EEM_Price_Type::instance()->get_new_price_type();
988
+			$this->_set_add_edit_form_tags('insert_price_type');
989
+		}
990
+
991
+		if (! $price_type instanceof EE_Price_Type) {
992
+			throw new RuntimeException(
993
+				sprintf(
994
+					esc_html__(
995
+						'A valid Price Type could not be retrieved from the database with ID: %1$s',
996
+						'event_espresso'
997
+					),
998
+					$PRT_ID
999
+				)
1000
+			);
1001
+		}
1002
+
1003
+		$this->_template_args['PRT_ID']     = $PRT_ID;
1004
+		$this->_template_args['price_type'] = $price_type;
1005
+
1006
+		$base_types    = EEM_Price_Type::instance()->get_base_types();
1007
+		$select_values = [];
1008
+		foreach ($base_types as $ref => $text) {
1009
+			if ($ref == EEM_Price_Type::base_type_base_price) {
1010
+				// do not allow creation of base_type_base_prices because that's a system only base type.
1011
+				continue;
1012
+			}
1013
+			$select_values[] = ['id' => $ref, 'text' => $text];
1014
+		}
1015
+
1016
+		$this->_template_args['base_type_select'] = EEH_Form_Fields::select_input(
1017
+			'base_type',
1018
+			$select_values,
1019
+			$price_type->base_type(),
1020
+			'id="price-type-base-type-slct"'
1021
+		);
1022
+
1023
+		$this->_template_args['learn_more_about_pricing_link'] = $this->_learn_more_about_pricing_link();
1024
+		$this->_template_args['admin_page_content']            = $this->_edit_price_type_details_meta_box();
1025
+
1026
+		$redirect_URL = add_query_arg(['action' => 'price_types'], $this->_admin_base_url);
1027
+		$this->_set_publish_post_box_vars('id', $PRT_ID, false, $redirect_URL);
1028
+		// the details template wrapper
1029
+		$this->display_admin_page_with_sidebar();
1030
+	}
1031
+
1032
+
1033
+	/**
1034
+	 * _edit_price_type_details_meta_box
1035
+	 *
1036
+	 * @return string
1037
+	 */
1038
+	public function _edit_price_type_details_meta_box(): string
1039
+	{
1040
+		return EEH_Template::display_template(
1041
+			PRICING_TEMPLATE_PATH . 'pricing_type_details_main_meta_box.template.php',
1042
+			$this->_template_args,
1043
+			true
1044
+		);
1045
+	}
1046
+
1047
+
1048
+	/**
1049
+	 * @return array
1050
+	 */
1051
+	protected function set_price_type_column_values(): array
1052
+	{
1053
+		$base_type  = $this->request->getRequestParam(
1054
+			'base_type',
1055
+			EEM_Price_Type::base_type_base_price,
1056
+			DataType::INTEGER
1057
+		);
1058
+		$is_percent = $this->request->getRequestParam('PRT_is_percent', 0, DataType::INTEGER);
1059
+		$order      = $this->request->getRequestParam('PRT_order', 0, DataType::INTEGER);
1060
+		switch ($base_type) {
1061
+			case EEM_Price_Type::base_type_base_price:
1062
+				$is_percent = 0;
1063
+				$order      = 0;
1064
+				break;
1065
+
1066
+			case EEM_Price_Type::base_type_discount:
1067
+			case EEM_Price_Type::base_type_surcharge:
1068
+				break;
1069
+
1070
+			case EEM_Price_Type::base_type_tax:
1071
+				$is_percent = 1;
1072
+				break;
1073
+		}
1074
+
1075
+		return [
1076
+			'PBT_ID'         => $base_type,
1077
+			'PRT_name'       => $this->request->getRequestParam('PRT_name', ''),
1078
+			'PRT_is_percent' => $is_percent,
1079
+			'PRT_order'      => $order,
1080
+			'PRT_deleted'    => 0,
1081
+		];
1082
+	}
1083
+
1084
+
1085
+	/**
1086
+	 * @param bool $new_price_type - whether to insert or update
1087
+	 * @return void
1088
+	 * @throws EE_Error
1089
+	 * @throws ReflectionException
1090
+	 */
1091
+	protected function _insert_or_update_price_type(bool $new_price_type = false)
1092
+	{
1093
+		// why be so pessimistic ???  : (
1094
+		$success = 0;
1095
+
1096
+		$set_column_values = $this->set_price_type_column_values();
1097
+		// is this a new Price ?
1098
+		if ($new_price_type) {
1099
+			// run the insert
1100
+			if ($PRT_ID = EEM_Price_Type::instance()->insert($set_column_values)) {
1101
+				$success = 1;
1102
+			}
1103
+			$action_desc = 'created';
1104
+		} else {
1105
+			$PRT_ID = $this->request->getRequestParam('PRT_ID', 0, DataType::INTEGER);
1106
+			// run the update
1107
+			$where_cols_n_values = ['PRT_ID' => $PRT_ID];
1108
+			if (EEM_Price_Type::instance()->update($set_column_values, [$where_cols_n_values])) {
1109
+				$success = 1;
1110
+			}
1111
+			$action_desc = 'updated';
1112
+		}
1113
+
1114
+		$query_args = ['action' => 'edit_price_type', 'id' => $PRT_ID];
1115
+		$this->_redirect_after_action($success, 'Price Type', $action_desc, $query_args);
1116
+	}
1117
+
1118
+
1119
+	/**
1120
+	 * @param bool $trash - whether to move item to trash (TRUE) or restore it (FALSE)
1121
+	 * @return void
1122
+	 * @throws EE_Error
1123
+	 * @throws ReflectionException
1124
+	 */
1125
+	protected function _trash_or_restore_price_type(bool $trash = true)
1126
+	{
1127
+		$entity_model = EEM_Price_Type::instance();
1128
+		$action       = $trash ? EE_Admin_List_Table::ACTION_TRASH : EE_Admin_List_Table::ACTION_RESTORE;
1129
+		$success      = $this->trashRestoreDeleteEntities($entity_model, 'id', $action, 'PRT_deleted');
1130
+		if ($success) {
1131
+			$msg = $trash
1132
+				? esc_html(
1133
+					_n(
1134
+						'The Price Type has been trashed',
1135
+						'The Price Types have been trashed',
1136
+						$success,
1137
+						'event_espresso'
1138
+					)
1139
+				)
1140
+				: esc_html(
1141
+					_n(
1142
+						'The Price Type has been restored',
1143
+						'The Price Types have been restored',
1144
+						$success,
1145
+						'event_espresso'
1146
+					)
1147
+				);
1148
+			EE_Error::add_success($msg);
1149
+		}
1150
+		$this->_redirect_after_action('', '', '', ['action' => 'price_types'], true);
1151
+	}
1152
+
1153
+
1154
+	/**
1155
+	 * @return void
1156
+	 * @throws EE_Error
1157
+	 * @throws ReflectionException
1158
+	 */
1159
+	protected function _delete_price_type()
1160
+	{
1161
+		$entity_model = EEM_Price_Type::instance();
1162
+		$deleted      = $this->trashRestoreDeleteEntities($entity_model, 'id');
1163
+		$this->_redirect_after_action(
1164
+			$deleted,
1165
+			$entity_model->item_name($deleted),
1166
+			'deleted',
1167
+			['action' => 'price_types']
1168
+		);
1169
+	}
1170
+
1171
+
1172
+	/**
1173
+	 * @return string
1174
+	 */
1175
+	protected function _learn_more_about_pricing_link(): string
1176
+	{
1177
+		return '
1178 1178
             <a class="hidden" style="margin:0 20px; cursor:pointer; font-size:12px;" >
1179 1179
                 ' . esc_html__('learn more about how pricing works', 'event_espresso') . '
1180 1180
             </a>';
1181
-    }
1182
-
1183
-
1184
-    /**
1185
-     * @throws EE_Error
1186
-     */
1187
-    protected function _tax_settings()
1188
-    {
1189
-        $this->_set_add_edit_form_tags('update_tax_settings');
1190
-        $this->_set_publish_post_box_vars(null, false, false, null, false);
1191
-        $this->_template_args['admin_page_content'] = $this->tax_settings_form()->get_html();
1192
-        $this->display_admin_page_with_sidebar();
1193
-    }
1194
-
1195
-
1196
-    /**
1197
-     * @return EE_Form_Section_Proper
1198
-     * @throws EE_Error
1199
-     */
1200
-    protected function tax_settings_form(): EE_Form_Section_Proper
1201
-    {
1202
-        $tax_settings = EE_Config::instance()->tax_settings;
1203
-        return new EE_Form_Section_Proper(
1204
-            [
1205
-                'name'            => 'tax_settings_form',
1206
-                'html_id'         => 'tax_settings_form',
1207
-                'html_class'      => 'padding',
1208
-                'layout_strategy' => new EE_Div_Per_Section_Layout(),
1209
-                'subsections'     => apply_filters(
1210
-                    'FHEE__Pricing_Admin_Page__tax_settings_form__form_subsections',
1211
-                    [
1212
-                        'tax_settings' => new EE_Form_Section_Proper(
1213
-                            [
1214
-                                'name'            => 'tax_settings_tbl',
1215
-                                'html_id'         => 'tax_settings_tbl',
1216
-                                'html_class'      => 'form-table',
1217
-                                'layout_strategy' => new EE_Admin_Two_Column_Layout(),
1218
-                                'subsections'     => [
1219
-                                    'prices_displayed_including_taxes' => new EE_Yes_No_Input(
1220
-                                        [
1221
-                                            'html_label_text'         => esc_html__(
1222
-                                                'Show Prices With Taxes Included?',
1223
-                                                'event_espresso'
1224
-                                            ),
1225
-                                            'html_help_text'          => esc_html__(
1226
-                                                'Indicates whether or not to display prices with the taxes included',
1227
-                                                'event_espresso'
1228
-                                            ),
1229
-                                            'default'                 => $tax_settings->prices_displayed_including_taxes
1230
-                                                                         ?? true,
1231
-                                            'display_html_label_text' => false,
1232
-                                        ]
1233
-                                    ),
1234
-                                ],
1235
-                            ]
1236
-                        ),
1237
-                    ]
1238
-                ),
1239
-            ]
1240
-        );
1241
-    }
1242
-
1243
-
1244
-    /**
1245
-     * _update_tax_settings
1246
-     *
1247
-     * @return void
1248
-     * @throws EE_Error
1249
-     * @throws ReflectionException
1250
-     * @since 4.9.13
1251
-     */
1252
-    public function _update_tax_settings()
1253
-    {
1254
-        $tax_settings = EE_Config::instance()->tax_settings;
1255
-        if (! $tax_settings instanceof EE_Tax_Config) {
1256
-            $tax_settings = new EE_Tax_Config();
1257
-        }
1258
-        try {
1259
-            $tax_form = $this->tax_settings_form();
1260
-            // check for form submission
1261
-            if ($tax_form->was_submitted()) {
1262
-                // capture form data
1263
-                $tax_form->receive_form_submission();
1264
-                // validate form data
1265
-                if ($tax_form->is_valid()) {
1266
-                    // grab validated data from form
1267
-                    $valid_data = $tax_form->valid_data();
1268
-                    // set data on config
1269
-                    $tax_settings->prices_displayed_including_taxes =
1270
-                        $valid_data['tax_settings']['prices_displayed_including_taxes'];
1271
-                } else {
1272
-                    if ($tax_form->submission_error_message() !== '') {
1273
-                        EE_Error::add_error(
1274
-                            $tax_form->submission_error_message(),
1275
-                            __FILE__,
1276
-                            __FUNCTION__,
1277
-                            __LINE__
1278
-                        );
1279
-                    }
1280
-                }
1281
-            }
1282
-        } catch (EE_Error $e) {
1283
-            EE_Error::add_error($e->get_error(), __FILE__, __FUNCTION__, __LINE__);
1284
-        }
1285
-
1286
-        $what    = 'Tax Settings';
1287
-        $success = $this->_update_espresso_configuration(
1288
-            $what,
1289
-            $tax_settings,
1290
-            __FILE__,
1291
-            __FUNCTION__,
1292
-            __LINE__
1293
-        );
1294
-        $this->_redirect_after_action($success, $what, 'updated', ['action' => 'tax_settings']);
1295
-    }
1181
+	}
1182
+
1183
+
1184
+	/**
1185
+	 * @throws EE_Error
1186
+	 */
1187
+	protected function _tax_settings()
1188
+	{
1189
+		$this->_set_add_edit_form_tags('update_tax_settings');
1190
+		$this->_set_publish_post_box_vars(null, false, false, null, false);
1191
+		$this->_template_args['admin_page_content'] = $this->tax_settings_form()->get_html();
1192
+		$this->display_admin_page_with_sidebar();
1193
+	}
1194
+
1195
+
1196
+	/**
1197
+	 * @return EE_Form_Section_Proper
1198
+	 * @throws EE_Error
1199
+	 */
1200
+	protected function tax_settings_form(): EE_Form_Section_Proper
1201
+	{
1202
+		$tax_settings = EE_Config::instance()->tax_settings;
1203
+		return new EE_Form_Section_Proper(
1204
+			[
1205
+				'name'            => 'tax_settings_form',
1206
+				'html_id'         => 'tax_settings_form',
1207
+				'html_class'      => 'padding',
1208
+				'layout_strategy' => new EE_Div_Per_Section_Layout(),
1209
+				'subsections'     => apply_filters(
1210
+					'FHEE__Pricing_Admin_Page__tax_settings_form__form_subsections',
1211
+					[
1212
+						'tax_settings' => new EE_Form_Section_Proper(
1213
+							[
1214
+								'name'            => 'tax_settings_tbl',
1215
+								'html_id'         => 'tax_settings_tbl',
1216
+								'html_class'      => 'form-table',
1217
+								'layout_strategy' => new EE_Admin_Two_Column_Layout(),
1218
+								'subsections'     => [
1219
+									'prices_displayed_including_taxes' => new EE_Yes_No_Input(
1220
+										[
1221
+											'html_label_text'         => esc_html__(
1222
+												'Show Prices With Taxes Included?',
1223
+												'event_espresso'
1224
+											),
1225
+											'html_help_text'          => esc_html__(
1226
+												'Indicates whether or not to display prices with the taxes included',
1227
+												'event_espresso'
1228
+											),
1229
+											'default'                 => $tax_settings->prices_displayed_including_taxes
1230
+																		 ?? true,
1231
+											'display_html_label_text' => false,
1232
+										]
1233
+									),
1234
+								],
1235
+							]
1236
+						),
1237
+					]
1238
+				),
1239
+			]
1240
+		);
1241
+	}
1242
+
1243
+
1244
+	/**
1245
+	 * _update_tax_settings
1246
+	 *
1247
+	 * @return void
1248
+	 * @throws EE_Error
1249
+	 * @throws ReflectionException
1250
+	 * @since 4.9.13
1251
+	 */
1252
+	public function _update_tax_settings()
1253
+	{
1254
+		$tax_settings = EE_Config::instance()->tax_settings;
1255
+		if (! $tax_settings instanceof EE_Tax_Config) {
1256
+			$tax_settings = new EE_Tax_Config();
1257
+		}
1258
+		try {
1259
+			$tax_form = $this->tax_settings_form();
1260
+			// check for form submission
1261
+			if ($tax_form->was_submitted()) {
1262
+				// capture form data
1263
+				$tax_form->receive_form_submission();
1264
+				// validate form data
1265
+				if ($tax_form->is_valid()) {
1266
+					// grab validated data from form
1267
+					$valid_data = $tax_form->valid_data();
1268
+					// set data on config
1269
+					$tax_settings->prices_displayed_including_taxes =
1270
+						$valid_data['tax_settings']['prices_displayed_including_taxes'];
1271
+				} else {
1272
+					if ($tax_form->submission_error_message() !== '') {
1273
+						EE_Error::add_error(
1274
+							$tax_form->submission_error_message(),
1275
+							__FILE__,
1276
+							__FUNCTION__,
1277
+							__LINE__
1278
+						);
1279
+					}
1280
+				}
1281
+			}
1282
+		} catch (EE_Error $e) {
1283
+			EE_Error::add_error($e->get_error(), __FILE__, __FUNCTION__, __LINE__);
1284
+		}
1285
+
1286
+		$what    = 'Tax Settings';
1287
+		$success = $this->_update_espresso_configuration(
1288
+			$what,
1289
+			$tax_settings,
1290
+			__FILE__,
1291
+			__FUNCTION__,
1292
+			__LINE__
1293
+		);
1294
+		$this->_redirect_after_action($success, $what, 'updated', ['action' => 'tax_settings']);
1295
+	}
1296 1296
 }
Please login to merge, or discard this patch.
caffeinated/admin/new/pricing/espresso_events_Pricing_Hooks.class.php 2 patches
Indentation   +2175 added lines, -2175 removed lines patch added patch discarded remove patch
@@ -15,2193 +15,2193 @@
 block discarded – undo
15 15
  */
16 16
 class espresso_events_Pricing_Hooks extends EE_Admin_Hooks
17 17
 {
18
-    /**
19
-     * This property is just used to hold the status of whether an event is currently being
20
-     * created (true) or edited (false)
21
-     *
22
-     * @access protected
23
-     * @var bool
24
-     */
25
-    protected $_is_creating_event;
26
-
27
-    /**
28
-     * Used to contain the format strings for date and time that will be used for php date and
29
-     * time.
30
-     * Is set in the _set_hooks_properties() method.
31
-     *
32
-     * @var array
33
-     */
34
-    protected $_date_format_strings;
35
-
36
-    /**
37
-     * @var string $_date_time_format
38
-     */
39
-    protected $_date_time_format;
40
-
41
-
42
-    /**
43
-     * @throws InvalidArgumentException
44
-     * @throws InvalidInterfaceException
45
-     * @throws InvalidDataTypeException
46
-     */
47
-    protected function _set_hooks_properties()
48
-    {
49
-        $this->_name = 'pricing';
50
-        // capability check
51
-        if (
52
-            $this->_adminpage_obj->adminConfig()->useAdvancedEditor()
53
-            || ! EE_Registry::instance()->CAP->current_user_can(
54
-                'ee_read_default_prices',
55
-                'advanced_ticket_datetime_metabox'
56
-            )
57
-        ) {
58
-            $this->_metaboxes      = [];
59
-            $this->_scripts_styles = [];
60
-            return;
61
-        }
62
-        $this->_setup_metaboxes();
63
-        $this->_set_date_time_formats();
64
-        $this->_validate_format_strings();
65
-        $this->_set_scripts_styles();
66
-        add_filter(
67
-            'FHEE__Events_Admin_Page___insert_update_cpt_item__event_update_callbacks',
68
-            [$this, 'caf_updates']
69
-        );
70
-    }
71
-
72
-
73
-    /**
74
-     * @return void
75
-     */
76
-    protected function _setup_metaboxes()
77
-    {
78
-        // if we were going to add our own metaboxes we'd use the below.
79
-        $this->_metaboxes        = [
80
-            0 => [
81
-                'page_route' => ['edit', 'create_new'],
82
-                'func'       => [$this, 'pricing_metabox'],
83
-                'label'      => esc_html__('Event Tickets & Datetimes', 'event_espresso'),
84
-                'priority'   => 'high',
85
-                'context'    => 'normal',
86
-            ],
87
-        ];
88
-        $this->_remove_metaboxes = [
89
-            0 => [
90
-                'page_route' => ['edit', 'create_new'],
91
-                'id'         => 'espresso_event_editor_tickets',
92
-                'context'    => 'normal',
93
-            ],
94
-        ];
95
-    }
96
-
97
-
98
-    /**
99
-     * @return void
100
-     */
101
-    protected function _set_date_time_formats()
102
-    {
103
-        /**
104
-         * Format strings for date and time.  Defaults are existing behaviour from 4.1.
105
-         * Note, that if you return null as the value for 'date', and 'time' in the array, then
106
-         * EE will automatically use the set wp_options, 'date_format', and 'time_format'.
107
-         *
108
-         * @since 4.6.7
109
-         * @var array  Expected an array returned with 'date' and 'time' keys.
110
-         */
111
-        $this->_date_format_strings = apply_filters(
112
-            'FHEE__espresso_events_Pricing_Hooks___set_hooks_properties__date_format_strings',
113
-            [
114
-                'date' => 'Y-m-d',
115
-                'time' => 'h:i a',
116
-            ]
117
-        );
118
-        // validate
119
-        $this->_date_format_strings['date'] = $this->_date_format_strings['date'] ?? '';
120
-        $this->_date_format_strings['time'] = $this->_date_format_strings['time'] ?? '';
121
-
122
-        $this->_date_time_format = $this->_date_format_strings['date'] . ' ' . $this->_date_format_strings['time'];
123
-    }
124
-
125
-
126
-    /**
127
-     * @return void
128
-     */
129
-    protected function _validate_format_strings()
130
-    {
131
-        // validate format strings
132
-        $format_validation = EEH_DTT_Helper::validate_format_string(
133
-            $this->_date_time_format
134
-        );
135
-        if (is_array($format_validation)) {
136
-            $msg = '<p>';
137
-            $msg .= sprintf(
138
-                esc_html__(
139
-                    'The format "%s" was likely added via a filter and is invalid for the following reasons:',
140
-                    'event_espresso'
141
-                ),
142
-                $this->_date_time_format
143
-            );
144
-            $msg .= '</p><ul>';
145
-            foreach ($format_validation as $error) {
146
-                $msg .= '<li>' . $error . '</li>';
147
-            }
148
-            $msg .= '</ul><p>';
149
-            $msg .= sprintf(
150
-                esc_html__(
151
-                    '%sPlease note that your date and time formats have been reset to "Y-m-d" and "h:i a" respectively.%s',
152
-                    'event_espresso'
153
-                ),
154
-                '<span style="color:#D54E21;">',
155
-                '</span>'
156
-            );
157
-            $msg .= '</p>';
158
-            EE_Error::add_attention($msg, __FILE__, __FUNCTION__, __LINE__);
159
-            $this->_date_format_strings = [
160
-                'date' => 'Y-m-d',
161
-                'time' => 'h:i a',
162
-            ];
163
-        }
164
-    }
165
-
166
-
167
-    /**
168
-     * @return void
169
-     */
170
-    protected function _set_scripts_styles()
171
-    {
172
-        $this->_scripts_styles = [
173
-            'registers'   => [
174
-                'ee-tickets-datetimes-css' => [
175
-                    'url'  => PRICING_ASSETS_URL . 'event-tickets-datetimes.css',
176
-                    'type' => 'css',
177
-                ],
178
-                'ee-dtt-ticket-metabox'    => [
179
-                    'url'     => PRICING_ASSETS_URL . 'ee-datetime-ticket-metabox.js',
180
-                    'depends' => ['ee-datepicker', 'ee-dialog', 'underscore'],
181
-                ],
182
-            ],
183
-            'deregisters' => [
184
-                'event-editor-css'       => ['type' => 'css'],
185
-                'event-datetime-metabox' => ['type' => 'js'],
186
-            ],
187
-            'enqueues'    => [
188
-                'ee-tickets-datetimes-css' => ['edit', 'create_new'],
189
-                'ee-dtt-ticket-metabox'    => ['edit', 'create_new'],
190
-            ],
191
-            'localize'    => [
192
-                'ee-dtt-ticket-metabox' => [
193
-                    'DTT_TRASH_BLOCK'       => [
194
-                        'main_warning'            => esc_html__(
195
-                            'The Datetime you are attempting to trash is the only datetime selected for the following ticket(s):',
196
-                            'event_espresso'
197
-                        ),
198
-                        'after_warning'           => esc_html__(
199
-                            'In order to trash this datetime you must first make sure the above ticket(s) are assigned to other datetimes.',
200
-                            'event_espresso'
201
-                        ),
202
-                        'cancel_button'           => '
18
+	/**
19
+	 * This property is just used to hold the status of whether an event is currently being
20
+	 * created (true) or edited (false)
21
+	 *
22
+	 * @access protected
23
+	 * @var bool
24
+	 */
25
+	protected $_is_creating_event;
26
+
27
+	/**
28
+	 * Used to contain the format strings for date and time that will be used for php date and
29
+	 * time.
30
+	 * Is set in the _set_hooks_properties() method.
31
+	 *
32
+	 * @var array
33
+	 */
34
+	protected $_date_format_strings;
35
+
36
+	/**
37
+	 * @var string $_date_time_format
38
+	 */
39
+	protected $_date_time_format;
40
+
41
+
42
+	/**
43
+	 * @throws InvalidArgumentException
44
+	 * @throws InvalidInterfaceException
45
+	 * @throws InvalidDataTypeException
46
+	 */
47
+	protected function _set_hooks_properties()
48
+	{
49
+		$this->_name = 'pricing';
50
+		// capability check
51
+		if (
52
+			$this->_adminpage_obj->adminConfig()->useAdvancedEditor()
53
+			|| ! EE_Registry::instance()->CAP->current_user_can(
54
+				'ee_read_default_prices',
55
+				'advanced_ticket_datetime_metabox'
56
+			)
57
+		) {
58
+			$this->_metaboxes      = [];
59
+			$this->_scripts_styles = [];
60
+			return;
61
+		}
62
+		$this->_setup_metaboxes();
63
+		$this->_set_date_time_formats();
64
+		$this->_validate_format_strings();
65
+		$this->_set_scripts_styles();
66
+		add_filter(
67
+			'FHEE__Events_Admin_Page___insert_update_cpt_item__event_update_callbacks',
68
+			[$this, 'caf_updates']
69
+		);
70
+	}
71
+
72
+
73
+	/**
74
+	 * @return void
75
+	 */
76
+	protected function _setup_metaboxes()
77
+	{
78
+		// if we were going to add our own metaboxes we'd use the below.
79
+		$this->_metaboxes        = [
80
+			0 => [
81
+				'page_route' => ['edit', 'create_new'],
82
+				'func'       => [$this, 'pricing_metabox'],
83
+				'label'      => esc_html__('Event Tickets & Datetimes', 'event_espresso'),
84
+				'priority'   => 'high',
85
+				'context'    => 'normal',
86
+			],
87
+		];
88
+		$this->_remove_metaboxes = [
89
+			0 => [
90
+				'page_route' => ['edit', 'create_new'],
91
+				'id'         => 'espresso_event_editor_tickets',
92
+				'context'    => 'normal',
93
+			],
94
+		];
95
+	}
96
+
97
+
98
+	/**
99
+	 * @return void
100
+	 */
101
+	protected function _set_date_time_formats()
102
+	{
103
+		/**
104
+		 * Format strings for date and time.  Defaults are existing behaviour from 4.1.
105
+		 * Note, that if you return null as the value for 'date', and 'time' in the array, then
106
+		 * EE will automatically use the set wp_options, 'date_format', and 'time_format'.
107
+		 *
108
+		 * @since 4.6.7
109
+		 * @var array  Expected an array returned with 'date' and 'time' keys.
110
+		 */
111
+		$this->_date_format_strings = apply_filters(
112
+			'FHEE__espresso_events_Pricing_Hooks___set_hooks_properties__date_format_strings',
113
+			[
114
+				'date' => 'Y-m-d',
115
+				'time' => 'h:i a',
116
+			]
117
+		);
118
+		// validate
119
+		$this->_date_format_strings['date'] = $this->_date_format_strings['date'] ?? '';
120
+		$this->_date_format_strings['time'] = $this->_date_format_strings['time'] ?? '';
121
+
122
+		$this->_date_time_format = $this->_date_format_strings['date'] . ' ' . $this->_date_format_strings['time'];
123
+	}
124
+
125
+
126
+	/**
127
+	 * @return void
128
+	 */
129
+	protected function _validate_format_strings()
130
+	{
131
+		// validate format strings
132
+		$format_validation = EEH_DTT_Helper::validate_format_string(
133
+			$this->_date_time_format
134
+		);
135
+		if (is_array($format_validation)) {
136
+			$msg = '<p>';
137
+			$msg .= sprintf(
138
+				esc_html__(
139
+					'The format "%s" was likely added via a filter and is invalid for the following reasons:',
140
+					'event_espresso'
141
+				),
142
+				$this->_date_time_format
143
+			);
144
+			$msg .= '</p><ul>';
145
+			foreach ($format_validation as $error) {
146
+				$msg .= '<li>' . $error . '</li>';
147
+			}
148
+			$msg .= '</ul><p>';
149
+			$msg .= sprintf(
150
+				esc_html__(
151
+					'%sPlease note that your date and time formats have been reset to "Y-m-d" and "h:i a" respectively.%s',
152
+					'event_espresso'
153
+				),
154
+				'<span style="color:#D54E21;">',
155
+				'</span>'
156
+			);
157
+			$msg .= '</p>';
158
+			EE_Error::add_attention($msg, __FILE__, __FUNCTION__, __LINE__);
159
+			$this->_date_format_strings = [
160
+				'date' => 'Y-m-d',
161
+				'time' => 'h:i a',
162
+			];
163
+		}
164
+	}
165
+
166
+
167
+	/**
168
+	 * @return void
169
+	 */
170
+	protected function _set_scripts_styles()
171
+	{
172
+		$this->_scripts_styles = [
173
+			'registers'   => [
174
+				'ee-tickets-datetimes-css' => [
175
+					'url'  => PRICING_ASSETS_URL . 'event-tickets-datetimes.css',
176
+					'type' => 'css',
177
+				],
178
+				'ee-dtt-ticket-metabox'    => [
179
+					'url'     => PRICING_ASSETS_URL . 'ee-datetime-ticket-metabox.js',
180
+					'depends' => ['ee-datepicker', 'ee-dialog', 'underscore'],
181
+				],
182
+			],
183
+			'deregisters' => [
184
+				'event-editor-css'       => ['type' => 'css'],
185
+				'event-datetime-metabox' => ['type' => 'js'],
186
+			],
187
+			'enqueues'    => [
188
+				'ee-tickets-datetimes-css' => ['edit', 'create_new'],
189
+				'ee-dtt-ticket-metabox'    => ['edit', 'create_new'],
190
+			],
191
+			'localize'    => [
192
+				'ee-dtt-ticket-metabox' => [
193
+					'DTT_TRASH_BLOCK'       => [
194
+						'main_warning'            => esc_html__(
195
+							'The Datetime you are attempting to trash is the only datetime selected for the following ticket(s):',
196
+							'event_espresso'
197
+						),
198
+						'after_warning'           => esc_html__(
199
+							'In order to trash this datetime you must first make sure the above ticket(s) are assigned to other datetimes.',
200
+							'event_espresso'
201
+						),
202
+						'cancel_button'           => '
203 203
                             <button class="button--secondary ee-modal-cancel">
204 204
                                 ' . esc_html__('Cancel', 'event_espresso') . '
205 205
                             </button>',
206
-                        'close_button'            => '
206
+						'close_button'            => '
207 207
                             <button class="button--secondary ee-modal-cancel">
208 208
                                 ' . esc_html__('Close', 'event_espresso') . '
209 209
                             </button>',
210
-                        'single_warning_from_tkt' => esc_html__(
211
-                            '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.',
212
-                            'event_espresso'
213
-                        ),
214
-                        'single_warning_from_dtt' => esc_html__(
215
-                            '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.',
216
-                            'event_espresso'
217
-                        ),
218
-                        'dismiss_button'          => '
210
+						'single_warning_from_tkt' => esc_html__(
211
+							'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.',
212
+							'event_espresso'
213
+						),
214
+						'single_warning_from_dtt' => esc_html__(
215
+							'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.',
216
+							'event_espresso'
217
+						),
218
+						'dismiss_button'          => '
219 219
                             <button class="button--secondary ee-modal-cancel">
220 220
                                 ' . esc_html__('Dismiss', 'event_espresso') . '
221 221
                             </button>',
222
-                    ],
223
-                    'DTT_ERROR_MSG'         => [
224
-                        'no_ticket_name' => esc_html__('General Admission', 'event_espresso'),
225
-                        'dismiss_button' => '
222
+					],
223
+					'DTT_ERROR_MSG'         => [
224
+						'no_ticket_name' => esc_html__('General Admission', 'event_espresso'),
225
+						'dismiss_button' => '
226 226
                             <div class="save-cancel-button-container">
227 227
                                 <button class="button--secondary ee-modal-cancel">
228 228
                                     ' . esc_html__('Dismiss', 'event_espresso') . '
229 229
                                 </button>
230 230
                             </div>',
231
-                    ],
232
-                    'DTT_OVERSELL_WARNING'  => [
233
-                        'datetime_ticket' => esc_html__(
234
-                            '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.',
235
-                            'event_espresso'
236
-                        ),
237
-                        'ticket_datetime' => esc_html__(
238
-                            '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.',
239
-                            'event_espresso'
240
-                        ),
241
-                    ],
242
-                    'DTT_CONVERTED_FORMATS' => EEH_DTT_Helper::convert_php_to_js_and_moment_date_formats(
243
-                        $this->_date_format_strings['date'],
244
-                        $this->_date_format_strings['time']
245
-                    ),
246
-                    'DTT_START_OF_WEEK'     => ['dayValue' => (int) get_option('start_of_week')],
247
-                ],
248
-            ],
249
-        ];
250
-    }
251
-
252
-
253
-    /**
254
-     * @param array $update_callbacks
255
-     * @return array
256
-     */
257
-    public function caf_updates(array $update_callbacks): array
258
-    {
259
-        unset($update_callbacks['_default_tickets_update']);
260
-        $update_callbacks['datetime_and_tickets_caf_update'] = [$this, 'datetime_and_tickets_caf_update'];
261
-        return $update_callbacks;
262
-    }
263
-
264
-
265
-    /**
266
-     * Handles saving everything related to Tickets (datetimes, tickets, prices)
267
-     *
268
-     * @param EE_Event $event The Event object we're attaching data to
269
-     * @param array    $data  The request data from the form
270
-     * @throws ReflectionException
271
-     * @throws Exception
272
-     * @throws InvalidInterfaceException
273
-     * @throws InvalidDataTypeException
274
-     * @throws EE_Error
275
-     * @throws InvalidArgumentException
276
-     */
277
-    public function datetime_and_tickets_caf_update(EE_Event $event, array $data)
278
-    {
279
-        // first we need to start with datetimes cause they are the "root" items attached to events.
280
-        $saved_datetimes = $this->_update_datetimes($event, $data);
281
-        // next tackle the tickets (and prices?)
282
-        $this->_update_tickets($event, $saved_datetimes, $data);
283
-    }
284
-
285
-
286
-    /**
287
-     * update event_datetimes
288
-     *
289
-     * @param EE_Event $event Event being updated
290
-     * @param array    $data  the request data from the form
291
-     * @return EE_Datetime[]
292
-     * @throws Exception
293
-     * @throws ReflectionException
294
-     * @throws InvalidInterfaceException
295
-     * @throws InvalidDataTypeException
296
-     * @throws InvalidArgumentException
297
-     * @throws EE_Error
298
-     */
299
-    protected function _update_datetimes(EE_Event $event, array $data): array
300
-    {
301
-        $saved_datetime_ids  = [];
302
-        $saved_datetime_objs = [];
303
-        $timezone       = $data['timezone_string'] ?? null;
304
-        $datetime_model = EEM_Datetime::instance($timezone);
305
-
306
-        if (empty($data['edit_event_datetimes']) || ! is_array($data['edit_event_datetimes'])) {
307
-            throw new InvalidArgumentException(
308
-                esc_html__(
309
-                    'The "edit_event_datetimes" array is invalid therefore the event can not be updated.',
310
-                    'event_espresso'
311
-                )
312
-            );
313
-        }
314
-        foreach ($data['edit_event_datetimes'] as $row => $datetime_data) {
315
-            // trim all values to ensure any excess whitespace is removed.
316
-            $datetime_data = array_map(
317
-                function ($datetime_data) {
318
-                    return is_array($datetime_data) ? $datetime_data : trim($datetime_data);
319
-                },
320
-                $datetime_data
321
-            );
322
-
323
-            $datetime_data['DTT_EVT_end'] = isset($datetime_data['DTT_EVT_end'])
324
-                                            && ! empty($datetime_data['DTT_EVT_end'])
325
-                ? $datetime_data['DTT_EVT_end']
326
-                : $datetime_data['DTT_EVT_start'];
327
-            $datetime_values              = [
328
-                'DTT_ID'          => ! empty($datetime_data['DTT_ID'])
329
-                    ? $datetime_data['DTT_ID']
330
-                    : null,
331
-                'DTT_name'        => ! empty($datetime_data['DTT_name'])
332
-                    ? $datetime_data['DTT_name']
333
-                    : '',
334
-                'DTT_description' => ! empty($datetime_data['DTT_description'])
335
-                    ? $datetime_data['DTT_description']
336
-                    : '',
337
-                'DTT_EVT_start'   => $datetime_data['DTT_EVT_start'],
338
-                'DTT_EVT_end'     => $datetime_data['DTT_EVT_end'],
339
-                'DTT_reg_limit'   => empty($datetime_data['DTT_reg_limit'])
340
-                    ? EE_INF
341
-                    : $datetime_data['DTT_reg_limit'],
342
-                'DTT_order'       => ! isset($datetime_data['DTT_order'])
343
-                    ? $row
344
-                    : $datetime_data['DTT_order'],
345
-            ];
346
-
347
-            // if we have an id then let's get existing object first and then set the new values.
348
-            // Otherwise we instantiate a new object for save.
349
-            if (! empty($datetime_data['DTT_ID'])) {
350
-                $datetime = EE_Registry::instance()
351
-                                       ->load_model('Datetime', [$timezone])
352
-                                       ->get_one_by_ID($datetime_data['DTT_ID']);
353
-                // set date and time format according to what is set in this class.
354
-                $datetime->set_date_format($this->_date_format_strings['date']);
355
-                $datetime->set_time_format($this->_date_format_strings['time']);
356
-                foreach ($datetime_values as $field => $value) {
357
-                    $datetime->set($field, $value);
358
-                }
359
-
360
-                // make sure the $datetime_id here is saved just in case
361
-                // after the add_relation_to() the autosave replaces it.
362
-                // We need to do this so we dont' TRASH the parent DTT.
363
-                // (save the ID for both key and value to avoid duplications)
364
-                $saved_datetime_ids[ $datetime->ID() ] = $datetime->ID();
365
-            } else {
366
-                $datetime = EE_Datetime::new_instance(
367
-                    $datetime_values,
368
-                    $timezone,
369
-                    [$this->_date_format_strings['date'], $this->_date_format_strings['time']]
370
-                );
371
-                foreach ($datetime_values as $field => $value) {
372
-                    $datetime->set($field, $value);
373
-                }
374
-            }
375
-            $datetime->save();
376
-            do_action(
377
-                'AHEE__espresso_events_Pricing_Hooks___update_datetimes_after_save',
378
-                $datetime,
379
-                $row,
380
-                $datetime_data,
381
-                $data
382
-            );
383
-            $datetime = $event->_add_relation_to($datetime, 'Datetime');
384
-            // before going any further make sure our dates are setup correctly
385
-            // so that the end date is always equal or greater than the start date.
386
-            if ($datetime->get_raw('DTT_EVT_start') > $datetime->get_raw('DTT_EVT_end')) {
387
-                $datetime->set('DTT_EVT_end', $datetime->get('DTT_EVT_start'));
388
-                $datetime = EEH_DTT_Helper::date_time_add($datetime, 'DTT_EVT_end', 'days');
389
-                $datetime->save();
390
-            }
391
-            // now we have to make sure we add the new DTT_ID to the $saved_datetime_ids array
392
-            // because it is possible there was a new one created for the autosave.
393
-            // (save the ID for both key and value to avoid duplications)
394
-            $DTT_ID                        = $datetime->ID();
395
-            $saved_datetime_ids[ $DTT_ID ] = $DTT_ID;
396
-            $saved_datetime_objs[ $row ]   = $datetime;
397
-            // @todo if ANY of these updates fail then we want the appropriate global error message.
398
-        }
399
-        $event->save();
400
-        // now we need to REMOVE any datetimes that got deleted.
401
-        // Keep in mind that this process will only kick in for datetimes that don't have any DTT_sold on them.
402
-        // So its safe to permanently delete at this point.
403
-        $old_datetimes = explode(',', $data['datetime_IDs']);
404
-        $old_datetimes = $old_datetimes[0] === '' ? [] : $old_datetimes;
405
-        if (is_array($old_datetimes)) {
406
-            $datetimes_to_delete = array_diff($old_datetimes, $saved_datetime_ids);
407
-            foreach ($datetimes_to_delete as $id) {
408
-                $id = absint($id);
409
-                if (empty($id)) {
410
-                    continue;
411
-                }
412
-                $dtt_to_remove = $datetime_model->get_one_by_ID($id);
413
-                // remove tkt relationships.
414
-                $related_tickets = $dtt_to_remove->get_many_related('Ticket');
415
-                foreach ($related_tickets as $ticket) {
416
-                    $dtt_to_remove->_remove_relation_to($ticket, 'Ticket');
417
-                }
418
-                $event->_remove_relation_to($id, 'Datetime');
419
-                $dtt_to_remove->refresh_cache_of_related_objects();
420
-            }
421
-        }
422
-        return $saved_datetime_objs;
423
-    }
424
-
425
-
426
-    /**
427
-     * update tickets
428
-     *
429
-     * @param EE_Event      $event           Event object being updated
430
-     * @param EE_Datetime[] $saved_datetimes an array of datetime ids being updated
431
-     * @param array         $data            incoming request data
432
-     * @return EE_Ticket[]
433
-     * @throws Exception
434
-     * @throws ReflectionException
435
-     * @throws InvalidInterfaceException
436
-     * @throws InvalidDataTypeException
437
-     * @throws InvalidArgumentException
438
-     * @throws EE_Error
439
-     */
440
-    protected function _update_tickets(EE_Event $event, array $saved_datetimes, array $data): array
441
-    {
442
-        $new_ticket = null;
443
-        // stripslashes because WP filtered the $_POST ($data) array to add slashes
444
-        $data          = stripslashes_deep($data);
445
-        $timezone      = $data['timezone_string'] ?? null;
446
-        $ticket_model = EEM_Ticket::instance($timezone);
447
-
448
-        $saved_tickets = [];
449
-        $old_tickets   = isset($data['ticket_IDs']) ? explode(',', $data['ticket_IDs']) : [];
450
-        if (empty($data['edit_tickets']) || ! is_array($data['edit_tickets'])) {
451
-            throw new InvalidArgumentException(
452
-                esc_html__(
453
-                    'The "edit_tickets" array is invalid therefore the event can not be updated.',
454
-                    'event_espresso'
455
-                )
456
-            );
457
-        }
458
-        foreach ($data['edit_tickets'] as $row => $ticket_data) {
459
-            $update_prices = $create_new_TKT = false;
460
-            // figure out what datetimes were added to the ticket
461
-            // and what datetimes were removed from the ticket in the session.
462
-            $starting_ticket_datetime_rows = explode(',', $data['starting_ticket_datetime_rows'][ $row ]);
463
-            $ticket_datetime_rows          = explode(',', $data['ticket_datetime_rows'][ $row ]);
464
-            $datetimes_added               = array_diff($ticket_datetime_rows, $starting_ticket_datetime_rows);
465
-            $datetimes_removed             = array_diff($starting_ticket_datetime_rows, $ticket_datetime_rows);
466
-            // trim inputs to ensure any excess whitespace is removed.
467
-            $ticket_data = array_map(
468
-                function ($ticket_data) {
469
-                    return is_array($ticket_data) ? $ticket_data : trim($ticket_data);
470
-                },
471
-                $ticket_data
472
-            );
473
-            // note we are doing conversions to floats here instead of allowing EE_Money_Field to handle
474
-            // because we're doing calculations prior to using the models.
475
-            // note incoming ['TKT_price'] value is already in standard notation (via js).
476
-            $ticket_price = isset($ticket_data['TKT_price'])
477
-                ? round((float) $ticket_data['TKT_price'], 3)
478
-                : 0;
479
-            // note incoming base price needs converted from localized value.
480
-            $base_price = isset($ticket_data['TKT_base_price'])
481
-                ? EEH_Money::convert_to_float_from_localized_money($ticket_data['TKT_base_price'])
482
-                : 0;
483
-            // if ticket price == 0 and $base_price != 0 then ticket price == base_price
484
-            $ticket_price  = $ticket_price === 0 && $base_price !== 0
485
-                ? $base_price
486
-                : $ticket_price;
487
-            $base_price_id = $ticket_data['TKT_base_price_ID'] ?? 0;
488
-            $price_rows    = is_array($data['edit_prices']) && isset($data['edit_prices'][ $row ])
489
-                ? $data['edit_prices'][ $row ]
490
-                : [];
491
-            $now           = null;
492
-            if (empty($ticket_data['TKT_start_date'])) {
493
-                // lets' use now in the set timezone.
494
-                $now                           = new DateTime('now', new DateTimeZone($event->get_timezone()));
495
-                $ticket_data['TKT_start_date'] = $now->format($this->_date_time_format);
496
-            }
497
-            if (empty($ticket_data['TKT_end_date'])) {
498
-                /**
499
-                 * set the TKT_end_date to the first datetime attached to the ticket.
500
-                 */
501
-                $first_datetime              = $saved_datetimes[ reset($ticket_datetime_rows) ];
502
-                $ticket_data['TKT_end_date'] = $first_datetime->start_date_and_time($this->_date_time_format);
503
-            }
504
-            $TKT_values = [
505
-                'TKT_ID'          => ! empty($ticket_data['TKT_ID']) ? $ticket_data['TKT_ID'] : null,
506
-                'TTM_ID'          => ! empty($ticket_data['TTM_ID']) ? $ticket_data['TTM_ID'] : 0,
507
-                'TKT_name'        => ! empty($ticket_data['TKT_name']) ? $ticket_data['TKT_name'] : '',
508
-                'TKT_description' => ! empty($ticket_data['TKT_description'])
509
-                                     && $ticket_data['TKT_description'] !== esc_html__(
510
-                    'You can modify this description',
511
-                    'event_espresso'
512
-                )
513
-                    ? $ticket_data['TKT_description']
514
-                    : '',
515
-                'TKT_start_date'  => $ticket_data['TKT_start_date'],
516
-                'TKT_end_date'    => $ticket_data['TKT_end_date'],
517
-                'TKT_qty'         => ! isset($ticket_data['TKT_qty']) || $ticket_data['TKT_qty'] === ''
518
-                    ? EE_INF
519
-                    : $ticket_data['TKT_qty'],
520
-                'TKT_uses'        => ! isset($ticket_data['TKT_uses']) || $ticket_data['TKT_uses'] === ''
521
-                    ? EE_INF
522
-                    : $ticket_data['TKT_uses'],
523
-                'TKT_min'         => empty($ticket_data['TKT_min']) ? 0 : $ticket_data['TKT_min'],
524
-                'TKT_max'         => empty($ticket_data['TKT_max']) ? EE_INF : $ticket_data['TKT_max'],
525
-                'TKT_row'         => $row,
526
-                'TKT_order'       => $ticket_data['TKT_order'] ?? 0,
527
-                'TKT_taxable'     => ! empty($ticket_data['TKT_taxable']) ? 1 : 0,
528
-                'TKT_required'    => ! empty($ticket_data['TKT_required']) ? 1 : 0,
529
-                'TKT_price'       => $ticket_price,
530
-            ];
531
-            // if this is a default TKT, then we need to set the TKT_ID to 0 and update accordingly,
532
-            // which means in turn that the prices will become new prices as well.
533
-            if (isset($ticket_data['TKT_is_default']) && $ticket_data['TKT_is_default']) {
534
-                $TKT_values['TKT_ID']         = 0;
535
-                $TKT_values['TKT_is_default'] = 0;
536
-                $update_prices                = true;
537
-            }
538
-            // if we have a TKT_ID then we need to get that existing TKT_obj and update it
539
-            // we actually do our saves ahead of doing any add_relations to
540
-            // because its entirely possible that this ticket wasn't removed or added to any datetime in the session
541
-            // but DID have it's items modified.
542
-            // keep in mind that if the TKT has been sold (and we have changed pricing information),
543
-            // then we won't be updating the ticket but instead a new ticket will be created and the old one archived.
544
-            if (absint($TKT_values['TKT_ID'])) {
545
-                $ticket = EE_Registry::instance()
546
-                                     ->load_model('Ticket', [$timezone])
547
-                                     ->get_one_by_ID($TKT_values['TKT_ID']);
548
-                if ($ticket instanceof EE_Ticket) {
549
-                    $ticket = $this->_update_ticket_datetimes(
550
-                        $ticket,
551
-                        $saved_datetimes,
552
-                        $datetimes_added,
553
-                        $datetimes_removed
554
-                    );
555
-                    // are there any registrations using this ticket ?
556
-                    $tickets_sold = $ticket->count_related(
557
-                        'Registration',
558
-                        [
559
-                            [
560
-                                'STS_ID' => ['NOT IN', [EEM_Registration::status_id_incomplete]],
561
-                            ],
562
-                        ]
563
-                    );
564
-                    // set ticket formats
565
-                    $ticket->set_date_format($this->_date_format_strings['date']);
566
-                    $ticket->set_time_format($this->_date_format_strings['time']);
567
-                    // let's just check the total price for the existing ticket
568
-                    // and determine if it matches the new total price.
569
-                    // if they are different then we create a new ticket (if tickets sold)
570
-                    // if they aren't different then we go ahead and modify existing ticket.
571
-                    $create_new_TKT = $tickets_sold > 0 && $ticket_price !== $ticket->price() && ! $ticket->deleted();
572
-                    // set new values
573
-                    foreach ($TKT_values as $field => $value) {
574
-                        if ($field === 'TKT_qty') {
575
-                            $ticket->set_qty($value);
576
-                        } else {
577
-                            $ticket->set($field, $value);
578
-                        }
579
-                    }
580
-                    // if $create_new_TKT is false then we can safely update the existing ticket.
581
-                    // Otherwise we have to create a new ticket.
582
-                    if ($create_new_TKT) {
583
-                        $new_ticket = $this->_duplicate_ticket(
584
-                            $ticket,
585
-                            $price_rows,
586
-                            $ticket_price,
587
-                            $base_price,
588
-                            $base_price_id
589
-                        );
590
-                    }
591
-                }
592
-            } else {
593
-                // no TKT_id so a new TKT
594
-                $ticket = EE_Ticket::new_instance(
595
-                    $TKT_values,
596
-                    $timezone,
597
-                    [$this->_date_format_strings['date'], $this->_date_format_strings['time']]
598
-                );
599
-                if ($ticket instanceof EE_Ticket) {
600
-                    // make sure ticket has an ID of setting relations won't work
601
-                    $ticket->save();
602
-                    $ticket        = $this->_update_ticket_datetimes(
603
-                        $ticket,
604
-                        $saved_datetimes,
605
-                        $datetimes_added,
606
-                        $datetimes_removed
607
-                    );
608
-                    $update_prices = true;
609
-                }
610
-            }
611
-            // make sure any current values have been saved.
612
-            // $ticket->save();
613
-            // before going any further make sure our dates are setup correctly
614
-            // so that the end date is always equal or greater than the start date.
615
-            if ($ticket->get_raw('TKT_start_date') > $ticket->get_raw('TKT_end_date')) {
616
-                $ticket->set('TKT_end_date', $ticket->get('TKT_start_date'));
617
-                $ticket = EEH_DTT_Helper::date_time_add($ticket, 'TKT_end_date', 'days');
618
-            }
619
-            // let's make sure the base price is handled
620
-            $ticket = ! $create_new_TKT
621
-                ? $this->_add_prices_to_ticket(
622
-                    [],
623
-                    $ticket,
624
-                    $update_prices,
625
-                    $base_price,
626
-                    $base_price_id
627
-                )
628
-                : $ticket;
629
-            // add/update price_modifiers
630
-            $ticket = ! $create_new_TKT
631
-                ? $this->_add_prices_to_ticket($price_rows, $ticket, $update_prices)
632
-                : $ticket;
633
-            // need to make sue that the TKT_price is accurate after saving the prices.
634
-            $ticket->ensure_TKT_Price_correct();
635
-            // handle CREATING a default ticket from the incoming ticket but ONLY if this isn't an autosave.
636
-            if (! defined('DOING_AUTOSAVE') && ! empty($ticket_data['TKT_is_default_selector'])) {
637
-                $new_default = clone $ticket;
638
-                $new_default->set('TKT_ID', 0);
639
-                $new_default->set('TKT_is_default', 1);
640
-                $new_default->set('TKT_row', 1);
641
-                $new_default->set('TKT_price', $ticket_price);
642
-                // remove any datetime relations cause we DON'T want datetime relations attached
643
-                // (note this is just removing the cached relations in the object)
644
-                $new_default->_remove_relations('Datetime');
645
-                // @todo we need to add the current attached prices as new prices to the new default ticket.
646
-                $new_default = $this->_add_prices_to_ticket(
647
-                    $price_rows,
648
-                    $new_default,
649
-                    true
650
-                );
651
-                // don't forget the base price!
652
-                $new_default = $this->_add_prices_to_ticket(
653
-                    [],
654
-                    $new_default,
655
-                    true,
656
-                    $base_price,
657
-                    $base_price_id
658
-                );
659
-                $new_default->save();
660
-                do_action(
661
-                    'AHEE__espresso_events_Pricing_Hooks___update_tkts_new_default_ticket',
662
-                    $new_default,
663
-                    $row,
664
-                    $ticket,
665
-                    $data
666
-                );
667
-            }
668
-            // DO ALL datetime relationships for both current tickets and any archived tickets
669
-            // for the given datetime that are related to the current ticket.
670
-            // TODO... not sure exactly how we're going to do this considering we don't know
671
-            // what current ticket the archived tickets are related to
672
-            // (and TKT_parent is used for autosaves so that's not a field we can reliably use).
673
-            // let's assign any tickets that have been setup to the saved_tickets tracker
674
-            // save existing TKT
675
-            $ticket->save();
676
-            if ($create_new_TKT && $new_ticket instanceof EE_Ticket) {
677
-                // save new TKT
678
-                $new_ticket->save();
679
-                // add new ticket to array
680
-                $saved_tickets[ $new_ticket->ID() ] = $new_ticket;
681
-                do_action(
682
-                    'AHEE__espresso_events_Pricing_Hooks___update_tkts_new_ticket',
683
-                    $new_ticket,
684
-                    $row,
685
-                    $ticket_data,
686
-                    $data
687
-                );
688
-            } else {
689
-                // add ticket to saved tickets
690
-                $saved_tickets[ $ticket->ID() ] = $ticket;
691
-                do_action(
692
-                    'AHEE__espresso_events_Pricing_Hooks___update_tkts_update_ticket',
693
-                    $ticket,
694
-                    $row,
695
-                    $ticket_data,
696
-                    $data
697
-                );
698
-            }
699
-        }
700
-        // now we need to handle tickets actually "deleted permanently".
701
-        // There are cases where we'd want this to happen
702
-        // (i.e. autosaves are happening and then in between autosaves the user trashes a ticket).
703
-        // Or a draft event was saved and in the process of editing a ticket is trashed.
704
-        // No sense in keeping all the related data in the db!
705
-        $old_tickets     = isset($old_tickets[0]) && $old_tickets[0] === '' ? [] : $old_tickets;
706
-        $tickets_removed = array_diff($old_tickets, array_keys($saved_tickets));
707
-        foreach ($tickets_removed as $id) {
708
-            $id = absint($id);
709
-            // get the ticket for this id
710
-            $ticket_to_remove = $ticket_model->get_one_by_ID($id);
711
-            // if this tkt is a default tkt we leave it alone cause it won't be attached to the datetime
712
-            if ($ticket_to_remove->get('TKT_is_default')) {
713
-                continue;
714
-            }
715
-            // if this ticket has any registrations attached so then we just ARCHIVE
716
-            // because we don't actually permanently delete these tickets.
717
-            if ($ticket_to_remove->count_related('Registration') > 0) {
718
-                $ticket_to_remove->delete();
719
-                continue;
720
-            }
721
-            // need to get all the related datetimes on this ticket and remove from every single one of them
722
-            // (remember this process can ONLY kick off if there are NO tickets_sold)
723
-            $datetimes = $ticket_to_remove->get_many_related('Datetime');
724
-            foreach ($datetimes as $datetime) {
725
-                $ticket_to_remove->_remove_relation_to($datetime, 'Datetime');
726
-            }
727
-            // need to do the same for prices (except these prices can also be deleted because again,
728
-            // tickets can only be trashed if they don't have any TKTs sold (otherwise they are just archived))
729
-            $ticket_to_remove->delete_related('Price');
730
-            do_action('AHEE__espresso_events_Pricing_Hooks___update_tkts_delete_ticket', $ticket_to_remove);
731
-            // finally let's delete this ticket
732
-            // (which should not be blocked at this point b/c we've removed all our relationships)
733
-            $ticket_to_remove->delete_or_restore();
734
-        }
735
-        return $saved_tickets;
736
-    }
737
-
738
-
739
-    /**
740
-     * @access  protected
741
-     * @param EE_Ticket     $ticket
742
-     * @param EE_Datetime[] $saved_datetimes
743
-     * @param int[]         $added_datetimes
744
-     * @param int[]         $removed_datetimes
745
-     * @return EE_Ticket
746
-     * @throws EE_Error
747
-     * @throws ReflectionException
748
-     */
749
-    protected function _update_ticket_datetimes(
750
-        EE_Ticket $ticket,
751
-        array $saved_datetimes = [],
752
-        array $added_datetimes = [],
753
-        array $removed_datetimes = []
754
-    ): EE_Ticket {
755
-        // to start we have to add the ticket to all the datetimes its supposed to be with,
756
-        // and removing the ticket from datetimes it got removed from.
757
-        // first let's add datetimes
758
-        if (! empty($added_datetimes) && is_array($added_datetimes)) {
759
-            foreach ($added_datetimes as $row_id) {
760
-                if (isset($saved_datetimes[ $row_id ]) && $saved_datetimes[ $row_id ] instanceof EE_Datetime) {
761
-                    $ticket->_add_relation_to($saved_datetimes[ $row_id ], 'Datetime');
762
-                    // Is this an existing ticket (has an ID) and does it have any sold?
763
-                    // If so, then we need to add that to the DTT sold because this DTT is getting added.
764
-                    if ($ticket->ID() && $ticket->sold() > 0) {
765
-                        $saved_datetimes[ $row_id ]->increaseSold($ticket->sold(), false);
766
-                    }
767
-                }
768
-            }
769
-        }
770
-        // then remove datetimes
771
-        if (! empty($removed_datetimes) && is_array($removed_datetimes)) {
772
-            foreach ($removed_datetimes as $row_id) {
773
-                // its entirely possible that a datetime got deleted (instead of just removed from relationship.
774
-                // So make sure we skip over this if the datetime isn't in the $saved_datetimes array)
775
-                if (isset($saved_datetimes[ $row_id ]) && $saved_datetimes[ $row_id ] instanceof EE_Datetime) {
776
-                    $ticket->_remove_relation_to($saved_datetimes[ $row_id ], 'Datetime');
777
-                }
778
-            }
779
-        }
780
-        // cap ticket qty by datetime reg limits
781
-        $ticket->set_qty(min($ticket->qty(), $ticket->qty('reg_limit')));
782
-        return $ticket;
783
-    }
784
-
785
-
786
-    /**
787
-     * @access  protected
788
-     * @param EE_Ticket $ticket
789
-     * @param array     $price_rows
790
-     * @param int|float $ticket_price
791
-     * @param int|float $base_price
792
-     * @param int       $base_price_id
793
-     * @return EE_Ticket
794
-     * @throws ReflectionException
795
-     * @throws InvalidArgumentException
796
-     * @throws InvalidInterfaceException
797
-     * @throws InvalidDataTypeException
798
-     * @throws EE_Error
799
-     */
800
-    protected function _duplicate_ticket(
801
-        EE_Ticket $ticket,
802
-        array $price_rows = [],
803
-        $ticket_price = 0,
804
-        $base_price = 0,
805
-        int $base_price_id = 0
806
-    ): EE_Ticket {
807
-        // create new ticket that's a copy of the existing
808
-        // except a new id of course (and not archived)
809
-        // AND has the new TKT_price associated with it.
810
-        $new_ticket = clone $ticket;
811
-        $new_ticket->set('TKT_ID', 0);
812
-        $new_ticket->set_deleted(0);
813
-        $new_ticket->set_price($ticket_price);
814
-        $new_ticket->set_sold(0);
815
-        // let's get a new ID for this ticket
816
-        $new_ticket->save();
817
-        // we also need to make sure this new ticket gets the same datetime attachments as the archived ticket
818
-        $datetimes_on_existing = $ticket->datetimes();
819
-        $new_ticket            = $this->_update_ticket_datetimes(
820
-            $new_ticket,
821
-            $datetimes_on_existing,
822
-            array_keys($datetimes_on_existing)
823
-        );
824
-        // $ticket will get archived later b/c we are NOT adding it to the saved_tickets array.
825
-        // if existing $ticket has sold amount, then we need to adjust the qty for the new TKT to = the remaining
826
-        // available.
827
-        if ($ticket->sold() > 0) {
828
-            $new_qty = $ticket->qty() - $ticket->sold();
829
-            $new_ticket->set_qty($new_qty);
830
-        }
831
-        // now we update the prices just for this ticket
832
-        $new_ticket = $this->_add_prices_to_ticket($price_rows, $new_ticket, true);
833
-        // and we update the base price
834
-        return $this->_add_prices_to_ticket(
835
-            [],
836
-            $new_ticket,
837
-            true,
838
-            $base_price,
839
-            $base_price_id
840
-        );
841
-    }
842
-
843
-
844
-    /**
845
-     * This attaches a list of given prices to a ticket.
846
-     * Note we dont' have to worry about ever removing relationships (or archiving prices) because if there is a change
847
-     * in price information on a ticket, a new ticket is created anyways so the archived ticket will retain the old
848
-     * price info and prices are automatically "archived" via the ticket.
849
-     *
850
-     * @access  private
851
-     * @param array     $prices        Array of prices from the form.
852
-     * @param EE_Ticket $ticket        EE_Ticket object that prices are being attached to.
853
-     * @param bool      $new_prices    Whether attach existing incoming prices or create new ones.
854
-     * @param int|bool  $base_price    if FALSE then NOT doing a base price add.
855
-     * @param int|bool  $base_price_id if present then this is the base_price_id being updated.
856
-     * @return EE_Ticket
857
-     * @throws ReflectionException
858
-     * @throws InvalidArgumentException
859
-     * @throws InvalidInterfaceException
860
-     * @throws InvalidDataTypeException
861
-     * @throws EE_Error
862
-     */
863
-    protected function _add_prices_to_ticket(
864
-        array $prices,
865
-        EE_Ticket $ticket,
866
-        bool $new_prices = false,
867
-        $base_price = false,
868
-        $base_price_id = false
869
-    ): EE_Ticket {
870
-        $price_model = EEM_Price::instance();
871
-        // let's just get any current prices that may exist on the given ticket
872
-        // so we can remove any prices that got trashed in this session.
873
-        $current_prices_on_ticket = $base_price !== false
874
-            ? $ticket->base_price(true)
875
-            : $ticket->price_modifiers();
876
-        $updated_prices           = [];
877
-        // if $base_price ! FALSE then updating a base price.
878
-        if ($base_price !== false) {
879
-            $prices[1] = [
880
-                'PRC_ID'     => $new_prices || $base_price_id === 1 ? null : $base_price_id,
881
-                'PRT_ID'     => 1,
882
-                'PRC_amount' => $base_price,
883
-                'PRC_name'   => $ticket->get('TKT_name'),
884
-                'PRC_desc'   => $ticket->get('TKT_description'),
885
-            ];
886
-        }
887
-        // possibly need to save ticket
888
-        if (! $ticket->ID()) {
889
-            $ticket->save();
890
-        }
891
-        foreach ($prices as $row => $prc) {
892
-            $prt_id = ! empty($prc['PRT_ID']) ? $prc['PRT_ID'] : null;
893
-            if (empty($prt_id)) {
894
-                continue;
895
-            } //prices MUST have a price type id.
896
-            $PRC_values = [
897
-                'PRC_ID'         => ! empty($prc['PRC_ID']) ? $prc['PRC_ID'] : null,
898
-                'PRT_ID'         => $prt_id,
899
-                'PRC_amount'     => ! empty($prc['PRC_amount']) ? $prc['PRC_amount'] : 0,
900
-                'PRC_name'       => ! empty($prc['PRC_name']) ? $prc['PRC_name'] : '',
901
-                'PRC_desc'       => ! empty($prc['PRC_desc']) ? $prc['PRC_desc'] : '',
902
-                'PRC_is_default' => false,
903
-                // make sure we set PRC_is_default to false for all ticket saves from event_editor
904
-                'PRC_order'      => $row,
905
-            ];
906
-            if ($new_prices || empty($PRC_values['PRC_ID'])) {
907
-                $PRC_values['PRC_ID'] = 0;
908
-                $price                = EE_Registry::instance()->load_class(
909
-                    'Price',
910
-                    [$PRC_values],
911
-                    false,
912
-                    false
913
-                );
914
-            } else {
915
-                $price = $price_model->get_one_by_ID($prc['PRC_ID']);
916
-                // update this price with new values
917
-                foreach ($PRC_values as $field => $value) {
918
-                    $price->set($field, $value);
919
-                }
920
-            }
921
-            $price->save();
922
-            $updated_prices[ $price->ID() ] = $price;
923
-            $ticket->_add_relation_to($price, 'Price');
924
-        }
925
-        // now let's remove any prices that got removed from the ticket
926
-        if (! empty($current_prices_on_ticket)) {
927
-            $current          = array_keys($current_prices_on_ticket);
928
-            $updated          = array_keys($updated_prices);
929
-            $prices_to_remove = array_diff($current, $updated);
930
-            if (! empty($prices_to_remove)) {
931
-                foreach ($prices_to_remove as $prc_id) {
932
-                    $p = $current_prices_on_ticket[ $prc_id ];
933
-                    $ticket->_remove_relation_to($p, 'Price');
934
-                    // delete permanently the price
935
-                    $p->delete_or_restore();
936
-                }
937
-            }
938
-        }
939
-        return $ticket;
940
-    }
941
-
942
-
943
-    /**
944
-     * @throws ReflectionException
945
-     * @throws InvalidArgumentException
946
-     * @throws InvalidInterfaceException
947
-     * @throws InvalidDataTypeException
948
-     * @throws DomainException
949
-     * @throws EE_Error
950
-     */
951
-    public function pricing_metabox()
952
-    {
953
-        $event                 = $this->_adminpage_obj->get_cpt_model_obj();
954
-        $timezone = $event instanceof EE_Event ? $event->timezone_string() : null;
955
-        $price_model = EEM_Price::instance($timezone);
956
-        $ticket_model = EEM_Ticket::instance($timezone);
957
-        $datetime_model = EEM_Datetime::instance($timezone);
958
-
959
-        // set is_creating_event property.
960
-        $EVT_ID                   = $event->ID();
961
-        $this->_is_creating_event = empty($this->_req_data['post']);
962
-        $existing_datetime_ids = $existing_ticket_ids = $datetime_tickets = $ticket_datetimes = [];
963
-
964
-        // default main template args
965
-        $main_template_args = [
966
-            'event_datetime_help_link' => EEH_Template::get_help_tab_link(
967
-                'event_editor_event_datetimes_help_tab',
968
-                $this->_adminpage_obj->page_slug,
969
-                $this->_adminpage_obj->get_req_action()
970
-            ),
971
-
972
-            // todo need to add a filter to the template for the help text
973
-            // in the Events_Admin_Page core file so we can add further help
974
-            'add_new_dtt_help_link'    => EEH_Template::get_help_tab_link(
975
-                'add_new_dtt_info',
976
-                $this->_adminpage_obj->page_slug,
977
-                $this->_adminpage_obj->get_req_action()
978
-            ),
979
-            // todo need to add this help info id to the Events_Admin_Page core file so we can access it here.
980
-            'datetime_rows'            => '',
981
-            'show_tickets_container'   => '',
982
-            'ticket_rows'              => '',
983
-            'ee_collapsible_status'    => ' ee-collapsible-open'
984
-            // $this->_adminpage_obj->get_cpt_model_obj()->ID() > 0 ? ' ee-collapsible-closed' : ' ee-collapsible-open'
985
-        ];
986
-        do_action('AHEE_log', __FILE__, __FUNCTION__, '');
987
-
988
-        /**
989
-         * 1. Start with retrieving Datetimes
990
-         * 2. For each datetime get related tickets
991
-         * 3. For each ticket get related prices
992
-         */
993
-        $datetimes                            = $datetime_model->get_all_event_dates($EVT_ID);
994
-        $main_template_args['total_dtt_rows'] = count($datetimes);
995
-
996
-        /**
997
-         * @see https://events.codebasehq.com/projects/event-espresso/tickets/9486
998
-         * for why we are counting $datetime_row and then setting that on the Datetime object
999
-         */
1000
-        $datetime_row = 1;
1001
-        foreach ($datetimes as $datetime) {
1002
-            $DTT_ID = $datetime->get('DTT_ID');
1003
-            $datetime->set('DTT_order', $datetime_row);
1004
-            $existing_datetime_ids[] = $DTT_ID;
1005
-            // tickets attached
1006
-            $related_tickets = $datetime->ID() > 0
1007
-                ? $datetime->get_many_related(
1008
-                    'Ticket',
1009
-                    [
1010
-                        [
1011
-                            'OR' => ['TKT_deleted' => 1, 'TKT_deleted*' => 0],
1012
-                        ],
1013
-                        'default_where_conditions' => 'none',
1014
-                        'order_by'                 => ['TKT_order' => 'ASC'],
1015
-                    ]
1016
-                )
1017
-                : [];
1018
-            // if there are no related tickets this is likely a new event OR auto-draft
1019
-            // event so we need to generate the default tickets because datetimes
1020
-            // ALWAYS have at least one related ticket!!.  EXCEPT, we dont' do this if there is already more than one
1021
-            // datetime on the event.
1022
-            if (empty($related_tickets) && count($datetimes) < 2) {
1023
-                $related_tickets = $ticket_model->get_all_default_tickets();
1024
-                // this should be ordered by TKT_ID, so let's grab the first default ticket
1025
-                // (which will be the main default) and ensure it has any default prices added to it (but do NOT save).
1026
-                $default_prices      = $price_model->get_all_default_prices();
1027
-                $main_default_ticket = reset($related_tickets);
1028
-                if ($main_default_ticket instanceof EE_Ticket) {
1029
-                    foreach ($default_prices as $default_price) {
1030
-                        if ($default_price instanceof EE_Price && $default_price->is_base_price()) {
1031
-                            continue;
1032
-                        }
1033
-                        $main_default_ticket->cache('Price', $default_price);
1034
-                    }
1035
-                }
1036
-            }
1037
-            // we can't actually setup rows in this loop yet cause we don't know all
1038
-            // the unique tickets for this event yet (tickets are linked through all datetimes).
1039
-            // So we're going to temporarily cache some of that information.
1040
-            // loop through and setup the ticket rows and make sure the order is set.
1041
-            foreach ($related_tickets as $ticket) {
1042
-                $TKT_ID     = $ticket->get('TKT_ID');
1043
-                $ticket_row = $ticket->get('TKT_row');
1044
-                // we only want unique tickets in our final display!!
1045
-                if (! in_array($TKT_ID, $existing_ticket_ids, true)) {
1046
-                    $existing_ticket_ids[] = $TKT_ID;
1047
-                    $all_tickets[]         = $ticket;
1048
-                }
1049
-                // temporary cache of this ticket info for this datetime for later processing of datetime rows.
1050
-                $datetime_tickets[ $DTT_ID ][] = $ticket_row;
1051
-                // temporary cache of this datetime info for this ticket for later processing of ticket rows.
1052
-                if (
1053
-                    ! isset($ticket_datetimes[ $TKT_ID ])
1054
-                    || ! in_array($datetime_row, $ticket_datetimes[ $TKT_ID ], true)
1055
-                ) {
1056
-                    $ticket_datetimes[ $TKT_ID ][] = $datetime_row;
1057
-                }
1058
-            }
1059
-            $datetime_row++;
1060
-        }
1061
-        $main_template_args['total_ticket_rows']     = count($existing_ticket_ids);
1062
-        $main_template_args['existing_ticket_ids']   = implode(',', $existing_ticket_ids);
1063
-        $main_template_args['existing_datetime_ids'] = implode(',', $existing_datetime_ids);
1064
-        // sort $all_tickets by order
1065
-        usort(
1066
-            $all_tickets,
1067
-            function (EE_Ticket $a, EE_Ticket $b) {
1068
-                $a_order = (int) $a->get('TKT_order');
1069
-                $b_order = (int) $b->get('TKT_order');
1070
-                if ($a_order === $b_order) {
1071
-                    return 0;
1072
-                }
1073
-                return ($a_order < $b_order) ? -1 : 1;
1074
-            }
1075
-        );
1076
-        // k NOW we have all the data we need for setting up the datetime rows
1077
-        // and ticket rows so we start our datetime loop again.
1078
-        $datetime_row = 1;
1079
-        foreach ($datetimes as $datetime) {
1080
-            $main_template_args['datetime_rows'] .= $this->_get_datetime_row(
1081
-                $datetime_row,
1082
-                $datetime,
1083
-                $datetime_tickets,
1084
-                $all_tickets,
1085
-                false,
1086
-                $datetimes
1087
-            );
1088
-            $datetime_row++;
1089
-        }
1090
-        // then loop through all tickets for the ticket rows.
1091
-        $ticket_row = 1;
1092
-        foreach ($all_tickets as $ticket) {
1093
-            $main_template_args['ticket_rows'] .= $this->_get_ticket_row(
1094
-                $ticket_row,
1095
-                $ticket,
1096
-                $ticket_datetimes,
1097
-                $datetimes,
1098
-                false,
1099
-                $all_tickets
1100
-            );
1101
-            $ticket_row++;
1102
-        }
1103
-        $main_template_args['ticket_js_structure'] = $this->_get_ticket_js_structure($datetimes, $all_tickets);
1104
-
1105
-        $status_change_notice = LoaderFactory::getLoader()->getShared(
1106
-            'EventEspresso\core\domain\services\admin\notices\status_change\StatusChangeNotice'
1107
-        );
1108
-
1109
-        $main_template_args['status_change_notice'] = $status_change_notice->display(
1110
-            '__event-editor',
1111
-            'espresso-events'
1112
-        );
1113
-
1114
-        EEH_Template::display_template(
1115
-            PRICING_TEMPLATE_PATH . 'event_tickets_metabox_main.template.php',
1116
-            $main_template_args
1117
-        );
1118
-    }
1119
-
1120
-
1121
-    /**
1122
-     * @param int|string  $datetime_row
1123
-     * @param EE_Datetime $datetime
1124
-     * @param array       $datetime_tickets
1125
-     * @param array       $all_tickets
1126
-     * @param bool        $default
1127
-     * @param array       $all_datetimes
1128
-     * @return string
1129
-     * @throws DomainException
1130
-     * @throws EE_Error
1131
-     * @throws ReflectionException
1132
-     */
1133
-    protected function _get_datetime_row(
1134
-        $datetime_row,
1135
-        EE_Datetime $datetime,
1136
-        array $datetime_tickets = [],
1137
-        array $all_tickets = [],
1138
-        bool $default = false,
1139
-        array $all_datetimes = []
1140
-    ): string {
1141
-        return EEH_Template::display_template(
1142
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_row_wrapper.template.php',
1143
-            [
1144
-                'dtt_edit_row'             => $this->_get_dtt_edit_row(
1145
-                    $datetime_row,
1146
-                    $datetime,
1147
-                    $default,
1148
-                    $all_datetimes
1149
-                ),
1150
-                'dtt_attached_tickets_row' => $this->_get_dtt_attached_tickets_row(
1151
-                    $datetime_row,
1152
-                    $datetime,
1153
-                    $datetime_tickets,
1154
-                    $all_tickets,
1155
-                    $default
1156
-                ),
1157
-                'dtt_row'                  => $default ? 'DTTNUM' : $datetime_row,
1158
-            ],
1159
-            true
1160
-        );
1161
-    }
1162
-
1163
-
1164
-    /**
1165
-     * This method is used to generate a datetime fields  edit row.
1166
-     * The same row is used to generate a row with valid DTT objects
1167
-     * and the default row that is used as the skeleton by the js.
1168
-     *
1169
-     * @param int|string       $datetime_row  The row number for the row being generated.
1170
-     * @param EE_Datetime|null $datetime
1171
-     * @param bool             $default       Whether a default row is being generated or not.
1172
-     * @param EE_Datetime[]    $all_datetimes This is the array of all datetimes used in the editor.
1173
-     * @return string
1174
-     * @throws EE_Error
1175
-     * @throws ReflectionException
1176
-     */
1177
-    protected function _get_dtt_edit_row(
1178
-        $datetime_row,
1179
-        ?EE_Datetime $datetime,
1180
-        bool $default,
1181
-        array $all_datetimes
1182
-    ): string {
1183
-        // if the incoming $datetime object is NOT an instance of EE_Datetime then force default to true.
1184
-        $default                     = ! $datetime instanceof EE_Datetime ? true : $default;
1185
-        $template_args               = [
1186
-            'dtt_row'              => $default ? 'DTTNUM' : $datetime_row,
1187
-            'event_datetimes_name' => $default ? 'DTTNAMEATTR' : 'edit_event_datetimes',
1188
-            'edit_dtt_expanded'    => '',
1189
-            'DTT_ID'               => $default ? '' : $datetime->ID(),
1190
-            'DTT_name'             => $default ? '' : $datetime->get_f('DTT_name'),
1191
-            'DTT_description'      => $default ? '' : $datetime->get_f('DTT_description'),
1192
-            'DTT_EVT_start'        => $default ? '' : $datetime->start_date($this->_date_time_format),
1193
-            'DTT_EVT_end'          => $default ? '' : $datetime->end_date($this->_date_time_format),
1194
-            'DTT_reg_limit'        => $default
1195
-                ? ''
1196
-                : $datetime->get_pretty(
1197
-                    'DTT_reg_limit',
1198
-                    'input'
1199
-                ),
1200
-            'DTT_order'            => $default ? 'DTTNUM' : $datetime_row,
1201
-            'dtt_sold'             => $default ? '0' : $datetime->get('DTT_sold'),
1202
-            'dtt_reserved'         => $default ? '0' : $datetime->reserved(),
1203
-            'clone_icon'           => ! empty($datetime) && $datetime->get('DTT_sold') > 0
1204
-                ? ''
1205
-                : 'clone-icon ee-icon ee-icon-clone clickable',
1206
-            'trash_icon'           => ! empty($datetime) && $datetime->get('DTT_sold') > 0
1207
-                ? 'dashicons dashicons-lock'
1208
-                : 'trash-icon dashicons dashicons-post-trash clickable',
1209
-            'reg_list_url'         => $default || ! $datetime->event() instanceof EE_Event
1210
-                ? ''
1211
-                : EE_Admin_Page::add_query_args_and_nonce(
1212
-                    [
1213
-                        'event_id'    => $datetime->event()->ID(),
1214
-                        'datetime_id' => $datetime->ID(),
1215
-                        'use_filters' => true,
1216
-                    ],
1217
-                    REG_ADMIN_URL
1218
-                ),
1219
-        ];
1220
-        $template_args['show_trash'] = count($all_datetimes) === 1
1221
-                                       && $template_args['trash_icon'] !== 'dashicons dashicons-lock'
1222
-            ? 'display:none'
1223
-            : '';
1224
-        // allow filtering of template args at this point.
1225
-        $template_args = apply_filters(
1226
-            'FHEE__espresso_events_Pricing_Hooks___get_dtt_edit_row__template_args',
1227
-            $template_args,
1228
-            $datetime_row,
1229
-            $datetime,
1230
-            $default,
1231
-            $all_datetimes,
1232
-            $this->_is_creating_event
1233
-        );
1234
-        return EEH_Template::display_template(
1235
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_edit_row.template.php',
1236
-            $template_args,
1237
-            true
1238
-        );
1239
-    }
1240
-
1241
-
1242
-    /**
1243
-     * @param int|string       $datetime_row
1244
-     * @param EE_Datetime|null $datetime
1245
-     * @param array            $datetime_tickets
1246
-     * @param array            $all_tickets
1247
-     * @param bool             $default
1248
-     * @return string
1249
-     * @throws DomainException
1250
-     * @throws EE_Error
1251
-     * @throws ReflectionException
1252
-     */
1253
-    protected function _get_dtt_attached_tickets_row(
1254
-        $datetime_row,
1255
-        ?EE_Datetime $datetime,
1256
-        array $datetime_tickets = [],
1257
-        array $all_tickets = [],
1258
-        bool $default = false
1259
-    ): string {
1260
-        $template_args = [
1261
-            'dtt_row'                           => $default ? 'DTTNUM' : $datetime_row,
1262
-            'event_datetimes_name'              => $default ? 'DTTNAMEATTR' : 'edit_event_datetimes',
1263
-            'DTT_description'                   => $default ? '' : $datetime->get_f('DTT_description'),
1264
-            'datetime_tickets_list'             => $default ? '<li class="hidden"></li>' : '',
1265
-            'show_tickets_row'                  => 'display:none;',
1266
-            'add_new_datetime_ticket_help_link' => EEH_Template::get_help_tab_link(
1267
-                'add_new_ticket_via_datetime',
1268
-                $this->_adminpage_obj->page_slug,
1269
-                $this->_adminpage_obj->get_req_action()
1270
-            ),
1271
-            // todo need to add this help info id to the Events_Admin_Page core file so we can access it here.
1272
-            'DTT_ID'                            => $default ? '' : $datetime->ID(),
1273
-        ];
1274
-        // need to setup the list items (but only if this isn't a default skeleton setup)
1275
-        if (! $default) {
1276
-            $ticket_row = 1;
1277
-            foreach ($all_tickets as $ticket) {
1278
-                $template_args['datetime_tickets_list'] .= $this->_get_datetime_tickets_list_item(
1279
-                    $datetime_row,
1280
-                    $ticket_row,
1281
-                    $datetime,
1282
-                    $ticket,
1283
-                    $datetime_tickets
1284
-                );
1285
-                $ticket_row++;
1286
-            }
1287
-        }
1288
-        // filter template args at this point
1289
-        $template_args = apply_filters(
1290
-            'FHEE__espresso_events_Pricing_Hooks___get_dtt_attached_ticket_row__template_args',
1291
-            $template_args,
1292
-            $datetime_row,
1293
-            $datetime,
1294
-            $datetime_tickets,
1295
-            $all_tickets,
1296
-            $default,
1297
-            $this->_is_creating_event
1298
-        );
1299
-        return EEH_Template::display_template(
1300
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_attached_tickets_row.template.php',
1301
-            $template_args,
1302
-            true
1303
-        );
1304
-    }
1305
-
1306
-
1307
-    /**
1308
-     * @param int|string       $datetime_row
1309
-     * @param int|string       $ticket_row
1310
-     * @param EE_Datetime|null $datetime
1311
-     * @param EE_Ticket|null   $ticket
1312
-     * @param array            $datetime_tickets
1313
-     * @param bool             $default
1314
-     * @return string
1315
-     * @throws EE_Error
1316
-     * @throws ReflectionException
1317
-     */
1318
-    protected function _get_datetime_tickets_list_item(
1319
-        $datetime_row,
1320
-        $ticket_row,
1321
-        ?EE_Datetime $datetime,
1322
-        ?EE_Ticket $ticket,
1323
-        array $datetime_tickets = [],
1324
-        bool $default = false
1325
-    ): string {
1326
-        $datetime_tickets = $datetime instanceof EE_Datetime && isset($datetime_tickets[ $datetime->ID() ])
1327
-            ? $datetime_tickets[ $datetime->ID() ]
1328
-            : [];
1329
-        $display_row      = $ticket instanceof EE_Ticket ? $ticket->get('TKT_row') : 0;
1330
-        $no_ticket        = $default && empty($ticket);
1331
-        $template_args    = [
1332
-            'dtt_row'                 => $default
1333
-                ? 'DTTNUM'
1334
-                : $datetime_row,
1335
-            'tkt_row'                 => $no_ticket
1336
-                ? 'TICKETNUM'
1337
-                : $ticket_row,
1338
-            'datetime_ticket_checked' => in_array($display_row, $datetime_tickets, true)
1339
-                ? ' checked'
1340
-                : '',
1341
-            'ticket_selected'         => in_array($display_row, $datetime_tickets, true)
1342
-                ? ' ticket-selected'
1343
-                : '',
1344
-            'TKT_name'                => $no_ticket
1345
-                ? 'TKTNAME'
1346
-                : $ticket->get('TKT_name'),
1347
-            'tkt_status_class'        => $no_ticket || $this->_is_creating_event
1348
-                ? ' tkt-status-' . EE_Ticket::onsale
1349
-                : ' tkt-status-' . $ticket->ticket_status(),
1350
-        ];
1351
-        // filter template args
1352
-        $template_args = apply_filters(
1353
-            'FHEE__espresso_events_Pricing_Hooks___get_datetime_tickets_list_item__template_args',
1354
-            $template_args,
1355
-            $datetime_row,
1356
-            $ticket_row,
1357
-            $datetime,
1358
-            $ticket,
1359
-            $datetime_tickets,
1360
-            $default,
1361
-            $this->_is_creating_event
1362
-        );
1363
-        return EEH_Template::display_template(
1364
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_dtt_tickets_list.template.php',
1365
-            $template_args,
1366
-            true
1367
-        );
1368
-    }
1369
-
1370
-
1371
-    /**
1372
-     * This generates the ticket row for tickets.
1373
-     * This same method is used to generate both the actual rows and the js skeleton row
1374
-     * (when default === true)
1375
-     *
1376
-     * @param int|string     $ticket_row       Represents the row number being generated.
1377
-     * @param EE_Ticket|null $ticket
1378
-     * @param EE_Datetime[]  $ticket_datetimes Either an array of all datetimes on all tickets indexed by each ticket
1379
-     *                                         or empty for default
1380
-     * @param EE_Datetime[]  $all_datetimes    All Datetimes on the event or empty for default.
1381
-     * @param bool           $default          Whether default row being generated or not.
1382
-     * @param EE_Ticket[]    $all_tickets      This is an array of all tickets attached to the event
1383
-     *                                         (or empty in the case of defaults)
1384
-     * @return string
1385
-     * @throws InvalidArgumentException
1386
-     * @throws InvalidInterfaceException
1387
-     * @throws InvalidDataTypeException
1388
-     * @throws DomainException
1389
-     * @throws EE_Error
1390
-     * @throws ReflectionException
1391
-     */
1392
-    protected function _get_ticket_row(
1393
-        $ticket_row,
1394
-        ?EE_Ticket $ticket,
1395
-        array $ticket_datetimes,
1396
-        array $all_datetimes,
1397
-        bool $default = false,
1398
-        array $all_tickets = []
1399
-    ): string {
1400
-        // if $ticket is not an instance of EE_Ticket then force default to true.
1401
-        $default = ! $ticket instanceof EE_Ticket ? true : $default;
1402
-        $prices  = ! empty($ticket) && ! $default
1403
-            ? $ticket->get_many_related(
1404
-                'Price',
1405
-                ['default_where_conditions' => 'none', 'order_by' => ['PRC_order' => 'ASC']]
1406
-            )
1407
-            : [];
1408
-        // if there is only one price (which would be the base price)
1409
-        // or NO prices and this ticket is a default ticket,
1410
-        // let's just make sure there are no cached default prices on the object.
1411
-        // This is done by not including any query_params.
1412
-        if ($ticket instanceof EE_Ticket && $ticket->is_default() && (count($prices) === 1 || empty($prices))) {
1413
-            $prices = $ticket->prices();
1414
-        }
1415
-        // check if we're dealing with a default ticket in which case
1416
-        // we don't want any starting_ticket_datetime_row values set
1417
-        // (otherwise there won't be any new relationships created for tickets based off of the default ticket).
1418
-        // This will future proof in case there is ever any behaviour change between what the primary_key defaults to.
1419
-        $default_datetime = $default || ($ticket instanceof EE_Ticket && $ticket->is_default());
1420
-        $ticket_datetimes = $ticket instanceof EE_Ticket && isset($ticket_datetimes[ $ticket->ID() ])
1421
-            ? $ticket_datetimes[ $ticket->ID() ]
1422
-            : [];
1423
-        $ticket_subtotal  = $default ? 0 : $ticket->get_ticket_subtotal();
1424
-        $base_price       = $default ? null : $ticket->base_price();
1425
-        $count_price_mods = EEM_Price::instance()->get_all_default_prices(true);
1426
-        // breaking out complicated condition for ticket_status
1427
-        if ($default) {
1428
-            $ticket_status_class = ' tkt-status-' . EE_Ticket::onsale;
1429
-        } else {
1430
-            $ticket_status_class = $ticket->is_default()
1431
-                ? ' tkt-status-' . EE_Ticket::onsale
1432
-                : ' tkt-status-' . $ticket->ticket_status();
1433
-        }
1434
-        // breaking out complicated condition for TKT_taxable
1435
-        if ($default) {
1436
-            $TKT_taxable = '';
1437
-        } else {
1438
-            $TKT_taxable = $ticket->taxable()
1439
-                ? 'checked'
1440
-                : '';
1441
-        }
1442
-        if ($default) {
1443
-            $TKT_status = EEH_Template::pretty_status(EE_Ticket::onsale, false, 'sentence');
1444
-        } elseif ($ticket->is_default()) {
1445
-            $TKT_status = EEH_Template::pretty_status(EE_Ticket::onsale, false, 'sentence');
1446
-        } else {
1447
-            $TKT_status = $ticket->ticket_status(true);
1448
-        }
1449
-        if ($default) {
1450
-            $TKT_min = '';
1451
-        } else {
1452
-            $TKT_min = $ticket->min();
1453
-            if ($TKT_min === -1 || $TKT_min === 0) {
1454
-                $TKT_min = '';
1455
-            }
1456
-        }
1457
-        $template_args                 = [
1458
-            'tkt_row'                       => $default ? 'TICKETNUM' : $ticket_row,
1459
-            'TKT_order'                     => $default ? 'TICKETNUM' : $ticket_row,
1460
-            // on initial page load this will always be the correct order.
1461
-            'tkt_status_class'              => $ticket_status_class,
1462
-            'display_edit_tkt_row'          => 'display:none;',
1463
-            'edit_tkt_expanded'             => '',
1464
-            'edit_tickets_name'             => $default ? 'TICKETNAMEATTR' : 'edit_tickets',
1465
-            'TKT_name'                      => $default ? '' : $ticket->get_f('TKT_name'),
1466
-            'TKT_start_date'                => $default
1467
-                ? ''
1468
-                : $ticket->get_date('TKT_start_date', $this->_date_time_format),
1469
-            'TKT_end_date'                  => $default
1470
-                ? ''
1471
-                : $ticket->get_date('TKT_end_date', $this->_date_time_format),
1472
-            'TKT_status'                    => $TKT_status,
1473
-            'TKT_price'                     => $default
1474
-                ? ''
1475
-                : EEH_Template::format_currency(
1476
-                    $ticket->get_ticket_total_with_taxes(),
1477
-                    false,
1478
-                    false
1479
-                ),
1480
-            'TKT_price_code'                => EE_Registry::instance()->CFG->currency->code,
1481
-            'TKT_price_amount'              => $default ? 0 : $ticket_subtotal,
1482
-            'TKT_qty'                       => $default
1483
-                ? ''
1484
-                : $ticket->get_pretty('TKT_qty', 'symbol'),
1485
-            'TKT_qty_for_input'             => $default
1486
-                ? ''
1487
-                : $ticket->get_pretty('TKT_qty', 'input'),
1488
-            'TKT_uses'                      => $default
1489
-                ? ''
1490
-                : $ticket->get_pretty('TKT_uses', 'input'),
1491
-            'TKT_min'                       => $TKT_min,
1492
-            'TKT_max'                       => $default
1493
-                ? ''
1494
-                : $ticket->get_pretty('TKT_max', 'input'),
1495
-            'TKT_sold'                      => $default ? 0 : $ticket->tickets_sold(),
1496
-            'TKT_reserved'                  => $default ? 0 : $ticket->reserved(),
1497
-            'TKT_registrations'             => $default
1498
-                ? 0
1499
-                : $ticket->count_registrations(
1500
-                    [
1501
-                        [
1502
-                            'STS_ID' => [
1503
-                                '!=',
1504
-                                EEM_Registration::status_id_incomplete,
1505
-                            ],
1506
-                        ],
1507
-                    ]
1508
-                ),
1509
-            'TKT_ID'                        => $default ? 0 : $ticket->ID(),
1510
-            'TKT_description'               => $default ? '' : $ticket->get_raw('TKT_description'),
1511
-            'TKT_is_default'                => $default ? 0 : $ticket->is_default(),
1512
-            'TKT_required'                  => $default ? 0 : $ticket->required(),
1513
-            'TKT_is_default_selector'       => '',
1514
-            'ticket_price_rows'             => '',
1515
-            'TKT_base_price'                => $default || ! $base_price instanceof EE_Price
1516
-                ? ''
1517
-                : $base_price->get_pretty('PRC_amount', 'localized_float'),
1518
-            'TKT_base_price_ID'             => $default || ! $base_price instanceof EE_Price ? 0 : $base_price->ID(),
1519
-            'show_price_modifier'           => count($prices) > 1 || ($default && $count_price_mods > 0)
1520
-                ? ''
1521
-                : 'display:none;',
1522
-            'show_price_mod_button'         => count($prices) > 1
1523
-                                               || ($default && $count_price_mods > 0)
1524
-                                               || (! $default && $ticket->deleted())
1525
-                ? 'display:none;'
1526
-                : '',
1527
-            'total_price_rows'              => count($prices) > 1 ? count($prices) : 1,
1528
-            'ticket_datetimes_list'         => $default ? '<li class="hidden"></li>' : '',
1529
-            'starting_ticket_datetime_rows' => $default || $default_datetime ? '' : implode(',', $ticket_datetimes),
1530
-            'ticket_datetime_rows'          => $default ? '' : implode(',', $ticket_datetimes),
1531
-            'existing_ticket_price_ids'     => $default ? '' : implode(',', array_keys($prices)),
1532
-            'ticket_template_id'            => $default ? 0 : $ticket->get('TTM_ID'),
1533
-            'TKT_taxable'                   => $TKT_taxable,
1534
-            'display_subtotal'              => $ticket instanceof EE_Ticket && $ticket->taxable()
1535
-                ? ''
1536
-                : 'display:none;',
1537
-            'price_currency_symbol'         => EE_Registry::instance()->CFG->currency->sign,
1538
-            'TKT_subtotal_amount_display'   => EEH_Template::format_currency(
1539
-                $ticket_subtotal,
1540
-                false,
1541
-                false
1542
-            ),
1543
-            'TKT_subtotal_amount'           => $ticket_subtotal,
1544
-            'tax_rows'                      => $this->_get_tax_rows($ticket_row, $ticket),
1545
-            'disabled'                      => $ticket instanceof EE_Ticket && $ticket->deleted(),
1546
-            'ticket_archive_class'          => $ticket instanceof EE_Ticket && $ticket->deleted()
1547
-                ? ' ticket-archived'
1548
-                : '',
1549
-            'trash_icon'                    => $ticket instanceof EE_Ticket
1550
-                                               && $ticket->deleted()
1551
-                                               && ! $ticket->is_permanently_deleteable()
1552
-                ? 'dashicons dashicons-lock '
1553
-                : 'trash-icon dashicons dashicons-post-trash clickable',
1554
-            'clone_icon'                    => $ticket instanceof EE_Ticket && $ticket->deleted()
1555
-                ? ''
1556
-                : 'clone-icon ee-icon ee-icon-clone clickable',
1557
-        ];
1558
-        $template_args['trash_hidden'] = count($all_tickets) === 1
1559
-                                         && $template_args['trash_icon'] !== 'dashicons dashicons-lock'
1560
-            ? 'display:none'
1561
-            : '';
1562
-        // handle rows that should NOT be empty
1563
-        if (empty($template_args['TKT_start_date'])) {
1564
-            // if empty then the start date will be now.
1565
-            $template_args['TKT_start_date']   = date(
1566
-                $this->_date_time_format,
1567
-                current_time('timestamp')
1568
-            );
1569
-            $template_args['tkt_status_class'] = ' tkt-status-' . EE_Ticket::onsale;
1570
-        }
1571
-        if (empty($template_args['TKT_end_date'])) {
1572
-            // get the earliest datetime (if present);
1573
-            $earliest_datetime = $this->_adminpage_obj->get_cpt_model_obj()->ID() > 0
1574
-                ? $this->_adminpage_obj->get_cpt_model_obj()->get_first_related(
1575
-                    'Datetime',
1576
-                    ['order_by' => ['DTT_EVT_start' => 'ASC']]
1577
-                )
1578
-                : null;
1579
-            if (! empty($earliest_datetime)) {
1580
-                $template_args['TKT_end_date'] = $earliest_datetime->get_datetime(
1581
-                    'DTT_EVT_start',
1582
-                    $this->_date_time_format
1583
-                );
1584
-            } else {
1585
-                // default so let's just use what's been set for the default date-time which is 30 days from now.
1586
-                $template_args['TKT_end_date'] = date(
1587
-                    $this->_date_time_format,
1588
-                    mktime(
1589
-                        24,
1590
-                        0,
1591
-                        0,
1592
-                        date('m'),
1593
-                        date('d') + 29,
1594
-                        date('Y')
1595
-                    )
1596
-                );
1597
-            }
1598
-            $template_args['tkt_status_class'] = ' tkt-status-' . EE_Ticket::onsale;
1599
-        }
1600
-        // generate ticket_datetime items
1601
-        if (! $default) {
1602
-            $datetime_row = 1;
1603
-            foreach ($all_datetimes as $datetime) {
1604
-                $template_args['ticket_datetimes_list'] .= $this->_get_ticket_datetime_list_item(
1605
-                    $datetime_row,
1606
-                    $ticket_row,
1607
-                    $datetime,
1608
-                    $ticket,
1609
-                    $ticket_datetimes,
1610
-                    $default
1611
-                );
1612
-                $datetime_row++;
1613
-            }
1614
-        }
1615
-        $price_row = 1;
1616
-        foreach ($prices as $price) {
1617
-            if (! $price instanceof EE_Price) {
1618
-                continue;
1619
-            }
1620
-            if ($price->is_base_price()) {
1621
-                $price_row++;
1622
-                continue;
1623
-            }
1624
-
1625
-            $show_trash  = ! ((count($prices) > 1 && $price_row === 1) || count($prices) === 1);
1626
-            $show_create = ! (count($prices) > 1 && count($prices) !== $price_row);
1627
-
1628
-            $template_args['ticket_price_rows'] .= $this->_get_ticket_price_row(
1629
-                $ticket_row,
1630
-                $price_row,
1631
-                $price,
1632
-                $default,
1633
-                $ticket,
1634
-                $show_trash,
1635
-                $show_create
1636
-            );
1637
-            $price_row++;
1638
-        }
1639
-        // filter $template_args
1640
-        $template_args = apply_filters(
1641
-            'FHEE__espresso_events_Pricing_Hooks___get_ticket_row__template_args',
1642
-            $template_args,
1643
-            $ticket_row,
1644
-            $ticket,
1645
-            $ticket_datetimes,
1646
-            $all_datetimes,
1647
-            $default,
1648
-            $all_tickets,
1649
-            $this->_is_creating_event
1650
-        );
1651
-        return EEH_Template::display_template(
1652
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_row.template.php',
1653
-            $template_args,
1654
-            true
1655
-        );
1656
-    }
1657
-
1658
-
1659
-    /**
1660
-     * @param int|string     $ticket_row
1661
-     * @param EE_Ticket|null $ticket
1662
-     * @return string
1663
-     * @throws DomainException
1664
-     * @throws EE_Error
1665
-     * @throws ReflectionException
1666
-     */
1667
-    protected function _get_tax_rows($ticket_row, ?EE_Ticket $ticket): string
1668
-    {
1669
-        $tax_rows = '';
1670
-        /** @var EE_Price[] $taxes */
1671
-        $taxes = empty($ticket) ? EE_Taxes::get_taxes_for_admin() : $ticket->get_ticket_taxes_for_admin();
1672
-        foreach ($taxes as $tax) {
1673
-            $tax_added     = $this->_get_tax_added($tax, $ticket);
1674
-            $template_args = [
1675
-                'display_tax'       => ! empty($ticket) && $ticket->get('TKT_taxable')
1676
-                    ? ''
1677
-                    : 'display:none;',
1678
-                'tax_id'            => $tax->ID(),
1679
-                'tkt_row'           => $ticket_row,
1680
-                'tax_label'         => $tax->get('PRC_name'),
1681
-                'tax_added'         => $tax_added,
1682
-                'tax_added_display' => EEH_Template::format_currency($tax_added, false, false),
1683
-                'tax_amount'        => $tax->get('PRC_amount'),
1684
-            ];
1685
-            $template_args = apply_filters(
1686
-                'FHEE__espresso_events_Pricing_Hooks___get_tax_rows__template_args',
1687
-                $template_args,
1688
-                $ticket_row,
1689
-                $ticket,
1690
-                $this->_is_creating_event
1691
-            );
1692
-            $tax_rows      .= EEH_Template::display_template(
1693
-                PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_tax_row.template.php',
1694
-                $template_args,
1695
-                true
1696
-            );
1697
-        }
1698
-        return $tax_rows;
1699
-    }
1700
-
1701
-
1702
-    /**
1703
-     * @param EE_Price       $tax
1704
-     * @param EE_Ticket|null $ticket
1705
-     * @return float|int
1706
-     * @throws EE_Error
1707
-     * @throws ReflectionException
1708
-     */
1709
-    protected function _get_tax_added(EE_Price $tax, ?EE_Ticket $ticket)
1710
-    {
1711
-        $subtotal = empty($ticket) ? 0 : $ticket->get_ticket_subtotal();
1712
-        return $subtotal * $tax->get('PRC_amount') / 100;
1713
-    }
1714
-
1715
-
1716
-    /**
1717
-     * @param int|string     $ticket_row
1718
-     * @param int|string     $price_row
1719
-     * @param EE_Price|null  $price
1720
-     * @param bool           $default
1721
-     * @param EE_Ticket|null $ticket
1722
-     * @param bool           $show_trash
1723
-     * @param bool           $show_create
1724
-     * @return string
1725
-     * @throws InvalidArgumentException
1726
-     * @throws InvalidInterfaceException
1727
-     * @throws InvalidDataTypeException
1728
-     * @throws DomainException
1729
-     * @throws EE_Error
1730
-     * @throws ReflectionException
1731
-     */
1732
-    protected function _get_ticket_price_row(
1733
-        $ticket_row,
1734
-        $price_row,
1735
-        ?EE_Price $price,
1736
-        bool $default,
1737
-        ?EE_Ticket $ticket,
1738
-        bool $show_trash = true,
1739
-        bool $show_create = true
1740
-    ): string {
1741
-        $send_disabled = ! empty($ticket) && $ticket->get('TKT_deleted');
1742
-        $template_args = [
1743
-            'tkt_row'               => $default && empty($ticket)
1744
-                ? 'TICKETNUM'
1745
-                : $ticket_row,
1746
-            'PRC_order'             => $default && empty($price)
1747
-                ? 'PRICENUM'
1748
-                : $price_row,
1749
-            'edit_prices_name'      => $default && empty($price)
1750
-                ? 'PRICENAMEATTR'
1751
-                : 'edit_prices',
1752
-            'price_type_selector'   => $default && empty($price)
1753
-                ? $this->_get_base_price_template($ticket_row, $price_row, $price, true)
1754
-                : $this->_get_price_type_selector(
1755
-                    $ticket_row,
1756
-                    $price_row,
1757
-                    $price,
1758
-                    $default,
1759
-                    $send_disabled
1760
-                ),
1761
-            'PRC_ID'                => $default && empty($price)
1762
-                ? 0
1763
-                : $price->ID(),
1764
-            'PRC_is_default'        => $default && empty($price)
1765
-                ? 0
1766
-                : $price->get('PRC_is_default'),
1767
-            'PRC_name'              => $default && empty($price)
1768
-                ? ''
1769
-                : $price->get('PRC_name'),
1770
-            'price_currency_symbol' => EE_Registry::instance()->CFG->currency->sign,
1771
-            'show_plus_or_minus'    => $default && empty($price)
1772
-                ? ''
1773
-                : 'display:none;',
1774
-            'show_plus'             => ($default && empty($price)) || ($price->is_discount() || $price->is_base_price())
1775
-                ? 'display:none;'
1776
-                : '',
1777
-            'show_minus'            => ($default && empty($price)) || ! $price->is_discount()
1778
-                ? 'display:none;'
1779
-                : '',
1780
-            'show_currency_symbol'  => ($default && empty($price)) || $price->is_percent()
1781
-                ? 'display:none'
1782
-                : '',
1783
-            'PRC_amount'            => $default && empty($price)
1784
-                ? 0
1785
-                : $price->get_pretty('PRC_amount', 'localized_float'),
1786
-            'show_percentage'       => ($default && empty($price)) || ! $price->is_percent()
1787
-                ? 'display:none;'
1788
-                : '',
1789
-            'show_trash_icon'       => $show_trash
1790
-                ? ''
1791
-                : ' style="display:none;"',
1792
-            'show_create_button'    => $show_create
1793
-                ? ''
1794
-                : ' style="display:none;"',
1795
-            'PRC_desc'              => $default && empty($price)
1796
-                ? ''
1797
-                : $price->get('PRC_desc'),
1798
-            'disabled'              => ! empty($ticket) && $ticket->get('TKT_deleted'),
1799
-        ];
1800
-        $template_args = apply_filters(
1801
-            'FHEE__espresso_events_Pricing_Hooks___get_ticket_price_row__template_args',
1802
-            $template_args,
1803
-            $ticket_row,
1804
-            $price_row,
1805
-            $price,
1806
-            $default,
1807
-            $ticket,
1808
-            $show_trash,
1809
-            $show_create,
1810
-            $this->_is_creating_event
1811
-        );
1812
-        return EEH_Template::display_template(
1813
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_price_row.template.php',
1814
-            $template_args,
1815
-            true
1816
-        );
1817
-    }
1818
-
1819
-
1820
-    /**
1821
-     * @param int|string    $ticket_row
1822
-     * @param int|string    $price_row
1823
-     * @param EE_Price|null $price
1824
-     * @param bool          $default
1825
-     * @param bool          $disabled
1826
-     * @return string
1827
-     * @throws ReflectionException
1828
-     * @throws InvalidArgumentException
1829
-     * @throws InvalidInterfaceException
1830
-     * @throws InvalidDataTypeException
1831
-     * @throws DomainException
1832
-     * @throws EE_Error
1833
-     */
1834
-    protected function _get_price_type_selector(
1835
-        $ticket_row,
1836
-        $price_row,
1837
-        ?EE_Price $price,
1838
-        bool $default,
1839
-        bool $disabled = false
1840
-    ): string {
1841
-        if ($price->is_base_price()) {
1842
-            return $this->_get_base_price_template(
1843
-                $ticket_row,
1844
-                $price_row,
1845
-                $price,
1846
-                $default
1847
-            );
1848
-        }
1849
-        return $this->_get_price_modifier_template(
1850
-            $ticket_row,
1851
-            $price_row,
1852
-            $price,
1853
-            $default,
1854
-            $disabled
1855
-        );
1856
-    }
1857
-
1858
-
1859
-    /**
1860
-     * @param int|string    $ticket_row
1861
-     * @param int|string    $price_row
1862
-     * @param EE_Price|null $price
1863
-     * @param bool          $default
1864
-     * @return string
1865
-     * @throws DomainException
1866
-     * @throws EE_Error
1867
-     * @throws ReflectionException
1868
-     */
1869
-    protected function _get_base_price_template(
1870
-        $ticket_row,
1871
-        $price_row,
1872
-        ?EE_Price $price,
1873
-        bool $default
1874
-    ): string {
1875
-        $template_args = [
1876
-            'tkt_row'                   => $default ? 'TICKETNUM' : $ticket_row,
1877
-            'PRC_order'                 => $default && empty($price) ? 'PRICENUM' : $price_row,
1878
-            'PRT_ID'                    => $default && empty($price) ? 1 : $price->get('PRT_ID'),
1879
-            'PRT_name'                  => esc_html__('Price', 'event_espresso'),
1880
-            'price_selected_operator'   => '+',
1881
-            'price_selected_is_percent' => 0,
1882
-        ];
1883
-        $template_args = apply_filters(
1884
-            'FHEE__espresso_events_Pricing_Hooks___get_base_price_template__template_args',
1885
-            $template_args,
1886
-            $ticket_row,
1887
-            $price_row,
1888
-            $price,
1889
-            $default,
1890
-            $this->_is_creating_event
1891
-        );
1892
-        return EEH_Template::display_template(
1893
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_price_type_base.template.php',
1894
-            $template_args,
1895
-            true
1896
-        );
1897
-    }
1898
-
1899
-
1900
-    /**
1901
-     * @param int|string    $ticket_row
1902
-     * @param int|string    $price_row
1903
-     * @param EE_Price|null $price
1904
-     * @param bool          $default
1905
-     * @param bool          $disabled
1906
-     * @return string
1907
-     * @throws ReflectionException
1908
-     * @throws InvalidArgumentException
1909
-     * @throws InvalidInterfaceException
1910
-     * @throws InvalidDataTypeException
1911
-     * @throws DomainException
1912
-     * @throws EE_Error
1913
-     */
1914
-    protected function _get_price_modifier_template(
1915
-        $ticket_row,
1916
-        $price_row,
1917
-        ?EE_Price $price,
1918
-        bool $default,
1919
-        bool $disabled = false
1920
-    ): string {
1921
-        $select_name = $default && ! $price instanceof EE_Price
1922
-            ? 'edit_prices[TICKETNUM][PRICENUM][PRT_ID]'
1923
-            : 'edit_prices[' . esc_attr($ticket_row) . '][' . esc_attr($price_row) . '][PRT_ID]';
1924
-
1925
-        $price_type_model       = EEM_Price_Type::instance();
1926
-        $price_types            = $price_type_model->get_all(
1927
-            [
1928
-                [
1929
-                    'OR' => [
1930
-                        'PBT_ID'  => '2',
1931
-                        'PBT_ID*' => '3',
1932
-                    ],
1933
-                ],
1934
-            ]
1935
-        );
1936
-        $all_price_types        = $default && ! $price instanceof EE_Price
1937
-            ? [esc_html__('Select Modifier', 'event_espresso')]
1938
-            : [];
1939
-        $selected_price_type_id = $default && ! $price instanceof EE_Price ? 0 : $price->type();
1940
-        $price_option_spans     = '';
1941
-        // setup price types for selector
1942
-        foreach ($price_types as $price_type) {
1943
-            if (! $price_type instanceof EE_Price_Type) {
1944
-                continue;
1945
-            }
1946
-            $all_price_types[ $price_type->ID() ] = $price_type->get('PRT_name');
1947
-            // while we're in the loop let's setup the option spans used by js
1948
-            $span_args          = [
1949
-                'PRT_ID'         => $price_type->ID(),
1950
-                'PRT_operator'   => $price_type->is_discount() ? '-' : '+',
1951
-                'PRT_is_percent' => $price_type->get('PRT_is_percent') ? 1 : 0,
1952
-            ];
1953
-            $price_option_spans .= EEH_Template::display_template(
1954
-                PRICING_TEMPLATE_PATH . 'event_tickets_datetime_price_option_span.template.php',
1955
-                $span_args,
1956
-                true
1957
-            );
1958
-        }
1959
-
1960
-        $select_name = $disabled
1961
-            ? 'archive_price[' . $ticket_row . '][' . $price_row . '][PRT_ID]'
1962
-            : $select_name;
1963
-
1964
-        $select_input = new EE_Select_Input(
1965
-            $all_price_types,
1966
-            [
1967
-                'default'               => $selected_price_type_id,
1968
-                'html_name'             => $select_name,
1969
-                'html_class'            => 'edit-price-PRT_ID',
1970
-                'other_html_attributes' => $disabled ? 'style="width:auto;" disabled' : 'style="width:auto;"',
1971
-            ]
1972
-        );
1973
-
1974
-        $price_selected_operator   = $price instanceof EE_Price && $price->is_discount() ? '-' : '+';
1975
-        $price_selected_operator   = $default && ! $price instanceof EE_Price ? '' : $price_selected_operator;
1976
-        $price_selected_is_percent = $price instanceof EE_Price && $price->is_percent() ? 1 : 0;
1977
-        $price_selected_is_percent = $default && ! $price instanceof EE_Price ? '' : $price_selected_is_percent;
1978
-        $template_args             = [
1979
-            'tkt_row'                   => $default ? 'TICKETNUM' : $ticket_row,
1980
-            'PRC_order'                 => $default && ! $price instanceof EE_Price ? 'PRICENUM' : $price_row,
1981
-            'price_modifier_selector'   => $select_input->get_html_for_input(),
1982
-            'main_name'                 => $select_name,
1983
-            'selected_price_type_id'    => $selected_price_type_id,
1984
-            'price_option_spans'        => $price_option_spans,
1985
-            'price_selected_operator'   => $price_selected_operator,
1986
-            'price_selected_is_percent' => $price_selected_is_percent,
1987
-            'disabled'                  => $disabled,
1988
-        ];
1989
-        $template_args             = apply_filters(
1990
-            'FHEE__espresso_events_Pricing_Hooks___get_price_modifier_template__template_args',
1991
-            $template_args,
1992
-            $ticket_row,
1993
-            $price_row,
1994
-            $price,
1995
-            $default,
1996
-            $disabled,
1997
-            $this->_is_creating_event
1998
-        );
1999
-        return EEH_Template::display_template(
2000
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_price_modifier_selector.template.php',
2001
-            $template_args,
2002
-            true
2003
-        );
2004
-    }
2005
-
2006
-
2007
-    /**
2008
-     * @param int|string       $datetime_row
2009
-     * @param int|string       $ticket_row
2010
-     * @param EE_Datetime|null $datetime
2011
-     * @param EE_Ticket|null   $ticket
2012
-     * @param array            $ticket_datetimes
2013
-     * @param bool             $default
2014
-     * @return string
2015
-     * @throws DomainException
2016
-     * @throws EE_Error
2017
-     * @throws ReflectionException
2018
-     */
2019
-    protected function _get_ticket_datetime_list_item(
2020
-        $datetime_row,
2021
-        $ticket_row,
2022
-        ?EE_Datetime $datetime,
2023
-        ?EE_Ticket $ticket,
2024
-        array $ticket_datetimes = [],
2025
-        bool $default = false
2026
-    ): string {
2027
-        $ticket_datetimes = $ticket instanceof EE_Ticket && isset($ticket_datetimes[ $ticket->ID() ])
2028
-            ? $ticket_datetimes[ $ticket->ID() ]
2029
-            : [];
2030
-        $template_args    = [
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, $ticket_datetimes, true)
2038
-                ? ' ticket-selected'
2039
-                : '',
2040
-            'ticket_datetime_checked'  => in_array($datetime_row, $ticket_datetimes, true)
2041
-                ? ' 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
-
2066
-
2067
-    /**
2068
-     * @param array $all_datetimes
2069
-     * @param array $all_tickets
2070
-     * @return string
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(array $all_datetimes = [], array $all_tickets = []): string
2079
-    {
2080
-        $template_args = [
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
-                [],
2091
-                [],
2092
-                true
2093
-            ),
2094
-            'default_price_row'         => $this->_get_ticket_price_row(
2095
-                'TICKETNUM',
2096
-                'PRICENUM',
2097
-                null,
2098
-                true,
2099
-                null
2100
-            ),
2101
-
2102
-            'default_price_rows'                       => '',
2103
-            'default_base_price_amount'                => 0,
2104
-            'default_base_price_name'                  => '',
2105
-            'default_base_price_description'           => '',
2106
-            'default_price_modifier_selector_row'      => $this->_get_price_modifier_template(
2107
-                'TICKETNUM',
2108
-                'PRICENUM',
2109
-                null,
2110
-                true
2111
-            ),
2112
-            'default_available_tickets_for_datetime'   => $this->_get_dtt_attached_tickets_row(
2113
-                'DTTNUM',
2114
-                null,
2115
-                [],
2116
-                [],
2117
-                true
2118
-            ),
2119
-            'existing_available_datetime_tickets_list' => '',
2120
-            'existing_available_ticket_datetimes_list' => '',
2121
-            'new_available_datetime_ticket_list_item'  => $this->_get_datetime_tickets_list_item(
2122
-                'DTTNUM',
2123
-                'TICKETNUM',
2124
-                null,
2125
-                null,
2126
-                [],
2127
-                true
2128
-            ),
2129
-            'new_available_ticket_datetime_list_item'  => $this->_get_ticket_datetime_list_item(
2130
-                'DTTNUM',
2131
-                'TICKETNUM',
2132
-                null,
2133
-                null,
2134
-                [],
2135
-                true
2136
-            ),
2137
-        ];
2138
-        $ticket_row    = 1;
2139
-        foreach ($all_tickets as $ticket) {
2140
-            $template_args['existing_available_datetime_tickets_list'] .= $this->_get_datetime_tickets_list_item(
2141
-                'DTTNUM',
2142
-                $ticket_row,
2143
-                null,
2144
-                $ticket,
2145
-                [],
2146
-                true
2147
-            );
2148
-            $ticket_row++;
2149
-        }
2150
-        $datetime_row = 1;
2151
-        foreach ($all_datetimes as $datetime) {
2152
-            $template_args['existing_available_ticket_datetimes_list'] .= $this->_get_ticket_datetime_list_item(
2153
-                $datetime_row,
2154
-                'TICKETNUM',
2155
-                $datetime,
2156
-                null,
2157
-                [],
2158
-                true
2159
-            );
2160
-            $datetime_row++;
2161
-        }
2162
-        $price_model    = EEM_Price::instance();
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
-
2180
-            $show_trash  = ! ((count($default_prices) > 1 && $price_row === 1) || count($default_prices) === 1);
2181
-            $show_create = ! (count($default_prices) > 1 && count($default_prices) !== $price_row);
2182
-
2183
-            $template_args['default_price_rows'] .= $this->_get_ticket_price_row(
2184
-                'TICKETNUM',
2185
-                $price_row,
2186
-                $price,
2187
-                true,
2188
-                null,
2189
-                $show_trash,
2190
-                $show_create
2191
-            );
2192
-            $price_row++;
2193
-        }
2194
-        $template_args = apply_filters(
2195
-            'FHEE__espresso_events_Pricing_Hooks___get_ticket_js_structure__template_args',
2196
-            $template_args,
2197
-            $all_datetimes,
2198
-            $all_tickets,
2199
-            $this->_is_creating_event
2200
-        );
2201
-        return EEH_Template::display_template(
2202
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_js_structure.template.php',
2203
-            $template_args,
2204
-            true
2205
-        );
2206
-    }
231
+					],
232
+					'DTT_OVERSELL_WARNING'  => [
233
+						'datetime_ticket' => esc_html__(
234
+							'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.',
235
+							'event_espresso'
236
+						),
237
+						'ticket_datetime' => esc_html__(
238
+							'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.',
239
+							'event_espresso'
240
+						),
241
+					],
242
+					'DTT_CONVERTED_FORMATS' => EEH_DTT_Helper::convert_php_to_js_and_moment_date_formats(
243
+						$this->_date_format_strings['date'],
244
+						$this->_date_format_strings['time']
245
+					),
246
+					'DTT_START_OF_WEEK'     => ['dayValue' => (int) get_option('start_of_week')],
247
+				],
248
+			],
249
+		];
250
+	}
251
+
252
+
253
+	/**
254
+	 * @param array $update_callbacks
255
+	 * @return array
256
+	 */
257
+	public function caf_updates(array $update_callbacks): array
258
+	{
259
+		unset($update_callbacks['_default_tickets_update']);
260
+		$update_callbacks['datetime_and_tickets_caf_update'] = [$this, 'datetime_and_tickets_caf_update'];
261
+		return $update_callbacks;
262
+	}
263
+
264
+
265
+	/**
266
+	 * Handles saving everything related to Tickets (datetimes, tickets, prices)
267
+	 *
268
+	 * @param EE_Event $event The Event object we're attaching data to
269
+	 * @param array    $data  The request data from the form
270
+	 * @throws ReflectionException
271
+	 * @throws Exception
272
+	 * @throws InvalidInterfaceException
273
+	 * @throws InvalidDataTypeException
274
+	 * @throws EE_Error
275
+	 * @throws InvalidArgumentException
276
+	 */
277
+	public function datetime_and_tickets_caf_update(EE_Event $event, array $data)
278
+	{
279
+		// first we need to start with datetimes cause they are the "root" items attached to events.
280
+		$saved_datetimes = $this->_update_datetimes($event, $data);
281
+		// next tackle the tickets (and prices?)
282
+		$this->_update_tickets($event, $saved_datetimes, $data);
283
+	}
284
+
285
+
286
+	/**
287
+	 * update event_datetimes
288
+	 *
289
+	 * @param EE_Event $event Event being updated
290
+	 * @param array    $data  the request data from the form
291
+	 * @return EE_Datetime[]
292
+	 * @throws Exception
293
+	 * @throws ReflectionException
294
+	 * @throws InvalidInterfaceException
295
+	 * @throws InvalidDataTypeException
296
+	 * @throws InvalidArgumentException
297
+	 * @throws EE_Error
298
+	 */
299
+	protected function _update_datetimes(EE_Event $event, array $data): array
300
+	{
301
+		$saved_datetime_ids  = [];
302
+		$saved_datetime_objs = [];
303
+		$timezone       = $data['timezone_string'] ?? null;
304
+		$datetime_model = EEM_Datetime::instance($timezone);
305
+
306
+		if (empty($data['edit_event_datetimes']) || ! is_array($data['edit_event_datetimes'])) {
307
+			throw new InvalidArgumentException(
308
+				esc_html__(
309
+					'The "edit_event_datetimes" array is invalid therefore the event can not be updated.',
310
+					'event_espresso'
311
+				)
312
+			);
313
+		}
314
+		foreach ($data['edit_event_datetimes'] as $row => $datetime_data) {
315
+			// trim all values to ensure any excess whitespace is removed.
316
+			$datetime_data = array_map(
317
+				function ($datetime_data) {
318
+					return is_array($datetime_data) ? $datetime_data : trim($datetime_data);
319
+				},
320
+				$datetime_data
321
+			);
322
+
323
+			$datetime_data['DTT_EVT_end'] = isset($datetime_data['DTT_EVT_end'])
324
+											&& ! empty($datetime_data['DTT_EVT_end'])
325
+				? $datetime_data['DTT_EVT_end']
326
+				: $datetime_data['DTT_EVT_start'];
327
+			$datetime_values              = [
328
+				'DTT_ID'          => ! empty($datetime_data['DTT_ID'])
329
+					? $datetime_data['DTT_ID']
330
+					: null,
331
+				'DTT_name'        => ! empty($datetime_data['DTT_name'])
332
+					? $datetime_data['DTT_name']
333
+					: '',
334
+				'DTT_description' => ! empty($datetime_data['DTT_description'])
335
+					? $datetime_data['DTT_description']
336
+					: '',
337
+				'DTT_EVT_start'   => $datetime_data['DTT_EVT_start'],
338
+				'DTT_EVT_end'     => $datetime_data['DTT_EVT_end'],
339
+				'DTT_reg_limit'   => empty($datetime_data['DTT_reg_limit'])
340
+					? EE_INF
341
+					: $datetime_data['DTT_reg_limit'],
342
+				'DTT_order'       => ! isset($datetime_data['DTT_order'])
343
+					? $row
344
+					: $datetime_data['DTT_order'],
345
+			];
346
+
347
+			// if we have an id then let's get existing object first and then set the new values.
348
+			// Otherwise we instantiate a new object for save.
349
+			if (! empty($datetime_data['DTT_ID'])) {
350
+				$datetime = EE_Registry::instance()
351
+									   ->load_model('Datetime', [$timezone])
352
+									   ->get_one_by_ID($datetime_data['DTT_ID']);
353
+				// set date and time format according to what is set in this class.
354
+				$datetime->set_date_format($this->_date_format_strings['date']);
355
+				$datetime->set_time_format($this->_date_format_strings['time']);
356
+				foreach ($datetime_values as $field => $value) {
357
+					$datetime->set($field, $value);
358
+				}
359
+
360
+				// make sure the $datetime_id here is saved just in case
361
+				// after the add_relation_to() the autosave replaces it.
362
+				// We need to do this so we dont' TRASH the parent DTT.
363
+				// (save the ID for both key and value to avoid duplications)
364
+				$saved_datetime_ids[ $datetime->ID() ] = $datetime->ID();
365
+			} else {
366
+				$datetime = EE_Datetime::new_instance(
367
+					$datetime_values,
368
+					$timezone,
369
+					[$this->_date_format_strings['date'], $this->_date_format_strings['time']]
370
+				);
371
+				foreach ($datetime_values as $field => $value) {
372
+					$datetime->set($field, $value);
373
+				}
374
+			}
375
+			$datetime->save();
376
+			do_action(
377
+				'AHEE__espresso_events_Pricing_Hooks___update_datetimes_after_save',
378
+				$datetime,
379
+				$row,
380
+				$datetime_data,
381
+				$data
382
+			);
383
+			$datetime = $event->_add_relation_to($datetime, 'Datetime');
384
+			// before going any further make sure our dates are setup correctly
385
+			// so that the end date is always equal or greater than the start date.
386
+			if ($datetime->get_raw('DTT_EVT_start') > $datetime->get_raw('DTT_EVT_end')) {
387
+				$datetime->set('DTT_EVT_end', $datetime->get('DTT_EVT_start'));
388
+				$datetime = EEH_DTT_Helper::date_time_add($datetime, 'DTT_EVT_end', 'days');
389
+				$datetime->save();
390
+			}
391
+			// now we have to make sure we add the new DTT_ID to the $saved_datetime_ids array
392
+			// because it is possible there was a new one created for the autosave.
393
+			// (save the ID for both key and value to avoid duplications)
394
+			$DTT_ID                        = $datetime->ID();
395
+			$saved_datetime_ids[ $DTT_ID ] = $DTT_ID;
396
+			$saved_datetime_objs[ $row ]   = $datetime;
397
+			// @todo if ANY of these updates fail then we want the appropriate global error message.
398
+		}
399
+		$event->save();
400
+		// now we need to REMOVE any datetimes that got deleted.
401
+		// Keep in mind that this process will only kick in for datetimes that don't have any DTT_sold on them.
402
+		// So its safe to permanently delete at this point.
403
+		$old_datetimes = explode(',', $data['datetime_IDs']);
404
+		$old_datetimes = $old_datetimes[0] === '' ? [] : $old_datetimes;
405
+		if (is_array($old_datetimes)) {
406
+			$datetimes_to_delete = array_diff($old_datetimes, $saved_datetime_ids);
407
+			foreach ($datetimes_to_delete as $id) {
408
+				$id = absint($id);
409
+				if (empty($id)) {
410
+					continue;
411
+				}
412
+				$dtt_to_remove = $datetime_model->get_one_by_ID($id);
413
+				// remove tkt relationships.
414
+				$related_tickets = $dtt_to_remove->get_many_related('Ticket');
415
+				foreach ($related_tickets as $ticket) {
416
+					$dtt_to_remove->_remove_relation_to($ticket, 'Ticket');
417
+				}
418
+				$event->_remove_relation_to($id, 'Datetime');
419
+				$dtt_to_remove->refresh_cache_of_related_objects();
420
+			}
421
+		}
422
+		return $saved_datetime_objs;
423
+	}
424
+
425
+
426
+	/**
427
+	 * update tickets
428
+	 *
429
+	 * @param EE_Event      $event           Event object being updated
430
+	 * @param EE_Datetime[] $saved_datetimes an array of datetime ids being updated
431
+	 * @param array         $data            incoming request data
432
+	 * @return EE_Ticket[]
433
+	 * @throws Exception
434
+	 * @throws ReflectionException
435
+	 * @throws InvalidInterfaceException
436
+	 * @throws InvalidDataTypeException
437
+	 * @throws InvalidArgumentException
438
+	 * @throws EE_Error
439
+	 */
440
+	protected function _update_tickets(EE_Event $event, array $saved_datetimes, array $data): array
441
+	{
442
+		$new_ticket = null;
443
+		// stripslashes because WP filtered the $_POST ($data) array to add slashes
444
+		$data          = stripslashes_deep($data);
445
+		$timezone      = $data['timezone_string'] ?? null;
446
+		$ticket_model = EEM_Ticket::instance($timezone);
447
+
448
+		$saved_tickets = [];
449
+		$old_tickets   = isset($data['ticket_IDs']) ? explode(',', $data['ticket_IDs']) : [];
450
+		if (empty($data['edit_tickets']) || ! is_array($data['edit_tickets'])) {
451
+			throw new InvalidArgumentException(
452
+				esc_html__(
453
+					'The "edit_tickets" array is invalid therefore the event can not be updated.',
454
+					'event_espresso'
455
+				)
456
+			);
457
+		}
458
+		foreach ($data['edit_tickets'] as $row => $ticket_data) {
459
+			$update_prices = $create_new_TKT = false;
460
+			// figure out what datetimes were added to the ticket
461
+			// and what datetimes were removed from the ticket in the session.
462
+			$starting_ticket_datetime_rows = explode(',', $data['starting_ticket_datetime_rows'][ $row ]);
463
+			$ticket_datetime_rows          = explode(',', $data['ticket_datetime_rows'][ $row ]);
464
+			$datetimes_added               = array_diff($ticket_datetime_rows, $starting_ticket_datetime_rows);
465
+			$datetimes_removed             = array_diff($starting_ticket_datetime_rows, $ticket_datetime_rows);
466
+			// trim inputs to ensure any excess whitespace is removed.
467
+			$ticket_data = array_map(
468
+				function ($ticket_data) {
469
+					return is_array($ticket_data) ? $ticket_data : trim($ticket_data);
470
+				},
471
+				$ticket_data
472
+			);
473
+			// note we are doing conversions to floats here instead of allowing EE_Money_Field to handle
474
+			// because we're doing calculations prior to using the models.
475
+			// note incoming ['TKT_price'] value is already in standard notation (via js).
476
+			$ticket_price = isset($ticket_data['TKT_price'])
477
+				? round((float) $ticket_data['TKT_price'], 3)
478
+				: 0;
479
+			// note incoming base price needs converted from localized value.
480
+			$base_price = isset($ticket_data['TKT_base_price'])
481
+				? EEH_Money::convert_to_float_from_localized_money($ticket_data['TKT_base_price'])
482
+				: 0;
483
+			// if ticket price == 0 and $base_price != 0 then ticket price == base_price
484
+			$ticket_price  = $ticket_price === 0 && $base_price !== 0
485
+				? $base_price
486
+				: $ticket_price;
487
+			$base_price_id = $ticket_data['TKT_base_price_ID'] ?? 0;
488
+			$price_rows    = is_array($data['edit_prices']) && isset($data['edit_prices'][ $row ])
489
+				? $data['edit_prices'][ $row ]
490
+				: [];
491
+			$now           = null;
492
+			if (empty($ticket_data['TKT_start_date'])) {
493
+				// lets' use now in the set timezone.
494
+				$now                           = new DateTime('now', new DateTimeZone($event->get_timezone()));
495
+				$ticket_data['TKT_start_date'] = $now->format($this->_date_time_format);
496
+			}
497
+			if (empty($ticket_data['TKT_end_date'])) {
498
+				/**
499
+				 * set the TKT_end_date to the first datetime attached to the ticket.
500
+				 */
501
+				$first_datetime              = $saved_datetimes[ reset($ticket_datetime_rows) ];
502
+				$ticket_data['TKT_end_date'] = $first_datetime->start_date_and_time($this->_date_time_format);
503
+			}
504
+			$TKT_values = [
505
+				'TKT_ID'          => ! empty($ticket_data['TKT_ID']) ? $ticket_data['TKT_ID'] : null,
506
+				'TTM_ID'          => ! empty($ticket_data['TTM_ID']) ? $ticket_data['TTM_ID'] : 0,
507
+				'TKT_name'        => ! empty($ticket_data['TKT_name']) ? $ticket_data['TKT_name'] : '',
508
+				'TKT_description' => ! empty($ticket_data['TKT_description'])
509
+									 && $ticket_data['TKT_description'] !== esc_html__(
510
+					'You can modify this description',
511
+					'event_espresso'
512
+				)
513
+					? $ticket_data['TKT_description']
514
+					: '',
515
+				'TKT_start_date'  => $ticket_data['TKT_start_date'],
516
+				'TKT_end_date'    => $ticket_data['TKT_end_date'],
517
+				'TKT_qty'         => ! isset($ticket_data['TKT_qty']) || $ticket_data['TKT_qty'] === ''
518
+					? EE_INF
519
+					: $ticket_data['TKT_qty'],
520
+				'TKT_uses'        => ! isset($ticket_data['TKT_uses']) || $ticket_data['TKT_uses'] === ''
521
+					? EE_INF
522
+					: $ticket_data['TKT_uses'],
523
+				'TKT_min'         => empty($ticket_data['TKT_min']) ? 0 : $ticket_data['TKT_min'],
524
+				'TKT_max'         => empty($ticket_data['TKT_max']) ? EE_INF : $ticket_data['TKT_max'],
525
+				'TKT_row'         => $row,
526
+				'TKT_order'       => $ticket_data['TKT_order'] ?? 0,
527
+				'TKT_taxable'     => ! empty($ticket_data['TKT_taxable']) ? 1 : 0,
528
+				'TKT_required'    => ! empty($ticket_data['TKT_required']) ? 1 : 0,
529
+				'TKT_price'       => $ticket_price,
530
+			];
531
+			// if this is a default TKT, then we need to set the TKT_ID to 0 and update accordingly,
532
+			// which means in turn that the prices will become new prices as well.
533
+			if (isset($ticket_data['TKT_is_default']) && $ticket_data['TKT_is_default']) {
534
+				$TKT_values['TKT_ID']         = 0;
535
+				$TKT_values['TKT_is_default'] = 0;
536
+				$update_prices                = true;
537
+			}
538
+			// if we have a TKT_ID then we need to get that existing TKT_obj and update it
539
+			// we actually do our saves ahead of doing any add_relations to
540
+			// because its entirely possible that this ticket wasn't removed or added to any datetime in the session
541
+			// but DID have it's items modified.
542
+			// keep in mind that if the TKT has been sold (and we have changed pricing information),
543
+			// then we won't be updating the ticket but instead a new ticket will be created and the old one archived.
544
+			if (absint($TKT_values['TKT_ID'])) {
545
+				$ticket = EE_Registry::instance()
546
+									 ->load_model('Ticket', [$timezone])
547
+									 ->get_one_by_ID($TKT_values['TKT_ID']);
548
+				if ($ticket instanceof EE_Ticket) {
549
+					$ticket = $this->_update_ticket_datetimes(
550
+						$ticket,
551
+						$saved_datetimes,
552
+						$datetimes_added,
553
+						$datetimes_removed
554
+					);
555
+					// are there any registrations using this ticket ?
556
+					$tickets_sold = $ticket->count_related(
557
+						'Registration',
558
+						[
559
+							[
560
+								'STS_ID' => ['NOT IN', [EEM_Registration::status_id_incomplete]],
561
+							],
562
+						]
563
+					);
564
+					// set ticket formats
565
+					$ticket->set_date_format($this->_date_format_strings['date']);
566
+					$ticket->set_time_format($this->_date_format_strings['time']);
567
+					// let's just check the total price for the existing ticket
568
+					// and determine if it matches the new total price.
569
+					// if they are different then we create a new ticket (if tickets sold)
570
+					// if they aren't different then we go ahead and modify existing ticket.
571
+					$create_new_TKT = $tickets_sold > 0 && $ticket_price !== $ticket->price() && ! $ticket->deleted();
572
+					// set new values
573
+					foreach ($TKT_values as $field => $value) {
574
+						if ($field === 'TKT_qty') {
575
+							$ticket->set_qty($value);
576
+						} else {
577
+							$ticket->set($field, $value);
578
+						}
579
+					}
580
+					// if $create_new_TKT is false then we can safely update the existing ticket.
581
+					// Otherwise we have to create a new ticket.
582
+					if ($create_new_TKT) {
583
+						$new_ticket = $this->_duplicate_ticket(
584
+							$ticket,
585
+							$price_rows,
586
+							$ticket_price,
587
+							$base_price,
588
+							$base_price_id
589
+						);
590
+					}
591
+				}
592
+			} else {
593
+				// no TKT_id so a new TKT
594
+				$ticket = EE_Ticket::new_instance(
595
+					$TKT_values,
596
+					$timezone,
597
+					[$this->_date_format_strings['date'], $this->_date_format_strings['time']]
598
+				);
599
+				if ($ticket instanceof EE_Ticket) {
600
+					// make sure ticket has an ID of setting relations won't work
601
+					$ticket->save();
602
+					$ticket        = $this->_update_ticket_datetimes(
603
+						$ticket,
604
+						$saved_datetimes,
605
+						$datetimes_added,
606
+						$datetimes_removed
607
+					);
608
+					$update_prices = true;
609
+				}
610
+			}
611
+			// make sure any current values have been saved.
612
+			// $ticket->save();
613
+			// before going any further make sure our dates are setup correctly
614
+			// so that the end date is always equal or greater than the start date.
615
+			if ($ticket->get_raw('TKT_start_date') > $ticket->get_raw('TKT_end_date')) {
616
+				$ticket->set('TKT_end_date', $ticket->get('TKT_start_date'));
617
+				$ticket = EEH_DTT_Helper::date_time_add($ticket, 'TKT_end_date', 'days');
618
+			}
619
+			// let's make sure the base price is handled
620
+			$ticket = ! $create_new_TKT
621
+				? $this->_add_prices_to_ticket(
622
+					[],
623
+					$ticket,
624
+					$update_prices,
625
+					$base_price,
626
+					$base_price_id
627
+				)
628
+				: $ticket;
629
+			// add/update price_modifiers
630
+			$ticket = ! $create_new_TKT
631
+				? $this->_add_prices_to_ticket($price_rows, $ticket, $update_prices)
632
+				: $ticket;
633
+			// need to make sue that the TKT_price is accurate after saving the prices.
634
+			$ticket->ensure_TKT_Price_correct();
635
+			// handle CREATING a default ticket from the incoming ticket but ONLY if this isn't an autosave.
636
+			if (! defined('DOING_AUTOSAVE') && ! empty($ticket_data['TKT_is_default_selector'])) {
637
+				$new_default = clone $ticket;
638
+				$new_default->set('TKT_ID', 0);
639
+				$new_default->set('TKT_is_default', 1);
640
+				$new_default->set('TKT_row', 1);
641
+				$new_default->set('TKT_price', $ticket_price);
642
+				// remove any datetime relations cause we DON'T want datetime relations attached
643
+				// (note this is just removing the cached relations in the object)
644
+				$new_default->_remove_relations('Datetime');
645
+				// @todo we need to add the current attached prices as new prices to the new default ticket.
646
+				$new_default = $this->_add_prices_to_ticket(
647
+					$price_rows,
648
+					$new_default,
649
+					true
650
+				);
651
+				// don't forget the base price!
652
+				$new_default = $this->_add_prices_to_ticket(
653
+					[],
654
+					$new_default,
655
+					true,
656
+					$base_price,
657
+					$base_price_id
658
+				);
659
+				$new_default->save();
660
+				do_action(
661
+					'AHEE__espresso_events_Pricing_Hooks___update_tkts_new_default_ticket',
662
+					$new_default,
663
+					$row,
664
+					$ticket,
665
+					$data
666
+				);
667
+			}
668
+			// DO ALL datetime relationships for both current tickets and any archived tickets
669
+			// for the given datetime that are related to the current ticket.
670
+			// TODO... not sure exactly how we're going to do this considering we don't know
671
+			// what current ticket the archived tickets are related to
672
+			// (and TKT_parent is used for autosaves so that's not a field we can reliably use).
673
+			// let's assign any tickets that have been setup to the saved_tickets tracker
674
+			// save existing TKT
675
+			$ticket->save();
676
+			if ($create_new_TKT && $new_ticket instanceof EE_Ticket) {
677
+				// save new TKT
678
+				$new_ticket->save();
679
+				// add new ticket to array
680
+				$saved_tickets[ $new_ticket->ID() ] = $new_ticket;
681
+				do_action(
682
+					'AHEE__espresso_events_Pricing_Hooks___update_tkts_new_ticket',
683
+					$new_ticket,
684
+					$row,
685
+					$ticket_data,
686
+					$data
687
+				);
688
+			} else {
689
+				// add ticket to saved tickets
690
+				$saved_tickets[ $ticket->ID() ] = $ticket;
691
+				do_action(
692
+					'AHEE__espresso_events_Pricing_Hooks___update_tkts_update_ticket',
693
+					$ticket,
694
+					$row,
695
+					$ticket_data,
696
+					$data
697
+				);
698
+			}
699
+		}
700
+		// now we need to handle tickets actually "deleted permanently".
701
+		// There are cases where we'd want this to happen
702
+		// (i.e. autosaves are happening and then in between autosaves the user trashes a ticket).
703
+		// Or a draft event was saved and in the process of editing a ticket is trashed.
704
+		// No sense in keeping all the related data in the db!
705
+		$old_tickets     = isset($old_tickets[0]) && $old_tickets[0] === '' ? [] : $old_tickets;
706
+		$tickets_removed = array_diff($old_tickets, array_keys($saved_tickets));
707
+		foreach ($tickets_removed as $id) {
708
+			$id = absint($id);
709
+			// get the ticket for this id
710
+			$ticket_to_remove = $ticket_model->get_one_by_ID($id);
711
+			// if this tkt is a default tkt we leave it alone cause it won't be attached to the datetime
712
+			if ($ticket_to_remove->get('TKT_is_default')) {
713
+				continue;
714
+			}
715
+			// if this ticket has any registrations attached so then we just ARCHIVE
716
+			// because we don't actually permanently delete these tickets.
717
+			if ($ticket_to_remove->count_related('Registration') > 0) {
718
+				$ticket_to_remove->delete();
719
+				continue;
720
+			}
721
+			// need to get all the related datetimes on this ticket and remove from every single one of them
722
+			// (remember this process can ONLY kick off if there are NO tickets_sold)
723
+			$datetimes = $ticket_to_remove->get_many_related('Datetime');
724
+			foreach ($datetimes as $datetime) {
725
+				$ticket_to_remove->_remove_relation_to($datetime, 'Datetime');
726
+			}
727
+			// need to do the same for prices (except these prices can also be deleted because again,
728
+			// tickets can only be trashed if they don't have any TKTs sold (otherwise they are just archived))
729
+			$ticket_to_remove->delete_related('Price');
730
+			do_action('AHEE__espresso_events_Pricing_Hooks___update_tkts_delete_ticket', $ticket_to_remove);
731
+			// finally let's delete this ticket
732
+			// (which should not be blocked at this point b/c we've removed all our relationships)
733
+			$ticket_to_remove->delete_or_restore();
734
+		}
735
+		return $saved_tickets;
736
+	}
737
+
738
+
739
+	/**
740
+	 * @access  protected
741
+	 * @param EE_Ticket     $ticket
742
+	 * @param EE_Datetime[] $saved_datetimes
743
+	 * @param int[]         $added_datetimes
744
+	 * @param int[]         $removed_datetimes
745
+	 * @return EE_Ticket
746
+	 * @throws EE_Error
747
+	 * @throws ReflectionException
748
+	 */
749
+	protected function _update_ticket_datetimes(
750
+		EE_Ticket $ticket,
751
+		array $saved_datetimes = [],
752
+		array $added_datetimes = [],
753
+		array $removed_datetimes = []
754
+	): EE_Ticket {
755
+		// to start we have to add the ticket to all the datetimes its supposed to be with,
756
+		// and removing the ticket from datetimes it got removed from.
757
+		// first let's add datetimes
758
+		if (! empty($added_datetimes) && is_array($added_datetimes)) {
759
+			foreach ($added_datetimes as $row_id) {
760
+				if (isset($saved_datetimes[ $row_id ]) && $saved_datetimes[ $row_id ] instanceof EE_Datetime) {
761
+					$ticket->_add_relation_to($saved_datetimes[ $row_id ], 'Datetime');
762
+					// Is this an existing ticket (has an ID) and does it have any sold?
763
+					// If so, then we need to add that to the DTT sold because this DTT is getting added.
764
+					if ($ticket->ID() && $ticket->sold() > 0) {
765
+						$saved_datetimes[ $row_id ]->increaseSold($ticket->sold(), false);
766
+					}
767
+				}
768
+			}
769
+		}
770
+		// then remove datetimes
771
+		if (! empty($removed_datetimes) && is_array($removed_datetimes)) {
772
+			foreach ($removed_datetimes as $row_id) {
773
+				// its entirely possible that a datetime got deleted (instead of just removed from relationship.
774
+				// So make sure we skip over this if the datetime isn't in the $saved_datetimes array)
775
+				if (isset($saved_datetimes[ $row_id ]) && $saved_datetimes[ $row_id ] instanceof EE_Datetime) {
776
+					$ticket->_remove_relation_to($saved_datetimes[ $row_id ], 'Datetime');
777
+				}
778
+			}
779
+		}
780
+		// cap ticket qty by datetime reg limits
781
+		$ticket->set_qty(min($ticket->qty(), $ticket->qty('reg_limit')));
782
+		return $ticket;
783
+	}
784
+
785
+
786
+	/**
787
+	 * @access  protected
788
+	 * @param EE_Ticket $ticket
789
+	 * @param array     $price_rows
790
+	 * @param int|float $ticket_price
791
+	 * @param int|float $base_price
792
+	 * @param int       $base_price_id
793
+	 * @return EE_Ticket
794
+	 * @throws ReflectionException
795
+	 * @throws InvalidArgumentException
796
+	 * @throws InvalidInterfaceException
797
+	 * @throws InvalidDataTypeException
798
+	 * @throws EE_Error
799
+	 */
800
+	protected function _duplicate_ticket(
801
+		EE_Ticket $ticket,
802
+		array $price_rows = [],
803
+		$ticket_price = 0,
804
+		$base_price = 0,
805
+		int $base_price_id = 0
806
+	): EE_Ticket {
807
+		// create new ticket that's a copy of the existing
808
+		// except a new id of course (and not archived)
809
+		// AND has the new TKT_price associated with it.
810
+		$new_ticket = clone $ticket;
811
+		$new_ticket->set('TKT_ID', 0);
812
+		$new_ticket->set_deleted(0);
813
+		$new_ticket->set_price($ticket_price);
814
+		$new_ticket->set_sold(0);
815
+		// let's get a new ID for this ticket
816
+		$new_ticket->save();
817
+		// we also need to make sure this new ticket gets the same datetime attachments as the archived ticket
818
+		$datetimes_on_existing = $ticket->datetimes();
819
+		$new_ticket            = $this->_update_ticket_datetimes(
820
+			$new_ticket,
821
+			$datetimes_on_existing,
822
+			array_keys($datetimes_on_existing)
823
+		);
824
+		// $ticket will get archived later b/c we are NOT adding it to the saved_tickets array.
825
+		// if existing $ticket has sold amount, then we need to adjust the qty for the new TKT to = the remaining
826
+		// available.
827
+		if ($ticket->sold() > 0) {
828
+			$new_qty = $ticket->qty() - $ticket->sold();
829
+			$new_ticket->set_qty($new_qty);
830
+		}
831
+		// now we update the prices just for this ticket
832
+		$new_ticket = $this->_add_prices_to_ticket($price_rows, $new_ticket, true);
833
+		// and we update the base price
834
+		return $this->_add_prices_to_ticket(
835
+			[],
836
+			$new_ticket,
837
+			true,
838
+			$base_price,
839
+			$base_price_id
840
+		);
841
+	}
842
+
843
+
844
+	/**
845
+	 * This attaches a list of given prices to a ticket.
846
+	 * Note we dont' have to worry about ever removing relationships (or archiving prices) because if there is a change
847
+	 * in price information on a ticket, a new ticket is created anyways so the archived ticket will retain the old
848
+	 * price info and prices are automatically "archived" via the ticket.
849
+	 *
850
+	 * @access  private
851
+	 * @param array     $prices        Array of prices from the form.
852
+	 * @param EE_Ticket $ticket        EE_Ticket object that prices are being attached to.
853
+	 * @param bool      $new_prices    Whether attach existing incoming prices or create new ones.
854
+	 * @param int|bool  $base_price    if FALSE then NOT doing a base price add.
855
+	 * @param int|bool  $base_price_id if present then this is the base_price_id being updated.
856
+	 * @return EE_Ticket
857
+	 * @throws ReflectionException
858
+	 * @throws InvalidArgumentException
859
+	 * @throws InvalidInterfaceException
860
+	 * @throws InvalidDataTypeException
861
+	 * @throws EE_Error
862
+	 */
863
+	protected function _add_prices_to_ticket(
864
+		array $prices,
865
+		EE_Ticket $ticket,
866
+		bool $new_prices = false,
867
+		$base_price = false,
868
+		$base_price_id = false
869
+	): EE_Ticket {
870
+		$price_model = EEM_Price::instance();
871
+		// let's just get any current prices that may exist on the given ticket
872
+		// so we can remove any prices that got trashed in this session.
873
+		$current_prices_on_ticket = $base_price !== false
874
+			? $ticket->base_price(true)
875
+			: $ticket->price_modifiers();
876
+		$updated_prices           = [];
877
+		// if $base_price ! FALSE then updating a base price.
878
+		if ($base_price !== false) {
879
+			$prices[1] = [
880
+				'PRC_ID'     => $new_prices || $base_price_id === 1 ? null : $base_price_id,
881
+				'PRT_ID'     => 1,
882
+				'PRC_amount' => $base_price,
883
+				'PRC_name'   => $ticket->get('TKT_name'),
884
+				'PRC_desc'   => $ticket->get('TKT_description'),
885
+			];
886
+		}
887
+		// possibly need to save ticket
888
+		if (! $ticket->ID()) {
889
+			$ticket->save();
890
+		}
891
+		foreach ($prices as $row => $prc) {
892
+			$prt_id = ! empty($prc['PRT_ID']) ? $prc['PRT_ID'] : null;
893
+			if (empty($prt_id)) {
894
+				continue;
895
+			} //prices MUST have a price type id.
896
+			$PRC_values = [
897
+				'PRC_ID'         => ! empty($prc['PRC_ID']) ? $prc['PRC_ID'] : null,
898
+				'PRT_ID'         => $prt_id,
899
+				'PRC_amount'     => ! empty($prc['PRC_amount']) ? $prc['PRC_amount'] : 0,
900
+				'PRC_name'       => ! empty($prc['PRC_name']) ? $prc['PRC_name'] : '',
901
+				'PRC_desc'       => ! empty($prc['PRC_desc']) ? $prc['PRC_desc'] : '',
902
+				'PRC_is_default' => false,
903
+				// make sure we set PRC_is_default to false for all ticket saves from event_editor
904
+				'PRC_order'      => $row,
905
+			];
906
+			if ($new_prices || empty($PRC_values['PRC_ID'])) {
907
+				$PRC_values['PRC_ID'] = 0;
908
+				$price                = EE_Registry::instance()->load_class(
909
+					'Price',
910
+					[$PRC_values],
911
+					false,
912
+					false
913
+				);
914
+			} else {
915
+				$price = $price_model->get_one_by_ID($prc['PRC_ID']);
916
+				// update this price with new values
917
+				foreach ($PRC_values as $field => $value) {
918
+					$price->set($field, $value);
919
+				}
920
+			}
921
+			$price->save();
922
+			$updated_prices[ $price->ID() ] = $price;
923
+			$ticket->_add_relation_to($price, 'Price');
924
+		}
925
+		// now let's remove any prices that got removed from the ticket
926
+		if (! empty($current_prices_on_ticket)) {
927
+			$current          = array_keys($current_prices_on_ticket);
928
+			$updated          = array_keys($updated_prices);
929
+			$prices_to_remove = array_diff($current, $updated);
930
+			if (! empty($prices_to_remove)) {
931
+				foreach ($prices_to_remove as $prc_id) {
932
+					$p = $current_prices_on_ticket[ $prc_id ];
933
+					$ticket->_remove_relation_to($p, 'Price');
934
+					// delete permanently the price
935
+					$p->delete_or_restore();
936
+				}
937
+			}
938
+		}
939
+		return $ticket;
940
+	}
941
+
942
+
943
+	/**
944
+	 * @throws ReflectionException
945
+	 * @throws InvalidArgumentException
946
+	 * @throws InvalidInterfaceException
947
+	 * @throws InvalidDataTypeException
948
+	 * @throws DomainException
949
+	 * @throws EE_Error
950
+	 */
951
+	public function pricing_metabox()
952
+	{
953
+		$event                 = $this->_adminpage_obj->get_cpt_model_obj();
954
+		$timezone = $event instanceof EE_Event ? $event->timezone_string() : null;
955
+		$price_model = EEM_Price::instance($timezone);
956
+		$ticket_model = EEM_Ticket::instance($timezone);
957
+		$datetime_model = EEM_Datetime::instance($timezone);
958
+
959
+		// set is_creating_event property.
960
+		$EVT_ID                   = $event->ID();
961
+		$this->_is_creating_event = empty($this->_req_data['post']);
962
+		$existing_datetime_ids = $existing_ticket_ids = $datetime_tickets = $ticket_datetimes = [];
963
+
964
+		// default main template args
965
+		$main_template_args = [
966
+			'event_datetime_help_link' => EEH_Template::get_help_tab_link(
967
+				'event_editor_event_datetimes_help_tab',
968
+				$this->_adminpage_obj->page_slug,
969
+				$this->_adminpage_obj->get_req_action()
970
+			),
971
+
972
+			// todo need to add a filter to the template for the help text
973
+			// in the Events_Admin_Page core file so we can add further help
974
+			'add_new_dtt_help_link'    => EEH_Template::get_help_tab_link(
975
+				'add_new_dtt_info',
976
+				$this->_adminpage_obj->page_slug,
977
+				$this->_adminpage_obj->get_req_action()
978
+			),
979
+			// todo need to add this help info id to the Events_Admin_Page core file so we can access it here.
980
+			'datetime_rows'            => '',
981
+			'show_tickets_container'   => '',
982
+			'ticket_rows'              => '',
983
+			'ee_collapsible_status'    => ' ee-collapsible-open'
984
+			// $this->_adminpage_obj->get_cpt_model_obj()->ID() > 0 ? ' ee-collapsible-closed' : ' ee-collapsible-open'
985
+		];
986
+		do_action('AHEE_log', __FILE__, __FUNCTION__, '');
987
+
988
+		/**
989
+		 * 1. Start with retrieving Datetimes
990
+		 * 2. For each datetime get related tickets
991
+		 * 3. For each ticket get related prices
992
+		 */
993
+		$datetimes                            = $datetime_model->get_all_event_dates($EVT_ID);
994
+		$main_template_args['total_dtt_rows'] = count($datetimes);
995
+
996
+		/**
997
+		 * @see https://events.codebasehq.com/projects/event-espresso/tickets/9486
998
+		 * for why we are counting $datetime_row and then setting that on the Datetime object
999
+		 */
1000
+		$datetime_row = 1;
1001
+		foreach ($datetimes as $datetime) {
1002
+			$DTT_ID = $datetime->get('DTT_ID');
1003
+			$datetime->set('DTT_order', $datetime_row);
1004
+			$existing_datetime_ids[] = $DTT_ID;
1005
+			// tickets attached
1006
+			$related_tickets = $datetime->ID() > 0
1007
+				? $datetime->get_many_related(
1008
+					'Ticket',
1009
+					[
1010
+						[
1011
+							'OR' => ['TKT_deleted' => 1, 'TKT_deleted*' => 0],
1012
+						],
1013
+						'default_where_conditions' => 'none',
1014
+						'order_by'                 => ['TKT_order' => 'ASC'],
1015
+					]
1016
+				)
1017
+				: [];
1018
+			// if there are no related tickets this is likely a new event OR auto-draft
1019
+			// event so we need to generate the default tickets because datetimes
1020
+			// ALWAYS have at least one related ticket!!.  EXCEPT, we dont' do this if there is already more than one
1021
+			// datetime on the event.
1022
+			if (empty($related_tickets) && count($datetimes) < 2) {
1023
+				$related_tickets = $ticket_model->get_all_default_tickets();
1024
+				// this should be ordered by TKT_ID, so let's grab the first default ticket
1025
+				// (which will be the main default) and ensure it has any default prices added to it (but do NOT save).
1026
+				$default_prices      = $price_model->get_all_default_prices();
1027
+				$main_default_ticket = reset($related_tickets);
1028
+				if ($main_default_ticket instanceof EE_Ticket) {
1029
+					foreach ($default_prices as $default_price) {
1030
+						if ($default_price instanceof EE_Price && $default_price->is_base_price()) {
1031
+							continue;
1032
+						}
1033
+						$main_default_ticket->cache('Price', $default_price);
1034
+					}
1035
+				}
1036
+			}
1037
+			// we can't actually setup rows in this loop yet cause we don't know all
1038
+			// the unique tickets for this event yet (tickets are linked through all datetimes).
1039
+			// So we're going to temporarily cache some of that information.
1040
+			// loop through and setup the ticket rows and make sure the order is set.
1041
+			foreach ($related_tickets as $ticket) {
1042
+				$TKT_ID     = $ticket->get('TKT_ID');
1043
+				$ticket_row = $ticket->get('TKT_row');
1044
+				// we only want unique tickets in our final display!!
1045
+				if (! in_array($TKT_ID, $existing_ticket_ids, true)) {
1046
+					$existing_ticket_ids[] = $TKT_ID;
1047
+					$all_tickets[]         = $ticket;
1048
+				}
1049
+				// temporary cache of this ticket info for this datetime for later processing of datetime rows.
1050
+				$datetime_tickets[ $DTT_ID ][] = $ticket_row;
1051
+				// temporary cache of this datetime info for this ticket for later processing of ticket rows.
1052
+				if (
1053
+					! isset($ticket_datetimes[ $TKT_ID ])
1054
+					|| ! in_array($datetime_row, $ticket_datetimes[ $TKT_ID ], true)
1055
+				) {
1056
+					$ticket_datetimes[ $TKT_ID ][] = $datetime_row;
1057
+				}
1058
+			}
1059
+			$datetime_row++;
1060
+		}
1061
+		$main_template_args['total_ticket_rows']     = count($existing_ticket_ids);
1062
+		$main_template_args['existing_ticket_ids']   = implode(',', $existing_ticket_ids);
1063
+		$main_template_args['existing_datetime_ids'] = implode(',', $existing_datetime_ids);
1064
+		// sort $all_tickets by order
1065
+		usort(
1066
+			$all_tickets,
1067
+			function (EE_Ticket $a, EE_Ticket $b) {
1068
+				$a_order = (int) $a->get('TKT_order');
1069
+				$b_order = (int) $b->get('TKT_order');
1070
+				if ($a_order === $b_order) {
1071
+					return 0;
1072
+				}
1073
+				return ($a_order < $b_order) ? -1 : 1;
1074
+			}
1075
+		);
1076
+		// k NOW we have all the data we need for setting up the datetime rows
1077
+		// and ticket rows so we start our datetime loop again.
1078
+		$datetime_row = 1;
1079
+		foreach ($datetimes as $datetime) {
1080
+			$main_template_args['datetime_rows'] .= $this->_get_datetime_row(
1081
+				$datetime_row,
1082
+				$datetime,
1083
+				$datetime_tickets,
1084
+				$all_tickets,
1085
+				false,
1086
+				$datetimes
1087
+			);
1088
+			$datetime_row++;
1089
+		}
1090
+		// then loop through all tickets for the ticket rows.
1091
+		$ticket_row = 1;
1092
+		foreach ($all_tickets as $ticket) {
1093
+			$main_template_args['ticket_rows'] .= $this->_get_ticket_row(
1094
+				$ticket_row,
1095
+				$ticket,
1096
+				$ticket_datetimes,
1097
+				$datetimes,
1098
+				false,
1099
+				$all_tickets
1100
+			);
1101
+			$ticket_row++;
1102
+		}
1103
+		$main_template_args['ticket_js_structure'] = $this->_get_ticket_js_structure($datetimes, $all_tickets);
1104
+
1105
+		$status_change_notice = LoaderFactory::getLoader()->getShared(
1106
+			'EventEspresso\core\domain\services\admin\notices\status_change\StatusChangeNotice'
1107
+		);
1108
+
1109
+		$main_template_args['status_change_notice'] = $status_change_notice->display(
1110
+			'__event-editor',
1111
+			'espresso-events'
1112
+		);
1113
+
1114
+		EEH_Template::display_template(
1115
+			PRICING_TEMPLATE_PATH . 'event_tickets_metabox_main.template.php',
1116
+			$main_template_args
1117
+		);
1118
+	}
1119
+
1120
+
1121
+	/**
1122
+	 * @param int|string  $datetime_row
1123
+	 * @param EE_Datetime $datetime
1124
+	 * @param array       $datetime_tickets
1125
+	 * @param array       $all_tickets
1126
+	 * @param bool        $default
1127
+	 * @param array       $all_datetimes
1128
+	 * @return string
1129
+	 * @throws DomainException
1130
+	 * @throws EE_Error
1131
+	 * @throws ReflectionException
1132
+	 */
1133
+	protected function _get_datetime_row(
1134
+		$datetime_row,
1135
+		EE_Datetime $datetime,
1136
+		array $datetime_tickets = [],
1137
+		array $all_tickets = [],
1138
+		bool $default = false,
1139
+		array $all_datetimes = []
1140
+	): string {
1141
+		return EEH_Template::display_template(
1142
+			PRICING_TEMPLATE_PATH . 'event_tickets_datetime_row_wrapper.template.php',
1143
+			[
1144
+				'dtt_edit_row'             => $this->_get_dtt_edit_row(
1145
+					$datetime_row,
1146
+					$datetime,
1147
+					$default,
1148
+					$all_datetimes
1149
+				),
1150
+				'dtt_attached_tickets_row' => $this->_get_dtt_attached_tickets_row(
1151
+					$datetime_row,
1152
+					$datetime,
1153
+					$datetime_tickets,
1154
+					$all_tickets,
1155
+					$default
1156
+				),
1157
+				'dtt_row'                  => $default ? 'DTTNUM' : $datetime_row,
1158
+			],
1159
+			true
1160
+		);
1161
+	}
1162
+
1163
+
1164
+	/**
1165
+	 * This method is used to generate a datetime fields  edit row.
1166
+	 * The same row is used to generate a row with valid DTT objects
1167
+	 * and the default row that is used as the skeleton by the js.
1168
+	 *
1169
+	 * @param int|string       $datetime_row  The row number for the row being generated.
1170
+	 * @param EE_Datetime|null $datetime
1171
+	 * @param bool             $default       Whether a default row is being generated or not.
1172
+	 * @param EE_Datetime[]    $all_datetimes This is the array of all datetimes used in the editor.
1173
+	 * @return string
1174
+	 * @throws EE_Error
1175
+	 * @throws ReflectionException
1176
+	 */
1177
+	protected function _get_dtt_edit_row(
1178
+		$datetime_row,
1179
+		?EE_Datetime $datetime,
1180
+		bool $default,
1181
+		array $all_datetimes
1182
+	): string {
1183
+		// if the incoming $datetime object is NOT an instance of EE_Datetime then force default to true.
1184
+		$default                     = ! $datetime instanceof EE_Datetime ? true : $default;
1185
+		$template_args               = [
1186
+			'dtt_row'              => $default ? 'DTTNUM' : $datetime_row,
1187
+			'event_datetimes_name' => $default ? 'DTTNAMEATTR' : 'edit_event_datetimes',
1188
+			'edit_dtt_expanded'    => '',
1189
+			'DTT_ID'               => $default ? '' : $datetime->ID(),
1190
+			'DTT_name'             => $default ? '' : $datetime->get_f('DTT_name'),
1191
+			'DTT_description'      => $default ? '' : $datetime->get_f('DTT_description'),
1192
+			'DTT_EVT_start'        => $default ? '' : $datetime->start_date($this->_date_time_format),
1193
+			'DTT_EVT_end'          => $default ? '' : $datetime->end_date($this->_date_time_format),
1194
+			'DTT_reg_limit'        => $default
1195
+				? ''
1196
+				: $datetime->get_pretty(
1197
+					'DTT_reg_limit',
1198
+					'input'
1199
+				),
1200
+			'DTT_order'            => $default ? 'DTTNUM' : $datetime_row,
1201
+			'dtt_sold'             => $default ? '0' : $datetime->get('DTT_sold'),
1202
+			'dtt_reserved'         => $default ? '0' : $datetime->reserved(),
1203
+			'clone_icon'           => ! empty($datetime) && $datetime->get('DTT_sold') > 0
1204
+				? ''
1205
+				: 'clone-icon ee-icon ee-icon-clone clickable',
1206
+			'trash_icon'           => ! empty($datetime) && $datetime->get('DTT_sold') > 0
1207
+				? 'dashicons dashicons-lock'
1208
+				: 'trash-icon dashicons dashicons-post-trash clickable',
1209
+			'reg_list_url'         => $default || ! $datetime->event() instanceof EE_Event
1210
+				? ''
1211
+				: EE_Admin_Page::add_query_args_and_nonce(
1212
+					[
1213
+						'event_id'    => $datetime->event()->ID(),
1214
+						'datetime_id' => $datetime->ID(),
1215
+						'use_filters' => true,
1216
+					],
1217
+					REG_ADMIN_URL
1218
+				),
1219
+		];
1220
+		$template_args['show_trash'] = count($all_datetimes) === 1
1221
+									   && $template_args['trash_icon'] !== 'dashicons dashicons-lock'
1222
+			? 'display:none'
1223
+			: '';
1224
+		// allow filtering of template args at this point.
1225
+		$template_args = apply_filters(
1226
+			'FHEE__espresso_events_Pricing_Hooks___get_dtt_edit_row__template_args',
1227
+			$template_args,
1228
+			$datetime_row,
1229
+			$datetime,
1230
+			$default,
1231
+			$all_datetimes,
1232
+			$this->_is_creating_event
1233
+		);
1234
+		return EEH_Template::display_template(
1235
+			PRICING_TEMPLATE_PATH . 'event_tickets_datetime_edit_row.template.php',
1236
+			$template_args,
1237
+			true
1238
+		);
1239
+	}
1240
+
1241
+
1242
+	/**
1243
+	 * @param int|string       $datetime_row
1244
+	 * @param EE_Datetime|null $datetime
1245
+	 * @param array            $datetime_tickets
1246
+	 * @param array            $all_tickets
1247
+	 * @param bool             $default
1248
+	 * @return string
1249
+	 * @throws DomainException
1250
+	 * @throws EE_Error
1251
+	 * @throws ReflectionException
1252
+	 */
1253
+	protected function _get_dtt_attached_tickets_row(
1254
+		$datetime_row,
1255
+		?EE_Datetime $datetime,
1256
+		array $datetime_tickets = [],
1257
+		array $all_tickets = [],
1258
+		bool $default = false
1259
+	): string {
1260
+		$template_args = [
1261
+			'dtt_row'                           => $default ? 'DTTNUM' : $datetime_row,
1262
+			'event_datetimes_name'              => $default ? 'DTTNAMEATTR' : 'edit_event_datetimes',
1263
+			'DTT_description'                   => $default ? '' : $datetime->get_f('DTT_description'),
1264
+			'datetime_tickets_list'             => $default ? '<li class="hidden"></li>' : '',
1265
+			'show_tickets_row'                  => 'display:none;',
1266
+			'add_new_datetime_ticket_help_link' => EEH_Template::get_help_tab_link(
1267
+				'add_new_ticket_via_datetime',
1268
+				$this->_adminpage_obj->page_slug,
1269
+				$this->_adminpage_obj->get_req_action()
1270
+			),
1271
+			// todo need to add this help info id to the Events_Admin_Page core file so we can access it here.
1272
+			'DTT_ID'                            => $default ? '' : $datetime->ID(),
1273
+		];
1274
+		// need to setup the list items (but only if this isn't a default skeleton setup)
1275
+		if (! $default) {
1276
+			$ticket_row = 1;
1277
+			foreach ($all_tickets as $ticket) {
1278
+				$template_args['datetime_tickets_list'] .= $this->_get_datetime_tickets_list_item(
1279
+					$datetime_row,
1280
+					$ticket_row,
1281
+					$datetime,
1282
+					$ticket,
1283
+					$datetime_tickets
1284
+				);
1285
+				$ticket_row++;
1286
+			}
1287
+		}
1288
+		// filter template args at this point
1289
+		$template_args = apply_filters(
1290
+			'FHEE__espresso_events_Pricing_Hooks___get_dtt_attached_ticket_row__template_args',
1291
+			$template_args,
1292
+			$datetime_row,
1293
+			$datetime,
1294
+			$datetime_tickets,
1295
+			$all_tickets,
1296
+			$default,
1297
+			$this->_is_creating_event
1298
+		);
1299
+		return EEH_Template::display_template(
1300
+			PRICING_TEMPLATE_PATH . 'event_tickets_datetime_attached_tickets_row.template.php',
1301
+			$template_args,
1302
+			true
1303
+		);
1304
+	}
1305
+
1306
+
1307
+	/**
1308
+	 * @param int|string       $datetime_row
1309
+	 * @param int|string       $ticket_row
1310
+	 * @param EE_Datetime|null $datetime
1311
+	 * @param EE_Ticket|null   $ticket
1312
+	 * @param array            $datetime_tickets
1313
+	 * @param bool             $default
1314
+	 * @return string
1315
+	 * @throws EE_Error
1316
+	 * @throws ReflectionException
1317
+	 */
1318
+	protected function _get_datetime_tickets_list_item(
1319
+		$datetime_row,
1320
+		$ticket_row,
1321
+		?EE_Datetime $datetime,
1322
+		?EE_Ticket $ticket,
1323
+		array $datetime_tickets = [],
1324
+		bool $default = false
1325
+	): string {
1326
+		$datetime_tickets = $datetime instanceof EE_Datetime && isset($datetime_tickets[ $datetime->ID() ])
1327
+			? $datetime_tickets[ $datetime->ID() ]
1328
+			: [];
1329
+		$display_row      = $ticket instanceof EE_Ticket ? $ticket->get('TKT_row') : 0;
1330
+		$no_ticket        = $default && empty($ticket);
1331
+		$template_args    = [
1332
+			'dtt_row'                 => $default
1333
+				? 'DTTNUM'
1334
+				: $datetime_row,
1335
+			'tkt_row'                 => $no_ticket
1336
+				? 'TICKETNUM'
1337
+				: $ticket_row,
1338
+			'datetime_ticket_checked' => in_array($display_row, $datetime_tickets, true)
1339
+				? ' checked'
1340
+				: '',
1341
+			'ticket_selected'         => in_array($display_row, $datetime_tickets, true)
1342
+				? ' ticket-selected'
1343
+				: '',
1344
+			'TKT_name'                => $no_ticket
1345
+				? 'TKTNAME'
1346
+				: $ticket->get('TKT_name'),
1347
+			'tkt_status_class'        => $no_ticket || $this->_is_creating_event
1348
+				? ' tkt-status-' . EE_Ticket::onsale
1349
+				: ' tkt-status-' . $ticket->ticket_status(),
1350
+		];
1351
+		// filter template args
1352
+		$template_args = apply_filters(
1353
+			'FHEE__espresso_events_Pricing_Hooks___get_datetime_tickets_list_item__template_args',
1354
+			$template_args,
1355
+			$datetime_row,
1356
+			$ticket_row,
1357
+			$datetime,
1358
+			$ticket,
1359
+			$datetime_tickets,
1360
+			$default,
1361
+			$this->_is_creating_event
1362
+		);
1363
+		return EEH_Template::display_template(
1364
+			PRICING_TEMPLATE_PATH . 'event_tickets_datetime_dtt_tickets_list.template.php',
1365
+			$template_args,
1366
+			true
1367
+		);
1368
+	}
1369
+
1370
+
1371
+	/**
1372
+	 * This generates the ticket row for tickets.
1373
+	 * This same method is used to generate both the actual rows and the js skeleton row
1374
+	 * (when default === true)
1375
+	 *
1376
+	 * @param int|string     $ticket_row       Represents the row number being generated.
1377
+	 * @param EE_Ticket|null $ticket
1378
+	 * @param EE_Datetime[]  $ticket_datetimes Either an array of all datetimes on all tickets indexed by each ticket
1379
+	 *                                         or empty for default
1380
+	 * @param EE_Datetime[]  $all_datetimes    All Datetimes on the event or empty for default.
1381
+	 * @param bool           $default          Whether default row being generated or not.
1382
+	 * @param EE_Ticket[]    $all_tickets      This is an array of all tickets attached to the event
1383
+	 *                                         (or empty in the case of defaults)
1384
+	 * @return string
1385
+	 * @throws InvalidArgumentException
1386
+	 * @throws InvalidInterfaceException
1387
+	 * @throws InvalidDataTypeException
1388
+	 * @throws DomainException
1389
+	 * @throws EE_Error
1390
+	 * @throws ReflectionException
1391
+	 */
1392
+	protected function _get_ticket_row(
1393
+		$ticket_row,
1394
+		?EE_Ticket $ticket,
1395
+		array $ticket_datetimes,
1396
+		array $all_datetimes,
1397
+		bool $default = false,
1398
+		array $all_tickets = []
1399
+	): string {
1400
+		// if $ticket is not an instance of EE_Ticket then force default to true.
1401
+		$default = ! $ticket instanceof EE_Ticket ? true : $default;
1402
+		$prices  = ! empty($ticket) && ! $default
1403
+			? $ticket->get_many_related(
1404
+				'Price',
1405
+				['default_where_conditions' => 'none', 'order_by' => ['PRC_order' => 'ASC']]
1406
+			)
1407
+			: [];
1408
+		// if there is only one price (which would be the base price)
1409
+		// or NO prices and this ticket is a default ticket,
1410
+		// let's just make sure there are no cached default prices on the object.
1411
+		// This is done by not including any query_params.
1412
+		if ($ticket instanceof EE_Ticket && $ticket->is_default() && (count($prices) === 1 || empty($prices))) {
1413
+			$prices = $ticket->prices();
1414
+		}
1415
+		// check if we're dealing with a default ticket in which case
1416
+		// we don't want any starting_ticket_datetime_row values set
1417
+		// (otherwise there won't be any new relationships created for tickets based off of the default ticket).
1418
+		// This will future proof in case there is ever any behaviour change between what the primary_key defaults to.
1419
+		$default_datetime = $default || ($ticket instanceof EE_Ticket && $ticket->is_default());
1420
+		$ticket_datetimes = $ticket instanceof EE_Ticket && isset($ticket_datetimes[ $ticket->ID() ])
1421
+			? $ticket_datetimes[ $ticket->ID() ]
1422
+			: [];
1423
+		$ticket_subtotal  = $default ? 0 : $ticket->get_ticket_subtotal();
1424
+		$base_price       = $default ? null : $ticket->base_price();
1425
+		$count_price_mods = EEM_Price::instance()->get_all_default_prices(true);
1426
+		// breaking out complicated condition for ticket_status
1427
+		if ($default) {
1428
+			$ticket_status_class = ' tkt-status-' . EE_Ticket::onsale;
1429
+		} else {
1430
+			$ticket_status_class = $ticket->is_default()
1431
+				? ' tkt-status-' . EE_Ticket::onsale
1432
+				: ' tkt-status-' . $ticket->ticket_status();
1433
+		}
1434
+		// breaking out complicated condition for TKT_taxable
1435
+		if ($default) {
1436
+			$TKT_taxable = '';
1437
+		} else {
1438
+			$TKT_taxable = $ticket->taxable()
1439
+				? 'checked'
1440
+				: '';
1441
+		}
1442
+		if ($default) {
1443
+			$TKT_status = EEH_Template::pretty_status(EE_Ticket::onsale, false, 'sentence');
1444
+		} elseif ($ticket->is_default()) {
1445
+			$TKT_status = EEH_Template::pretty_status(EE_Ticket::onsale, false, 'sentence');
1446
+		} else {
1447
+			$TKT_status = $ticket->ticket_status(true);
1448
+		}
1449
+		if ($default) {
1450
+			$TKT_min = '';
1451
+		} else {
1452
+			$TKT_min = $ticket->min();
1453
+			if ($TKT_min === -1 || $TKT_min === 0) {
1454
+				$TKT_min = '';
1455
+			}
1456
+		}
1457
+		$template_args                 = [
1458
+			'tkt_row'                       => $default ? 'TICKETNUM' : $ticket_row,
1459
+			'TKT_order'                     => $default ? 'TICKETNUM' : $ticket_row,
1460
+			// on initial page load this will always be the correct order.
1461
+			'tkt_status_class'              => $ticket_status_class,
1462
+			'display_edit_tkt_row'          => 'display:none;',
1463
+			'edit_tkt_expanded'             => '',
1464
+			'edit_tickets_name'             => $default ? 'TICKETNAMEATTR' : 'edit_tickets',
1465
+			'TKT_name'                      => $default ? '' : $ticket->get_f('TKT_name'),
1466
+			'TKT_start_date'                => $default
1467
+				? ''
1468
+				: $ticket->get_date('TKT_start_date', $this->_date_time_format),
1469
+			'TKT_end_date'                  => $default
1470
+				? ''
1471
+				: $ticket->get_date('TKT_end_date', $this->_date_time_format),
1472
+			'TKT_status'                    => $TKT_status,
1473
+			'TKT_price'                     => $default
1474
+				? ''
1475
+				: EEH_Template::format_currency(
1476
+					$ticket->get_ticket_total_with_taxes(),
1477
+					false,
1478
+					false
1479
+				),
1480
+			'TKT_price_code'                => EE_Registry::instance()->CFG->currency->code,
1481
+			'TKT_price_amount'              => $default ? 0 : $ticket_subtotal,
1482
+			'TKT_qty'                       => $default
1483
+				? ''
1484
+				: $ticket->get_pretty('TKT_qty', 'symbol'),
1485
+			'TKT_qty_for_input'             => $default
1486
+				? ''
1487
+				: $ticket->get_pretty('TKT_qty', 'input'),
1488
+			'TKT_uses'                      => $default
1489
+				? ''
1490
+				: $ticket->get_pretty('TKT_uses', 'input'),
1491
+			'TKT_min'                       => $TKT_min,
1492
+			'TKT_max'                       => $default
1493
+				? ''
1494
+				: $ticket->get_pretty('TKT_max', 'input'),
1495
+			'TKT_sold'                      => $default ? 0 : $ticket->tickets_sold(),
1496
+			'TKT_reserved'                  => $default ? 0 : $ticket->reserved(),
1497
+			'TKT_registrations'             => $default
1498
+				? 0
1499
+				: $ticket->count_registrations(
1500
+					[
1501
+						[
1502
+							'STS_ID' => [
1503
+								'!=',
1504
+								EEM_Registration::status_id_incomplete,
1505
+							],
1506
+						],
1507
+					]
1508
+				),
1509
+			'TKT_ID'                        => $default ? 0 : $ticket->ID(),
1510
+			'TKT_description'               => $default ? '' : $ticket->get_raw('TKT_description'),
1511
+			'TKT_is_default'                => $default ? 0 : $ticket->is_default(),
1512
+			'TKT_required'                  => $default ? 0 : $ticket->required(),
1513
+			'TKT_is_default_selector'       => '',
1514
+			'ticket_price_rows'             => '',
1515
+			'TKT_base_price'                => $default || ! $base_price instanceof EE_Price
1516
+				? ''
1517
+				: $base_price->get_pretty('PRC_amount', 'localized_float'),
1518
+			'TKT_base_price_ID'             => $default || ! $base_price instanceof EE_Price ? 0 : $base_price->ID(),
1519
+			'show_price_modifier'           => count($prices) > 1 || ($default && $count_price_mods > 0)
1520
+				? ''
1521
+				: 'display:none;',
1522
+			'show_price_mod_button'         => count($prices) > 1
1523
+											   || ($default && $count_price_mods > 0)
1524
+											   || (! $default && $ticket->deleted())
1525
+				? 'display:none;'
1526
+				: '',
1527
+			'total_price_rows'              => count($prices) > 1 ? count($prices) : 1,
1528
+			'ticket_datetimes_list'         => $default ? '<li class="hidden"></li>' : '',
1529
+			'starting_ticket_datetime_rows' => $default || $default_datetime ? '' : implode(',', $ticket_datetimes),
1530
+			'ticket_datetime_rows'          => $default ? '' : implode(',', $ticket_datetimes),
1531
+			'existing_ticket_price_ids'     => $default ? '' : implode(',', array_keys($prices)),
1532
+			'ticket_template_id'            => $default ? 0 : $ticket->get('TTM_ID'),
1533
+			'TKT_taxable'                   => $TKT_taxable,
1534
+			'display_subtotal'              => $ticket instanceof EE_Ticket && $ticket->taxable()
1535
+				? ''
1536
+				: 'display:none;',
1537
+			'price_currency_symbol'         => EE_Registry::instance()->CFG->currency->sign,
1538
+			'TKT_subtotal_amount_display'   => EEH_Template::format_currency(
1539
+				$ticket_subtotal,
1540
+				false,
1541
+				false
1542
+			),
1543
+			'TKT_subtotal_amount'           => $ticket_subtotal,
1544
+			'tax_rows'                      => $this->_get_tax_rows($ticket_row, $ticket),
1545
+			'disabled'                      => $ticket instanceof EE_Ticket && $ticket->deleted(),
1546
+			'ticket_archive_class'          => $ticket instanceof EE_Ticket && $ticket->deleted()
1547
+				? ' ticket-archived'
1548
+				: '',
1549
+			'trash_icon'                    => $ticket instanceof EE_Ticket
1550
+											   && $ticket->deleted()
1551
+											   && ! $ticket->is_permanently_deleteable()
1552
+				? 'dashicons dashicons-lock '
1553
+				: 'trash-icon dashicons dashicons-post-trash clickable',
1554
+			'clone_icon'                    => $ticket instanceof EE_Ticket && $ticket->deleted()
1555
+				? ''
1556
+				: 'clone-icon ee-icon ee-icon-clone clickable',
1557
+		];
1558
+		$template_args['trash_hidden'] = count($all_tickets) === 1
1559
+										 && $template_args['trash_icon'] !== 'dashicons dashicons-lock'
1560
+			? 'display:none'
1561
+			: '';
1562
+		// handle rows that should NOT be empty
1563
+		if (empty($template_args['TKT_start_date'])) {
1564
+			// if empty then the start date will be now.
1565
+			$template_args['TKT_start_date']   = date(
1566
+				$this->_date_time_format,
1567
+				current_time('timestamp')
1568
+			);
1569
+			$template_args['tkt_status_class'] = ' tkt-status-' . EE_Ticket::onsale;
1570
+		}
1571
+		if (empty($template_args['TKT_end_date'])) {
1572
+			// get the earliest datetime (if present);
1573
+			$earliest_datetime = $this->_adminpage_obj->get_cpt_model_obj()->ID() > 0
1574
+				? $this->_adminpage_obj->get_cpt_model_obj()->get_first_related(
1575
+					'Datetime',
1576
+					['order_by' => ['DTT_EVT_start' => 'ASC']]
1577
+				)
1578
+				: null;
1579
+			if (! empty($earliest_datetime)) {
1580
+				$template_args['TKT_end_date'] = $earliest_datetime->get_datetime(
1581
+					'DTT_EVT_start',
1582
+					$this->_date_time_format
1583
+				);
1584
+			} else {
1585
+				// default so let's just use what's been set for the default date-time which is 30 days from now.
1586
+				$template_args['TKT_end_date'] = date(
1587
+					$this->_date_time_format,
1588
+					mktime(
1589
+						24,
1590
+						0,
1591
+						0,
1592
+						date('m'),
1593
+						date('d') + 29,
1594
+						date('Y')
1595
+					)
1596
+				);
1597
+			}
1598
+			$template_args['tkt_status_class'] = ' tkt-status-' . EE_Ticket::onsale;
1599
+		}
1600
+		// generate ticket_datetime items
1601
+		if (! $default) {
1602
+			$datetime_row = 1;
1603
+			foreach ($all_datetimes as $datetime) {
1604
+				$template_args['ticket_datetimes_list'] .= $this->_get_ticket_datetime_list_item(
1605
+					$datetime_row,
1606
+					$ticket_row,
1607
+					$datetime,
1608
+					$ticket,
1609
+					$ticket_datetimes,
1610
+					$default
1611
+				);
1612
+				$datetime_row++;
1613
+			}
1614
+		}
1615
+		$price_row = 1;
1616
+		foreach ($prices as $price) {
1617
+			if (! $price instanceof EE_Price) {
1618
+				continue;
1619
+			}
1620
+			if ($price->is_base_price()) {
1621
+				$price_row++;
1622
+				continue;
1623
+			}
1624
+
1625
+			$show_trash  = ! ((count($prices) > 1 && $price_row === 1) || count($prices) === 1);
1626
+			$show_create = ! (count($prices) > 1 && count($prices) !== $price_row);
1627
+
1628
+			$template_args['ticket_price_rows'] .= $this->_get_ticket_price_row(
1629
+				$ticket_row,
1630
+				$price_row,
1631
+				$price,
1632
+				$default,
1633
+				$ticket,
1634
+				$show_trash,
1635
+				$show_create
1636
+			);
1637
+			$price_row++;
1638
+		}
1639
+		// filter $template_args
1640
+		$template_args = apply_filters(
1641
+			'FHEE__espresso_events_Pricing_Hooks___get_ticket_row__template_args',
1642
+			$template_args,
1643
+			$ticket_row,
1644
+			$ticket,
1645
+			$ticket_datetimes,
1646
+			$all_datetimes,
1647
+			$default,
1648
+			$all_tickets,
1649
+			$this->_is_creating_event
1650
+		);
1651
+		return EEH_Template::display_template(
1652
+			PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_row.template.php',
1653
+			$template_args,
1654
+			true
1655
+		);
1656
+	}
1657
+
1658
+
1659
+	/**
1660
+	 * @param int|string     $ticket_row
1661
+	 * @param EE_Ticket|null $ticket
1662
+	 * @return string
1663
+	 * @throws DomainException
1664
+	 * @throws EE_Error
1665
+	 * @throws ReflectionException
1666
+	 */
1667
+	protected function _get_tax_rows($ticket_row, ?EE_Ticket $ticket): string
1668
+	{
1669
+		$tax_rows = '';
1670
+		/** @var EE_Price[] $taxes */
1671
+		$taxes = empty($ticket) ? EE_Taxes::get_taxes_for_admin() : $ticket->get_ticket_taxes_for_admin();
1672
+		foreach ($taxes as $tax) {
1673
+			$tax_added     = $this->_get_tax_added($tax, $ticket);
1674
+			$template_args = [
1675
+				'display_tax'       => ! empty($ticket) && $ticket->get('TKT_taxable')
1676
+					? ''
1677
+					: 'display:none;',
1678
+				'tax_id'            => $tax->ID(),
1679
+				'tkt_row'           => $ticket_row,
1680
+				'tax_label'         => $tax->get('PRC_name'),
1681
+				'tax_added'         => $tax_added,
1682
+				'tax_added_display' => EEH_Template::format_currency($tax_added, false, false),
1683
+				'tax_amount'        => $tax->get('PRC_amount'),
1684
+			];
1685
+			$template_args = apply_filters(
1686
+				'FHEE__espresso_events_Pricing_Hooks___get_tax_rows__template_args',
1687
+				$template_args,
1688
+				$ticket_row,
1689
+				$ticket,
1690
+				$this->_is_creating_event
1691
+			);
1692
+			$tax_rows      .= EEH_Template::display_template(
1693
+				PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_tax_row.template.php',
1694
+				$template_args,
1695
+				true
1696
+			);
1697
+		}
1698
+		return $tax_rows;
1699
+	}
1700
+
1701
+
1702
+	/**
1703
+	 * @param EE_Price       $tax
1704
+	 * @param EE_Ticket|null $ticket
1705
+	 * @return float|int
1706
+	 * @throws EE_Error
1707
+	 * @throws ReflectionException
1708
+	 */
1709
+	protected function _get_tax_added(EE_Price $tax, ?EE_Ticket $ticket)
1710
+	{
1711
+		$subtotal = empty($ticket) ? 0 : $ticket->get_ticket_subtotal();
1712
+		return $subtotal * $tax->get('PRC_amount') / 100;
1713
+	}
1714
+
1715
+
1716
+	/**
1717
+	 * @param int|string     $ticket_row
1718
+	 * @param int|string     $price_row
1719
+	 * @param EE_Price|null  $price
1720
+	 * @param bool           $default
1721
+	 * @param EE_Ticket|null $ticket
1722
+	 * @param bool           $show_trash
1723
+	 * @param bool           $show_create
1724
+	 * @return string
1725
+	 * @throws InvalidArgumentException
1726
+	 * @throws InvalidInterfaceException
1727
+	 * @throws InvalidDataTypeException
1728
+	 * @throws DomainException
1729
+	 * @throws EE_Error
1730
+	 * @throws ReflectionException
1731
+	 */
1732
+	protected function _get_ticket_price_row(
1733
+		$ticket_row,
1734
+		$price_row,
1735
+		?EE_Price $price,
1736
+		bool $default,
1737
+		?EE_Ticket $ticket,
1738
+		bool $show_trash = true,
1739
+		bool $show_create = true
1740
+	): string {
1741
+		$send_disabled = ! empty($ticket) && $ticket->get('TKT_deleted');
1742
+		$template_args = [
1743
+			'tkt_row'               => $default && empty($ticket)
1744
+				? 'TICKETNUM'
1745
+				: $ticket_row,
1746
+			'PRC_order'             => $default && empty($price)
1747
+				? 'PRICENUM'
1748
+				: $price_row,
1749
+			'edit_prices_name'      => $default && empty($price)
1750
+				? 'PRICENAMEATTR'
1751
+				: 'edit_prices',
1752
+			'price_type_selector'   => $default && empty($price)
1753
+				? $this->_get_base_price_template($ticket_row, $price_row, $price, true)
1754
+				: $this->_get_price_type_selector(
1755
+					$ticket_row,
1756
+					$price_row,
1757
+					$price,
1758
+					$default,
1759
+					$send_disabled
1760
+				),
1761
+			'PRC_ID'                => $default && empty($price)
1762
+				? 0
1763
+				: $price->ID(),
1764
+			'PRC_is_default'        => $default && empty($price)
1765
+				? 0
1766
+				: $price->get('PRC_is_default'),
1767
+			'PRC_name'              => $default && empty($price)
1768
+				? ''
1769
+				: $price->get('PRC_name'),
1770
+			'price_currency_symbol' => EE_Registry::instance()->CFG->currency->sign,
1771
+			'show_plus_or_minus'    => $default && empty($price)
1772
+				? ''
1773
+				: 'display:none;',
1774
+			'show_plus'             => ($default && empty($price)) || ($price->is_discount() || $price->is_base_price())
1775
+				? 'display:none;'
1776
+				: '',
1777
+			'show_minus'            => ($default && empty($price)) || ! $price->is_discount()
1778
+				? 'display:none;'
1779
+				: '',
1780
+			'show_currency_symbol'  => ($default && empty($price)) || $price->is_percent()
1781
+				? 'display:none'
1782
+				: '',
1783
+			'PRC_amount'            => $default && empty($price)
1784
+				? 0
1785
+				: $price->get_pretty('PRC_amount', 'localized_float'),
1786
+			'show_percentage'       => ($default && empty($price)) || ! $price->is_percent()
1787
+				? 'display:none;'
1788
+				: '',
1789
+			'show_trash_icon'       => $show_trash
1790
+				? ''
1791
+				: ' style="display:none;"',
1792
+			'show_create_button'    => $show_create
1793
+				? ''
1794
+				: ' style="display:none;"',
1795
+			'PRC_desc'              => $default && empty($price)
1796
+				? ''
1797
+				: $price->get('PRC_desc'),
1798
+			'disabled'              => ! empty($ticket) && $ticket->get('TKT_deleted'),
1799
+		];
1800
+		$template_args = apply_filters(
1801
+			'FHEE__espresso_events_Pricing_Hooks___get_ticket_price_row__template_args',
1802
+			$template_args,
1803
+			$ticket_row,
1804
+			$price_row,
1805
+			$price,
1806
+			$default,
1807
+			$ticket,
1808
+			$show_trash,
1809
+			$show_create,
1810
+			$this->_is_creating_event
1811
+		);
1812
+		return EEH_Template::display_template(
1813
+			PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_price_row.template.php',
1814
+			$template_args,
1815
+			true
1816
+		);
1817
+	}
1818
+
1819
+
1820
+	/**
1821
+	 * @param int|string    $ticket_row
1822
+	 * @param int|string    $price_row
1823
+	 * @param EE_Price|null $price
1824
+	 * @param bool          $default
1825
+	 * @param bool          $disabled
1826
+	 * @return string
1827
+	 * @throws ReflectionException
1828
+	 * @throws InvalidArgumentException
1829
+	 * @throws InvalidInterfaceException
1830
+	 * @throws InvalidDataTypeException
1831
+	 * @throws DomainException
1832
+	 * @throws EE_Error
1833
+	 */
1834
+	protected function _get_price_type_selector(
1835
+		$ticket_row,
1836
+		$price_row,
1837
+		?EE_Price $price,
1838
+		bool $default,
1839
+		bool $disabled = false
1840
+	): string {
1841
+		if ($price->is_base_price()) {
1842
+			return $this->_get_base_price_template(
1843
+				$ticket_row,
1844
+				$price_row,
1845
+				$price,
1846
+				$default
1847
+			);
1848
+		}
1849
+		return $this->_get_price_modifier_template(
1850
+			$ticket_row,
1851
+			$price_row,
1852
+			$price,
1853
+			$default,
1854
+			$disabled
1855
+		);
1856
+	}
1857
+
1858
+
1859
+	/**
1860
+	 * @param int|string    $ticket_row
1861
+	 * @param int|string    $price_row
1862
+	 * @param EE_Price|null $price
1863
+	 * @param bool          $default
1864
+	 * @return string
1865
+	 * @throws DomainException
1866
+	 * @throws EE_Error
1867
+	 * @throws ReflectionException
1868
+	 */
1869
+	protected function _get_base_price_template(
1870
+		$ticket_row,
1871
+		$price_row,
1872
+		?EE_Price $price,
1873
+		bool $default
1874
+	): string {
1875
+		$template_args = [
1876
+			'tkt_row'                   => $default ? 'TICKETNUM' : $ticket_row,
1877
+			'PRC_order'                 => $default && empty($price) ? 'PRICENUM' : $price_row,
1878
+			'PRT_ID'                    => $default && empty($price) ? 1 : $price->get('PRT_ID'),
1879
+			'PRT_name'                  => esc_html__('Price', 'event_espresso'),
1880
+			'price_selected_operator'   => '+',
1881
+			'price_selected_is_percent' => 0,
1882
+		];
1883
+		$template_args = apply_filters(
1884
+			'FHEE__espresso_events_Pricing_Hooks___get_base_price_template__template_args',
1885
+			$template_args,
1886
+			$ticket_row,
1887
+			$price_row,
1888
+			$price,
1889
+			$default,
1890
+			$this->_is_creating_event
1891
+		);
1892
+		return EEH_Template::display_template(
1893
+			PRICING_TEMPLATE_PATH . 'event_tickets_datetime_price_type_base.template.php',
1894
+			$template_args,
1895
+			true
1896
+		);
1897
+	}
1898
+
1899
+
1900
+	/**
1901
+	 * @param int|string    $ticket_row
1902
+	 * @param int|string    $price_row
1903
+	 * @param EE_Price|null $price
1904
+	 * @param bool          $default
1905
+	 * @param bool          $disabled
1906
+	 * @return string
1907
+	 * @throws ReflectionException
1908
+	 * @throws InvalidArgumentException
1909
+	 * @throws InvalidInterfaceException
1910
+	 * @throws InvalidDataTypeException
1911
+	 * @throws DomainException
1912
+	 * @throws EE_Error
1913
+	 */
1914
+	protected function _get_price_modifier_template(
1915
+		$ticket_row,
1916
+		$price_row,
1917
+		?EE_Price $price,
1918
+		bool $default,
1919
+		bool $disabled = false
1920
+	): string {
1921
+		$select_name = $default && ! $price instanceof EE_Price
1922
+			? 'edit_prices[TICKETNUM][PRICENUM][PRT_ID]'
1923
+			: 'edit_prices[' . esc_attr($ticket_row) . '][' . esc_attr($price_row) . '][PRT_ID]';
1924
+
1925
+		$price_type_model       = EEM_Price_Type::instance();
1926
+		$price_types            = $price_type_model->get_all(
1927
+			[
1928
+				[
1929
+					'OR' => [
1930
+						'PBT_ID'  => '2',
1931
+						'PBT_ID*' => '3',
1932
+					],
1933
+				],
1934
+			]
1935
+		);
1936
+		$all_price_types        = $default && ! $price instanceof EE_Price
1937
+			? [esc_html__('Select Modifier', 'event_espresso')]
1938
+			: [];
1939
+		$selected_price_type_id = $default && ! $price instanceof EE_Price ? 0 : $price->type();
1940
+		$price_option_spans     = '';
1941
+		// setup price types for selector
1942
+		foreach ($price_types as $price_type) {
1943
+			if (! $price_type instanceof EE_Price_Type) {
1944
+				continue;
1945
+			}
1946
+			$all_price_types[ $price_type->ID() ] = $price_type->get('PRT_name');
1947
+			// while we're in the loop let's setup the option spans used by js
1948
+			$span_args          = [
1949
+				'PRT_ID'         => $price_type->ID(),
1950
+				'PRT_operator'   => $price_type->is_discount() ? '-' : '+',
1951
+				'PRT_is_percent' => $price_type->get('PRT_is_percent') ? 1 : 0,
1952
+			];
1953
+			$price_option_spans .= EEH_Template::display_template(
1954
+				PRICING_TEMPLATE_PATH . 'event_tickets_datetime_price_option_span.template.php',
1955
+				$span_args,
1956
+				true
1957
+			);
1958
+		}
1959
+
1960
+		$select_name = $disabled
1961
+			? 'archive_price[' . $ticket_row . '][' . $price_row . '][PRT_ID]'
1962
+			: $select_name;
1963
+
1964
+		$select_input = new EE_Select_Input(
1965
+			$all_price_types,
1966
+			[
1967
+				'default'               => $selected_price_type_id,
1968
+				'html_name'             => $select_name,
1969
+				'html_class'            => 'edit-price-PRT_ID',
1970
+				'other_html_attributes' => $disabled ? 'style="width:auto;" disabled' : 'style="width:auto;"',
1971
+			]
1972
+		);
1973
+
1974
+		$price_selected_operator   = $price instanceof EE_Price && $price->is_discount() ? '-' : '+';
1975
+		$price_selected_operator   = $default && ! $price instanceof EE_Price ? '' : $price_selected_operator;
1976
+		$price_selected_is_percent = $price instanceof EE_Price && $price->is_percent() ? 1 : 0;
1977
+		$price_selected_is_percent = $default && ! $price instanceof EE_Price ? '' : $price_selected_is_percent;
1978
+		$template_args             = [
1979
+			'tkt_row'                   => $default ? 'TICKETNUM' : $ticket_row,
1980
+			'PRC_order'                 => $default && ! $price instanceof EE_Price ? 'PRICENUM' : $price_row,
1981
+			'price_modifier_selector'   => $select_input->get_html_for_input(),
1982
+			'main_name'                 => $select_name,
1983
+			'selected_price_type_id'    => $selected_price_type_id,
1984
+			'price_option_spans'        => $price_option_spans,
1985
+			'price_selected_operator'   => $price_selected_operator,
1986
+			'price_selected_is_percent' => $price_selected_is_percent,
1987
+			'disabled'                  => $disabled,
1988
+		];
1989
+		$template_args             = apply_filters(
1990
+			'FHEE__espresso_events_Pricing_Hooks___get_price_modifier_template__template_args',
1991
+			$template_args,
1992
+			$ticket_row,
1993
+			$price_row,
1994
+			$price,
1995
+			$default,
1996
+			$disabled,
1997
+			$this->_is_creating_event
1998
+		);
1999
+		return EEH_Template::display_template(
2000
+			PRICING_TEMPLATE_PATH . 'event_tickets_datetime_price_modifier_selector.template.php',
2001
+			$template_args,
2002
+			true
2003
+		);
2004
+	}
2005
+
2006
+
2007
+	/**
2008
+	 * @param int|string       $datetime_row
2009
+	 * @param int|string       $ticket_row
2010
+	 * @param EE_Datetime|null $datetime
2011
+	 * @param EE_Ticket|null   $ticket
2012
+	 * @param array            $ticket_datetimes
2013
+	 * @param bool             $default
2014
+	 * @return string
2015
+	 * @throws DomainException
2016
+	 * @throws EE_Error
2017
+	 * @throws ReflectionException
2018
+	 */
2019
+	protected function _get_ticket_datetime_list_item(
2020
+		$datetime_row,
2021
+		$ticket_row,
2022
+		?EE_Datetime $datetime,
2023
+		?EE_Ticket $ticket,
2024
+		array $ticket_datetimes = [],
2025
+		bool $default = false
2026
+	): string {
2027
+		$ticket_datetimes = $ticket instanceof EE_Ticket && isset($ticket_datetimes[ $ticket->ID() ])
2028
+			? $ticket_datetimes[ $ticket->ID() ]
2029
+			: [];
2030
+		$template_args    = [
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, $ticket_datetimes, true)
2038
+				? ' ticket-selected'
2039
+				: '',
2040
+			'ticket_datetime_checked'  => in_array($datetime_row, $ticket_datetimes, true)
2041
+				? ' 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
+
2066
+
2067
+	/**
2068
+	 * @param array $all_datetimes
2069
+	 * @param array $all_tickets
2070
+	 * @return string
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(array $all_datetimes = [], array $all_tickets = []): string
2079
+	{
2080
+		$template_args = [
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
+				[],
2091
+				[],
2092
+				true
2093
+			),
2094
+			'default_price_row'         => $this->_get_ticket_price_row(
2095
+				'TICKETNUM',
2096
+				'PRICENUM',
2097
+				null,
2098
+				true,
2099
+				null
2100
+			),
2101
+
2102
+			'default_price_rows'                       => '',
2103
+			'default_base_price_amount'                => 0,
2104
+			'default_base_price_name'                  => '',
2105
+			'default_base_price_description'           => '',
2106
+			'default_price_modifier_selector_row'      => $this->_get_price_modifier_template(
2107
+				'TICKETNUM',
2108
+				'PRICENUM',
2109
+				null,
2110
+				true
2111
+			),
2112
+			'default_available_tickets_for_datetime'   => $this->_get_dtt_attached_tickets_row(
2113
+				'DTTNUM',
2114
+				null,
2115
+				[],
2116
+				[],
2117
+				true
2118
+			),
2119
+			'existing_available_datetime_tickets_list' => '',
2120
+			'existing_available_ticket_datetimes_list' => '',
2121
+			'new_available_datetime_ticket_list_item'  => $this->_get_datetime_tickets_list_item(
2122
+				'DTTNUM',
2123
+				'TICKETNUM',
2124
+				null,
2125
+				null,
2126
+				[],
2127
+				true
2128
+			),
2129
+			'new_available_ticket_datetime_list_item'  => $this->_get_ticket_datetime_list_item(
2130
+				'DTTNUM',
2131
+				'TICKETNUM',
2132
+				null,
2133
+				null,
2134
+				[],
2135
+				true
2136
+			),
2137
+		];
2138
+		$ticket_row    = 1;
2139
+		foreach ($all_tickets as $ticket) {
2140
+			$template_args['existing_available_datetime_tickets_list'] .= $this->_get_datetime_tickets_list_item(
2141
+				'DTTNUM',
2142
+				$ticket_row,
2143
+				null,
2144
+				$ticket,
2145
+				[],
2146
+				true
2147
+			);
2148
+			$ticket_row++;
2149
+		}
2150
+		$datetime_row = 1;
2151
+		foreach ($all_datetimes as $datetime) {
2152
+			$template_args['existing_available_ticket_datetimes_list'] .= $this->_get_ticket_datetime_list_item(
2153
+				$datetime_row,
2154
+				'TICKETNUM',
2155
+				$datetime,
2156
+				null,
2157
+				[],
2158
+				true
2159
+			);
2160
+			$datetime_row++;
2161
+		}
2162
+		$price_model    = EEM_Price::instance();
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
+
2180
+			$show_trash  = ! ((count($default_prices) > 1 && $price_row === 1) || count($default_prices) === 1);
2181
+			$show_create = ! (count($default_prices) > 1 && count($default_prices) !== $price_row);
2182
+
2183
+			$template_args['default_price_rows'] .= $this->_get_ticket_price_row(
2184
+				'TICKETNUM',
2185
+				$price_row,
2186
+				$price,
2187
+				true,
2188
+				null,
2189
+				$show_trash,
2190
+				$show_create
2191
+			);
2192
+			$price_row++;
2193
+		}
2194
+		$template_args = apply_filters(
2195
+			'FHEE__espresso_events_Pricing_Hooks___get_ticket_js_structure__template_args',
2196
+			$template_args,
2197
+			$all_datetimes,
2198
+			$all_tickets,
2199
+			$this->_is_creating_event
2200
+		);
2201
+		return EEH_Template::display_template(
2202
+			PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_js_structure.template.php',
2203
+			$template_args,
2204
+			true
2205
+		);
2206
+	}
2207 2207
 }
Please login to merge, or discard this patch.
Spacing   +88 added lines, -88 removed lines patch added patch discarded remove patch
@@ -76,7 +76,7 @@  discard block
 block discarded – undo
76 76
     protected function _setup_metaboxes()
77 77
     {
78 78
         // if we were going to add our own metaboxes we'd use the below.
79
-        $this->_metaboxes        = [
79
+        $this->_metaboxes = [
80 80
             0 => [
81 81
                 'page_route' => ['edit', 'create_new'],
82 82
                 'func'       => [$this, 'pricing_metabox'],
@@ -119,7 +119,7 @@  discard block
 block discarded – undo
119 119
         $this->_date_format_strings['date'] = $this->_date_format_strings['date'] ?? '';
120 120
         $this->_date_format_strings['time'] = $this->_date_format_strings['time'] ?? '';
121 121
 
122
-        $this->_date_time_format = $this->_date_format_strings['date'] . ' ' . $this->_date_format_strings['time'];
122
+        $this->_date_time_format = $this->_date_format_strings['date'].' '.$this->_date_format_strings['time'];
123 123
     }
124 124
 
125 125
 
@@ -143,7 +143,7 @@  discard block
 block discarded – undo
143 143
             );
144 144
             $msg .= '</p><ul>';
145 145
             foreach ($format_validation as $error) {
146
-                $msg .= '<li>' . $error . '</li>';
146
+                $msg .= '<li>'.$error.'</li>';
147 147
             }
148 148
             $msg .= '</ul><p>';
149 149
             $msg .= sprintf(
@@ -172,11 +172,11 @@  discard block
 block discarded – undo
172 172
         $this->_scripts_styles = [
173 173
             'registers'   => [
174 174
                 'ee-tickets-datetimes-css' => [
175
-                    'url'  => PRICING_ASSETS_URL . 'event-tickets-datetimes.css',
175
+                    'url'  => PRICING_ASSETS_URL.'event-tickets-datetimes.css',
176 176
                     'type' => 'css',
177 177
                 ],
178 178
                 'ee-dtt-ticket-metabox'    => [
179
-                    'url'     => PRICING_ASSETS_URL . 'ee-datetime-ticket-metabox.js',
179
+                    'url'     => PRICING_ASSETS_URL.'ee-datetime-ticket-metabox.js',
180 180
                     'depends' => ['ee-datepicker', 'ee-dialog', 'underscore'],
181 181
                 ],
182 182
             ],
@@ -201,11 +201,11 @@  discard block
 block discarded – undo
201 201
                         ),
202 202
                         'cancel_button'           => '
203 203
                             <button class="button--secondary ee-modal-cancel">
204
-                                ' . esc_html__('Cancel', 'event_espresso') . '
204
+                                ' . esc_html__('Cancel', 'event_espresso').'
205 205
                             </button>',
206 206
                         'close_button'            => '
207 207
                             <button class="button--secondary ee-modal-cancel">
208
-                                ' . esc_html__('Close', 'event_espresso') . '
208
+                                ' . esc_html__('Close', 'event_espresso').'
209 209
                             </button>',
210 210
                         'single_warning_from_tkt' => esc_html__(
211 211
                             '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,7 +217,7 @@  discard block
 block discarded – undo
217 217
                         ),
218 218
                         'dismiss_button'          => '
219 219
                             <button class="button--secondary ee-modal-cancel">
220
-                                ' . esc_html__('Dismiss', 'event_espresso') . '
220
+                                ' . esc_html__('Dismiss', 'event_espresso').'
221 221
                             </button>',
222 222
                     ],
223 223
                     'DTT_ERROR_MSG'         => [
@@ -225,7 +225,7 @@  discard block
 block discarded – undo
225 225
                         'dismiss_button' => '
226 226
                             <div class="save-cancel-button-container">
227 227
                                 <button class="button--secondary ee-modal-cancel">
228
-                                    ' . esc_html__('Dismiss', 'event_espresso') . '
228
+                                    ' . esc_html__('Dismiss', 'event_espresso').'
229 229
                                 </button>
230 230
                             </div>',
231 231
                     ],
@@ -314,7 +314,7 @@  discard block
 block discarded – undo
314 314
         foreach ($data['edit_event_datetimes'] as $row => $datetime_data) {
315 315
             // trim all values to ensure any excess whitespace is removed.
316 316
             $datetime_data = array_map(
317
-                function ($datetime_data) {
317
+                function($datetime_data) {
318 318
                     return is_array($datetime_data) ? $datetime_data : trim($datetime_data);
319 319
                 },
320 320
                 $datetime_data
@@ -324,7 +324,7 @@  discard block
 block discarded – undo
324 324
                                             && ! empty($datetime_data['DTT_EVT_end'])
325 325
                 ? $datetime_data['DTT_EVT_end']
326 326
                 : $datetime_data['DTT_EVT_start'];
327
-            $datetime_values              = [
327
+            $datetime_values = [
328 328
                 'DTT_ID'          => ! empty($datetime_data['DTT_ID'])
329 329
                     ? $datetime_data['DTT_ID']
330 330
                     : null,
@@ -346,7 +346,7 @@  discard block
 block discarded – undo
346 346
 
347 347
             // if we have an id then let's get existing object first and then set the new values.
348 348
             // Otherwise we instantiate a new object for save.
349
-            if (! empty($datetime_data['DTT_ID'])) {
349
+            if ( ! empty($datetime_data['DTT_ID'])) {
350 350
                 $datetime = EE_Registry::instance()
351 351
                                        ->load_model('Datetime', [$timezone])
352 352
                                        ->get_one_by_ID($datetime_data['DTT_ID']);
@@ -361,7 +361,7 @@  discard block
 block discarded – undo
361 361
                 // after the add_relation_to() the autosave replaces it.
362 362
                 // We need to do this so we dont' TRASH the parent DTT.
363 363
                 // (save the ID for both key and value to avoid duplications)
364
-                $saved_datetime_ids[ $datetime->ID() ] = $datetime->ID();
364
+                $saved_datetime_ids[$datetime->ID()] = $datetime->ID();
365 365
             } else {
366 366
                 $datetime = EE_Datetime::new_instance(
367 367
                     $datetime_values,
@@ -392,8 +392,8 @@  discard block
 block discarded – undo
392 392
             // because it is possible there was a new one created for the autosave.
393 393
             // (save the ID for both key and value to avoid duplications)
394 394
             $DTT_ID                        = $datetime->ID();
395
-            $saved_datetime_ids[ $DTT_ID ] = $DTT_ID;
396
-            $saved_datetime_objs[ $row ]   = $datetime;
395
+            $saved_datetime_ids[$DTT_ID] = $DTT_ID;
396
+            $saved_datetime_objs[$row]   = $datetime;
397 397
             // @todo if ANY of these updates fail then we want the appropriate global error message.
398 398
         }
399 399
         $event->save();
@@ -459,13 +459,13 @@  discard block
 block discarded – undo
459 459
             $update_prices = $create_new_TKT = false;
460 460
             // figure out what datetimes were added to the ticket
461 461
             // and what datetimes were removed from the ticket in the session.
462
-            $starting_ticket_datetime_rows = explode(',', $data['starting_ticket_datetime_rows'][ $row ]);
463
-            $ticket_datetime_rows          = explode(',', $data['ticket_datetime_rows'][ $row ]);
462
+            $starting_ticket_datetime_rows = explode(',', $data['starting_ticket_datetime_rows'][$row]);
463
+            $ticket_datetime_rows          = explode(',', $data['ticket_datetime_rows'][$row]);
464 464
             $datetimes_added               = array_diff($ticket_datetime_rows, $starting_ticket_datetime_rows);
465 465
             $datetimes_removed             = array_diff($starting_ticket_datetime_rows, $ticket_datetime_rows);
466 466
             // trim inputs to ensure any excess whitespace is removed.
467 467
             $ticket_data = array_map(
468
-                function ($ticket_data) {
468
+                function($ticket_data) {
469 469
                     return is_array($ticket_data) ? $ticket_data : trim($ticket_data);
470 470
                 },
471 471
                 $ticket_data
@@ -485,8 +485,8 @@  discard block
 block discarded – undo
485 485
                 ? $base_price
486 486
                 : $ticket_price;
487 487
             $base_price_id = $ticket_data['TKT_base_price_ID'] ?? 0;
488
-            $price_rows    = is_array($data['edit_prices']) && isset($data['edit_prices'][ $row ])
489
-                ? $data['edit_prices'][ $row ]
488
+            $price_rows    = is_array($data['edit_prices']) && isset($data['edit_prices'][$row])
489
+                ? $data['edit_prices'][$row]
490 490
                 : [];
491 491
             $now           = null;
492 492
             if (empty($ticket_data['TKT_start_date'])) {
@@ -498,7 +498,7 @@  discard block
 block discarded – undo
498 498
                 /**
499 499
                  * set the TKT_end_date to the first datetime attached to the ticket.
500 500
                  */
501
-                $first_datetime              = $saved_datetimes[ reset($ticket_datetime_rows) ];
501
+                $first_datetime              = $saved_datetimes[reset($ticket_datetime_rows)];
502 502
                 $ticket_data['TKT_end_date'] = $first_datetime->start_date_and_time($this->_date_time_format);
503 503
             }
504 504
             $TKT_values = [
@@ -599,7 +599,7 @@  discard block
 block discarded – undo
599 599
                 if ($ticket instanceof EE_Ticket) {
600 600
                     // make sure ticket has an ID of setting relations won't work
601 601
                     $ticket->save();
602
-                    $ticket        = $this->_update_ticket_datetimes(
602
+                    $ticket = $this->_update_ticket_datetimes(
603 603
                         $ticket,
604 604
                         $saved_datetimes,
605 605
                         $datetimes_added,
@@ -633,7 +633,7 @@  discard block
 block discarded – undo
633 633
             // need to make sue that the TKT_price is accurate after saving the prices.
634 634
             $ticket->ensure_TKT_Price_correct();
635 635
             // handle CREATING a default ticket from the incoming ticket but ONLY if this isn't an autosave.
636
-            if (! defined('DOING_AUTOSAVE') && ! empty($ticket_data['TKT_is_default_selector'])) {
636
+            if ( ! defined('DOING_AUTOSAVE') && ! empty($ticket_data['TKT_is_default_selector'])) {
637 637
                 $new_default = clone $ticket;
638 638
                 $new_default->set('TKT_ID', 0);
639 639
                 $new_default->set('TKT_is_default', 1);
@@ -677,7 +677,7 @@  discard block
 block discarded – undo
677 677
                 // save new TKT
678 678
                 $new_ticket->save();
679 679
                 // add new ticket to array
680
-                $saved_tickets[ $new_ticket->ID() ] = $new_ticket;
680
+                $saved_tickets[$new_ticket->ID()] = $new_ticket;
681 681
                 do_action(
682 682
                     'AHEE__espresso_events_Pricing_Hooks___update_tkts_new_ticket',
683 683
                     $new_ticket,
@@ -687,7 +687,7 @@  discard block
 block discarded – undo
687 687
                 );
688 688
             } else {
689 689
                 // add ticket to saved tickets
690
-                $saved_tickets[ $ticket->ID() ] = $ticket;
690
+                $saved_tickets[$ticket->ID()] = $ticket;
691 691
                 do_action(
692 692
                     'AHEE__espresso_events_Pricing_Hooks___update_tkts_update_ticket',
693 693
                     $ticket,
@@ -755,25 +755,25 @@  discard block
 block discarded – undo
755 755
         // to start we have to add the ticket to all the datetimes its supposed to be with,
756 756
         // and removing the ticket from datetimes it got removed from.
757 757
         // first let's add datetimes
758
-        if (! empty($added_datetimes) && is_array($added_datetimes)) {
758
+        if ( ! empty($added_datetimes) && is_array($added_datetimes)) {
759 759
             foreach ($added_datetimes as $row_id) {
760
-                if (isset($saved_datetimes[ $row_id ]) && $saved_datetimes[ $row_id ] instanceof EE_Datetime) {
761
-                    $ticket->_add_relation_to($saved_datetimes[ $row_id ], 'Datetime');
760
+                if (isset($saved_datetimes[$row_id]) && $saved_datetimes[$row_id] instanceof EE_Datetime) {
761
+                    $ticket->_add_relation_to($saved_datetimes[$row_id], 'Datetime');
762 762
                     // Is this an existing ticket (has an ID) and does it have any sold?
763 763
                     // If so, then we need to add that to the DTT sold because this DTT is getting added.
764 764
                     if ($ticket->ID() && $ticket->sold() > 0) {
765
-                        $saved_datetimes[ $row_id ]->increaseSold($ticket->sold(), false);
765
+                        $saved_datetimes[$row_id]->increaseSold($ticket->sold(), false);
766 766
                     }
767 767
                 }
768 768
             }
769 769
         }
770 770
         // then remove datetimes
771
-        if (! empty($removed_datetimes) && is_array($removed_datetimes)) {
771
+        if ( ! empty($removed_datetimes) && is_array($removed_datetimes)) {
772 772
             foreach ($removed_datetimes as $row_id) {
773 773
                 // its entirely possible that a datetime got deleted (instead of just removed from relationship.
774 774
                 // So make sure we skip over this if the datetime isn't in the $saved_datetimes array)
775
-                if (isset($saved_datetimes[ $row_id ]) && $saved_datetimes[ $row_id ] instanceof EE_Datetime) {
776
-                    $ticket->_remove_relation_to($saved_datetimes[ $row_id ], 'Datetime');
775
+                if (isset($saved_datetimes[$row_id]) && $saved_datetimes[$row_id] instanceof EE_Datetime) {
776
+                    $ticket->_remove_relation_to($saved_datetimes[$row_id], 'Datetime');
777 777
                 }
778 778
             }
779 779
         }
@@ -885,7 +885,7 @@  discard block
 block discarded – undo
885 885
             ];
886 886
         }
887 887
         // possibly need to save ticket
888
-        if (! $ticket->ID()) {
888
+        if ( ! $ticket->ID()) {
889 889
             $ticket->save();
890 890
         }
891 891
         foreach ($prices as $row => $prc) {
@@ -919,17 +919,17 @@  discard block
 block discarded – undo
919 919
                 }
920 920
             }
921 921
             $price->save();
922
-            $updated_prices[ $price->ID() ] = $price;
922
+            $updated_prices[$price->ID()] = $price;
923 923
             $ticket->_add_relation_to($price, 'Price');
924 924
         }
925 925
         // now let's remove any prices that got removed from the ticket
926
-        if (! empty($current_prices_on_ticket)) {
926
+        if ( ! empty($current_prices_on_ticket)) {
927 927
             $current          = array_keys($current_prices_on_ticket);
928 928
             $updated          = array_keys($updated_prices);
929 929
             $prices_to_remove = array_diff($current, $updated);
930
-            if (! empty($prices_to_remove)) {
930
+            if ( ! empty($prices_to_remove)) {
931 931
                 foreach ($prices_to_remove as $prc_id) {
932
-                    $p = $current_prices_on_ticket[ $prc_id ];
932
+                    $p = $current_prices_on_ticket[$prc_id];
933 933
                     $ticket->_remove_relation_to($p, 'Price');
934 934
                     // delete permanently the price
935 935
                     $p->delete_or_restore();
@@ -950,7 +950,7 @@  discard block
 block discarded – undo
950 950
      */
951 951
     public function pricing_metabox()
952 952
     {
953
-        $event                 = $this->_adminpage_obj->get_cpt_model_obj();
953
+        $event = $this->_adminpage_obj->get_cpt_model_obj();
954 954
         $timezone = $event instanceof EE_Event ? $event->timezone_string() : null;
955 955
         $price_model = EEM_Price::instance($timezone);
956 956
         $ticket_model = EEM_Ticket::instance($timezone);
@@ -1042,18 +1042,18 @@  discard block
 block discarded – undo
1042 1042
                 $TKT_ID     = $ticket->get('TKT_ID');
1043 1043
                 $ticket_row = $ticket->get('TKT_row');
1044 1044
                 // we only want unique tickets in our final display!!
1045
-                if (! in_array($TKT_ID, $existing_ticket_ids, true)) {
1045
+                if ( ! in_array($TKT_ID, $existing_ticket_ids, true)) {
1046 1046
                     $existing_ticket_ids[] = $TKT_ID;
1047 1047
                     $all_tickets[]         = $ticket;
1048 1048
                 }
1049 1049
                 // temporary cache of this ticket info for this datetime for later processing of datetime rows.
1050
-                $datetime_tickets[ $DTT_ID ][] = $ticket_row;
1050
+                $datetime_tickets[$DTT_ID][] = $ticket_row;
1051 1051
                 // temporary cache of this datetime info for this ticket for later processing of ticket rows.
1052 1052
                 if (
1053
-                    ! isset($ticket_datetimes[ $TKT_ID ])
1054
-                    || ! in_array($datetime_row, $ticket_datetimes[ $TKT_ID ], true)
1053
+                    ! isset($ticket_datetimes[$TKT_ID])
1054
+                    || ! in_array($datetime_row, $ticket_datetimes[$TKT_ID], true)
1055 1055
                 ) {
1056
-                    $ticket_datetimes[ $TKT_ID ][] = $datetime_row;
1056
+                    $ticket_datetimes[$TKT_ID][] = $datetime_row;
1057 1057
                 }
1058 1058
             }
1059 1059
             $datetime_row++;
@@ -1064,7 +1064,7 @@  discard block
 block discarded – undo
1064 1064
         // sort $all_tickets by order
1065 1065
         usort(
1066 1066
             $all_tickets,
1067
-            function (EE_Ticket $a, EE_Ticket $b) {
1067
+            function(EE_Ticket $a, EE_Ticket $b) {
1068 1068
                 $a_order = (int) $a->get('TKT_order');
1069 1069
                 $b_order = (int) $b->get('TKT_order');
1070 1070
                 if ($a_order === $b_order) {
@@ -1112,7 +1112,7 @@  discard block
 block discarded – undo
1112 1112
         );
1113 1113
 
1114 1114
         EEH_Template::display_template(
1115
-            PRICING_TEMPLATE_PATH . 'event_tickets_metabox_main.template.php',
1115
+            PRICING_TEMPLATE_PATH.'event_tickets_metabox_main.template.php',
1116 1116
             $main_template_args
1117 1117
         );
1118 1118
     }
@@ -1139,7 +1139,7 @@  discard block
 block discarded – undo
1139 1139
         array $all_datetimes = []
1140 1140
     ): string {
1141 1141
         return EEH_Template::display_template(
1142
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_row_wrapper.template.php',
1142
+            PRICING_TEMPLATE_PATH.'event_tickets_datetime_row_wrapper.template.php',
1143 1143
             [
1144 1144
                 'dtt_edit_row'             => $this->_get_dtt_edit_row(
1145 1145
                     $datetime_row,
@@ -1232,7 +1232,7 @@  discard block
 block discarded – undo
1232 1232
             $this->_is_creating_event
1233 1233
         );
1234 1234
         return EEH_Template::display_template(
1235
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_edit_row.template.php',
1235
+            PRICING_TEMPLATE_PATH.'event_tickets_datetime_edit_row.template.php',
1236 1236
             $template_args,
1237 1237
             true
1238 1238
         );
@@ -1272,7 +1272,7 @@  discard block
 block discarded – undo
1272 1272
             'DTT_ID'                            => $default ? '' : $datetime->ID(),
1273 1273
         ];
1274 1274
         // need to setup the list items (but only if this isn't a default skeleton setup)
1275
-        if (! $default) {
1275
+        if ( ! $default) {
1276 1276
             $ticket_row = 1;
1277 1277
             foreach ($all_tickets as $ticket) {
1278 1278
                 $template_args['datetime_tickets_list'] .= $this->_get_datetime_tickets_list_item(
@@ -1297,7 +1297,7 @@  discard block
 block discarded – undo
1297 1297
             $this->_is_creating_event
1298 1298
         );
1299 1299
         return EEH_Template::display_template(
1300
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_attached_tickets_row.template.php',
1300
+            PRICING_TEMPLATE_PATH.'event_tickets_datetime_attached_tickets_row.template.php',
1301 1301
             $template_args,
1302 1302
             true
1303 1303
         );
@@ -1323,8 +1323,8 @@  discard block
 block discarded – undo
1323 1323
         array $datetime_tickets = [],
1324 1324
         bool $default = false
1325 1325
     ): string {
1326
-        $datetime_tickets = $datetime instanceof EE_Datetime && isset($datetime_tickets[ $datetime->ID() ])
1327
-            ? $datetime_tickets[ $datetime->ID() ]
1326
+        $datetime_tickets = $datetime instanceof EE_Datetime && isset($datetime_tickets[$datetime->ID()])
1327
+            ? $datetime_tickets[$datetime->ID()]
1328 1328
             : [];
1329 1329
         $display_row      = $ticket instanceof EE_Ticket ? $ticket->get('TKT_row') : 0;
1330 1330
         $no_ticket        = $default && empty($ticket);
@@ -1345,8 +1345,8 @@  discard block
 block discarded – undo
1345 1345
                 ? 'TKTNAME'
1346 1346
                 : $ticket->get('TKT_name'),
1347 1347
             'tkt_status_class'        => $no_ticket || $this->_is_creating_event
1348
-                ? ' tkt-status-' . EE_Ticket::onsale
1349
-                : ' tkt-status-' . $ticket->ticket_status(),
1348
+                ? ' tkt-status-'.EE_Ticket::onsale
1349
+                : ' tkt-status-'.$ticket->ticket_status(),
1350 1350
         ];
1351 1351
         // filter template args
1352 1352
         $template_args = apply_filters(
@@ -1361,7 +1361,7 @@  discard block
 block discarded – undo
1361 1361
             $this->_is_creating_event
1362 1362
         );
1363 1363
         return EEH_Template::display_template(
1364
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_dtt_tickets_list.template.php',
1364
+            PRICING_TEMPLATE_PATH.'event_tickets_datetime_dtt_tickets_list.template.php',
1365 1365
             $template_args,
1366 1366
             true
1367 1367
         );
@@ -1417,19 +1417,19 @@  discard block
 block discarded – undo
1417 1417
         // (otherwise there won't be any new relationships created for tickets based off of the default ticket).
1418 1418
         // This will future proof in case there is ever any behaviour change between what the primary_key defaults to.
1419 1419
         $default_datetime = $default || ($ticket instanceof EE_Ticket && $ticket->is_default());
1420
-        $ticket_datetimes = $ticket instanceof EE_Ticket && isset($ticket_datetimes[ $ticket->ID() ])
1421
-            ? $ticket_datetimes[ $ticket->ID() ]
1420
+        $ticket_datetimes = $ticket instanceof EE_Ticket && isset($ticket_datetimes[$ticket->ID()])
1421
+            ? $ticket_datetimes[$ticket->ID()]
1422 1422
             : [];
1423 1423
         $ticket_subtotal  = $default ? 0 : $ticket->get_ticket_subtotal();
1424 1424
         $base_price       = $default ? null : $ticket->base_price();
1425 1425
         $count_price_mods = EEM_Price::instance()->get_all_default_prices(true);
1426 1426
         // breaking out complicated condition for ticket_status
1427 1427
         if ($default) {
1428
-            $ticket_status_class = ' tkt-status-' . EE_Ticket::onsale;
1428
+            $ticket_status_class = ' tkt-status-'.EE_Ticket::onsale;
1429 1429
         } else {
1430 1430
             $ticket_status_class = $ticket->is_default()
1431
-                ? ' tkt-status-' . EE_Ticket::onsale
1432
-                : ' tkt-status-' . $ticket->ticket_status();
1431
+                ? ' tkt-status-'.EE_Ticket::onsale
1432
+                : ' tkt-status-'.$ticket->ticket_status();
1433 1433
         }
1434 1434
         // breaking out complicated condition for TKT_taxable
1435 1435
         if ($default) {
@@ -1454,7 +1454,7 @@  discard block
 block discarded – undo
1454 1454
                 $TKT_min = '';
1455 1455
             }
1456 1456
         }
1457
-        $template_args                 = [
1457
+        $template_args = [
1458 1458
             'tkt_row'                       => $default ? 'TICKETNUM' : $ticket_row,
1459 1459
             'TKT_order'                     => $default ? 'TICKETNUM' : $ticket_row,
1460 1460
             // on initial page load this will always be the correct order.
@@ -1521,7 +1521,7 @@  discard block
 block discarded – undo
1521 1521
                 : 'display:none;',
1522 1522
             'show_price_mod_button'         => count($prices) > 1
1523 1523
                                                || ($default && $count_price_mods > 0)
1524
-                                               || (! $default && $ticket->deleted())
1524
+                                               || ( ! $default && $ticket->deleted())
1525 1525
                 ? 'display:none;'
1526 1526
                 : '',
1527 1527
             'total_price_rows'              => count($prices) > 1 ? count($prices) : 1,
@@ -1562,11 +1562,11 @@  discard block
 block discarded – undo
1562 1562
         // handle rows that should NOT be empty
1563 1563
         if (empty($template_args['TKT_start_date'])) {
1564 1564
             // if empty then the start date will be now.
1565
-            $template_args['TKT_start_date']   = date(
1565
+            $template_args['TKT_start_date'] = date(
1566 1566
                 $this->_date_time_format,
1567 1567
                 current_time('timestamp')
1568 1568
             );
1569
-            $template_args['tkt_status_class'] = ' tkt-status-' . EE_Ticket::onsale;
1569
+            $template_args['tkt_status_class'] = ' tkt-status-'.EE_Ticket::onsale;
1570 1570
         }
1571 1571
         if (empty($template_args['TKT_end_date'])) {
1572 1572
             // get the earliest datetime (if present);
@@ -1576,7 +1576,7 @@  discard block
 block discarded – undo
1576 1576
                     ['order_by' => ['DTT_EVT_start' => 'ASC']]
1577 1577
                 )
1578 1578
                 : null;
1579
-            if (! empty($earliest_datetime)) {
1579
+            if ( ! empty($earliest_datetime)) {
1580 1580
                 $template_args['TKT_end_date'] = $earliest_datetime->get_datetime(
1581 1581
                     'DTT_EVT_start',
1582 1582
                     $this->_date_time_format
@@ -1595,10 +1595,10 @@  discard block
 block discarded – undo
1595 1595
                     )
1596 1596
                 );
1597 1597
             }
1598
-            $template_args['tkt_status_class'] = ' tkt-status-' . EE_Ticket::onsale;
1598
+            $template_args['tkt_status_class'] = ' tkt-status-'.EE_Ticket::onsale;
1599 1599
         }
1600 1600
         // generate ticket_datetime items
1601
-        if (! $default) {
1601
+        if ( ! $default) {
1602 1602
             $datetime_row = 1;
1603 1603
             foreach ($all_datetimes as $datetime) {
1604 1604
                 $template_args['ticket_datetimes_list'] .= $this->_get_ticket_datetime_list_item(
@@ -1614,7 +1614,7 @@  discard block
 block discarded – undo
1614 1614
         }
1615 1615
         $price_row = 1;
1616 1616
         foreach ($prices as $price) {
1617
-            if (! $price instanceof EE_Price) {
1617
+            if ( ! $price instanceof EE_Price) {
1618 1618
                 continue;
1619 1619
             }
1620 1620
             if ($price->is_base_price()) {
@@ -1649,7 +1649,7 @@  discard block
 block discarded – undo
1649 1649
             $this->_is_creating_event
1650 1650
         );
1651 1651
         return EEH_Template::display_template(
1652
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_row.template.php',
1652
+            PRICING_TEMPLATE_PATH.'event_tickets_datetime_ticket_row.template.php',
1653 1653
             $template_args,
1654 1654
             true
1655 1655
         );
@@ -1689,8 +1689,8 @@  discard block
 block discarded – undo
1689 1689
                 $ticket,
1690 1690
                 $this->_is_creating_event
1691 1691
             );
1692
-            $tax_rows      .= EEH_Template::display_template(
1693
-                PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_tax_row.template.php',
1692
+            $tax_rows .= EEH_Template::display_template(
1693
+                PRICING_TEMPLATE_PATH.'event_tickets_datetime_ticket_tax_row.template.php',
1694 1694
                 $template_args,
1695 1695
                 true
1696 1696
             );
@@ -1810,7 +1810,7 @@  discard block
 block discarded – undo
1810 1810
             $this->_is_creating_event
1811 1811
         );
1812 1812
         return EEH_Template::display_template(
1813
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_price_row.template.php',
1813
+            PRICING_TEMPLATE_PATH.'event_tickets_datetime_ticket_price_row.template.php',
1814 1814
             $template_args,
1815 1815
             true
1816 1816
         );
@@ -1890,7 +1890,7 @@  discard block
 block discarded – undo
1890 1890
             $this->_is_creating_event
1891 1891
         );
1892 1892
         return EEH_Template::display_template(
1893
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_price_type_base.template.php',
1893
+            PRICING_TEMPLATE_PATH.'event_tickets_datetime_price_type_base.template.php',
1894 1894
             $template_args,
1895 1895
             true
1896 1896
         );
@@ -1920,7 +1920,7 @@  discard block
 block discarded – undo
1920 1920
     ): string {
1921 1921
         $select_name = $default && ! $price instanceof EE_Price
1922 1922
             ? 'edit_prices[TICKETNUM][PRICENUM][PRT_ID]'
1923
-            : 'edit_prices[' . esc_attr($ticket_row) . '][' . esc_attr($price_row) . '][PRT_ID]';
1923
+            : 'edit_prices['.esc_attr($ticket_row).']['.esc_attr($price_row).'][PRT_ID]';
1924 1924
 
1925 1925
         $price_type_model       = EEM_Price_Type::instance();
1926 1926
         $price_types            = $price_type_model->get_all(
@@ -1940,25 +1940,25 @@  discard block
 block discarded – undo
1940 1940
         $price_option_spans     = '';
1941 1941
         // setup price types for selector
1942 1942
         foreach ($price_types as $price_type) {
1943
-            if (! $price_type instanceof EE_Price_Type) {
1943
+            if ( ! $price_type instanceof EE_Price_Type) {
1944 1944
                 continue;
1945 1945
             }
1946
-            $all_price_types[ $price_type->ID() ] = $price_type->get('PRT_name');
1946
+            $all_price_types[$price_type->ID()] = $price_type->get('PRT_name');
1947 1947
             // while we're in the loop let's setup the option spans used by js
1948
-            $span_args          = [
1948
+            $span_args = [
1949 1949
                 'PRT_ID'         => $price_type->ID(),
1950 1950
                 'PRT_operator'   => $price_type->is_discount() ? '-' : '+',
1951 1951
                 'PRT_is_percent' => $price_type->get('PRT_is_percent') ? 1 : 0,
1952 1952
             ];
1953 1953
             $price_option_spans .= EEH_Template::display_template(
1954
-                PRICING_TEMPLATE_PATH . 'event_tickets_datetime_price_option_span.template.php',
1954
+                PRICING_TEMPLATE_PATH.'event_tickets_datetime_price_option_span.template.php',
1955 1955
                 $span_args,
1956 1956
                 true
1957 1957
             );
1958 1958
         }
1959 1959
 
1960 1960
         $select_name = $disabled
1961
-            ? 'archive_price[' . $ticket_row . '][' . $price_row . '][PRT_ID]'
1961
+            ? 'archive_price['.$ticket_row.']['.$price_row.'][PRT_ID]'
1962 1962
             : $select_name;
1963 1963
 
1964 1964
         $select_input = new EE_Select_Input(
@@ -1986,7 +1986,7 @@  discard block
 block discarded – undo
1986 1986
             'price_selected_is_percent' => $price_selected_is_percent,
1987 1987
             'disabled'                  => $disabled,
1988 1988
         ];
1989
-        $template_args             = apply_filters(
1989
+        $template_args = apply_filters(
1990 1990
             'FHEE__espresso_events_Pricing_Hooks___get_price_modifier_template__template_args',
1991 1991
             $template_args,
1992 1992
             $ticket_row,
@@ -1997,7 +1997,7 @@  discard block
 block discarded – undo
1997 1997
             $this->_is_creating_event
1998 1998
         );
1999 1999
         return EEH_Template::display_template(
2000
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_price_modifier_selector.template.php',
2000
+            PRICING_TEMPLATE_PATH.'event_tickets_datetime_price_modifier_selector.template.php',
2001 2001
             $template_args,
2002 2002
             true
2003 2003
         );
@@ -2024,8 +2024,8 @@  discard block
 block discarded – undo
2024 2024
         array $ticket_datetimes = [],
2025 2025
         bool $default = false
2026 2026
     ): string {
2027
-        $ticket_datetimes = $ticket instanceof EE_Ticket && isset($ticket_datetimes[ $ticket->ID() ])
2028
-            ? $ticket_datetimes[ $ticket->ID() ]
2027
+        $ticket_datetimes = $ticket instanceof EE_Ticket && isset($ticket_datetimes[$ticket->ID()])
2028
+            ? $ticket_datetimes[$ticket->ID()]
2029 2029
             : [];
2030 2030
         $template_args    = [
2031 2031
             'dtt_row'                  => $default && ! $datetime instanceof EE_Datetime
@@ -2045,7 +2045,7 @@  discard block
 block discarded – undo
2045 2045
                 : $datetime->get_dtt_display_name(true),
2046 2046
             'tkt_status_class'         => '',
2047 2047
         ];
2048
-        $template_args    = apply_filters(
2048
+        $template_args = apply_filters(
2049 2049
             'FHEE__espresso_events_Pricing_Hooks___get_ticket_datetime_list_item__template_args',
2050 2050
             $template_args,
2051 2051
             $datetime_row,
@@ -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
         );
@@ -2135,7 +2135,7 @@  discard block
 block discarded – undo
2135 2135
                 true
2136 2136
             ),
2137 2137
         ];
2138
-        $ticket_row    = 1;
2138
+        $ticket_row = 1;
2139 2139
         foreach ($all_tickets as $ticket) {
2140 2140
             $template_args['existing_available_datetime_tickets_list'] .= $this->_get_datetime_tickets_list_item(
2141 2141
                 '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
                 );
@@ -2199,7 +2199,7 @@  discard block
 block discarded – undo
2199 2199
             $this->_is_creating_event
2200 2200
         );
2201 2201
         return EEH_Template::display_template(
2202
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_js_structure.template.php',
2202
+            PRICING_TEMPLATE_PATH.'event_tickets_datetime_ticket_js_structure.template.php',
2203 2203
             $template_args,
2204 2204
             true
2205 2205
         );
Please login to merge, or discard this patch.
caffeinated/admin/new/tickets/Tickets_List_Table.class.php 2 patches
Indentation   +169 added lines, -169 removed lines patch added patch discarded remove patch
@@ -16,173 +16,173 @@
 block discarded – undo
16 16
  */
17 17
 class Tickets_List_Table extends EE_Admin_List_Table
18 18
 {
19
-    protected function _setup_data()
20
-    {
21
-        \EEH_Debug_Tools::printr(__FUNCTION__, __CLASS__, __FILE__, __LINE__, 2);
22
-        $trashed = $this->_admin_page->get_view() == 'trashed' ? true : false;
23
-        $this->_data = $this->_admin_page->get_default_tickets($this->_per_page, false, $trashed);
24
-        $this->_all_data_count = $this->_admin_page->get_default_tickets($this->_per_page, true, false);
25
-        $this->_trashed_count = $this->_admin_page->get_default_tickets($this->_per_page, true, true);
26
-    }
27
-
28
-
29
-    protected function _set_properties()
30
-    {
31
-        $this->_wp_list_args = array(
32
-            'singular' => esc_html__('ticket', 'event_espresso'),
33
-            'plural'   => esc_html__('tickets', 'event_espresso'),
34
-            'ajax'     => true,
35
-            'screen'   => $this->_admin_page->get_current_screen()->id,
36
-        );
37
-
38
-        $this->_columns = array(
39
-            'cb'              => '<input type="checkbox" />', // Render a checkbox instead of text
40
-            'id'              => esc_html__('ID', 'event_espresso'),
41
-            'TKT_name'        => esc_html__('Name', 'event_espresso'),
42
-            'TKT_description' => esc_html__('Description', 'event_espresso'),
43
-            'TKT_qty'         => esc_html__('Quantity', 'event_espresso'),
44
-            'TKT_uses'        => esc_html__('Uses', 'event_espresso'),
45
-            'TKT_min'         => esc_html__('Minimum', 'event_espresso'),
46
-            'TKT_max'         => esc_html__('Maximum', 'event_espresso'),
47
-            'TKT_price'       => esc_html__('Price', 'event_espresso'),
48
-            'TKT_taxable'     => esc_html__('Taxable', 'event_espresso'),
49
-        );
50
-
51
-        $this->_sortable_columns = array(
52
-            // TRUE means its already sorted
53
-            'id' => array('TKT_ID', false),
54
-            'TKT_name'        => array('TKT_name', true),
55
-            'TKT_description' => array('TKT_description', false),
56
-            'TKT_qty'         => array('TKT_qty', false),
57
-            'TKT_uses'        => array('TKT_uses', false),
58
-            'TKT_min'         => array('TKT_min', false),
59
-            'TKT_max'         => array('TKT_max', false),
60
-            'TKT_price'       => array('TKT_price', false),
61
-        );
62
-
63
-        $this->_hidden_columns = array();
64
-    }
65
-
66
-
67
-    protected function _get_table_filters()
68
-    {
69
-    }
70
-
71
-
72
-    protected function _add_view_counts()
73
-    {
74
-        $this->_views['all']['count'] = $this->_all_data_count;
75
-        if (EE_Registry::instance()->CAP->current_user_can('ee_delete_default_tickets', 'trash_ticket')) {
76
-            $this->_views['trashed']['count'] = $this->_trashed_count;
77
-        }
78
-    }
79
-
80
-
81
-    public function column_cb($item)
82
-    {
83
-        return $item->ID() === 1
84
-            ? '<span class="dashicons dashicons-lock"></span>'
85
-            : sprintf(
86
-                '<input type="checkbox" name="checkbox[%1$s]" value="%1$s" />',
87
-                $item->ID()
88
-            );
89
-    }
90
-
91
-
92
-    /**
93
-     * @param EE_Ticket $item
94
-     * @return string
95
-     * @throws EE_Error
96
-     * @throws ReflectionException
97
-     */
98
-    public function column_id($item): string
99
-    {
100
-        $content = '<span class="ee-entity-id">' . $item->ID() . '</span>';
101
-        $content .= '<span class="show-on-mobile-view-only">' . $this->column_TKT_name($item, false) . '</span>';
102
-        return $this->columnContent('id', $content, 'end');
103
-    }
104
-
105
-
106
-    public function column_TKT_name(EE_Ticket $ticket, bool $prep_content = true): string
107
-    {
108
-        // build row actions
109
-        $actions = array();
110
-
111
-        // trash links
112
-        if ($ticket->ID() !== 1) {
113
-            if ($this->_view == 'all') {
114
-                $trash_lnk_url = EE_Admin_Page::add_query_args_and_nonce(array(
115
-                    'action' => 'trash_ticket',
116
-                    'TKT_ID' => $ticket->ID(),
117
-                ), TICKETS_ADMIN_URL);
118
-                $actions['trash'] = '<a href="' . $trash_lnk_url . '" aria-label="'
119
-                                    . esc_attr__('Move Ticket to trash', 'event_espresso') . '">'
120
-                                    . esc_html__('Trash', 'event_espresso') . '</a>';
121
-            } else {
122
-                // restore price link
123
-                $restore_lnk_url = EE_Admin_Page::add_query_args_and_nonce(array(
124
-                    'action' => 'restore_ticket',
125
-                    'TKT_ID' => $ticket->ID(),
126
-                ), TICKETS_ADMIN_URL);
127
-                $actions['restore'] = '<a href="' . $restore_lnk_url . '" aria-label="'
128
-                                      . esc_attr__('Restore Ticket', 'event_espresso') . '">'
129
-                                      . esc_html__('Restore', 'event_espresso') . '</a>';
130
-                // delete price link
131
-                $delete_lnk_url = EE_Admin_Page::add_query_args_and_nonce(array(
132
-                    'action' => 'delete_ticket',
133
-                    'TKT_ID' => $ticket->ID(),
134
-                ), TICKETS_ADMIN_URL);
135
-                $actions['delete'] = '<a href="' . $delete_lnk_url . '" aria-label="'
136
-                                     . esc_attr__('Delete Ticket Permanently', 'event_espresso') . '">'
137
-                                     . esc_html__('Delete Permanently', 'event_espresso') . '</a>';
138
-            }
139
-        }
140
-        // return $ticket->get('TKT_name') . $this->row_actions($actions);
141
-        $content = $prep_content ? $ticket->name() . $this->row_actions($actions) : $ticket->name();
142
-        return $prep_content ? $this->columnContent('TKT_name', $content) : $content;
143
-    }
144
-
145
-
146
-    public function column_TKT_description(EE_Ticket $ticket)
147
-    {
148
-        return $this->columnContent('TKT_description', $ticket->description());
149
-    }
150
-
151
-
152
-    public function column_TKT_qty(EE_Ticket $ticket)
153
-    {
154
-
155
-        return $ticket->get_pretty('TKT_qty', 'text');
156
-        // return $this->columnContent('TKT_qty', $ticket->qty());
157
-    }
158
-
159
-
160
-    public function column_TKT_uses(EE_Ticket $ticket)
161
-    {
162
-        return $ticket->get_pretty('TKT_uses', 'text');
163
-    }
164
-
165
-
166
-    public function column_TKT_min(EE_Ticket $ticket)
167
-    {
168
-        return $ticket->get('TKT_min');
169
-    }
170
-
171
-
172
-    public function column_TKT_max(EE_Ticket $ticket)
173
-    {
174
-        return $ticket->get_pretty('TKT_max', 'text');
175
-    }
176
-
177
-
178
-    public function column_TKT_price(EE_Ticket $ticket)
179
-    {
180
-        return EEH_Template::format_currency($ticket->get('TKT_price'));
181
-    }
182
-
183
-
184
-    public function column_TKT_taxable(EE_Ticket $ticket)
185
-    {
186
-        return $ticket->get('TKT_taxable') ? esc_html__('Yes', 'event_espresso') : esc_html__('No', 'event_espresso');
187
-    }
19
+	protected function _setup_data()
20
+	{
21
+		\EEH_Debug_Tools::printr(__FUNCTION__, __CLASS__, __FILE__, __LINE__, 2);
22
+		$trashed = $this->_admin_page->get_view() == 'trashed' ? true : false;
23
+		$this->_data = $this->_admin_page->get_default_tickets($this->_per_page, false, $trashed);
24
+		$this->_all_data_count = $this->_admin_page->get_default_tickets($this->_per_page, true, false);
25
+		$this->_trashed_count = $this->_admin_page->get_default_tickets($this->_per_page, true, true);
26
+	}
27
+
28
+
29
+	protected function _set_properties()
30
+	{
31
+		$this->_wp_list_args = array(
32
+			'singular' => esc_html__('ticket', 'event_espresso'),
33
+			'plural'   => esc_html__('tickets', 'event_espresso'),
34
+			'ajax'     => true,
35
+			'screen'   => $this->_admin_page->get_current_screen()->id,
36
+		);
37
+
38
+		$this->_columns = array(
39
+			'cb'              => '<input type="checkbox" />', // Render a checkbox instead of text
40
+			'id'              => esc_html__('ID', 'event_espresso'),
41
+			'TKT_name'        => esc_html__('Name', 'event_espresso'),
42
+			'TKT_description' => esc_html__('Description', 'event_espresso'),
43
+			'TKT_qty'         => esc_html__('Quantity', 'event_espresso'),
44
+			'TKT_uses'        => esc_html__('Uses', 'event_espresso'),
45
+			'TKT_min'         => esc_html__('Minimum', 'event_espresso'),
46
+			'TKT_max'         => esc_html__('Maximum', 'event_espresso'),
47
+			'TKT_price'       => esc_html__('Price', 'event_espresso'),
48
+			'TKT_taxable'     => esc_html__('Taxable', 'event_espresso'),
49
+		);
50
+
51
+		$this->_sortable_columns = array(
52
+			// TRUE means its already sorted
53
+			'id' => array('TKT_ID', false),
54
+			'TKT_name'        => array('TKT_name', true),
55
+			'TKT_description' => array('TKT_description', false),
56
+			'TKT_qty'         => array('TKT_qty', false),
57
+			'TKT_uses'        => array('TKT_uses', false),
58
+			'TKT_min'         => array('TKT_min', false),
59
+			'TKT_max'         => array('TKT_max', false),
60
+			'TKT_price'       => array('TKT_price', false),
61
+		);
62
+
63
+		$this->_hidden_columns = array();
64
+	}
65
+
66
+
67
+	protected function _get_table_filters()
68
+	{
69
+	}
70
+
71
+
72
+	protected function _add_view_counts()
73
+	{
74
+		$this->_views['all']['count'] = $this->_all_data_count;
75
+		if (EE_Registry::instance()->CAP->current_user_can('ee_delete_default_tickets', 'trash_ticket')) {
76
+			$this->_views['trashed']['count'] = $this->_trashed_count;
77
+		}
78
+	}
79
+
80
+
81
+	public function column_cb($item)
82
+	{
83
+		return $item->ID() === 1
84
+			? '<span class="dashicons dashicons-lock"></span>'
85
+			: sprintf(
86
+				'<input type="checkbox" name="checkbox[%1$s]" value="%1$s" />',
87
+				$item->ID()
88
+			);
89
+	}
90
+
91
+
92
+	/**
93
+	 * @param EE_Ticket $item
94
+	 * @return string
95
+	 * @throws EE_Error
96
+	 * @throws ReflectionException
97
+	 */
98
+	public function column_id($item): string
99
+	{
100
+		$content = '<span class="ee-entity-id">' . $item->ID() . '</span>';
101
+		$content .= '<span class="show-on-mobile-view-only">' . $this->column_TKT_name($item, false) . '</span>';
102
+		return $this->columnContent('id', $content, 'end');
103
+	}
104
+
105
+
106
+	public function column_TKT_name(EE_Ticket $ticket, bool $prep_content = true): string
107
+	{
108
+		// build row actions
109
+		$actions = array();
110
+
111
+		// trash links
112
+		if ($ticket->ID() !== 1) {
113
+			if ($this->_view == 'all') {
114
+				$trash_lnk_url = EE_Admin_Page::add_query_args_and_nonce(array(
115
+					'action' => 'trash_ticket',
116
+					'TKT_ID' => $ticket->ID(),
117
+				), TICKETS_ADMIN_URL);
118
+				$actions['trash'] = '<a href="' . $trash_lnk_url . '" aria-label="'
119
+									. esc_attr__('Move Ticket to trash', 'event_espresso') . '">'
120
+									. esc_html__('Trash', 'event_espresso') . '</a>';
121
+			} else {
122
+				// restore price link
123
+				$restore_lnk_url = EE_Admin_Page::add_query_args_and_nonce(array(
124
+					'action' => 'restore_ticket',
125
+					'TKT_ID' => $ticket->ID(),
126
+				), TICKETS_ADMIN_URL);
127
+				$actions['restore'] = '<a href="' . $restore_lnk_url . '" aria-label="'
128
+									  . esc_attr__('Restore Ticket', 'event_espresso') . '">'
129
+									  . esc_html__('Restore', 'event_espresso') . '</a>';
130
+				// delete price link
131
+				$delete_lnk_url = EE_Admin_Page::add_query_args_and_nonce(array(
132
+					'action' => 'delete_ticket',
133
+					'TKT_ID' => $ticket->ID(),
134
+				), TICKETS_ADMIN_URL);
135
+				$actions['delete'] = '<a href="' . $delete_lnk_url . '" aria-label="'
136
+									 . esc_attr__('Delete Ticket Permanently', 'event_espresso') . '">'
137
+									 . esc_html__('Delete Permanently', 'event_espresso') . '</a>';
138
+			}
139
+		}
140
+		// return $ticket->get('TKT_name') . $this->row_actions($actions);
141
+		$content = $prep_content ? $ticket->name() . $this->row_actions($actions) : $ticket->name();
142
+		return $prep_content ? $this->columnContent('TKT_name', $content) : $content;
143
+	}
144
+
145
+
146
+	public function column_TKT_description(EE_Ticket $ticket)
147
+	{
148
+		return $this->columnContent('TKT_description', $ticket->description());
149
+	}
150
+
151
+
152
+	public function column_TKT_qty(EE_Ticket $ticket)
153
+	{
154
+
155
+		return $ticket->get_pretty('TKT_qty', 'text');
156
+		// return $this->columnContent('TKT_qty', $ticket->qty());
157
+	}
158
+
159
+
160
+	public function column_TKT_uses(EE_Ticket $ticket)
161
+	{
162
+		return $ticket->get_pretty('TKT_uses', 'text');
163
+	}
164
+
165
+
166
+	public function column_TKT_min(EE_Ticket $ticket)
167
+	{
168
+		return $ticket->get('TKT_min');
169
+	}
170
+
171
+
172
+	public function column_TKT_max(EE_Ticket $ticket)
173
+	{
174
+		return $ticket->get_pretty('TKT_max', 'text');
175
+	}
176
+
177
+
178
+	public function column_TKT_price(EE_Ticket $ticket)
179
+	{
180
+		return EEH_Template::format_currency($ticket->get('TKT_price'));
181
+	}
182
+
183
+
184
+	public function column_TKT_taxable(EE_Ticket $ticket)
185
+	{
186
+		return $ticket->get('TKT_taxable') ? esc_html__('Yes', 'event_espresso') : esc_html__('No', 'event_espresso');
187
+	}
188 188
 }
Please login to merge, or discard this patch.
Spacing   +12 added lines, -12 removed lines patch added patch discarded remove patch
@@ -97,8 +97,8 @@  discard block
 block discarded – undo
97 97
      */
98 98
     public function column_id($item): string
99 99
     {
100
-        $content = '<span class="ee-entity-id">' . $item->ID() . '</span>';
101
-        $content .= '<span class="show-on-mobile-view-only">' . $this->column_TKT_name($item, false) . '</span>';
100
+        $content = '<span class="ee-entity-id">'.$item->ID().'</span>';
101
+        $content .= '<span class="show-on-mobile-view-only">'.$this->column_TKT_name($item, false).'</span>';
102 102
         return $this->columnContent('id', $content, 'end');
103 103
     }
104 104
 
@@ -115,30 +115,30 @@  discard block
 block discarded – undo
115 115
                     'action' => 'trash_ticket',
116 116
                     'TKT_ID' => $ticket->ID(),
117 117
                 ), TICKETS_ADMIN_URL);
118
-                $actions['trash'] = '<a href="' . $trash_lnk_url . '" aria-label="'
119
-                                    . esc_attr__('Move Ticket to trash', 'event_espresso') . '">'
120
-                                    . esc_html__('Trash', 'event_espresso') . '</a>';
118
+                $actions['trash'] = '<a href="'.$trash_lnk_url.'" aria-label="'
119
+                                    . esc_attr__('Move Ticket to trash', 'event_espresso').'">'
120
+                                    . esc_html__('Trash', 'event_espresso').'</a>';
121 121
             } else {
122 122
                 // restore price link
123 123
                 $restore_lnk_url = EE_Admin_Page::add_query_args_and_nonce(array(
124 124
                     'action' => 'restore_ticket',
125 125
                     'TKT_ID' => $ticket->ID(),
126 126
                 ), TICKETS_ADMIN_URL);
127
-                $actions['restore'] = '<a href="' . $restore_lnk_url . '" aria-label="'
128
-                                      . esc_attr__('Restore Ticket', 'event_espresso') . '">'
129
-                                      . esc_html__('Restore', 'event_espresso') . '</a>';
127
+                $actions['restore'] = '<a href="'.$restore_lnk_url.'" aria-label="'
128
+                                      . esc_attr__('Restore Ticket', 'event_espresso').'">'
129
+                                      . esc_html__('Restore', 'event_espresso').'</a>';
130 130
                 // delete price link
131 131
                 $delete_lnk_url = EE_Admin_Page::add_query_args_and_nonce(array(
132 132
                     'action' => 'delete_ticket',
133 133
                     'TKT_ID' => $ticket->ID(),
134 134
                 ), TICKETS_ADMIN_URL);
135
-                $actions['delete'] = '<a href="' . $delete_lnk_url . '" aria-label="'
136
-                                     . esc_attr__('Delete Ticket Permanently', 'event_espresso') . '">'
137
-                                     . esc_html__('Delete Permanently', 'event_espresso') . '</a>';
135
+                $actions['delete'] = '<a href="'.$delete_lnk_url.'" aria-label="'
136
+                                     . esc_attr__('Delete Ticket Permanently', 'event_espresso').'">'
137
+                                     . esc_html__('Delete Permanently', 'event_espresso').'</a>';
138 138
             }
139 139
         }
140 140
         // return $ticket->get('TKT_name') . $this->row_actions($actions);
141
-        $content = $prep_content ? $ticket->name() . $this->row_actions($actions) : $ticket->name();
141
+        $content = $prep_content ? $ticket->name().$this->row_actions($actions) : $ticket->name();
142 142
         return $prep_content ? $this->columnContent('TKT_name', $content) : $content;
143 143
     }
144 144
 
Please login to merge, or discard this patch.