Completed
Branch FET/extra-logging-when-trashin... (44a8f3)
by
unknown
16:47 queued 14:37
created
core/db_classes/EE_Registration.class.php 2 patches
Spacing   +15 added lines, -15 removed lines patch added patch discarded remove patch
@@ -148,7 +148,7 @@  discard block
 block discarded – undo
148 148
     {
149 149
         switch ($field_name) {
150 150
             case 'REG_code':
151
-                if (! empty($field_value) && $this->reg_code() === null) {
151
+                if ( ! empty($field_value) && $this->reg_code() === null) {
152 152
                     $this->set_reg_code($field_value, $use_default);
153 153
                 }
154 154
                 break;
@@ -449,7 +449,7 @@  discard block
 block discarded – undo
449 449
     public function event()
450 450
     {
451 451
         $event = $this->get_first_related('Event');
452
-        if (! $event instanceof \EE_Event) {
452
+        if ( ! $event instanceof \EE_Event) {
453 453
             throw new EntityNotFoundException('Event ID', $this->event_ID());
454 454
         }
455 455
         return $event;
@@ -496,7 +496,7 @@  discard block
 block discarded – undo
496 496
     {
497 497
         // reserved ticket and datetime counts will be decremented as sold counts are incremented
498 498
         // so stop tracking that this reg has a ticket reserved
499
-        $this->release_reserved_ticket(false, "REG: {$this->ID()} (ln:" . __LINE__ . ')');
499
+        $this->release_reserved_ticket(false, "REG: {$this->ID()} (ln:".__LINE__.')');
500 500
         $ticket = $this->ticket();
501 501
         $ticket->increaseSold();
502 502
         // possibly set event status to sold out
@@ -546,7 +546,7 @@  discard block
 block discarded – undo
546 546
             $reserved = $this->update_extra_meta(EE_Registration::HAS_RESERVED_TICKET_KEY, true);
547 547
             if ($reserved && $update_ticket) {
548 548
                 $ticket = $this->ticket();
549
-                $ticket->increaseReserved(1, "REG: {$this->ID()} (ln:" . __LINE__ . ')');
549
+                $ticket->increaseReserved(1, "REG: {$this->ID()} (ln:".__LINE__.')');
550 550
                 $this->update_extra_meta('reserve_ticket', "{$this->ticket_ID()} from {$source}");
551 551
                 $ticket->save();
552 552
             }
@@ -574,7 +574,7 @@  discard block
 block discarded – undo
574 574
             $reserved = $this->update_extra_meta(EE_Registration::HAS_RESERVED_TICKET_KEY, false);
575 575
             if ($reserved && $update_ticket) {
576 576
                 $ticket = $this->ticket();
577
-                $ticket->decreaseReserved(1, true, "REG: {$this->ID()} (ln:" . __LINE__ . ')');
577
+                $ticket->decreaseReserved(1, true, "REG: {$this->ID()} (ln:".__LINE__.')');
578 578
                 $this->update_extra_meta('release_reserved_ticket', "{$this->ticket_ID()} from {$source}");
579 579
             }
580 580
         }
@@ -1460,7 +1460,7 @@  discard block
 block discarded – undo
1460 1460
                     : '';
1461 1461
                 break;
1462 1462
         }
1463
-        return $icon . $status[ $this->status_ID() ];
1463
+        return $icon.$status[$this->status_ID()];
1464 1464
     }
1465 1465
 
1466 1466
 
@@ -1716,7 +1716,7 @@  discard block
 block discarded – undo
1716 1716
     {
1717 1717
         $DTT_ID = EEM_Datetime::instance()->ensure_is_ID($DTT_OR_ID);
1718 1718
         // first check registration status
1719
-        if (! $DTT_ID || ($check_approved && ! $this->is_approved())) {
1719
+        if ( ! $DTT_ID || ($check_approved && ! $this->is_approved())) {
1720 1720
             return false;
1721 1721
         }
1722 1722
         // is there a datetime ticket that matches this dtt_ID?
@@ -1756,7 +1756,7 @@  discard block
 block discarded – undo
1756 1756
     {
1757 1757
         $DTT_ID = EEM_Datetime::instance()->ensure_is_ID($DTT_OR_ID);
1758 1758
 
1759
-        if (! $DTT_ID) {
1759
+        if ( ! $DTT_ID) {
1760 1760
             return false;
1761 1761
         }
1762 1762
 
@@ -1764,7 +1764,7 @@  discard block
 block discarded – undo
1764 1764
 
1765 1765
         // if max uses is not set or equals infinity then return true cause its not a factor for whether user can
1766 1766
         // check-in or not.
1767
-        if (! $max_uses || $max_uses === EE_INF) {
1767
+        if ( ! $max_uses || $max_uses === EE_INF) {
1768 1768
             return true;
1769 1769
         }
1770 1770
 
@@ -1828,7 +1828,7 @@  discard block
 block discarded – undo
1828 1828
             $datetime = $this->get_latest_related_datetime();
1829 1829
             $DTT_ID = $datetime instanceof EE_Datetime ? $datetime->ID() : 0;
1830 1830
             // verify the registration can checkin for the given DTT_ID
1831
-        } elseif (! $this->can_checkin($DTT_ID, $verify)) {
1831
+        } elseif ( ! $this->can_checkin($DTT_ID, $verify)) {
1832 1832
             EE_Error::add_error(
1833 1833
                 sprintf(
1834 1834
                     esc_html__(
@@ -1851,7 +1851,7 @@  discard block
 block discarded – undo
1851 1851
         );
1852 1852
         // start by getting the current status so we know what status we'll be changing to.
1853 1853
         $cur_status = $this->check_in_status_for_datetime($DTT_ID);
1854
-        $status_to = $status_paths[ $cur_status ];
1854
+        $status_to = $status_paths[$cur_status];
1855 1855
         // database only records true for checked IN or false for checked OUT
1856 1856
         // no record ( null ) means checked in NEVER, but we obviously don't save that
1857 1857
         $new_status = $status_to === EE_Checkin::status_checked_in;
@@ -2042,7 +2042,7 @@  discard block
 block discarded – undo
2042 2042
     public function transaction()
2043 2043
     {
2044 2044
         $transaction = $this->get_first_related('Transaction');
2045
-        if (! $transaction instanceof \EE_Transaction) {
2045
+        if ( ! $transaction instanceof \EE_Transaction) {
2046 2046
             throw new EntityNotFoundException('Transaction ID', $this->transaction_ID());
2047 2047
         }
2048 2048
         return $transaction;
@@ -2115,11 +2115,11 @@  discard block
 block discarded – undo
2115 2115
             );
2116 2116
             return;
2117 2117
         }
2118
-        if (! $this->reg_code()) {
2118
+        if ( ! $this->reg_code()) {
2119 2119
             parent::set('REG_code', $REG_code, $use_default);
2120 2120
         } else {
2121 2121
             EE_Error::doing_it_wrong(
2122
-                __CLASS__ . '::' . __FUNCTION__,
2122
+                __CLASS__.'::'.__FUNCTION__,
2123 2123
                 esc_html__('Can not change a registration REG_code once it has been set.', 'event_espresso'),
2124 2124
                 '4.6.0'
2125 2125
             );
@@ -2310,7 +2310,7 @@  discard block
 block discarded – undo
2310 2310
                 break;
2311 2311
             }
2312 2312
         }
2313
-        if (! ($line_item instanceof \EE_Line_Item && $line_item->OBJ_type() === 'Ticket')) {
2313
+        if ( ! ($line_item instanceof \EE_Line_Item && $line_item->OBJ_type() === 'Ticket')) {
2314 2314
             throw new EntityNotFoundException('Line Item Ticket ID', $ticket->ID());
2315 2315
         }
2316 2316
         return $line_item;
Please login to merge, or discard this patch.
Indentation   +2531 added lines, -2531 removed lines patch added patch discarded remove patch
@@ -16,2535 +16,2535 @@
 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
-     * extra meta key for tracking when registrations are trashed and by who
59
-     *
60
-     * @type string
61
-     */
62
-    const EXTRA_META_KEY_REG_TRASHED = 'registration-trashed';
63
-
64
-    /**
65
-     * extra meta key for tracking when registrations are restored and by who
66
-     *
67
-     * @type string
68
-     */
69
-    const EXTRA_META_KEY_REG_RESTORED = 'registration-restored';
70
-
71
-
72
-    /**
73
-     * @param array  $props_n_values          incoming values
74
-     * @param string $timezone                incoming timezone (if not set the timezone set for the website will be
75
-     *                                        used.)
76
-     * @param array  $date_formats            incoming date_formats in an array where the first value is the
77
-     *                                        date_format and the second value is the time format
78
-     * @return EE_Registration
79
-     * @throws EE_Error
80
-     * @throws InvalidArgumentException
81
-     * @throws InvalidDataTypeException
82
-     * @throws InvalidInterfaceException
83
-     * @throws ReflectionException
84
-     */
85
-    public static function new_instance($props_n_values = array(), $timezone = null, $date_formats = array())
86
-    {
87
-        $has_object = parent::_check_for_object($props_n_values, __CLASS__, $timezone, $date_formats);
88
-        return $has_object ? $has_object : new self($props_n_values, false, $timezone, $date_formats);
89
-    }
90
-
91
-
92
-    /**
93
-     * @param array  $props_n_values  incoming values from the database
94
-     * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
95
-     *                                the website will be used.
96
-     * @return EE_Registration
97
-     * @throws EE_Error
98
-     * @throws InvalidArgumentException
99
-     * @throws InvalidDataTypeException
100
-     * @throws InvalidInterfaceException
101
-     * @throws ReflectionException
102
-     */
103
-    public static function new_instance_from_db($props_n_values = array(), $timezone = null)
104
-    {
105
-        return new self($props_n_values, true, $timezone);
106
-    }
107
-
108
-
109
-    /**
110
-     *        Set Event ID
111
-     *
112
-     * @param        int $EVT_ID Event ID
113
-     * @throws DomainException
114
-     * @throws EE_Error
115
-     * @throws EntityNotFoundException
116
-     * @throws InvalidArgumentException
117
-     * @throws InvalidDataTypeException
118
-     * @throws InvalidInterfaceException
119
-     * @throws ReflectionException
120
-     * @throws RuntimeException
121
-     * @throws UnexpectedEntityException
122
-     */
123
-    public function set_event($EVT_ID = 0)
124
-    {
125
-        $this->set('EVT_ID', $EVT_ID);
126
-    }
127
-
128
-
129
-    /**
130
-     * Overrides parent set() method so that all calls to set( 'REG_code', $REG_code ) OR set( 'STS_ID', $STS_ID ) can
131
-     * be routed to internal methods
132
-     *
133
-     * @param string $field_name
134
-     * @param mixed  $field_value
135
-     * @param bool   $use_default
136
-     * @throws DomainException
137
-     * @throws EE_Error
138
-     * @throws EntityNotFoundException
139
-     * @throws InvalidArgumentException
140
-     * @throws InvalidDataTypeException
141
-     * @throws InvalidInterfaceException
142
-     * @throws ReflectionException
143
-     * @throws RuntimeException
144
-     * @throws UnexpectedEntityException
145
-     */
146
-    public function set($field_name, $field_value, $use_default = false)
147
-    {
148
-        switch ($field_name) {
149
-            case 'REG_code':
150
-                if (! empty($field_value) && $this->reg_code() === null) {
151
-                    $this->set_reg_code($field_value, $use_default);
152
-                }
153
-                break;
154
-            case 'STS_ID':
155
-                $this->set_status($field_value, $use_default);
156
-                break;
157
-            default:
158
-                parent::set($field_name, $field_value, $use_default);
159
-        }
160
-    }
161
-
162
-
163
-    /**
164
-     * Set Status ID
165
-     * updates the registration status and ALSO...
166
-     * calls reserve_registration_space() if the reg status changes TO approved from any other reg status
167
-     * calls release_registration_space() if the reg status changes FROM approved to any other reg status
168
-     *
169
-     * @param string                $new_STS_ID
170
-     * @param boolean               $use_default
171
-     * @param ContextInterface|null $context
172
-     * @return bool
173
-     * @throws DomainException
174
-     * @throws EE_Error
175
-     * @throws EntityNotFoundException
176
-     * @throws InvalidArgumentException
177
-     * @throws InvalidDataTypeException
178
-     * @throws InvalidInterfaceException
179
-     * @throws ReflectionException
180
-     * @throws RuntimeException
181
-     * @throws UnexpectedEntityException
182
-     */
183
-    public function set_status($new_STS_ID = null, $use_default = false, ContextInterface $context = null)
184
-    {
185
-        // get current REG_Status
186
-        $old_STS_ID = $this->status_ID();
187
-        // if status has changed
188
-        if (
189
-            $old_STS_ID !== $new_STS_ID // and that status has actually changed
190
-            && ! empty($old_STS_ID) // and that old status is actually set
191
-            && ! empty($new_STS_ID) // as well as the new status
192
-            && $this->ID() // ensure registration is in the db
193
-        ) {
194
-            // update internal status first
195
-            parent::set('STS_ID', $new_STS_ID, $use_default);
196
-            // THEN handle other changes that occur when reg status changes
197
-            // TO approved
198
-            if ($new_STS_ID === EEM_Registration::status_id_approved) {
199
-                // reserve a space by incrementing ticket and datetime sold values
200
-                $this->reserveRegistrationSpace();
201
-                do_action('AHEE__EE_Registration__set_status__to_approved', $this, $old_STS_ID, $new_STS_ID, $context);
202
-                // OR FROM  approved
203
-            } elseif ($old_STS_ID === EEM_Registration::status_id_approved) {
204
-                // release a space by decrementing ticket and datetime sold values
205
-                $this->releaseRegistrationSpace();
206
-                do_action(
207
-                    'AHEE__EE_Registration__set_status__from_approved',
208
-                    $this,
209
-                    $old_STS_ID,
210
-                    $new_STS_ID,
211
-                    $context
212
-                );
213
-            }
214
-            // update status
215
-            parent::set('STS_ID', $new_STS_ID, $use_default);
216
-            $this->updateIfCanceledOrReinstated($new_STS_ID, $old_STS_ID, $context);
217
-            if ($this->statusChangeUpdatesTransaction($context)) {
218
-                $this->updateTransactionAfterStatusChange();
219
-            }
220
-            do_action('AHEE__EE_Registration__set_status__after_update', $this, $old_STS_ID, $new_STS_ID, $context);
221
-            return true;
222
-        }
223
-        // even though the old value matches the new value, it's still good to
224
-        // allow the parent set method to have a say
225
-        parent::set('STS_ID', $new_STS_ID, $use_default);
226
-        return true;
227
-    }
228
-
229
-
230
-    /**
231
-     * update REGs and TXN when cancelled or declined registrations involved
232
-     *
233
-     * @param string                $new_STS_ID
234
-     * @param string                $old_STS_ID
235
-     * @param ContextInterface|null $context
236
-     * @throws EE_Error
237
-     * @throws InvalidArgumentException
238
-     * @throws InvalidDataTypeException
239
-     * @throws InvalidInterfaceException
240
-     * @throws ReflectionException
241
-     * @throws RuntimeException
242
-     */
243
-    private function updateIfCanceledOrReinstated($new_STS_ID, $old_STS_ID, ContextInterface $context = null)
244
-    {
245
-        // these reg statuses should not be considered in any calculations involving monies owing
246
-        $closed_reg_statuses = EEM_Registration::closed_reg_statuses();
247
-        // true if registration has been cancelled or declined
248
-        $this->updateIfCanceled(
249
-            $closed_reg_statuses,
250
-            $new_STS_ID,
251
-            $old_STS_ID,
252
-            $context
253
-        );
254
-        $this->updateIfReinstated(
255
-            $closed_reg_statuses,
256
-            $new_STS_ID,
257
-            $old_STS_ID,
258
-            $context
259
-        );
260
-    }
261
-
262
-
263
-    /**
264
-     * update REGs and TXN when cancelled or declined registrations involved
265
-     *
266
-     * @param array                 $closed_reg_statuses
267
-     * @param string                $new_STS_ID
268
-     * @param string                $old_STS_ID
269
-     * @param ContextInterface|null $context
270
-     * @throws EE_Error
271
-     * @throws InvalidArgumentException
272
-     * @throws InvalidDataTypeException
273
-     * @throws InvalidInterfaceException
274
-     * @throws ReflectionException
275
-     * @throws RuntimeException
276
-     */
277
-    private function updateIfCanceled(
278
-        array $closed_reg_statuses,
279
-        $new_STS_ID,
280
-        $old_STS_ID,
281
-        ContextInterface $context = null
282
-    ) {
283
-        // true if registration has been cancelled or declined
284
-        if (
285
-            in_array($new_STS_ID, $closed_reg_statuses, true)
286
-            && ! in_array($old_STS_ID, $closed_reg_statuses, true)
287
-        ) {
288
-            /** @type EE_Registration_Processor $registration_processor */
289
-            $registration_processor = EE_Registry::instance()->load_class('Registration_Processor');
290
-            /** @type EE_Transaction_Processor $transaction_processor */
291
-            $transaction_processor = EE_Registry::instance()->load_class('Transaction_Processor');
292
-            // cancelled or declined registration
293
-            $registration_processor->update_registration_after_being_canceled_or_declined(
294
-                $this,
295
-                $closed_reg_statuses
296
-            );
297
-            $transaction_processor->update_transaction_after_canceled_or_declined_registration(
298
-                $this,
299
-                $closed_reg_statuses,
300
-                false
301
-            );
302
-            do_action(
303
-                'AHEE__EE_Registration__set_status__canceled_or_declined',
304
-                $this,
305
-                $old_STS_ID,
306
-                $new_STS_ID,
307
-                $context
308
-            );
309
-            return;
310
-        }
311
-    }
312
-
313
-
314
-    /**
315
-     * update REGs and TXN when cancelled or declined registrations involved
316
-     *
317
-     * @param array                 $closed_reg_statuses
318
-     * @param string                $new_STS_ID
319
-     * @param string                $old_STS_ID
320
-     * @param ContextInterface|null $context
321
-     * @throws EE_Error
322
-     * @throws InvalidArgumentException
323
-     * @throws InvalidDataTypeException
324
-     * @throws InvalidInterfaceException
325
-     * @throws ReflectionException
326
-     * @throws RuntimeException
327
-     */
328
-    private function updateIfReinstated(
329
-        array $closed_reg_statuses,
330
-        $new_STS_ID,
331
-        $old_STS_ID,
332
-        ContextInterface $context = null
333
-    ) {
334
-        // true if reinstating cancelled or declined registration
335
-        if (
336
-            in_array($old_STS_ID, $closed_reg_statuses, true)
337
-            && ! in_array($new_STS_ID, $closed_reg_statuses, true)
338
-        ) {
339
-            /** @type EE_Registration_Processor $registration_processor */
340
-            $registration_processor = EE_Registry::instance()->load_class('Registration_Processor');
341
-            /** @type EE_Transaction_Processor $transaction_processor */
342
-            $transaction_processor = EE_Registry::instance()->load_class('Transaction_Processor');
343
-            // reinstating cancelled or declined registration
344
-            $registration_processor->update_canceled_or_declined_registration_after_being_reinstated(
345
-                $this,
346
-                $closed_reg_statuses
347
-            );
348
-            $transaction_processor->update_transaction_after_reinstating_canceled_registration(
349
-                $this,
350
-                $closed_reg_statuses,
351
-                false
352
-            );
353
-            do_action(
354
-                'AHEE__EE_Registration__set_status__after_reinstated',
355
-                $this,
356
-                $old_STS_ID,
357
-                $new_STS_ID,
358
-                $context
359
-            );
360
-        }
361
-    }
362
-
363
-
364
-    /**
365
-     * @param ContextInterface|null $context
366
-     * @return bool
367
-     */
368
-    private function statusChangeUpdatesTransaction(ContextInterface $context = null)
369
-    {
370
-        $contexts_that_do_not_update_transaction = (array) apply_filters(
371
-            'AHEE__EE_Registration__statusChangeUpdatesTransaction__contexts_that_do_not_update_transaction',
372
-            array('spco_reg_step_attendee_information_process_registrations'),
373
-            $context,
374
-            $this
375
-        );
376
-        return ! (
377
-            $context instanceof ContextInterface
378
-            && in_array($context->slug(), $contexts_that_do_not_update_transaction, true)
379
-        );
380
-    }
381
-
382
-
383
-    /**
384
-     * @throws EE_Error
385
-     * @throws EntityNotFoundException
386
-     * @throws InvalidArgumentException
387
-     * @throws InvalidDataTypeException
388
-     * @throws InvalidInterfaceException
389
-     * @throws ReflectionException
390
-     * @throws RuntimeException
391
-     */
392
-    private function updateTransactionAfterStatusChange()
393
-    {
394
-        /** @type EE_Transaction_Payments $transaction_payments */
395
-        $transaction_payments = EE_Registry::instance()->load_class('Transaction_Payments');
396
-        $transaction_payments->recalculate_transaction_total($this->transaction(), false);
397
-        $this->transaction()->update_status_based_on_total_paid();
398
-    }
399
-
400
-
401
-    /**
402
-     * get Status ID
403
-     *
404
-     * @throws EE_Error
405
-     * @throws InvalidArgumentException
406
-     * @throws InvalidDataTypeException
407
-     * @throws InvalidInterfaceException
408
-     * @throws ReflectionException
409
-     */
410
-    public function status_ID()
411
-    {
412
-        return $this->get('STS_ID');
413
-    }
414
-
415
-
416
-    /**
417
-     * Gets the ticket this registration is for
418
-     *
419
-     * @param boolean $include_archived whether to include archived tickets or not.
420
-     * @return EE_Ticket|EE_Base_Class
421
-     * @throws EE_Error
422
-     * @throws InvalidArgumentException
423
-     * @throws InvalidDataTypeException
424
-     * @throws InvalidInterfaceException
425
-     * @throws ReflectionException
426
-     */
427
-    public function ticket($include_archived = true)
428
-    {
429
-        $query_params = array();
430
-        if ($include_archived) {
431
-            $query_params['default_where_conditions'] = 'none';
432
-        }
433
-        return $this->get_first_related('Ticket', $query_params);
434
-    }
435
-
436
-
437
-    /**
438
-     * Gets the event this registration is for
439
-     *
440
-     * @return EE_Event
441
-     * @throws EE_Error
442
-     * @throws EntityNotFoundException
443
-     * @throws InvalidArgumentException
444
-     * @throws InvalidDataTypeException
445
-     * @throws InvalidInterfaceException
446
-     * @throws ReflectionException
447
-     */
448
-    public function event()
449
-    {
450
-        $event = $this->get_first_related('Event');
451
-        if (! $event instanceof \EE_Event) {
452
-            throw new EntityNotFoundException('Event ID', $this->event_ID());
453
-        }
454
-        return $event;
455
-    }
456
-
457
-
458
-    /**
459
-     * Gets the "author" of the registration.  Note that for the purposes of registrations, the author will correspond
460
-     * with the author of the event this registration is for.
461
-     *
462
-     * @since 4.5.0
463
-     * @return int
464
-     * @throws EE_Error
465
-     * @throws EntityNotFoundException
466
-     * @throws InvalidArgumentException
467
-     * @throws InvalidDataTypeException
468
-     * @throws InvalidInterfaceException
469
-     * @throws ReflectionException
470
-     */
471
-    public function wp_user()
472
-    {
473
-        $event = $this->event();
474
-        if ($event instanceof EE_Event) {
475
-            return $event->wp_user();
476
-        }
477
-        return 0;
478
-    }
479
-
480
-
481
-    /**
482
-     * increments this registration's related ticket sold and corresponding datetime sold values
483
-     *
484
-     * @return void
485
-     * @throws DomainException
486
-     * @throws EE_Error
487
-     * @throws EntityNotFoundException
488
-     * @throws InvalidArgumentException
489
-     * @throws InvalidDataTypeException
490
-     * @throws InvalidInterfaceException
491
-     * @throws ReflectionException
492
-     * @throws UnexpectedEntityException
493
-     */
494
-    private function reserveRegistrationSpace()
495
-    {
496
-        // reserved ticket and datetime counts will be decremented as sold counts are incremented
497
-        // so stop tracking that this reg has a ticket reserved
498
-        $this->release_reserved_ticket(false, "REG: {$this->ID()} (ln:" . __LINE__ . ')');
499
-        $ticket = $this->ticket();
500
-        $ticket->increaseSold();
501
-        // possibly set event status to sold out
502
-        $this->event()->perform_sold_out_status_check();
503
-    }
504
-
505
-
506
-    /**
507
-     * decrements (subtracts) this registration's related ticket sold and corresponding datetime sold values
508
-     *
509
-     * @return void
510
-     * @throws DomainException
511
-     * @throws EE_Error
512
-     * @throws EntityNotFoundException
513
-     * @throws InvalidArgumentException
514
-     * @throws InvalidDataTypeException
515
-     * @throws InvalidInterfaceException
516
-     * @throws ReflectionException
517
-     * @throws UnexpectedEntityException
518
-     */
519
-    private function releaseRegistrationSpace()
520
-    {
521
-        $ticket = $this->ticket();
522
-        $ticket->decreaseSold();
523
-        // possibly change event status from sold out back to previous status
524
-        $this->event()->perform_sold_out_status_check();
525
-    }
526
-
527
-
528
-    /**
529
-     * tracks this registration's ticket reservation in extra meta
530
-     * and can increment related ticket reserved and corresponding datetime reserved values
531
-     *
532
-     * @param bool   $update_ticket if true, will increment ticket and datetime reserved count
533
-     * @param string $source
534
-     * @return void
535
-     * @throws EE_Error
536
-     * @throws InvalidArgumentException
537
-     * @throws InvalidDataTypeException
538
-     * @throws InvalidInterfaceException
539
-     * @throws ReflectionException
540
-     */
541
-    public function reserve_ticket($update_ticket = false, $source = 'unknown')
542
-    {
543
-        // only reserve ticket if space is not currently reserved
544
-        if ((bool) $this->get_extra_meta(EE_Registration::HAS_RESERVED_TICKET_KEY, true) !== true) {
545
-            $reserved = $this->update_extra_meta(EE_Registration::HAS_RESERVED_TICKET_KEY, true);
546
-            if ($reserved && $update_ticket) {
547
-                $ticket = $this->ticket();
548
-                $ticket->increaseReserved(1, "REG: {$this->ID()} (ln:" . __LINE__ . ')');
549
-                $this->update_extra_meta('reserve_ticket', "{$this->ticket_ID()} from {$source}");
550
-                $ticket->save();
551
-            }
552
-        }
553
-    }
554
-
555
-
556
-    /**
557
-     * stops tracking this registration's ticket reservation in extra meta
558
-     * decrements (subtracts) related ticket reserved and corresponding datetime reserved values
559
-     *
560
-     * @param bool   $update_ticket if true, will decrement ticket and datetime reserved count
561
-     * @param string $source
562
-     * @return void
563
-     * @throws EE_Error
564
-     * @throws InvalidArgumentException
565
-     * @throws InvalidDataTypeException
566
-     * @throws InvalidInterfaceException
567
-     * @throws ReflectionException
568
-     */
569
-    public function release_reserved_ticket($update_ticket = false, $source = 'unknown')
570
-    {
571
-        // only release ticket if space is currently reserved
572
-        if ((bool) $this->get_extra_meta(EE_Registration::HAS_RESERVED_TICKET_KEY, true) === true) {
573
-            $reserved = $this->update_extra_meta(EE_Registration::HAS_RESERVED_TICKET_KEY, false);
574
-            if ($reserved && $update_ticket) {
575
-                $ticket = $this->ticket();
576
-                $ticket->decreaseReserved(1, true, "REG: {$this->ID()} (ln:" . __LINE__ . ')');
577
-                $this->update_extra_meta('release_reserved_ticket', "{$this->ticket_ID()} from {$source}");
578
-            }
579
-        }
580
-    }
581
-
582
-
583
-    /**
584
-     * Set Attendee ID
585
-     *
586
-     * @param        int $ATT_ID Attendee ID
587
-     * @throws DomainException
588
-     * @throws EE_Error
589
-     * @throws EntityNotFoundException
590
-     * @throws InvalidArgumentException
591
-     * @throws InvalidDataTypeException
592
-     * @throws InvalidInterfaceException
593
-     * @throws ReflectionException
594
-     * @throws RuntimeException
595
-     * @throws UnexpectedEntityException
596
-     */
597
-    public function set_attendee_id($ATT_ID = 0)
598
-    {
599
-        $this->set('ATT_ID', $ATT_ID);
600
-    }
601
-
602
-
603
-    /**
604
-     *        Set Transaction ID
605
-     *
606
-     * @param        int $TXN_ID Transaction ID
607
-     * @throws DomainException
608
-     * @throws EE_Error
609
-     * @throws EntityNotFoundException
610
-     * @throws InvalidArgumentException
611
-     * @throws InvalidDataTypeException
612
-     * @throws InvalidInterfaceException
613
-     * @throws ReflectionException
614
-     * @throws RuntimeException
615
-     * @throws UnexpectedEntityException
616
-     */
617
-    public function set_transaction_id($TXN_ID = 0)
618
-    {
619
-        $this->set('TXN_ID', $TXN_ID);
620
-    }
621
-
622
-
623
-    /**
624
-     *        Set Session
625
-     *
626
-     * @param    string $REG_session PHP Session ID
627
-     * @throws DomainException
628
-     * @throws EE_Error
629
-     * @throws EntityNotFoundException
630
-     * @throws InvalidArgumentException
631
-     * @throws InvalidDataTypeException
632
-     * @throws InvalidInterfaceException
633
-     * @throws ReflectionException
634
-     * @throws RuntimeException
635
-     * @throws UnexpectedEntityException
636
-     */
637
-    public function set_session($REG_session = '')
638
-    {
639
-        $this->set('REG_session', $REG_session);
640
-    }
641
-
642
-
643
-    /**
644
-     *        Set Registration URL Link
645
-     *
646
-     * @param    string $REG_url_link Registration URL Link
647
-     * @throws DomainException
648
-     * @throws EE_Error
649
-     * @throws EntityNotFoundException
650
-     * @throws InvalidArgumentException
651
-     * @throws InvalidDataTypeException
652
-     * @throws InvalidInterfaceException
653
-     * @throws ReflectionException
654
-     * @throws RuntimeException
655
-     * @throws UnexpectedEntityException
656
-     */
657
-    public function set_reg_url_link($REG_url_link = '')
658
-    {
659
-        $this->set('REG_url_link', $REG_url_link);
660
-    }
661
-
662
-
663
-    /**
664
-     *        Set Attendee Counter
665
-     *
666
-     * @param        int $REG_count Primary Attendee
667
-     * @throws DomainException
668
-     * @throws EE_Error
669
-     * @throws EntityNotFoundException
670
-     * @throws InvalidArgumentException
671
-     * @throws InvalidDataTypeException
672
-     * @throws InvalidInterfaceException
673
-     * @throws ReflectionException
674
-     * @throws RuntimeException
675
-     * @throws UnexpectedEntityException
676
-     */
677
-    public function set_count($REG_count = 1)
678
-    {
679
-        $this->set('REG_count', $REG_count);
680
-    }
681
-
682
-
683
-    /**
684
-     *        Set Group Size
685
-     *
686
-     * @param        boolean $REG_group_size Group Registration
687
-     * @throws DomainException
688
-     * @throws EE_Error
689
-     * @throws EntityNotFoundException
690
-     * @throws InvalidArgumentException
691
-     * @throws InvalidDataTypeException
692
-     * @throws InvalidInterfaceException
693
-     * @throws ReflectionException
694
-     * @throws RuntimeException
695
-     * @throws UnexpectedEntityException
696
-     */
697
-    public function set_group_size($REG_group_size = false)
698
-    {
699
-        $this->set('REG_group_size', $REG_group_size);
700
-    }
701
-
702
-
703
-    /**
704
-     *    is_not_approved -  convenience method that returns TRUE if REG status ID ==
705
-     *    EEM_Registration::status_id_not_approved
706
-     *
707
-     * @return        boolean
708
-     * @throws EE_Error
709
-     * @throws InvalidArgumentException
710
-     * @throws InvalidDataTypeException
711
-     * @throws InvalidInterfaceException
712
-     * @throws ReflectionException
713
-     */
714
-    public function is_not_approved()
715
-    {
716
-        return $this->status_ID() === EEM_Registration::status_id_not_approved;
717
-    }
718
-
719
-
720
-    /**
721
-     *    is_pending_payment -  convenience method that returns TRUE if REG status ID ==
722
-     *    EEM_Registration::status_id_pending_payment
723
-     *
724
-     * @return        boolean
725
-     * @throws EE_Error
726
-     * @throws InvalidArgumentException
727
-     * @throws InvalidDataTypeException
728
-     * @throws InvalidInterfaceException
729
-     * @throws ReflectionException
730
-     */
731
-    public function is_pending_payment()
732
-    {
733
-        return $this->status_ID() === EEM_Registration::status_id_pending_payment;
734
-    }
735
-
736
-
737
-    /**
738
-     *    is_approved -  convenience method that returns TRUE if REG status ID == EEM_Registration::status_id_approved
739
-     *
740
-     * @return        boolean
741
-     * @throws EE_Error
742
-     * @throws InvalidArgumentException
743
-     * @throws InvalidDataTypeException
744
-     * @throws InvalidInterfaceException
745
-     * @throws ReflectionException
746
-     */
747
-    public function is_approved()
748
-    {
749
-        return $this->status_ID() === EEM_Registration::status_id_approved;
750
-    }
751
-
752
-
753
-    /**
754
-     *    is_cancelled -  convenience method that returns TRUE if REG status ID == EEM_Registration::status_id_cancelled
755
-     *
756
-     * @return        boolean
757
-     * @throws EE_Error
758
-     * @throws InvalidArgumentException
759
-     * @throws InvalidDataTypeException
760
-     * @throws InvalidInterfaceException
761
-     * @throws ReflectionException
762
-     */
763
-    public function is_cancelled()
764
-    {
765
-        return $this->status_ID() === EEM_Registration::status_id_cancelled;
766
-    }
767
-
768
-
769
-    /**
770
-     *    is_declined -  convenience method that returns TRUE if REG status ID == EEM_Registration::status_id_declined
771
-     *
772
-     * @return        boolean
773
-     * @throws EE_Error
774
-     * @throws InvalidArgumentException
775
-     * @throws InvalidDataTypeException
776
-     * @throws InvalidInterfaceException
777
-     * @throws ReflectionException
778
-     */
779
-    public function is_declined()
780
-    {
781
-        return $this->status_ID() === EEM_Registration::status_id_declined;
782
-    }
783
-
784
-
785
-    /**
786
-     *    is_incomplete -  convenience method that returns TRUE if REG status ID ==
787
-     *    EEM_Registration::status_id_incomplete
788
-     *
789
-     * @return        boolean
790
-     * @throws EE_Error
791
-     * @throws InvalidArgumentException
792
-     * @throws InvalidDataTypeException
793
-     * @throws InvalidInterfaceException
794
-     * @throws ReflectionException
795
-     */
796
-    public function is_incomplete()
797
-    {
798
-        return $this->status_ID() === EEM_Registration::status_id_incomplete;
799
-    }
800
-
801
-
802
-    /**
803
-     *        Set Registration Date
804
-     *
805
-     * @param        mixed ( int or string ) $REG_date Registration Date - Unix timestamp or string representation of
806
-     *                                                 Date
807
-     * @throws DomainException
808
-     * @throws EE_Error
809
-     * @throws EntityNotFoundException
810
-     * @throws InvalidArgumentException
811
-     * @throws InvalidDataTypeException
812
-     * @throws InvalidInterfaceException
813
-     * @throws ReflectionException
814
-     * @throws RuntimeException
815
-     * @throws UnexpectedEntityException
816
-     */
817
-    public function set_reg_date($REG_date = false)
818
-    {
819
-        $this->set('REG_date', $REG_date);
820
-    }
821
-
822
-
823
-    /**
824
-     *    Set final price owing for this registration after all ticket/price modifications
825
-     *
826
-     * @param    float $REG_final_price
827
-     * @throws DomainException
828
-     * @throws EE_Error
829
-     * @throws EntityNotFoundException
830
-     * @throws InvalidArgumentException
831
-     * @throws InvalidDataTypeException
832
-     * @throws InvalidInterfaceException
833
-     * @throws ReflectionException
834
-     * @throws RuntimeException
835
-     * @throws UnexpectedEntityException
836
-     */
837
-    public function set_final_price($REG_final_price = 0.00)
838
-    {
839
-        $this->set('REG_final_price', $REG_final_price);
840
-    }
841
-
842
-
843
-    /**
844
-     *    Set amount paid towards this registration's final price
845
-     *
846
-     * @param    float $REG_paid
847
-     * @throws DomainException
848
-     * @throws EE_Error
849
-     * @throws EntityNotFoundException
850
-     * @throws InvalidArgumentException
851
-     * @throws InvalidDataTypeException
852
-     * @throws InvalidInterfaceException
853
-     * @throws ReflectionException
854
-     * @throws RuntimeException
855
-     * @throws UnexpectedEntityException
856
-     */
857
-    public function set_paid($REG_paid = 0.00)
858
-    {
859
-        $this->set('REG_paid', $REG_paid);
860
-    }
861
-
862
-
863
-    /**
864
-     *        Attendee Is Going
865
-     *
866
-     * @param        boolean $REG_att_is_going Attendee Is Going
867
-     * @throws DomainException
868
-     * @throws EE_Error
869
-     * @throws EntityNotFoundException
870
-     * @throws InvalidArgumentException
871
-     * @throws InvalidDataTypeException
872
-     * @throws InvalidInterfaceException
873
-     * @throws ReflectionException
874
-     * @throws RuntimeException
875
-     * @throws UnexpectedEntityException
876
-     */
877
-    public function set_att_is_going($REG_att_is_going = false)
878
-    {
879
-        $this->set('REG_att_is_going', $REG_att_is_going);
880
-    }
881
-
882
-
883
-    /**
884
-     * Gets the related attendee
885
-     *
886
-     * @return EE_Attendee|EE_Base_Class
887
-     * @throws EE_Error
888
-     * @throws InvalidArgumentException
889
-     * @throws InvalidDataTypeException
890
-     * @throws InvalidInterfaceException
891
-     * @throws ReflectionException
892
-     */
893
-    public function attendee()
894
-    {
895
-        return $this->get_first_related('Attendee');
896
-    }
897
-
898
-    /**
899
-     * Gets the name of the attendee.
900
-     * @since 4.10.12.p
901
-     * @param bool $apply_html_entities set to true if you want to use HTML entities.
902
-     * @return string
903
-     * @throws EE_Error
904
-     * @throws InvalidArgumentException
905
-     * @throws InvalidDataTypeException
906
-     * @throws InvalidInterfaceException
907
-     * @throws ReflectionException
908
-     */
909
-    public function attendeeName($apply_html_entities = false)
910
-    {
911
-        $attendee = $this->get_first_related('Attendee');
912
-        if ($attendee instanceof EE_Attendee) {
913
-            $attendee_name = $attendee->full_name($apply_html_entities);
914
-        } else {
915
-            $attendee_name = esc_html__('Unknown', 'event_espresso');
916
-        }
917
-        return $attendee_name;
918
-    }
919
-
920
-
921
-    /**
922
-     *        get Event ID
923
-     */
924
-    public function event_ID()
925
-    {
926
-        return $this->get('EVT_ID');
927
-    }
928
-
929
-
930
-    /**
931
-     *        get Event ID
932
-     */
933
-    public function event_name()
934
-    {
935
-        $event = $this->event_obj();
936
-        if ($event) {
937
-            return $event->name();
938
-        } else {
939
-            return null;
940
-        }
941
-    }
942
-
943
-
944
-    /**
945
-     * Fetches the event this registration is for
946
-     *
947
-     * @return EE_Base_Class|EE_Event
948
-     * @throws EE_Error
949
-     * @throws InvalidArgumentException
950
-     * @throws InvalidDataTypeException
951
-     * @throws InvalidInterfaceException
952
-     * @throws ReflectionException
953
-     */
954
-    public function event_obj()
955
-    {
956
-        return $this->get_first_related('Event');
957
-    }
958
-
959
-
960
-    /**
961
-     *        get Attendee ID
962
-     */
963
-    public function attendee_ID()
964
-    {
965
-        return $this->get('ATT_ID');
966
-    }
967
-
968
-
969
-    /**
970
-     *        get PHP Session ID
971
-     */
972
-    public function session_ID()
973
-    {
974
-        return $this->get('REG_session');
975
-    }
976
-
977
-
978
-    /**
979
-     * Gets the string which represents the URL trigger for the receipt template in the message template system.
980
-     *
981
-     * @param string $messenger 'pdf' or 'html'.  Default 'html'.
982
-     * @return string
983
-     * @throws DomainException
984
-     * @throws EE_Error
985
-     * @throws InvalidArgumentException
986
-     * @throws InvalidDataTypeException
987
-     * @throws InvalidInterfaceException
988
-     * @throws ReflectionException
989
-     */
990
-    public function receipt_url($messenger = 'html')
991
-    {
992
-
993
-        /**
994
-         * The below will be deprecated one version after this.  We check first if there is a custom receipt template
995
-         * already in use on old system.  If there is then we just return the standard url for it.
996
-         *
997
-         * @since 4.5.0
998
-         */
999
-        $template_relative_path = 'modules/gateways/Invoice/lib/templates/receipt_body.template.php';
1000
-        $has_custom = EEH_Template::locate_template(
1001
-            $template_relative_path,
1002
-            array(),
1003
-            true,
1004
-            true,
1005
-            true
1006
-        );
1007
-
1008
-        if ($has_custom) {
1009
-            return add_query_arg(array('receipt' => 'true'), $this->invoice_url('launch'));
1010
-        }
1011
-        return apply_filters('FHEE__EE_Registration__receipt_url__receipt_url', '', $this, $messenger, 'receipt');
1012
-    }
1013
-
1014
-
1015
-    /**
1016
-     * Gets the string which represents the URL trigger for the invoice template in the message template system.
1017
-     *
1018
-     * @param string $messenger 'pdf' or 'html'.  Default 'html'.
1019
-     * @return string
1020
-     * @throws DomainException
1021
-     * @throws EE_Error
1022
-     * @throws InvalidArgumentException
1023
-     * @throws InvalidDataTypeException
1024
-     * @throws InvalidInterfaceException
1025
-     * @throws ReflectionException
1026
-     */
1027
-    public function invoice_url($messenger = 'html')
1028
-    {
1029
-        /**
1030
-         * The below will be deprecated one version after this.  We check first if there is a custom invoice template
1031
-         * already in use on old system.  If there is then we just return the standard url for it.
1032
-         *
1033
-         * @since 4.5.0
1034
-         */
1035
-        $template_relative_path = 'modules/gateways/Invoice/lib/templates/invoice_body.template.php';
1036
-        $has_custom = EEH_Template::locate_template(
1037
-            $template_relative_path,
1038
-            array(),
1039
-            true,
1040
-            true,
1041
-            true
1042
-        );
1043
-
1044
-        if ($has_custom) {
1045
-            if ($messenger == 'html') {
1046
-                return $this->invoice_url('launch');
1047
-            }
1048
-            $route = $messenger == 'download' || $messenger == 'pdf' ? 'download_invoice' : 'launch_invoice';
1049
-
1050
-            $query_args = array('ee' => $route, 'id' => $this->reg_url_link());
1051
-            if ($messenger == 'html') {
1052
-                $query_args['html'] = true;
1053
-            }
1054
-            return add_query_arg($query_args, get_permalink(EE_Registry::instance()->CFG->core->thank_you_page_id));
1055
-        }
1056
-        return apply_filters('FHEE__EE_Registration__invoice_url__invoice_url', '', $this, $messenger, 'invoice');
1057
-    }
1058
-
1059
-
1060
-    /**
1061
-     * get Registration URL Link
1062
-     *
1063
-     * @return string
1064
-     * @throws EE_Error
1065
-     * @throws InvalidArgumentException
1066
-     * @throws InvalidDataTypeException
1067
-     * @throws InvalidInterfaceException
1068
-     * @throws ReflectionException
1069
-     */
1070
-    public function reg_url_link()
1071
-    {
1072
-        return (string) $this->get('REG_url_link');
1073
-    }
1074
-
1075
-
1076
-    /**
1077
-     * Echoes out invoice_url()
1078
-     *
1079
-     * @param string $type 'download','launch', or 'html' (default is 'launch')
1080
-     * @return void
1081
-     * @throws DomainException
1082
-     * @throws EE_Error
1083
-     * @throws InvalidArgumentException
1084
-     * @throws InvalidDataTypeException
1085
-     * @throws InvalidInterfaceException
1086
-     * @throws ReflectionException
1087
-     */
1088
-    public function e_invoice_url($type = 'launch')
1089
-    {
1090
-        echo esc_url_raw($this->invoice_url($type));
1091
-    }
1092
-
1093
-
1094
-    /**
1095
-     * Echoes out payment_overview_url
1096
-     */
1097
-    public function e_payment_overview_url()
1098
-    {
1099
-        echo esc_url_raw($this->payment_overview_url());
1100
-    }
1101
-
1102
-
1103
-    /**
1104
-     * Gets the URL for the checkout payment options reg step
1105
-     * with this registration's REG_url_link added as a query parameter
1106
-     *
1107
-     * @param bool $clear_session Set to true when you want to clear the session on revisiting the
1108
-     *                            payment overview url.
1109
-     * @return string
1110
-     * @throws EE_Error
1111
-     * @throws InvalidArgumentException
1112
-     * @throws InvalidDataTypeException
1113
-     * @throws InvalidInterfaceException
1114
-     * @throws ReflectionException
1115
-     */
1116
-    public function payment_overview_url($clear_session = false)
1117
-    {
1118
-        return add_query_arg(
1119
-            (array) apply_filters(
1120
-                'FHEE__EE_Registration__payment_overview_url__query_args',
1121
-                array(
1122
-                    'e_reg_url_link' => $this->reg_url_link(),
1123
-                    'step'           => 'payment_options',
1124
-                    'revisit'        => true,
1125
-                    'clear_session'  => (bool) $clear_session,
1126
-                ),
1127
-                $this
1128
-            ),
1129
-            EE_Registry::instance()->CFG->core->reg_page_url()
1130
-        );
1131
-    }
1132
-
1133
-
1134
-    /**
1135
-     * Gets the URL for the checkout attendee information reg step
1136
-     * with this registration's REG_url_link added as a query parameter
1137
-     *
1138
-     * @return string
1139
-     * @throws EE_Error
1140
-     * @throws InvalidArgumentException
1141
-     * @throws InvalidDataTypeException
1142
-     * @throws InvalidInterfaceException
1143
-     * @throws ReflectionException
1144
-     */
1145
-    public function edit_attendee_information_url()
1146
-    {
1147
-        return add_query_arg(
1148
-            (array) apply_filters(
1149
-                'FHEE__EE_Registration__edit_attendee_information_url__query_args',
1150
-                array(
1151
-                    'e_reg_url_link' => $this->reg_url_link(),
1152
-                    'step'           => 'attendee_information',
1153
-                    'revisit'        => true,
1154
-                ),
1155
-                $this
1156
-            ),
1157
-            EE_Registry::instance()->CFG->core->reg_page_url()
1158
-        );
1159
-    }
1160
-
1161
-
1162
-    /**
1163
-     * Simply generates and returns the appropriate admin_url link to edit this registration
1164
-     *
1165
-     * @return string
1166
-     * @throws EE_Error
1167
-     * @throws InvalidArgumentException
1168
-     * @throws InvalidDataTypeException
1169
-     * @throws InvalidInterfaceException
1170
-     * @throws ReflectionException
1171
-     */
1172
-    public function get_admin_edit_url()
1173
-    {
1174
-        return EEH_URL::add_query_args_and_nonce(
1175
-            array(
1176
-                'page'    => 'espresso_registrations',
1177
-                'action'  => 'view_registration',
1178
-                '_REG_ID' => $this->ID(),
1179
-            ),
1180
-            admin_url('admin.php')
1181
-        );
1182
-    }
1183
-
1184
-
1185
-    /**
1186
-     * is_primary_registrant?
1187
-     *
1188
-     * @throws EE_Error
1189
-     * @throws InvalidArgumentException
1190
-     * @throws InvalidDataTypeException
1191
-     * @throws InvalidInterfaceException
1192
-     * @throws ReflectionException
1193
-     */
1194
-    public function is_primary_registrant()
1195
-    {
1196
-        return (int) $this->get('REG_count') === 1;
1197
-    }
1198
-
1199
-
1200
-    /**
1201
-     * This returns the primary registration object for this registration group (which may be this object).
1202
-     *
1203
-     * @return EE_Registration
1204
-     * @throws EE_Error
1205
-     * @throws InvalidArgumentException
1206
-     * @throws InvalidDataTypeException
1207
-     * @throws InvalidInterfaceException
1208
-     * @throws ReflectionException
1209
-     */
1210
-    public function get_primary_registration()
1211
-    {
1212
-        if ($this->is_primary_registrant()) {
1213
-            return $this;
1214
-        }
1215
-
1216
-        // k reg_count !== 1 so let's get the EE_Registration object matching this txn_id and reg_count == 1
1217
-        /** @var EE_Registration $primary_registrant */
1218
-        $primary_registrant = EEM_Registration::instance()->get_one(
1219
-            array(
1220
-                array(
1221
-                    'TXN_ID'    => $this->transaction_ID(),
1222
-                    'REG_count' => 1,
1223
-                ),
1224
-            )
1225
-        );
1226
-        return $primary_registrant;
1227
-    }
1228
-
1229
-
1230
-    /**
1231
-     * get  Attendee Number
1232
-     *
1233
-     * @throws EE_Error
1234
-     * @throws InvalidArgumentException
1235
-     * @throws InvalidDataTypeException
1236
-     * @throws InvalidInterfaceException
1237
-     * @throws ReflectionException
1238
-     */
1239
-    public function count()
1240
-    {
1241
-        return $this->get('REG_count');
1242
-    }
1243
-
1244
-
1245
-    /**
1246
-     * get Group Size
1247
-     *
1248
-     * @throws EE_Error
1249
-     * @throws InvalidArgumentException
1250
-     * @throws InvalidDataTypeException
1251
-     * @throws InvalidInterfaceException
1252
-     * @throws ReflectionException
1253
-     */
1254
-    public function group_size()
1255
-    {
1256
-        return $this->get('REG_group_size');
1257
-    }
1258
-
1259
-
1260
-    /**
1261
-     * get Registration Date
1262
-     *
1263
-     * @throws EE_Error
1264
-     * @throws InvalidArgumentException
1265
-     * @throws InvalidDataTypeException
1266
-     * @throws InvalidInterfaceException
1267
-     * @throws ReflectionException
1268
-     */
1269
-    public function date()
1270
-    {
1271
-        return $this->get('REG_date');
1272
-    }
1273
-
1274
-
1275
-    /**
1276
-     * gets a pretty date
1277
-     *
1278
-     * @param string $date_format
1279
-     * @param string $time_format
1280
-     * @return string
1281
-     * @throws EE_Error
1282
-     * @throws InvalidArgumentException
1283
-     * @throws InvalidDataTypeException
1284
-     * @throws InvalidInterfaceException
1285
-     * @throws ReflectionException
1286
-     */
1287
-    public function pretty_date($date_format = null, $time_format = null)
1288
-    {
1289
-        return $this->get_datetime('REG_date', $date_format, $time_format);
1290
-    }
1291
-
1292
-
1293
-    /**
1294
-     * final_price
1295
-     * the registration's share of the transaction total, so that the
1296
-     * sum of all the transaction's REG_final_prices equal the transaction's total
1297
-     *
1298
-     * @return float
1299
-     * @throws EE_Error
1300
-     * @throws InvalidArgumentException
1301
-     * @throws InvalidDataTypeException
1302
-     * @throws InvalidInterfaceException
1303
-     * @throws ReflectionException
1304
-     */
1305
-    public function final_price()
1306
-    {
1307
-        return $this->get('REG_final_price');
1308
-    }
1309
-
1310
-
1311
-    /**
1312
-     * pretty_final_price
1313
-     *  final price as formatted string, with correct decimal places and currency symbol
1314
-     *
1315
-     * @return string
1316
-     * @throws EE_Error
1317
-     * @throws InvalidArgumentException
1318
-     * @throws InvalidDataTypeException
1319
-     * @throws InvalidInterfaceException
1320
-     * @throws ReflectionException
1321
-     */
1322
-    public function pretty_final_price()
1323
-    {
1324
-        return $this->get_pretty('REG_final_price');
1325
-    }
1326
-
1327
-
1328
-    /**
1329
-     * get paid (yeah)
1330
-     *
1331
-     * @return float
1332
-     * @throws EE_Error
1333
-     * @throws InvalidArgumentException
1334
-     * @throws InvalidDataTypeException
1335
-     * @throws InvalidInterfaceException
1336
-     * @throws ReflectionException
1337
-     */
1338
-    public function paid()
1339
-    {
1340
-        return $this->get('REG_paid');
1341
-    }
1342
-
1343
-
1344
-    /**
1345
-     * pretty_paid
1346
-     *
1347
-     * @return float
1348
-     * @throws EE_Error
1349
-     * @throws InvalidArgumentException
1350
-     * @throws InvalidDataTypeException
1351
-     * @throws InvalidInterfaceException
1352
-     * @throws ReflectionException
1353
-     */
1354
-    public function pretty_paid()
1355
-    {
1356
-        return $this->get_pretty('REG_paid');
1357
-    }
1358
-
1359
-
1360
-    /**
1361
-     * owes_monies_and_can_pay
1362
-     * whether or not this registration has monies owing and it's' status allows payment
1363
-     *
1364
-     * @param array $requires_payment
1365
-     * @return bool
1366
-     * @throws EE_Error
1367
-     * @throws InvalidArgumentException
1368
-     * @throws InvalidDataTypeException
1369
-     * @throws InvalidInterfaceException
1370
-     * @throws ReflectionException
1371
-     */
1372
-    public function owes_monies_and_can_pay($requires_payment = array())
1373
-    {
1374
-        // these reg statuses require payment (if event is not free)
1375
-        $requires_payment = ! empty($requires_payment)
1376
-            ? $requires_payment
1377
-            : EEM_Registration::reg_statuses_that_allow_payment();
1378
-        if (
1379
-            $this->final_price() !== 0 &&
1380
-            $this->final_price() !== $this->paid() &&
1381
-            in_array($this->status_ID(), $requires_payment)
1382
-        ) {
1383
-            return true;
1384
-        }
1385
-        return false;
1386
-    }
1387
-
1388
-
1389
-    /**
1390
-     * Prints out the return value of $this->pretty_status()
1391
-     *
1392
-     * @param bool $show_icons
1393
-     * @return void
1394
-     * @throws EE_Error
1395
-     * @throws InvalidArgumentException
1396
-     * @throws InvalidDataTypeException
1397
-     * @throws InvalidInterfaceException
1398
-     * @throws ReflectionException
1399
-     */
1400
-    public function e_pretty_status($show_icons = false)
1401
-    {
1402
-        echo wp_kses($this->pretty_status($show_icons), AllowedTags::getAllowedTags());
1403
-    }
1404
-
1405
-
1406
-    /**
1407
-     * Returns a nice version of the status for displaying to customers
1408
-     *
1409
-     * @param bool $show_icons
1410
-     * @return string
1411
-     * @throws EE_Error
1412
-     * @throws InvalidArgumentException
1413
-     * @throws InvalidDataTypeException
1414
-     * @throws InvalidInterfaceException
1415
-     * @throws ReflectionException
1416
-     */
1417
-    public function pretty_status($show_icons = false)
1418
-    {
1419
-        $status = EEM_Status::instance()->localized_status(
1420
-            array($this->status_ID() => esc_html__('unknown', 'event_espresso')),
1421
-            false,
1422
-            'sentence'
1423
-        );
1424
-        $icon = '';
1425
-        switch ($this->status_ID()) {
1426
-            case EEM_Registration::status_id_approved:
1427
-                $icon = $show_icons
1428
-                    ? '<span class="dashicons dashicons-star-filled ee-icon-size-16 green-text"></span>'
1429
-                    : '';
1430
-                break;
1431
-            case EEM_Registration::status_id_pending_payment:
1432
-                $icon = $show_icons
1433
-                    ? '<span class="dashicons dashicons-star-half ee-icon-size-16 orange-text"></span>'
1434
-                    : '';
1435
-                break;
1436
-            case EEM_Registration::status_id_not_approved:
1437
-                $icon = $show_icons
1438
-                    ? '<span class="dashicons dashicons-marker ee-icon-size-16 orange-text"></span>'
1439
-                    : '';
1440
-                break;
1441
-            case EEM_Registration::status_id_cancelled:
1442
-                $icon = $show_icons
1443
-                    ? '<span class="dashicons dashicons-no ee-icon-size-16 lt-grey-text"></span>'
1444
-                    : '';
1445
-                break;
1446
-            case EEM_Registration::status_id_incomplete:
1447
-                $icon = $show_icons
1448
-                    ? '<span class="dashicons dashicons-no ee-icon-size-16 lt-orange-text"></span>'
1449
-                    : '';
1450
-                break;
1451
-            case EEM_Registration::status_id_declined:
1452
-                $icon = $show_icons
1453
-                    ? '<span class="dashicons dashicons-no ee-icon-size-16 red-text"></span>'
1454
-                    : '';
1455
-                break;
1456
-            case EEM_Registration::status_id_wait_list:
1457
-                $icon = $show_icons
1458
-                    ? '<span class="dashicons dashicons-clipboard ee-icon-size-16 purple-text"></span>'
1459
-                    : '';
1460
-                break;
1461
-        }
1462
-        return $icon . $status[ $this->status_ID() ];
1463
-    }
1464
-
1465
-
1466
-    /**
1467
-     *        get Attendee Is Going
1468
-     */
1469
-    public function att_is_going()
1470
-    {
1471
-        return $this->get('REG_att_is_going');
1472
-    }
1473
-
1474
-
1475
-    /**
1476
-     * Gets related answers
1477
-     *
1478
-     * @param array $query_params @see
1479
-     *                            https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1480
-     * @return EE_Answer[]|EE_Base_Class[]
1481
-     * @throws EE_Error
1482
-     * @throws InvalidArgumentException
1483
-     * @throws InvalidDataTypeException
1484
-     * @throws InvalidInterfaceException
1485
-     * @throws ReflectionException
1486
-     */
1487
-    public function answers($query_params = null)
1488
-    {
1489
-        return $this->get_many_related('Answer', $query_params);
1490
-    }
1491
-
1492
-
1493
-    /**
1494
-     * Gets the registration's answer value to the specified question
1495
-     * (either the question's ID or a question object)
1496
-     *
1497
-     * @param EE_Question|int $question
1498
-     * @param bool            $pretty_value
1499
-     * @return array|string if pretty_value= true, the result will always be a string
1500
-     * (because the answer might be an array of answer values, so passing pretty_value=true
1501
-     * will convert it into some kind of string)
1502
-     * @throws EE_Error
1503
-     * @throws InvalidArgumentException
1504
-     * @throws InvalidDataTypeException
1505
-     * @throws InvalidInterfaceException
1506
-     */
1507
-    public function answer_value_to_question($question, $pretty_value = true)
1508
-    {
1509
-        $question_id = EEM_Question::instance()->ensure_is_ID($question);
1510
-        return EEM_Answer::instance()->get_answer_value_to_question($this, $question_id, $pretty_value);
1511
-    }
1512
-
1513
-
1514
-    /**
1515
-     * question_groups
1516
-     * returns an array of EE_Question_Group objects for this registration
1517
-     *
1518
-     * @return EE_Question_Group[]
1519
-     * @throws EE_Error
1520
-     * @throws InvalidArgumentException
1521
-     * @throws InvalidDataTypeException
1522
-     * @throws InvalidInterfaceException
1523
-     * @throws ReflectionException
1524
-     */
1525
-    public function question_groups()
1526
-    {
1527
-        return EEM_Event::instance()->get_question_groups_for_event($this->event_ID(), $this);
1528
-    }
1529
-
1530
-
1531
-    /**
1532
-     * count_question_groups
1533
-     * returns a count of the number of EE_Question_Group objects for this registration
1534
-     *
1535
-     * @return int
1536
-     * @throws EE_Error
1537
-     * @throws EntityNotFoundException
1538
-     * @throws InvalidArgumentException
1539
-     * @throws InvalidDataTypeException
1540
-     * @throws InvalidInterfaceException
1541
-     * @throws ReflectionException
1542
-     */
1543
-    public function count_question_groups()
1544
-    {
1545
-        return EEM_Event::instance()->count_related(
1546
-            $this->event_ID(),
1547
-            'Question_Group',
1548
-            [
1549
-                [
1550
-                    'Event_Question_Group.'
1551
-                    . EEM_Event_Question_Group::instance()->fieldNameForContext($this->is_primary_registrant()) => true,
1552
-                ]
1553
-            ]
1554
-        );
1555
-    }
1556
-
1557
-
1558
-    /**
1559
-     * Returns the registration date in the 'standard' string format
1560
-     * (function may be improved in the future to allow for different formats and timezones)
1561
-     *
1562
-     * @return string
1563
-     * @throws EE_Error
1564
-     * @throws InvalidArgumentException
1565
-     * @throws InvalidDataTypeException
1566
-     * @throws InvalidInterfaceException
1567
-     * @throws ReflectionException
1568
-     */
1569
-    public function reg_date()
1570
-    {
1571
-        return $this->get_datetime('REG_date');
1572
-    }
1573
-
1574
-
1575
-    /**
1576
-     * Gets the datetime-ticket for this registration (ie, it can be used to isolate
1577
-     * the ticket this registration purchased, or the datetime they have registered
1578
-     * to attend)
1579
-     *
1580
-     * @return EE_Base_Class|EE_Datetime_Ticket
1581
-     * @throws EE_Error
1582
-     * @throws InvalidArgumentException
1583
-     * @throws InvalidDataTypeException
1584
-     * @throws InvalidInterfaceException
1585
-     * @throws ReflectionException
1586
-     */
1587
-    public function datetime_ticket()
1588
-    {
1589
-        return $this->get_first_related('Datetime_Ticket');
1590
-    }
1591
-
1592
-
1593
-    /**
1594
-     * Sets the registration's datetime_ticket.
1595
-     *
1596
-     * @param EE_Datetime_Ticket $datetime_ticket
1597
-     * @return EE_Base_Class|EE_Datetime_Ticket
1598
-     * @throws EE_Error
1599
-     * @throws InvalidArgumentException
1600
-     * @throws InvalidDataTypeException
1601
-     * @throws InvalidInterfaceException
1602
-     * @throws ReflectionException
1603
-     */
1604
-    public function set_datetime_ticket($datetime_ticket)
1605
-    {
1606
-        return $this->_add_relation_to($datetime_ticket, 'Datetime_Ticket');
1607
-    }
1608
-
1609
-
1610
-    /**
1611
-     * Gets deleted
1612
-     *
1613
-     * @return bool
1614
-     * @throws EE_Error
1615
-     * @throws InvalidArgumentException
1616
-     * @throws InvalidDataTypeException
1617
-     * @throws InvalidInterfaceException
1618
-     * @throws ReflectionException
1619
-     */
1620
-    public function deleted()
1621
-    {
1622
-        return $this->get('REG_deleted');
1623
-    }
1624
-
1625
-
1626
-    /**
1627
-     * Sets deleted
1628
-     *
1629
-     * @param boolean $deleted
1630
-     * @return void
1631
-     * @throws DomainException
1632
-     * @throws EE_Error
1633
-     * @throws EntityNotFoundException
1634
-     * @throws InvalidArgumentException
1635
-     * @throws InvalidDataTypeException
1636
-     * @throws InvalidInterfaceException
1637
-     * @throws ReflectionException
1638
-     * @throws RuntimeException
1639
-     * @throws UnexpectedEntityException
1640
-     */
1641
-    public function set_deleted($deleted)
1642
-    {
1643
-        if ($deleted) {
1644
-            $this->delete(__METHOD__);
1645
-        } else {
1646
-            $this->restore(__METHOD__);
1647
-        }
1648
-    }
1649
-
1650
-
1651
-    /**
1652
-     * Get the status object of this object
1653
-     *
1654
-     * @return EE_Base_Class|EE_Status
1655
-     * @throws EE_Error
1656
-     * @throws InvalidArgumentException
1657
-     * @throws InvalidDataTypeException
1658
-     * @throws InvalidInterfaceException
1659
-     * @throws ReflectionException
1660
-     */
1661
-    public function status_obj()
1662
-    {
1663
-        return $this->get_first_related('Status');
1664
-    }
1665
-
1666
-
1667
-    /**
1668
-     * Returns the number of times this registration has checked into any of the datetimes
1669
-     * its available for
1670
-     *
1671
-     * @return int
1672
-     * @throws EE_Error
1673
-     * @throws InvalidArgumentException
1674
-     * @throws InvalidDataTypeException
1675
-     * @throws InvalidInterfaceException
1676
-     * @throws ReflectionException
1677
-     */
1678
-    public function count_checkins()
1679
-    {
1680
-        return $this->get_model()->count_related($this, 'Checkin');
1681
-    }
1682
-
1683
-
1684
-    /**
1685
-     * Returns the number of current Check-ins this registration is checked into for any of the datetimes the
1686
-     * registration is for.  Note, this is ONLY checked in (does not include checkedout)
1687
-     *
1688
-     * @return int
1689
-     * @throws EE_Error
1690
-     * @throws InvalidArgumentException
1691
-     * @throws InvalidDataTypeException
1692
-     * @throws InvalidInterfaceException
1693
-     * @throws ReflectionException
1694
-     */
1695
-    public function count_checkins_not_checkedout()
1696
-    {
1697
-        return $this->get_model()->count_related($this, 'Checkin', array(array('CHK_in' => 1)));
1698
-    }
1699
-
1700
-
1701
-    /**
1702
-     * The purpose of this method is simply to check whether this registration can checkin to the given datetime.
1703
-     *
1704
-     * @param int | EE_Datetime $DTT_OR_ID      The datetime the registration is being checked against
1705
-     * @param bool              $check_approved This is used to indicate whether the caller wants can_checkin to also
1706
-     *                                          consider registration status as well as datetime access.
1707
-     * @return bool
1708
-     * @throws EE_Error
1709
-     * @throws InvalidArgumentException
1710
-     * @throws InvalidDataTypeException
1711
-     * @throws InvalidInterfaceException
1712
-     * @throws ReflectionException
1713
-     */
1714
-    public function can_checkin($DTT_OR_ID, $check_approved = true)
1715
-    {
1716
-        $DTT_ID = EEM_Datetime::instance()->ensure_is_ID($DTT_OR_ID);
1717
-        // first check registration status
1718
-        if (! $DTT_ID || ($check_approved && ! $this->is_approved())) {
1719
-            return false;
1720
-        }
1721
-        // is there a datetime ticket that matches this dtt_ID?
1722
-        if (
1723
-            ! (EEM_Datetime_Ticket::instance()->exists(
1724
-                array(
1725
-                    array(
1726
-                        'TKT_ID' => $this->get('TKT_ID'),
1727
-                        'DTT_ID' => $DTT_ID,
1728
-                    ),
1729
-                )
1730
-            ))
1731
-        ) {
1732
-            return false;
1733
-        }
1734
-
1735
-        // final check is against TKT_uses
1736
-        return $this->verify_can_checkin_against_TKT_uses($DTT_ID);
1737
-    }
1738
-
1739
-
1740
-    /**
1741
-     * This method verifies whether the user can checkin for the given datetime considering the max uses value set on
1742
-     * the ticket. To do this,  a query is done to get the count of the datetime records already checked into.  If the
1743
-     * datetime given does not have a check-in record and checking in for that datetime will exceed the allowed uses,
1744
-     * then return false.  Otherwise return true.
1745
-     *
1746
-     * @param int | EE_Datetime $DTT_OR_ID The datetime the registration is being checked against
1747
-     * @return bool true means can checkin.  false means cannot checkin.
1748
-     * @throws EE_Error
1749
-     * @throws InvalidArgumentException
1750
-     * @throws InvalidDataTypeException
1751
-     * @throws InvalidInterfaceException
1752
-     * @throws ReflectionException
1753
-     */
1754
-    public function verify_can_checkin_against_TKT_uses($DTT_OR_ID)
1755
-    {
1756
-        $DTT_ID = EEM_Datetime::instance()->ensure_is_ID($DTT_OR_ID);
1757
-
1758
-        if (! $DTT_ID) {
1759
-            return false;
1760
-        }
1761
-
1762
-        $max_uses = $this->ticket() instanceof EE_Ticket ? $this->ticket()->uses() : EE_INF;
1763
-
1764
-        // if max uses is not set or equals infinity then return true cause its not a factor for whether user can
1765
-        // check-in or not.
1766
-        if (! $max_uses || $max_uses === EE_INF) {
1767
-            return true;
1768
-        }
1769
-
1770
-        // does this datetime have a checkin record?  If so, then the dtt count has already been verified so we can just
1771
-        // go ahead and toggle.
1772
-        if (EEM_Checkin::instance()->exists(array(array('REG_ID' => $this->ID(), 'DTT_ID' => $DTT_ID)))) {
1773
-            return true;
1774
-        }
1775
-
1776
-        // made it here so the last check is whether the number of checkins per unique datetime on this registration
1777
-        // disallows further check-ins.
1778
-        $count_unique_dtt_checkins = EEM_Checkin::instance()->count(
1779
-            array(
1780
-                array(
1781
-                    'REG_ID' => $this->ID(),
1782
-                    'CHK_in' => true,
1783
-                ),
1784
-            ),
1785
-            'DTT_ID',
1786
-            true
1787
-        );
1788
-        // checkins have already reached their max number of uses
1789
-        // so registrant can NOT checkin
1790
-        if ($count_unique_dtt_checkins >= $max_uses) {
1791
-            EE_Error::add_error(
1792
-                esc_html__(
1793
-                    'Check-in denied because number of datetime uses for the ticket has been reached or exceeded.',
1794
-                    'event_espresso'
1795
-                ),
1796
-                __FILE__,
1797
-                __FUNCTION__,
1798
-                __LINE__
1799
-            );
1800
-            return false;
1801
-        }
1802
-        return true;
1803
-    }
1804
-
1805
-
1806
-    /**
1807
-     * toggle Check-in status for this registration
1808
-     * Check-ins are toggled in the following order:
1809
-     * never checked in -> checked in
1810
-     * checked in -> checked out
1811
-     * checked out -> checked in
1812
-     *
1813
-     * @param  int $DTT_ID  include specific datetime to toggle Check-in for.
1814
-     *                      If not included or null, then it is assumed latest datetime is being toggled.
1815
-     * @param bool $verify  If true then can_checkin() is used to verify whether the person
1816
-     *                      can be checked in or not.  Otherwise this forces change in checkin status.
1817
-     * @return bool|int     the chk_in status toggled to OR false if nothing got changed.
1818
-     * @throws EE_Error
1819
-     * @throws InvalidArgumentException
1820
-     * @throws InvalidDataTypeException
1821
-     * @throws InvalidInterfaceException
1822
-     * @throws ReflectionException
1823
-     */
1824
-    public function toggle_checkin_status($DTT_ID = null, $verify = false)
1825
-    {
1826
-        if (empty($DTT_ID)) {
1827
-            $datetime = $this->get_latest_related_datetime();
1828
-            $DTT_ID = $datetime instanceof EE_Datetime ? $datetime->ID() : 0;
1829
-            // verify the registration can checkin for the given DTT_ID
1830
-        } elseif (! $this->can_checkin($DTT_ID, $verify)) {
1831
-            EE_Error::add_error(
1832
-                sprintf(
1833
-                    esc_html__(
1834
-                        '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',
1835
-                        'event_espresso'
1836
-                    ),
1837
-                    $this->ID(),
1838
-                    $DTT_ID
1839
-                ),
1840
-                __FILE__,
1841
-                __FUNCTION__,
1842
-                __LINE__
1843
-            );
1844
-            return false;
1845
-        }
1846
-        $status_paths = array(
1847
-            EE_Checkin::status_checked_never => EE_Checkin::status_checked_in,
1848
-            EE_Checkin::status_checked_in    => EE_Checkin::status_checked_out,
1849
-            EE_Checkin::status_checked_out   => EE_Checkin::status_checked_in,
1850
-        );
1851
-        // start by getting the current status so we know what status we'll be changing to.
1852
-        $cur_status = $this->check_in_status_for_datetime($DTT_ID);
1853
-        $status_to = $status_paths[ $cur_status ];
1854
-        // database only records true for checked IN or false for checked OUT
1855
-        // no record ( null ) means checked in NEVER, but we obviously don't save that
1856
-        $new_status = $status_to === EE_Checkin::status_checked_in;
1857
-        // add relation - note Check-ins are always creating new rows
1858
-        // because we are keeping track of Check-ins over time.
1859
-        // Eventually we'll probably want to show a list table
1860
-        // for the individual Check-ins so that they can be managed.
1861
-        $checkin = EE_Checkin::new_instance(
1862
-            array(
1863
-                'REG_ID' => $this->ID(),
1864
-                'DTT_ID' => $DTT_ID,
1865
-                'CHK_in' => $new_status,
1866
-            )
1867
-        );
1868
-        // if the record could not be saved then return false
1869
-        if ($checkin->save() === 0) {
1870
-            if (WP_DEBUG) {
1871
-                global $wpdb;
1872
-                $error = sprintf(
1873
-                    esc_html__(
1874
-                        'Registration check in update failed because of the following database error: %1$s%2$s',
1875
-                        'event_espresso'
1876
-                    ),
1877
-                    '<br />',
1878
-                    $wpdb->last_error
1879
-                );
1880
-            } else {
1881
-                $error = esc_html__(
1882
-                    'Registration check in update failed because of an unknown database error',
1883
-                    'event_espresso'
1884
-                );
1885
-            }
1886
-            EE_Error::add_error($error, __FILE__, __FUNCTION__, __LINE__);
1887
-            return false;
1888
-        }
1889
-        // Fire a checked_in and checkout_out action.
1890
-        $checked_status = $status_to === EE_Checkin::status_checked_in ? 'checked_in' : 'checked_out';
1891
-        do_action("AHEE__EE_Registration__toggle_checkin_status__{$checked_status}", $this, $DTT_ID);
1892
-        return $status_to;
1893
-    }
1894
-
1895
-
1896
-    /**
1897
-     * Returns the latest datetime related to this registration (via the ticket attached to the registration).
1898
-     * "Latest" is defined by the `DTT_EVT_start` column.
1899
-     *
1900
-     * @return EE_Datetime|null
1901
-     * @throws EE_Error
1902
-     * @throws InvalidArgumentException
1903
-     * @throws InvalidDataTypeException
1904
-     * @throws InvalidInterfaceException
1905
-     * @throws ReflectionException
1906
-     */
1907
-    public function get_latest_related_datetime()
1908
-    {
1909
-        return EEM_Datetime::instance()->get_one(
1910
-            array(
1911
-                array(
1912
-                    'Ticket.Registration.REG_ID' => $this->ID(),
1913
-                ),
1914
-                'order_by' => array('DTT_EVT_start' => 'DESC'),
1915
-            )
1916
-        );
1917
-    }
1918
-
1919
-
1920
-    /**
1921
-     * Returns the earliest datetime related to this registration (via the ticket attached to the registration).
1922
-     * "Earliest" is defined by the `DTT_EVT_start` column.
1923
-     *
1924
-     * @return EE_Base_Class|EE_Soft_Delete_Base_Class|NULL
1925
-     * @throws EE_Error
1926
-     * @throws InvalidArgumentException
1927
-     * @throws InvalidDataTypeException
1928
-     * @throws InvalidInterfaceException
1929
-     * @throws ReflectionException
1930
-     */
1931
-    public function get_earliest_related_datetime()
1932
-    {
1933
-        return EEM_Datetime::instance()->get_one(
1934
-            array(
1935
-                array(
1936
-                    'Ticket.Registration.REG_ID' => $this->ID(),
1937
-                ),
1938
-                'order_by' => array('DTT_EVT_start' => 'ASC'),
1939
-            )
1940
-        );
1941
-    }
1942
-
1943
-
1944
-    /**
1945
-     * This method simply returns the check-in status for this registration and the given datetime.
1946
-     * If neither the datetime nor the checkin values are provided as arguments,
1947
-     * then this will return the LATEST check-in status for the registration across all datetimes it belongs to.
1948
-     *
1949
-     * @param  int       $DTT_ID  The ID of the datetime we're checking against
1950
-     *                            (if empty we'll get the primary datetime for
1951
-     *                            this registration (via event) and use it's ID);
1952
-     * @param EE_Checkin $checkin If present, we use the given checkin object rather than the dtt_id.
1953
-     * @return int                Integer representing Check-in status.
1954
-     * @throws EE_Error
1955
-     * @throws InvalidArgumentException
1956
-     * @throws InvalidDataTypeException
1957
-     * @throws InvalidInterfaceException
1958
-     * @throws ReflectionException
1959
-     */
1960
-    public function check_in_status_for_datetime($DTT_ID = 0, $checkin = null)
1961
-    {
1962
-        $checkin_query_params = array(
1963
-            'order_by' => array('CHK_timestamp' => 'DESC'),
1964
-        );
1965
-
1966
-        if ($DTT_ID > 0) {
1967
-            $checkin_query_params[0] = array('DTT_ID' => $DTT_ID);
1968
-        }
1969
-
1970
-        // get checkin object (if exists)
1971
-        $checkin = $checkin instanceof EE_Checkin
1972
-            ? $checkin
1973
-            : $this->get_first_related('Checkin', $checkin_query_params);
1974
-        if ($checkin instanceof EE_Checkin) {
1975
-            if ($checkin->get('CHK_in')) {
1976
-                return EE_Checkin::status_checked_in; // checked in
1977
-            }
1978
-            return EE_Checkin::status_checked_out; // had checked in but is now checked out.
1979
-        }
1980
-        return EE_Checkin::status_checked_never; // never been checked in
1981
-    }
1982
-
1983
-
1984
-    /**
1985
-     * This method returns a localized message for the toggled Check-in message.
1986
-     *
1987
-     * @param  int $DTT_ID include specific datetime to get the correct Check-in message.  If not included or null,
1988
-     *                     then it is assumed Check-in for primary datetime was toggled.
1989
-     * @param bool $error  This just flags that you want an error message returned. This is put in so that the error
1990
-     *                     message can be customized with the attendee name.
1991
-     * @return string internationalized message
1992
-     * @throws EE_Error
1993
-     * @throws InvalidArgumentException
1994
-     * @throws InvalidDataTypeException
1995
-     * @throws InvalidInterfaceException
1996
-     * @throws ReflectionException
1997
-     */
1998
-    public function get_checkin_msg($DTT_ID, $error = false)
1999
-    {
2000
-        // let's get the attendee first so we can include the name of the attendee
2001
-        $attendee = $this->get_first_related('Attendee');
2002
-        if ($attendee instanceof EE_Attendee) {
2003
-            if ($error) {
2004
-                return sprintf(
2005
-                    esc_html__("%s's check-in status was not changed.", "event_espresso"),
2006
-                    $attendee->full_name()
2007
-                );
2008
-            }
2009
-            $cur_status = $this->check_in_status_for_datetime($DTT_ID);
2010
-            // what is the status message going to be?
2011
-            switch ($cur_status) {
2012
-                case EE_Checkin::status_checked_never:
2013
-                    return sprintf(
2014
-                        esc_html__('%s has been removed from Check-in records', 'event_espresso'),
2015
-                        $attendee->full_name()
2016
-                    );
2017
-                    break;
2018
-                case EE_Checkin::status_checked_in:
2019
-                    return sprintf(esc_html__('%s has been checked in', 'event_espresso'), $attendee->full_name());
2020
-                    break;
2021
-                case EE_Checkin::status_checked_out:
2022
-                    return sprintf(esc_html__('%s has been checked out', 'event_espresso'), $attendee->full_name());
2023
-                    break;
2024
-            }
2025
-        }
2026
-        return esc_html__('The check-in status could not be determined.', 'event_espresso');
2027
-    }
2028
-
2029
-
2030
-    /**
2031
-     * Returns the related EE_Transaction to this registration
2032
-     *
2033
-     * @return EE_Transaction
2034
-     * @throws EE_Error
2035
-     * @throws EntityNotFoundException
2036
-     * @throws InvalidArgumentException
2037
-     * @throws InvalidDataTypeException
2038
-     * @throws InvalidInterfaceException
2039
-     * @throws ReflectionException
2040
-     */
2041
-    public function transaction()
2042
-    {
2043
-        $transaction = $this->get_first_related('Transaction');
2044
-        if (! $transaction instanceof \EE_Transaction) {
2045
-            throw new EntityNotFoundException('Transaction ID', $this->transaction_ID());
2046
-        }
2047
-        return $transaction;
2048
-    }
2049
-
2050
-
2051
-    /**
2052
-     * get Registration Code
2053
-     *
2054
-     * @return mixed
2055
-     * @throws EE_Error
2056
-     * @throws InvalidArgumentException
2057
-     * @throws InvalidDataTypeException
2058
-     * @throws InvalidInterfaceException
2059
-     * @throws ReflectionException
2060
-     */
2061
-    public function reg_code()
2062
-    {
2063
-        return $this->get('REG_code');
2064
-    }
2065
-
2066
-
2067
-    /**
2068
-     * @return mixed
2069
-     * @throws EE_Error
2070
-     * @throws InvalidArgumentException
2071
-     * @throws InvalidDataTypeException
2072
-     * @throws InvalidInterfaceException
2073
-     * @throws ReflectionException
2074
-     */
2075
-    public function transaction_ID()
2076
-    {
2077
-        return $this->get('TXN_ID');
2078
-    }
2079
-
2080
-
2081
-    /**
2082
-     * @return int
2083
-     * @throws EE_Error
2084
-     * @throws InvalidArgumentException
2085
-     * @throws InvalidDataTypeException
2086
-     * @throws InvalidInterfaceException
2087
-     * @throws ReflectionException
2088
-     */
2089
-    public function ticket_ID()
2090
-    {
2091
-        return $this->get('TKT_ID');
2092
-    }
2093
-
2094
-
2095
-    /**
2096
-     * Set Registration Code
2097
-     *
2098
-     * @param    string  $REG_code Registration Code
2099
-     * @param    boolean $use_default
2100
-     * @throws EE_Error
2101
-     * @throws InvalidArgumentException
2102
-     * @throws InvalidDataTypeException
2103
-     * @throws InvalidInterfaceException
2104
-     * @throws ReflectionException
2105
-     */
2106
-    public function set_reg_code($REG_code, $use_default = false)
2107
-    {
2108
-        if (empty($REG_code)) {
2109
-            EE_Error::add_error(
2110
-                esc_html__('REG_code can not be empty.', 'event_espresso'),
2111
-                __FILE__,
2112
-                __FUNCTION__,
2113
-                __LINE__
2114
-            );
2115
-            return;
2116
-        }
2117
-        if (! $this->reg_code()) {
2118
-            parent::set('REG_code', $REG_code, $use_default);
2119
-        } else {
2120
-            EE_Error::doing_it_wrong(
2121
-                __CLASS__ . '::' . __FUNCTION__,
2122
-                esc_html__('Can not change a registration REG_code once it has been set.', 'event_espresso'),
2123
-                '4.6.0'
2124
-            );
2125
-        }
2126
-    }
2127
-
2128
-
2129
-    /**
2130
-     * Returns all other registrations in the same group as this registrant who have the same ticket option.
2131
-     * Note, if you want to just get all registrations in the same transaction (group), use:
2132
-     *    $registration->transaction()->registrations();
2133
-     *
2134
-     * @since 4.5.0
2135
-     * @return EE_Registration[] or empty array if this isn't a group registration.
2136
-     * @throws EE_Error
2137
-     * @throws InvalidArgumentException
2138
-     * @throws InvalidDataTypeException
2139
-     * @throws InvalidInterfaceException
2140
-     * @throws ReflectionException
2141
-     */
2142
-    public function get_all_other_registrations_in_group()
2143
-    {
2144
-        if ($this->group_size() < 2) {
2145
-            return array();
2146
-        }
2147
-
2148
-        $query[0] = array(
2149
-            'TXN_ID' => $this->transaction_ID(),
2150
-            'REG_ID' => array('!=', $this->ID()),
2151
-            'TKT_ID' => $this->ticket_ID(),
2152
-        );
2153
-        /** @var EE_Registration[] $registrations */
2154
-        $registrations = $this->get_model()->get_all($query);
2155
-        return $registrations;
2156
-    }
2157
-
2158
-
2159
-    /**
2160
-     * Return the link to the admin details for the object.
2161
-     *
2162
-     * @return string
2163
-     * @throws EE_Error
2164
-     * @throws InvalidArgumentException
2165
-     * @throws InvalidDataTypeException
2166
-     * @throws InvalidInterfaceException
2167
-     * @throws ReflectionException
2168
-     */
2169
-    public function get_admin_details_link()
2170
-    {
2171
-        EE_Registry::instance()->load_helper('URL');
2172
-        return EEH_URL::add_query_args_and_nonce(
2173
-            array(
2174
-                'page'    => 'espresso_registrations',
2175
-                'action'  => 'view_registration',
2176
-                '_REG_ID' => $this->ID(),
2177
-            ),
2178
-            admin_url('admin.php')
2179
-        );
2180
-    }
2181
-
2182
-
2183
-    /**
2184
-     * Returns the link to the editor for the object.  Sometimes this is the same as the details.
2185
-     *
2186
-     * @return string
2187
-     * @throws EE_Error
2188
-     * @throws InvalidArgumentException
2189
-     * @throws InvalidDataTypeException
2190
-     * @throws InvalidInterfaceException
2191
-     * @throws ReflectionException
2192
-     */
2193
-    public function get_admin_edit_link()
2194
-    {
2195
-        return $this->get_admin_details_link();
2196
-    }
2197
-
2198
-
2199
-    /**
2200
-     * Returns the link to a settings page for the object.
2201
-     *
2202
-     * @return string
2203
-     * @throws EE_Error
2204
-     * @throws InvalidArgumentException
2205
-     * @throws InvalidDataTypeException
2206
-     * @throws InvalidInterfaceException
2207
-     * @throws ReflectionException
2208
-     */
2209
-    public function get_admin_settings_link()
2210
-    {
2211
-        return $this->get_admin_details_link();
2212
-    }
2213
-
2214
-
2215
-    /**
2216
-     * Returns the link to the "overview" for the object (typically the "list table" view).
2217
-     *
2218
-     * @return string
2219
-     * @throws EE_Error
2220
-     * @throws InvalidArgumentException
2221
-     * @throws InvalidDataTypeException
2222
-     * @throws InvalidInterfaceException
2223
-     * @throws ReflectionException
2224
-     */
2225
-    public function get_admin_overview_link()
2226
-    {
2227
-        EE_Registry::instance()->load_helper('URL');
2228
-        return EEH_URL::add_query_args_and_nonce(
2229
-            array(
2230
-                'page' => 'espresso_registrations',
2231
-            ),
2232
-            admin_url('admin.php')
2233
-        );
2234
-    }
2235
-
2236
-
2237
-    /**
2238
-     * @param array $query_params
2239
-     * @return EE_Base_Class[]|EE_Registration[]
2240
-     * @throws EE_Error
2241
-     * @throws InvalidArgumentException
2242
-     * @throws InvalidDataTypeException
2243
-     * @throws InvalidInterfaceException
2244
-     * @throws ReflectionException
2245
-     */
2246
-    public function payments($query_params = array())
2247
-    {
2248
-        return $this->get_many_related('Payment', $query_params);
2249
-    }
2250
-
2251
-
2252
-    /**
2253
-     * @param array $query_params
2254
-     * @return EE_Base_Class[]|EE_Registration_Payment[]
2255
-     * @throws EE_Error
2256
-     * @throws InvalidArgumentException
2257
-     * @throws InvalidDataTypeException
2258
-     * @throws InvalidInterfaceException
2259
-     * @throws ReflectionException
2260
-     */
2261
-    public function registration_payments($query_params = array())
2262
-    {
2263
-        return $this->get_many_related('Registration_Payment', $query_params);
2264
-    }
2265
-
2266
-
2267
-    /**
2268
-     * This grabs the payment method corresponding to the last payment made for the amount owing on the registration.
2269
-     * Note: if there are no payments on the registration there will be no payment method returned.
2270
-     *
2271
-     * @return EE_Payment|EE_Payment_Method|null
2272
-     * @throws EE_Error
2273
-     * @throws InvalidArgumentException
2274
-     * @throws InvalidDataTypeException
2275
-     * @throws InvalidInterfaceException
2276
-     */
2277
-    public function payment_method()
2278
-    {
2279
-        return EEM_Payment_Method::instance()->get_last_used_for_registration($this);
2280
-    }
2281
-
2282
-
2283
-    /**
2284
-     * @return \EE_Line_Item
2285
-     * @throws EE_Error
2286
-     * @throws EntityNotFoundException
2287
-     * @throws InvalidArgumentException
2288
-     * @throws InvalidDataTypeException
2289
-     * @throws InvalidInterfaceException
2290
-     * @throws ReflectionException
2291
-     */
2292
-    public function ticket_line_item()
2293
-    {
2294
-        $ticket = $this->ticket();
2295
-        $transaction = $this->transaction();
2296
-        $line_item = null;
2297
-        $ticket_line_items = \EEH_Line_Item::get_line_items_by_object_type_and_IDs(
2298
-            $transaction->total_line_item(),
2299
-            'Ticket',
2300
-            array($ticket->ID())
2301
-        );
2302
-        foreach ($ticket_line_items as $ticket_line_item) {
2303
-            if (
2304
-                $ticket_line_item instanceof \EE_Line_Item
2305
-                && $ticket_line_item->OBJ_type() === 'Ticket'
2306
-                && $ticket_line_item->OBJ_ID() === $ticket->ID()
2307
-            ) {
2308
-                $line_item = $ticket_line_item;
2309
-                break;
2310
-            }
2311
-        }
2312
-        if (! ($line_item instanceof \EE_Line_Item && $line_item->OBJ_type() === 'Ticket')) {
2313
-            throw new EntityNotFoundException('Line Item Ticket ID', $ticket->ID());
2314
-        }
2315
-        return $line_item;
2316
-    }
2317
-
2318
-
2319
-    /**
2320
-     * Soft Deletes this model object.
2321
-     *
2322
-     * @param string $source function name that called this method
2323
-     * @return boolean | int
2324
-     * @throws DomainException
2325
-     * @throws EE_Error
2326
-     * @throws EntityNotFoundException
2327
-     * @throws InvalidArgumentException
2328
-     * @throws InvalidDataTypeException
2329
-     * @throws InvalidInterfaceException
2330
-     * @throws ReflectionException
2331
-     * @throws RuntimeException
2332
-     * @throws UnexpectedEntityException
2333
-     */
2334
-    public function delete($source = 'unknown')
2335
-    {
2336
-        if ($this->update_extra_meta(EE_Registration::PRE_TRASH_REG_STATUS_KEY, $this->status_ID()) === true) {
2337
-            $current_user = wp_get_current_user();
2338
-            $this->add_extra_meta(
2339
-                EE_Registration::EXTRA_META_KEY_REG_TRASHED,
2340
-                array(
2341
-                    'trashed-by' => $current_user->ID ? $current_user->display_name : 'unauthed user',
2342
-                    'timestamp'  => time(),
2343
-                    'source'     => $source,
2344
-                )
2345
-            );
2346
-            $this->set_status(EEM_Registration::status_id_cancelled);
2347
-        }
2348
-        return parent::delete();
2349
-    }
2350
-
2351
-
2352
-    /**
2353
-     * Restores whatever the previous status was on a registration before it was trashed (if possible)
2354
-     *
2355
-     * @param string $source function name that called this method
2356
-     * @return bool|int
2357
-     * @throws DomainException
2358
-     * @throws EE_Error
2359
-     * @throws EntityNotFoundException
2360
-     * @throws InvalidArgumentException
2361
-     * @throws InvalidDataTypeException
2362
-     * @throws InvalidInterfaceException
2363
-     * @throws ReflectionException
2364
-     * @throws RuntimeException
2365
-     * @throws UnexpectedEntityException
2366
-     */
2367
-    public function restore($source = 'unknown')
2368
-    {
2369
-        $previous_status = $this->get_extra_meta(
2370
-            EE_Registration::PRE_TRASH_REG_STATUS_KEY,
2371
-            true,
2372
-            EEM_Registration::status_id_cancelled
2373
-        );
2374
-        if ($previous_status) {
2375
-            $this->delete_extra_meta(EE_Registration::PRE_TRASH_REG_STATUS_KEY);
2376
-            $this->set_status($previous_status);
2377
-        }
2378
-        $current_user = wp_get_current_user();
2379
-        $this->add_extra_meta(
2380
-            EE_Registration::EXTRA_META_KEY_REG_RESTORED,
2381
-            array(
2382
-                'restored-by' => $current_user->ID ? $current_user->display_name : 'unauthed user',
2383
-                'timestamp'   => time(),
2384
-                'source'      => $source,
2385
-            )
2386
-        );
2387
-        return parent::restore();
2388
-    }
2389
-
2390
-
2391
-    /**
2392
-     * possibly toggle Registration status based on comparison of REG_paid vs REG_final_price
2393
-     *
2394
-     * @param  boolean $trigger_set_status_logic EE_Registration::set_status() can trigger additional logic
2395
-     *                                           depending on whether the reg status changes to or from "Approved"
2396
-     * @return boolean whether the Registration status was updated
2397
-     * @throws DomainException
2398
-     * @throws EE_Error
2399
-     * @throws EntityNotFoundException
2400
-     * @throws InvalidArgumentException
2401
-     * @throws InvalidDataTypeException
2402
-     * @throws InvalidInterfaceException
2403
-     * @throws ReflectionException
2404
-     * @throws RuntimeException
2405
-     * @throws UnexpectedEntityException
2406
-     */
2407
-    public function updateStatusBasedOnTotalPaid($trigger_set_status_logic = true)
2408
-    {
2409
-        $paid = $this->paid();
2410
-        $price = $this->final_price();
2411
-        switch (true) {
2412
-            // overpaid or paid
2413
-            case EEH_Money::compare_floats($paid, $price, '>'):
2414
-            case EEH_Money::compare_floats($paid, $price):
2415
-                $new_status = EEM_Registration::status_id_approved;
2416
-                break;
2417
-            //  underpaid
2418
-            case EEH_Money::compare_floats($paid, $price, '<'):
2419
-                $new_status = EEM_Registration::status_id_pending_payment;
2420
-                break;
2421
-            // uhhh Houston...
2422
-            default:
2423
-                throw new RuntimeException(
2424
-                    esc_html__('The total paid calculation for this registration is inaccurate.', 'event_espresso')
2425
-                );
2426
-        }
2427
-        if ($new_status !== $this->status_ID()) {
2428
-            if ($trigger_set_status_logic) {
2429
-                return $this->set_status($new_status);
2430
-            }
2431
-            parent::set('STS_ID', $new_status);
2432
-            return true;
2433
-        }
2434
-        return false;
2435
-    }
2436
-
2437
-
2438
-    /*************************** DEPRECATED ***************************/
2439
-
2440
-
2441
-    /**
2442
-     * @deprecated
2443
-     * @since     4.7.0
2444
-     */
2445
-    public function price_paid()
2446
-    {
2447
-        EE_Error::doing_it_wrong(
2448
-            'EE_Registration::price_paid()',
2449
-            esc_html__(
2450
-                'This method is deprecated, please use EE_Registration::final_price() instead.',
2451
-                'event_espresso'
2452
-            ),
2453
-            '4.7.0'
2454
-        );
2455
-        return $this->final_price();
2456
-    }
2457
-
2458
-
2459
-    /**
2460
-     * @deprecated
2461
-     * @since     4.7.0
2462
-     * @param    float $REG_final_price
2463
-     * @throws EE_Error
2464
-     * @throws EntityNotFoundException
2465
-     * @throws InvalidArgumentException
2466
-     * @throws InvalidDataTypeException
2467
-     * @throws InvalidInterfaceException
2468
-     * @throws ReflectionException
2469
-     * @throws RuntimeException
2470
-     * @throws DomainException
2471
-     */
2472
-    public function set_price_paid($REG_final_price = 0.00)
2473
-    {
2474
-        EE_Error::doing_it_wrong(
2475
-            'EE_Registration::set_price_paid()',
2476
-            esc_html__(
2477
-                'This method is deprecated, please use EE_Registration::set_final_price() instead.',
2478
-                'event_espresso'
2479
-            ),
2480
-            '4.7.0'
2481
-        );
2482
-        $this->set_final_price($REG_final_price);
2483
-    }
2484
-
2485
-
2486
-    /**
2487
-     * @deprecated
2488
-     * @since 4.7.0
2489
-     * @return string
2490
-     * @throws EE_Error
2491
-     * @throws InvalidArgumentException
2492
-     * @throws InvalidDataTypeException
2493
-     * @throws InvalidInterfaceException
2494
-     * @throws ReflectionException
2495
-     */
2496
-    public function pretty_price_paid()
2497
-    {
2498
-        EE_Error::doing_it_wrong(
2499
-            'EE_Registration::pretty_price_paid()',
2500
-            esc_html__(
2501
-                'This method is deprecated, please use EE_Registration::pretty_final_price() instead.',
2502
-                'event_espresso'
2503
-            ),
2504
-            '4.7.0'
2505
-        );
2506
-        return $this->pretty_final_price();
2507
-    }
2508
-
2509
-
2510
-    /**
2511
-     * Gets the primary datetime related to this registration via the related Event to this registration
2512
-     *
2513
-     * @deprecated 4.9.17
2514
-     * @return EE_Datetime
2515
-     * @throws EE_Error
2516
-     * @throws EntityNotFoundException
2517
-     * @throws InvalidArgumentException
2518
-     * @throws InvalidDataTypeException
2519
-     * @throws InvalidInterfaceException
2520
-     * @throws ReflectionException
2521
-     */
2522
-    public function get_related_primary_datetime()
2523
-    {
2524
-        EE_Error::doing_it_wrong(
2525
-            __METHOD__,
2526
-            esc_html__(
2527
-                'Use EE_Registration::get_latest_related_datetime() or EE_Registration::get_earliest_related_datetime()',
2528
-                'event_espresso'
2529
-            ),
2530
-            '4.9.17',
2531
-            '5.0.0'
2532
-        );
2533
-        return $this->event()->primary_datetime();
2534
-    }
2535
-
2536
-    /**
2537
-     * Returns the contact's name (or "Unknown" if there is no contact.)
2538
-     * @since 4.10.12.p
2539
-     * @return string
2540
-     * @throws EE_Error
2541
-     * @throws InvalidArgumentException
2542
-     * @throws InvalidDataTypeException
2543
-     * @throws InvalidInterfaceException
2544
-     * @throws ReflectionException
2545
-     */
2546
-    public function name()
2547
-    {
2548
-        return $this->attendeeName();
2549
-    }
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
+	 * extra meta key for tracking when registrations are trashed and by who
59
+	 *
60
+	 * @type string
61
+	 */
62
+	const EXTRA_META_KEY_REG_TRASHED = 'registration-trashed';
63
+
64
+	/**
65
+	 * extra meta key for tracking when registrations are restored and by who
66
+	 *
67
+	 * @type string
68
+	 */
69
+	const EXTRA_META_KEY_REG_RESTORED = 'registration-restored';
70
+
71
+
72
+	/**
73
+	 * @param array  $props_n_values          incoming values
74
+	 * @param string $timezone                incoming timezone (if not set the timezone set for the website will be
75
+	 *                                        used.)
76
+	 * @param array  $date_formats            incoming date_formats in an array where the first value is the
77
+	 *                                        date_format and the second value is the time format
78
+	 * @return EE_Registration
79
+	 * @throws EE_Error
80
+	 * @throws InvalidArgumentException
81
+	 * @throws InvalidDataTypeException
82
+	 * @throws InvalidInterfaceException
83
+	 * @throws ReflectionException
84
+	 */
85
+	public static function new_instance($props_n_values = array(), $timezone = null, $date_formats = array())
86
+	{
87
+		$has_object = parent::_check_for_object($props_n_values, __CLASS__, $timezone, $date_formats);
88
+		return $has_object ? $has_object : new self($props_n_values, false, $timezone, $date_formats);
89
+	}
90
+
91
+
92
+	/**
93
+	 * @param array  $props_n_values  incoming values from the database
94
+	 * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
95
+	 *                                the website will be used.
96
+	 * @return EE_Registration
97
+	 * @throws EE_Error
98
+	 * @throws InvalidArgumentException
99
+	 * @throws InvalidDataTypeException
100
+	 * @throws InvalidInterfaceException
101
+	 * @throws ReflectionException
102
+	 */
103
+	public static function new_instance_from_db($props_n_values = array(), $timezone = null)
104
+	{
105
+		return new self($props_n_values, true, $timezone);
106
+	}
107
+
108
+
109
+	/**
110
+	 *        Set Event ID
111
+	 *
112
+	 * @param        int $EVT_ID Event ID
113
+	 * @throws DomainException
114
+	 * @throws EE_Error
115
+	 * @throws EntityNotFoundException
116
+	 * @throws InvalidArgumentException
117
+	 * @throws InvalidDataTypeException
118
+	 * @throws InvalidInterfaceException
119
+	 * @throws ReflectionException
120
+	 * @throws RuntimeException
121
+	 * @throws UnexpectedEntityException
122
+	 */
123
+	public function set_event($EVT_ID = 0)
124
+	{
125
+		$this->set('EVT_ID', $EVT_ID);
126
+	}
127
+
128
+
129
+	/**
130
+	 * Overrides parent set() method so that all calls to set( 'REG_code', $REG_code ) OR set( 'STS_ID', $STS_ID ) can
131
+	 * be routed to internal methods
132
+	 *
133
+	 * @param string $field_name
134
+	 * @param mixed  $field_value
135
+	 * @param bool   $use_default
136
+	 * @throws DomainException
137
+	 * @throws EE_Error
138
+	 * @throws EntityNotFoundException
139
+	 * @throws InvalidArgumentException
140
+	 * @throws InvalidDataTypeException
141
+	 * @throws InvalidInterfaceException
142
+	 * @throws ReflectionException
143
+	 * @throws RuntimeException
144
+	 * @throws UnexpectedEntityException
145
+	 */
146
+	public function set($field_name, $field_value, $use_default = false)
147
+	{
148
+		switch ($field_name) {
149
+			case 'REG_code':
150
+				if (! empty($field_value) && $this->reg_code() === null) {
151
+					$this->set_reg_code($field_value, $use_default);
152
+				}
153
+				break;
154
+			case 'STS_ID':
155
+				$this->set_status($field_value, $use_default);
156
+				break;
157
+			default:
158
+				parent::set($field_name, $field_value, $use_default);
159
+		}
160
+	}
161
+
162
+
163
+	/**
164
+	 * Set Status ID
165
+	 * updates the registration status and ALSO...
166
+	 * calls reserve_registration_space() if the reg status changes TO approved from any other reg status
167
+	 * calls release_registration_space() if the reg status changes FROM approved to any other reg status
168
+	 *
169
+	 * @param string                $new_STS_ID
170
+	 * @param boolean               $use_default
171
+	 * @param ContextInterface|null $context
172
+	 * @return bool
173
+	 * @throws DomainException
174
+	 * @throws EE_Error
175
+	 * @throws EntityNotFoundException
176
+	 * @throws InvalidArgumentException
177
+	 * @throws InvalidDataTypeException
178
+	 * @throws InvalidInterfaceException
179
+	 * @throws ReflectionException
180
+	 * @throws RuntimeException
181
+	 * @throws UnexpectedEntityException
182
+	 */
183
+	public function set_status($new_STS_ID = null, $use_default = false, ContextInterface $context = null)
184
+	{
185
+		// get current REG_Status
186
+		$old_STS_ID = $this->status_ID();
187
+		// if status has changed
188
+		if (
189
+			$old_STS_ID !== $new_STS_ID // and that status has actually changed
190
+			&& ! empty($old_STS_ID) // and that old status is actually set
191
+			&& ! empty($new_STS_ID) // as well as the new status
192
+			&& $this->ID() // ensure registration is in the db
193
+		) {
194
+			// update internal status first
195
+			parent::set('STS_ID', $new_STS_ID, $use_default);
196
+			// THEN handle other changes that occur when reg status changes
197
+			// TO approved
198
+			if ($new_STS_ID === EEM_Registration::status_id_approved) {
199
+				// reserve a space by incrementing ticket and datetime sold values
200
+				$this->reserveRegistrationSpace();
201
+				do_action('AHEE__EE_Registration__set_status__to_approved', $this, $old_STS_ID, $new_STS_ID, $context);
202
+				// OR FROM  approved
203
+			} elseif ($old_STS_ID === EEM_Registration::status_id_approved) {
204
+				// release a space by decrementing ticket and datetime sold values
205
+				$this->releaseRegistrationSpace();
206
+				do_action(
207
+					'AHEE__EE_Registration__set_status__from_approved',
208
+					$this,
209
+					$old_STS_ID,
210
+					$new_STS_ID,
211
+					$context
212
+				);
213
+			}
214
+			// update status
215
+			parent::set('STS_ID', $new_STS_ID, $use_default);
216
+			$this->updateIfCanceledOrReinstated($new_STS_ID, $old_STS_ID, $context);
217
+			if ($this->statusChangeUpdatesTransaction($context)) {
218
+				$this->updateTransactionAfterStatusChange();
219
+			}
220
+			do_action('AHEE__EE_Registration__set_status__after_update', $this, $old_STS_ID, $new_STS_ID, $context);
221
+			return true;
222
+		}
223
+		// even though the old value matches the new value, it's still good to
224
+		// allow the parent set method to have a say
225
+		parent::set('STS_ID', $new_STS_ID, $use_default);
226
+		return true;
227
+	}
228
+
229
+
230
+	/**
231
+	 * update REGs and TXN when cancelled or declined registrations involved
232
+	 *
233
+	 * @param string                $new_STS_ID
234
+	 * @param string                $old_STS_ID
235
+	 * @param ContextInterface|null $context
236
+	 * @throws EE_Error
237
+	 * @throws InvalidArgumentException
238
+	 * @throws InvalidDataTypeException
239
+	 * @throws InvalidInterfaceException
240
+	 * @throws ReflectionException
241
+	 * @throws RuntimeException
242
+	 */
243
+	private function updateIfCanceledOrReinstated($new_STS_ID, $old_STS_ID, ContextInterface $context = null)
244
+	{
245
+		// these reg statuses should not be considered in any calculations involving monies owing
246
+		$closed_reg_statuses = EEM_Registration::closed_reg_statuses();
247
+		// true if registration has been cancelled or declined
248
+		$this->updateIfCanceled(
249
+			$closed_reg_statuses,
250
+			$new_STS_ID,
251
+			$old_STS_ID,
252
+			$context
253
+		);
254
+		$this->updateIfReinstated(
255
+			$closed_reg_statuses,
256
+			$new_STS_ID,
257
+			$old_STS_ID,
258
+			$context
259
+		);
260
+	}
261
+
262
+
263
+	/**
264
+	 * update REGs and TXN when cancelled or declined registrations involved
265
+	 *
266
+	 * @param array                 $closed_reg_statuses
267
+	 * @param string                $new_STS_ID
268
+	 * @param string                $old_STS_ID
269
+	 * @param ContextInterface|null $context
270
+	 * @throws EE_Error
271
+	 * @throws InvalidArgumentException
272
+	 * @throws InvalidDataTypeException
273
+	 * @throws InvalidInterfaceException
274
+	 * @throws ReflectionException
275
+	 * @throws RuntimeException
276
+	 */
277
+	private function updateIfCanceled(
278
+		array $closed_reg_statuses,
279
+		$new_STS_ID,
280
+		$old_STS_ID,
281
+		ContextInterface $context = null
282
+	) {
283
+		// true if registration has been cancelled or declined
284
+		if (
285
+			in_array($new_STS_ID, $closed_reg_statuses, true)
286
+			&& ! in_array($old_STS_ID, $closed_reg_statuses, true)
287
+		) {
288
+			/** @type EE_Registration_Processor $registration_processor */
289
+			$registration_processor = EE_Registry::instance()->load_class('Registration_Processor');
290
+			/** @type EE_Transaction_Processor $transaction_processor */
291
+			$transaction_processor = EE_Registry::instance()->load_class('Transaction_Processor');
292
+			// cancelled or declined registration
293
+			$registration_processor->update_registration_after_being_canceled_or_declined(
294
+				$this,
295
+				$closed_reg_statuses
296
+			);
297
+			$transaction_processor->update_transaction_after_canceled_or_declined_registration(
298
+				$this,
299
+				$closed_reg_statuses,
300
+				false
301
+			);
302
+			do_action(
303
+				'AHEE__EE_Registration__set_status__canceled_or_declined',
304
+				$this,
305
+				$old_STS_ID,
306
+				$new_STS_ID,
307
+				$context
308
+			);
309
+			return;
310
+		}
311
+	}
312
+
313
+
314
+	/**
315
+	 * update REGs and TXN when cancelled or declined registrations involved
316
+	 *
317
+	 * @param array                 $closed_reg_statuses
318
+	 * @param string                $new_STS_ID
319
+	 * @param string                $old_STS_ID
320
+	 * @param ContextInterface|null $context
321
+	 * @throws EE_Error
322
+	 * @throws InvalidArgumentException
323
+	 * @throws InvalidDataTypeException
324
+	 * @throws InvalidInterfaceException
325
+	 * @throws ReflectionException
326
+	 * @throws RuntimeException
327
+	 */
328
+	private function updateIfReinstated(
329
+		array $closed_reg_statuses,
330
+		$new_STS_ID,
331
+		$old_STS_ID,
332
+		ContextInterface $context = null
333
+	) {
334
+		// true if reinstating cancelled or declined registration
335
+		if (
336
+			in_array($old_STS_ID, $closed_reg_statuses, true)
337
+			&& ! in_array($new_STS_ID, $closed_reg_statuses, true)
338
+		) {
339
+			/** @type EE_Registration_Processor $registration_processor */
340
+			$registration_processor = EE_Registry::instance()->load_class('Registration_Processor');
341
+			/** @type EE_Transaction_Processor $transaction_processor */
342
+			$transaction_processor = EE_Registry::instance()->load_class('Transaction_Processor');
343
+			// reinstating cancelled or declined registration
344
+			$registration_processor->update_canceled_or_declined_registration_after_being_reinstated(
345
+				$this,
346
+				$closed_reg_statuses
347
+			);
348
+			$transaction_processor->update_transaction_after_reinstating_canceled_registration(
349
+				$this,
350
+				$closed_reg_statuses,
351
+				false
352
+			);
353
+			do_action(
354
+				'AHEE__EE_Registration__set_status__after_reinstated',
355
+				$this,
356
+				$old_STS_ID,
357
+				$new_STS_ID,
358
+				$context
359
+			);
360
+		}
361
+	}
362
+
363
+
364
+	/**
365
+	 * @param ContextInterface|null $context
366
+	 * @return bool
367
+	 */
368
+	private function statusChangeUpdatesTransaction(ContextInterface $context = null)
369
+	{
370
+		$contexts_that_do_not_update_transaction = (array) apply_filters(
371
+			'AHEE__EE_Registration__statusChangeUpdatesTransaction__contexts_that_do_not_update_transaction',
372
+			array('spco_reg_step_attendee_information_process_registrations'),
373
+			$context,
374
+			$this
375
+		);
376
+		return ! (
377
+			$context instanceof ContextInterface
378
+			&& in_array($context->slug(), $contexts_that_do_not_update_transaction, true)
379
+		);
380
+	}
381
+
382
+
383
+	/**
384
+	 * @throws EE_Error
385
+	 * @throws EntityNotFoundException
386
+	 * @throws InvalidArgumentException
387
+	 * @throws InvalidDataTypeException
388
+	 * @throws InvalidInterfaceException
389
+	 * @throws ReflectionException
390
+	 * @throws RuntimeException
391
+	 */
392
+	private function updateTransactionAfterStatusChange()
393
+	{
394
+		/** @type EE_Transaction_Payments $transaction_payments */
395
+		$transaction_payments = EE_Registry::instance()->load_class('Transaction_Payments');
396
+		$transaction_payments->recalculate_transaction_total($this->transaction(), false);
397
+		$this->transaction()->update_status_based_on_total_paid();
398
+	}
399
+
400
+
401
+	/**
402
+	 * get Status ID
403
+	 *
404
+	 * @throws EE_Error
405
+	 * @throws InvalidArgumentException
406
+	 * @throws InvalidDataTypeException
407
+	 * @throws InvalidInterfaceException
408
+	 * @throws ReflectionException
409
+	 */
410
+	public function status_ID()
411
+	{
412
+		return $this->get('STS_ID');
413
+	}
414
+
415
+
416
+	/**
417
+	 * Gets the ticket this registration is for
418
+	 *
419
+	 * @param boolean $include_archived whether to include archived tickets or not.
420
+	 * @return EE_Ticket|EE_Base_Class
421
+	 * @throws EE_Error
422
+	 * @throws InvalidArgumentException
423
+	 * @throws InvalidDataTypeException
424
+	 * @throws InvalidInterfaceException
425
+	 * @throws ReflectionException
426
+	 */
427
+	public function ticket($include_archived = true)
428
+	{
429
+		$query_params = array();
430
+		if ($include_archived) {
431
+			$query_params['default_where_conditions'] = 'none';
432
+		}
433
+		return $this->get_first_related('Ticket', $query_params);
434
+	}
435
+
436
+
437
+	/**
438
+	 * Gets the event this registration is for
439
+	 *
440
+	 * @return EE_Event
441
+	 * @throws EE_Error
442
+	 * @throws EntityNotFoundException
443
+	 * @throws InvalidArgumentException
444
+	 * @throws InvalidDataTypeException
445
+	 * @throws InvalidInterfaceException
446
+	 * @throws ReflectionException
447
+	 */
448
+	public function event()
449
+	{
450
+		$event = $this->get_first_related('Event');
451
+		if (! $event instanceof \EE_Event) {
452
+			throw new EntityNotFoundException('Event ID', $this->event_ID());
453
+		}
454
+		return $event;
455
+	}
456
+
457
+
458
+	/**
459
+	 * Gets the "author" of the registration.  Note that for the purposes of registrations, the author will correspond
460
+	 * with the author of the event this registration is for.
461
+	 *
462
+	 * @since 4.5.0
463
+	 * @return int
464
+	 * @throws EE_Error
465
+	 * @throws EntityNotFoundException
466
+	 * @throws InvalidArgumentException
467
+	 * @throws InvalidDataTypeException
468
+	 * @throws InvalidInterfaceException
469
+	 * @throws ReflectionException
470
+	 */
471
+	public function wp_user()
472
+	{
473
+		$event = $this->event();
474
+		if ($event instanceof EE_Event) {
475
+			return $event->wp_user();
476
+		}
477
+		return 0;
478
+	}
479
+
480
+
481
+	/**
482
+	 * increments this registration's related ticket sold and corresponding datetime sold values
483
+	 *
484
+	 * @return void
485
+	 * @throws DomainException
486
+	 * @throws EE_Error
487
+	 * @throws EntityNotFoundException
488
+	 * @throws InvalidArgumentException
489
+	 * @throws InvalidDataTypeException
490
+	 * @throws InvalidInterfaceException
491
+	 * @throws ReflectionException
492
+	 * @throws UnexpectedEntityException
493
+	 */
494
+	private function reserveRegistrationSpace()
495
+	{
496
+		// reserved ticket and datetime counts will be decremented as sold counts are incremented
497
+		// so stop tracking that this reg has a ticket reserved
498
+		$this->release_reserved_ticket(false, "REG: {$this->ID()} (ln:" . __LINE__ . ')');
499
+		$ticket = $this->ticket();
500
+		$ticket->increaseSold();
501
+		// possibly set event status to sold out
502
+		$this->event()->perform_sold_out_status_check();
503
+	}
504
+
505
+
506
+	/**
507
+	 * decrements (subtracts) this registration's related ticket sold and corresponding datetime sold values
508
+	 *
509
+	 * @return void
510
+	 * @throws DomainException
511
+	 * @throws EE_Error
512
+	 * @throws EntityNotFoundException
513
+	 * @throws InvalidArgumentException
514
+	 * @throws InvalidDataTypeException
515
+	 * @throws InvalidInterfaceException
516
+	 * @throws ReflectionException
517
+	 * @throws UnexpectedEntityException
518
+	 */
519
+	private function releaseRegistrationSpace()
520
+	{
521
+		$ticket = $this->ticket();
522
+		$ticket->decreaseSold();
523
+		// possibly change event status from sold out back to previous status
524
+		$this->event()->perform_sold_out_status_check();
525
+	}
526
+
527
+
528
+	/**
529
+	 * tracks this registration's ticket reservation in extra meta
530
+	 * and can increment related ticket reserved and corresponding datetime reserved values
531
+	 *
532
+	 * @param bool   $update_ticket if true, will increment ticket and datetime reserved count
533
+	 * @param string $source
534
+	 * @return void
535
+	 * @throws EE_Error
536
+	 * @throws InvalidArgumentException
537
+	 * @throws InvalidDataTypeException
538
+	 * @throws InvalidInterfaceException
539
+	 * @throws ReflectionException
540
+	 */
541
+	public function reserve_ticket($update_ticket = false, $source = 'unknown')
542
+	{
543
+		// only reserve ticket if space is not currently reserved
544
+		if ((bool) $this->get_extra_meta(EE_Registration::HAS_RESERVED_TICKET_KEY, true) !== true) {
545
+			$reserved = $this->update_extra_meta(EE_Registration::HAS_RESERVED_TICKET_KEY, true);
546
+			if ($reserved && $update_ticket) {
547
+				$ticket = $this->ticket();
548
+				$ticket->increaseReserved(1, "REG: {$this->ID()} (ln:" . __LINE__ . ')');
549
+				$this->update_extra_meta('reserve_ticket', "{$this->ticket_ID()} from {$source}");
550
+				$ticket->save();
551
+			}
552
+		}
553
+	}
554
+
555
+
556
+	/**
557
+	 * stops tracking this registration's ticket reservation in extra meta
558
+	 * decrements (subtracts) related ticket reserved and corresponding datetime reserved values
559
+	 *
560
+	 * @param bool   $update_ticket if true, will decrement ticket and datetime reserved count
561
+	 * @param string $source
562
+	 * @return void
563
+	 * @throws EE_Error
564
+	 * @throws InvalidArgumentException
565
+	 * @throws InvalidDataTypeException
566
+	 * @throws InvalidInterfaceException
567
+	 * @throws ReflectionException
568
+	 */
569
+	public function release_reserved_ticket($update_ticket = false, $source = 'unknown')
570
+	{
571
+		// only release ticket if space is currently reserved
572
+		if ((bool) $this->get_extra_meta(EE_Registration::HAS_RESERVED_TICKET_KEY, true) === true) {
573
+			$reserved = $this->update_extra_meta(EE_Registration::HAS_RESERVED_TICKET_KEY, false);
574
+			if ($reserved && $update_ticket) {
575
+				$ticket = $this->ticket();
576
+				$ticket->decreaseReserved(1, true, "REG: {$this->ID()} (ln:" . __LINE__ . ')');
577
+				$this->update_extra_meta('release_reserved_ticket', "{$this->ticket_ID()} from {$source}");
578
+			}
579
+		}
580
+	}
581
+
582
+
583
+	/**
584
+	 * Set Attendee ID
585
+	 *
586
+	 * @param        int $ATT_ID Attendee ID
587
+	 * @throws DomainException
588
+	 * @throws EE_Error
589
+	 * @throws EntityNotFoundException
590
+	 * @throws InvalidArgumentException
591
+	 * @throws InvalidDataTypeException
592
+	 * @throws InvalidInterfaceException
593
+	 * @throws ReflectionException
594
+	 * @throws RuntimeException
595
+	 * @throws UnexpectedEntityException
596
+	 */
597
+	public function set_attendee_id($ATT_ID = 0)
598
+	{
599
+		$this->set('ATT_ID', $ATT_ID);
600
+	}
601
+
602
+
603
+	/**
604
+	 *        Set Transaction ID
605
+	 *
606
+	 * @param        int $TXN_ID Transaction ID
607
+	 * @throws DomainException
608
+	 * @throws EE_Error
609
+	 * @throws EntityNotFoundException
610
+	 * @throws InvalidArgumentException
611
+	 * @throws InvalidDataTypeException
612
+	 * @throws InvalidInterfaceException
613
+	 * @throws ReflectionException
614
+	 * @throws RuntimeException
615
+	 * @throws UnexpectedEntityException
616
+	 */
617
+	public function set_transaction_id($TXN_ID = 0)
618
+	{
619
+		$this->set('TXN_ID', $TXN_ID);
620
+	}
621
+
622
+
623
+	/**
624
+	 *        Set Session
625
+	 *
626
+	 * @param    string $REG_session PHP Session ID
627
+	 * @throws DomainException
628
+	 * @throws EE_Error
629
+	 * @throws EntityNotFoundException
630
+	 * @throws InvalidArgumentException
631
+	 * @throws InvalidDataTypeException
632
+	 * @throws InvalidInterfaceException
633
+	 * @throws ReflectionException
634
+	 * @throws RuntimeException
635
+	 * @throws UnexpectedEntityException
636
+	 */
637
+	public function set_session($REG_session = '')
638
+	{
639
+		$this->set('REG_session', $REG_session);
640
+	}
641
+
642
+
643
+	/**
644
+	 *        Set Registration URL Link
645
+	 *
646
+	 * @param    string $REG_url_link Registration URL Link
647
+	 * @throws DomainException
648
+	 * @throws EE_Error
649
+	 * @throws EntityNotFoundException
650
+	 * @throws InvalidArgumentException
651
+	 * @throws InvalidDataTypeException
652
+	 * @throws InvalidInterfaceException
653
+	 * @throws ReflectionException
654
+	 * @throws RuntimeException
655
+	 * @throws UnexpectedEntityException
656
+	 */
657
+	public function set_reg_url_link($REG_url_link = '')
658
+	{
659
+		$this->set('REG_url_link', $REG_url_link);
660
+	}
661
+
662
+
663
+	/**
664
+	 *        Set Attendee Counter
665
+	 *
666
+	 * @param        int $REG_count Primary Attendee
667
+	 * @throws DomainException
668
+	 * @throws EE_Error
669
+	 * @throws EntityNotFoundException
670
+	 * @throws InvalidArgumentException
671
+	 * @throws InvalidDataTypeException
672
+	 * @throws InvalidInterfaceException
673
+	 * @throws ReflectionException
674
+	 * @throws RuntimeException
675
+	 * @throws UnexpectedEntityException
676
+	 */
677
+	public function set_count($REG_count = 1)
678
+	{
679
+		$this->set('REG_count', $REG_count);
680
+	}
681
+
682
+
683
+	/**
684
+	 *        Set Group Size
685
+	 *
686
+	 * @param        boolean $REG_group_size Group Registration
687
+	 * @throws DomainException
688
+	 * @throws EE_Error
689
+	 * @throws EntityNotFoundException
690
+	 * @throws InvalidArgumentException
691
+	 * @throws InvalidDataTypeException
692
+	 * @throws InvalidInterfaceException
693
+	 * @throws ReflectionException
694
+	 * @throws RuntimeException
695
+	 * @throws UnexpectedEntityException
696
+	 */
697
+	public function set_group_size($REG_group_size = false)
698
+	{
699
+		$this->set('REG_group_size', $REG_group_size);
700
+	}
701
+
702
+
703
+	/**
704
+	 *    is_not_approved -  convenience method that returns TRUE if REG status ID ==
705
+	 *    EEM_Registration::status_id_not_approved
706
+	 *
707
+	 * @return        boolean
708
+	 * @throws EE_Error
709
+	 * @throws InvalidArgumentException
710
+	 * @throws InvalidDataTypeException
711
+	 * @throws InvalidInterfaceException
712
+	 * @throws ReflectionException
713
+	 */
714
+	public function is_not_approved()
715
+	{
716
+		return $this->status_ID() === EEM_Registration::status_id_not_approved;
717
+	}
718
+
719
+
720
+	/**
721
+	 *    is_pending_payment -  convenience method that returns TRUE if REG status ID ==
722
+	 *    EEM_Registration::status_id_pending_payment
723
+	 *
724
+	 * @return        boolean
725
+	 * @throws EE_Error
726
+	 * @throws InvalidArgumentException
727
+	 * @throws InvalidDataTypeException
728
+	 * @throws InvalidInterfaceException
729
+	 * @throws ReflectionException
730
+	 */
731
+	public function is_pending_payment()
732
+	{
733
+		return $this->status_ID() === EEM_Registration::status_id_pending_payment;
734
+	}
735
+
736
+
737
+	/**
738
+	 *    is_approved -  convenience method that returns TRUE if REG status ID == EEM_Registration::status_id_approved
739
+	 *
740
+	 * @return        boolean
741
+	 * @throws EE_Error
742
+	 * @throws InvalidArgumentException
743
+	 * @throws InvalidDataTypeException
744
+	 * @throws InvalidInterfaceException
745
+	 * @throws ReflectionException
746
+	 */
747
+	public function is_approved()
748
+	{
749
+		return $this->status_ID() === EEM_Registration::status_id_approved;
750
+	}
751
+
752
+
753
+	/**
754
+	 *    is_cancelled -  convenience method that returns TRUE if REG status ID == EEM_Registration::status_id_cancelled
755
+	 *
756
+	 * @return        boolean
757
+	 * @throws EE_Error
758
+	 * @throws InvalidArgumentException
759
+	 * @throws InvalidDataTypeException
760
+	 * @throws InvalidInterfaceException
761
+	 * @throws ReflectionException
762
+	 */
763
+	public function is_cancelled()
764
+	{
765
+		return $this->status_ID() === EEM_Registration::status_id_cancelled;
766
+	}
767
+
768
+
769
+	/**
770
+	 *    is_declined -  convenience method that returns TRUE if REG status ID == EEM_Registration::status_id_declined
771
+	 *
772
+	 * @return        boolean
773
+	 * @throws EE_Error
774
+	 * @throws InvalidArgumentException
775
+	 * @throws InvalidDataTypeException
776
+	 * @throws InvalidInterfaceException
777
+	 * @throws ReflectionException
778
+	 */
779
+	public function is_declined()
780
+	{
781
+		return $this->status_ID() === EEM_Registration::status_id_declined;
782
+	}
783
+
784
+
785
+	/**
786
+	 *    is_incomplete -  convenience method that returns TRUE if REG status ID ==
787
+	 *    EEM_Registration::status_id_incomplete
788
+	 *
789
+	 * @return        boolean
790
+	 * @throws EE_Error
791
+	 * @throws InvalidArgumentException
792
+	 * @throws InvalidDataTypeException
793
+	 * @throws InvalidInterfaceException
794
+	 * @throws ReflectionException
795
+	 */
796
+	public function is_incomplete()
797
+	{
798
+		return $this->status_ID() === EEM_Registration::status_id_incomplete;
799
+	}
800
+
801
+
802
+	/**
803
+	 *        Set Registration Date
804
+	 *
805
+	 * @param        mixed ( int or string ) $REG_date Registration Date - Unix timestamp or string representation of
806
+	 *                                                 Date
807
+	 * @throws DomainException
808
+	 * @throws EE_Error
809
+	 * @throws EntityNotFoundException
810
+	 * @throws InvalidArgumentException
811
+	 * @throws InvalidDataTypeException
812
+	 * @throws InvalidInterfaceException
813
+	 * @throws ReflectionException
814
+	 * @throws RuntimeException
815
+	 * @throws UnexpectedEntityException
816
+	 */
817
+	public function set_reg_date($REG_date = false)
818
+	{
819
+		$this->set('REG_date', $REG_date);
820
+	}
821
+
822
+
823
+	/**
824
+	 *    Set final price owing for this registration after all ticket/price modifications
825
+	 *
826
+	 * @param    float $REG_final_price
827
+	 * @throws DomainException
828
+	 * @throws EE_Error
829
+	 * @throws EntityNotFoundException
830
+	 * @throws InvalidArgumentException
831
+	 * @throws InvalidDataTypeException
832
+	 * @throws InvalidInterfaceException
833
+	 * @throws ReflectionException
834
+	 * @throws RuntimeException
835
+	 * @throws UnexpectedEntityException
836
+	 */
837
+	public function set_final_price($REG_final_price = 0.00)
838
+	{
839
+		$this->set('REG_final_price', $REG_final_price);
840
+	}
841
+
842
+
843
+	/**
844
+	 *    Set amount paid towards this registration's final price
845
+	 *
846
+	 * @param    float $REG_paid
847
+	 * @throws DomainException
848
+	 * @throws EE_Error
849
+	 * @throws EntityNotFoundException
850
+	 * @throws InvalidArgumentException
851
+	 * @throws InvalidDataTypeException
852
+	 * @throws InvalidInterfaceException
853
+	 * @throws ReflectionException
854
+	 * @throws RuntimeException
855
+	 * @throws UnexpectedEntityException
856
+	 */
857
+	public function set_paid($REG_paid = 0.00)
858
+	{
859
+		$this->set('REG_paid', $REG_paid);
860
+	}
861
+
862
+
863
+	/**
864
+	 *        Attendee Is Going
865
+	 *
866
+	 * @param        boolean $REG_att_is_going Attendee Is Going
867
+	 * @throws DomainException
868
+	 * @throws EE_Error
869
+	 * @throws EntityNotFoundException
870
+	 * @throws InvalidArgumentException
871
+	 * @throws InvalidDataTypeException
872
+	 * @throws InvalidInterfaceException
873
+	 * @throws ReflectionException
874
+	 * @throws RuntimeException
875
+	 * @throws UnexpectedEntityException
876
+	 */
877
+	public function set_att_is_going($REG_att_is_going = false)
878
+	{
879
+		$this->set('REG_att_is_going', $REG_att_is_going);
880
+	}
881
+
882
+
883
+	/**
884
+	 * Gets the related attendee
885
+	 *
886
+	 * @return EE_Attendee|EE_Base_Class
887
+	 * @throws EE_Error
888
+	 * @throws InvalidArgumentException
889
+	 * @throws InvalidDataTypeException
890
+	 * @throws InvalidInterfaceException
891
+	 * @throws ReflectionException
892
+	 */
893
+	public function attendee()
894
+	{
895
+		return $this->get_first_related('Attendee');
896
+	}
897
+
898
+	/**
899
+	 * Gets the name of the attendee.
900
+	 * @since 4.10.12.p
901
+	 * @param bool $apply_html_entities set to true if you want to use HTML entities.
902
+	 * @return string
903
+	 * @throws EE_Error
904
+	 * @throws InvalidArgumentException
905
+	 * @throws InvalidDataTypeException
906
+	 * @throws InvalidInterfaceException
907
+	 * @throws ReflectionException
908
+	 */
909
+	public function attendeeName($apply_html_entities = false)
910
+	{
911
+		$attendee = $this->get_first_related('Attendee');
912
+		if ($attendee instanceof EE_Attendee) {
913
+			$attendee_name = $attendee->full_name($apply_html_entities);
914
+		} else {
915
+			$attendee_name = esc_html__('Unknown', 'event_espresso');
916
+		}
917
+		return $attendee_name;
918
+	}
919
+
920
+
921
+	/**
922
+	 *        get Event ID
923
+	 */
924
+	public function event_ID()
925
+	{
926
+		return $this->get('EVT_ID');
927
+	}
928
+
929
+
930
+	/**
931
+	 *        get Event ID
932
+	 */
933
+	public function event_name()
934
+	{
935
+		$event = $this->event_obj();
936
+		if ($event) {
937
+			return $event->name();
938
+		} else {
939
+			return null;
940
+		}
941
+	}
942
+
943
+
944
+	/**
945
+	 * Fetches the event this registration is for
946
+	 *
947
+	 * @return EE_Base_Class|EE_Event
948
+	 * @throws EE_Error
949
+	 * @throws InvalidArgumentException
950
+	 * @throws InvalidDataTypeException
951
+	 * @throws InvalidInterfaceException
952
+	 * @throws ReflectionException
953
+	 */
954
+	public function event_obj()
955
+	{
956
+		return $this->get_first_related('Event');
957
+	}
958
+
959
+
960
+	/**
961
+	 *        get Attendee ID
962
+	 */
963
+	public function attendee_ID()
964
+	{
965
+		return $this->get('ATT_ID');
966
+	}
967
+
968
+
969
+	/**
970
+	 *        get PHP Session ID
971
+	 */
972
+	public function session_ID()
973
+	{
974
+		return $this->get('REG_session');
975
+	}
976
+
977
+
978
+	/**
979
+	 * Gets the string which represents the URL trigger for the receipt template in the message template system.
980
+	 *
981
+	 * @param string $messenger 'pdf' or 'html'.  Default 'html'.
982
+	 * @return string
983
+	 * @throws DomainException
984
+	 * @throws EE_Error
985
+	 * @throws InvalidArgumentException
986
+	 * @throws InvalidDataTypeException
987
+	 * @throws InvalidInterfaceException
988
+	 * @throws ReflectionException
989
+	 */
990
+	public function receipt_url($messenger = 'html')
991
+	{
992
+
993
+		/**
994
+		 * The below will be deprecated one version after this.  We check first if there is a custom receipt template
995
+		 * already in use on old system.  If there is then we just return the standard url for it.
996
+		 *
997
+		 * @since 4.5.0
998
+		 */
999
+		$template_relative_path = 'modules/gateways/Invoice/lib/templates/receipt_body.template.php';
1000
+		$has_custom = EEH_Template::locate_template(
1001
+			$template_relative_path,
1002
+			array(),
1003
+			true,
1004
+			true,
1005
+			true
1006
+		);
1007
+
1008
+		if ($has_custom) {
1009
+			return add_query_arg(array('receipt' => 'true'), $this->invoice_url('launch'));
1010
+		}
1011
+		return apply_filters('FHEE__EE_Registration__receipt_url__receipt_url', '', $this, $messenger, 'receipt');
1012
+	}
1013
+
1014
+
1015
+	/**
1016
+	 * Gets the string which represents the URL trigger for the invoice template in the message template system.
1017
+	 *
1018
+	 * @param string $messenger 'pdf' or 'html'.  Default 'html'.
1019
+	 * @return string
1020
+	 * @throws DomainException
1021
+	 * @throws EE_Error
1022
+	 * @throws InvalidArgumentException
1023
+	 * @throws InvalidDataTypeException
1024
+	 * @throws InvalidInterfaceException
1025
+	 * @throws ReflectionException
1026
+	 */
1027
+	public function invoice_url($messenger = 'html')
1028
+	{
1029
+		/**
1030
+		 * The below will be deprecated one version after this.  We check first if there is a custom invoice template
1031
+		 * already in use on old system.  If there is then we just return the standard url for it.
1032
+		 *
1033
+		 * @since 4.5.0
1034
+		 */
1035
+		$template_relative_path = 'modules/gateways/Invoice/lib/templates/invoice_body.template.php';
1036
+		$has_custom = EEH_Template::locate_template(
1037
+			$template_relative_path,
1038
+			array(),
1039
+			true,
1040
+			true,
1041
+			true
1042
+		);
1043
+
1044
+		if ($has_custom) {
1045
+			if ($messenger == 'html') {
1046
+				return $this->invoice_url('launch');
1047
+			}
1048
+			$route = $messenger == 'download' || $messenger == 'pdf' ? 'download_invoice' : 'launch_invoice';
1049
+
1050
+			$query_args = array('ee' => $route, 'id' => $this->reg_url_link());
1051
+			if ($messenger == 'html') {
1052
+				$query_args['html'] = true;
1053
+			}
1054
+			return add_query_arg($query_args, get_permalink(EE_Registry::instance()->CFG->core->thank_you_page_id));
1055
+		}
1056
+		return apply_filters('FHEE__EE_Registration__invoice_url__invoice_url', '', $this, $messenger, 'invoice');
1057
+	}
1058
+
1059
+
1060
+	/**
1061
+	 * get Registration URL Link
1062
+	 *
1063
+	 * @return string
1064
+	 * @throws EE_Error
1065
+	 * @throws InvalidArgumentException
1066
+	 * @throws InvalidDataTypeException
1067
+	 * @throws InvalidInterfaceException
1068
+	 * @throws ReflectionException
1069
+	 */
1070
+	public function reg_url_link()
1071
+	{
1072
+		return (string) $this->get('REG_url_link');
1073
+	}
1074
+
1075
+
1076
+	/**
1077
+	 * Echoes out invoice_url()
1078
+	 *
1079
+	 * @param string $type 'download','launch', or 'html' (default is 'launch')
1080
+	 * @return void
1081
+	 * @throws DomainException
1082
+	 * @throws EE_Error
1083
+	 * @throws InvalidArgumentException
1084
+	 * @throws InvalidDataTypeException
1085
+	 * @throws InvalidInterfaceException
1086
+	 * @throws ReflectionException
1087
+	 */
1088
+	public function e_invoice_url($type = 'launch')
1089
+	{
1090
+		echo esc_url_raw($this->invoice_url($type));
1091
+	}
1092
+
1093
+
1094
+	/**
1095
+	 * Echoes out payment_overview_url
1096
+	 */
1097
+	public function e_payment_overview_url()
1098
+	{
1099
+		echo esc_url_raw($this->payment_overview_url());
1100
+	}
1101
+
1102
+
1103
+	/**
1104
+	 * Gets the URL for the checkout payment options reg step
1105
+	 * with this registration's REG_url_link added as a query parameter
1106
+	 *
1107
+	 * @param bool $clear_session Set to true when you want to clear the session on revisiting the
1108
+	 *                            payment overview url.
1109
+	 * @return string
1110
+	 * @throws EE_Error
1111
+	 * @throws InvalidArgumentException
1112
+	 * @throws InvalidDataTypeException
1113
+	 * @throws InvalidInterfaceException
1114
+	 * @throws ReflectionException
1115
+	 */
1116
+	public function payment_overview_url($clear_session = false)
1117
+	{
1118
+		return add_query_arg(
1119
+			(array) apply_filters(
1120
+				'FHEE__EE_Registration__payment_overview_url__query_args',
1121
+				array(
1122
+					'e_reg_url_link' => $this->reg_url_link(),
1123
+					'step'           => 'payment_options',
1124
+					'revisit'        => true,
1125
+					'clear_session'  => (bool) $clear_session,
1126
+				),
1127
+				$this
1128
+			),
1129
+			EE_Registry::instance()->CFG->core->reg_page_url()
1130
+		);
1131
+	}
1132
+
1133
+
1134
+	/**
1135
+	 * Gets the URL for the checkout attendee information reg step
1136
+	 * with this registration's REG_url_link added as a query parameter
1137
+	 *
1138
+	 * @return string
1139
+	 * @throws EE_Error
1140
+	 * @throws InvalidArgumentException
1141
+	 * @throws InvalidDataTypeException
1142
+	 * @throws InvalidInterfaceException
1143
+	 * @throws ReflectionException
1144
+	 */
1145
+	public function edit_attendee_information_url()
1146
+	{
1147
+		return add_query_arg(
1148
+			(array) apply_filters(
1149
+				'FHEE__EE_Registration__edit_attendee_information_url__query_args',
1150
+				array(
1151
+					'e_reg_url_link' => $this->reg_url_link(),
1152
+					'step'           => 'attendee_information',
1153
+					'revisit'        => true,
1154
+				),
1155
+				$this
1156
+			),
1157
+			EE_Registry::instance()->CFG->core->reg_page_url()
1158
+		);
1159
+	}
1160
+
1161
+
1162
+	/**
1163
+	 * Simply generates and returns the appropriate admin_url link to edit this registration
1164
+	 *
1165
+	 * @return string
1166
+	 * @throws EE_Error
1167
+	 * @throws InvalidArgumentException
1168
+	 * @throws InvalidDataTypeException
1169
+	 * @throws InvalidInterfaceException
1170
+	 * @throws ReflectionException
1171
+	 */
1172
+	public function get_admin_edit_url()
1173
+	{
1174
+		return EEH_URL::add_query_args_and_nonce(
1175
+			array(
1176
+				'page'    => 'espresso_registrations',
1177
+				'action'  => 'view_registration',
1178
+				'_REG_ID' => $this->ID(),
1179
+			),
1180
+			admin_url('admin.php')
1181
+		);
1182
+	}
1183
+
1184
+
1185
+	/**
1186
+	 * is_primary_registrant?
1187
+	 *
1188
+	 * @throws EE_Error
1189
+	 * @throws InvalidArgumentException
1190
+	 * @throws InvalidDataTypeException
1191
+	 * @throws InvalidInterfaceException
1192
+	 * @throws ReflectionException
1193
+	 */
1194
+	public function is_primary_registrant()
1195
+	{
1196
+		return (int) $this->get('REG_count') === 1;
1197
+	}
1198
+
1199
+
1200
+	/**
1201
+	 * This returns the primary registration object for this registration group (which may be this object).
1202
+	 *
1203
+	 * @return EE_Registration
1204
+	 * @throws EE_Error
1205
+	 * @throws InvalidArgumentException
1206
+	 * @throws InvalidDataTypeException
1207
+	 * @throws InvalidInterfaceException
1208
+	 * @throws ReflectionException
1209
+	 */
1210
+	public function get_primary_registration()
1211
+	{
1212
+		if ($this->is_primary_registrant()) {
1213
+			return $this;
1214
+		}
1215
+
1216
+		// k reg_count !== 1 so let's get the EE_Registration object matching this txn_id and reg_count == 1
1217
+		/** @var EE_Registration $primary_registrant */
1218
+		$primary_registrant = EEM_Registration::instance()->get_one(
1219
+			array(
1220
+				array(
1221
+					'TXN_ID'    => $this->transaction_ID(),
1222
+					'REG_count' => 1,
1223
+				),
1224
+			)
1225
+		);
1226
+		return $primary_registrant;
1227
+	}
1228
+
1229
+
1230
+	/**
1231
+	 * get  Attendee Number
1232
+	 *
1233
+	 * @throws EE_Error
1234
+	 * @throws InvalidArgumentException
1235
+	 * @throws InvalidDataTypeException
1236
+	 * @throws InvalidInterfaceException
1237
+	 * @throws ReflectionException
1238
+	 */
1239
+	public function count()
1240
+	{
1241
+		return $this->get('REG_count');
1242
+	}
1243
+
1244
+
1245
+	/**
1246
+	 * get Group Size
1247
+	 *
1248
+	 * @throws EE_Error
1249
+	 * @throws InvalidArgumentException
1250
+	 * @throws InvalidDataTypeException
1251
+	 * @throws InvalidInterfaceException
1252
+	 * @throws ReflectionException
1253
+	 */
1254
+	public function group_size()
1255
+	{
1256
+		return $this->get('REG_group_size');
1257
+	}
1258
+
1259
+
1260
+	/**
1261
+	 * get Registration Date
1262
+	 *
1263
+	 * @throws EE_Error
1264
+	 * @throws InvalidArgumentException
1265
+	 * @throws InvalidDataTypeException
1266
+	 * @throws InvalidInterfaceException
1267
+	 * @throws ReflectionException
1268
+	 */
1269
+	public function date()
1270
+	{
1271
+		return $this->get('REG_date');
1272
+	}
1273
+
1274
+
1275
+	/**
1276
+	 * gets a pretty date
1277
+	 *
1278
+	 * @param string $date_format
1279
+	 * @param string $time_format
1280
+	 * @return string
1281
+	 * @throws EE_Error
1282
+	 * @throws InvalidArgumentException
1283
+	 * @throws InvalidDataTypeException
1284
+	 * @throws InvalidInterfaceException
1285
+	 * @throws ReflectionException
1286
+	 */
1287
+	public function pretty_date($date_format = null, $time_format = null)
1288
+	{
1289
+		return $this->get_datetime('REG_date', $date_format, $time_format);
1290
+	}
1291
+
1292
+
1293
+	/**
1294
+	 * final_price
1295
+	 * the registration's share of the transaction total, so that the
1296
+	 * sum of all the transaction's REG_final_prices equal the transaction's total
1297
+	 *
1298
+	 * @return float
1299
+	 * @throws EE_Error
1300
+	 * @throws InvalidArgumentException
1301
+	 * @throws InvalidDataTypeException
1302
+	 * @throws InvalidInterfaceException
1303
+	 * @throws ReflectionException
1304
+	 */
1305
+	public function final_price()
1306
+	{
1307
+		return $this->get('REG_final_price');
1308
+	}
1309
+
1310
+
1311
+	/**
1312
+	 * pretty_final_price
1313
+	 *  final price as formatted string, with correct decimal places and currency symbol
1314
+	 *
1315
+	 * @return string
1316
+	 * @throws EE_Error
1317
+	 * @throws InvalidArgumentException
1318
+	 * @throws InvalidDataTypeException
1319
+	 * @throws InvalidInterfaceException
1320
+	 * @throws ReflectionException
1321
+	 */
1322
+	public function pretty_final_price()
1323
+	{
1324
+		return $this->get_pretty('REG_final_price');
1325
+	}
1326
+
1327
+
1328
+	/**
1329
+	 * get paid (yeah)
1330
+	 *
1331
+	 * @return float
1332
+	 * @throws EE_Error
1333
+	 * @throws InvalidArgumentException
1334
+	 * @throws InvalidDataTypeException
1335
+	 * @throws InvalidInterfaceException
1336
+	 * @throws ReflectionException
1337
+	 */
1338
+	public function paid()
1339
+	{
1340
+		return $this->get('REG_paid');
1341
+	}
1342
+
1343
+
1344
+	/**
1345
+	 * pretty_paid
1346
+	 *
1347
+	 * @return float
1348
+	 * @throws EE_Error
1349
+	 * @throws InvalidArgumentException
1350
+	 * @throws InvalidDataTypeException
1351
+	 * @throws InvalidInterfaceException
1352
+	 * @throws ReflectionException
1353
+	 */
1354
+	public function pretty_paid()
1355
+	{
1356
+		return $this->get_pretty('REG_paid');
1357
+	}
1358
+
1359
+
1360
+	/**
1361
+	 * owes_monies_and_can_pay
1362
+	 * whether or not this registration has monies owing and it's' status allows payment
1363
+	 *
1364
+	 * @param array $requires_payment
1365
+	 * @return bool
1366
+	 * @throws EE_Error
1367
+	 * @throws InvalidArgumentException
1368
+	 * @throws InvalidDataTypeException
1369
+	 * @throws InvalidInterfaceException
1370
+	 * @throws ReflectionException
1371
+	 */
1372
+	public function owes_monies_and_can_pay($requires_payment = array())
1373
+	{
1374
+		// these reg statuses require payment (if event is not free)
1375
+		$requires_payment = ! empty($requires_payment)
1376
+			? $requires_payment
1377
+			: EEM_Registration::reg_statuses_that_allow_payment();
1378
+		if (
1379
+			$this->final_price() !== 0 &&
1380
+			$this->final_price() !== $this->paid() &&
1381
+			in_array($this->status_ID(), $requires_payment)
1382
+		) {
1383
+			return true;
1384
+		}
1385
+		return false;
1386
+	}
1387
+
1388
+
1389
+	/**
1390
+	 * Prints out the return value of $this->pretty_status()
1391
+	 *
1392
+	 * @param bool $show_icons
1393
+	 * @return void
1394
+	 * @throws EE_Error
1395
+	 * @throws InvalidArgumentException
1396
+	 * @throws InvalidDataTypeException
1397
+	 * @throws InvalidInterfaceException
1398
+	 * @throws ReflectionException
1399
+	 */
1400
+	public function e_pretty_status($show_icons = false)
1401
+	{
1402
+		echo wp_kses($this->pretty_status($show_icons), AllowedTags::getAllowedTags());
1403
+	}
1404
+
1405
+
1406
+	/**
1407
+	 * Returns a nice version of the status for displaying to customers
1408
+	 *
1409
+	 * @param bool $show_icons
1410
+	 * @return string
1411
+	 * @throws EE_Error
1412
+	 * @throws InvalidArgumentException
1413
+	 * @throws InvalidDataTypeException
1414
+	 * @throws InvalidInterfaceException
1415
+	 * @throws ReflectionException
1416
+	 */
1417
+	public function pretty_status($show_icons = false)
1418
+	{
1419
+		$status = EEM_Status::instance()->localized_status(
1420
+			array($this->status_ID() => esc_html__('unknown', 'event_espresso')),
1421
+			false,
1422
+			'sentence'
1423
+		);
1424
+		$icon = '';
1425
+		switch ($this->status_ID()) {
1426
+			case EEM_Registration::status_id_approved:
1427
+				$icon = $show_icons
1428
+					? '<span class="dashicons dashicons-star-filled ee-icon-size-16 green-text"></span>'
1429
+					: '';
1430
+				break;
1431
+			case EEM_Registration::status_id_pending_payment:
1432
+				$icon = $show_icons
1433
+					? '<span class="dashicons dashicons-star-half ee-icon-size-16 orange-text"></span>'
1434
+					: '';
1435
+				break;
1436
+			case EEM_Registration::status_id_not_approved:
1437
+				$icon = $show_icons
1438
+					? '<span class="dashicons dashicons-marker ee-icon-size-16 orange-text"></span>'
1439
+					: '';
1440
+				break;
1441
+			case EEM_Registration::status_id_cancelled:
1442
+				$icon = $show_icons
1443
+					? '<span class="dashicons dashicons-no ee-icon-size-16 lt-grey-text"></span>'
1444
+					: '';
1445
+				break;
1446
+			case EEM_Registration::status_id_incomplete:
1447
+				$icon = $show_icons
1448
+					? '<span class="dashicons dashicons-no ee-icon-size-16 lt-orange-text"></span>'
1449
+					: '';
1450
+				break;
1451
+			case EEM_Registration::status_id_declined:
1452
+				$icon = $show_icons
1453
+					? '<span class="dashicons dashicons-no ee-icon-size-16 red-text"></span>'
1454
+					: '';
1455
+				break;
1456
+			case EEM_Registration::status_id_wait_list:
1457
+				$icon = $show_icons
1458
+					? '<span class="dashicons dashicons-clipboard ee-icon-size-16 purple-text"></span>'
1459
+					: '';
1460
+				break;
1461
+		}
1462
+		return $icon . $status[ $this->status_ID() ];
1463
+	}
1464
+
1465
+
1466
+	/**
1467
+	 *        get Attendee Is Going
1468
+	 */
1469
+	public function att_is_going()
1470
+	{
1471
+		return $this->get('REG_att_is_going');
1472
+	}
1473
+
1474
+
1475
+	/**
1476
+	 * Gets related answers
1477
+	 *
1478
+	 * @param array $query_params @see
1479
+	 *                            https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1480
+	 * @return EE_Answer[]|EE_Base_Class[]
1481
+	 * @throws EE_Error
1482
+	 * @throws InvalidArgumentException
1483
+	 * @throws InvalidDataTypeException
1484
+	 * @throws InvalidInterfaceException
1485
+	 * @throws ReflectionException
1486
+	 */
1487
+	public function answers($query_params = null)
1488
+	{
1489
+		return $this->get_many_related('Answer', $query_params);
1490
+	}
1491
+
1492
+
1493
+	/**
1494
+	 * Gets the registration's answer value to the specified question
1495
+	 * (either the question's ID or a question object)
1496
+	 *
1497
+	 * @param EE_Question|int $question
1498
+	 * @param bool            $pretty_value
1499
+	 * @return array|string if pretty_value= true, the result will always be a string
1500
+	 * (because the answer might be an array of answer values, so passing pretty_value=true
1501
+	 * will convert it into some kind of string)
1502
+	 * @throws EE_Error
1503
+	 * @throws InvalidArgumentException
1504
+	 * @throws InvalidDataTypeException
1505
+	 * @throws InvalidInterfaceException
1506
+	 */
1507
+	public function answer_value_to_question($question, $pretty_value = true)
1508
+	{
1509
+		$question_id = EEM_Question::instance()->ensure_is_ID($question);
1510
+		return EEM_Answer::instance()->get_answer_value_to_question($this, $question_id, $pretty_value);
1511
+	}
1512
+
1513
+
1514
+	/**
1515
+	 * question_groups
1516
+	 * returns an array of EE_Question_Group objects for this registration
1517
+	 *
1518
+	 * @return EE_Question_Group[]
1519
+	 * @throws EE_Error
1520
+	 * @throws InvalidArgumentException
1521
+	 * @throws InvalidDataTypeException
1522
+	 * @throws InvalidInterfaceException
1523
+	 * @throws ReflectionException
1524
+	 */
1525
+	public function question_groups()
1526
+	{
1527
+		return EEM_Event::instance()->get_question_groups_for_event($this->event_ID(), $this);
1528
+	}
1529
+
1530
+
1531
+	/**
1532
+	 * count_question_groups
1533
+	 * returns a count of the number of EE_Question_Group objects for this registration
1534
+	 *
1535
+	 * @return int
1536
+	 * @throws EE_Error
1537
+	 * @throws EntityNotFoundException
1538
+	 * @throws InvalidArgumentException
1539
+	 * @throws InvalidDataTypeException
1540
+	 * @throws InvalidInterfaceException
1541
+	 * @throws ReflectionException
1542
+	 */
1543
+	public function count_question_groups()
1544
+	{
1545
+		return EEM_Event::instance()->count_related(
1546
+			$this->event_ID(),
1547
+			'Question_Group',
1548
+			[
1549
+				[
1550
+					'Event_Question_Group.'
1551
+					. EEM_Event_Question_Group::instance()->fieldNameForContext($this->is_primary_registrant()) => true,
1552
+				]
1553
+			]
1554
+		);
1555
+	}
1556
+
1557
+
1558
+	/**
1559
+	 * Returns the registration date in the 'standard' string format
1560
+	 * (function may be improved in the future to allow for different formats and timezones)
1561
+	 *
1562
+	 * @return string
1563
+	 * @throws EE_Error
1564
+	 * @throws InvalidArgumentException
1565
+	 * @throws InvalidDataTypeException
1566
+	 * @throws InvalidInterfaceException
1567
+	 * @throws ReflectionException
1568
+	 */
1569
+	public function reg_date()
1570
+	{
1571
+		return $this->get_datetime('REG_date');
1572
+	}
1573
+
1574
+
1575
+	/**
1576
+	 * Gets the datetime-ticket for this registration (ie, it can be used to isolate
1577
+	 * the ticket this registration purchased, or the datetime they have registered
1578
+	 * to attend)
1579
+	 *
1580
+	 * @return EE_Base_Class|EE_Datetime_Ticket
1581
+	 * @throws EE_Error
1582
+	 * @throws InvalidArgumentException
1583
+	 * @throws InvalidDataTypeException
1584
+	 * @throws InvalidInterfaceException
1585
+	 * @throws ReflectionException
1586
+	 */
1587
+	public function datetime_ticket()
1588
+	{
1589
+		return $this->get_first_related('Datetime_Ticket');
1590
+	}
1591
+
1592
+
1593
+	/**
1594
+	 * Sets the registration's datetime_ticket.
1595
+	 *
1596
+	 * @param EE_Datetime_Ticket $datetime_ticket
1597
+	 * @return EE_Base_Class|EE_Datetime_Ticket
1598
+	 * @throws EE_Error
1599
+	 * @throws InvalidArgumentException
1600
+	 * @throws InvalidDataTypeException
1601
+	 * @throws InvalidInterfaceException
1602
+	 * @throws ReflectionException
1603
+	 */
1604
+	public function set_datetime_ticket($datetime_ticket)
1605
+	{
1606
+		return $this->_add_relation_to($datetime_ticket, 'Datetime_Ticket');
1607
+	}
1608
+
1609
+
1610
+	/**
1611
+	 * Gets deleted
1612
+	 *
1613
+	 * @return bool
1614
+	 * @throws EE_Error
1615
+	 * @throws InvalidArgumentException
1616
+	 * @throws InvalidDataTypeException
1617
+	 * @throws InvalidInterfaceException
1618
+	 * @throws ReflectionException
1619
+	 */
1620
+	public function deleted()
1621
+	{
1622
+		return $this->get('REG_deleted');
1623
+	}
1624
+
1625
+
1626
+	/**
1627
+	 * Sets deleted
1628
+	 *
1629
+	 * @param boolean $deleted
1630
+	 * @return void
1631
+	 * @throws DomainException
1632
+	 * @throws EE_Error
1633
+	 * @throws EntityNotFoundException
1634
+	 * @throws InvalidArgumentException
1635
+	 * @throws InvalidDataTypeException
1636
+	 * @throws InvalidInterfaceException
1637
+	 * @throws ReflectionException
1638
+	 * @throws RuntimeException
1639
+	 * @throws UnexpectedEntityException
1640
+	 */
1641
+	public function set_deleted($deleted)
1642
+	{
1643
+		if ($deleted) {
1644
+			$this->delete(__METHOD__);
1645
+		} else {
1646
+			$this->restore(__METHOD__);
1647
+		}
1648
+	}
1649
+
1650
+
1651
+	/**
1652
+	 * Get the status object of this object
1653
+	 *
1654
+	 * @return EE_Base_Class|EE_Status
1655
+	 * @throws EE_Error
1656
+	 * @throws InvalidArgumentException
1657
+	 * @throws InvalidDataTypeException
1658
+	 * @throws InvalidInterfaceException
1659
+	 * @throws ReflectionException
1660
+	 */
1661
+	public function status_obj()
1662
+	{
1663
+		return $this->get_first_related('Status');
1664
+	}
1665
+
1666
+
1667
+	/**
1668
+	 * Returns the number of times this registration has checked into any of the datetimes
1669
+	 * its available for
1670
+	 *
1671
+	 * @return int
1672
+	 * @throws EE_Error
1673
+	 * @throws InvalidArgumentException
1674
+	 * @throws InvalidDataTypeException
1675
+	 * @throws InvalidInterfaceException
1676
+	 * @throws ReflectionException
1677
+	 */
1678
+	public function count_checkins()
1679
+	{
1680
+		return $this->get_model()->count_related($this, 'Checkin');
1681
+	}
1682
+
1683
+
1684
+	/**
1685
+	 * Returns the number of current Check-ins this registration is checked into for any of the datetimes the
1686
+	 * registration is for.  Note, this is ONLY checked in (does not include checkedout)
1687
+	 *
1688
+	 * @return int
1689
+	 * @throws EE_Error
1690
+	 * @throws InvalidArgumentException
1691
+	 * @throws InvalidDataTypeException
1692
+	 * @throws InvalidInterfaceException
1693
+	 * @throws ReflectionException
1694
+	 */
1695
+	public function count_checkins_not_checkedout()
1696
+	{
1697
+		return $this->get_model()->count_related($this, 'Checkin', array(array('CHK_in' => 1)));
1698
+	}
1699
+
1700
+
1701
+	/**
1702
+	 * The purpose of this method is simply to check whether this registration can checkin to the given datetime.
1703
+	 *
1704
+	 * @param int | EE_Datetime $DTT_OR_ID      The datetime the registration is being checked against
1705
+	 * @param bool              $check_approved This is used to indicate whether the caller wants can_checkin to also
1706
+	 *                                          consider registration status as well as datetime access.
1707
+	 * @return bool
1708
+	 * @throws EE_Error
1709
+	 * @throws InvalidArgumentException
1710
+	 * @throws InvalidDataTypeException
1711
+	 * @throws InvalidInterfaceException
1712
+	 * @throws ReflectionException
1713
+	 */
1714
+	public function can_checkin($DTT_OR_ID, $check_approved = true)
1715
+	{
1716
+		$DTT_ID = EEM_Datetime::instance()->ensure_is_ID($DTT_OR_ID);
1717
+		// first check registration status
1718
+		if (! $DTT_ID || ($check_approved && ! $this->is_approved())) {
1719
+			return false;
1720
+		}
1721
+		// is there a datetime ticket that matches this dtt_ID?
1722
+		if (
1723
+			! (EEM_Datetime_Ticket::instance()->exists(
1724
+				array(
1725
+					array(
1726
+						'TKT_ID' => $this->get('TKT_ID'),
1727
+						'DTT_ID' => $DTT_ID,
1728
+					),
1729
+				)
1730
+			))
1731
+		) {
1732
+			return false;
1733
+		}
1734
+
1735
+		// final check is against TKT_uses
1736
+		return $this->verify_can_checkin_against_TKT_uses($DTT_ID);
1737
+	}
1738
+
1739
+
1740
+	/**
1741
+	 * This method verifies whether the user can checkin for the given datetime considering the max uses value set on
1742
+	 * the ticket. To do this,  a query is done to get the count of the datetime records already checked into.  If the
1743
+	 * datetime given does not have a check-in record and checking in for that datetime will exceed the allowed uses,
1744
+	 * then return false.  Otherwise return true.
1745
+	 *
1746
+	 * @param int | EE_Datetime $DTT_OR_ID The datetime the registration is being checked against
1747
+	 * @return bool true means can checkin.  false means cannot checkin.
1748
+	 * @throws EE_Error
1749
+	 * @throws InvalidArgumentException
1750
+	 * @throws InvalidDataTypeException
1751
+	 * @throws InvalidInterfaceException
1752
+	 * @throws ReflectionException
1753
+	 */
1754
+	public function verify_can_checkin_against_TKT_uses($DTT_OR_ID)
1755
+	{
1756
+		$DTT_ID = EEM_Datetime::instance()->ensure_is_ID($DTT_OR_ID);
1757
+
1758
+		if (! $DTT_ID) {
1759
+			return false;
1760
+		}
1761
+
1762
+		$max_uses = $this->ticket() instanceof EE_Ticket ? $this->ticket()->uses() : EE_INF;
1763
+
1764
+		// if max uses is not set or equals infinity then return true cause its not a factor for whether user can
1765
+		// check-in or not.
1766
+		if (! $max_uses || $max_uses === EE_INF) {
1767
+			return true;
1768
+		}
1769
+
1770
+		// does this datetime have a checkin record?  If so, then the dtt count has already been verified so we can just
1771
+		// go ahead and toggle.
1772
+		if (EEM_Checkin::instance()->exists(array(array('REG_ID' => $this->ID(), 'DTT_ID' => $DTT_ID)))) {
1773
+			return true;
1774
+		}
1775
+
1776
+		// made it here so the last check is whether the number of checkins per unique datetime on this registration
1777
+		// disallows further check-ins.
1778
+		$count_unique_dtt_checkins = EEM_Checkin::instance()->count(
1779
+			array(
1780
+				array(
1781
+					'REG_ID' => $this->ID(),
1782
+					'CHK_in' => true,
1783
+				),
1784
+			),
1785
+			'DTT_ID',
1786
+			true
1787
+		);
1788
+		// checkins have already reached their max number of uses
1789
+		// so registrant can NOT checkin
1790
+		if ($count_unique_dtt_checkins >= $max_uses) {
1791
+			EE_Error::add_error(
1792
+				esc_html__(
1793
+					'Check-in denied because number of datetime uses for the ticket has been reached or exceeded.',
1794
+					'event_espresso'
1795
+				),
1796
+				__FILE__,
1797
+				__FUNCTION__,
1798
+				__LINE__
1799
+			);
1800
+			return false;
1801
+		}
1802
+		return true;
1803
+	}
1804
+
1805
+
1806
+	/**
1807
+	 * toggle Check-in status for this registration
1808
+	 * Check-ins are toggled in the following order:
1809
+	 * never checked in -> checked in
1810
+	 * checked in -> checked out
1811
+	 * checked out -> checked in
1812
+	 *
1813
+	 * @param  int $DTT_ID  include specific datetime to toggle Check-in for.
1814
+	 *                      If not included or null, then it is assumed latest datetime is being toggled.
1815
+	 * @param bool $verify  If true then can_checkin() is used to verify whether the person
1816
+	 *                      can be checked in or not.  Otherwise this forces change in checkin status.
1817
+	 * @return bool|int     the chk_in status toggled to OR false if nothing got changed.
1818
+	 * @throws EE_Error
1819
+	 * @throws InvalidArgumentException
1820
+	 * @throws InvalidDataTypeException
1821
+	 * @throws InvalidInterfaceException
1822
+	 * @throws ReflectionException
1823
+	 */
1824
+	public function toggle_checkin_status($DTT_ID = null, $verify = false)
1825
+	{
1826
+		if (empty($DTT_ID)) {
1827
+			$datetime = $this->get_latest_related_datetime();
1828
+			$DTT_ID = $datetime instanceof EE_Datetime ? $datetime->ID() : 0;
1829
+			// verify the registration can checkin for the given DTT_ID
1830
+		} elseif (! $this->can_checkin($DTT_ID, $verify)) {
1831
+			EE_Error::add_error(
1832
+				sprintf(
1833
+					esc_html__(
1834
+						'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',
1835
+						'event_espresso'
1836
+					),
1837
+					$this->ID(),
1838
+					$DTT_ID
1839
+				),
1840
+				__FILE__,
1841
+				__FUNCTION__,
1842
+				__LINE__
1843
+			);
1844
+			return false;
1845
+		}
1846
+		$status_paths = array(
1847
+			EE_Checkin::status_checked_never => EE_Checkin::status_checked_in,
1848
+			EE_Checkin::status_checked_in    => EE_Checkin::status_checked_out,
1849
+			EE_Checkin::status_checked_out   => EE_Checkin::status_checked_in,
1850
+		);
1851
+		// start by getting the current status so we know what status we'll be changing to.
1852
+		$cur_status = $this->check_in_status_for_datetime($DTT_ID);
1853
+		$status_to = $status_paths[ $cur_status ];
1854
+		// database only records true for checked IN or false for checked OUT
1855
+		// no record ( null ) means checked in NEVER, but we obviously don't save that
1856
+		$new_status = $status_to === EE_Checkin::status_checked_in;
1857
+		// add relation - note Check-ins are always creating new rows
1858
+		// because we are keeping track of Check-ins over time.
1859
+		// Eventually we'll probably want to show a list table
1860
+		// for the individual Check-ins so that they can be managed.
1861
+		$checkin = EE_Checkin::new_instance(
1862
+			array(
1863
+				'REG_ID' => $this->ID(),
1864
+				'DTT_ID' => $DTT_ID,
1865
+				'CHK_in' => $new_status,
1866
+			)
1867
+		);
1868
+		// if the record could not be saved then return false
1869
+		if ($checkin->save() === 0) {
1870
+			if (WP_DEBUG) {
1871
+				global $wpdb;
1872
+				$error = sprintf(
1873
+					esc_html__(
1874
+						'Registration check in update failed because of the following database error: %1$s%2$s',
1875
+						'event_espresso'
1876
+					),
1877
+					'<br />',
1878
+					$wpdb->last_error
1879
+				);
1880
+			} else {
1881
+				$error = esc_html__(
1882
+					'Registration check in update failed because of an unknown database error',
1883
+					'event_espresso'
1884
+				);
1885
+			}
1886
+			EE_Error::add_error($error, __FILE__, __FUNCTION__, __LINE__);
1887
+			return false;
1888
+		}
1889
+		// Fire a checked_in and checkout_out action.
1890
+		$checked_status = $status_to === EE_Checkin::status_checked_in ? 'checked_in' : 'checked_out';
1891
+		do_action("AHEE__EE_Registration__toggle_checkin_status__{$checked_status}", $this, $DTT_ID);
1892
+		return $status_to;
1893
+	}
1894
+
1895
+
1896
+	/**
1897
+	 * Returns the latest datetime related to this registration (via the ticket attached to the registration).
1898
+	 * "Latest" is defined by the `DTT_EVT_start` column.
1899
+	 *
1900
+	 * @return EE_Datetime|null
1901
+	 * @throws EE_Error
1902
+	 * @throws InvalidArgumentException
1903
+	 * @throws InvalidDataTypeException
1904
+	 * @throws InvalidInterfaceException
1905
+	 * @throws ReflectionException
1906
+	 */
1907
+	public function get_latest_related_datetime()
1908
+	{
1909
+		return EEM_Datetime::instance()->get_one(
1910
+			array(
1911
+				array(
1912
+					'Ticket.Registration.REG_ID' => $this->ID(),
1913
+				),
1914
+				'order_by' => array('DTT_EVT_start' => 'DESC'),
1915
+			)
1916
+		);
1917
+	}
1918
+
1919
+
1920
+	/**
1921
+	 * Returns the earliest datetime related to this registration (via the ticket attached to the registration).
1922
+	 * "Earliest" is defined by the `DTT_EVT_start` column.
1923
+	 *
1924
+	 * @return EE_Base_Class|EE_Soft_Delete_Base_Class|NULL
1925
+	 * @throws EE_Error
1926
+	 * @throws InvalidArgumentException
1927
+	 * @throws InvalidDataTypeException
1928
+	 * @throws InvalidInterfaceException
1929
+	 * @throws ReflectionException
1930
+	 */
1931
+	public function get_earliest_related_datetime()
1932
+	{
1933
+		return EEM_Datetime::instance()->get_one(
1934
+			array(
1935
+				array(
1936
+					'Ticket.Registration.REG_ID' => $this->ID(),
1937
+				),
1938
+				'order_by' => array('DTT_EVT_start' => 'ASC'),
1939
+			)
1940
+		);
1941
+	}
1942
+
1943
+
1944
+	/**
1945
+	 * This method simply returns the check-in status for this registration and the given datetime.
1946
+	 * If neither the datetime nor the checkin values are provided as arguments,
1947
+	 * then this will return the LATEST check-in status for the registration across all datetimes it belongs to.
1948
+	 *
1949
+	 * @param  int       $DTT_ID  The ID of the datetime we're checking against
1950
+	 *                            (if empty we'll get the primary datetime for
1951
+	 *                            this registration (via event) and use it's ID);
1952
+	 * @param EE_Checkin $checkin If present, we use the given checkin object rather than the dtt_id.
1953
+	 * @return int                Integer representing Check-in status.
1954
+	 * @throws EE_Error
1955
+	 * @throws InvalidArgumentException
1956
+	 * @throws InvalidDataTypeException
1957
+	 * @throws InvalidInterfaceException
1958
+	 * @throws ReflectionException
1959
+	 */
1960
+	public function check_in_status_for_datetime($DTT_ID = 0, $checkin = null)
1961
+	{
1962
+		$checkin_query_params = array(
1963
+			'order_by' => array('CHK_timestamp' => 'DESC'),
1964
+		);
1965
+
1966
+		if ($DTT_ID > 0) {
1967
+			$checkin_query_params[0] = array('DTT_ID' => $DTT_ID);
1968
+		}
1969
+
1970
+		// get checkin object (if exists)
1971
+		$checkin = $checkin instanceof EE_Checkin
1972
+			? $checkin
1973
+			: $this->get_first_related('Checkin', $checkin_query_params);
1974
+		if ($checkin instanceof EE_Checkin) {
1975
+			if ($checkin->get('CHK_in')) {
1976
+				return EE_Checkin::status_checked_in; // checked in
1977
+			}
1978
+			return EE_Checkin::status_checked_out; // had checked in but is now checked out.
1979
+		}
1980
+		return EE_Checkin::status_checked_never; // never been checked in
1981
+	}
1982
+
1983
+
1984
+	/**
1985
+	 * This method returns a localized message for the toggled Check-in message.
1986
+	 *
1987
+	 * @param  int $DTT_ID include specific datetime to get the correct Check-in message.  If not included or null,
1988
+	 *                     then it is assumed Check-in for primary datetime was toggled.
1989
+	 * @param bool $error  This just flags that you want an error message returned. This is put in so that the error
1990
+	 *                     message can be customized with the attendee name.
1991
+	 * @return string internationalized message
1992
+	 * @throws EE_Error
1993
+	 * @throws InvalidArgumentException
1994
+	 * @throws InvalidDataTypeException
1995
+	 * @throws InvalidInterfaceException
1996
+	 * @throws ReflectionException
1997
+	 */
1998
+	public function get_checkin_msg($DTT_ID, $error = false)
1999
+	{
2000
+		// let's get the attendee first so we can include the name of the attendee
2001
+		$attendee = $this->get_first_related('Attendee');
2002
+		if ($attendee instanceof EE_Attendee) {
2003
+			if ($error) {
2004
+				return sprintf(
2005
+					esc_html__("%s's check-in status was not changed.", "event_espresso"),
2006
+					$attendee->full_name()
2007
+				);
2008
+			}
2009
+			$cur_status = $this->check_in_status_for_datetime($DTT_ID);
2010
+			// what is the status message going to be?
2011
+			switch ($cur_status) {
2012
+				case EE_Checkin::status_checked_never:
2013
+					return sprintf(
2014
+						esc_html__('%s has been removed from Check-in records', 'event_espresso'),
2015
+						$attendee->full_name()
2016
+					);
2017
+					break;
2018
+				case EE_Checkin::status_checked_in:
2019
+					return sprintf(esc_html__('%s has been checked in', 'event_espresso'), $attendee->full_name());
2020
+					break;
2021
+				case EE_Checkin::status_checked_out:
2022
+					return sprintf(esc_html__('%s has been checked out', 'event_espresso'), $attendee->full_name());
2023
+					break;
2024
+			}
2025
+		}
2026
+		return esc_html__('The check-in status could not be determined.', 'event_espresso');
2027
+	}
2028
+
2029
+
2030
+	/**
2031
+	 * Returns the related EE_Transaction to this registration
2032
+	 *
2033
+	 * @return EE_Transaction
2034
+	 * @throws EE_Error
2035
+	 * @throws EntityNotFoundException
2036
+	 * @throws InvalidArgumentException
2037
+	 * @throws InvalidDataTypeException
2038
+	 * @throws InvalidInterfaceException
2039
+	 * @throws ReflectionException
2040
+	 */
2041
+	public function transaction()
2042
+	{
2043
+		$transaction = $this->get_first_related('Transaction');
2044
+		if (! $transaction instanceof \EE_Transaction) {
2045
+			throw new EntityNotFoundException('Transaction ID', $this->transaction_ID());
2046
+		}
2047
+		return $transaction;
2048
+	}
2049
+
2050
+
2051
+	/**
2052
+	 * get Registration Code
2053
+	 *
2054
+	 * @return mixed
2055
+	 * @throws EE_Error
2056
+	 * @throws InvalidArgumentException
2057
+	 * @throws InvalidDataTypeException
2058
+	 * @throws InvalidInterfaceException
2059
+	 * @throws ReflectionException
2060
+	 */
2061
+	public function reg_code()
2062
+	{
2063
+		return $this->get('REG_code');
2064
+	}
2065
+
2066
+
2067
+	/**
2068
+	 * @return mixed
2069
+	 * @throws EE_Error
2070
+	 * @throws InvalidArgumentException
2071
+	 * @throws InvalidDataTypeException
2072
+	 * @throws InvalidInterfaceException
2073
+	 * @throws ReflectionException
2074
+	 */
2075
+	public function transaction_ID()
2076
+	{
2077
+		return $this->get('TXN_ID');
2078
+	}
2079
+
2080
+
2081
+	/**
2082
+	 * @return int
2083
+	 * @throws EE_Error
2084
+	 * @throws InvalidArgumentException
2085
+	 * @throws InvalidDataTypeException
2086
+	 * @throws InvalidInterfaceException
2087
+	 * @throws ReflectionException
2088
+	 */
2089
+	public function ticket_ID()
2090
+	{
2091
+		return $this->get('TKT_ID');
2092
+	}
2093
+
2094
+
2095
+	/**
2096
+	 * Set Registration Code
2097
+	 *
2098
+	 * @param    string  $REG_code Registration Code
2099
+	 * @param    boolean $use_default
2100
+	 * @throws EE_Error
2101
+	 * @throws InvalidArgumentException
2102
+	 * @throws InvalidDataTypeException
2103
+	 * @throws InvalidInterfaceException
2104
+	 * @throws ReflectionException
2105
+	 */
2106
+	public function set_reg_code($REG_code, $use_default = false)
2107
+	{
2108
+		if (empty($REG_code)) {
2109
+			EE_Error::add_error(
2110
+				esc_html__('REG_code can not be empty.', 'event_espresso'),
2111
+				__FILE__,
2112
+				__FUNCTION__,
2113
+				__LINE__
2114
+			);
2115
+			return;
2116
+		}
2117
+		if (! $this->reg_code()) {
2118
+			parent::set('REG_code', $REG_code, $use_default);
2119
+		} else {
2120
+			EE_Error::doing_it_wrong(
2121
+				__CLASS__ . '::' . __FUNCTION__,
2122
+				esc_html__('Can not change a registration REG_code once it has been set.', 'event_espresso'),
2123
+				'4.6.0'
2124
+			);
2125
+		}
2126
+	}
2127
+
2128
+
2129
+	/**
2130
+	 * Returns all other registrations in the same group as this registrant who have the same ticket option.
2131
+	 * Note, if you want to just get all registrations in the same transaction (group), use:
2132
+	 *    $registration->transaction()->registrations();
2133
+	 *
2134
+	 * @since 4.5.0
2135
+	 * @return EE_Registration[] or empty array if this isn't a group registration.
2136
+	 * @throws EE_Error
2137
+	 * @throws InvalidArgumentException
2138
+	 * @throws InvalidDataTypeException
2139
+	 * @throws InvalidInterfaceException
2140
+	 * @throws ReflectionException
2141
+	 */
2142
+	public function get_all_other_registrations_in_group()
2143
+	{
2144
+		if ($this->group_size() < 2) {
2145
+			return array();
2146
+		}
2147
+
2148
+		$query[0] = array(
2149
+			'TXN_ID' => $this->transaction_ID(),
2150
+			'REG_ID' => array('!=', $this->ID()),
2151
+			'TKT_ID' => $this->ticket_ID(),
2152
+		);
2153
+		/** @var EE_Registration[] $registrations */
2154
+		$registrations = $this->get_model()->get_all($query);
2155
+		return $registrations;
2156
+	}
2157
+
2158
+
2159
+	/**
2160
+	 * Return the link to the admin details for the object.
2161
+	 *
2162
+	 * @return string
2163
+	 * @throws EE_Error
2164
+	 * @throws InvalidArgumentException
2165
+	 * @throws InvalidDataTypeException
2166
+	 * @throws InvalidInterfaceException
2167
+	 * @throws ReflectionException
2168
+	 */
2169
+	public function get_admin_details_link()
2170
+	{
2171
+		EE_Registry::instance()->load_helper('URL');
2172
+		return EEH_URL::add_query_args_and_nonce(
2173
+			array(
2174
+				'page'    => 'espresso_registrations',
2175
+				'action'  => 'view_registration',
2176
+				'_REG_ID' => $this->ID(),
2177
+			),
2178
+			admin_url('admin.php')
2179
+		);
2180
+	}
2181
+
2182
+
2183
+	/**
2184
+	 * Returns the link to the editor for the object.  Sometimes this is the same as the details.
2185
+	 *
2186
+	 * @return string
2187
+	 * @throws EE_Error
2188
+	 * @throws InvalidArgumentException
2189
+	 * @throws InvalidDataTypeException
2190
+	 * @throws InvalidInterfaceException
2191
+	 * @throws ReflectionException
2192
+	 */
2193
+	public function get_admin_edit_link()
2194
+	{
2195
+		return $this->get_admin_details_link();
2196
+	}
2197
+
2198
+
2199
+	/**
2200
+	 * Returns the link to a settings page for the object.
2201
+	 *
2202
+	 * @return string
2203
+	 * @throws EE_Error
2204
+	 * @throws InvalidArgumentException
2205
+	 * @throws InvalidDataTypeException
2206
+	 * @throws InvalidInterfaceException
2207
+	 * @throws ReflectionException
2208
+	 */
2209
+	public function get_admin_settings_link()
2210
+	{
2211
+		return $this->get_admin_details_link();
2212
+	}
2213
+
2214
+
2215
+	/**
2216
+	 * Returns the link to the "overview" for the object (typically the "list table" view).
2217
+	 *
2218
+	 * @return string
2219
+	 * @throws EE_Error
2220
+	 * @throws InvalidArgumentException
2221
+	 * @throws InvalidDataTypeException
2222
+	 * @throws InvalidInterfaceException
2223
+	 * @throws ReflectionException
2224
+	 */
2225
+	public function get_admin_overview_link()
2226
+	{
2227
+		EE_Registry::instance()->load_helper('URL');
2228
+		return EEH_URL::add_query_args_and_nonce(
2229
+			array(
2230
+				'page' => 'espresso_registrations',
2231
+			),
2232
+			admin_url('admin.php')
2233
+		);
2234
+	}
2235
+
2236
+
2237
+	/**
2238
+	 * @param array $query_params
2239
+	 * @return EE_Base_Class[]|EE_Registration[]
2240
+	 * @throws EE_Error
2241
+	 * @throws InvalidArgumentException
2242
+	 * @throws InvalidDataTypeException
2243
+	 * @throws InvalidInterfaceException
2244
+	 * @throws ReflectionException
2245
+	 */
2246
+	public function payments($query_params = array())
2247
+	{
2248
+		return $this->get_many_related('Payment', $query_params);
2249
+	}
2250
+
2251
+
2252
+	/**
2253
+	 * @param array $query_params
2254
+	 * @return EE_Base_Class[]|EE_Registration_Payment[]
2255
+	 * @throws EE_Error
2256
+	 * @throws InvalidArgumentException
2257
+	 * @throws InvalidDataTypeException
2258
+	 * @throws InvalidInterfaceException
2259
+	 * @throws ReflectionException
2260
+	 */
2261
+	public function registration_payments($query_params = array())
2262
+	{
2263
+		return $this->get_many_related('Registration_Payment', $query_params);
2264
+	}
2265
+
2266
+
2267
+	/**
2268
+	 * This grabs the payment method corresponding to the last payment made for the amount owing on the registration.
2269
+	 * Note: if there are no payments on the registration there will be no payment method returned.
2270
+	 *
2271
+	 * @return EE_Payment|EE_Payment_Method|null
2272
+	 * @throws EE_Error
2273
+	 * @throws InvalidArgumentException
2274
+	 * @throws InvalidDataTypeException
2275
+	 * @throws InvalidInterfaceException
2276
+	 */
2277
+	public function payment_method()
2278
+	{
2279
+		return EEM_Payment_Method::instance()->get_last_used_for_registration($this);
2280
+	}
2281
+
2282
+
2283
+	/**
2284
+	 * @return \EE_Line_Item
2285
+	 * @throws EE_Error
2286
+	 * @throws EntityNotFoundException
2287
+	 * @throws InvalidArgumentException
2288
+	 * @throws InvalidDataTypeException
2289
+	 * @throws InvalidInterfaceException
2290
+	 * @throws ReflectionException
2291
+	 */
2292
+	public function ticket_line_item()
2293
+	{
2294
+		$ticket = $this->ticket();
2295
+		$transaction = $this->transaction();
2296
+		$line_item = null;
2297
+		$ticket_line_items = \EEH_Line_Item::get_line_items_by_object_type_and_IDs(
2298
+			$transaction->total_line_item(),
2299
+			'Ticket',
2300
+			array($ticket->ID())
2301
+		);
2302
+		foreach ($ticket_line_items as $ticket_line_item) {
2303
+			if (
2304
+				$ticket_line_item instanceof \EE_Line_Item
2305
+				&& $ticket_line_item->OBJ_type() === 'Ticket'
2306
+				&& $ticket_line_item->OBJ_ID() === $ticket->ID()
2307
+			) {
2308
+				$line_item = $ticket_line_item;
2309
+				break;
2310
+			}
2311
+		}
2312
+		if (! ($line_item instanceof \EE_Line_Item && $line_item->OBJ_type() === 'Ticket')) {
2313
+			throw new EntityNotFoundException('Line Item Ticket ID', $ticket->ID());
2314
+		}
2315
+		return $line_item;
2316
+	}
2317
+
2318
+
2319
+	/**
2320
+	 * Soft Deletes this model object.
2321
+	 *
2322
+	 * @param string $source function name that called this method
2323
+	 * @return boolean | int
2324
+	 * @throws DomainException
2325
+	 * @throws EE_Error
2326
+	 * @throws EntityNotFoundException
2327
+	 * @throws InvalidArgumentException
2328
+	 * @throws InvalidDataTypeException
2329
+	 * @throws InvalidInterfaceException
2330
+	 * @throws ReflectionException
2331
+	 * @throws RuntimeException
2332
+	 * @throws UnexpectedEntityException
2333
+	 */
2334
+	public function delete($source = 'unknown')
2335
+	{
2336
+		if ($this->update_extra_meta(EE_Registration::PRE_TRASH_REG_STATUS_KEY, $this->status_ID()) === true) {
2337
+			$current_user = wp_get_current_user();
2338
+			$this->add_extra_meta(
2339
+				EE_Registration::EXTRA_META_KEY_REG_TRASHED,
2340
+				array(
2341
+					'trashed-by' => $current_user->ID ? $current_user->display_name : 'unauthed user',
2342
+					'timestamp'  => time(),
2343
+					'source'     => $source,
2344
+				)
2345
+			);
2346
+			$this->set_status(EEM_Registration::status_id_cancelled);
2347
+		}
2348
+		return parent::delete();
2349
+	}
2350
+
2351
+
2352
+	/**
2353
+	 * Restores whatever the previous status was on a registration before it was trashed (if possible)
2354
+	 *
2355
+	 * @param string $source function name that called this method
2356
+	 * @return bool|int
2357
+	 * @throws DomainException
2358
+	 * @throws EE_Error
2359
+	 * @throws EntityNotFoundException
2360
+	 * @throws InvalidArgumentException
2361
+	 * @throws InvalidDataTypeException
2362
+	 * @throws InvalidInterfaceException
2363
+	 * @throws ReflectionException
2364
+	 * @throws RuntimeException
2365
+	 * @throws UnexpectedEntityException
2366
+	 */
2367
+	public function restore($source = 'unknown')
2368
+	{
2369
+		$previous_status = $this->get_extra_meta(
2370
+			EE_Registration::PRE_TRASH_REG_STATUS_KEY,
2371
+			true,
2372
+			EEM_Registration::status_id_cancelled
2373
+		);
2374
+		if ($previous_status) {
2375
+			$this->delete_extra_meta(EE_Registration::PRE_TRASH_REG_STATUS_KEY);
2376
+			$this->set_status($previous_status);
2377
+		}
2378
+		$current_user = wp_get_current_user();
2379
+		$this->add_extra_meta(
2380
+			EE_Registration::EXTRA_META_KEY_REG_RESTORED,
2381
+			array(
2382
+				'restored-by' => $current_user->ID ? $current_user->display_name : 'unauthed user',
2383
+				'timestamp'   => time(),
2384
+				'source'      => $source,
2385
+			)
2386
+		);
2387
+		return parent::restore();
2388
+	}
2389
+
2390
+
2391
+	/**
2392
+	 * possibly toggle Registration status based on comparison of REG_paid vs REG_final_price
2393
+	 *
2394
+	 * @param  boolean $trigger_set_status_logic EE_Registration::set_status() can trigger additional logic
2395
+	 *                                           depending on whether the reg status changes to or from "Approved"
2396
+	 * @return boolean whether the Registration status was updated
2397
+	 * @throws DomainException
2398
+	 * @throws EE_Error
2399
+	 * @throws EntityNotFoundException
2400
+	 * @throws InvalidArgumentException
2401
+	 * @throws InvalidDataTypeException
2402
+	 * @throws InvalidInterfaceException
2403
+	 * @throws ReflectionException
2404
+	 * @throws RuntimeException
2405
+	 * @throws UnexpectedEntityException
2406
+	 */
2407
+	public function updateStatusBasedOnTotalPaid($trigger_set_status_logic = true)
2408
+	{
2409
+		$paid = $this->paid();
2410
+		$price = $this->final_price();
2411
+		switch (true) {
2412
+			// overpaid or paid
2413
+			case EEH_Money::compare_floats($paid, $price, '>'):
2414
+			case EEH_Money::compare_floats($paid, $price):
2415
+				$new_status = EEM_Registration::status_id_approved;
2416
+				break;
2417
+			//  underpaid
2418
+			case EEH_Money::compare_floats($paid, $price, '<'):
2419
+				$new_status = EEM_Registration::status_id_pending_payment;
2420
+				break;
2421
+			// uhhh Houston...
2422
+			default:
2423
+				throw new RuntimeException(
2424
+					esc_html__('The total paid calculation for this registration is inaccurate.', 'event_espresso')
2425
+				);
2426
+		}
2427
+		if ($new_status !== $this->status_ID()) {
2428
+			if ($trigger_set_status_logic) {
2429
+				return $this->set_status($new_status);
2430
+			}
2431
+			parent::set('STS_ID', $new_status);
2432
+			return true;
2433
+		}
2434
+		return false;
2435
+	}
2436
+
2437
+
2438
+	/*************************** DEPRECATED ***************************/
2439
+
2440
+
2441
+	/**
2442
+	 * @deprecated
2443
+	 * @since     4.7.0
2444
+	 */
2445
+	public function price_paid()
2446
+	{
2447
+		EE_Error::doing_it_wrong(
2448
+			'EE_Registration::price_paid()',
2449
+			esc_html__(
2450
+				'This method is deprecated, please use EE_Registration::final_price() instead.',
2451
+				'event_espresso'
2452
+			),
2453
+			'4.7.0'
2454
+		);
2455
+		return $this->final_price();
2456
+	}
2457
+
2458
+
2459
+	/**
2460
+	 * @deprecated
2461
+	 * @since     4.7.0
2462
+	 * @param    float $REG_final_price
2463
+	 * @throws EE_Error
2464
+	 * @throws EntityNotFoundException
2465
+	 * @throws InvalidArgumentException
2466
+	 * @throws InvalidDataTypeException
2467
+	 * @throws InvalidInterfaceException
2468
+	 * @throws ReflectionException
2469
+	 * @throws RuntimeException
2470
+	 * @throws DomainException
2471
+	 */
2472
+	public function set_price_paid($REG_final_price = 0.00)
2473
+	{
2474
+		EE_Error::doing_it_wrong(
2475
+			'EE_Registration::set_price_paid()',
2476
+			esc_html__(
2477
+				'This method is deprecated, please use EE_Registration::set_final_price() instead.',
2478
+				'event_espresso'
2479
+			),
2480
+			'4.7.0'
2481
+		);
2482
+		$this->set_final_price($REG_final_price);
2483
+	}
2484
+
2485
+
2486
+	/**
2487
+	 * @deprecated
2488
+	 * @since 4.7.0
2489
+	 * @return string
2490
+	 * @throws EE_Error
2491
+	 * @throws InvalidArgumentException
2492
+	 * @throws InvalidDataTypeException
2493
+	 * @throws InvalidInterfaceException
2494
+	 * @throws ReflectionException
2495
+	 */
2496
+	public function pretty_price_paid()
2497
+	{
2498
+		EE_Error::doing_it_wrong(
2499
+			'EE_Registration::pretty_price_paid()',
2500
+			esc_html__(
2501
+				'This method is deprecated, please use EE_Registration::pretty_final_price() instead.',
2502
+				'event_espresso'
2503
+			),
2504
+			'4.7.0'
2505
+		);
2506
+		return $this->pretty_final_price();
2507
+	}
2508
+
2509
+
2510
+	/**
2511
+	 * Gets the primary datetime related to this registration via the related Event to this registration
2512
+	 *
2513
+	 * @deprecated 4.9.17
2514
+	 * @return EE_Datetime
2515
+	 * @throws EE_Error
2516
+	 * @throws EntityNotFoundException
2517
+	 * @throws InvalidArgumentException
2518
+	 * @throws InvalidDataTypeException
2519
+	 * @throws InvalidInterfaceException
2520
+	 * @throws ReflectionException
2521
+	 */
2522
+	public function get_related_primary_datetime()
2523
+	{
2524
+		EE_Error::doing_it_wrong(
2525
+			__METHOD__,
2526
+			esc_html__(
2527
+				'Use EE_Registration::get_latest_related_datetime() or EE_Registration::get_earliest_related_datetime()',
2528
+				'event_espresso'
2529
+			),
2530
+			'4.9.17',
2531
+			'5.0.0'
2532
+		);
2533
+		return $this->event()->primary_datetime();
2534
+	}
2535
+
2536
+	/**
2537
+	 * Returns the contact's name (or "Unknown" if there is no contact.)
2538
+	 * @since 4.10.12.p
2539
+	 * @return string
2540
+	 * @throws EE_Error
2541
+	 * @throws InvalidArgumentException
2542
+	 * @throws InvalidDataTypeException
2543
+	 * @throws InvalidInterfaceException
2544
+	 * @throws ReflectionException
2545
+	 */
2546
+	public function name()
2547
+	{
2548
+		return $this->attendeeName();
2549
+	}
2550 2550
 }
Please login to merge, or discard this patch.
core/db_classes/EE_Transaction.class.php 1 patch
Indentation   +1730 added lines, -1730 removed lines patch added patch discarded remove patch
@@ -13,1734 +13,1734 @@
 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
-     * extra meta key for tracking when transactions are deleted and by who
24
-     *
25
-     * @type string
26
-     */
27
-    const EXTRA_META_KEY_TXN_DELETED = 'transaction-deleted';
28
-
29
-    /**
30
-     * txn status upon initial construction.
31
-     *
32
-     * @var string
33
-     */
34
-    protected $_old_txn_status;
35
-
36
-
37
-    /**
38
-     * @param array  $props_n_values          incoming values
39
-     * @param string $timezone                incoming timezone
40
-     *                                        (if not set the timezone set for the website will be used.)
41
-     * @param array  $date_formats            incoming date_formats in an array where the first value is the
42
-     *                                        date_format and the second value is the time format
43
-     * @return EE_Transaction
44
-     * @throws EE_Error
45
-     * @throws InvalidArgumentException
46
-     * @throws InvalidDataTypeException
47
-     * @throws InvalidInterfaceException
48
-     * @throws ReflectionException
49
-     */
50
-    public static function new_instance($props_n_values = array(), $timezone = null, $date_formats = array())
51
-    {
52
-        $has_object = parent::_check_for_object($props_n_values, __CLASS__, $timezone, $date_formats);
53
-        $txn = $has_object
54
-            ? $has_object
55
-            : new self($props_n_values, false, $timezone, $date_formats);
56
-        if (! $has_object) {
57
-            $txn->set_old_txn_status($txn->status_ID());
58
-        }
59
-        return $txn;
60
-    }
61
-
62
-
63
-    /**
64
-     * @param array  $props_n_values  incoming values from the database
65
-     * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
66
-     *                                the website will be used.
67
-     * @return EE_Transaction
68
-     * @throws EE_Error
69
-     * @throws InvalidArgumentException
70
-     * @throws InvalidDataTypeException
71
-     * @throws InvalidInterfaceException
72
-     * @throws ReflectionException
73
-     */
74
-    public static function new_instance_from_db($props_n_values = array(), $timezone = null)
75
-    {
76
-        $txn = new self($props_n_values, true, $timezone);
77
-        $txn->set_old_txn_status($txn->status_ID());
78
-        return $txn;
79
-    }
80
-
81
-
82
-    /**
83
-     * Sets a meta field indicating that this TXN is locked and should not be updated in the db.
84
-     * If a lock has already been set, then we will attempt to remove it in case it has expired.
85
-     * If that also fails, then an exception is thrown.
86
-     *
87
-     * @throws EE_Error
88
-     * @throws InvalidArgumentException
89
-     * @throws InvalidDataTypeException
90
-     * @throws InvalidInterfaceException
91
-     * @throws ReflectionException
92
-     */
93
-    public function lock()
94
-    {
95
-        // attempt to set lock, but if that fails...
96
-        if (! $this->add_extra_meta('lock', time(), true)) {
97
-            // then attempt to remove the lock in case it is expired
98
-            if ($this->_remove_expired_lock()) {
99
-                // if removal was successful, then try setting lock again
100
-                $this->lock();
101
-            } else {
102
-                // but if the lock can not be removed, then throw an exception
103
-                throw new EE_Error(
104
-                    sprintf(
105
-                        esc_html__(
106
-                            '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.',
107
-                            'event_espresso'
108
-                        ),
109
-                        $this->ID()
110
-                    )
111
-                );
112
-            }
113
-        }
114
-    }
115
-
116
-
117
-    /**
118
-     * removes transaction lock applied in EE_Transaction::lock()
119
-     *
120
-     * @return int
121
-     * @throws EE_Error
122
-     * @throws InvalidArgumentException
123
-     * @throws InvalidDataTypeException
124
-     * @throws InvalidInterfaceException
125
-     * @throws ReflectionException
126
-     */
127
-    public function unlock()
128
-    {
129
-        return $this->delete_extra_meta('lock');
130
-    }
131
-
132
-
133
-    /**
134
-     * Decides whether or not now is the right time to update the transaction.
135
-     * This is useful because we don't always know if it is safe to update the transaction
136
-     * and its related data. why?
137
-     * because it's possible that the transaction is being used in another
138
-     * request and could overwrite anything we save.
139
-     * So we want to only update the txn once we know that won't happen.
140
-     * We also check that the lock isn't expired, and remove it if it is
141
-     *
142
-     * @return boolean
143
-     * @throws EE_Error
144
-     * @throws InvalidArgumentException
145
-     * @throws InvalidDataTypeException
146
-     * @throws InvalidInterfaceException
147
-     * @throws ReflectionException
148
-     */
149
-    public function is_locked()
150
-    {
151
-        // if TXN is not locked, then return false immediately
152
-        if (! $this->_get_lock()) {
153
-            return false;
154
-        }
155
-        // if not, then let's try and remove the lock in case it's expired...
156
-        // _remove_expired_lock() returns 0 when lock is valid (ie: removed = false)
157
-        // and a positive number if the lock was removed (ie: number of locks deleted),
158
-        // so we need to return the opposite
159
-        return ! $this->_remove_expired_lock() ? true : false;
160
-    }
161
-
162
-
163
-    /**
164
-     * Gets the meta field indicating that this TXN is locked
165
-     *
166
-     * @return int
167
-     * @throws EE_Error
168
-     * @throws InvalidArgumentException
169
-     * @throws InvalidDataTypeException
170
-     * @throws InvalidInterfaceException
171
-     * @throws ReflectionException
172
-     */
173
-    protected function _get_lock()
174
-    {
175
-        return (int) $this->get_extra_meta('lock', true, 0);
176
-    }
177
-
178
-
179
-    /**
180
-     * If the lock on this transaction is expired, then we want to remove it so that the transaction can be updated
181
-     *
182
-     * @return int
183
-     * @throws EE_Error
184
-     * @throws InvalidArgumentException
185
-     * @throws InvalidDataTypeException
186
-     * @throws InvalidInterfaceException
187
-     * @throws ReflectionException
188
-     */
189
-    protected function _remove_expired_lock()
190
-    {
191
-        $locked = $this->_get_lock();
192
-        if ($locked && time() - EE_Transaction::LOCK_EXPIRATION > $locked) {
193
-            return $this->unlock();
194
-        }
195
-        return 0;
196
-    }
197
-
198
-
199
-    /**
200
-     * Set transaction total
201
-     *
202
-     * @param float $total total value of transaction
203
-     * @throws EE_Error
204
-     * @throws InvalidArgumentException
205
-     * @throws InvalidDataTypeException
206
-     * @throws InvalidInterfaceException
207
-     * @throws ReflectionException
208
-     */
209
-    public function set_total($total = 0.00)
210
-    {
211
-        $this->set('TXN_total', (float) $total);
212
-    }
213
-
214
-
215
-    /**
216
-     * Set Total Amount Paid to Date
217
-     *
218
-     * @param float $total_paid total amount paid to date (sum of all payments)
219
-     * @throws EE_Error
220
-     * @throws InvalidArgumentException
221
-     * @throws InvalidDataTypeException
222
-     * @throws InvalidInterfaceException
223
-     * @throws ReflectionException
224
-     */
225
-    public function set_paid($total_paid = 0.00)
226
-    {
227
-        $this->set('TXN_paid', (float) $total_paid);
228
-    }
229
-
230
-
231
-    /**
232
-     * Set transaction status
233
-     *
234
-     * @param string $status        whether the transaction is open, declined, accepted,
235
-     *                              or any number of custom values that can be set
236
-     * @throws EE_Error
237
-     * @throws InvalidArgumentException
238
-     * @throws InvalidDataTypeException
239
-     * @throws InvalidInterfaceException
240
-     * @throws ReflectionException
241
-     */
242
-    public function set_status($status = '')
243
-    {
244
-        $this->set('STS_ID', $status);
245
-    }
246
-
247
-
248
-    /**
249
-     * Set hash salt
250
-     *
251
-     * @param string $hash_salt required for some payment gateways
252
-     * @throws EE_Error
253
-     * @throws InvalidArgumentException
254
-     * @throws InvalidDataTypeException
255
-     * @throws InvalidInterfaceException
256
-     * @throws ReflectionException
257
-     */
258
-    public function set_hash_salt($hash_salt = '')
259
-    {
260
-        $this->set('TXN_hash_salt', $hash_salt);
261
-    }
262
-
263
-
264
-    /**
265
-     * Sets TXN_reg_steps array
266
-     *
267
-     * @param array $txn_reg_steps
268
-     * @throws EE_Error
269
-     * @throws InvalidArgumentException
270
-     * @throws InvalidDataTypeException
271
-     * @throws InvalidInterfaceException
272
-     * @throws ReflectionException
273
-     */
274
-    public function set_reg_steps(array $txn_reg_steps)
275
-    {
276
-        $this->set('TXN_reg_steps', $txn_reg_steps);
277
-    }
278
-
279
-
280
-    /**
281
-     * Gets TXN_reg_steps
282
-     *
283
-     * @return array
284
-     * @throws EE_Error
285
-     * @throws InvalidArgumentException
286
-     * @throws InvalidDataTypeException
287
-     * @throws InvalidInterfaceException
288
-     * @throws ReflectionException
289
-     */
290
-    public function reg_steps()
291
-    {
292
-        $TXN_reg_steps = $this->get('TXN_reg_steps');
293
-        return is_array($TXN_reg_steps) ? (array) $TXN_reg_steps : array();
294
-    }
295
-
296
-
297
-    /**
298
-     * @return string of transaction's total cost, with currency symbol and decimal
299
-     * @throws EE_Error
300
-     * @throws InvalidArgumentException
301
-     * @throws InvalidDataTypeException
302
-     * @throws InvalidInterfaceException
303
-     * @throws ReflectionException
304
-     */
305
-    public function pretty_total()
306
-    {
307
-        return $this->get_pretty('TXN_total');
308
-    }
309
-
310
-
311
-    /**
312
-     * Gets the amount paid in a pretty string (formatted and with currency symbol)
313
-     *
314
-     * @return string
315
-     * @throws EE_Error
316
-     * @throws InvalidArgumentException
317
-     * @throws InvalidDataTypeException
318
-     * @throws InvalidInterfaceException
319
-     * @throws ReflectionException
320
-     */
321
-    public function pretty_paid()
322
-    {
323
-        return $this->get_pretty('TXN_paid');
324
-    }
325
-
326
-
327
-    /**
328
-     * calculate the amount remaining for this transaction and return;
329
-     *
330
-     * @return float amount remaining
331
-     * @throws EE_Error
332
-     * @throws InvalidArgumentException
333
-     * @throws InvalidDataTypeException
334
-     * @throws InvalidInterfaceException
335
-     * @throws ReflectionException
336
-     */
337
-    public function remaining()
338
-    {
339
-        return $this->total() - $this->paid();
340
-    }
341
-
342
-
343
-    /**
344
-     * get Transaction Total
345
-     *
346
-     * @return float
347
-     * @throws EE_Error
348
-     * @throws InvalidArgumentException
349
-     * @throws InvalidDataTypeException
350
-     * @throws InvalidInterfaceException
351
-     * @throws ReflectionException
352
-     */
353
-    public function total()
354
-    {
355
-        return (float) $this->get('TXN_total');
356
-    }
357
-
358
-
359
-    /**
360
-     * get Total Amount Paid to Date
361
-     *
362
-     * @return float
363
-     * @throws EE_Error
364
-     * @throws InvalidArgumentException
365
-     * @throws InvalidDataTypeException
366
-     * @throws InvalidInterfaceException
367
-     * @throws ReflectionException
368
-     */
369
-    public function paid()
370
-    {
371
-        return (float) $this->get('TXN_paid');
372
-    }
373
-
374
-
375
-    /**
376
-     * @return mixed|null
377
-     * @throws EE_Error
378
-     * @throws InvalidArgumentException
379
-     * @throws InvalidDataTypeException
380
-     * @throws InvalidInterfaceException
381
-     * @throws ReflectionException
382
-     */
383
-    public function get_cart_session()
384
-    {
385
-        $session_data = (array) $this->get('TXN_session_data');
386
-        return isset($session_data['cart']) && $session_data['cart'] instanceof EE_Cart
387
-            ? $session_data['cart']
388
-            : null;
389
-    }
390
-
391
-
392
-    /**
393
-     * get Transaction session data
394
-     *
395
-     * @return array|mixed
396
-     * @throws EE_Error
397
-     * @throws InvalidArgumentException
398
-     * @throws InvalidDataTypeException
399
-     * @throws InvalidInterfaceException
400
-     * @throws ReflectionException
401
-     */
402
-    public function session_data()
403
-    {
404
-        $session_data = $this->get('TXN_session_data');
405
-        if (empty($session_data)) {
406
-            $session_data = array(
407
-                'id'            => null,
408
-                'user_id'       => null,
409
-                'ip_address'    => null,
410
-                'user_agent'    => null,
411
-                'init_access'   => null,
412
-                'last_access'   => null,
413
-                'pages_visited' => array(),
414
-            );
415
-        }
416
-        return $session_data;
417
-    }
418
-
419
-
420
-    /**
421
-     * Set session data within the TXN object
422
-     *
423
-     * @param EE_Session|array $session_data
424
-     * @throws EE_Error
425
-     * @throws InvalidArgumentException
426
-     * @throws InvalidDataTypeException
427
-     * @throws InvalidInterfaceException
428
-     * @throws ReflectionException
429
-     */
430
-    public function set_txn_session_data($session_data)
431
-    {
432
-        if ($session_data instanceof EE_Session) {
433
-            $this->set('TXN_session_data', $session_data->get_session_data(null, true));
434
-        } else {
435
-            $this->set('TXN_session_data', $session_data);
436
-        }
437
-    }
438
-
439
-
440
-    /**
441
-     * get Transaction hash salt
442
-     *
443
-     * @return mixed
444
-     * @throws EE_Error
445
-     * @throws InvalidArgumentException
446
-     * @throws InvalidDataTypeException
447
-     * @throws InvalidInterfaceException
448
-     * @throws ReflectionException
449
-     */
450
-    public function hash_salt_()
451
-    {
452
-        return $this->get('TXN_hash_salt');
453
-    }
454
-
455
-
456
-    /**
457
-     * Returns the transaction datetime as either:
458
-     *            - unix timestamp format ($format = false, $gmt = true)
459
-     *            - formatted date string including the UTC (timezone) offset ($format = true ($gmt
460
-     *              has no affect with this option)), this also may include a timezone abbreviation if the
461
-     *              set timezone in this class differs from what the timezone is on the blog.
462
-     *            - formatted date string including the UTC (timezone) offset (default).
463
-     *
464
-     * @param boolean $format   - whether to return a unix timestamp (default) or formatted date string
465
-     * @param boolean $gmt      - whether to return a unix timestamp with UTC offset applied (default)
466
-     *                          or no UTC offset applied
467
-     * @return string | int
468
-     * @throws EE_Error
469
-     * @throws InvalidArgumentException
470
-     * @throws InvalidDataTypeException
471
-     * @throws InvalidInterfaceException
472
-     * @throws ReflectionException
473
-     */
474
-    public function datetime($format = false, $gmt = false)
475
-    {
476
-        if ($format) {
477
-            return $this->get_pretty('TXN_timestamp');
478
-        }
479
-        if ($gmt) {
480
-            return $this->get_raw('TXN_timestamp');
481
-        }
482
-        return $this->get('TXN_timestamp');
483
-    }
484
-
485
-
486
-    /**
487
-     * Gets registrations on this transaction
488
-     *
489
-     * @param array   $query_params array of query parameters
490
-     * @param boolean $get_cached   TRUE to retrieve cached registrations or FALSE to pull from the db
491
-     * @return EE_Base_Class[]|EE_Registration[]
492
-     * @throws EE_Error
493
-     * @throws InvalidArgumentException
494
-     * @throws InvalidDataTypeException
495
-     * @throws InvalidInterfaceException
496
-     * @throws ReflectionException
497
-     */
498
-    public function registrations($query_params = array(), $get_cached = false)
499
-    {
500
-        $query_params = (empty($query_params) || ! is_array($query_params))
501
-            ? array(
502
-                'order_by' => array(
503
-                    'Event.EVT_name'     => 'ASC',
504
-                    'Attendee.ATT_lname' => 'ASC',
505
-                    'Attendee.ATT_fname' => 'ASC',
506
-                ),
507
-            )
508
-            : $query_params;
509
-        $query_params = $get_cached ? array() : $query_params;
510
-        return $this->get_many_related('Registration', $query_params);
511
-    }
512
-
513
-
514
-    /**
515
-     * Gets all the attendees for this transaction (handy for use with EE_Attendee's get_registrations_for_event
516
-     * function for getting attendees and how many registrations they each have for an event)
517
-     *
518
-     * @return mixed EE_Attendee[] by default, int if $output is set to 'COUNT'
519
-     * @throws EE_Error
520
-     * @throws InvalidArgumentException
521
-     * @throws InvalidDataTypeException
522
-     * @throws InvalidInterfaceException
523
-     * @throws ReflectionException
524
-     */
525
-    public function attendees()
526
-    {
527
-        return $this->get_many_related('Attendee', array(array('Registration.Transaction.TXN_ID' => $this->ID())));
528
-    }
529
-
530
-
531
-    /**
532
-     * Gets payments for this transaction. Unlike other such functions, order by 'DESC' by default
533
-     *
534
-     * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
535
-     * @return EE_Base_Class[]|EE_Payment[]
536
-     * @throws EE_Error
537
-     * @throws InvalidArgumentException
538
-     * @throws InvalidDataTypeException
539
-     * @throws InvalidInterfaceException
540
-     * @throws ReflectionException
541
-     */
542
-    public function payments($query_params = array())
543
-    {
544
-        return $this->get_many_related('Payment', $query_params);
545
-    }
546
-
547
-
548
-    /**
549
-     * gets only approved payments for this transaction
550
-     *
551
-     * @return EE_Base_Class[]|EE_Payment[]
552
-     * @throws EE_Error
553
-     * @throws InvalidArgumentException
554
-     * @throws ReflectionException
555
-     * @throws InvalidDataTypeException
556
-     * @throws InvalidInterfaceException
557
-     */
558
-    public function approved_payments()
559
-    {
560
-        EE_Registry::instance()->load_model('Payment');
561
-        return $this->get_many_related(
562
-            'Payment',
563
-            array(
564
-                array('STS_ID' => EEM_Payment::status_id_approved),
565
-                'order_by' => array('PAY_timestamp' => 'DESC'),
566
-            )
567
-        );
568
-    }
569
-
570
-
571
-    /**
572
-     * Gets all payments which have not been approved
573
-     *
574
-     * @return EE_Base_Class[]|EEI_Payment[]
575
-     * @throws EE_Error if a model is misconfigured somehow
576
-     * @throws InvalidArgumentException
577
-     * @throws InvalidDataTypeException
578
-     * @throws InvalidInterfaceException
579
-     * @throws ReflectionException
580
-     */
581
-    public function pending_payments()
582
-    {
583
-        return $this->get_many_related(
584
-            'Payment',
585
-            array(
586
-                array(
587
-                    'STS_ID' => EEM_Payment::status_id_pending,
588
-                ),
589
-                'order_by' => array(
590
-                    'PAY_timestamp' => 'DESC',
591
-                ),
592
-            )
593
-        );
594
-    }
595
-
596
-
597
-    /**
598
-     * echoes $this->pretty_status()
599
-     *
600
-     * @param bool $show_icons
601
-     * @throws EE_Error
602
-     * @throws InvalidArgumentException
603
-     * @throws InvalidDataTypeException
604
-     * @throws InvalidInterfaceException
605
-     * @throws ReflectionException
606
-     */
607
-    public function e_pretty_status($show_icons = false)
608
-    {
609
-        echo wp_kses($this->pretty_status($show_icons), AllowedTags::getAllowedTags());
610
-    }
611
-
612
-
613
-    /**
614
-     * returns a pretty version of the status, good for displaying to users
615
-     *
616
-     * @param bool $show_icons
617
-     * @return string
618
-     * @throws EE_Error
619
-     * @throws InvalidArgumentException
620
-     * @throws InvalidDataTypeException
621
-     * @throws InvalidInterfaceException
622
-     * @throws ReflectionException
623
-     */
624
-    public function pretty_status($show_icons = false)
625
-    {
626
-        $status = EEM_Status::instance()->localized_status(
627
-            array($this->status_ID() => esc_html__('unknown', 'event_espresso')),
628
-            false,
629
-            'sentence'
630
-        );
631
-        $icon = '';
632
-        switch ($this->status_ID()) {
633
-            case EEM_Transaction::complete_status_code:
634
-                $icon = $show_icons ? '<span class="dashicons dashicons-yes ee-icon-size-24 green-text"></span>' : '';
635
-                break;
636
-            case EEM_Transaction::incomplete_status_code:
637
-                $icon = $show_icons ? '<span class="dashicons dashicons-marker ee-icon-size-16 lt-blue-text"></span>'
638
-                    : '';
639
-                break;
640
-            case EEM_Transaction::abandoned_status_code:
641
-                $icon = $show_icons ? '<span class="dashicons dashicons-marker ee-icon-size-16 red-text"></span>' : '';
642
-                break;
643
-            case EEM_Transaction::failed_status_code:
644
-                $icon = $show_icons ? '<span class="dashicons dashicons-no ee-icon-size-16 red-text"></span>' : '';
645
-                break;
646
-            case EEM_Transaction::overpaid_status_code:
647
-                $icon = $show_icons ? '<span class="dashicons dashicons-plus ee-icon-size-16 orange-text"></span>' : '';
648
-                break;
649
-        }
650
-        return $icon . $status[ $this->status_ID() ];
651
-    }
652
-
653
-
654
-    /**
655
-     * get Transaction Status
656
-     *
657
-     * @return mixed
658
-     * @throws EE_Error
659
-     * @throws InvalidArgumentException
660
-     * @throws InvalidDataTypeException
661
-     * @throws InvalidInterfaceException
662
-     * @throws ReflectionException
663
-     */
664
-    public function status_ID()
665
-    {
666
-        return $this->get('STS_ID');
667
-    }
668
-
669
-
670
-    /**
671
-     * Returns TRUE or FALSE for whether or not this transaction cost any money
672
-     *
673
-     * @return boolean
674
-     * @throws EE_Error
675
-     * @throws InvalidArgumentException
676
-     * @throws InvalidDataTypeException
677
-     * @throws InvalidInterfaceException
678
-     * @throws ReflectionException
679
-     */
680
-    public function is_free()
681
-    {
682
-        return EEH_Money::compare_floats($this->get('TXN_total'), 0, '==');
683
-    }
684
-
685
-
686
-    /**
687
-     * Returns whether this transaction is complete
688
-     * Useful in templates and other logic for deciding if we should ask for another payment...
689
-     *
690
-     * @return boolean
691
-     * @throws EE_Error
692
-     * @throws InvalidArgumentException
693
-     * @throws InvalidDataTypeException
694
-     * @throws InvalidInterfaceException
695
-     * @throws ReflectionException
696
-     */
697
-    public function is_completed()
698
-    {
699
-        return $this->status_ID() === EEM_Transaction::complete_status_code;
700
-    }
701
-
702
-
703
-    /**
704
-     * Returns whether this transaction is incomplete
705
-     * Useful in templates and other logic for deciding if we should ask for another payment...
706
-     *
707
-     * @return boolean
708
-     * @throws EE_Error
709
-     * @throws InvalidArgumentException
710
-     * @throws InvalidDataTypeException
711
-     * @throws InvalidInterfaceException
712
-     * @throws ReflectionException
713
-     */
714
-    public function is_incomplete()
715
-    {
716
-        return $this->status_ID() === EEM_Transaction::incomplete_status_code;
717
-    }
718
-
719
-
720
-    /**
721
-     * Returns whether this transaction is overpaid
722
-     * Useful in templates and other logic for deciding if monies need to be refunded
723
-     *
724
-     * @return boolean
725
-     * @throws EE_Error
726
-     * @throws InvalidArgumentException
727
-     * @throws InvalidDataTypeException
728
-     * @throws InvalidInterfaceException
729
-     * @throws ReflectionException
730
-     */
731
-    public function is_overpaid()
732
-    {
733
-        return $this->status_ID() === EEM_Transaction::overpaid_status_code;
734
-    }
735
-
736
-
737
-    /**
738
-     * Returns whether this transaction was abandoned
739
-     * meaning that the transaction/registration process was somehow interrupted and never completed
740
-     * but that contact information exists for at least one registrant
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_abandoned()
750
-    {
751
-        return $this->status_ID() === EEM_Transaction::abandoned_status_code;
752
-    }
753
-
754
-
755
-    /**
756
-     * Returns whether this transaction failed
757
-     * meaning that the transaction/registration process was somehow interrupted and never completed
758
-     * and that NO contact information exists for any registrants
759
-     *
760
-     * @return boolean
761
-     * @throws EE_Error
762
-     * @throws InvalidArgumentException
763
-     * @throws InvalidDataTypeException
764
-     * @throws InvalidInterfaceException
765
-     * @throws ReflectionException
766
-     */
767
-    public function failed()
768
-    {
769
-        return $this->status_ID() === EEM_Transaction::failed_status_code;
770
-    }
771
-
772
-
773
-    /**
774
-     * This returns the url for the invoice of this transaction
775
-     *
776
-     * @param string $type 'html' or 'pdf' (default is pdf)
777
-     * @return string
778
-     * @throws DomainException
779
-     * @throws EE_Error
780
-     * @throws InvalidArgumentException
781
-     * @throws InvalidDataTypeException
782
-     * @throws InvalidInterfaceException
783
-     * @throws ReflectionException
784
-     */
785
-    public function invoice_url($type = 'html')
786
-    {
787
-        $REG = $this->primary_registration();
788
-        if (! $REG instanceof EE_Registration) {
789
-            return '';
790
-        }
791
-        return $REG->invoice_url($type);
792
-    }
793
-
794
-
795
-    /**
796
-     * Gets the primary registration only
797
-     *
798
-     * @return EE_Base_Class|EE_Registration
799
-     * @throws EE_Error
800
-     * @throws InvalidArgumentException
801
-     * @throws InvalidDataTypeException
802
-     * @throws InvalidInterfaceException
803
-     * @throws ReflectionException
804
-     */
805
-    public function primary_registration()
806
-    {
807
-        $registrations = (array) $this->get_many_related(
808
-            'Registration',
809
-            array(array('REG_count' => EEM_Registration::PRIMARY_REGISTRANT_COUNT))
810
-        );
811
-        foreach ($registrations as $registration) {
812
-            // valid registration that is NOT cancelled or declined ?
813
-            if (
814
-                $registration instanceof EE_Registration
815
-                && ! in_array($registration->status_ID(), EEM_Registration::closed_reg_statuses(), true)
816
-            ) {
817
-                return $registration;
818
-            }
819
-        }
820
-        // nothing valid found, so just return first thing from array of results
821
-        return reset($registrations);
822
-    }
823
-
824
-
825
-    /**
826
-     * Gets the URL for viewing the receipt
827
-     *
828
-     * @param string $type 'pdf' or 'html' (default is 'html')
829
-     * @return string
830
-     * @throws DomainException
831
-     * @throws EE_Error
832
-     * @throws InvalidArgumentException
833
-     * @throws InvalidDataTypeException
834
-     * @throws InvalidInterfaceException
835
-     * @throws ReflectionException
836
-     */
837
-    public function receipt_url($type = 'html')
838
-    {
839
-        $REG = $this->primary_registration();
840
-        if (! $REG instanceof EE_Registration) {
841
-            return '';
842
-        }
843
-        return $REG->receipt_url($type);
844
-    }
845
-
846
-
847
-    /**
848
-     * Gets the URL of the thank you page with this registration REG_url_link added as
849
-     * a query parameter
850
-     *
851
-     * @return string
852
-     * @throws EE_Error
853
-     * @throws InvalidArgumentException
854
-     * @throws InvalidDataTypeException
855
-     * @throws InvalidInterfaceException
856
-     * @throws ReflectionException
857
-     */
858
-    public function payment_overview_url()
859
-    {
860
-        $primary_registration = $this->primary_registration();
861
-        return $primary_registration instanceof EE_Registration ? $primary_registration->payment_overview_url() : false;
862
-    }
863
-
864
-
865
-    /**
866
-     * @return string
867
-     * @throws EE_Error
868
-     * @throws InvalidArgumentException
869
-     * @throws InvalidDataTypeException
870
-     * @throws InvalidInterfaceException
871
-     * @throws ReflectionException
872
-     */
873
-    public function gateway_response_on_transaction()
874
-    {
875
-        $payment = $this->get_first_related('Payment');
876
-        return $payment instanceof EE_Payment ? $payment->gateway_response() : '';
877
-    }
878
-
879
-
880
-    /**
881
-     * Get the status object of this object
882
-     *
883
-     * @return EE_Base_Class|EE_Status
884
-     * @throws EE_Error
885
-     * @throws InvalidArgumentException
886
-     * @throws InvalidDataTypeException
887
-     * @throws InvalidInterfaceException
888
-     * @throws ReflectionException
889
-     */
890
-    public function status_obj()
891
-    {
892
-        return $this->get_first_related('Status');
893
-    }
894
-
895
-
896
-    /**
897
-     * Gets all the extra meta info on this payment
898
-     *
899
-     * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
900
-     * @return EE_Base_Class[]|EE_Extra_Meta
901
-     * @throws EE_Error
902
-     * @throws InvalidArgumentException
903
-     * @throws InvalidDataTypeException
904
-     * @throws InvalidInterfaceException
905
-     * @throws ReflectionException
906
-     */
907
-    public function extra_meta($query_params = array())
908
-    {
909
-        return $this->get_many_related('Extra_Meta', $query_params);
910
-    }
911
-
912
-
913
-    /**
914
-     * Wrapper for _add_relation_to
915
-     *
916
-     * @param EE_Registration $registration
917
-     * @return EE_Base_Class the relation was added to
918
-     * @throws EE_Error
919
-     * @throws InvalidArgumentException
920
-     * @throws InvalidDataTypeException
921
-     * @throws InvalidInterfaceException
922
-     * @throws ReflectionException
923
-     */
924
-    public function add_registration(EE_Registration $registration)
925
-    {
926
-        return $this->_add_relation_to($registration, 'Registration');
927
-    }
928
-
929
-
930
-    /**
931
-     * Removes the given registration from being related (even before saving this transaction).
932
-     * If an ID/index is provided and this transaction isn't saved yet, removes it from list of cached relations
933
-     *
934
-     * @param int $registration_or_id
935
-     * @return EE_Base_Class that was removed from being related
936
-     * @throws EE_Error
937
-     * @throws InvalidArgumentException
938
-     * @throws InvalidDataTypeException
939
-     * @throws InvalidInterfaceException
940
-     * @throws ReflectionException
941
-     */
942
-    public function remove_registration_with_id($registration_or_id)
943
-    {
944
-        return $this->_remove_relation_to($registration_or_id, 'Registration');
945
-    }
946
-
947
-
948
-    /**
949
-     * Gets all the line items which are for ACTUAL items
950
-     *
951
-     * @return EE_Line_Item[]
952
-     * @throws EE_Error
953
-     * @throws InvalidArgumentException
954
-     * @throws InvalidDataTypeException
955
-     * @throws InvalidInterfaceException
956
-     * @throws ReflectionException
957
-     */
958
-    public function items_purchased()
959
-    {
960
-        return $this->line_items(array(array('LIN_type' => EEM_Line_Item::type_line_item)));
961
-    }
962
-
963
-
964
-    /**
965
-     * Wrapper for _add_relation_to
966
-     *
967
-     * @param EE_Line_Item $line_item
968
-     * @return EE_Base_Class the relation was added to
969
-     * @throws EE_Error
970
-     * @throws InvalidArgumentException
971
-     * @throws InvalidDataTypeException
972
-     * @throws InvalidInterfaceException
973
-     * @throws ReflectionException
974
-     */
975
-    public function add_line_item(EE_Line_Item $line_item)
976
-    {
977
-        return $this->_add_relation_to($line_item, 'Line_Item');
978
-    }
979
-
980
-
981
-    /**
982
-     * Gets ALL the line items related to this transaction (unstructured)
983
-     *
984
-     * @param array $query_params
985
-     * @return EE_Base_Class[]|EE_Line_Item[]
986
-     * @throws EE_Error
987
-     * @throws InvalidArgumentException
988
-     * @throws InvalidDataTypeException
989
-     * @throws InvalidInterfaceException
990
-     * @throws ReflectionException
991
-     */
992
-    public function line_items($query_params = array())
993
-    {
994
-        return $this->get_many_related('Line_Item', $query_params);
995
-    }
996
-
997
-
998
-    /**
999
-     * Gets all the line items which are taxes on the total
1000
-     *
1001
-     * @return EE_Line_Item[]
1002
-     * @throws EE_Error
1003
-     * @throws InvalidArgumentException
1004
-     * @throws InvalidDataTypeException
1005
-     * @throws InvalidInterfaceException
1006
-     * @throws ReflectionException
1007
-     */
1008
-    public function tax_items()
1009
-    {
1010
-        return $this->line_items(array(array('LIN_type' => EEM_Line_Item::type_tax)));
1011
-    }
1012
-
1013
-
1014
-    /**
1015
-     * Gets the total line item (which is a parent of all other related line items,
1016
-     * meaning it takes them all into account on its total)
1017
-     *
1018
-     * @param bool $create_if_not_found
1019
-     * @return \EE_Line_Item
1020
-     * @throws EE_Error
1021
-     * @throws InvalidArgumentException
1022
-     * @throws InvalidDataTypeException
1023
-     * @throws InvalidInterfaceException
1024
-     * @throws ReflectionException
1025
-     */
1026
-    public function total_line_item($create_if_not_found = true)
1027
-    {
1028
-        $item = $this->get_first_related('Line_Item', array(array('LIN_type' => EEM_Line_Item::type_total)));
1029
-        if (! $item && $create_if_not_found) {
1030
-            $item = EEH_Line_Item::create_total_line_item($this);
1031
-        }
1032
-        return $item;
1033
-    }
1034
-
1035
-
1036
-    /**
1037
-     * Returns the total amount of tax on this transaction
1038
-     * (assumes there's only one tax subtotal line item)
1039
-     *
1040
-     * @return float
1041
-     * @throws EE_Error
1042
-     * @throws InvalidArgumentException
1043
-     * @throws InvalidDataTypeException
1044
-     * @throws InvalidInterfaceException
1045
-     * @throws ReflectionException
1046
-     */
1047
-    public function tax_total()
1048
-    {
1049
-        $tax_line_item = $this->tax_total_line_item();
1050
-        if ($tax_line_item) {
1051
-            return (float) $tax_line_item->total();
1052
-        }
1053
-        return (float) 0;
1054
-    }
1055
-
1056
-
1057
-    /**
1058
-     * Gets the tax subtotal line item (assumes there's only one)
1059
-     *
1060
-     * @return EE_Line_Item
1061
-     * @throws EE_Error
1062
-     * @throws InvalidArgumentException
1063
-     * @throws InvalidDataTypeException
1064
-     * @throws InvalidInterfaceException
1065
-     * @throws ReflectionException
1066
-     */
1067
-    public function tax_total_line_item()
1068
-    {
1069
-        return EEH_Line_Item::get_taxes_subtotal($this->total_line_item());
1070
-    }
1071
-
1072
-
1073
-    /**
1074
-     * Gets the array of billing info for the gateway and for this transaction's primary registration's attendee.
1075
-     *
1076
-     * @return EE_Form_Section_Proper
1077
-     * @throws EE_Error
1078
-     * @throws InvalidArgumentException
1079
-     * @throws InvalidDataTypeException
1080
-     * @throws InvalidInterfaceException
1081
-     * @throws ReflectionException
1082
-     */
1083
-    public function billing_info()
1084
-    {
1085
-        $payment_method = $this->payment_method();
1086
-        if (! $payment_method) {
1087
-            EE_Error::add_error(
1088
-                esc_html__(
1089
-                    'Could not find billing info for transaction because no gateway has been used for it yet',
1090
-                    'event_espresso'
1091
-                ),
1092
-                __FILE__,
1093
-                __FUNCTION__,
1094
-                __LINE__
1095
-            );
1096
-            return null;
1097
-        }
1098
-        $primary_reg = $this->primary_registration();
1099
-        if (! $primary_reg) {
1100
-            EE_Error::add_error(
1101
-                esc_html__(
1102
-                    'Cannot get billing info for gateway %s on transaction because no primary registration exists',
1103
-                    'event_espresso'
1104
-                ),
1105
-                __FILE__,
1106
-                __FUNCTION__,
1107
-                __LINE__
1108
-            );
1109
-            return null;
1110
-        }
1111
-        $attendee = $primary_reg->attendee();
1112
-        if (! $attendee) {
1113
-            EE_Error::add_error(
1114
-                esc_html__(
1115
-                    'Cannot get billing info for gateway %s on transaction because the primary registration has no attendee exists',
1116
-                    'event_espresso'
1117
-                ),
1118
-                __FILE__,
1119
-                __FUNCTION__,
1120
-                __LINE__
1121
-            );
1122
-            return null;
1123
-        }
1124
-        return $attendee->billing_info_for_payment_method($payment_method);
1125
-    }
1126
-
1127
-
1128
-    /**
1129
-     * Gets PMD_ID
1130
-     *
1131
-     * @return int
1132
-     * @throws EE_Error
1133
-     * @throws InvalidArgumentException
1134
-     * @throws InvalidDataTypeException
1135
-     * @throws InvalidInterfaceException
1136
-     * @throws ReflectionException
1137
-     */
1138
-    public function payment_method_ID()
1139
-    {
1140
-        return $this->get('PMD_ID');
1141
-    }
1142
-
1143
-
1144
-    /**
1145
-     * Sets PMD_ID
1146
-     *
1147
-     * @param int $PMD_ID
1148
-     * @throws EE_Error
1149
-     * @throws InvalidArgumentException
1150
-     * @throws InvalidDataTypeException
1151
-     * @throws InvalidInterfaceException
1152
-     * @throws ReflectionException
1153
-     */
1154
-    public function set_payment_method_ID($PMD_ID)
1155
-    {
1156
-        $this->set('PMD_ID', $PMD_ID);
1157
-    }
1158
-
1159
-
1160
-    /**
1161
-     * Gets the last-used payment method on this transaction
1162
-     * (we COULD just use the last-made payment, but some payment methods, namely
1163
-     * offline ones, dont' create payments)
1164
-     *
1165
-     * @return EE_Payment_Method
1166
-     * @throws EE_Error
1167
-     * @throws InvalidArgumentException
1168
-     * @throws InvalidDataTypeException
1169
-     * @throws InvalidInterfaceException
1170
-     * @throws ReflectionException
1171
-     */
1172
-    public function payment_method()
1173
-    {
1174
-        $pm = $this->get_first_related('Payment_Method');
1175
-        if ($pm instanceof EE_Payment_Method) {
1176
-            return $pm;
1177
-        }
1178
-        $last_payment = $this->last_payment();
1179
-        if ($last_payment instanceof EE_Payment && $last_payment->payment_method()) {
1180
-            return $last_payment->payment_method();
1181
-        }
1182
-        return null;
1183
-    }
1184
-
1185
-
1186
-    /**
1187
-     * Gets the last payment made
1188
-     *
1189
-     * @return EE_Base_Class|EE_Payment
1190
-     * @throws EE_Error
1191
-     * @throws InvalidArgumentException
1192
-     * @throws InvalidDataTypeException
1193
-     * @throws InvalidInterfaceException
1194
-     * @throws ReflectionException
1195
-     */
1196
-    public function last_payment()
1197
-    {
1198
-        return $this->get_first_related('Payment', array('order_by' => array('PAY_ID' => 'desc')));
1199
-    }
1200
-
1201
-
1202
-    /**
1203
-     * Gets all the line items which are unrelated to tickets on this transaction
1204
-     *
1205
-     * @return EE_Line_Item[]
1206
-     * @throws EE_Error
1207
-     * @throws InvalidArgumentException
1208
-     * @throws InvalidDataTypeException
1209
-     * @throws InvalidInterfaceException
1210
-     * @throws ReflectionException
1211
-     */
1212
-    public function non_ticket_line_items()
1213
-    {
1214
-        return EEM_Line_Item::instance()->get_all_non_ticket_line_items_for_transaction($this->ID());
1215
-    }
1216
-
1217
-
1218
-    /**
1219
-     * possibly toggles TXN status
1220
-     *
1221
-     * @param  boolean $update whether to save the TXN
1222
-     * @return bool whether the TXN was saved
1223
-     * @throws EE_Error
1224
-     * @throws InvalidArgumentException
1225
-     * @throws InvalidDataTypeException
1226
-     * @throws InvalidInterfaceException
1227
-     * @throws ReflectionException
1228
-     * @throws RuntimeException
1229
-     */
1230
-    public function update_status_based_on_total_paid($update = true)
1231
-    {
1232
-        // set transaction status based on comparison of TXN_paid vs TXN_total
1233
-        if (EEH_Money::compare_floats($this->paid(), $this->total(), '>')) {
1234
-            $new_txn_status = EEM_Transaction::overpaid_status_code;
1235
-        } elseif (EEH_Money::compare_floats($this->paid(), $this->total())) {
1236
-            $new_txn_status = EEM_Transaction::complete_status_code;
1237
-        } elseif (EEH_Money::compare_floats($this->paid(), $this->total(), '<')) {
1238
-            $new_txn_status = EEM_Transaction::incomplete_status_code;
1239
-        } else {
1240
-            throw new RuntimeException(
1241
-                esc_html__('The total paid calculation for this transaction is inaccurate.', 'event_espresso')
1242
-            );
1243
-        }
1244
-        if ($new_txn_status !== $this->status_ID()) {
1245
-            $this->set_status($new_txn_status);
1246
-            if ($update) {
1247
-                return $this->save() ? true : false;
1248
-            }
1249
-        }
1250
-        return false;
1251
-    }
1252
-
1253
-
1254
-    /**
1255
-     * Updates the transaction's status and total_paid based on all the payments
1256
-     * that apply to it
1257
-     *
1258
-     * @deprecated
1259
-     * @return array|bool
1260
-     * @throws EE_Error
1261
-     * @throws InvalidArgumentException
1262
-     * @throws ReflectionException
1263
-     * @throws InvalidDataTypeException
1264
-     * @throws InvalidInterfaceException
1265
-     */
1266
-    public function update_based_on_payments()
1267
-    {
1268
-        EE_Error::doing_it_wrong(
1269
-            __CLASS__ . '::' . __FUNCTION__,
1270
-            sprintf(
1271
-                esc_html__('This method is deprecated. Please use "%s" instead', 'event_espresso'),
1272
-                'EE_Transaction_Processor::update_transaction_and_registrations_after_checkout_or_payment()'
1273
-            ),
1274
-            '4.6.0'
1275
-        );
1276
-        /** @type EE_Transaction_Processor $transaction_processor */
1277
-        $transaction_processor = EE_Registry::instance()->load_class('Transaction_Processor');
1278
-        return $transaction_processor->update_transaction_and_registrations_after_checkout_or_payment($this);
1279
-    }
1280
-
1281
-
1282
-    /**
1283
-     * @return string
1284
-     */
1285
-    public function old_txn_status()
1286
-    {
1287
-        return $this->_old_txn_status;
1288
-    }
1289
-
1290
-
1291
-    /**
1292
-     * @param string $old_txn_status
1293
-     */
1294
-    public function set_old_txn_status($old_txn_status)
1295
-    {
1296
-        // only set the first time
1297
-        if ($this->_old_txn_status === null) {
1298
-            $this->_old_txn_status = $old_txn_status;
1299
-        }
1300
-    }
1301
-
1302
-
1303
-    /**
1304
-     * reg_status_updated
1305
-     *
1306
-     * @return bool
1307
-     * @throws EE_Error
1308
-     * @throws InvalidArgumentException
1309
-     * @throws InvalidDataTypeException
1310
-     * @throws InvalidInterfaceException
1311
-     * @throws ReflectionException
1312
-     */
1313
-    public function txn_status_updated()
1314
-    {
1315
-        return $this->status_ID() !== $this->_old_txn_status && $this->_old_txn_status !== null;
1316
-    }
1317
-
1318
-
1319
-    /**
1320
-     * _reg_steps_completed
1321
-     * if $check_all is TRUE, then returns TRUE if ALL reg steps have been marked as completed,
1322
-     * if a $reg_step_slug is provided, then this step will be skipped when testing for completion
1323
-     * if $check_all is FALSE and a $reg_step_slug is provided, then ONLY that reg step will be tested for completion
1324
-     *
1325
-     * @param string $reg_step_slug
1326
-     * @param bool   $check_all
1327
-     * @return bool|int
1328
-     * @throws EE_Error
1329
-     * @throws InvalidArgumentException
1330
-     * @throws InvalidDataTypeException
1331
-     * @throws InvalidInterfaceException
1332
-     * @throws ReflectionException
1333
-     */
1334
-    private function _reg_steps_completed($reg_step_slug = '', $check_all = true)
1335
-    {
1336
-        $reg_steps = $this->reg_steps();
1337
-        if (! is_array($reg_steps) || empty($reg_steps)) {
1338
-            return false;
1339
-        }
1340
-        // loop thru reg steps array)
1341
-        foreach ($reg_steps as $slug => $reg_step_completed) {
1342
-            // if NOT checking ALL steps (only checking one step)
1343
-            if (! $check_all) {
1344
-                // and this is the one
1345
-                if ($slug === $reg_step_slug) {
1346
-                    return $reg_step_completed;
1347
-                }
1348
-                // skip to next reg step in loop
1349
-                continue;
1350
-            }
1351
-            // $check_all must be true, else we would never have gotten to this point
1352
-            if ($slug === $reg_step_slug) {
1353
-                // if we reach this point, then we are testing either:
1354
-                // all_reg_steps_completed_except() or
1355
-                // all_reg_steps_completed_except_final_step(),
1356
-                // and since this is the reg step EXCEPTION being tested
1357
-                // we want to return true (yes true) if this reg step is NOT completed
1358
-                // ie: "is everything completed except the final step?"
1359
-                // "that is correct... the final step is not completed, but all others are."
1360
-                return $reg_step_completed !== true;
1361
-            }
1362
-            if ($reg_step_completed !== true) {
1363
-                // if any reg step is NOT completed, then ALL steps are not completed
1364
-                return false;
1365
-            }
1366
-        }
1367
-        return true;
1368
-    }
1369
-
1370
-
1371
-    /**
1372
-     * all_reg_steps_completed
1373
-     * returns:
1374
-     *    true if ALL reg steps have been marked as completed
1375
-     *        or false if any step is not completed
1376
-     *
1377
-     * @return bool
1378
-     * @throws EE_Error
1379
-     * @throws InvalidArgumentException
1380
-     * @throws InvalidDataTypeException
1381
-     * @throws InvalidInterfaceException
1382
-     * @throws ReflectionException
1383
-     */
1384
-    public function all_reg_steps_completed()
1385
-    {
1386
-        return $this->_reg_steps_completed();
1387
-    }
1388
-
1389
-
1390
-    /**
1391
-     * all_reg_steps_completed_except
1392
-     * returns:
1393
-     *        true if ALL reg steps, except a particular step that you wish to skip over, have been marked as completed
1394
-     *        or false if any other step is not completed
1395
-     *        or false if ALL steps are completed including the exception you are testing !!!
1396
-     *
1397
-     * @param string $exception
1398
-     * @return bool
1399
-     * @throws EE_Error
1400
-     * @throws InvalidArgumentException
1401
-     * @throws InvalidDataTypeException
1402
-     * @throws InvalidInterfaceException
1403
-     * @throws ReflectionException
1404
-     */
1405
-    public function all_reg_steps_completed_except($exception = '')
1406
-    {
1407
-        return $this->_reg_steps_completed($exception);
1408
-    }
1409
-
1410
-
1411
-    /**
1412
-     * all_reg_steps_completed_except
1413
-     * returns:
1414
-     *        true if ALL reg steps, except the final step, have been marked as completed
1415
-     *        or false if any step is not completed
1416
-     *    or false if ALL steps are completed including the final step !!!
1417
-     *
1418
-     * @return bool
1419
-     * @throws EE_Error
1420
-     * @throws InvalidArgumentException
1421
-     * @throws InvalidDataTypeException
1422
-     * @throws InvalidInterfaceException
1423
-     * @throws ReflectionException
1424
-     */
1425
-    public function all_reg_steps_completed_except_final_step()
1426
-    {
1427
-        return $this->_reg_steps_completed('finalize_registration');
1428
-    }
1429
-
1430
-
1431
-    /**
1432
-     * reg_step_completed
1433
-     * returns:
1434
-     *    true if a specific reg step has been marked as completed
1435
-     *    a Unix timestamp if it has been initialized but not yet completed,
1436
-     *    or false if it has not yet been initialized
1437
-     *
1438
-     * @param string $reg_step_slug
1439
-     * @return bool|int
1440
-     * @throws EE_Error
1441
-     * @throws InvalidArgumentException
1442
-     * @throws InvalidDataTypeException
1443
-     * @throws InvalidInterfaceException
1444
-     * @throws ReflectionException
1445
-     */
1446
-    public function reg_step_completed($reg_step_slug)
1447
-    {
1448
-        return $this->_reg_steps_completed($reg_step_slug, false);
1449
-    }
1450
-
1451
-
1452
-    /**
1453
-     * completed_final_reg_step
1454
-     * returns:
1455
-     *    true if the finalize_registration reg step has been marked as completed
1456
-     *    a Unix timestamp if it has been initialized but not yet completed,
1457
-     *    or false if it has not yet been initialized
1458
-     *
1459
-     * @return bool|int
1460
-     * @throws EE_Error
1461
-     * @throws InvalidArgumentException
1462
-     * @throws InvalidDataTypeException
1463
-     * @throws InvalidInterfaceException
1464
-     * @throws ReflectionException
1465
-     */
1466
-    public function final_reg_step_completed()
1467
-    {
1468
-        return $this->_reg_steps_completed('finalize_registration', false);
1469
-    }
1470
-
1471
-
1472
-    /**
1473
-     * set_reg_step_initiated
1474
-     * given a valid TXN_reg_step, this sets it's value to a unix timestamp
1475
-     *
1476
-     * @param string $reg_step_slug
1477
-     * @return boolean
1478
-     * @throws EE_Error
1479
-     * @throws InvalidArgumentException
1480
-     * @throws InvalidDataTypeException
1481
-     * @throws InvalidInterfaceException
1482
-     * @throws ReflectionException
1483
-     */
1484
-    public function set_reg_step_initiated($reg_step_slug)
1485
-    {
1486
-        return $this->_set_reg_step_completed_status($reg_step_slug, time());
1487
-    }
1488
-
1489
-
1490
-    /**
1491
-     * set_reg_step_completed
1492
-     * given a valid TXN_reg_step, this sets the step as completed
1493
-     *
1494
-     * @param string $reg_step_slug
1495
-     * @return boolean
1496
-     * @throws EE_Error
1497
-     * @throws InvalidArgumentException
1498
-     * @throws InvalidDataTypeException
1499
-     * @throws InvalidInterfaceException
1500
-     * @throws ReflectionException
1501
-     */
1502
-    public function set_reg_step_completed($reg_step_slug)
1503
-    {
1504
-        return $this->_set_reg_step_completed_status($reg_step_slug, true);
1505
-    }
1506
-
1507
-
1508
-    /**
1509
-     * set_reg_step_completed
1510
-     * given a valid TXN_reg_step slug, this sets the step as NOT completed
1511
-     *
1512
-     * @param string $reg_step_slug
1513
-     * @return boolean
1514
-     * @throws EE_Error
1515
-     * @throws InvalidArgumentException
1516
-     * @throws InvalidDataTypeException
1517
-     * @throws InvalidInterfaceException
1518
-     * @throws ReflectionException
1519
-     */
1520
-    public function set_reg_step_not_completed($reg_step_slug)
1521
-    {
1522
-        return $this->_set_reg_step_completed_status($reg_step_slug, false);
1523
-    }
1524
-
1525
-
1526
-    /**
1527
-     * set_reg_step_completed
1528
-     * given a valid reg step slug, this sets the TXN_reg_step completed status which is either:
1529
-     *
1530
-     * @param  string      $reg_step_slug
1531
-     * @param  boolean|int $status
1532
-     * @return boolean
1533
-     * @throws EE_Error
1534
-     * @throws InvalidArgumentException
1535
-     * @throws InvalidDataTypeException
1536
-     * @throws InvalidInterfaceException
1537
-     * @throws ReflectionException
1538
-     */
1539
-    private function _set_reg_step_completed_status($reg_step_slug, $status)
1540
-    {
1541
-        // validate status
1542
-        $status = is_bool($status) || is_int($status) ? $status : false;
1543
-        // get reg steps array
1544
-        $txn_reg_steps = $this->reg_steps();
1545
-        // if reg step does NOT exist
1546
-        if (! isset($txn_reg_steps[ $reg_step_slug ])) {
1547
-            return false;
1548
-        }
1549
-        // if  we're trying to complete a step that is already completed
1550
-        if ($txn_reg_steps[ $reg_step_slug ] === true) {
1551
-            return true;
1552
-        }
1553
-        // if  we're trying to complete a step that hasn't even started
1554
-        if ($status === true && $txn_reg_steps[ $reg_step_slug ] === false) {
1555
-            return false;
1556
-        }
1557
-        // if current status value matches the incoming value (no change)
1558
-        // type casting as int means values should collapse to either 0, 1, or a timestamp like 1234567890
1559
-        if ((int) $txn_reg_steps[ $reg_step_slug ] === (int) $status) {
1560
-            // this will happen in cases where multiple AJAX requests occur during the same step
1561
-            return true;
1562
-        }
1563
-        // if we're trying to set a start time, but it has already been set...
1564
-        if (is_numeric($status) && is_numeric($txn_reg_steps[ $reg_step_slug ])) {
1565
-            // skip the update below, but don't return FALSE so that errors won't be displayed
1566
-            return true;
1567
-        }
1568
-        // update completed status
1569
-        $txn_reg_steps[ $reg_step_slug ] = $status;
1570
-        $this->set_reg_steps($txn_reg_steps);
1571
-        $this->save();
1572
-        return true;
1573
-    }
1574
-
1575
-
1576
-    /**
1577
-     * remove_reg_step
1578
-     * given a valid TXN_reg_step slug, this will remove (unset)
1579
-     * the reg step from the TXN reg step array
1580
-     *
1581
-     * @param string $reg_step_slug
1582
-     * @return void
1583
-     * @throws EE_Error
1584
-     * @throws InvalidArgumentException
1585
-     * @throws InvalidDataTypeException
1586
-     * @throws InvalidInterfaceException
1587
-     * @throws ReflectionException
1588
-     */
1589
-    public function remove_reg_step($reg_step_slug)
1590
-    {
1591
-        // get reg steps array
1592
-        $txn_reg_steps = $this->reg_steps();
1593
-        unset($txn_reg_steps[ $reg_step_slug ]);
1594
-        $this->set_reg_steps($txn_reg_steps);
1595
-    }
1596
-
1597
-
1598
-    /**
1599
-     * toggle_failed_transaction_status
1600
-     * upgrades a TXNs status from failed to abandoned,
1601
-     * meaning that contact information has been captured for at least one registrant
1602
-     *
1603
-     * @param bool $save
1604
-     * @return bool
1605
-     * @throws EE_Error
1606
-     * @throws InvalidArgumentException
1607
-     * @throws InvalidDataTypeException
1608
-     * @throws InvalidInterfaceException
1609
-     * @throws ReflectionException
1610
-     */
1611
-    public function toggle_failed_transaction_status($save = true)
1612
-    {
1613
-        // if TXN status is still set as "failed"...
1614
-        if ($this->status_ID() === EEM_Transaction::failed_status_code) {
1615
-            $this->set_status(EEM_Transaction::abandoned_status_code);
1616
-            if ($save) {
1617
-                $this->save();
1618
-            }
1619
-            return true;
1620
-        }
1621
-        return false;
1622
-    }
1623
-
1624
-
1625
-    /**
1626
-     * toggle_abandoned_transaction_status
1627
-     * upgrades a TXNs status from failed or abandoned to incomplete
1628
-     *
1629
-     * @return bool
1630
-     * @throws EE_Error
1631
-     * @throws InvalidArgumentException
1632
-     * @throws InvalidDataTypeException
1633
-     * @throws InvalidInterfaceException
1634
-     * @throws ReflectionException
1635
-     */
1636
-    public function toggle_abandoned_transaction_status()
1637
-    {
1638
-        // if TXN status has not been updated already due to a payment, and is still set as "failed" or "abandoned"...
1639
-        $txn_status = $this->status_ID();
1640
-        if (
1641
-            $txn_status === EEM_Transaction::failed_status_code
1642
-            || $txn_status === EEM_Transaction::abandoned_status_code
1643
-        ) {
1644
-            // if a contact record for the primary registrant has been created
1645
-            if (
1646
-                $this->primary_registration() instanceof EE_Registration
1647
-                && $this->primary_registration()->attendee() instanceof EE_Attendee
1648
-            ) {
1649
-                $this->set_status(EEM_Transaction::incomplete_status_code);
1650
-            } else {
1651
-                // no contact record? yer abandoned!
1652
-                $this->set_status(EEM_Transaction::abandoned_status_code);
1653
-            }
1654
-            return true;
1655
-        }
1656
-        return false;
1657
-    }
1658
-
1659
-
1660
-    /**
1661
-     * checks if an Abandoned TXN has any related payments, and if so,
1662
-     * updates the TXN status based on the amount paid
1663
-     *
1664
-     * @throws EE_Error
1665
-     * @throws InvalidArgumentException
1666
-     * @throws InvalidDataTypeException
1667
-     * @throws InvalidInterfaceException
1668
-     * @throws ReflectionException
1669
-     * @throws RuntimeException
1670
-     * @throws ReflectionException
1671
-     */
1672
-    public function verify_abandoned_transaction_status()
1673
-    {
1674
-        if ($this->status_ID() !== EEM_Transaction::abandoned_status_code) {
1675
-            return;
1676
-        }
1677
-        $payments = $this->get_many_related('Payment');
1678
-        if (! empty($payments)) {
1679
-            foreach ($payments as $payment) {
1680
-                if ($payment instanceof EE_Payment) {
1681
-                    // kk this TXN should NOT be abandoned
1682
-                    $this->update_status_based_on_total_paid();
1683
-                    if (! (defined('DOING_AJAX') && DOING_AJAX) && is_admin()) {
1684
-                        EE_Error::add_attention(
1685
-                            sprintf(
1686
-                                esc_html__(
1687
-                                    '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.',
1688
-                                    'event_espresso'
1689
-                                ),
1690
-                                $this->ID(),
1691
-                                $this->pretty_status()
1692
-                            )
1693
-                        );
1694
-                    }
1695
-                    // get final reg step status
1696
-                    $finalized = $this->final_reg_step_completed();
1697
-                    // if the 'finalize_registration' step has been initiated (has a timestamp)
1698
-                    // but has not yet been fully completed (TRUE)
1699
-                    if (is_int($finalized) && $finalized !== false && $finalized !== true) {
1700
-                        $this->set_reg_step_completed('finalize_registration');
1701
-                        $this->save();
1702
-                    }
1703
-                }
1704
-            }
1705
-        }
1706
-    }
1707
-
1708
-
1709
-    /**
1710
-     * @since 4.10.4.p
1711
-     * @throws EE_Error
1712
-     * @throws InvalidArgumentException
1713
-     * @throws InvalidDataTypeException
1714
-     * @throws InvalidInterfaceException
1715
-     * @throws ReflectionException
1716
-     * @throws RuntimeException
1717
-     */
1718
-    public function recalculateLineItems()
1719
-    {
1720
-        $total_line_item = $this->total_line_item(false);
1721
-        if ($total_line_item instanceof EE_Line_Item) {
1722
-            EEH_Line_Item::resetIsTaxableForTickets($total_line_item);
1723
-            return EEH_Line_Item::apply_taxes($total_line_item, true);
1724
-        }
1725
-        return false;
1726
-	}
1727
-
1728
-
1729
-    /**
1730
-     * @param string $source function name that called this method
1731
-     * @return boolean | int
1732
-     */
1733
-    public function delete($source = 'unknown')
1734
-    {
1735
-        $current_user = wp_get_current_user();
1736
-        $this->add_extra_meta(
1737
-            EE_Transaction::EXTRA_META_KEY_TXN_DELETED,
1738
-            array(
1739
-                'deleted-by' => $current_user->ID ? $current_user->display_name : 'unauthed user',
1740
-                'timestamp'  => time(),
1741
-                'source'     => $source,
1742
-            )
1743
-        );
1744
-        return parent::delete();
1745
-    }
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
+	 * extra meta key for tracking when transactions are deleted and by who
24
+	 *
25
+	 * @type string
26
+	 */
27
+	const EXTRA_META_KEY_TXN_DELETED = 'transaction-deleted';
28
+
29
+	/**
30
+	 * txn status upon initial construction.
31
+	 *
32
+	 * @var string
33
+	 */
34
+	protected $_old_txn_status;
35
+
36
+
37
+	/**
38
+	 * @param array  $props_n_values          incoming values
39
+	 * @param string $timezone                incoming timezone
40
+	 *                                        (if not set the timezone set for the website will be used.)
41
+	 * @param array  $date_formats            incoming date_formats in an array where the first value is the
42
+	 *                                        date_format and the second value is the time format
43
+	 * @return EE_Transaction
44
+	 * @throws EE_Error
45
+	 * @throws InvalidArgumentException
46
+	 * @throws InvalidDataTypeException
47
+	 * @throws InvalidInterfaceException
48
+	 * @throws ReflectionException
49
+	 */
50
+	public static function new_instance($props_n_values = array(), $timezone = null, $date_formats = array())
51
+	{
52
+		$has_object = parent::_check_for_object($props_n_values, __CLASS__, $timezone, $date_formats);
53
+		$txn = $has_object
54
+			? $has_object
55
+			: new self($props_n_values, false, $timezone, $date_formats);
56
+		if (! $has_object) {
57
+			$txn->set_old_txn_status($txn->status_ID());
58
+		}
59
+		return $txn;
60
+	}
61
+
62
+
63
+	/**
64
+	 * @param array  $props_n_values  incoming values from the database
65
+	 * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
66
+	 *                                the website will be used.
67
+	 * @return EE_Transaction
68
+	 * @throws EE_Error
69
+	 * @throws InvalidArgumentException
70
+	 * @throws InvalidDataTypeException
71
+	 * @throws InvalidInterfaceException
72
+	 * @throws ReflectionException
73
+	 */
74
+	public static function new_instance_from_db($props_n_values = array(), $timezone = null)
75
+	{
76
+		$txn = new self($props_n_values, true, $timezone);
77
+		$txn->set_old_txn_status($txn->status_ID());
78
+		return $txn;
79
+	}
80
+
81
+
82
+	/**
83
+	 * Sets a meta field indicating that this TXN is locked and should not be updated in the db.
84
+	 * If a lock has already been set, then we will attempt to remove it in case it has expired.
85
+	 * If that also fails, then an exception is thrown.
86
+	 *
87
+	 * @throws EE_Error
88
+	 * @throws InvalidArgumentException
89
+	 * @throws InvalidDataTypeException
90
+	 * @throws InvalidInterfaceException
91
+	 * @throws ReflectionException
92
+	 */
93
+	public function lock()
94
+	{
95
+		// attempt to set lock, but if that fails...
96
+		if (! $this->add_extra_meta('lock', time(), true)) {
97
+			// then attempt to remove the lock in case it is expired
98
+			if ($this->_remove_expired_lock()) {
99
+				// if removal was successful, then try setting lock again
100
+				$this->lock();
101
+			} else {
102
+				// but if the lock can not be removed, then throw an exception
103
+				throw new EE_Error(
104
+					sprintf(
105
+						esc_html__(
106
+							'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.',
107
+							'event_espresso'
108
+						),
109
+						$this->ID()
110
+					)
111
+				);
112
+			}
113
+		}
114
+	}
115
+
116
+
117
+	/**
118
+	 * removes transaction lock applied in EE_Transaction::lock()
119
+	 *
120
+	 * @return int
121
+	 * @throws EE_Error
122
+	 * @throws InvalidArgumentException
123
+	 * @throws InvalidDataTypeException
124
+	 * @throws InvalidInterfaceException
125
+	 * @throws ReflectionException
126
+	 */
127
+	public function unlock()
128
+	{
129
+		return $this->delete_extra_meta('lock');
130
+	}
131
+
132
+
133
+	/**
134
+	 * Decides whether or not now is the right time to update the transaction.
135
+	 * This is useful because we don't always know if it is safe to update the transaction
136
+	 * and its related data. why?
137
+	 * because it's possible that the transaction is being used in another
138
+	 * request and could overwrite anything we save.
139
+	 * So we want to only update the txn once we know that won't happen.
140
+	 * We also check that the lock isn't expired, and remove it if it is
141
+	 *
142
+	 * @return boolean
143
+	 * @throws EE_Error
144
+	 * @throws InvalidArgumentException
145
+	 * @throws InvalidDataTypeException
146
+	 * @throws InvalidInterfaceException
147
+	 * @throws ReflectionException
148
+	 */
149
+	public function is_locked()
150
+	{
151
+		// if TXN is not locked, then return false immediately
152
+		if (! $this->_get_lock()) {
153
+			return false;
154
+		}
155
+		// if not, then let's try and remove the lock in case it's expired...
156
+		// _remove_expired_lock() returns 0 when lock is valid (ie: removed = false)
157
+		// and a positive number if the lock was removed (ie: number of locks deleted),
158
+		// so we need to return the opposite
159
+		return ! $this->_remove_expired_lock() ? true : false;
160
+	}
161
+
162
+
163
+	/**
164
+	 * Gets the meta field indicating that this TXN is locked
165
+	 *
166
+	 * @return int
167
+	 * @throws EE_Error
168
+	 * @throws InvalidArgumentException
169
+	 * @throws InvalidDataTypeException
170
+	 * @throws InvalidInterfaceException
171
+	 * @throws ReflectionException
172
+	 */
173
+	protected function _get_lock()
174
+	{
175
+		return (int) $this->get_extra_meta('lock', true, 0);
176
+	}
177
+
178
+
179
+	/**
180
+	 * If the lock on this transaction is expired, then we want to remove it so that the transaction can be updated
181
+	 *
182
+	 * @return int
183
+	 * @throws EE_Error
184
+	 * @throws InvalidArgumentException
185
+	 * @throws InvalidDataTypeException
186
+	 * @throws InvalidInterfaceException
187
+	 * @throws ReflectionException
188
+	 */
189
+	protected function _remove_expired_lock()
190
+	{
191
+		$locked = $this->_get_lock();
192
+		if ($locked && time() - EE_Transaction::LOCK_EXPIRATION > $locked) {
193
+			return $this->unlock();
194
+		}
195
+		return 0;
196
+	}
197
+
198
+
199
+	/**
200
+	 * Set transaction total
201
+	 *
202
+	 * @param float $total total value of transaction
203
+	 * @throws EE_Error
204
+	 * @throws InvalidArgumentException
205
+	 * @throws InvalidDataTypeException
206
+	 * @throws InvalidInterfaceException
207
+	 * @throws ReflectionException
208
+	 */
209
+	public function set_total($total = 0.00)
210
+	{
211
+		$this->set('TXN_total', (float) $total);
212
+	}
213
+
214
+
215
+	/**
216
+	 * Set Total Amount Paid to Date
217
+	 *
218
+	 * @param float $total_paid total amount paid to date (sum of all payments)
219
+	 * @throws EE_Error
220
+	 * @throws InvalidArgumentException
221
+	 * @throws InvalidDataTypeException
222
+	 * @throws InvalidInterfaceException
223
+	 * @throws ReflectionException
224
+	 */
225
+	public function set_paid($total_paid = 0.00)
226
+	{
227
+		$this->set('TXN_paid', (float) $total_paid);
228
+	}
229
+
230
+
231
+	/**
232
+	 * Set transaction status
233
+	 *
234
+	 * @param string $status        whether the transaction is open, declined, accepted,
235
+	 *                              or any number of custom values that can be set
236
+	 * @throws EE_Error
237
+	 * @throws InvalidArgumentException
238
+	 * @throws InvalidDataTypeException
239
+	 * @throws InvalidInterfaceException
240
+	 * @throws ReflectionException
241
+	 */
242
+	public function set_status($status = '')
243
+	{
244
+		$this->set('STS_ID', $status);
245
+	}
246
+
247
+
248
+	/**
249
+	 * Set hash salt
250
+	 *
251
+	 * @param string $hash_salt required for some payment gateways
252
+	 * @throws EE_Error
253
+	 * @throws InvalidArgumentException
254
+	 * @throws InvalidDataTypeException
255
+	 * @throws InvalidInterfaceException
256
+	 * @throws ReflectionException
257
+	 */
258
+	public function set_hash_salt($hash_salt = '')
259
+	{
260
+		$this->set('TXN_hash_salt', $hash_salt);
261
+	}
262
+
263
+
264
+	/**
265
+	 * Sets TXN_reg_steps array
266
+	 *
267
+	 * @param array $txn_reg_steps
268
+	 * @throws EE_Error
269
+	 * @throws InvalidArgumentException
270
+	 * @throws InvalidDataTypeException
271
+	 * @throws InvalidInterfaceException
272
+	 * @throws ReflectionException
273
+	 */
274
+	public function set_reg_steps(array $txn_reg_steps)
275
+	{
276
+		$this->set('TXN_reg_steps', $txn_reg_steps);
277
+	}
278
+
279
+
280
+	/**
281
+	 * Gets TXN_reg_steps
282
+	 *
283
+	 * @return array
284
+	 * @throws EE_Error
285
+	 * @throws InvalidArgumentException
286
+	 * @throws InvalidDataTypeException
287
+	 * @throws InvalidInterfaceException
288
+	 * @throws ReflectionException
289
+	 */
290
+	public function reg_steps()
291
+	{
292
+		$TXN_reg_steps = $this->get('TXN_reg_steps');
293
+		return is_array($TXN_reg_steps) ? (array) $TXN_reg_steps : array();
294
+	}
295
+
296
+
297
+	/**
298
+	 * @return string of transaction's total cost, with currency symbol and decimal
299
+	 * @throws EE_Error
300
+	 * @throws InvalidArgumentException
301
+	 * @throws InvalidDataTypeException
302
+	 * @throws InvalidInterfaceException
303
+	 * @throws ReflectionException
304
+	 */
305
+	public function pretty_total()
306
+	{
307
+		return $this->get_pretty('TXN_total');
308
+	}
309
+
310
+
311
+	/**
312
+	 * Gets the amount paid in a pretty string (formatted and with currency symbol)
313
+	 *
314
+	 * @return string
315
+	 * @throws EE_Error
316
+	 * @throws InvalidArgumentException
317
+	 * @throws InvalidDataTypeException
318
+	 * @throws InvalidInterfaceException
319
+	 * @throws ReflectionException
320
+	 */
321
+	public function pretty_paid()
322
+	{
323
+		return $this->get_pretty('TXN_paid');
324
+	}
325
+
326
+
327
+	/**
328
+	 * calculate the amount remaining for this transaction and return;
329
+	 *
330
+	 * @return float amount remaining
331
+	 * @throws EE_Error
332
+	 * @throws InvalidArgumentException
333
+	 * @throws InvalidDataTypeException
334
+	 * @throws InvalidInterfaceException
335
+	 * @throws ReflectionException
336
+	 */
337
+	public function remaining()
338
+	{
339
+		return $this->total() - $this->paid();
340
+	}
341
+
342
+
343
+	/**
344
+	 * get Transaction Total
345
+	 *
346
+	 * @return float
347
+	 * @throws EE_Error
348
+	 * @throws InvalidArgumentException
349
+	 * @throws InvalidDataTypeException
350
+	 * @throws InvalidInterfaceException
351
+	 * @throws ReflectionException
352
+	 */
353
+	public function total()
354
+	{
355
+		return (float) $this->get('TXN_total');
356
+	}
357
+
358
+
359
+	/**
360
+	 * get Total Amount Paid to Date
361
+	 *
362
+	 * @return float
363
+	 * @throws EE_Error
364
+	 * @throws InvalidArgumentException
365
+	 * @throws InvalidDataTypeException
366
+	 * @throws InvalidInterfaceException
367
+	 * @throws ReflectionException
368
+	 */
369
+	public function paid()
370
+	{
371
+		return (float) $this->get('TXN_paid');
372
+	}
373
+
374
+
375
+	/**
376
+	 * @return mixed|null
377
+	 * @throws EE_Error
378
+	 * @throws InvalidArgumentException
379
+	 * @throws InvalidDataTypeException
380
+	 * @throws InvalidInterfaceException
381
+	 * @throws ReflectionException
382
+	 */
383
+	public function get_cart_session()
384
+	{
385
+		$session_data = (array) $this->get('TXN_session_data');
386
+		return isset($session_data['cart']) && $session_data['cart'] instanceof EE_Cart
387
+			? $session_data['cart']
388
+			: null;
389
+	}
390
+
391
+
392
+	/**
393
+	 * get Transaction session data
394
+	 *
395
+	 * @return array|mixed
396
+	 * @throws EE_Error
397
+	 * @throws InvalidArgumentException
398
+	 * @throws InvalidDataTypeException
399
+	 * @throws InvalidInterfaceException
400
+	 * @throws ReflectionException
401
+	 */
402
+	public function session_data()
403
+	{
404
+		$session_data = $this->get('TXN_session_data');
405
+		if (empty($session_data)) {
406
+			$session_data = array(
407
+				'id'            => null,
408
+				'user_id'       => null,
409
+				'ip_address'    => null,
410
+				'user_agent'    => null,
411
+				'init_access'   => null,
412
+				'last_access'   => null,
413
+				'pages_visited' => array(),
414
+			);
415
+		}
416
+		return $session_data;
417
+	}
418
+
419
+
420
+	/**
421
+	 * Set session data within the TXN object
422
+	 *
423
+	 * @param EE_Session|array $session_data
424
+	 * @throws EE_Error
425
+	 * @throws InvalidArgumentException
426
+	 * @throws InvalidDataTypeException
427
+	 * @throws InvalidInterfaceException
428
+	 * @throws ReflectionException
429
+	 */
430
+	public function set_txn_session_data($session_data)
431
+	{
432
+		if ($session_data instanceof EE_Session) {
433
+			$this->set('TXN_session_data', $session_data->get_session_data(null, true));
434
+		} else {
435
+			$this->set('TXN_session_data', $session_data);
436
+		}
437
+	}
438
+
439
+
440
+	/**
441
+	 * get Transaction hash salt
442
+	 *
443
+	 * @return mixed
444
+	 * @throws EE_Error
445
+	 * @throws InvalidArgumentException
446
+	 * @throws InvalidDataTypeException
447
+	 * @throws InvalidInterfaceException
448
+	 * @throws ReflectionException
449
+	 */
450
+	public function hash_salt_()
451
+	{
452
+		return $this->get('TXN_hash_salt');
453
+	}
454
+
455
+
456
+	/**
457
+	 * Returns the transaction datetime as either:
458
+	 *            - unix timestamp format ($format = false, $gmt = true)
459
+	 *            - formatted date string including the UTC (timezone) offset ($format = true ($gmt
460
+	 *              has no affect with this option)), this also may include a timezone abbreviation if the
461
+	 *              set timezone in this class differs from what the timezone is on the blog.
462
+	 *            - formatted date string including the UTC (timezone) offset (default).
463
+	 *
464
+	 * @param boolean $format   - whether to return a unix timestamp (default) or formatted date string
465
+	 * @param boolean $gmt      - whether to return a unix timestamp with UTC offset applied (default)
466
+	 *                          or no UTC offset applied
467
+	 * @return string | int
468
+	 * @throws EE_Error
469
+	 * @throws InvalidArgumentException
470
+	 * @throws InvalidDataTypeException
471
+	 * @throws InvalidInterfaceException
472
+	 * @throws ReflectionException
473
+	 */
474
+	public function datetime($format = false, $gmt = false)
475
+	{
476
+		if ($format) {
477
+			return $this->get_pretty('TXN_timestamp');
478
+		}
479
+		if ($gmt) {
480
+			return $this->get_raw('TXN_timestamp');
481
+		}
482
+		return $this->get('TXN_timestamp');
483
+	}
484
+
485
+
486
+	/**
487
+	 * Gets registrations on this transaction
488
+	 *
489
+	 * @param array   $query_params array of query parameters
490
+	 * @param boolean $get_cached   TRUE to retrieve cached registrations or FALSE to pull from the db
491
+	 * @return EE_Base_Class[]|EE_Registration[]
492
+	 * @throws EE_Error
493
+	 * @throws InvalidArgumentException
494
+	 * @throws InvalidDataTypeException
495
+	 * @throws InvalidInterfaceException
496
+	 * @throws ReflectionException
497
+	 */
498
+	public function registrations($query_params = array(), $get_cached = false)
499
+	{
500
+		$query_params = (empty($query_params) || ! is_array($query_params))
501
+			? array(
502
+				'order_by' => array(
503
+					'Event.EVT_name'     => 'ASC',
504
+					'Attendee.ATT_lname' => 'ASC',
505
+					'Attendee.ATT_fname' => 'ASC',
506
+				),
507
+			)
508
+			: $query_params;
509
+		$query_params = $get_cached ? array() : $query_params;
510
+		return $this->get_many_related('Registration', $query_params);
511
+	}
512
+
513
+
514
+	/**
515
+	 * Gets all the attendees for this transaction (handy for use with EE_Attendee's get_registrations_for_event
516
+	 * function for getting attendees and how many registrations they each have for an event)
517
+	 *
518
+	 * @return mixed EE_Attendee[] by default, int if $output is set to 'COUNT'
519
+	 * @throws EE_Error
520
+	 * @throws InvalidArgumentException
521
+	 * @throws InvalidDataTypeException
522
+	 * @throws InvalidInterfaceException
523
+	 * @throws ReflectionException
524
+	 */
525
+	public function attendees()
526
+	{
527
+		return $this->get_many_related('Attendee', array(array('Registration.Transaction.TXN_ID' => $this->ID())));
528
+	}
529
+
530
+
531
+	/**
532
+	 * Gets payments for this transaction. Unlike other such functions, order by 'DESC' by default
533
+	 *
534
+	 * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
535
+	 * @return EE_Base_Class[]|EE_Payment[]
536
+	 * @throws EE_Error
537
+	 * @throws InvalidArgumentException
538
+	 * @throws InvalidDataTypeException
539
+	 * @throws InvalidInterfaceException
540
+	 * @throws ReflectionException
541
+	 */
542
+	public function payments($query_params = array())
543
+	{
544
+		return $this->get_many_related('Payment', $query_params);
545
+	}
546
+
547
+
548
+	/**
549
+	 * gets only approved payments for this transaction
550
+	 *
551
+	 * @return EE_Base_Class[]|EE_Payment[]
552
+	 * @throws EE_Error
553
+	 * @throws InvalidArgumentException
554
+	 * @throws ReflectionException
555
+	 * @throws InvalidDataTypeException
556
+	 * @throws InvalidInterfaceException
557
+	 */
558
+	public function approved_payments()
559
+	{
560
+		EE_Registry::instance()->load_model('Payment');
561
+		return $this->get_many_related(
562
+			'Payment',
563
+			array(
564
+				array('STS_ID' => EEM_Payment::status_id_approved),
565
+				'order_by' => array('PAY_timestamp' => 'DESC'),
566
+			)
567
+		);
568
+	}
569
+
570
+
571
+	/**
572
+	 * Gets all payments which have not been approved
573
+	 *
574
+	 * @return EE_Base_Class[]|EEI_Payment[]
575
+	 * @throws EE_Error if a model is misconfigured somehow
576
+	 * @throws InvalidArgumentException
577
+	 * @throws InvalidDataTypeException
578
+	 * @throws InvalidInterfaceException
579
+	 * @throws ReflectionException
580
+	 */
581
+	public function pending_payments()
582
+	{
583
+		return $this->get_many_related(
584
+			'Payment',
585
+			array(
586
+				array(
587
+					'STS_ID' => EEM_Payment::status_id_pending,
588
+				),
589
+				'order_by' => array(
590
+					'PAY_timestamp' => 'DESC',
591
+				),
592
+			)
593
+		);
594
+	}
595
+
596
+
597
+	/**
598
+	 * echoes $this->pretty_status()
599
+	 *
600
+	 * @param bool $show_icons
601
+	 * @throws EE_Error
602
+	 * @throws InvalidArgumentException
603
+	 * @throws InvalidDataTypeException
604
+	 * @throws InvalidInterfaceException
605
+	 * @throws ReflectionException
606
+	 */
607
+	public function e_pretty_status($show_icons = false)
608
+	{
609
+		echo wp_kses($this->pretty_status($show_icons), AllowedTags::getAllowedTags());
610
+	}
611
+
612
+
613
+	/**
614
+	 * returns a pretty version of the status, good for displaying to users
615
+	 *
616
+	 * @param bool $show_icons
617
+	 * @return string
618
+	 * @throws EE_Error
619
+	 * @throws InvalidArgumentException
620
+	 * @throws InvalidDataTypeException
621
+	 * @throws InvalidInterfaceException
622
+	 * @throws ReflectionException
623
+	 */
624
+	public function pretty_status($show_icons = false)
625
+	{
626
+		$status = EEM_Status::instance()->localized_status(
627
+			array($this->status_ID() => esc_html__('unknown', 'event_espresso')),
628
+			false,
629
+			'sentence'
630
+		);
631
+		$icon = '';
632
+		switch ($this->status_ID()) {
633
+			case EEM_Transaction::complete_status_code:
634
+				$icon = $show_icons ? '<span class="dashicons dashicons-yes ee-icon-size-24 green-text"></span>' : '';
635
+				break;
636
+			case EEM_Transaction::incomplete_status_code:
637
+				$icon = $show_icons ? '<span class="dashicons dashicons-marker ee-icon-size-16 lt-blue-text"></span>'
638
+					: '';
639
+				break;
640
+			case EEM_Transaction::abandoned_status_code:
641
+				$icon = $show_icons ? '<span class="dashicons dashicons-marker ee-icon-size-16 red-text"></span>' : '';
642
+				break;
643
+			case EEM_Transaction::failed_status_code:
644
+				$icon = $show_icons ? '<span class="dashicons dashicons-no ee-icon-size-16 red-text"></span>' : '';
645
+				break;
646
+			case EEM_Transaction::overpaid_status_code:
647
+				$icon = $show_icons ? '<span class="dashicons dashicons-plus ee-icon-size-16 orange-text"></span>' : '';
648
+				break;
649
+		}
650
+		return $icon . $status[ $this->status_ID() ];
651
+	}
652
+
653
+
654
+	/**
655
+	 * get Transaction Status
656
+	 *
657
+	 * @return mixed
658
+	 * @throws EE_Error
659
+	 * @throws InvalidArgumentException
660
+	 * @throws InvalidDataTypeException
661
+	 * @throws InvalidInterfaceException
662
+	 * @throws ReflectionException
663
+	 */
664
+	public function status_ID()
665
+	{
666
+		return $this->get('STS_ID');
667
+	}
668
+
669
+
670
+	/**
671
+	 * Returns TRUE or FALSE for whether or not this transaction cost any money
672
+	 *
673
+	 * @return boolean
674
+	 * @throws EE_Error
675
+	 * @throws InvalidArgumentException
676
+	 * @throws InvalidDataTypeException
677
+	 * @throws InvalidInterfaceException
678
+	 * @throws ReflectionException
679
+	 */
680
+	public function is_free()
681
+	{
682
+		return EEH_Money::compare_floats($this->get('TXN_total'), 0, '==');
683
+	}
684
+
685
+
686
+	/**
687
+	 * Returns whether this transaction is complete
688
+	 * Useful in templates and other logic for deciding if we should ask for another payment...
689
+	 *
690
+	 * @return boolean
691
+	 * @throws EE_Error
692
+	 * @throws InvalidArgumentException
693
+	 * @throws InvalidDataTypeException
694
+	 * @throws InvalidInterfaceException
695
+	 * @throws ReflectionException
696
+	 */
697
+	public function is_completed()
698
+	{
699
+		return $this->status_ID() === EEM_Transaction::complete_status_code;
700
+	}
701
+
702
+
703
+	/**
704
+	 * Returns whether this transaction is incomplete
705
+	 * Useful in templates and other logic for deciding if we should ask for another payment...
706
+	 *
707
+	 * @return boolean
708
+	 * @throws EE_Error
709
+	 * @throws InvalidArgumentException
710
+	 * @throws InvalidDataTypeException
711
+	 * @throws InvalidInterfaceException
712
+	 * @throws ReflectionException
713
+	 */
714
+	public function is_incomplete()
715
+	{
716
+		return $this->status_ID() === EEM_Transaction::incomplete_status_code;
717
+	}
718
+
719
+
720
+	/**
721
+	 * Returns whether this transaction is overpaid
722
+	 * Useful in templates and other logic for deciding if monies need to be refunded
723
+	 *
724
+	 * @return boolean
725
+	 * @throws EE_Error
726
+	 * @throws InvalidArgumentException
727
+	 * @throws InvalidDataTypeException
728
+	 * @throws InvalidInterfaceException
729
+	 * @throws ReflectionException
730
+	 */
731
+	public function is_overpaid()
732
+	{
733
+		return $this->status_ID() === EEM_Transaction::overpaid_status_code;
734
+	}
735
+
736
+
737
+	/**
738
+	 * Returns whether this transaction was abandoned
739
+	 * meaning that the transaction/registration process was somehow interrupted and never completed
740
+	 * but that contact information exists for at least one registrant
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_abandoned()
750
+	{
751
+		return $this->status_ID() === EEM_Transaction::abandoned_status_code;
752
+	}
753
+
754
+
755
+	/**
756
+	 * Returns whether this transaction failed
757
+	 * meaning that the transaction/registration process was somehow interrupted and never completed
758
+	 * and that NO contact information exists for any registrants
759
+	 *
760
+	 * @return boolean
761
+	 * @throws EE_Error
762
+	 * @throws InvalidArgumentException
763
+	 * @throws InvalidDataTypeException
764
+	 * @throws InvalidInterfaceException
765
+	 * @throws ReflectionException
766
+	 */
767
+	public function failed()
768
+	{
769
+		return $this->status_ID() === EEM_Transaction::failed_status_code;
770
+	}
771
+
772
+
773
+	/**
774
+	 * This returns the url for the invoice of this transaction
775
+	 *
776
+	 * @param string $type 'html' or 'pdf' (default is pdf)
777
+	 * @return string
778
+	 * @throws DomainException
779
+	 * @throws EE_Error
780
+	 * @throws InvalidArgumentException
781
+	 * @throws InvalidDataTypeException
782
+	 * @throws InvalidInterfaceException
783
+	 * @throws ReflectionException
784
+	 */
785
+	public function invoice_url($type = 'html')
786
+	{
787
+		$REG = $this->primary_registration();
788
+		if (! $REG instanceof EE_Registration) {
789
+			return '';
790
+		}
791
+		return $REG->invoice_url($type);
792
+	}
793
+
794
+
795
+	/**
796
+	 * Gets the primary registration only
797
+	 *
798
+	 * @return EE_Base_Class|EE_Registration
799
+	 * @throws EE_Error
800
+	 * @throws InvalidArgumentException
801
+	 * @throws InvalidDataTypeException
802
+	 * @throws InvalidInterfaceException
803
+	 * @throws ReflectionException
804
+	 */
805
+	public function primary_registration()
806
+	{
807
+		$registrations = (array) $this->get_many_related(
808
+			'Registration',
809
+			array(array('REG_count' => EEM_Registration::PRIMARY_REGISTRANT_COUNT))
810
+		);
811
+		foreach ($registrations as $registration) {
812
+			// valid registration that is NOT cancelled or declined ?
813
+			if (
814
+				$registration instanceof EE_Registration
815
+				&& ! in_array($registration->status_ID(), EEM_Registration::closed_reg_statuses(), true)
816
+			) {
817
+				return $registration;
818
+			}
819
+		}
820
+		// nothing valid found, so just return first thing from array of results
821
+		return reset($registrations);
822
+	}
823
+
824
+
825
+	/**
826
+	 * Gets the URL for viewing the receipt
827
+	 *
828
+	 * @param string $type 'pdf' or 'html' (default is 'html')
829
+	 * @return string
830
+	 * @throws DomainException
831
+	 * @throws EE_Error
832
+	 * @throws InvalidArgumentException
833
+	 * @throws InvalidDataTypeException
834
+	 * @throws InvalidInterfaceException
835
+	 * @throws ReflectionException
836
+	 */
837
+	public function receipt_url($type = 'html')
838
+	{
839
+		$REG = $this->primary_registration();
840
+		if (! $REG instanceof EE_Registration) {
841
+			return '';
842
+		}
843
+		return $REG->receipt_url($type);
844
+	}
845
+
846
+
847
+	/**
848
+	 * Gets the URL of the thank you page with this registration REG_url_link added as
849
+	 * a query parameter
850
+	 *
851
+	 * @return string
852
+	 * @throws EE_Error
853
+	 * @throws InvalidArgumentException
854
+	 * @throws InvalidDataTypeException
855
+	 * @throws InvalidInterfaceException
856
+	 * @throws ReflectionException
857
+	 */
858
+	public function payment_overview_url()
859
+	{
860
+		$primary_registration = $this->primary_registration();
861
+		return $primary_registration instanceof EE_Registration ? $primary_registration->payment_overview_url() : false;
862
+	}
863
+
864
+
865
+	/**
866
+	 * @return string
867
+	 * @throws EE_Error
868
+	 * @throws InvalidArgumentException
869
+	 * @throws InvalidDataTypeException
870
+	 * @throws InvalidInterfaceException
871
+	 * @throws ReflectionException
872
+	 */
873
+	public function gateway_response_on_transaction()
874
+	{
875
+		$payment = $this->get_first_related('Payment');
876
+		return $payment instanceof EE_Payment ? $payment->gateway_response() : '';
877
+	}
878
+
879
+
880
+	/**
881
+	 * Get the status object of this object
882
+	 *
883
+	 * @return EE_Base_Class|EE_Status
884
+	 * @throws EE_Error
885
+	 * @throws InvalidArgumentException
886
+	 * @throws InvalidDataTypeException
887
+	 * @throws InvalidInterfaceException
888
+	 * @throws ReflectionException
889
+	 */
890
+	public function status_obj()
891
+	{
892
+		return $this->get_first_related('Status');
893
+	}
894
+
895
+
896
+	/**
897
+	 * Gets all the extra meta info on this payment
898
+	 *
899
+	 * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
900
+	 * @return EE_Base_Class[]|EE_Extra_Meta
901
+	 * @throws EE_Error
902
+	 * @throws InvalidArgumentException
903
+	 * @throws InvalidDataTypeException
904
+	 * @throws InvalidInterfaceException
905
+	 * @throws ReflectionException
906
+	 */
907
+	public function extra_meta($query_params = array())
908
+	{
909
+		return $this->get_many_related('Extra_Meta', $query_params);
910
+	}
911
+
912
+
913
+	/**
914
+	 * Wrapper for _add_relation_to
915
+	 *
916
+	 * @param EE_Registration $registration
917
+	 * @return EE_Base_Class the relation was added to
918
+	 * @throws EE_Error
919
+	 * @throws InvalidArgumentException
920
+	 * @throws InvalidDataTypeException
921
+	 * @throws InvalidInterfaceException
922
+	 * @throws ReflectionException
923
+	 */
924
+	public function add_registration(EE_Registration $registration)
925
+	{
926
+		return $this->_add_relation_to($registration, 'Registration');
927
+	}
928
+
929
+
930
+	/**
931
+	 * Removes the given registration from being related (even before saving this transaction).
932
+	 * If an ID/index is provided and this transaction isn't saved yet, removes it from list of cached relations
933
+	 *
934
+	 * @param int $registration_or_id
935
+	 * @return EE_Base_Class that was removed from being related
936
+	 * @throws EE_Error
937
+	 * @throws InvalidArgumentException
938
+	 * @throws InvalidDataTypeException
939
+	 * @throws InvalidInterfaceException
940
+	 * @throws ReflectionException
941
+	 */
942
+	public function remove_registration_with_id($registration_or_id)
943
+	{
944
+		return $this->_remove_relation_to($registration_or_id, 'Registration');
945
+	}
946
+
947
+
948
+	/**
949
+	 * Gets all the line items which are for ACTUAL items
950
+	 *
951
+	 * @return EE_Line_Item[]
952
+	 * @throws EE_Error
953
+	 * @throws InvalidArgumentException
954
+	 * @throws InvalidDataTypeException
955
+	 * @throws InvalidInterfaceException
956
+	 * @throws ReflectionException
957
+	 */
958
+	public function items_purchased()
959
+	{
960
+		return $this->line_items(array(array('LIN_type' => EEM_Line_Item::type_line_item)));
961
+	}
962
+
963
+
964
+	/**
965
+	 * Wrapper for _add_relation_to
966
+	 *
967
+	 * @param EE_Line_Item $line_item
968
+	 * @return EE_Base_Class the relation was added to
969
+	 * @throws EE_Error
970
+	 * @throws InvalidArgumentException
971
+	 * @throws InvalidDataTypeException
972
+	 * @throws InvalidInterfaceException
973
+	 * @throws ReflectionException
974
+	 */
975
+	public function add_line_item(EE_Line_Item $line_item)
976
+	{
977
+		return $this->_add_relation_to($line_item, 'Line_Item');
978
+	}
979
+
980
+
981
+	/**
982
+	 * Gets ALL the line items related to this transaction (unstructured)
983
+	 *
984
+	 * @param array $query_params
985
+	 * @return EE_Base_Class[]|EE_Line_Item[]
986
+	 * @throws EE_Error
987
+	 * @throws InvalidArgumentException
988
+	 * @throws InvalidDataTypeException
989
+	 * @throws InvalidInterfaceException
990
+	 * @throws ReflectionException
991
+	 */
992
+	public function line_items($query_params = array())
993
+	{
994
+		return $this->get_many_related('Line_Item', $query_params);
995
+	}
996
+
997
+
998
+	/**
999
+	 * Gets all the line items which are taxes on the total
1000
+	 *
1001
+	 * @return EE_Line_Item[]
1002
+	 * @throws EE_Error
1003
+	 * @throws InvalidArgumentException
1004
+	 * @throws InvalidDataTypeException
1005
+	 * @throws InvalidInterfaceException
1006
+	 * @throws ReflectionException
1007
+	 */
1008
+	public function tax_items()
1009
+	{
1010
+		return $this->line_items(array(array('LIN_type' => EEM_Line_Item::type_tax)));
1011
+	}
1012
+
1013
+
1014
+	/**
1015
+	 * Gets the total line item (which is a parent of all other related line items,
1016
+	 * meaning it takes them all into account on its total)
1017
+	 *
1018
+	 * @param bool $create_if_not_found
1019
+	 * @return \EE_Line_Item
1020
+	 * @throws EE_Error
1021
+	 * @throws InvalidArgumentException
1022
+	 * @throws InvalidDataTypeException
1023
+	 * @throws InvalidInterfaceException
1024
+	 * @throws ReflectionException
1025
+	 */
1026
+	public function total_line_item($create_if_not_found = true)
1027
+	{
1028
+		$item = $this->get_first_related('Line_Item', array(array('LIN_type' => EEM_Line_Item::type_total)));
1029
+		if (! $item && $create_if_not_found) {
1030
+			$item = EEH_Line_Item::create_total_line_item($this);
1031
+		}
1032
+		return $item;
1033
+	}
1034
+
1035
+
1036
+	/**
1037
+	 * Returns the total amount of tax on this transaction
1038
+	 * (assumes there's only one tax subtotal line item)
1039
+	 *
1040
+	 * @return float
1041
+	 * @throws EE_Error
1042
+	 * @throws InvalidArgumentException
1043
+	 * @throws InvalidDataTypeException
1044
+	 * @throws InvalidInterfaceException
1045
+	 * @throws ReflectionException
1046
+	 */
1047
+	public function tax_total()
1048
+	{
1049
+		$tax_line_item = $this->tax_total_line_item();
1050
+		if ($tax_line_item) {
1051
+			return (float) $tax_line_item->total();
1052
+		}
1053
+		return (float) 0;
1054
+	}
1055
+
1056
+
1057
+	/**
1058
+	 * Gets the tax subtotal line item (assumes there's only one)
1059
+	 *
1060
+	 * @return EE_Line_Item
1061
+	 * @throws EE_Error
1062
+	 * @throws InvalidArgumentException
1063
+	 * @throws InvalidDataTypeException
1064
+	 * @throws InvalidInterfaceException
1065
+	 * @throws ReflectionException
1066
+	 */
1067
+	public function tax_total_line_item()
1068
+	{
1069
+		return EEH_Line_Item::get_taxes_subtotal($this->total_line_item());
1070
+	}
1071
+
1072
+
1073
+	/**
1074
+	 * Gets the array of billing info for the gateway and for this transaction's primary registration's attendee.
1075
+	 *
1076
+	 * @return EE_Form_Section_Proper
1077
+	 * @throws EE_Error
1078
+	 * @throws InvalidArgumentException
1079
+	 * @throws InvalidDataTypeException
1080
+	 * @throws InvalidInterfaceException
1081
+	 * @throws ReflectionException
1082
+	 */
1083
+	public function billing_info()
1084
+	{
1085
+		$payment_method = $this->payment_method();
1086
+		if (! $payment_method) {
1087
+			EE_Error::add_error(
1088
+				esc_html__(
1089
+					'Could not find billing info for transaction because no gateway has been used for it yet',
1090
+					'event_espresso'
1091
+				),
1092
+				__FILE__,
1093
+				__FUNCTION__,
1094
+				__LINE__
1095
+			);
1096
+			return null;
1097
+		}
1098
+		$primary_reg = $this->primary_registration();
1099
+		if (! $primary_reg) {
1100
+			EE_Error::add_error(
1101
+				esc_html__(
1102
+					'Cannot get billing info for gateway %s on transaction because no primary registration exists',
1103
+					'event_espresso'
1104
+				),
1105
+				__FILE__,
1106
+				__FUNCTION__,
1107
+				__LINE__
1108
+			);
1109
+			return null;
1110
+		}
1111
+		$attendee = $primary_reg->attendee();
1112
+		if (! $attendee) {
1113
+			EE_Error::add_error(
1114
+				esc_html__(
1115
+					'Cannot get billing info for gateway %s on transaction because the primary registration has no attendee exists',
1116
+					'event_espresso'
1117
+				),
1118
+				__FILE__,
1119
+				__FUNCTION__,
1120
+				__LINE__
1121
+			);
1122
+			return null;
1123
+		}
1124
+		return $attendee->billing_info_for_payment_method($payment_method);
1125
+	}
1126
+
1127
+
1128
+	/**
1129
+	 * Gets PMD_ID
1130
+	 *
1131
+	 * @return int
1132
+	 * @throws EE_Error
1133
+	 * @throws InvalidArgumentException
1134
+	 * @throws InvalidDataTypeException
1135
+	 * @throws InvalidInterfaceException
1136
+	 * @throws ReflectionException
1137
+	 */
1138
+	public function payment_method_ID()
1139
+	{
1140
+		return $this->get('PMD_ID');
1141
+	}
1142
+
1143
+
1144
+	/**
1145
+	 * Sets PMD_ID
1146
+	 *
1147
+	 * @param int $PMD_ID
1148
+	 * @throws EE_Error
1149
+	 * @throws InvalidArgumentException
1150
+	 * @throws InvalidDataTypeException
1151
+	 * @throws InvalidInterfaceException
1152
+	 * @throws ReflectionException
1153
+	 */
1154
+	public function set_payment_method_ID($PMD_ID)
1155
+	{
1156
+		$this->set('PMD_ID', $PMD_ID);
1157
+	}
1158
+
1159
+
1160
+	/**
1161
+	 * Gets the last-used payment method on this transaction
1162
+	 * (we COULD just use the last-made payment, but some payment methods, namely
1163
+	 * offline ones, dont' create payments)
1164
+	 *
1165
+	 * @return EE_Payment_Method
1166
+	 * @throws EE_Error
1167
+	 * @throws InvalidArgumentException
1168
+	 * @throws InvalidDataTypeException
1169
+	 * @throws InvalidInterfaceException
1170
+	 * @throws ReflectionException
1171
+	 */
1172
+	public function payment_method()
1173
+	{
1174
+		$pm = $this->get_first_related('Payment_Method');
1175
+		if ($pm instanceof EE_Payment_Method) {
1176
+			return $pm;
1177
+		}
1178
+		$last_payment = $this->last_payment();
1179
+		if ($last_payment instanceof EE_Payment && $last_payment->payment_method()) {
1180
+			return $last_payment->payment_method();
1181
+		}
1182
+		return null;
1183
+	}
1184
+
1185
+
1186
+	/**
1187
+	 * Gets the last payment made
1188
+	 *
1189
+	 * @return EE_Base_Class|EE_Payment
1190
+	 * @throws EE_Error
1191
+	 * @throws InvalidArgumentException
1192
+	 * @throws InvalidDataTypeException
1193
+	 * @throws InvalidInterfaceException
1194
+	 * @throws ReflectionException
1195
+	 */
1196
+	public function last_payment()
1197
+	{
1198
+		return $this->get_first_related('Payment', array('order_by' => array('PAY_ID' => 'desc')));
1199
+	}
1200
+
1201
+
1202
+	/**
1203
+	 * Gets all the line items which are unrelated to tickets on this transaction
1204
+	 *
1205
+	 * @return EE_Line_Item[]
1206
+	 * @throws EE_Error
1207
+	 * @throws InvalidArgumentException
1208
+	 * @throws InvalidDataTypeException
1209
+	 * @throws InvalidInterfaceException
1210
+	 * @throws ReflectionException
1211
+	 */
1212
+	public function non_ticket_line_items()
1213
+	{
1214
+		return EEM_Line_Item::instance()->get_all_non_ticket_line_items_for_transaction($this->ID());
1215
+	}
1216
+
1217
+
1218
+	/**
1219
+	 * possibly toggles TXN status
1220
+	 *
1221
+	 * @param  boolean $update whether to save the TXN
1222
+	 * @return bool whether the TXN was saved
1223
+	 * @throws EE_Error
1224
+	 * @throws InvalidArgumentException
1225
+	 * @throws InvalidDataTypeException
1226
+	 * @throws InvalidInterfaceException
1227
+	 * @throws ReflectionException
1228
+	 * @throws RuntimeException
1229
+	 */
1230
+	public function update_status_based_on_total_paid($update = true)
1231
+	{
1232
+		// set transaction status based on comparison of TXN_paid vs TXN_total
1233
+		if (EEH_Money::compare_floats($this->paid(), $this->total(), '>')) {
1234
+			$new_txn_status = EEM_Transaction::overpaid_status_code;
1235
+		} elseif (EEH_Money::compare_floats($this->paid(), $this->total())) {
1236
+			$new_txn_status = EEM_Transaction::complete_status_code;
1237
+		} elseif (EEH_Money::compare_floats($this->paid(), $this->total(), '<')) {
1238
+			$new_txn_status = EEM_Transaction::incomplete_status_code;
1239
+		} else {
1240
+			throw new RuntimeException(
1241
+				esc_html__('The total paid calculation for this transaction is inaccurate.', 'event_espresso')
1242
+			);
1243
+		}
1244
+		if ($new_txn_status !== $this->status_ID()) {
1245
+			$this->set_status($new_txn_status);
1246
+			if ($update) {
1247
+				return $this->save() ? true : false;
1248
+			}
1249
+		}
1250
+		return false;
1251
+	}
1252
+
1253
+
1254
+	/**
1255
+	 * Updates the transaction's status and total_paid based on all the payments
1256
+	 * that apply to it
1257
+	 *
1258
+	 * @deprecated
1259
+	 * @return array|bool
1260
+	 * @throws EE_Error
1261
+	 * @throws InvalidArgumentException
1262
+	 * @throws ReflectionException
1263
+	 * @throws InvalidDataTypeException
1264
+	 * @throws InvalidInterfaceException
1265
+	 */
1266
+	public function update_based_on_payments()
1267
+	{
1268
+		EE_Error::doing_it_wrong(
1269
+			__CLASS__ . '::' . __FUNCTION__,
1270
+			sprintf(
1271
+				esc_html__('This method is deprecated. Please use "%s" instead', 'event_espresso'),
1272
+				'EE_Transaction_Processor::update_transaction_and_registrations_after_checkout_or_payment()'
1273
+			),
1274
+			'4.6.0'
1275
+		);
1276
+		/** @type EE_Transaction_Processor $transaction_processor */
1277
+		$transaction_processor = EE_Registry::instance()->load_class('Transaction_Processor');
1278
+		return $transaction_processor->update_transaction_and_registrations_after_checkout_or_payment($this);
1279
+	}
1280
+
1281
+
1282
+	/**
1283
+	 * @return string
1284
+	 */
1285
+	public function old_txn_status()
1286
+	{
1287
+		return $this->_old_txn_status;
1288
+	}
1289
+
1290
+
1291
+	/**
1292
+	 * @param string $old_txn_status
1293
+	 */
1294
+	public function set_old_txn_status($old_txn_status)
1295
+	{
1296
+		// only set the first time
1297
+		if ($this->_old_txn_status === null) {
1298
+			$this->_old_txn_status = $old_txn_status;
1299
+		}
1300
+	}
1301
+
1302
+
1303
+	/**
1304
+	 * reg_status_updated
1305
+	 *
1306
+	 * @return bool
1307
+	 * @throws EE_Error
1308
+	 * @throws InvalidArgumentException
1309
+	 * @throws InvalidDataTypeException
1310
+	 * @throws InvalidInterfaceException
1311
+	 * @throws ReflectionException
1312
+	 */
1313
+	public function txn_status_updated()
1314
+	{
1315
+		return $this->status_ID() !== $this->_old_txn_status && $this->_old_txn_status !== null;
1316
+	}
1317
+
1318
+
1319
+	/**
1320
+	 * _reg_steps_completed
1321
+	 * if $check_all is TRUE, then returns TRUE if ALL reg steps have been marked as completed,
1322
+	 * if a $reg_step_slug is provided, then this step will be skipped when testing for completion
1323
+	 * if $check_all is FALSE and a $reg_step_slug is provided, then ONLY that reg step will be tested for completion
1324
+	 *
1325
+	 * @param string $reg_step_slug
1326
+	 * @param bool   $check_all
1327
+	 * @return bool|int
1328
+	 * @throws EE_Error
1329
+	 * @throws InvalidArgumentException
1330
+	 * @throws InvalidDataTypeException
1331
+	 * @throws InvalidInterfaceException
1332
+	 * @throws ReflectionException
1333
+	 */
1334
+	private function _reg_steps_completed($reg_step_slug = '', $check_all = true)
1335
+	{
1336
+		$reg_steps = $this->reg_steps();
1337
+		if (! is_array($reg_steps) || empty($reg_steps)) {
1338
+			return false;
1339
+		}
1340
+		// loop thru reg steps array)
1341
+		foreach ($reg_steps as $slug => $reg_step_completed) {
1342
+			// if NOT checking ALL steps (only checking one step)
1343
+			if (! $check_all) {
1344
+				// and this is the one
1345
+				if ($slug === $reg_step_slug) {
1346
+					return $reg_step_completed;
1347
+				}
1348
+				// skip to next reg step in loop
1349
+				continue;
1350
+			}
1351
+			// $check_all must be true, else we would never have gotten to this point
1352
+			if ($slug === $reg_step_slug) {
1353
+				// if we reach this point, then we are testing either:
1354
+				// all_reg_steps_completed_except() or
1355
+				// all_reg_steps_completed_except_final_step(),
1356
+				// and since this is the reg step EXCEPTION being tested
1357
+				// we want to return true (yes true) if this reg step is NOT completed
1358
+				// ie: "is everything completed except the final step?"
1359
+				// "that is correct... the final step is not completed, but all others are."
1360
+				return $reg_step_completed !== true;
1361
+			}
1362
+			if ($reg_step_completed !== true) {
1363
+				// if any reg step is NOT completed, then ALL steps are not completed
1364
+				return false;
1365
+			}
1366
+		}
1367
+		return true;
1368
+	}
1369
+
1370
+
1371
+	/**
1372
+	 * all_reg_steps_completed
1373
+	 * returns:
1374
+	 *    true if ALL reg steps have been marked as completed
1375
+	 *        or false if any step is not completed
1376
+	 *
1377
+	 * @return bool
1378
+	 * @throws EE_Error
1379
+	 * @throws InvalidArgumentException
1380
+	 * @throws InvalidDataTypeException
1381
+	 * @throws InvalidInterfaceException
1382
+	 * @throws ReflectionException
1383
+	 */
1384
+	public function all_reg_steps_completed()
1385
+	{
1386
+		return $this->_reg_steps_completed();
1387
+	}
1388
+
1389
+
1390
+	/**
1391
+	 * all_reg_steps_completed_except
1392
+	 * returns:
1393
+	 *        true if ALL reg steps, except a particular step that you wish to skip over, have been marked as completed
1394
+	 *        or false if any other step is not completed
1395
+	 *        or false if ALL steps are completed including the exception you are testing !!!
1396
+	 *
1397
+	 * @param string $exception
1398
+	 * @return bool
1399
+	 * @throws EE_Error
1400
+	 * @throws InvalidArgumentException
1401
+	 * @throws InvalidDataTypeException
1402
+	 * @throws InvalidInterfaceException
1403
+	 * @throws ReflectionException
1404
+	 */
1405
+	public function all_reg_steps_completed_except($exception = '')
1406
+	{
1407
+		return $this->_reg_steps_completed($exception);
1408
+	}
1409
+
1410
+
1411
+	/**
1412
+	 * all_reg_steps_completed_except
1413
+	 * returns:
1414
+	 *        true if ALL reg steps, except the final step, have been marked as completed
1415
+	 *        or false if any step is not completed
1416
+	 *    or false if ALL steps are completed including the final step !!!
1417
+	 *
1418
+	 * @return bool
1419
+	 * @throws EE_Error
1420
+	 * @throws InvalidArgumentException
1421
+	 * @throws InvalidDataTypeException
1422
+	 * @throws InvalidInterfaceException
1423
+	 * @throws ReflectionException
1424
+	 */
1425
+	public function all_reg_steps_completed_except_final_step()
1426
+	{
1427
+		return $this->_reg_steps_completed('finalize_registration');
1428
+	}
1429
+
1430
+
1431
+	/**
1432
+	 * reg_step_completed
1433
+	 * returns:
1434
+	 *    true if a specific reg step has been marked as completed
1435
+	 *    a Unix timestamp if it has been initialized but not yet completed,
1436
+	 *    or false if it has not yet been initialized
1437
+	 *
1438
+	 * @param string $reg_step_slug
1439
+	 * @return bool|int
1440
+	 * @throws EE_Error
1441
+	 * @throws InvalidArgumentException
1442
+	 * @throws InvalidDataTypeException
1443
+	 * @throws InvalidInterfaceException
1444
+	 * @throws ReflectionException
1445
+	 */
1446
+	public function reg_step_completed($reg_step_slug)
1447
+	{
1448
+		return $this->_reg_steps_completed($reg_step_slug, false);
1449
+	}
1450
+
1451
+
1452
+	/**
1453
+	 * completed_final_reg_step
1454
+	 * returns:
1455
+	 *    true if the finalize_registration reg step has been marked as completed
1456
+	 *    a Unix timestamp if it has been initialized but not yet completed,
1457
+	 *    or false if it has not yet been initialized
1458
+	 *
1459
+	 * @return bool|int
1460
+	 * @throws EE_Error
1461
+	 * @throws InvalidArgumentException
1462
+	 * @throws InvalidDataTypeException
1463
+	 * @throws InvalidInterfaceException
1464
+	 * @throws ReflectionException
1465
+	 */
1466
+	public function final_reg_step_completed()
1467
+	{
1468
+		return $this->_reg_steps_completed('finalize_registration', false);
1469
+	}
1470
+
1471
+
1472
+	/**
1473
+	 * set_reg_step_initiated
1474
+	 * given a valid TXN_reg_step, this sets it's value to a unix timestamp
1475
+	 *
1476
+	 * @param string $reg_step_slug
1477
+	 * @return boolean
1478
+	 * @throws EE_Error
1479
+	 * @throws InvalidArgumentException
1480
+	 * @throws InvalidDataTypeException
1481
+	 * @throws InvalidInterfaceException
1482
+	 * @throws ReflectionException
1483
+	 */
1484
+	public function set_reg_step_initiated($reg_step_slug)
1485
+	{
1486
+		return $this->_set_reg_step_completed_status($reg_step_slug, time());
1487
+	}
1488
+
1489
+
1490
+	/**
1491
+	 * set_reg_step_completed
1492
+	 * given a valid TXN_reg_step, this sets the step as completed
1493
+	 *
1494
+	 * @param string $reg_step_slug
1495
+	 * @return boolean
1496
+	 * @throws EE_Error
1497
+	 * @throws InvalidArgumentException
1498
+	 * @throws InvalidDataTypeException
1499
+	 * @throws InvalidInterfaceException
1500
+	 * @throws ReflectionException
1501
+	 */
1502
+	public function set_reg_step_completed($reg_step_slug)
1503
+	{
1504
+		return $this->_set_reg_step_completed_status($reg_step_slug, true);
1505
+	}
1506
+
1507
+
1508
+	/**
1509
+	 * set_reg_step_completed
1510
+	 * given a valid TXN_reg_step slug, this sets the step as NOT completed
1511
+	 *
1512
+	 * @param string $reg_step_slug
1513
+	 * @return boolean
1514
+	 * @throws EE_Error
1515
+	 * @throws InvalidArgumentException
1516
+	 * @throws InvalidDataTypeException
1517
+	 * @throws InvalidInterfaceException
1518
+	 * @throws ReflectionException
1519
+	 */
1520
+	public function set_reg_step_not_completed($reg_step_slug)
1521
+	{
1522
+		return $this->_set_reg_step_completed_status($reg_step_slug, false);
1523
+	}
1524
+
1525
+
1526
+	/**
1527
+	 * set_reg_step_completed
1528
+	 * given a valid reg step slug, this sets the TXN_reg_step completed status which is either:
1529
+	 *
1530
+	 * @param  string      $reg_step_slug
1531
+	 * @param  boolean|int $status
1532
+	 * @return boolean
1533
+	 * @throws EE_Error
1534
+	 * @throws InvalidArgumentException
1535
+	 * @throws InvalidDataTypeException
1536
+	 * @throws InvalidInterfaceException
1537
+	 * @throws ReflectionException
1538
+	 */
1539
+	private function _set_reg_step_completed_status($reg_step_slug, $status)
1540
+	{
1541
+		// validate status
1542
+		$status = is_bool($status) || is_int($status) ? $status : false;
1543
+		// get reg steps array
1544
+		$txn_reg_steps = $this->reg_steps();
1545
+		// if reg step does NOT exist
1546
+		if (! isset($txn_reg_steps[ $reg_step_slug ])) {
1547
+			return false;
1548
+		}
1549
+		// if  we're trying to complete a step that is already completed
1550
+		if ($txn_reg_steps[ $reg_step_slug ] === true) {
1551
+			return true;
1552
+		}
1553
+		// if  we're trying to complete a step that hasn't even started
1554
+		if ($status === true && $txn_reg_steps[ $reg_step_slug ] === false) {
1555
+			return false;
1556
+		}
1557
+		// if current status value matches the incoming value (no change)
1558
+		// type casting as int means values should collapse to either 0, 1, or a timestamp like 1234567890
1559
+		if ((int) $txn_reg_steps[ $reg_step_slug ] === (int) $status) {
1560
+			// this will happen in cases where multiple AJAX requests occur during the same step
1561
+			return true;
1562
+		}
1563
+		// if we're trying to set a start time, but it has already been set...
1564
+		if (is_numeric($status) && is_numeric($txn_reg_steps[ $reg_step_slug ])) {
1565
+			// skip the update below, but don't return FALSE so that errors won't be displayed
1566
+			return true;
1567
+		}
1568
+		// update completed status
1569
+		$txn_reg_steps[ $reg_step_slug ] = $status;
1570
+		$this->set_reg_steps($txn_reg_steps);
1571
+		$this->save();
1572
+		return true;
1573
+	}
1574
+
1575
+
1576
+	/**
1577
+	 * remove_reg_step
1578
+	 * given a valid TXN_reg_step slug, this will remove (unset)
1579
+	 * the reg step from the TXN reg step array
1580
+	 *
1581
+	 * @param string $reg_step_slug
1582
+	 * @return void
1583
+	 * @throws EE_Error
1584
+	 * @throws InvalidArgumentException
1585
+	 * @throws InvalidDataTypeException
1586
+	 * @throws InvalidInterfaceException
1587
+	 * @throws ReflectionException
1588
+	 */
1589
+	public function remove_reg_step($reg_step_slug)
1590
+	{
1591
+		// get reg steps array
1592
+		$txn_reg_steps = $this->reg_steps();
1593
+		unset($txn_reg_steps[ $reg_step_slug ]);
1594
+		$this->set_reg_steps($txn_reg_steps);
1595
+	}
1596
+
1597
+
1598
+	/**
1599
+	 * toggle_failed_transaction_status
1600
+	 * upgrades a TXNs status from failed to abandoned,
1601
+	 * meaning that contact information has been captured for at least one registrant
1602
+	 *
1603
+	 * @param bool $save
1604
+	 * @return bool
1605
+	 * @throws EE_Error
1606
+	 * @throws InvalidArgumentException
1607
+	 * @throws InvalidDataTypeException
1608
+	 * @throws InvalidInterfaceException
1609
+	 * @throws ReflectionException
1610
+	 */
1611
+	public function toggle_failed_transaction_status($save = true)
1612
+	{
1613
+		// if TXN status is still set as "failed"...
1614
+		if ($this->status_ID() === EEM_Transaction::failed_status_code) {
1615
+			$this->set_status(EEM_Transaction::abandoned_status_code);
1616
+			if ($save) {
1617
+				$this->save();
1618
+			}
1619
+			return true;
1620
+		}
1621
+		return false;
1622
+	}
1623
+
1624
+
1625
+	/**
1626
+	 * toggle_abandoned_transaction_status
1627
+	 * upgrades a TXNs status from failed or abandoned to incomplete
1628
+	 *
1629
+	 * @return bool
1630
+	 * @throws EE_Error
1631
+	 * @throws InvalidArgumentException
1632
+	 * @throws InvalidDataTypeException
1633
+	 * @throws InvalidInterfaceException
1634
+	 * @throws ReflectionException
1635
+	 */
1636
+	public function toggle_abandoned_transaction_status()
1637
+	{
1638
+		// if TXN status has not been updated already due to a payment, and is still set as "failed" or "abandoned"...
1639
+		$txn_status = $this->status_ID();
1640
+		if (
1641
+			$txn_status === EEM_Transaction::failed_status_code
1642
+			|| $txn_status === EEM_Transaction::abandoned_status_code
1643
+		) {
1644
+			// if a contact record for the primary registrant has been created
1645
+			if (
1646
+				$this->primary_registration() instanceof EE_Registration
1647
+				&& $this->primary_registration()->attendee() instanceof EE_Attendee
1648
+			) {
1649
+				$this->set_status(EEM_Transaction::incomplete_status_code);
1650
+			} else {
1651
+				// no contact record? yer abandoned!
1652
+				$this->set_status(EEM_Transaction::abandoned_status_code);
1653
+			}
1654
+			return true;
1655
+		}
1656
+		return false;
1657
+	}
1658
+
1659
+
1660
+	/**
1661
+	 * checks if an Abandoned TXN has any related payments, and if so,
1662
+	 * updates the TXN status based on the amount paid
1663
+	 *
1664
+	 * @throws EE_Error
1665
+	 * @throws InvalidArgumentException
1666
+	 * @throws InvalidDataTypeException
1667
+	 * @throws InvalidInterfaceException
1668
+	 * @throws ReflectionException
1669
+	 * @throws RuntimeException
1670
+	 * @throws ReflectionException
1671
+	 */
1672
+	public function verify_abandoned_transaction_status()
1673
+	{
1674
+		if ($this->status_ID() !== EEM_Transaction::abandoned_status_code) {
1675
+			return;
1676
+		}
1677
+		$payments = $this->get_many_related('Payment');
1678
+		if (! empty($payments)) {
1679
+			foreach ($payments as $payment) {
1680
+				if ($payment instanceof EE_Payment) {
1681
+					// kk this TXN should NOT be abandoned
1682
+					$this->update_status_based_on_total_paid();
1683
+					if (! (defined('DOING_AJAX') && DOING_AJAX) && is_admin()) {
1684
+						EE_Error::add_attention(
1685
+							sprintf(
1686
+								esc_html__(
1687
+									'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.',
1688
+									'event_espresso'
1689
+								),
1690
+								$this->ID(),
1691
+								$this->pretty_status()
1692
+							)
1693
+						);
1694
+					}
1695
+					// get final reg step status
1696
+					$finalized = $this->final_reg_step_completed();
1697
+					// if the 'finalize_registration' step has been initiated (has a timestamp)
1698
+					// but has not yet been fully completed (TRUE)
1699
+					if (is_int($finalized) && $finalized !== false && $finalized !== true) {
1700
+						$this->set_reg_step_completed('finalize_registration');
1701
+						$this->save();
1702
+					}
1703
+				}
1704
+			}
1705
+		}
1706
+	}
1707
+
1708
+
1709
+	/**
1710
+	 * @since 4.10.4.p
1711
+	 * @throws EE_Error
1712
+	 * @throws InvalidArgumentException
1713
+	 * @throws InvalidDataTypeException
1714
+	 * @throws InvalidInterfaceException
1715
+	 * @throws ReflectionException
1716
+	 * @throws RuntimeException
1717
+	 */
1718
+	public function recalculateLineItems()
1719
+	{
1720
+		$total_line_item = $this->total_line_item(false);
1721
+		if ($total_line_item instanceof EE_Line_Item) {
1722
+			EEH_Line_Item::resetIsTaxableForTickets($total_line_item);
1723
+			return EEH_Line_Item::apply_taxes($total_line_item, true);
1724
+		}
1725
+		return false;
1726
+	}
1727
+
1728
+
1729
+	/**
1730
+	 * @param string $source function name that called this method
1731
+	 * @return boolean | int
1732
+	 */
1733
+	public function delete($source = 'unknown')
1734
+	{
1735
+		$current_user = wp_get_current_user();
1736
+		$this->add_extra_meta(
1737
+			EE_Transaction::EXTRA_META_KEY_TXN_DELETED,
1738
+			array(
1739
+				'deleted-by' => $current_user->ID ? $current_user->display_name : 'unauthed user',
1740
+				'timestamp'  => time(),
1741
+				'source'     => $source,
1742
+			)
1743
+		);
1744
+		return parent::delete();
1745
+	}
1746 1746
 }
Please login to merge, or discard this patch.
admin_pages/registrations/Registrations_Admin_Page.core.php 1 patch
Indentation   +3680 added lines, -3680 removed lines patch added patch discarded remove patch
@@ -21,2225 +21,2225 @@  discard block
 block discarded – undo
21 21
  */
22 22
 class Registrations_Admin_Page extends EE_Admin_Page_CPT
23 23
 {
24
-    /**
25
-     * @var EE_Registration
26
-     */
27
-    private $_registration;
28
-
29
-    /**
30
-     * @var EE_Event
31
-     */
32
-    private $_reg_event;
33
-
34
-    /**
35
-     * @var EE_Session
36
-     */
37
-    private $_session;
38
-
39
-    /**
40
-     * @var array
41
-     */
42
-    private static $_reg_status;
43
-
44
-    /**
45
-     * Form for displaying the custom questions for this registration.
46
-     * This gets used a few times throughout the request so its best to cache it
47
-     *
48
-     * @var EE_Registration_Custom_Questions_Form
49
-     */
50
-    protected $_reg_custom_questions_form = null;
51
-
52
-    /**
53
-     * @var EEM_Registration $registration_model
54
-     */
55
-    private $registration_model;
56
-
57
-    /**
58
-     * @var EEM_Attendee $attendee_model
59
-     */
60
-    private $attendee_model;
61
-
62
-    /**
63
-     * @var EEM_Event $event_model
64
-     */
65
-    private $event_model;
66
-
67
-    /**
68
-     * @var EEM_Status $status_model
69
-     */
70
-    private $status_model;
71
-
72
-
73
-    /**
74
-     * @param bool $routing
75
-     * @throws EE_Error
76
-     * @throws InvalidArgumentException
77
-     * @throws InvalidDataTypeException
78
-     * @throws InvalidInterfaceException
79
-     * @throws ReflectionException
80
-     */
81
-    public function __construct($routing = true)
82
-    {
83
-        parent::__construct($routing);
84
-        add_action('wp_loaded', [$this, 'wp_loaded']);
85
-    }
86
-
87
-
88
-    /**
89
-     * @return EEM_Registration
90
-     * @throws InvalidArgumentException
91
-     * @throws InvalidDataTypeException
92
-     * @throws InvalidInterfaceException
93
-     * @since 4.10.2.p
94
-     */
95
-    protected function getRegistrationModel()
96
-    {
97
-        if (! $this->registration_model instanceof EEM_Registration) {
98
-            $this->registration_model = $this->getLoader()->getShared('EEM_Registration');
99
-        }
100
-        return $this->registration_model;
101
-    }
102
-
103
-
104
-    /**
105
-     * @return EEM_Attendee
106
-     * @throws InvalidArgumentException
107
-     * @throws InvalidDataTypeException
108
-     * @throws InvalidInterfaceException
109
-     * @since 4.10.2.p
110
-     */
111
-    protected function getAttendeeModel()
112
-    {
113
-        if (! $this->attendee_model instanceof EEM_Attendee) {
114
-            $this->attendee_model = $this->getLoader()->getShared('EEM_Attendee');
115
-        }
116
-        return $this->attendee_model;
117
-    }
118
-
119
-
120
-    /**
121
-     * @return EEM_Event
122
-     * @throws InvalidArgumentException
123
-     * @throws InvalidDataTypeException
124
-     * @throws InvalidInterfaceException
125
-     * @since 4.10.2.p
126
-     */
127
-    protected function getEventModel()
128
-    {
129
-        if (! $this->event_model instanceof EEM_Event) {
130
-            $this->event_model = $this->getLoader()->getShared('EEM_Event');
131
-        }
132
-        return $this->event_model;
133
-    }
134
-
135
-
136
-    /**
137
-     * @return EEM_Status
138
-     * @throws InvalidArgumentException
139
-     * @throws InvalidDataTypeException
140
-     * @throws InvalidInterfaceException
141
-     * @since 4.10.2.p
142
-     */
143
-    protected function getStatusModel()
144
-    {
145
-        if (! $this->status_model instanceof EEM_Status) {
146
-            $this->status_model = $this->getLoader()->getShared('EEM_Status');
147
-        }
148
-        return $this->status_model;
149
-    }
150
-
151
-
152
-    public function wp_loaded()
153
-    {
154
-        // when adding a new registration...
155
-        $action = $this->request->getRequestParam('action');
156
-        if ($action === 'new_registration') {
157
-            EE_System::do_not_cache();
158
-            if ($this->request->getRequestParam('processing_registration', 0, 'int') !== 1) {
159
-                // and it's NOT the attendee information reg step
160
-                // force cookie expiration by setting time to last week
161
-                setcookie('ee_registration_added', 0, time() - WEEK_IN_SECONDS, '/');
162
-                // and update the global
163
-                $_COOKIE['ee_registration_added'] = 0;
164
-            }
165
-        }
166
-    }
167
-
168
-
169
-    protected function _init_page_props()
170
-    {
171
-        $this->page_slug        = REG_PG_SLUG;
172
-        $this->_admin_base_url  = REG_ADMIN_URL;
173
-        $this->_admin_base_path = REG_ADMIN;
174
-        $this->page_label       = esc_html__('Registrations', 'event_espresso');
175
-        $this->_cpt_routes      = [
176
-            'add_new_attendee' => 'espresso_attendees',
177
-            'edit_attendee'    => 'espresso_attendees',
178
-            'insert_attendee'  => 'espresso_attendees',
179
-            'update_attendee'  => 'espresso_attendees',
180
-        ];
181
-        $this->_cpt_model_names = [
182
-            'add_new_attendee' => 'EEM_Attendee',
183
-            'edit_attendee'    => 'EEM_Attendee',
184
-        ];
185
-        $this->_cpt_edit_routes = [
186
-            'espresso_attendees' => 'edit_attendee',
187
-        ];
188
-        $this->_pagenow_map     = [
189
-            'add_new_attendee' => 'post-new.php',
190
-            'edit_attendee'    => 'post.php',
191
-            'trash'            => 'post.php',
192
-        ];
193
-        add_action('edit_form_after_title', [$this, 'after_title_form_fields'], 10);
194
-        // add filters so that the comment urls don't take users to a confusing 404 page
195
-        add_filter('get_comment_link', [$this, 'clear_comment_link'], 10, 2);
196
-    }
197
-
198
-
199
-    /**
200
-     * @param string     $link    The comment permalink with '#comment-$id' appended.
201
-     * @param WP_Comment $comment The current comment object.
202
-     * @return string
203
-     */
204
-    public function clear_comment_link($link, WP_Comment $comment)
205
-    {
206
-        // gotta make sure this only happens on this route
207
-        $post_type = get_post_type($comment->comment_post_ID);
208
-        if ($post_type === 'espresso_attendees') {
209
-            return '#commentsdiv';
210
-        }
211
-        return $link;
212
-    }
213
-
214
-
215
-    protected function _ajax_hooks()
216
-    {
217
-        // todo: all hooks for registrations ajax goes in here
218
-        add_action('wp_ajax_toggle_checkin_status', [$this, 'toggle_checkin_status']);
219
-    }
220
-
221
-
222
-    protected function _define_page_props()
223
-    {
224
-        $this->_admin_page_title = $this->page_label;
225
-        $this->_labels           = [
226
-            'buttons'                      => [
227
-                'add-registrant'      => esc_html__('Add New Registration', 'event_espresso'),
228
-                'add-attendee'        => esc_html__('Add Contact', 'event_espresso'),
229
-                'edit'                => esc_html__('Edit Contact', 'event_espresso'),
230
-                'csv_reg_report'      => esc_html__('Registrations CSV Report', 'event_espresso'),
231
-                'contact_list_report' => esc_html__('Contact List Report', 'event_espresso'),
232
-                'contact_list_export' => esc_html__('Export Data', 'event_espresso'),
233
-            ],
234
-            'publishbox'                   => [
235
-                'add_new_attendee' => esc_html__('Add Contact Record', 'event_espresso'),
236
-                'edit_attendee'    => esc_html__('Update Contact Record', 'event_espresso'),
237
-            ],
238
-            'hide_add_button_on_cpt_route' => [
239
-                'edit_attendee' => true,
240
-            ],
241
-        ];
242
-    }
243
-
244
-
245
-    /**
246
-     * grab url requests and route them
247
-     *
248
-     * @return void
249
-     * @throws EE_Error
250
-     */
251
-    public function _set_page_routes()
252
-    {
253
-        $this->_get_registration_status_array();
254
-        $REG_ID             = $this->request->getRequestParam('_REG_ID', 0, 'int');
255
-        $REG_ID             = $this->request->getRequestParam('reg_status_change_form[REG_ID]', $REG_ID, 'int');
256
-        $ATT_ID             = $this->request->getRequestParam('ATT_ID', 0, 'int');
257
-        $ATT_ID             = $this->request->getRequestParam('post', $ATT_ID, 'int');
258
-        $this->_page_routes = [
259
-            'default'                             => [
260
-                'func'       => [$this, '_registrations_overview_list_table'],
261
-                'capability' => 'ee_read_registrations',
262
-            ],
263
-            'view_registration'                   => [
264
-                'func'       => '_registration_details',
265
-                'capability' => 'ee_read_registration',
266
-                'obj_id'     => $REG_ID,
267
-            ],
268
-            'edit_registration'                   => [
269
-                'func'               => '_update_attendee_registration_form',
270
-                'noheader'           => true,
271
-                'headers_sent_route' => 'view_registration',
272
-                'capability'         => 'ee_edit_registration',
273
-                'obj_id'             => $REG_ID,
274
-                '_REG_ID'            => $REG_ID,
275
-            ],
276
-            'trash_registrations'                 => [
277
-                'func'       => '_trash_or_restore_registrations',
278
-                'args'       => ['trash' => true],
279
-                'noheader'   => true,
280
-                'capability' => 'ee_delete_registrations',
281
-            ],
282
-            'restore_registrations'               => [
283
-                'func'       => '_trash_or_restore_registrations',
284
-                'args'       => ['trash' => false],
285
-                'noheader'   => true,
286
-                'capability' => 'ee_delete_registrations',
287
-            ],
288
-            'delete_registrations'                => [
289
-                'func'       => '_delete_registrations',
290
-                'noheader'   => true,
291
-                'capability' => 'ee_delete_registrations',
292
-            ],
293
-            'new_registration'                    => [
294
-                'func'       => 'new_registration',
295
-                'capability' => 'ee_edit_registrations',
296
-            ],
297
-            'process_reg_step'                    => [
298
-                'func'       => 'process_reg_step',
299
-                'noheader'   => true,
300
-                'capability' => 'ee_edit_registrations',
301
-            ],
302
-            'redirect_to_txn'                     => [
303
-                'func'       => 'redirect_to_txn',
304
-                'noheader'   => true,
305
-                'capability' => 'ee_edit_registrations',
306
-            ],
307
-            'change_reg_status'                   => [
308
-                'func'       => '_change_reg_status',
309
-                'noheader'   => true,
310
-                'capability' => 'ee_edit_registration',
311
-                'obj_id'     => $REG_ID,
312
-            ],
313
-            'approve_registration'                => [
314
-                'func'       => 'approve_registration',
315
-                'noheader'   => true,
316
-                'capability' => 'ee_edit_registration',
317
-                'obj_id'     => $REG_ID,
318
-            ],
319
-            'approve_and_notify_registration'     => [
320
-                'func'       => 'approve_registration',
321
-                'noheader'   => true,
322
-                'args'       => [true],
323
-                'capability' => 'ee_edit_registration',
324
-                'obj_id'     => $REG_ID,
325
-            ],
326
-            'approve_registrations'               => [
327
-                'func'       => 'bulk_action_on_registrations',
328
-                'noheader'   => true,
329
-                'capability' => 'ee_edit_registrations',
330
-                'args'       => ['approve'],
331
-            ],
332
-            'approve_and_notify_registrations'    => [
333
-                'func'       => 'bulk_action_on_registrations',
334
-                'noheader'   => true,
335
-                'capability' => 'ee_edit_registrations',
336
-                'args'       => ['approve', true],
337
-            ],
338
-            'decline_registration'                => [
339
-                'func'       => 'decline_registration',
340
-                'noheader'   => true,
341
-                'capability' => 'ee_edit_registration',
342
-                'obj_id'     => $REG_ID,
343
-            ],
344
-            'decline_and_notify_registration'     => [
345
-                'func'       => 'decline_registration',
346
-                'noheader'   => true,
347
-                'args'       => [true],
348
-                'capability' => 'ee_edit_registration',
349
-                'obj_id'     => $REG_ID,
350
-            ],
351
-            'decline_registrations'               => [
352
-                'func'       => 'bulk_action_on_registrations',
353
-                'noheader'   => true,
354
-                'capability' => 'ee_edit_registrations',
355
-                'args'       => ['decline'],
356
-            ],
357
-            'decline_and_notify_registrations'    => [
358
-                'func'       => 'bulk_action_on_registrations',
359
-                'noheader'   => true,
360
-                'capability' => 'ee_edit_registrations',
361
-                'args'       => ['decline', true],
362
-            ],
363
-            'pending_registration'                => [
364
-                'func'       => 'pending_registration',
365
-                'noheader'   => true,
366
-                'capability' => 'ee_edit_registration',
367
-                'obj_id'     => $REG_ID,
368
-            ],
369
-            'pending_and_notify_registration'     => [
370
-                'func'       => 'pending_registration',
371
-                'noheader'   => true,
372
-                'args'       => [true],
373
-                'capability' => 'ee_edit_registration',
374
-                'obj_id'     => $REG_ID,
375
-            ],
376
-            'pending_registrations'               => [
377
-                'func'       => 'bulk_action_on_registrations',
378
-                'noheader'   => true,
379
-                'capability' => 'ee_edit_registrations',
380
-                'args'       => ['pending'],
381
-            ],
382
-            'pending_and_notify_registrations'    => [
383
-                'func'       => 'bulk_action_on_registrations',
384
-                'noheader'   => true,
385
-                'capability' => 'ee_edit_registrations',
386
-                'args'       => ['pending', true],
387
-            ],
388
-            'no_approve_registration'             => [
389
-                'func'       => 'not_approve_registration',
390
-                'noheader'   => true,
391
-                'capability' => 'ee_edit_registration',
392
-                'obj_id'     => $REG_ID,
393
-            ],
394
-            'no_approve_and_notify_registration'  => [
395
-                'func'       => 'not_approve_registration',
396
-                'noheader'   => true,
397
-                'args'       => [true],
398
-                'capability' => 'ee_edit_registration',
399
-                'obj_id'     => $REG_ID,
400
-            ],
401
-            'no_approve_registrations'            => [
402
-                'func'       => 'bulk_action_on_registrations',
403
-                'noheader'   => true,
404
-                'capability' => 'ee_edit_registrations',
405
-                'args'       => ['not_approve'],
406
-            ],
407
-            'no_approve_and_notify_registrations' => [
408
-                'func'       => 'bulk_action_on_registrations',
409
-                'noheader'   => true,
410
-                'capability' => 'ee_edit_registrations',
411
-                'args'       => ['not_approve', true],
412
-            ],
413
-            'cancel_registration'                 => [
414
-                'func'       => 'cancel_registration',
415
-                'noheader'   => true,
416
-                'capability' => 'ee_edit_registration',
417
-                'obj_id'     => $REG_ID,
418
-            ],
419
-            'cancel_and_notify_registration'      => [
420
-                'func'       => 'cancel_registration',
421
-                'noheader'   => true,
422
-                'args'       => [true],
423
-                'capability' => 'ee_edit_registration',
424
-                'obj_id'     => $REG_ID,
425
-            ],
426
-            'cancel_registrations'                => [
427
-                'func'       => 'bulk_action_on_registrations',
428
-                'noheader'   => true,
429
-                'capability' => 'ee_edit_registrations',
430
-                'args'       => ['cancel'],
431
-            ],
432
-            'cancel_and_notify_registrations'     => [
433
-                'func'       => 'bulk_action_on_registrations',
434
-                'noheader'   => true,
435
-                'capability' => 'ee_edit_registrations',
436
-                'args'       => ['cancel', true],
437
-            ],
438
-            'wait_list_registration'              => [
439
-                'func'       => 'wait_list_registration',
440
-                'noheader'   => true,
441
-                'capability' => 'ee_edit_registration',
442
-                'obj_id'     => $REG_ID,
443
-            ],
444
-            'wait_list_and_notify_registration'   => [
445
-                'func'       => 'wait_list_registration',
446
-                'noheader'   => true,
447
-                'args'       => [true],
448
-                'capability' => 'ee_edit_registration',
449
-                'obj_id'     => $REG_ID,
450
-            ],
451
-            'contact_list'                        => [
452
-                'func'       => '_attendee_contact_list_table',
453
-                'capability' => 'ee_read_contacts',
454
-            ],
455
-            'add_new_attendee'                    => [
456
-                'func' => '_create_new_cpt_item',
457
-                'args' => [
458
-                    'new_attendee' => true,
459
-                    'capability'   => 'ee_edit_contacts',
460
-                ],
461
-            ],
462
-            'edit_attendee'                       => [
463
-                'func'       => '_edit_cpt_item',
464
-                'capability' => 'ee_edit_contacts',
465
-                'obj_id'     => $ATT_ID,
466
-            ],
467
-            'duplicate_attendee'                  => [
468
-                'func'       => '_duplicate_attendee',
469
-                'noheader'   => true,
470
-                'capability' => 'ee_edit_contacts',
471
-                'obj_id'     => $ATT_ID,
472
-            ],
473
-            'insert_attendee'                     => [
474
-                'func'       => '_insert_or_update_attendee',
475
-                'args'       => [
476
-                    'new_attendee' => true,
477
-                ],
478
-                'noheader'   => true,
479
-                'capability' => 'ee_edit_contacts',
480
-            ],
481
-            'update_attendee'                     => [
482
-                'func'       => '_insert_or_update_attendee',
483
-                'args'       => [
484
-                    'new_attendee' => false,
485
-                ],
486
-                'noheader'   => true,
487
-                'capability' => 'ee_edit_contacts',
488
-                'obj_id'     => $ATT_ID,
489
-            ],
490
-            'trash_attendees'                     => [
491
-                'func'       => '_trash_or_restore_attendees',
492
-                'args'       => [
493
-                    'trash' => 'true',
494
-                ],
495
-                'noheader'   => true,
496
-                'capability' => 'ee_delete_contacts',
497
-            ],
498
-            'trash_attendee'                      => [
499
-                'func'       => '_trash_or_restore_attendees',
500
-                'args'       => [
501
-                    'trash' => true,
502
-                ],
503
-                'noheader'   => true,
504
-                'capability' => 'ee_delete_contacts',
505
-                'obj_id'     => $ATT_ID,
506
-            ],
507
-            'restore_attendees'                   => [
508
-                'func'       => '_trash_or_restore_attendees',
509
-                'args'       => [
510
-                    'trash' => false,
511
-                ],
512
-                'noheader'   => true,
513
-                'capability' => 'ee_delete_contacts',
514
-                'obj_id'     => $ATT_ID,
515
-            ],
516
-            'resend_registration'                 => [
517
-                'func'       => '_resend_registration',
518
-                'noheader'   => true,
519
-                'capability' => 'ee_send_message',
520
-            ],
521
-            'registrations_report'                => [
522
-                'func'       => [$this, '_registrations_report'],
523
-                'noheader'   => true,
524
-                'capability' => 'ee_read_registrations',
525
-            ],
526
-            'contact_list_export'                 => [
527
-                'func'       => '_contact_list_export',
528
-                'noheader'   => true,
529
-                'capability' => 'export',
530
-            ],
531
-            'contact_list_report'                 => [
532
-                'func'       => '_contact_list_report',
533
-                'noheader'   => true,
534
-                'capability' => 'ee_read_contacts',
535
-            ],
536
-        ];
537
-    }
538
-
539
-
540
-    protected function _set_page_config()
541
-    {
542
-        $REG_ID             = $this->request->getRequestParam('_REG_ID', 0, 'int');
543
-        $ATT_ID             = $this->request->getRequestParam('ATT_ID', 0, 'int');
544
-        $this->_page_config = [
545
-            'default'           => [
546
-                'nav'           => [
547
-                    'label' => esc_html__('Overview', 'event_espresso'),
548
-                    'order' => 5,
549
-                ],
550
-                'help_tabs'     => [
551
-                    'registrations_overview_help_tab'                       => [
552
-                        'title'    => esc_html__('Registrations Overview', 'event_espresso'),
553
-                        'filename' => 'registrations_overview',
554
-                    ],
555
-                    'registrations_overview_table_column_headings_help_tab' => [
556
-                        'title'    => esc_html__('Registrations Table Column Headings', 'event_espresso'),
557
-                        'filename' => 'registrations_overview_table_column_headings',
558
-                    ],
559
-                    'registrations_overview_filters_help_tab'               => [
560
-                        'title'    => esc_html__('Registration Filters', 'event_espresso'),
561
-                        'filename' => 'registrations_overview_filters',
562
-                    ],
563
-                    'registrations_overview_views_help_tab'                 => [
564
-                        'title'    => esc_html__('Registration Views', 'event_espresso'),
565
-                        'filename' => 'registrations_overview_views',
566
-                    ],
567
-                    'registrations_regoverview_other_help_tab'              => [
568
-                        'title'    => esc_html__('Registrations Other', 'event_espresso'),
569
-                        'filename' => 'registrations_overview_other',
570
-                    ],
571
-                ],
572
-                'qtips'         => ['Registration_List_Table_Tips'],
573
-                'list_table'    => 'EE_Registrations_List_Table',
574
-                'require_nonce' => false,
575
-            ],
576
-            'view_registration' => [
577
-                'nav'           => [
578
-                    'label'      => esc_html__('REG Details', 'event_espresso'),
579
-                    'order'      => 15,
580
-                    'url'        => $REG_ID
581
-                        ? add_query_arg(['_REG_ID' => $REG_ID], $this->_current_page_view_url)
582
-                        : $this->_admin_base_url,
583
-                    'persistent' => false,
584
-                ],
585
-                'help_tabs'     => [
586
-                    'registrations_details_help_tab'                    => [
587
-                        'title'    => esc_html__('Registration Details', 'event_espresso'),
588
-                        'filename' => 'registrations_details',
589
-                    ],
590
-                    'registrations_details_table_help_tab'              => [
591
-                        'title'    => esc_html__('Registration Details Table', 'event_espresso'),
592
-                        'filename' => 'registrations_details_table',
593
-                    ],
594
-                    'registrations_details_form_answers_help_tab'       => [
595
-                        'title'    => esc_html__('Registration Form Answers', 'event_espresso'),
596
-                        'filename' => 'registrations_details_form_answers',
597
-                    ],
598
-                    'registrations_details_registrant_details_help_tab' => [
599
-                        'title'    => esc_html__('Contact Details', 'event_espresso'),
600
-                        'filename' => 'registrations_details_registrant_details',
601
-                    ],
602
-                ],
603
-                'metaboxes'     => array_merge(
604
-                    $this->_default_espresso_metaboxes,
605
-                    ['_registration_details_metaboxes']
606
-                ),
607
-                'require_nonce' => false,
608
-            ],
609
-            'new_registration'  => [
610
-                'nav'           => [
611
-                    'label'      => esc_html__('Add New Registration', 'event_espresso'),
612
-                    'url'        => '#',
613
-                    'order'      => 15,
614
-                    'persistent' => false,
615
-                ],
616
-                'metaboxes'     => $this->_default_espresso_metaboxes,
617
-                'labels'        => [
618
-                    'publishbox' => esc_html__('Save Registration', 'event_espresso'),
619
-                ],
620
-                'require_nonce' => false,
621
-            ],
622
-            'add_new_attendee'  => [
623
-                'nav'           => [
624
-                    'label'      => esc_html__('Add Contact', 'event_espresso'),
625
-                    'order'      => 15,
626
-                    'persistent' => false,
627
-                ],
628
-                'metaboxes'     => array_merge(
629
-                    $this->_default_espresso_metaboxes,
630
-                    ['_publish_post_box', 'attendee_editor_metaboxes']
631
-                ),
632
-                'require_nonce' => false,
633
-            ],
634
-            'edit_attendee'     => [
635
-                'nav'           => [
636
-                    'label'      => esc_html__('Edit Contact', 'event_espresso'),
637
-                    'order'      => 15,
638
-                    'persistent' => false,
639
-                    'url'        => $ATT_ID
640
-                        ? add_query_arg(['ATT_ID' => $ATT_ID], $this->_current_page_view_url)
641
-                        : $this->_admin_base_url,
642
-                ],
643
-                'metaboxes'     => ['attendee_editor_metaboxes'],
644
-                'require_nonce' => false,
645
-            ],
646
-            'contact_list'      => [
647
-                'nav'           => [
648
-                    'label' => esc_html__('Contact List', 'event_espresso'),
649
-                    'order' => 20,
650
-                ],
651
-                'list_table'    => 'EE_Attendee_Contact_List_Table',
652
-                'help_tabs'     => [
653
-                    'registrations_contact_list_help_tab'                       => [
654
-                        'title'    => esc_html__('Registrations Contact List', 'event_espresso'),
655
-                        'filename' => 'registrations_contact_list',
656
-                    ],
657
-                    'registrations_contact-list_table_column_headings_help_tab' => [
658
-                        'title'    => esc_html__('Contact List Table Column Headings', 'event_espresso'),
659
-                        'filename' => 'registrations_contact_list_table_column_headings',
660
-                    ],
661
-                    'registrations_contact_list_views_help_tab'                 => [
662
-                        'title'    => esc_html__('Contact List Views', 'event_espresso'),
663
-                        'filename' => 'registrations_contact_list_views',
664
-                    ],
665
-                    'registrations_contact_list_other_help_tab'                 => [
666
-                        'title'    => esc_html__('Contact List Other', 'event_espresso'),
667
-                        'filename' => 'registrations_contact_list_other',
668
-                    ],
669
-                ],
670
-                'metaboxes'     => [],
671
-                'require_nonce' => false,
672
-            ],
673
-            // override default cpt routes
674
-            'create_new'        => '',
675
-            'edit'              => '',
676
-        ];
677
-    }
678
-
679
-
680
-    /**
681
-     * The below methods aren't used by this class currently
682
-     */
683
-    protected function _add_screen_options()
684
-    {
685
-    }
686
-
687
-
688
-    protected function _add_feature_pointers()
689
-    {
690
-    }
691
-
692
-
693
-    public function admin_init()
694
-    {
695
-        EE_Registry::$i18n_js_strings['update_att_qstns'] = esc_html__(
696
-            'click "Update Registration Questions" to save your changes',
697
-            'event_espresso'
698
-        );
699
-    }
700
-
701
-
702
-    public function admin_notices()
703
-    {
704
-    }
705
-
706
-
707
-    public function admin_footer_scripts()
708
-    {
709
-    }
710
-
711
-
712
-    /**
713
-     * get list of registration statuses
714
-     *
715
-     * @return void
716
-     * @throws EE_Error
717
-     */
718
-    private function _get_registration_status_array()
719
-    {
720
-        self::$_reg_status = EEM_Registration::reg_status_array([], true);
721
-    }
722
-
723
-
724
-    /**
725
-     * @throws InvalidArgumentException
726
-     * @throws InvalidDataTypeException
727
-     * @throws InvalidInterfaceException
728
-     * @since 4.10.2.p
729
-     */
730
-    protected function _add_screen_options_default()
731
-    {
732
-        $this->_per_page_screen_option();
733
-    }
734
-
735
-
736
-    /**
737
-     * @throws InvalidArgumentException
738
-     * @throws InvalidDataTypeException
739
-     * @throws InvalidInterfaceException
740
-     * @since 4.10.2.p
741
-     */
742
-    protected function _add_screen_options_contact_list()
743
-    {
744
-        $page_title              = $this->_admin_page_title;
745
-        $this->_admin_page_title = esc_html__('Contacts', 'event_espresso');
746
-        $this->_per_page_screen_option();
747
-        $this->_admin_page_title = $page_title;
748
-    }
749
-
750
-
751
-    public function load_scripts_styles()
752
-    {
753
-        // style
754
-        wp_register_style(
755
-            'espresso_reg',
756
-            REG_ASSETS_URL . 'espresso_registrations_admin.css',
757
-            ['ee-admin-css'],
758
-            EVENT_ESPRESSO_VERSION
759
-        );
760
-        wp_enqueue_style('espresso_reg');
761
-        // script
762
-        wp_register_script(
763
-            'espresso_reg',
764
-            REG_ASSETS_URL . 'espresso_registrations_admin.js',
765
-            ['jquery-ui-datepicker', 'jquery-ui-draggable', 'ee_admin_js'],
766
-            EVENT_ESPRESSO_VERSION,
767
-            true
768
-        );
769
-        wp_enqueue_script('espresso_reg');
770
-    }
771
-
772
-
773
-    /**
774
-     * @throws EE_Error
775
-     * @throws InvalidArgumentException
776
-     * @throws InvalidDataTypeException
777
-     * @throws InvalidInterfaceException
778
-     * @throws ReflectionException
779
-     * @since 4.10.2.p
780
-     */
781
-    public function load_scripts_styles_edit_attendee()
782
-    {
783
-        // stuff to only show up on our attendee edit details page.
784
-        $attendee_details_translations = [
785
-            'att_publish_text' => sprintf(
786
-            /* translators: The date and time */
787
-                wp_strip_all_tags(__('Created on: %s', 'event_espresso')),
788
-                '<b>' . $this->_cpt_model_obj->get_datetime('ATT_created') . '</b>'
789
-            ),
790
-        ];
791
-        wp_localize_script('espresso_reg', 'ATTENDEE_DETAILS', $attendee_details_translations);
792
-        wp_enqueue_script('jquery-validate');
793
-    }
794
-
795
-
796
-    /**
797
-     * @throws EE_Error
798
-     * @throws InvalidArgumentException
799
-     * @throws InvalidDataTypeException
800
-     * @throws InvalidInterfaceException
801
-     * @throws ReflectionException
802
-     * @since 4.10.2.p
803
-     */
804
-    public function load_scripts_styles_view_registration()
805
-    {
806
-        // styles
807
-        wp_enqueue_style('espresso-ui-theme');
808
-        // scripts
809
-        $this->_get_reg_custom_questions_form($this->_registration->ID());
810
-        $this->_reg_custom_questions_form->wp_enqueue_scripts();
811
-    }
812
-
813
-
814
-    public function load_scripts_styles_contact_list()
815
-    {
816
-        wp_dequeue_style('espresso_reg');
817
-        wp_register_style(
818
-            'espresso_att',
819
-            REG_ASSETS_URL . 'espresso_attendees_admin.css',
820
-            ['ee-admin-css'],
821
-            EVENT_ESPRESSO_VERSION
822
-        );
823
-        wp_enqueue_style('espresso_att');
824
-    }
825
-
826
-
827
-    public function load_scripts_styles_new_registration()
828
-    {
829
-        wp_register_script(
830
-            'ee-spco-for-admin',
831
-            REG_ASSETS_URL . 'spco_for_admin.js',
832
-            ['underscore', 'jquery'],
833
-            EVENT_ESPRESSO_VERSION,
834
-            true
835
-        );
836
-        wp_enqueue_script('ee-spco-for-admin');
837
-        add_filter('FHEE__EED_Ticket_Selector__load_tckt_slctr_assets', '__return_true');
838
-        EE_Form_Section_Proper::wp_enqueue_scripts();
839
-        EED_Ticket_Selector::load_tckt_slctr_assets();
840
-        EE_Datepicker_Input::enqueue_styles_and_scripts();
841
-    }
842
-
843
-
844
-    public function AHEE__EE_Admin_Page__route_admin_request_resend_registration()
845
-    {
846
-        add_filter('FHEE_load_EE_messages', '__return_true');
847
-    }
848
-
849
-
850
-    public function AHEE__EE_Admin_Page__route_admin_request_approve_registration()
851
-    {
852
-        add_filter('FHEE_load_EE_messages', '__return_true');
853
-    }
854
-
855
-
856
-    /**
857
-     * @throws EE_Error
858
-     * @throws InvalidArgumentException
859
-     * @throws InvalidDataTypeException
860
-     * @throws InvalidInterfaceException
861
-     * @throws ReflectionException
862
-     * @since 4.10.2.p
863
-     */
864
-    protected function _set_list_table_views_default()
865
-    {
866
-        // for notification related bulk actions we need to make sure only active messengers have an option.
867
-        EED_Messages::set_autoloaders();
868
-        /** @type EE_Message_Resource_Manager $message_resource_manager */
869
-        $message_resource_manager = EE_Registry::instance()->load_lib('Message_Resource_Manager');
870
-        $active_mts               = $message_resource_manager->list_of_active_message_types();
871
-        // key= bulk_action_slug, value= message type.
872
-        $match_array = [
873
-            'approve_registrations'    => 'registration',
874
-            'decline_registrations'    => 'declined_registration',
875
-            'pending_registrations'    => 'pending_approval',
876
-            'no_approve_registrations' => 'not_approved_registration',
877
-            'cancel_registrations'     => 'cancelled_registration',
878
-        ];
879
-        $can_send    = EE_Registry::instance()->CAP->current_user_can(
880
-            'ee_send_message',
881
-            'batch_send_messages'
882
-        );
883
-        /** setup reg status bulk actions **/
884
-        $def_reg_status_actions['approve_registrations'] = esc_html__('Approve Registrations', 'event_espresso');
885
-        if ($can_send && in_array($match_array['approve_registrations'], $active_mts, true)) {
886
-            $def_reg_status_actions['approve_and_notify_registrations'] = esc_html__(
887
-                'Approve and Notify Registrations',
888
-                'event_espresso'
889
-            );
890
-        }
891
-        $def_reg_status_actions['decline_registrations'] = esc_html__('Decline Registrations', 'event_espresso');
892
-        if ($can_send && in_array($match_array['decline_registrations'], $active_mts, true)) {
893
-            $def_reg_status_actions['decline_and_notify_registrations'] = esc_html__(
894
-                'Decline and Notify Registrations',
895
-                'event_espresso'
896
-            );
897
-        }
898
-        $def_reg_status_actions['pending_registrations'] = esc_html__(
899
-            'Set Registrations to Pending Payment',
900
-            'event_espresso'
901
-        );
902
-        if ($can_send && in_array($match_array['pending_registrations'], $active_mts, true)) {
903
-            $def_reg_status_actions['pending_and_notify_registrations'] = esc_html__(
904
-                'Set Registrations to Pending Payment and Notify',
905
-                'event_espresso'
906
-            );
907
-        }
908
-        $def_reg_status_actions['no_approve_registrations'] = esc_html__(
909
-            'Set Registrations to Not Approved',
910
-            'event_espresso'
911
-        );
912
-        if ($can_send && in_array($match_array['no_approve_registrations'], $active_mts, true)) {
913
-            $def_reg_status_actions['no_approve_and_notify_registrations'] = esc_html__(
914
-                'Set Registrations to Not Approved and Notify',
915
-                'event_espresso'
916
-            );
917
-        }
918
-        $def_reg_status_actions['cancel_registrations'] = esc_html__('Cancel Registrations', 'event_espresso');
919
-        if ($can_send && in_array($match_array['cancel_registrations'], $active_mts, true)) {
920
-            $def_reg_status_actions['cancel_and_notify_registrations'] = esc_html__(
921
-                'Cancel Registrations and Notify',
922
-                'event_espresso'
923
-            );
924
-        }
925
-        $def_reg_status_actions = apply_filters(
926
-            'FHEE__Registrations_Admin_Page___set_list_table_views_default__def_reg_status_actions_array',
927
-            $def_reg_status_actions,
928
-            $active_mts,
929
-            $can_send
930
-        );
931
-
932
-        $this->_views = [
933
-            'all'   => [
934
-                'slug'        => 'all',
935
-                'label'       => esc_html__('View All Registrations', 'event_espresso'),
936
-                'count'       => 0,
937
-                'bulk_action' => array_merge(
938
-                    $def_reg_status_actions,
939
-                    [
940
-                        'trash_registrations' => esc_html__('Trash Registrations', 'event_espresso'),
941
-                    ]
942
-                ),
943
-            ],
944
-            'month' => [
945
-                'slug'        => 'month',
946
-                'label'       => esc_html__('This Month', 'event_espresso'),
947
-                'count'       => 0,
948
-                'bulk_action' => array_merge(
949
-                    $def_reg_status_actions,
950
-                    [
951
-                        'trash_registrations' => esc_html__('Trash Registrations', 'event_espresso'),
952
-                    ]
953
-                ),
954
-            ],
955
-            'today' => [
956
-                'slug'        => 'today',
957
-                'label'       => sprintf(
958
-                    esc_html__('Today - %s', 'event_espresso'),
959
-                    date('M d, Y', current_time('timestamp'))
960
-                ),
961
-                'count'       => 0,
962
-                'bulk_action' => array_merge(
963
-                    $def_reg_status_actions,
964
-                    [
965
-                        'trash_registrations' => esc_html__('Trash Registrations', 'event_espresso'),
966
-                    ]
967
-                ),
968
-            ],
969
-        ];
970
-        if (
971
-            EE_Registry::instance()->CAP->current_user_can(
972
-                'ee_delete_registrations',
973
-                'espresso_registrations_delete_registration'
974
-            )
975
-        ) {
976
-            $this->_views['incomplete'] = [
977
-                'slug'        => 'incomplete',
978
-                'label'       => esc_html__('Incomplete', 'event_espresso'),
979
-                'count'       => 0,
980
-                'bulk_action' => [
981
-                    'trash_registrations' => esc_html__('Trash Registrations', 'event_espresso'),
982
-                ],
983
-            ];
984
-            $this->_views['trash']      = [
985
-                'slug'        => 'trash',
986
-                'label'       => esc_html__('Trash', 'event_espresso'),
987
-                'count'       => 0,
988
-                'bulk_action' => [
989
-                    'restore_registrations' => esc_html__('Restore Registrations', 'event_espresso'),
990
-                    'delete_registrations'  => esc_html__('Delete Registrations Permanently', 'event_espresso'),
991
-                ],
992
-            ];
993
-        }
994
-    }
995
-
996
-
997
-    protected function _set_list_table_views_contact_list()
998
-    {
999
-        $this->_views = [
1000
-            'in_use' => [
1001
-                'slug'        => 'in_use',
1002
-                'label'       => esc_html__('In Use', 'event_espresso'),
1003
-                'count'       => 0,
1004
-                'bulk_action' => [
1005
-                    'trash_attendees' => esc_html__('Move to Trash', 'event_espresso'),
1006
-                ],
1007
-            ],
1008
-        ];
1009
-        if (
1010
-            EE_Registry::instance()->CAP->current_user_can(
1011
-                'ee_delete_contacts',
1012
-                'espresso_registrations_trash_attendees'
1013
-            )
1014
-        ) {
1015
-            $this->_views['trash'] = [
1016
-                'slug'        => 'trash',
1017
-                'label'       => esc_html__('Trash', 'event_espresso'),
1018
-                'count'       => 0,
1019
-                'bulk_action' => [
1020
-                    'restore_attendees' => esc_html__('Restore from Trash', 'event_espresso'),
1021
-                ],
1022
-            ];
1023
-        }
1024
-    }
1025
-
1026
-
1027
-    /**
1028
-     * @return array
1029
-     * @throws EE_Error
1030
-     */
1031
-    protected function _registration_legend_items()
1032
-    {
1033
-        $fc_items = [
1034
-            'star-icon'        => [
1035
-                'class' => 'dashicons dashicons-star-filled lt-blue-icon ee-icon-size-8',
1036
-                'desc'  => esc_html__('This is the Primary Registrant', 'event_espresso'),
1037
-            ],
1038
-            'view_details'     => [
1039
-                'class' => 'dashicons dashicons-clipboard',
1040
-                'desc'  => esc_html__('View Registration Details', 'event_espresso'),
1041
-            ],
1042
-            'edit_attendee'    => [
1043
-                'class' => 'ee-icon ee-icon-user-edit ee-icon-size-16',
1044
-                'desc'  => esc_html__('Edit Contact Details', 'event_espresso'),
1045
-            ],
1046
-            'view_transaction' => [
1047
-                'class' => 'dashicons dashicons-cart',
1048
-                'desc'  => esc_html__('View Transaction Details', 'event_espresso'),
1049
-            ],
1050
-            'view_invoice'     => [
1051
-                'class' => 'dashicons dashicons-media-spreadsheet',
1052
-                'desc'  => esc_html__('View Transaction Invoice', 'event_espresso'),
1053
-            ],
1054
-        ];
1055
-        if (
1056
-            EE_Registry::instance()->CAP->current_user_can(
1057
-                'ee_send_message',
1058
-                'espresso_registrations_resend_registration'
1059
-            )
1060
-        ) {
1061
-            $fc_items['resend_registration'] = [
1062
-                'class' => 'dashicons dashicons-email-alt',
1063
-                'desc'  => esc_html__('Resend Registration Details', 'event_espresso'),
1064
-            ];
1065
-        } else {
1066
-            $fc_items['blank'] = ['class' => 'blank', 'desc' => ''];
1067
-        }
1068
-        if (
1069
-            EE_Registry::instance()->CAP->current_user_can(
1070
-                'ee_read_global_messages',
1071
-                'view_filtered_messages'
1072
-            )
1073
-        ) {
1074
-            $related_for_icon = EEH_MSG_Template::get_message_action_icon('see_notifications_for');
1075
-            if (is_array($related_for_icon) && isset($related_for_icon['css_class'], $related_for_icon['label'])) {
1076
-                $fc_items['view_related_messages'] = [
1077
-                    'class' => $related_for_icon['css_class'],
1078
-                    'desc'  => $related_for_icon['label'],
1079
-                ];
1080
-            }
1081
-        }
1082
-        $sc_items = [
1083
-            'approved_status'   => [
1084
-                'class' => 'ee-status-legend ee-status-legend-' . EEM_Registration::status_id_approved,
1085
-                'desc'  => EEH_Template::pretty_status(
1086
-                    EEM_Registration::status_id_approved,
1087
-                    false,
1088
-                    'sentence'
1089
-                ),
1090
-            ],
1091
-            'pending_status'    => [
1092
-                'class' => 'ee-status-legend ee-status-legend-' . EEM_Registration::status_id_pending_payment,
1093
-                'desc'  => EEH_Template::pretty_status(
1094
-                    EEM_Registration::status_id_pending_payment,
1095
-                    false,
1096
-                    'sentence'
1097
-                ),
1098
-            ],
1099
-            'wait_list'         => [
1100
-                'class' => 'ee-status-legend ee-status-legend-' . EEM_Registration::status_id_wait_list,
1101
-                'desc'  => EEH_Template::pretty_status(
1102
-                    EEM_Registration::status_id_wait_list,
1103
-                    false,
1104
-                    'sentence'
1105
-                ),
1106
-            ],
1107
-            'incomplete_status' => [
1108
-                'class' => 'ee-status-legend ee-status-legend-' . EEM_Registration::status_id_incomplete,
1109
-                'desc'  => EEH_Template::pretty_status(
1110
-                    EEM_Registration::status_id_incomplete,
1111
-                    false,
1112
-                    'sentence'
1113
-                ),
1114
-            ],
1115
-            'not_approved'      => [
1116
-                'class' => 'ee-status-legend ee-status-legend-' . EEM_Registration::status_id_not_approved,
1117
-                'desc'  => EEH_Template::pretty_status(
1118
-                    EEM_Registration::status_id_not_approved,
1119
-                    false,
1120
-                    'sentence'
1121
-                ),
1122
-            ],
1123
-            'declined_status'   => [
1124
-                'class' => 'ee-status-legend ee-status-legend-' . EEM_Registration::status_id_declined,
1125
-                'desc'  => EEH_Template::pretty_status(
1126
-                    EEM_Registration::status_id_declined,
1127
-                    false,
1128
-                    'sentence'
1129
-                ),
1130
-            ],
1131
-            'cancelled_status'  => [
1132
-                'class' => 'ee-status-legend ee-status-legend-' . EEM_Registration::status_id_cancelled,
1133
-                'desc'  => EEH_Template::pretty_status(
1134
-                    EEM_Registration::status_id_cancelled,
1135
-                    false,
1136
-                    'sentence'
1137
-                ),
1138
-            ],
1139
-        ];
1140
-        return array_merge($fc_items, $sc_items);
1141
-    }
1142
-
1143
-
1144
-
1145
-    /***************************************        REGISTRATION OVERVIEW        **************************************/
1146
-
1147
-
1148
-    /**
1149
-     * @throws DomainException
1150
-     * @throws EE_Error
1151
-     * @throws InvalidArgumentException
1152
-     * @throws InvalidDataTypeException
1153
-     * @throws InvalidInterfaceException
1154
-     */
1155
-    protected function _registrations_overview_list_table()
1156
-    {
1157
-        $this->appendAddNewRegistrationButtonToPageTitle();
1158
-        $header_text                  = '';
1159
-        $admin_page_header_decorators = [
1160
-            'EventEspresso\core\domain\services\admin\registrations\list_table\page_header\AttendeeFilterHeader',
1161
-            'EventEspresso\core\domain\services\admin\registrations\list_table\page_header\EventFilterHeader',
1162
-            'EventEspresso\core\domain\services\admin\registrations\list_table\page_header\DateFilterHeader',
1163
-            'EventEspresso\core\domain\services\admin\registrations\list_table\page_header\TicketFilterHeader',
1164
-        ];
1165
-        foreach ($admin_page_header_decorators as $admin_page_header_decorator) {
1166
-            $filter_header_decorator = $this->getLoader()->getNew($admin_page_header_decorator);
1167
-            $header_text             = $filter_header_decorator->getHeaderText($header_text);
1168
-        }
1169
-        $this->_template_args['admin_page_header'] = $header_text;
1170
-        $this->_template_args['after_list_table']  = $this->_display_legend($this->_registration_legend_items());
1171
-        $this->display_admin_list_table_page_with_no_sidebar();
1172
-    }
1173
-
1174
-
1175
-    /**
1176
-     * @throws EE_Error
1177
-     * @throws InvalidArgumentException
1178
-     * @throws InvalidDataTypeException
1179
-     * @throws InvalidInterfaceException
1180
-     */
1181
-    private function appendAddNewRegistrationButtonToPageTitle()
1182
-    {
1183
-        $EVT_ID = $this->request->getRequestParam('event_id', 0, 'int');
1184
-        if (
1185
-            $EVT_ID
1186
-            && EE_Registry::instance()->CAP->current_user_can(
1187
-                'ee_edit_registrations',
1188
-                'espresso_registrations_new_registration',
1189
-                $EVT_ID
1190
-            )
1191
-        ) {
1192
-            $this->_admin_page_title .= ' ' . $this->get_action_link_or_button(
1193
-                'new_registration',
1194
-                'add-registrant',
1195
-                ['event_id' => $EVT_ID],
1196
-                'add-new-h2'
1197
-            );
1198
-        }
1199
-    }
1200
-
1201
-
1202
-    /**
1203
-     * This sets the _registration property for the registration details screen
1204
-     *
1205
-     * @return void
1206
-     * @throws EE_Error
1207
-     * @throws InvalidArgumentException
1208
-     * @throws InvalidDataTypeException
1209
-     * @throws InvalidInterfaceException
1210
-     */
1211
-    private function _set_registration_object()
1212
-    {
1213
-        // get out if we've already set the object
1214
-        if ($this->_registration instanceof EE_Registration) {
1215
-            return;
1216
-        }
1217
-        $REG_ID = $this->request->getRequestParam('_REG_ID', 0, 'int');
1218
-        if ($this->_registration = $this->getRegistrationModel()->get_one_by_ID($REG_ID)) {
1219
-            return;
1220
-        }
1221
-        $error_msg = sprintf(
1222
-            esc_html__(
1223
-                'An error occurred and the details for Registration ID #%s could not be retrieved.',
1224
-                'event_espresso'
1225
-            ),
1226
-            $REG_ID
1227
-        );
1228
-        EE_Error::add_error($error_msg, __FILE__, __FUNCTION__, __LINE__);
1229
-        $this->_registration = null;
1230
-    }
1231
-
1232
-
1233
-    /**
1234
-     * Used to retrieve registrations for the list table.
1235
-     *
1236
-     * @param int  $per_page
1237
-     * @param bool $count
1238
-     * @param bool $this_month
1239
-     * @param bool $today
1240
-     * @return EE_Registration[]|int
1241
-     * @throws EE_Error
1242
-     * @throws InvalidArgumentException
1243
-     * @throws InvalidDataTypeException
1244
-     * @throws InvalidInterfaceException
1245
-     */
1246
-    public function get_registrations(
1247
-        $per_page = 10,
1248
-        $count = false,
1249
-        $this_month = false,
1250
-        $today = false
1251
-    ) {
1252
-        if ($this_month) {
1253
-            $this->request->setRequestParam('status', 'month');
1254
-        }
1255
-        if ($today) {
1256
-            $this->request->setRequestParam('status', 'today');
1257
-        }
1258
-        $query_params = $this->_get_registration_query_parameters([], $per_page, $count);
1259
-        /**
1260
-         * Override the default groupby added by EEM_Base so that sorts with multiple order bys work as expected
1261
-         *
1262
-         * @link https://events.codebasehq.com/projects/event-espresso/tickets/10093
1263
-         * @see  https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1264
-         *                      or if you have the development copy of EE you can view this at the path:
1265
-         *                      /docs/G--Model-System/model-query-params.md
1266
-         */
1267
-        $query_params['group_by'] = '';
1268
-
1269
-        return $count
1270
-            ? $this->getRegistrationModel()->count($query_params)
1271
-            /** @type EE_Registration[] */
1272
-            : $this->getRegistrationModel()->get_all($query_params);
1273
-    }
1274
-
1275
-
1276
-    /**
1277
-     * Retrieves the query parameters to be used by the Registration model for getting registrations.
1278
-     * Note: this listens to values on the request for some of the query parameters.
1279
-     *
1280
-     * @param array $request
1281
-     * @param int   $per_page
1282
-     * @param bool  $count
1283
-     * @return array
1284
-     * @throws EE_Error
1285
-     * @throws InvalidArgumentException
1286
-     * @throws InvalidDataTypeException
1287
-     * @throws InvalidInterfaceException
1288
-     */
1289
-    protected function _get_registration_query_parameters(
1290
-        $request = [],
1291
-        $per_page = 10,
1292
-        $count = false
1293
-    ) {
1294
-        /** @var EventEspresso\core\domain\services\admin\registrations\list_table\QueryBuilder $list_table_query_builder */
1295
-        $list_table_query_builder = $this->getLoader()->getNew(
1296
-            'EventEspresso\core\domain\services\admin\registrations\list_table\QueryBuilder',
1297
-            [null, null, $request]
1298
-        );
1299
-        return $list_table_query_builder->getQueryParams($per_page, $count);
1300
-    }
1301
-
1302
-
1303
-    public function get_registration_status_array()
1304
-    {
1305
-        return self::$_reg_status;
1306
-    }
1307
-
1308
-
1309
-
1310
-
1311
-    /***************************************        REGISTRATION DETAILS        ***************************************/
1312
-    /**
1313
-     * generates HTML for the View Registration Details Admin page
1314
-     *
1315
-     * @return void
1316
-     * @throws DomainException
1317
-     * @throws EE_Error
1318
-     * @throws InvalidArgumentException
1319
-     * @throws InvalidDataTypeException
1320
-     * @throws InvalidInterfaceException
1321
-     * @throws EntityNotFoundException
1322
-     * @throws ReflectionException
1323
-     */
1324
-    protected function _registration_details()
1325
-    {
1326
-        $this->_template_args = [];
1327
-        $this->_set_registration_object();
1328
-        if (is_object($this->_registration)) {
1329
-            $transaction                                   = $this->_registration->transaction()
1330
-                ? $this->_registration->transaction()
1331
-                : EE_Transaction::new_instance();
1332
-            $this->_session                                = $transaction->session_data();
1333
-            $event_id                                      = $this->_registration->event_ID();
1334
-            $this->_template_args['reg_nmbr']['value']     = $this->_registration->ID();
1335
-            $this->_template_args['reg_nmbr']['label']     = esc_html__('Registration Number', 'event_espresso');
1336
-            $this->_template_args['reg_datetime']['value'] = $this->_registration->get_i18n_datetime('REG_date');
1337
-            $this->_template_args['reg_datetime']['label'] = esc_html__('Date', 'event_espresso');
1338
-            $this->_template_args['grand_total']           = $transaction->total();
1339
-            $this->_template_args['currency_sign']         = EE_Registry::instance()->CFG->currency->sign;
1340
-            // link back to overview
1341
-            $this->_template_args['reg_overview_url']            = REG_ADMIN_URL;
1342
-            $this->_template_args['registration']                = $this->_registration;
1343
-            $this->_template_args['filtered_registrations_link'] = EE_Admin_Page::add_query_args_and_nonce(
1344
-                [
1345
-                    'action'   => 'default',
1346
-                    'event_id' => $event_id,
1347
-                ],
1348
-                REG_ADMIN_URL
1349
-            );
1350
-            $this->_template_args['filtered_transactions_link']  = EE_Admin_Page::add_query_args_and_nonce(
1351
-                [
1352
-                    'action' => 'default',
1353
-                    'EVT_ID' => $event_id,
1354
-                    'page'   => 'espresso_transactions',
1355
-                ],
1356
-                admin_url('admin.php')
1357
-            );
1358
-            $this->_template_args['event_link']                  = EE_Admin_Page::add_query_args_and_nonce(
1359
-                [
1360
-                    'page'   => 'espresso_events',
1361
-                    'action' => 'edit',
1362
-                    'post'   => $event_id,
1363
-                ],
1364
-                admin_url('admin.php')
1365
-            );
1366
-            // next and previous links
1367
-            $next_reg                                      = $this->_registration->next(
1368
-                null,
1369
-                [],
1370
-                'REG_ID'
1371
-            );
1372
-            $this->_template_args['next_registration']     = $next_reg
1373
-                ? $this->_next_link(
1374
-                    EE_Admin_Page::add_query_args_and_nonce(
1375
-                        [
1376
-                            'action'  => 'view_registration',
1377
-                            '_REG_ID' => $next_reg['REG_ID'],
1378
-                        ],
1379
-                        REG_ADMIN_URL
1380
-                    ),
1381
-                    'dashicons dashicons-arrow-right ee-icon-size-22'
1382
-                )
1383
-                : '';
1384
-            $previous_reg                                  = $this->_registration->previous(
1385
-                null,
1386
-                [],
1387
-                'REG_ID'
1388
-            );
1389
-            $this->_template_args['previous_registration'] = $previous_reg
1390
-                ? $this->_previous_link(
1391
-                    EE_Admin_Page::add_query_args_and_nonce(
1392
-                        [
1393
-                            'action'  => 'view_registration',
1394
-                            '_REG_ID' => $previous_reg['REG_ID'],
1395
-                        ],
1396
-                        REG_ADMIN_URL
1397
-                    ),
1398
-                    'dashicons dashicons-arrow-left ee-icon-size-22'
1399
-                )
1400
-                : '';
1401
-            // grab header
1402
-            $template_path                             = REG_TEMPLATE_PATH . 'reg_admin_details_header.template.php';
1403
-            $this->_template_args['REG_ID']            = $this->_registration->ID();
1404
-            $this->_template_args['admin_page_header'] = EEH_Template::display_template(
1405
-                $template_path,
1406
-                $this->_template_args,
1407
-                true
1408
-            );
1409
-        } else {
1410
-            $this->_template_args['admin_page_header'] = '';
1411
-            $this->_display_espresso_notices();
1412
-        }
1413
-        // the details template wrapper
1414
-        $this->display_admin_page_with_sidebar();
1415
-    }
1416
-
1417
-
1418
-    /**
1419
-     * @throws EE_Error
1420
-     * @throws InvalidArgumentException
1421
-     * @throws InvalidDataTypeException
1422
-     * @throws InvalidInterfaceException
1423
-     * @throws ReflectionException
1424
-     * @since 4.10.2.p
1425
-     */
1426
-    protected function _registration_details_metaboxes()
1427
-    {
1428
-        do_action('AHEE__Registrations_Admin_Page___registration_details_metabox__start', $this);
1429
-        $this->_set_registration_object();
1430
-        $attendee = $this->_registration instanceof EE_Registration ? $this->_registration->attendee() : null;
1431
-        add_meta_box(
1432
-            'edit-reg-status-mbox',
1433
-            esc_html__('Registration Status', 'event_espresso'),
1434
-            [$this, 'set_reg_status_buttons_metabox'],
1435
-            $this->_wp_page_slug,
1436
-            'normal',
1437
-            'high'
1438
-        );
1439
-        add_meta_box(
1440
-            'edit-reg-details-mbox',
1441
-            esc_html__('Registration Details', 'event_espresso'),
1442
-            [$this, '_reg_details_meta_box'],
1443
-            $this->_wp_page_slug,
1444
-            'normal',
1445
-            'high'
1446
-        );
1447
-        if (
1448
-            $attendee instanceof EE_Attendee
1449
-            && EE_Registry::instance()->CAP->current_user_can(
1450
-                'ee_read_registration',
1451
-                'edit-reg-questions-mbox',
1452
-                $this->_registration->ID()
1453
-            )
1454
-        ) {
1455
-            add_meta_box(
1456
-                'edit-reg-questions-mbox',
1457
-                esc_html__('Registration Form Answers', 'event_espresso'),
1458
-                [$this, '_reg_questions_meta_box'],
1459
-                $this->_wp_page_slug,
1460
-                'normal',
1461
-                'high'
1462
-            );
1463
-        }
1464
-        add_meta_box(
1465
-            'edit-reg-registrant-mbox',
1466
-            esc_html__('Contact Details', 'event_espresso'),
1467
-            [$this, '_reg_registrant_side_meta_box'],
1468
-            $this->_wp_page_slug,
1469
-            'side',
1470
-            'high'
1471
-        );
1472
-        if ($this->_registration->group_size() > 1) {
1473
-            add_meta_box(
1474
-                'edit-reg-attendees-mbox',
1475
-                esc_html__('Other Registrations in this Transaction', 'event_espresso'),
1476
-                [$this, '_reg_attendees_meta_box'],
1477
-                $this->_wp_page_slug,
1478
-                'normal',
1479
-                'high'
1480
-            );
1481
-        }
1482
-    }
1483
-
1484
-
1485
-    /**
1486
-     * set_reg_status_buttons_metabox
1487
-     *
1488
-     * @return void
1489
-     * @throws EE_Error
1490
-     * @throws EntityNotFoundException
1491
-     * @throws InvalidArgumentException
1492
-     * @throws InvalidDataTypeException
1493
-     * @throws InvalidInterfaceException
1494
-     * @throws ReflectionException
1495
-     */
1496
-    public function set_reg_status_buttons_metabox()
1497
-    {
1498
-        $this->_set_registration_object();
1499
-        $change_reg_status_form = $this->_generate_reg_status_change_form();
1500
-        $output                 = $change_reg_status_form->form_open(
1501
-            self::add_query_args_and_nonce(
1502
-                [
1503
-                    'action' => 'change_reg_status',
1504
-                ],
1505
-                REG_ADMIN_URL
1506
-            )
1507
-        );
1508
-        $output                 .= $change_reg_status_form->get_html();
1509
-        $output                 .= $change_reg_status_form->form_close();
1510
-        echo wp_kses($output, AllowedTags::getWithFormTags());
1511
-    }
1512
-
1513
-
1514
-    /**
1515
-     * @return EE_Form_Section_Proper
1516
-     * @throws EE_Error
1517
-     * @throws InvalidArgumentException
1518
-     * @throws InvalidDataTypeException
1519
-     * @throws InvalidInterfaceException
1520
-     * @throws EntityNotFoundException
1521
-     * @throws ReflectionException
1522
-     */
1523
-    protected function _generate_reg_status_change_form()
1524
-    {
1525
-        $reg_status_change_form_array = [
1526
-            'name'            => 'reg_status_change_form',
1527
-            'html_id'         => 'reg-status-change-form',
1528
-            'layout_strategy' => new EE_Admin_Two_Column_Layout(),
1529
-            'subsections'     => [
1530
-                'return'         => new EE_Hidden_Input(
1531
-                    [
1532
-                        'name'    => 'return',
1533
-                        'default' => 'view_registration',
1534
-                    ]
1535
-                ),
1536
-                'REG_ID'         => new EE_Hidden_Input(
1537
-                    [
1538
-                        'name'    => 'REG_ID',
1539
-                        'default' => $this->_registration->ID(),
1540
-                    ]
1541
-                ),
1542
-                'current_status' => new EE_Form_Section_HTML(
1543
-                    EEH_HTML::table(
1544
-                        EEH_HTML::tr(
1545
-                            EEH_HTML::th(
1546
-                                EEH_HTML::label(
1547
-                                    EEH_HTML::strong(
1548
-                                        esc_html__('Current Registration Status', 'event_espresso')
1549
-                                    )
1550
-                                )
1551
-                            )
1552
-                            . EEH_HTML::td(
1553
-                                EEH_HTML::strong(
1554
-                                    $this->_registration->pretty_status(),
1555
-                                    '',
1556
-                                    'status-' . $this->_registration->status_ID(),
1557
-                                    'line-height: 1em; font-size: 1.5em; font-weight: bold;'
1558
-                                )
1559
-                            )
1560
-                        )
1561
-                    )
1562
-                ),
1563
-            ],
1564
-        ];
1565
-        if (
1566
-            EE_Registry::instance()->CAP->current_user_can(
1567
-                'ee_edit_registration',
1568
-                'toggle_registration_status',
1569
-                $this->_registration->ID()
1570
-            )
1571
-        ) {
1572
-            $reg_status_change_form_array['subsections']['reg_status']         = new EE_Select_Input(
1573
-                $this->_get_reg_statuses(),
1574
-                [
1575
-                    'html_label_text' => esc_html__('Change Registration Status to', 'event_espresso'),
1576
-                    'default'         => $this->_registration->status_ID(),
1577
-                ]
1578
-            );
1579
-            $reg_status_change_form_array['subsections']['send_notifications'] = new EE_Yes_No_Input(
1580
-                [
1581
-                    'html_label_text' => esc_html__('Send Related Messages', 'event_espresso'),
1582
-                    'default'         => false,
1583
-                    'html_help_text'  => esc_html__(
1584
-                        'If set to "Yes", then the related messages will be sent to the registrant.',
1585
-                        'event_espresso'
1586
-                    ),
1587
-                ]
1588
-            );
1589
-            $reg_status_change_form_array['subsections']['submit']             = new EE_Submit_Input(
1590
-                [
1591
-                    'html_class'      => 'button-primary',
1592
-                    'html_label_text' => '&nbsp;',
1593
-                    'default'         => esc_html__('Update Registration Status', 'event_espresso'),
1594
-                ]
1595
-            );
1596
-        }
1597
-        return new EE_Form_Section_Proper($reg_status_change_form_array);
1598
-    }
1599
-
1600
-
1601
-    /**
1602
-     * Returns an array of all the buttons for the various statuses and switch status actions
1603
-     *
1604
-     * @return array
1605
-     * @throws EE_Error
1606
-     * @throws InvalidArgumentException
1607
-     * @throws InvalidDataTypeException
1608
-     * @throws InvalidInterfaceException
1609
-     * @throws EntityNotFoundException
1610
-     */
1611
-    protected function _get_reg_statuses()
1612
-    {
1613
-        $reg_status_array = $this->getRegistrationModel()->reg_status_array();
1614
-        unset($reg_status_array[ EEM_Registration::status_id_incomplete ]);
1615
-        // get current reg status
1616
-        $current_status = $this->_registration->status_ID();
1617
-        // is registration for free event? This will determine whether to display the pending payment option
1618
-        if (
1619
-            $current_status !== EEM_Registration::status_id_pending_payment
1620
-            && EEH_Money::compare_floats($this->_registration->ticket()->price(), 0.00)
1621
-        ) {
1622
-            unset($reg_status_array[ EEM_Registration::status_id_pending_payment ]);
1623
-        }
1624
-        return $this->getStatusModel()->localized_status($reg_status_array, false, 'sentence');
1625
-    }
1626
-
1627
-
1628
-    /**
1629
-     * This method is used when using _REG_ID from request which may or may not be an array of reg_ids.
1630
-     *
1631
-     * @param bool $status REG status given for changing registrations to.
1632
-     * @param bool $notify Whether to send messages notifications or not.
1633
-     * @return array (array with reg_id(s) updated and whether update was successful.
1634
-     * @throws DomainException
1635
-     * @throws EE_Error
1636
-     * @throws EntityNotFoundException
1637
-     * @throws InvalidArgumentException
1638
-     * @throws InvalidDataTypeException
1639
-     * @throws InvalidInterfaceException
1640
-     * @throws ReflectionException
1641
-     * @throws RuntimeException
1642
-     */
1643
-    protected function _set_registration_status_from_request($status = false, $notify = false)
1644
-    {
1645
-        $REG_IDs = $this->request->requestParamIsSet('reg_status_change_form')
1646
-            ? $this->request->getRequestParam('reg_status_change_form[REG_ID]', [], 'int', true)
1647
-            : $this->request->getRequestParam('_REG_ID', [], 'int', true);
1648
-
1649
-        // sanitize $REG_IDs
1650
-        $REG_IDs = array_map('absint', $REG_IDs);
1651
-        // and remove empty entries
1652
-        $REG_IDs = array_filter($REG_IDs);
1653
-
1654
-        $result = $this->_set_registration_status($REG_IDs, $status, $notify);
1655
-
1656
-        /**
1657
-         * Set and filter $_req_data['_REG_ID'] for any potential future messages notifications.
1658
-         * Currently this value is used downstream by the _process_resend_registration method.
1659
-         *
1660
-         * @param int|array                $registration_ids The registration ids that have had their status changed successfully.
1661
-         * @param bool                     $status           The status registrations were changed to.
1662
-         * @param bool                     $success          If the status was changed successfully for all registrations.
1663
-         * @param Registrations_Admin_Page $admin_page_object
1664
-         */
1665
-        $REG_ID = apply_filters(
1666
-            'FHEE__Registrations_Admin_Page___set_registration_status_from_request__REG_IDs',
1667
-            $result['REG_ID'],
1668
-            $status,
1669
-            $result['success'],
1670
-            $this
1671
-        );
1672
-        $this->request->setRequestParam('_REG_ID', $REG_ID);
1673
-
1674
-        // notify?
1675
-        if (
1676
-            $notify
1677
-            && $result['success']
1678
-            && ! empty($REG_ID)
1679
-            && EE_Registry::instance()->CAP->current_user_can(
1680
-                'ee_send_message',
1681
-                'espresso_registrations_resend_registration'
1682
-            )
1683
-        ) {
1684
-            $this->_process_resend_registration();
1685
-        }
1686
-        return $result;
1687
-    }
1688
-
1689
-
1690
-    /**
1691
-     * Set the registration status for the given reg_id (which may or may not be an array, it gets typecast to an
1692
-     * array). Note, this method does NOT take care of possible notifications.  That is required by calling code.
1693
-     *
1694
-     * @param array  $REG_IDs
1695
-     * @param string $status
1696
-     * @param bool   $notify Used to indicate whether notification was requested or not.  This determines the context
1697
-     *                       slug sent with setting the registration status.
1698
-     * @return array (an array with 'success' key representing whether status change was successful, and 'REG_ID' as
1699
-     * @throws EE_Error
1700
-     * @throws InvalidArgumentException
1701
-     * @throws InvalidDataTypeException
1702
-     * @throws InvalidInterfaceException
1703
-     * @throws ReflectionException
1704
-     * @throws RuntimeException
1705
-     * @throws EntityNotFoundException
1706
-     * @throws DomainException
1707
-     */
1708
-    protected function _set_registration_status($REG_IDs = [], $status = '', $notify = false)
1709
-    {
1710
-        $success = false;
1711
-        // typecast $REG_IDs
1712
-        $REG_IDs = (array) $REG_IDs;
1713
-        if (! empty($REG_IDs)) {
1714
-            $success = true;
1715
-            // set default status if none is passed
1716
-            $status         = $status ?: EEM_Registration::status_id_pending_payment;
1717
-            $status_context = $notify
1718
-                ? Domain::CONTEXT_REGISTRATION_STATUS_CHANGE_REGISTRATION_ADMIN_NOTIFY
1719
-                : Domain::CONTEXT_REGISTRATION_STATUS_CHANGE_REGISTRATION_ADMIN;
1720
-            // loop through REG_ID's and change status
1721
-            foreach ($REG_IDs as $REG_ID) {
1722
-                $registration = $this->getRegistrationModel()->get_one_by_ID($REG_ID);
1723
-                if ($registration instanceof EE_Registration) {
1724
-                    $registration->set_status(
1725
-                        $status,
1726
-                        false,
1727
-                        new Context(
1728
-                            $status_context,
1729
-                            esc_html__(
1730
-                                'Manually triggered status change on a Registration Admin Page route.',
1731
-                                'event_espresso'
1732
-                            )
1733
-                        )
1734
-                    );
1735
-                    $result = $registration->save();
1736
-                    // verifying explicit fails because update *may* just return 0 for 0 rows affected
1737
-                    $success = $result !== false ? $success : false;
1738
-                }
1739
-            }
1740
-        }
1741
-
1742
-        // return $success and processed registrations
1743
-        return ['REG_ID' => $REG_IDs, 'success' => $success];
1744
-    }
1745
-
1746
-
1747
-    /**
1748
-     * Common logic for setting up success message and redirecting to appropriate route
1749
-     *
1750
-     * @param string $STS_ID status id for the registration changed to
1751
-     * @param bool   $notify indicates whether the _set_registration_status_from_request does notifications or not.
1752
-     * @return void
1753
-     * @throws DomainException
1754
-     * @throws EE_Error
1755
-     * @throws EntityNotFoundException
1756
-     * @throws InvalidArgumentException
1757
-     * @throws InvalidDataTypeException
1758
-     * @throws InvalidInterfaceException
1759
-     * @throws ReflectionException
1760
-     * @throws RuntimeException
1761
-     */
1762
-    protected function _reg_status_change_return($STS_ID, $notify = false)
1763
-    {
1764
-        $result  = ! empty($STS_ID) ? $this->_set_registration_status_from_request($STS_ID, $notify)
1765
-            : ['success' => false];
1766
-        $success = isset($result['success']) && $result['success'];
1767
-        // setup success message
1768
-        if ($success) {
1769
-            if (is_array($result['REG_ID']) && count($result['REG_ID']) === 1) {
1770
-                $msg = sprintf(
1771
-                    esc_html__('Registration status has been set to %s', 'event_espresso'),
1772
-                    EEH_Template::pretty_status($STS_ID, false, 'lower')
1773
-                );
1774
-            } else {
1775
-                $msg = sprintf(
1776
-                    esc_html__('Registrations have been set to %s.', 'event_espresso'),
1777
-                    EEH_Template::pretty_status($STS_ID, false, 'lower')
1778
-                );
1779
-            }
1780
-            EE_Error::add_success($msg);
1781
-        } else {
1782
-            EE_Error::add_error(
1783
-                esc_html__(
1784
-                    'Something went wrong, and the status was not changed',
1785
-                    'event_espresso'
1786
-                ),
1787
-                __FILE__,
1788
-                __LINE__,
1789
-                __FUNCTION__
1790
-            );
1791
-        }
1792
-        $return = $this->request->getRequestParam('return');
1793
-        $route  = $return === 'view_registration'
1794
-            ? ['action' => 'view_registration', '_REG_ID' => reset($result['REG_ID'])]
1795
-            : ['action' => 'default'];
1796
-        $route  = $this->mergeExistingRequestParamsWithRedirectArgs($route);
1797
-        $this->_redirect_after_action($success, '', '', $route, true);
1798
-    }
1799
-
1800
-
1801
-    /**
1802
-     * incoming reg status change from reg details page.
1803
-     *
1804
-     * @return void
1805
-     * @throws EE_Error
1806
-     * @throws EntityNotFoundException
1807
-     * @throws InvalidArgumentException
1808
-     * @throws InvalidDataTypeException
1809
-     * @throws InvalidInterfaceException
1810
-     * @throws ReflectionException
1811
-     * @throws RuntimeException
1812
-     * @throws DomainException
1813
-     */
1814
-    protected function _change_reg_status()
1815
-    {
1816
-        $this->request->setRequestParam('return', 'view_registration');
1817
-        // set notify based on whether the send notifications toggle is set or not
1818
-        $notify     = $this->request->getRequestParam('reg_status_change_form[send_notifications]', false, 'bool');
1819
-        $reg_status = $this->request->getRequestParam('reg_status_change_form[reg_status]', '');
1820
-        $this->request->setRequestParam('reg_status_change_form[reg_status]', $reg_status);
1821
-        switch ($reg_status) {
1822
-            case EEM_Registration::status_id_approved:
1823
-            case EEH_Template::pretty_status(EEM_Registration::status_id_approved, false, 'sentence'):
1824
-                $this->approve_registration($notify);
1825
-                break;
1826
-            case EEM_Registration::status_id_pending_payment:
1827
-            case EEH_Template::pretty_status(EEM_Registration::status_id_pending_payment, false, 'sentence'):
1828
-                $this->pending_registration($notify);
1829
-                break;
1830
-            case EEM_Registration::status_id_not_approved:
1831
-            case EEH_Template::pretty_status(EEM_Registration::status_id_not_approved, false, 'sentence'):
1832
-                $this->not_approve_registration($notify);
1833
-                break;
1834
-            case EEM_Registration::status_id_declined:
1835
-            case EEH_Template::pretty_status(EEM_Registration::status_id_declined, false, 'sentence'):
1836
-                $this->decline_registration($notify);
1837
-                break;
1838
-            case EEM_Registration::status_id_cancelled:
1839
-            case EEH_Template::pretty_status(EEM_Registration::status_id_cancelled, false, 'sentence'):
1840
-                $this->cancel_registration($notify);
1841
-                break;
1842
-            case EEM_Registration::status_id_wait_list:
1843
-            case EEH_Template::pretty_status(EEM_Registration::status_id_wait_list, false, 'sentence'):
1844
-                $this->wait_list_registration($notify);
1845
-                break;
1846
-            case EEM_Registration::status_id_incomplete:
1847
-            default:
1848
-                $this->request->unSetRequestParam('return');
1849
-                $this->_reg_status_change_return('');
1850
-                break;
1851
-        }
1852
-    }
1853
-
1854
-
1855
-    /**
1856
-     * Callback for bulk action routes.
1857
-     * Note: although we could just register the singular route callbacks for each bulk action route as well, this
1858
-     * method was chosen so there is one central place all the registration status bulk actions are going through.
1859
-     * Potentially, this provides an easier place to locate logic that is specific to these bulk actions (as opposed to
1860
-     * when an action is happening on just a single registration).
1861
-     *
1862
-     * @param      $action
1863
-     * @param bool $notify
1864
-     */
1865
-    protected function bulk_action_on_registrations($action, $notify = false)
1866
-    {
1867
-        do_action(
1868
-            'AHEE__Registrations_Admin_Page__bulk_action_on_registrations__before_execution',
1869
-            $this,
1870
-            $action,
1871
-            $notify
1872
-        );
1873
-        $method = $action . '_registration';
1874
-        if (method_exists($this, $method)) {
1875
-            $this->$method($notify);
1876
-        }
1877
-    }
1878
-
1879
-
1880
-    /**
1881
-     * approve_registration
1882
-     *
1883
-     * @param bool $notify whether or not to notify the registrant about their approval.
1884
-     * @return void
1885
-     * @throws EE_Error
1886
-     * @throws EntityNotFoundException
1887
-     * @throws InvalidArgumentException
1888
-     * @throws InvalidDataTypeException
1889
-     * @throws InvalidInterfaceException
1890
-     * @throws ReflectionException
1891
-     * @throws RuntimeException
1892
-     * @throws DomainException
1893
-     */
1894
-    protected function approve_registration($notify = false)
1895
-    {
1896
-        $this->_reg_status_change_return(EEM_Registration::status_id_approved, $notify);
1897
-    }
1898
-
1899
-
1900
-    /**
1901
-     * decline_registration
1902
-     *
1903
-     * @param bool $notify whether or not to notify the registrant about their status change.
1904
-     * @return void
1905
-     * @throws EE_Error
1906
-     * @throws EntityNotFoundException
1907
-     * @throws InvalidArgumentException
1908
-     * @throws InvalidDataTypeException
1909
-     * @throws InvalidInterfaceException
1910
-     * @throws ReflectionException
1911
-     * @throws RuntimeException
1912
-     * @throws DomainException
1913
-     */
1914
-    protected function decline_registration($notify = false)
1915
-    {
1916
-        $this->_reg_status_change_return(EEM_Registration::status_id_declined, $notify);
1917
-    }
1918
-
1919
-
1920
-    /**
1921
-     * cancel_registration
1922
-     *
1923
-     * @param bool $notify whether or not to notify the registrant about their status change.
1924
-     * @return void
1925
-     * @throws EE_Error
1926
-     * @throws EntityNotFoundException
1927
-     * @throws InvalidArgumentException
1928
-     * @throws InvalidDataTypeException
1929
-     * @throws InvalidInterfaceException
1930
-     * @throws ReflectionException
1931
-     * @throws RuntimeException
1932
-     * @throws DomainException
1933
-     */
1934
-    protected function cancel_registration($notify = false)
1935
-    {
1936
-        $this->_reg_status_change_return(EEM_Registration::status_id_cancelled, $notify);
1937
-    }
1938
-
1939
-
1940
-    /**
1941
-     * not_approve_registration
1942
-     *
1943
-     * @param bool $notify whether or not to notify the registrant about their status change.
1944
-     * @return void
1945
-     * @throws EE_Error
1946
-     * @throws EntityNotFoundException
1947
-     * @throws InvalidArgumentException
1948
-     * @throws InvalidDataTypeException
1949
-     * @throws InvalidInterfaceException
1950
-     * @throws ReflectionException
1951
-     * @throws RuntimeException
1952
-     * @throws DomainException
1953
-     */
1954
-    protected function not_approve_registration($notify = false)
1955
-    {
1956
-        $this->_reg_status_change_return(EEM_Registration::status_id_not_approved, $notify);
1957
-    }
1958
-
1959
-
1960
-    /**
1961
-     * decline_registration
1962
-     *
1963
-     * @param bool $notify whether or not to notify the registrant about their status change.
1964
-     * @return void
1965
-     * @throws EE_Error
1966
-     * @throws EntityNotFoundException
1967
-     * @throws InvalidArgumentException
1968
-     * @throws InvalidDataTypeException
1969
-     * @throws InvalidInterfaceException
1970
-     * @throws ReflectionException
1971
-     * @throws RuntimeException
1972
-     * @throws DomainException
1973
-     */
1974
-    protected function pending_registration($notify = false)
1975
-    {
1976
-        $this->_reg_status_change_return(EEM_Registration::status_id_pending_payment, $notify);
1977
-    }
1978
-
1979
-
1980
-    /**
1981
-     * waitlist_registration
1982
-     *
1983
-     * @param bool $notify whether or not to notify the registrant about their status change.
1984
-     * @return void
1985
-     * @throws EE_Error
1986
-     * @throws EntityNotFoundException
1987
-     * @throws InvalidArgumentException
1988
-     * @throws InvalidDataTypeException
1989
-     * @throws InvalidInterfaceException
1990
-     * @throws ReflectionException
1991
-     * @throws RuntimeException
1992
-     * @throws DomainException
1993
-     */
1994
-    protected function wait_list_registration($notify = false)
1995
-    {
1996
-        $this->_reg_status_change_return(EEM_Registration::status_id_wait_list, $notify);
1997
-    }
1998
-
1999
-
2000
-    /**
2001
-     * generates HTML for the Registration main meta box
2002
-     *
2003
-     * @return void
2004
-     * @throws DomainException
2005
-     * @throws EE_Error
2006
-     * @throws InvalidArgumentException
2007
-     * @throws InvalidDataTypeException
2008
-     * @throws InvalidInterfaceException
2009
-     * @throws ReflectionException
2010
-     * @throws EntityNotFoundException
2011
-     */
2012
-    public function _reg_details_meta_box()
2013
-    {
2014
-        EEH_Autoloader::register_line_item_display_autoloaders();
2015
-        EEH_Autoloader::register_line_item_filter_autoloaders();
2016
-        EE_Registry::instance()->load_helper('Line_Item');
2017
-        $transaction    = $this->_registration->transaction() ? $this->_registration->transaction()
2018
-            : EE_Transaction::new_instance();
2019
-        $this->_session = $transaction->session_data();
2020
-        $filters        = new EE_Line_Item_Filter_Collection();
2021
-        $filters->add(new EE_Single_Registration_Line_Item_Filter($this->_registration));
2022
-        $filters->add(new EE_Non_Zero_Line_Item_Filter());
2023
-        $line_item_filter_processor              = new EE_Line_Item_Filter_Processor(
2024
-            $filters,
2025
-            $transaction->total_line_item()
2026
-        );
2027
-        $filtered_line_item_tree                 = $line_item_filter_processor->process();
2028
-        $line_item_display                       = new EE_Line_Item_Display(
2029
-            'reg_admin_table',
2030
-            'EE_Admin_Table_Registration_Line_Item_Display_Strategy'
2031
-        );
2032
-        $this->_template_args['line_item_table'] = $line_item_display->display_line_item(
2033
-            $filtered_line_item_tree,
2034
-            ['EE_Registration' => $this->_registration]
2035
-        );
2036
-        $attendee                                = $this->_registration->attendee();
2037
-        if (
2038
-            EE_Registry::instance()->CAP->current_user_can(
2039
-                'ee_read_transaction',
2040
-                'espresso_transactions_view_transaction'
2041
-            )
2042
-        ) {
2043
-            $this->_template_args['view_transaction_button'] = EEH_Template::get_button_or_link(
2044
-                EE_Admin_Page::add_query_args_and_nonce(
2045
-                    [
2046
-                        'action' => 'view_transaction',
2047
-                        'TXN_ID' => $transaction->ID(),
2048
-                    ],
2049
-                    TXN_ADMIN_URL
2050
-                ),
2051
-                esc_html__(' View Transaction', 'event_espresso'),
2052
-                'button secondary-button right',
2053
-                'dashicons dashicons-cart'
2054
-            );
2055
-        } else {
2056
-            $this->_template_args['view_transaction_button'] = '';
2057
-        }
2058
-        if (
2059
-            $attendee instanceof EE_Attendee
2060
-            && EE_Registry::instance()->CAP->current_user_can(
2061
-                'ee_send_message',
2062
-                'espresso_registrations_resend_registration'
2063
-            )
2064
-        ) {
2065
-            $this->_template_args['resend_registration_button'] = EEH_Template::get_button_or_link(
2066
-                EE_Admin_Page::add_query_args_and_nonce(
2067
-                    [
2068
-                        'action'      => 'resend_registration',
2069
-                        '_REG_ID'     => $this->_registration->ID(),
2070
-                        'redirect_to' => 'view_registration',
2071
-                    ],
2072
-                    REG_ADMIN_URL
2073
-                ),
2074
-                esc_html__(' Resend Registration', 'event_espresso'),
2075
-                'button secondary-button right',
2076
-                'dashicons dashicons-email-alt'
2077
-            );
2078
-        } else {
2079
-            $this->_template_args['resend_registration_button'] = '';
2080
-        }
2081
-        $this->_template_args['currency_sign'] = EE_Registry::instance()->CFG->currency->sign;
2082
-        $payment                               = $transaction->get_first_related('Payment');
2083
-        $payment                               = ! $payment instanceof EE_Payment
2084
-            ? EE_Payment::new_instance()
2085
-            : $payment;
2086
-        $payment_method                        = $payment->get_first_related('Payment_Method');
2087
-        $payment_method                        = ! $payment_method instanceof EE_Payment_Method
2088
-            ? EE_Payment_Method::new_instance()
2089
-            : $payment_method;
2090
-        $reg_details                           = [
2091
-            'payment_method'       => $payment_method->name(),
2092
-            'response_msg'         => $payment->gateway_response(),
2093
-            'registration_id'      => $this->_registration->get('REG_code'),
2094
-            'registration_session' => $this->_registration->session_ID(),
2095
-            'ip_address'           => isset($this->_session['ip_address']) ? $this->_session['ip_address'] : '',
2096
-            'user_agent'           => isset($this->_session['user_agent']) ? $this->_session['user_agent'] : '',
2097
-        ];
2098
-        if (isset($reg_details['registration_id'])) {
2099
-            $this->_template_args['reg_details']['registration_id']['value'] = $reg_details['registration_id'];
2100
-            $this->_template_args['reg_details']['registration_id']['label'] = esc_html__(
2101
-                'Registration ID',
2102
-                'event_espresso'
2103
-            );
2104
-            $this->_template_args['reg_details']['registration_id']['class'] = 'regular-text';
2105
-        }
2106
-        if (isset($reg_details['payment_method'])) {
2107
-            $this->_template_args['reg_details']['payment_method']['value'] = $reg_details['payment_method'];
2108
-            $this->_template_args['reg_details']['payment_method']['label'] = esc_html__(
2109
-                'Most Recent Payment Method',
2110
-                'event_espresso'
2111
-            );
2112
-            $this->_template_args['reg_details']['payment_method']['class'] = 'regular-text';
2113
-            $this->_template_args['reg_details']['response_msg']['value']   = $reg_details['response_msg'];
2114
-            $this->_template_args['reg_details']['response_msg']['label']   = esc_html__(
2115
-                'Payment method response',
2116
-                'event_espresso'
2117
-            );
2118
-            $this->_template_args['reg_details']['response_msg']['class']   = 'regular-text';
2119
-        }
2120
-        $this->_template_args['reg_details']['registration_session']['value'] = $reg_details['registration_session'];
2121
-        $this->_template_args['reg_details']['registration_session']['label'] = esc_html__(
2122
-            'Registration Session',
2123
-            'event_espresso'
2124
-        );
2125
-        $this->_template_args['reg_details']['registration_session']['class'] = 'regular-text';
2126
-        $this->_template_args['reg_details']['ip_address']['value']           = $reg_details['ip_address'];
2127
-        $this->_template_args['reg_details']['ip_address']['label']           = esc_html__(
2128
-            'Registration placed from IP',
2129
-            'event_espresso'
2130
-        );
2131
-        $this->_template_args['reg_details']['ip_address']['class']           = 'regular-text';
2132
-        $this->_template_args['reg_details']['user_agent']['value']           = $reg_details['user_agent'];
2133
-        $this->_template_args['reg_details']['user_agent']['label']           = esc_html__(
2134
-            'Registrant User Agent',
2135
-            'event_espresso'
2136
-        );
2137
-        $this->_template_args['reg_details']['user_agent']['class']           = 'large-text';
2138
-        $this->_template_args['event_link']                                   = EE_Admin_Page::add_query_args_and_nonce(
2139
-            [
2140
-                'action'   => 'default',
2141
-                'event_id' => $this->_registration->event_ID(),
2142
-            ],
2143
-            REG_ADMIN_URL
2144
-        );
2145
-        $this->_template_args['REG_ID']                                       = $this->_registration->ID();
2146
-        $this->_template_args['event_id']                                     = $this->_registration->event_ID();
2147
-        $template_path                                                        =
2148
-            REG_TEMPLATE_PATH . 'reg_admin_details_main_meta_box_reg_details.template.php';
2149
-        EEH_Template::display_template($template_path, $this->_template_args); // already escaped
2150
-    }
2151
-
2152
-
2153
-    /**
2154
-     * generates HTML for the Registration Questions meta box.
2155
-     * If pre-4.8.32.rc.000 hooks are used, uses old methods (with its filters),
2156
-     * otherwise uses new forms system
2157
-     *
2158
-     * @return void
2159
-     * @throws DomainException
2160
-     * @throws EE_Error
2161
-     * @throws InvalidArgumentException
2162
-     * @throws InvalidDataTypeException
2163
-     * @throws InvalidInterfaceException
2164
-     * @throws ReflectionException
2165
-     */
2166
-    public function _reg_questions_meta_box()
2167
-    {
2168
-        // allow someone to override this method entirely
2169
-        if (
2170
-            apply_filters(
2171
-                'FHEE__Registrations_Admin_Page___reg_questions_meta_box__do_default',
2172
-                true,
2173
-                $this,
2174
-                $this->_registration
2175
-            )
2176
-        ) {
2177
-            $form                                              = $this->_get_reg_custom_questions_form(
2178
-                $this->_registration->ID()
2179
-            );
2180
-            $this->_template_args['att_questions']             = count($form->subforms()) > 0
2181
-                ? $form->get_html_and_js()
2182
-                : '';
2183
-            $this->_template_args['reg_questions_form_action'] = 'edit_registration';
2184
-            $this->_template_args['REG_ID']                    = $this->_registration->ID();
2185
-            $template_path                                     =
2186
-                REG_TEMPLATE_PATH . 'reg_admin_details_main_meta_box_reg_questions.template.php';
2187
-            EEH_Template::display_template($template_path, $this->_template_args);
2188
-        }
2189
-    }
2190
-
2191
-
2192
-    /**
2193
-     * form_before_question_group
2194
-     *
2195
-     * @param string $output
2196
-     * @return        string
2197
-     * @deprecated    as of 4.8.32.rc.000
2198
-     */
2199
-    public function form_before_question_group($output)
2200
-    {
2201
-        EE_Error::doing_it_wrong(
2202
-            __CLASS__ . '::' . __FUNCTION__,
2203
-            esc_html__(
2204
-                'This method would have been protected but was used on a filter callback so needed to be public. Please discontinue usage as it will be removed soon.',
2205
-                'event_espresso'
2206
-            ),
2207
-            '4.8.32.rc.000'
2208
-        );
2209
-        return '
24
+	/**
25
+	 * @var EE_Registration
26
+	 */
27
+	private $_registration;
28
+
29
+	/**
30
+	 * @var EE_Event
31
+	 */
32
+	private $_reg_event;
33
+
34
+	/**
35
+	 * @var EE_Session
36
+	 */
37
+	private $_session;
38
+
39
+	/**
40
+	 * @var array
41
+	 */
42
+	private static $_reg_status;
43
+
44
+	/**
45
+	 * Form for displaying the custom questions for this registration.
46
+	 * This gets used a few times throughout the request so its best to cache it
47
+	 *
48
+	 * @var EE_Registration_Custom_Questions_Form
49
+	 */
50
+	protected $_reg_custom_questions_form = null;
51
+
52
+	/**
53
+	 * @var EEM_Registration $registration_model
54
+	 */
55
+	private $registration_model;
56
+
57
+	/**
58
+	 * @var EEM_Attendee $attendee_model
59
+	 */
60
+	private $attendee_model;
61
+
62
+	/**
63
+	 * @var EEM_Event $event_model
64
+	 */
65
+	private $event_model;
66
+
67
+	/**
68
+	 * @var EEM_Status $status_model
69
+	 */
70
+	private $status_model;
71
+
72
+
73
+	/**
74
+	 * @param bool $routing
75
+	 * @throws EE_Error
76
+	 * @throws InvalidArgumentException
77
+	 * @throws InvalidDataTypeException
78
+	 * @throws InvalidInterfaceException
79
+	 * @throws ReflectionException
80
+	 */
81
+	public function __construct($routing = true)
82
+	{
83
+		parent::__construct($routing);
84
+		add_action('wp_loaded', [$this, 'wp_loaded']);
85
+	}
86
+
87
+
88
+	/**
89
+	 * @return EEM_Registration
90
+	 * @throws InvalidArgumentException
91
+	 * @throws InvalidDataTypeException
92
+	 * @throws InvalidInterfaceException
93
+	 * @since 4.10.2.p
94
+	 */
95
+	protected function getRegistrationModel()
96
+	{
97
+		if (! $this->registration_model instanceof EEM_Registration) {
98
+			$this->registration_model = $this->getLoader()->getShared('EEM_Registration');
99
+		}
100
+		return $this->registration_model;
101
+	}
102
+
103
+
104
+	/**
105
+	 * @return EEM_Attendee
106
+	 * @throws InvalidArgumentException
107
+	 * @throws InvalidDataTypeException
108
+	 * @throws InvalidInterfaceException
109
+	 * @since 4.10.2.p
110
+	 */
111
+	protected function getAttendeeModel()
112
+	{
113
+		if (! $this->attendee_model instanceof EEM_Attendee) {
114
+			$this->attendee_model = $this->getLoader()->getShared('EEM_Attendee');
115
+		}
116
+		return $this->attendee_model;
117
+	}
118
+
119
+
120
+	/**
121
+	 * @return EEM_Event
122
+	 * @throws InvalidArgumentException
123
+	 * @throws InvalidDataTypeException
124
+	 * @throws InvalidInterfaceException
125
+	 * @since 4.10.2.p
126
+	 */
127
+	protected function getEventModel()
128
+	{
129
+		if (! $this->event_model instanceof EEM_Event) {
130
+			$this->event_model = $this->getLoader()->getShared('EEM_Event');
131
+		}
132
+		return $this->event_model;
133
+	}
134
+
135
+
136
+	/**
137
+	 * @return EEM_Status
138
+	 * @throws InvalidArgumentException
139
+	 * @throws InvalidDataTypeException
140
+	 * @throws InvalidInterfaceException
141
+	 * @since 4.10.2.p
142
+	 */
143
+	protected function getStatusModel()
144
+	{
145
+		if (! $this->status_model instanceof EEM_Status) {
146
+			$this->status_model = $this->getLoader()->getShared('EEM_Status');
147
+		}
148
+		return $this->status_model;
149
+	}
150
+
151
+
152
+	public function wp_loaded()
153
+	{
154
+		// when adding a new registration...
155
+		$action = $this->request->getRequestParam('action');
156
+		if ($action === 'new_registration') {
157
+			EE_System::do_not_cache();
158
+			if ($this->request->getRequestParam('processing_registration', 0, 'int') !== 1) {
159
+				// and it's NOT the attendee information reg step
160
+				// force cookie expiration by setting time to last week
161
+				setcookie('ee_registration_added', 0, time() - WEEK_IN_SECONDS, '/');
162
+				// and update the global
163
+				$_COOKIE['ee_registration_added'] = 0;
164
+			}
165
+		}
166
+	}
167
+
168
+
169
+	protected function _init_page_props()
170
+	{
171
+		$this->page_slug        = REG_PG_SLUG;
172
+		$this->_admin_base_url  = REG_ADMIN_URL;
173
+		$this->_admin_base_path = REG_ADMIN;
174
+		$this->page_label       = esc_html__('Registrations', 'event_espresso');
175
+		$this->_cpt_routes      = [
176
+			'add_new_attendee' => 'espresso_attendees',
177
+			'edit_attendee'    => 'espresso_attendees',
178
+			'insert_attendee'  => 'espresso_attendees',
179
+			'update_attendee'  => 'espresso_attendees',
180
+		];
181
+		$this->_cpt_model_names = [
182
+			'add_new_attendee' => 'EEM_Attendee',
183
+			'edit_attendee'    => 'EEM_Attendee',
184
+		];
185
+		$this->_cpt_edit_routes = [
186
+			'espresso_attendees' => 'edit_attendee',
187
+		];
188
+		$this->_pagenow_map     = [
189
+			'add_new_attendee' => 'post-new.php',
190
+			'edit_attendee'    => 'post.php',
191
+			'trash'            => 'post.php',
192
+		];
193
+		add_action('edit_form_after_title', [$this, 'after_title_form_fields'], 10);
194
+		// add filters so that the comment urls don't take users to a confusing 404 page
195
+		add_filter('get_comment_link', [$this, 'clear_comment_link'], 10, 2);
196
+	}
197
+
198
+
199
+	/**
200
+	 * @param string     $link    The comment permalink with '#comment-$id' appended.
201
+	 * @param WP_Comment $comment The current comment object.
202
+	 * @return string
203
+	 */
204
+	public function clear_comment_link($link, WP_Comment $comment)
205
+	{
206
+		// gotta make sure this only happens on this route
207
+		$post_type = get_post_type($comment->comment_post_ID);
208
+		if ($post_type === 'espresso_attendees') {
209
+			return '#commentsdiv';
210
+		}
211
+		return $link;
212
+	}
213
+
214
+
215
+	protected function _ajax_hooks()
216
+	{
217
+		// todo: all hooks for registrations ajax goes in here
218
+		add_action('wp_ajax_toggle_checkin_status', [$this, 'toggle_checkin_status']);
219
+	}
220
+
221
+
222
+	protected function _define_page_props()
223
+	{
224
+		$this->_admin_page_title = $this->page_label;
225
+		$this->_labels           = [
226
+			'buttons'                      => [
227
+				'add-registrant'      => esc_html__('Add New Registration', 'event_espresso'),
228
+				'add-attendee'        => esc_html__('Add Contact', 'event_espresso'),
229
+				'edit'                => esc_html__('Edit Contact', 'event_espresso'),
230
+				'csv_reg_report'      => esc_html__('Registrations CSV Report', 'event_espresso'),
231
+				'contact_list_report' => esc_html__('Contact List Report', 'event_espresso'),
232
+				'contact_list_export' => esc_html__('Export Data', 'event_espresso'),
233
+			],
234
+			'publishbox'                   => [
235
+				'add_new_attendee' => esc_html__('Add Contact Record', 'event_espresso'),
236
+				'edit_attendee'    => esc_html__('Update Contact Record', 'event_espresso'),
237
+			],
238
+			'hide_add_button_on_cpt_route' => [
239
+				'edit_attendee' => true,
240
+			],
241
+		];
242
+	}
243
+
244
+
245
+	/**
246
+	 * grab url requests and route them
247
+	 *
248
+	 * @return void
249
+	 * @throws EE_Error
250
+	 */
251
+	public function _set_page_routes()
252
+	{
253
+		$this->_get_registration_status_array();
254
+		$REG_ID             = $this->request->getRequestParam('_REG_ID', 0, 'int');
255
+		$REG_ID             = $this->request->getRequestParam('reg_status_change_form[REG_ID]', $REG_ID, 'int');
256
+		$ATT_ID             = $this->request->getRequestParam('ATT_ID', 0, 'int');
257
+		$ATT_ID             = $this->request->getRequestParam('post', $ATT_ID, 'int');
258
+		$this->_page_routes = [
259
+			'default'                             => [
260
+				'func'       => [$this, '_registrations_overview_list_table'],
261
+				'capability' => 'ee_read_registrations',
262
+			],
263
+			'view_registration'                   => [
264
+				'func'       => '_registration_details',
265
+				'capability' => 'ee_read_registration',
266
+				'obj_id'     => $REG_ID,
267
+			],
268
+			'edit_registration'                   => [
269
+				'func'               => '_update_attendee_registration_form',
270
+				'noheader'           => true,
271
+				'headers_sent_route' => 'view_registration',
272
+				'capability'         => 'ee_edit_registration',
273
+				'obj_id'             => $REG_ID,
274
+				'_REG_ID'            => $REG_ID,
275
+			],
276
+			'trash_registrations'                 => [
277
+				'func'       => '_trash_or_restore_registrations',
278
+				'args'       => ['trash' => true],
279
+				'noheader'   => true,
280
+				'capability' => 'ee_delete_registrations',
281
+			],
282
+			'restore_registrations'               => [
283
+				'func'       => '_trash_or_restore_registrations',
284
+				'args'       => ['trash' => false],
285
+				'noheader'   => true,
286
+				'capability' => 'ee_delete_registrations',
287
+			],
288
+			'delete_registrations'                => [
289
+				'func'       => '_delete_registrations',
290
+				'noheader'   => true,
291
+				'capability' => 'ee_delete_registrations',
292
+			],
293
+			'new_registration'                    => [
294
+				'func'       => 'new_registration',
295
+				'capability' => 'ee_edit_registrations',
296
+			],
297
+			'process_reg_step'                    => [
298
+				'func'       => 'process_reg_step',
299
+				'noheader'   => true,
300
+				'capability' => 'ee_edit_registrations',
301
+			],
302
+			'redirect_to_txn'                     => [
303
+				'func'       => 'redirect_to_txn',
304
+				'noheader'   => true,
305
+				'capability' => 'ee_edit_registrations',
306
+			],
307
+			'change_reg_status'                   => [
308
+				'func'       => '_change_reg_status',
309
+				'noheader'   => true,
310
+				'capability' => 'ee_edit_registration',
311
+				'obj_id'     => $REG_ID,
312
+			],
313
+			'approve_registration'                => [
314
+				'func'       => 'approve_registration',
315
+				'noheader'   => true,
316
+				'capability' => 'ee_edit_registration',
317
+				'obj_id'     => $REG_ID,
318
+			],
319
+			'approve_and_notify_registration'     => [
320
+				'func'       => 'approve_registration',
321
+				'noheader'   => true,
322
+				'args'       => [true],
323
+				'capability' => 'ee_edit_registration',
324
+				'obj_id'     => $REG_ID,
325
+			],
326
+			'approve_registrations'               => [
327
+				'func'       => 'bulk_action_on_registrations',
328
+				'noheader'   => true,
329
+				'capability' => 'ee_edit_registrations',
330
+				'args'       => ['approve'],
331
+			],
332
+			'approve_and_notify_registrations'    => [
333
+				'func'       => 'bulk_action_on_registrations',
334
+				'noheader'   => true,
335
+				'capability' => 'ee_edit_registrations',
336
+				'args'       => ['approve', true],
337
+			],
338
+			'decline_registration'                => [
339
+				'func'       => 'decline_registration',
340
+				'noheader'   => true,
341
+				'capability' => 'ee_edit_registration',
342
+				'obj_id'     => $REG_ID,
343
+			],
344
+			'decline_and_notify_registration'     => [
345
+				'func'       => 'decline_registration',
346
+				'noheader'   => true,
347
+				'args'       => [true],
348
+				'capability' => 'ee_edit_registration',
349
+				'obj_id'     => $REG_ID,
350
+			],
351
+			'decline_registrations'               => [
352
+				'func'       => 'bulk_action_on_registrations',
353
+				'noheader'   => true,
354
+				'capability' => 'ee_edit_registrations',
355
+				'args'       => ['decline'],
356
+			],
357
+			'decline_and_notify_registrations'    => [
358
+				'func'       => 'bulk_action_on_registrations',
359
+				'noheader'   => true,
360
+				'capability' => 'ee_edit_registrations',
361
+				'args'       => ['decline', true],
362
+			],
363
+			'pending_registration'                => [
364
+				'func'       => 'pending_registration',
365
+				'noheader'   => true,
366
+				'capability' => 'ee_edit_registration',
367
+				'obj_id'     => $REG_ID,
368
+			],
369
+			'pending_and_notify_registration'     => [
370
+				'func'       => 'pending_registration',
371
+				'noheader'   => true,
372
+				'args'       => [true],
373
+				'capability' => 'ee_edit_registration',
374
+				'obj_id'     => $REG_ID,
375
+			],
376
+			'pending_registrations'               => [
377
+				'func'       => 'bulk_action_on_registrations',
378
+				'noheader'   => true,
379
+				'capability' => 'ee_edit_registrations',
380
+				'args'       => ['pending'],
381
+			],
382
+			'pending_and_notify_registrations'    => [
383
+				'func'       => 'bulk_action_on_registrations',
384
+				'noheader'   => true,
385
+				'capability' => 'ee_edit_registrations',
386
+				'args'       => ['pending', true],
387
+			],
388
+			'no_approve_registration'             => [
389
+				'func'       => 'not_approve_registration',
390
+				'noheader'   => true,
391
+				'capability' => 'ee_edit_registration',
392
+				'obj_id'     => $REG_ID,
393
+			],
394
+			'no_approve_and_notify_registration'  => [
395
+				'func'       => 'not_approve_registration',
396
+				'noheader'   => true,
397
+				'args'       => [true],
398
+				'capability' => 'ee_edit_registration',
399
+				'obj_id'     => $REG_ID,
400
+			],
401
+			'no_approve_registrations'            => [
402
+				'func'       => 'bulk_action_on_registrations',
403
+				'noheader'   => true,
404
+				'capability' => 'ee_edit_registrations',
405
+				'args'       => ['not_approve'],
406
+			],
407
+			'no_approve_and_notify_registrations' => [
408
+				'func'       => 'bulk_action_on_registrations',
409
+				'noheader'   => true,
410
+				'capability' => 'ee_edit_registrations',
411
+				'args'       => ['not_approve', true],
412
+			],
413
+			'cancel_registration'                 => [
414
+				'func'       => 'cancel_registration',
415
+				'noheader'   => true,
416
+				'capability' => 'ee_edit_registration',
417
+				'obj_id'     => $REG_ID,
418
+			],
419
+			'cancel_and_notify_registration'      => [
420
+				'func'       => 'cancel_registration',
421
+				'noheader'   => true,
422
+				'args'       => [true],
423
+				'capability' => 'ee_edit_registration',
424
+				'obj_id'     => $REG_ID,
425
+			],
426
+			'cancel_registrations'                => [
427
+				'func'       => 'bulk_action_on_registrations',
428
+				'noheader'   => true,
429
+				'capability' => 'ee_edit_registrations',
430
+				'args'       => ['cancel'],
431
+			],
432
+			'cancel_and_notify_registrations'     => [
433
+				'func'       => 'bulk_action_on_registrations',
434
+				'noheader'   => true,
435
+				'capability' => 'ee_edit_registrations',
436
+				'args'       => ['cancel', true],
437
+			],
438
+			'wait_list_registration'              => [
439
+				'func'       => 'wait_list_registration',
440
+				'noheader'   => true,
441
+				'capability' => 'ee_edit_registration',
442
+				'obj_id'     => $REG_ID,
443
+			],
444
+			'wait_list_and_notify_registration'   => [
445
+				'func'       => 'wait_list_registration',
446
+				'noheader'   => true,
447
+				'args'       => [true],
448
+				'capability' => 'ee_edit_registration',
449
+				'obj_id'     => $REG_ID,
450
+			],
451
+			'contact_list'                        => [
452
+				'func'       => '_attendee_contact_list_table',
453
+				'capability' => 'ee_read_contacts',
454
+			],
455
+			'add_new_attendee'                    => [
456
+				'func' => '_create_new_cpt_item',
457
+				'args' => [
458
+					'new_attendee' => true,
459
+					'capability'   => 'ee_edit_contacts',
460
+				],
461
+			],
462
+			'edit_attendee'                       => [
463
+				'func'       => '_edit_cpt_item',
464
+				'capability' => 'ee_edit_contacts',
465
+				'obj_id'     => $ATT_ID,
466
+			],
467
+			'duplicate_attendee'                  => [
468
+				'func'       => '_duplicate_attendee',
469
+				'noheader'   => true,
470
+				'capability' => 'ee_edit_contacts',
471
+				'obj_id'     => $ATT_ID,
472
+			],
473
+			'insert_attendee'                     => [
474
+				'func'       => '_insert_or_update_attendee',
475
+				'args'       => [
476
+					'new_attendee' => true,
477
+				],
478
+				'noheader'   => true,
479
+				'capability' => 'ee_edit_contacts',
480
+			],
481
+			'update_attendee'                     => [
482
+				'func'       => '_insert_or_update_attendee',
483
+				'args'       => [
484
+					'new_attendee' => false,
485
+				],
486
+				'noheader'   => true,
487
+				'capability' => 'ee_edit_contacts',
488
+				'obj_id'     => $ATT_ID,
489
+			],
490
+			'trash_attendees'                     => [
491
+				'func'       => '_trash_or_restore_attendees',
492
+				'args'       => [
493
+					'trash' => 'true',
494
+				],
495
+				'noheader'   => true,
496
+				'capability' => 'ee_delete_contacts',
497
+			],
498
+			'trash_attendee'                      => [
499
+				'func'       => '_trash_or_restore_attendees',
500
+				'args'       => [
501
+					'trash' => true,
502
+				],
503
+				'noheader'   => true,
504
+				'capability' => 'ee_delete_contacts',
505
+				'obj_id'     => $ATT_ID,
506
+			],
507
+			'restore_attendees'                   => [
508
+				'func'       => '_trash_or_restore_attendees',
509
+				'args'       => [
510
+					'trash' => false,
511
+				],
512
+				'noheader'   => true,
513
+				'capability' => 'ee_delete_contacts',
514
+				'obj_id'     => $ATT_ID,
515
+			],
516
+			'resend_registration'                 => [
517
+				'func'       => '_resend_registration',
518
+				'noheader'   => true,
519
+				'capability' => 'ee_send_message',
520
+			],
521
+			'registrations_report'                => [
522
+				'func'       => [$this, '_registrations_report'],
523
+				'noheader'   => true,
524
+				'capability' => 'ee_read_registrations',
525
+			],
526
+			'contact_list_export'                 => [
527
+				'func'       => '_contact_list_export',
528
+				'noheader'   => true,
529
+				'capability' => 'export',
530
+			],
531
+			'contact_list_report'                 => [
532
+				'func'       => '_contact_list_report',
533
+				'noheader'   => true,
534
+				'capability' => 'ee_read_contacts',
535
+			],
536
+		];
537
+	}
538
+
539
+
540
+	protected function _set_page_config()
541
+	{
542
+		$REG_ID             = $this->request->getRequestParam('_REG_ID', 0, 'int');
543
+		$ATT_ID             = $this->request->getRequestParam('ATT_ID', 0, 'int');
544
+		$this->_page_config = [
545
+			'default'           => [
546
+				'nav'           => [
547
+					'label' => esc_html__('Overview', 'event_espresso'),
548
+					'order' => 5,
549
+				],
550
+				'help_tabs'     => [
551
+					'registrations_overview_help_tab'                       => [
552
+						'title'    => esc_html__('Registrations Overview', 'event_espresso'),
553
+						'filename' => 'registrations_overview',
554
+					],
555
+					'registrations_overview_table_column_headings_help_tab' => [
556
+						'title'    => esc_html__('Registrations Table Column Headings', 'event_espresso'),
557
+						'filename' => 'registrations_overview_table_column_headings',
558
+					],
559
+					'registrations_overview_filters_help_tab'               => [
560
+						'title'    => esc_html__('Registration Filters', 'event_espresso'),
561
+						'filename' => 'registrations_overview_filters',
562
+					],
563
+					'registrations_overview_views_help_tab'                 => [
564
+						'title'    => esc_html__('Registration Views', 'event_espresso'),
565
+						'filename' => 'registrations_overview_views',
566
+					],
567
+					'registrations_regoverview_other_help_tab'              => [
568
+						'title'    => esc_html__('Registrations Other', 'event_espresso'),
569
+						'filename' => 'registrations_overview_other',
570
+					],
571
+				],
572
+				'qtips'         => ['Registration_List_Table_Tips'],
573
+				'list_table'    => 'EE_Registrations_List_Table',
574
+				'require_nonce' => false,
575
+			],
576
+			'view_registration' => [
577
+				'nav'           => [
578
+					'label'      => esc_html__('REG Details', 'event_espresso'),
579
+					'order'      => 15,
580
+					'url'        => $REG_ID
581
+						? add_query_arg(['_REG_ID' => $REG_ID], $this->_current_page_view_url)
582
+						: $this->_admin_base_url,
583
+					'persistent' => false,
584
+				],
585
+				'help_tabs'     => [
586
+					'registrations_details_help_tab'                    => [
587
+						'title'    => esc_html__('Registration Details', 'event_espresso'),
588
+						'filename' => 'registrations_details',
589
+					],
590
+					'registrations_details_table_help_tab'              => [
591
+						'title'    => esc_html__('Registration Details Table', 'event_espresso'),
592
+						'filename' => 'registrations_details_table',
593
+					],
594
+					'registrations_details_form_answers_help_tab'       => [
595
+						'title'    => esc_html__('Registration Form Answers', 'event_espresso'),
596
+						'filename' => 'registrations_details_form_answers',
597
+					],
598
+					'registrations_details_registrant_details_help_tab' => [
599
+						'title'    => esc_html__('Contact Details', 'event_espresso'),
600
+						'filename' => 'registrations_details_registrant_details',
601
+					],
602
+				],
603
+				'metaboxes'     => array_merge(
604
+					$this->_default_espresso_metaboxes,
605
+					['_registration_details_metaboxes']
606
+				),
607
+				'require_nonce' => false,
608
+			],
609
+			'new_registration'  => [
610
+				'nav'           => [
611
+					'label'      => esc_html__('Add New Registration', 'event_espresso'),
612
+					'url'        => '#',
613
+					'order'      => 15,
614
+					'persistent' => false,
615
+				],
616
+				'metaboxes'     => $this->_default_espresso_metaboxes,
617
+				'labels'        => [
618
+					'publishbox' => esc_html__('Save Registration', 'event_espresso'),
619
+				],
620
+				'require_nonce' => false,
621
+			],
622
+			'add_new_attendee'  => [
623
+				'nav'           => [
624
+					'label'      => esc_html__('Add Contact', 'event_espresso'),
625
+					'order'      => 15,
626
+					'persistent' => false,
627
+				],
628
+				'metaboxes'     => array_merge(
629
+					$this->_default_espresso_metaboxes,
630
+					['_publish_post_box', 'attendee_editor_metaboxes']
631
+				),
632
+				'require_nonce' => false,
633
+			],
634
+			'edit_attendee'     => [
635
+				'nav'           => [
636
+					'label'      => esc_html__('Edit Contact', 'event_espresso'),
637
+					'order'      => 15,
638
+					'persistent' => false,
639
+					'url'        => $ATT_ID
640
+						? add_query_arg(['ATT_ID' => $ATT_ID], $this->_current_page_view_url)
641
+						: $this->_admin_base_url,
642
+				],
643
+				'metaboxes'     => ['attendee_editor_metaboxes'],
644
+				'require_nonce' => false,
645
+			],
646
+			'contact_list'      => [
647
+				'nav'           => [
648
+					'label' => esc_html__('Contact List', 'event_espresso'),
649
+					'order' => 20,
650
+				],
651
+				'list_table'    => 'EE_Attendee_Contact_List_Table',
652
+				'help_tabs'     => [
653
+					'registrations_contact_list_help_tab'                       => [
654
+						'title'    => esc_html__('Registrations Contact List', 'event_espresso'),
655
+						'filename' => 'registrations_contact_list',
656
+					],
657
+					'registrations_contact-list_table_column_headings_help_tab' => [
658
+						'title'    => esc_html__('Contact List Table Column Headings', 'event_espresso'),
659
+						'filename' => 'registrations_contact_list_table_column_headings',
660
+					],
661
+					'registrations_contact_list_views_help_tab'                 => [
662
+						'title'    => esc_html__('Contact List Views', 'event_espresso'),
663
+						'filename' => 'registrations_contact_list_views',
664
+					],
665
+					'registrations_contact_list_other_help_tab'                 => [
666
+						'title'    => esc_html__('Contact List Other', 'event_espresso'),
667
+						'filename' => 'registrations_contact_list_other',
668
+					],
669
+				],
670
+				'metaboxes'     => [],
671
+				'require_nonce' => false,
672
+			],
673
+			// override default cpt routes
674
+			'create_new'        => '',
675
+			'edit'              => '',
676
+		];
677
+	}
678
+
679
+
680
+	/**
681
+	 * The below methods aren't used by this class currently
682
+	 */
683
+	protected function _add_screen_options()
684
+	{
685
+	}
686
+
687
+
688
+	protected function _add_feature_pointers()
689
+	{
690
+	}
691
+
692
+
693
+	public function admin_init()
694
+	{
695
+		EE_Registry::$i18n_js_strings['update_att_qstns'] = esc_html__(
696
+			'click "Update Registration Questions" to save your changes',
697
+			'event_espresso'
698
+		);
699
+	}
700
+
701
+
702
+	public function admin_notices()
703
+	{
704
+	}
705
+
706
+
707
+	public function admin_footer_scripts()
708
+	{
709
+	}
710
+
711
+
712
+	/**
713
+	 * get list of registration statuses
714
+	 *
715
+	 * @return void
716
+	 * @throws EE_Error
717
+	 */
718
+	private function _get_registration_status_array()
719
+	{
720
+		self::$_reg_status = EEM_Registration::reg_status_array([], true);
721
+	}
722
+
723
+
724
+	/**
725
+	 * @throws InvalidArgumentException
726
+	 * @throws InvalidDataTypeException
727
+	 * @throws InvalidInterfaceException
728
+	 * @since 4.10.2.p
729
+	 */
730
+	protected function _add_screen_options_default()
731
+	{
732
+		$this->_per_page_screen_option();
733
+	}
734
+
735
+
736
+	/**
737
+	 * @throws InvalidArgumentException
738
+	 * @throws InvalidDataTypeException
739
+	 * @throws InvalidInterfaceException
740
+	 * @since 4.10.2.p
741
+	 */
742
+	protected function _add_screen_options_contact_list()
743
+	{
744
+		$page_title              = $this->_admin_page_title;
745
+		$this->_admin_page_title = esc_html__('Contacts', 'event_espresso');
746
+		$this->_per_page_screen_option();
747
+		$this->_admin_page_title = $page_title;
748
+	}
749
+
750
+
751
+	public function load_scripts_styles()
752
+	{
753
+		// style
754
+		wp_register_style(
755
+			'espresso_reg',
756
+			REG_ASSETS_URL . 'espresso_registrations_admin.css',
757
+			['ee-admin-css'],
758
+			EVENT_ESPRESSO_VERSION
759
+		);
760
+		wp_enqueue_style('espresso_reg');
761
+		// script
762
+		wp_register_script(
763
+			'espresso_reg',
764
+			REG_ASSETS_URL . 'espresso_registrations_admin.js',
765
+			['jquery-ui-datepicker', 'jquery-ui-draggable', 'ee_admin_js'],
766
+			EVENT_ESPRESSO_VERSION,
767
+			true
768
+		);
769
+		wp_enqueue_script('espresso_reg');
770
+	}
771
+
772
+
773
+	/**
774
+	 * @throws EE_Error
775
+	 * @throws InvalidArgumentException
776
+	 * @throws InvalidDataTypeException
777
+	 * @throws InvalidInterfaceException
778
+	 * @throws ReflectionException
779
+	 * @since 4.10.2.p
780
+	 */
781
+	public function load_scripts_styles_edit_attendee()
782
+	{
783
+		// stuff to only show up on our attendee edit details page.
784
+		$attendee_details_translations = [
785
+			'att_publish_text' => sprintf(
786
+			/* translators: The date and time */
787
+				wp_strip_all_tags(__('Created on: %s', 'event_espresso')),
788
+				'<b>' . $this->_cpt_model_obj->get_datetime('ATT_created') . '</b>'
789
+			),
790
+		];
791
+		wp_localize_script('espresso_reg', 'ATTENDEE_DETAILS', $attendee_details_translations);
792
+		wp_enqueue_script('jquery-validate');
793
+	}
794
+
795
+
796
+	/**
797
+	 * @throws EE_Error
798
+	 * @throws InvalidArgumentException
799
+	 * @throws InvalidDataTypeException
800
+	 * @throws InvalidInterfaceException
801
+	 * @throws ReflectionException
802
+	 * @since 4.10.2.p
803
+	 */
804
+	public function load_scripts_styles_view_registration()
805
+	{
806
+		// styles
807
+		wp_enqueue_style('espresso-ui-theme');
808
+		// scripts
809
+		$this->_get_reg_custom_questions_form($this->_registration->ID());
810
+		$this->_reg_custom_questions_form->wp_enqueue_scripts();
811
+	}
812
+
813
+
814
+	public function load_scripts_styles_contact_list()
815
+	{
816
+		wp_dequeue_style('espresso_reg');
817
+		wp_register_style(
818
+			'espresso_att',
819
+			REG_ASSETS_URL . 'espresso_attendees_admin.css',
820
+			['ee-admin-css'],
821
+			EVENT_ESPRESSO_VERSION
822
+		);
823
+		wp_enqueue_style('espresso_att');
824
+	}
825
+
826
+
827
+	public function load_scripts_styles_new_registration()
828
+	{
829
+		wp_register_script(
830
+			'ee-spco-for-admin',
831
+			REG_ASSETS_URL . 'spco_for_admin.js',
832
+			['underscore', 'jquery'],
833
+			EVENT_ESPRESSO_VERSION,
834
+			true
835
+		);
836
+		wp_enqueue_script('ee-spco-for-admin');
837
+		add_filter('FHEE__EED_Ticket_Selector__load_tckt_slctr_assets', '__return_true');
838
+		EE_Form_Section_Proper::wp_enqueue_scripts();
839
+		EED_Ticket_Selector::load_tckt_slctr_assets();
840
+		EE_Datepicker_Input::enqueue_styles_and_scripts();
841
+	}
842
+
843
+
844
+	public function AHEE__EE_Admin_Page__route_admin_request_resend_registration()
845
+	{
846
+		add_filter('FHEE_load_EE_messages', '__return_true');
847
+	}
848
+
849
+
850
+	public function AHEE__EE_Admin_Page__route_admin_request_approve_registration()
851
+	{
852
+		add_filter('FHEE_load_EE_messages', '__return_true');
853
+	}
854
+
855
+
856
+	/**
857
+	 * @throws EE_Error
858
+	 * @throws InvalidArgumentException
859
+	 * @throws InvalidDataTypeException
860
+	 * @throws InvalidInterfaceException
861
+	 * @throws ReflectionException
862
+	 * @since 4.10.2.p
863
+	 */
864
+	protected function _set_list_table_views_default()
865
+	{
866
+		// for notification related bulk actions we need to make sure only active messengers have an option.
867
+		EED_Messages::set_autoloaders();
868
+		/** @type EE_Message_Resource_Manager $message_resource_manager */
869
+		$message_resource_manager = EE_Registry::instance()->load_lib('Message_Resource_Manager');
870
+		$active_mts               = $message_resource_manager->list_of_active_message_types();
871
+		// key= bulk_action_slug, value= message type.
872
+		$match_array = [
873
+			'approve_registrations'    => 'registration',
874
+			'decline_registrations'    => 'declined_registration',
875
+			'pending_registrations'    => 'pending_approval',
876
+			'no_approve_registrations' => 'not_approved_registration',
877
+			'cancel_registrations'     => 'cancelled_registration',
878
+		];
879
+		$can_send    = EE_Registry::instance()->CAP->current_user_can(
880
+			'ee_send_message',
881
+			'batch_send_messages'
882
+		);
883
+		/** setup reg status bulk actions **/
884
+		$def_reg_status_actions['approve_registrations'] = esc_html__('Approve Registrations', 'event_espresso');
885
+		if ($can_send && in_array($match_array['approve_registrations'], $active_mts, true)) {
886
+			$def_reg_status_actions['approve_and_notify_registrations'] = esc_html__(
887
+				'Approve and Notify Registrations',
888
+				'event_espresso'
889
+			);
890
+		}
891
+		$def_reg_status_actions['decline_registrations'] = esc_html__('Decline Registrations', 'event_espresso');
892
+		if ($can_send && in_array($match_array['decline_registrations'], $active_mts, true)) {
893
+			$def_reg_status_actions['decline_and_notify_registrations'] = esc_html__(
894
+				'Decline and Notify Registrations',
895
+				'event_espresso'
896
+			);
897
+		}
898
+		$def_reg_status_actions['pending_registrations'] = esc_html__(
899
+			'Set Registrations to Pending Payment',
900
+			'event_espresso'
901
+		);
902
+		if ($can_send && in_array($match_array['pending_registrations'], $active_mts, true)) {
903
+			$def_reg_status_actions['pending_and_notify_registrations'] = esc_html__(
904
+				'Set Registrations to Pending Payment and Notify',
905
+				'event_espresso'
906
+			);
907
+		}
908
+		$def_reg_status_actions['no_approve_registrations'] = esc_html__(
909
+			'Set Registrations to Not Approved',
910
+			'event_espresso'
911
+		);
912
+		if ($can_send && in_array($match_array['no_approve_registrations'], $active_mts, true)) {
913
+			$def_reg_status_actions['no_approve_and_notify_registrations'] = esc_html__(
914
+				'Set Registrations to Not Approved and Notify',
915
+				'event_espresso'
916
+			);
917
+		}
918
+		$def_reg_status_actions['cancel_registrations'] = esc_html__('Cancel Registrations', 'event_espresso');
919
+		if ($can_send && in_array($match_array['cancel_registrations'], $active_mts, true)) {
920
+			$def_reg_status_actions['cancel_and_notify_registrations'] = esc_html__(
921
+				'Cancel Registrations and Notify',
922
+				'event_espresso'
923
+			);
924
+		}
925
+		$def_reg_status_actions = apply_filters(
926
+			'FHEE__Registrations_Admin_Page___set_list_table_views_default__def_reg_status_actions_array',
927
+			$def_reg_status_actions,
928
+			$active_mts,
929
+			$can_send
930
+		);
931
+
932
+		$this->_views = [
933
+			'all'   => [
934
+				'slug'        => 'all',
935
+				'label'       => esc_html__('View All Registrations', 'event_espresso'),
936
+				'count'       => 0,
937
+				'bulk_action' => array_merge(
938
+					$def_reg_status_actions,
939
+					[
940
+						'trash_registrations' => esc_html__('Trash Registrations', 'event_espresso'),
941
+					]
942
+				),
943
+			],
944
+			'month' => [
945
+				'slug'        => 'month',
946
+				'label'       => esc_html__('This Month', 'event_espresso'),
947
+				'count'       => 0,
948
+				'bulk_action' => array_merge(
949
+					$def_reg_status_actions,
950
+					[
951
+						'trash_registrations' => esc_html__('Trash Registrations', 'event_espresso'),
952
+					]
953
+				),
954
+			],
955
+			'today' => [
956
+				'slug'        => 'today',
957
+				'label'       => sprintf(
958
+					esc_html__('Today - %s', 'event_espresso'),
959
+					date('M d, Y', current_time('timestamp'))
960
+				),
961
+				'count'       => 0,
962
+				'bulk_action' => array_merge(
963
+					$def_reg_status_actions,
964
+					[
965
+						'trash_registrations' => esc_html__('Trash Registrations', 'event_espresso'),
966
+					]
967
+				),
968
+			],
969
+		];
970
+		if (
971
+			EE_Registry::instance()->CAP->current_user_can(
972
+				'ee_delete_registrations',
973
+				'espresso_registrations_delete_registration'
974
+			)
975
+		) {
976
+			$this->_views['incomplete'] = [
977
+				'slug'        => 'incomplete',
978
+				'label'       => esc_html__('Incomplete', 'event_espresso'),
979
+				'count'       => 0,
980
+				'bulk_action' => [
981
+					'trash_registrations' => esc_html__('Trash Registrations', 'event_espresso'),
982
+				],
983
+			];
984
+			$this->_views['trash']      = [
985
+				'slug'        => 'trash',
986
+				'label'       => esc_html__('Trash', 'event_espresso'),
987
+				'count'       => 0,
988
+				'bulk_action' => [
989
+					'restore_registrations' => esc_html__('Restore Registrations', 'event_espresso'),
990
+					'delete_registrations'  => esc_html__('Delete Registrations Permanently', 'event_espresso'),
991
+				],
992
+			];
993
+		}
994
+	}
995
+
996
+
997
+	protected function _set_list_table_views_contact_list()
998
+	{
999
+		$this->_views = [
1000
+			'in_use' => [
1001
+				'slug'        => 'in_use',
1002
+				'label'       => esc_html__('In Use', 'event_espresso'),
1003
+				'count'       => 0,
1004
+				'bulk_action' => [
1005
+					'trash_attendees' => esc_html__('Move to Trash', 'event_espresso'),
1006
+				],
1007
+			],
1008
+		];
1009
+		if (
1010
+			EE_Registry::instance()->CAP->current_user_can(
1011
+				'ee_delete_contacts',
1012
+				'espresso_registrations_trash_attendees'
1013
+			)
1014
+		) {
1015
+			$this->_views['trash'] = [
1016
+				'slug'        => 'trash',
1017
+				'label'       => esc_html__('Trash', 'event_espresso'),
1018
+				'count'       => 0,
1019
+				'bulk_action' => [
1020
+					'restore_attendees' => esc_html__('Restore from Trash', 'event_espresso'),
1021
+				],
1022
+			];
1023
+		}
1024
+	}
1025
+
1026
+
1027
+	/**
1028
+	 * @return array
1029
+	 * @throws EE_Error
1030
+	 */
1031
+	protected function _registration_legend_items()
1032
+	{
1033
+		$fc_items = [
1034
+			'star-icon'        => [
1035
+				'class' => 'dashicons dashicons-star-filled lt-blue-icon ee-icon-size-8',
1036
+				'desc'  => esc_html__('This is the Primary Registrant', 'event_espresso'),
1037
+			],
1038
+			'view_details'     => [
1039
+				'class' => 'dashicons dashicons-clipboard',
1040
+				'desc'  => esc_html__('View Registration Details', 'event_espresso'),
1041
+			],
1042
+			'edit_attendee'    => [
1043
+				'class' => 'ee-icon ee-icon-user-edit ee-icon-size-16',
1044
+				'desc'  => esc_html__('Edit Contact Details', 'event_espresso'),
1045
+			],
1046
+			'view_transaction' => [
1047
+				'class' => 'dashicons dashicons-cart',
1048
+				'desc'  => esc_html__('View Transaction Details', 'event_espresso'),
1049
+			],
1050
+			'view_invoice'     => [
1051
+				'class' => 'dashicons dashicons-media-spreadsheet',
1052
+				'desc'  => esc_html__('View Transaction Invoice', 'event_espresso'),
1053
+			],
1054
+		];
1055
+		if (
1056
+			EE_Registry::instance()->CAP->current_user_can(
1057
+				'ee_send_message',
1058
+				'espresso_registrations_resend_registration'
1059
+			)
1060
+		) {
1061
+			$fc_items['resend_registration'] = [
1062
+				'class' => 'dashicons dashicons-email-alt',
1063
+				'desc'  => esc_html__('Resend Registration Details', 'event_espresso'),
1064
+			];
1065
+		} else {
1066
+			$fc_items['blank'] = ['class' => 'blank', 'desc' => ''];
1067
+		}
1068
+		if (
1069
+			EE_Registry::instance()->CAP->current_user_can(
1070
+				'ee_read_global_messages',
1071
+				'view_filtered_messages'
1072
+			)
1073
+		) {
1074
+			$related_for_icon = EEH_MSG_Template::get_message_action_icon('see_notifications_for');
1075
+			if (is_array($related_for_icon) && isset($related_for_icon['css_class'], $related_for_icon['label'])) {
1076
+				$fc_items['view_related_messages'] = [
1077
+					'class' => $related_for_icon['css_class'],
1078
+					'desc'  => $related_for_icon['label'],
1079
+				];
1080
+			}
1081
+		}
1082
+		$sc_items = [
1083
+			'approved_status'   => [
1084
+				'class' => 'ee-status-legend ee-status-legend-' . EEM_Registration::status_id_approved,
1085
+				'desc'  => EEH_Template::pretty_status(
1086
+					EEM_Registration::status_id_approved,
1087
+					false,
1088
+					'sentence'
1089
+				),
1090
+			],
1091
+			'pending_status'    => [
1092
+				'class' => 'ee-status-legend ee-status-legend-' . EEM_Registration::status_id_pending_payment,
1093
+				'desc'  => EEH_Template::pretty_status(
1094
+					EEM_Registration::status_id_pending_payment,
1095
+					false,
1096
+					'sentence'
1097
+				),
1098
+			],
1099
+			'wait_list'         => [
1100
+				'class' => 'ee-status-legend ee-status-legend-' . EEM_Registration::status_id_wait_list,
1101
+				'desc'  => EEH_Template::pretty_status(
1102
+					EEM_Registration::status_id_wait_list,
1103
+					false,
1104
+					'sentence'
1105
+				),
1106
+			],
1107
+			'incomplete_status' => [
1108
+				'class' => 'ee-status-legend ee-status-legend-' . EEM_Registration::status_id_incomplete,
1109
+				'desc'  => EEH_Template::pretty_status(
1110
+					EEM_Registration::status_id_incomplete,
1111
+					false,
1112
+					'sentence'
1113
+				),
1114
+			],
1115
+			'not_approved'      => [
1116
+				'class' => 'ee-status-legend ee-status-legend-' . EEM_Registration::status_id_not_approved,
1117
+				'desc'  => EEH_Template::pretty_status(
1118
+					EEM_Registration::status_id_not_approved,
1119
+					false,
1120
+					'sentence'
1121
+				),
1122
+			],
1123
+			'declined_status'   => [
1124
+				'class' => 'ee-status-legend ee-status-legend-' . EEM_Registration::status_id_declined,
1125
+				'desc'  => EEH_Template::pretty_status(
1126
+					EEM_Registration::status_id_declined,
1127
+					false,
1128
+					'sentence'
1129
+				),
1130
+			],
1131
+			'cancelled_status'  => [
1132
+				'class' => 'ee-status-legend ee-status-legend-' . EEM_Registration::status_id_cancelled,
1133
+				'desc'  => EEH_Template::pretty_status(
1134
+					EEM_Registration::status_id_cancelled,
1135
+					false,
1136
+					'sentence'
1137
+				),
1138
+			],
1139
+		];
1140
+		return array_merge($fc_items, $sc_items);
1141
+	}
1142
+
1143
+
1144
+
1145
+	/***************************************        REGISTRATION OVERVIEW        **************************************/
1146
+
1147
+
1148
+	/**
1149
+	 * @throws DomainException
1150
+	 * @throws EE_Error
1151
+	 * @throws InvalidArgumentException
1152
+	 * @throws InvalidDataTypeException
1153
+	 * @throws InvalidInterfaceException
1154
+	 */
1155
+	protected function _registrations_overview_list_table()
1156
+	{
1157
+		$this->appendAddNewRegistrationButtonToPageTitle();
1158
+		$header_text                  = '';
1159
+		$admin_page_header_decorators = [
1160
+			'EventEspresso\core\domain\services\admin\registrations\list_table\page_header\AttendeeFilterHeader',
1161
+			'EventEspresso\core\domain\services\admin\registrations\list_table\page_header\EventFilterHeader',
1162
+			'EventEspresso\core\domain\services\admin\registrations\list_table\page_header\DateFilterHeader',
1163
+			'EventEspresso\core\domain\services\admin\registrations\list_table\page_header\TicketFilterHeader',
1164
+		];
1165
+		foreach ($admin_page_header_decorators as $admin_page_header_decorator) {
1166
+			$filter_header_decorator = $this->getLoader()->getNew($admin_page_header_decorator);
1167
+			$header_text             = $filter_header_decorator->getHeaderText($header_text);
1168
+		}
1169
+		$this->_template_args['admin_page_header'] = $header_text;
1170
+		$this->_template_args['after_list_table']  = $this->_display_legend($this->_registration_legend_items());
1171
+		$this->display_admin_list_table_page_with_no_sidebar();
1172
+	}
1173
+
1174
+
1175
+	/**
1176
+	 * @throws EE_Error
1177
+	 * @throws InvalidArgumentException
1178
+	 * @throws InvalidDataTypeException
1179
+	 * @throws InvalidInterfaceException
1180
+	 */
1181
+	private function appendAddNewRegistrationButtonToPageTitle()
1182
+	{
1183
+		$EVT_ID = $this->request->getRequestParam('event_id', 0, 'int');
1184
+		if (
1185
+			$EVT_ID
1186
+			&& EE_Registry::instance()->CAP->current_user_can(
1187
+				'ee_edit_registrations',
1188
+				'espresso_registrations_new_registration',
1189
+				$EVT_ID
1190
+			)
1191
+		) {
1192
+			$this->_admin_page_title .= ' ' . $this->get_action_link_or_button(
1193
+				'new_registration',
1194
+				'add-registrant',
1195
+				['event_id' => $EVT_ID],
1196
+				'add-new-h2'
1197
+			);
1198
+		}
1199
+	}
1200
+
1201
+
1202
+	/**
1203
+	 * This sets the _registration property for the registration details screen
1204
+	 *
1205
+	 * @return void
1206
+	 * @throws EE_Error
1207
+	 * @throws InvalidArgumentException
1208
+	 * @throws InvalidDataTypeException
1209
+	 * @throws InvalidInterfaceException
1210
+	 */
1211
+	private function _set_registration_object()
1212
+	{
1213
+		// get out if we've already set the object
1214
+		if ($this->_registration instanceof EE_Registration) {
1215
+			return;
1216
+		}
1217
+		$REG_ID = $this->request->getRequestParam('_REG_ID', 0, 'int');
1218
+		if ($this->_registration = $this->getRegistrationModel()->get_one_by_ID($REG_ID)) {
1219
+			return;
1220
+		}
1221
+		$error_msg = sprintf(
1222
+			esc_html__(
1223
+				'An error occurred and the details for Registration ID #%s could not be retrieved.',
1224
+				'event_espresso'
1225
+			),
1226
+			$REG_ID
1227
+		);
1228
+		EE_Error::add_error($error_msg, __FILE__, __FUNCTION__, __LINE__);
1229
+		$this->_registration = null;
1230
+	}
1231
+
1232
+
1233
+	/**
1234
+	 * Used to retrieve registrations for the list table.
1235
+	 *
1236
+	 * @param int  $per_page
1237
+	 * @param bool $count
1238
+	 * @param bool $this_month
1239
+	 * @param bool $today
1240
+	 * @return EE_Registration[]|int
1241
+	 * @throws EE_Error
1242
+	 * @throws InvalidArgumentException
1243
+	 * @throws InvalidDataTypeException
1244
+	 * @throws InvalidInterfaceException
1245
+	 */
1246
+	public function get_registrations(
1247
+		$per_page = 10,
1248
+		$count = false,
1249
+		$this_month = false,
1250
+		$today = false
1251
+	) {
1252
+		if ($this_month) {
1253
+			$this->request->setRequestParam('status', 'month');
1254
+		}
1255
+		if ($today) {
1256
+			$this->request->setRequestParam('status', 'today');
1257
+		}
1258
+		$query_params = $this->_get_registration_query_parameters([], $per_page, $count);
1259
+		/**
1260
+		 * Override the default groupby added by EEM_Base so that sorts with multiple order bys work as expected
1261
+		 *
1262
+		 * @link https://events.codebasehq.com/projects/event-espresso/tickets/10093
1263
+		 * @see  https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1264
+		 *                      or if you have the development copy of EE you can view this at the path:
1265
+		 *                      /docs/G--Model-System/model-query-params.md
1266
+		 */
1267
+		$query_params['group_by'] = '';
1268
+
1269
+		return $count
1270
+			? $this->getRegistrationModel()->count($query_params)
1271
+			/** @type EE_Registration[] */
1272
+			: $this->getRegistrationModel()->get_all($query_params);
1273
+	}
1274
+
1275
+
1276
+	/**
1277
+	 * Retrieves the query parameters to be used by the Registration model for getting registrations.
1278
+	 * Note: this listens to values on the request for some of the query parameters.
1279
+	 *
1280
+	 * @param array $request
1281
+	 * @param int   $per_page
1282
+	 * @param bool  $count
1283
+	 * @return array
1284
+	 * @throws EE_Error
1285
+	 * @throws InvalidArgumentException
1286
+	 * @throws InvalidDataTypeException
1287
+	 * @throws InvalidInterfaceException
1288
+	 */
1289
+	protected function _get_registration_query_parameters(
1290
+		$request = [],
1291
+		$per_page = 10,
1292
+		$count = false
1293
+	) {
1294
+		/** @var EventEspresso\core\domain\services\admin\registrations\list_table\QueryBuilder $list_table_query_builder */
1295
+		$list_table_query_builder = $this->getLoader()->getNew(
1296
+			'EventEspresso\core\domain\services\admin\registrations\list_table\QueryBuilder',
1297
+			[null, null, $request]
1298
+		);
1299
+		return $list_table_query_builder->getQueryParams($per_page, $count);
1300
+	}
1301
+
1302
+
1303
+	public function get_registration_status_array()
1304
+	{
1305
+		return self::$_reg_status;
1306
+	}
1307
+
1308
+
1309
+
1310
+
1311
+	/***************************************        REGISTRATION DETAILS        ***************************************/
1312
+	/**
1313
+	 * generates HTML for the View Registration Details Admin page
1314
+	 *
1315
+	 * @return void
1316
+	 * @throws DomainException
1317
+	 * @throws EE_Error
1318
+	 * @throws InvalidArgumentException
1319
+	 * @throws InvalidDataTypeException
1320
+	 * @throws InvalidInterfaceException
1321
+	 * @throws EntityNotFoundException
1322
+	 * @throws ReflectionException
1323
+	 */
1324
+	protected function _registration_details()
1325
+	{
1326
+		$this->_template_args = [];
1327
+		$this->_set_registration_object();
1328
+		if (is_object($this->_registration)) {
1329
+			$transaction                                   = $this->_registration->transaction()
1330
+				? $this->_registration->transaction()
1331
+				: EE_Transaction::new_instance();
1332
+			$this->_session                                = $transaction->session_data();
1333
+			$event_id                                      = $this->_registration->event_ID();
1334
+			$this->_template_args['reg_nmbr']['value']     = $this->_registration->ID();
1335
+			$this->_template_args['reg_nmbr']['label']     = esc_html__('Registration Number', 'event_espresso');
1336
+			$this->_template_args['reg_datetime']['value'] = $this->_registration->get_i18n_datetime('REG_date');
1337
+			$this->_template_args['reg_datetime']['label'] = esc_html__('Date', 'event_espresso');
1338
+			$this->_template_args['grand_total']           = $transaction->total();
1339
+			$this->_template_args['currency_sign']         = EE_Registry::instance()->CFG->currency->sign;
1340
+			// link back to overview
1341
+			$this->_template_args['reg_overview_url']            = REG_ADMIN_URL;
1342
+			$this->_template_args['registration']                = $this->_registration;
1343
+			$this->_template_args['filtered_registrations_link'] = EE_Admin_Page::add_query_args_and_nonce(
1344
+				[
1345
+					'action'   => 'default',
1346
+					'event_id' => $event_id,
1347
+				],
1348
+				REG_ADMIN_URL
1349
+			);
1350
+			$this->_template_args['filtered_transactions_link']  = EE_Admin_Page::add_query_args_and_nonce(
1351
+				[
1352
+					'action' => 'default',
1353
+					'EVT_ID' => $event_id,
1354
+					'page'   => 'espresso_transactions',
1355
+				],
1356
+				admin_url('admin.php')
1357
+			);
1358
+			$this->_template_args['event_link']                  = EE_Admin_Page::add_query_args_and_nonce(
1359
+				[
1360
+					'page'   => 'espresso_events',
1361
+					'action' => 'edit',
1362
+					'post'   => $event_id,
1363
+				],
1364
+				admin_url('admin.php')
1365
+			);
1366
+			// next and previous links
1367
+			$next_reg                                      = $this->_registration->next(
1368
+				null,
1369
+				[],
1370
+				'REG_ID'
1371
+			);
1372
+			$this->_template_args['next_registration']     = $next_reg
1373
+				? $this->_next_link(
1374
+					EE_Admin_Page::add_query_args_and_nonce(
1375
+						[
1376
+							'action'  => 'view_registration',
1377
+							'_REG_ID' => $next_reg['REG_ID'],
1378
+						],
1379
+						REG_ADMIN_URL
1380
+					),
1381
+					'dashicons dashicons-arrow-right ee-icon-size-22'
1382
+				)
1383
+				: '';
1384
+			$previous_reg                                  = $this->_registration->previous(
1385
+				null,
1386
+				[],
1387
+				'REG_ID'
1388
+			);
1389
+			$this->_template_args['previous_registration'] = $previous_reg
1390
+				? $this->_previous_link(
1391
+					EE_Admin_Page::add_query_args_and_nonce(
1392
+						[
1393
+							'action'  => 'view_registration',
1394
+							'_REG_ID' => $previous_reg['REG_ID'],
1395
+						],
1396
+						REG_ADMIN_URL
1397
+					),
1398
+					'dashicons dashicons-arrow-left ee-icon-size-22'
1399
+				)
1400
+				: '';
1401
+			// grab header
1402
+			$template_path                             = REG_TEMPLATE_PATH . 'reg_admin_details_header.template.php';
1403
+			$this->_template_args['REG_ID']            = $this->_registration->ID();
1404
+			$this->_template_args['admin_page_header'] = EEH_Template::display_template(
1405
+				$template_path,
1406
+				$this->_template_args,
1407
+				true
1408
+			);
1409
+		} else {
1410
+			$this->_template_args['admin_page_header'] = '';
1411
+			$this->_display_espresso_notices();
1412
+		}
1413
+		// the details template wrapper
1414
+		$this->display_admin_page_with_sidebar();
1415
+	}
1416
+
1417
+
1418
+	/**
1419
+	 * @throws EE_Error
1420
+	 * @throws InvalidArgumentException
1421
+	 * @throws InvalidDataTypeException
1422
+	 * @throws InvalidInterfaceException
1423
+	 * @throws ReflectionException
1424
+	 * @since 4.10.2.p
1425
+	 */
1426
+	protected function _registration_details_metaboxes()
1427
+	{
1428
+		do_action('AHEE__Registrations_Admin_Page___registration_details_metabox__start', $this);
1429
+		$this->_set_registration_object();
1430
+		$attendee = $this->_registration instanceof EE_Registration ? $this->_registration->attendee() : null;
1431
+		add_meta_box(
1432
+			'edit-reg-status-mbox',
1433
+			esc_html__('Registration Status', 'event_espresso'),
1434
+			[$this, 'set_reg_status_buttons_metabox'],
1435
+			$this->_wp_page_slug,
1436
+			'normal',
1437
+			'high'
1438
+		);
1439
+		add_meta_box(
1440
+			'edit-reg-details-mbox',
1441
+			esc_html__('Registration Details', 'event_espresso'),
1442
+			[$this, '_reg_details_meta_box'],
1443
+			$this->_wp_page_slug,
1444
+			'normal',
1445
+			'high'
1446
+		);
1447
+		if (
1448
+			$attendee instanceof EE_Attendee
1449
+			&& EE_Registry::instance()->CAP->current_user_can(
1450
+				'ee_read_registration',
1451
+				'edit-reg-questions-mbox',
1452
+				$this->_registration->ID()
1453
+			)
1454
+		) {
1455
+			add_meta_box(
1456
+				'edit-reg-questions-mbox',
1457
+				esc_html__('Registration Form Answers', 'event_espresso'),
1458
+				[$this, '_reg_questions_meta_box'],
1459
+				$this->_wp_page_slug,
1460
+				'normal',
1461
+				'high'
1462
+			);
1463
+		}
1464
+		add_meta_box(
1465
+			'edit-reg-registrant-mbox',
1466
+			esc_html__('Contact Details', 'event_espresso'),
1467
+			[$this, '_reg_registrant_side_meta_box'],
1468
+			$this->_wp_page_slug,
1469
+			'side',
1470
+			'high'
1471
+		);
1472
+		if ($this->_registration->group_size() > 1) {
1473
+			add_meta_box(
1474
+				'edit-reg-attendees-mbox',
1475
+				esc_html__('Other Registrations in this Transaction', 'event_espresso'),
1476
+				[$this, '_reg_attendees_meta_box'],
1477
+				$this->_wp_page_slug,
1478
+				'normal',
1479
+				'high'
1480
+			);
1481
+		}
1482
+	}
1483
+
1484
+
1485
+	/**
1486
+	 * set_reg_status_buttons_metabox
1487
+	 *
1488
+	 * @return void
1489
+	 * @throws EE_Error
1490
+	 * @throws EntityNotFoundException
1491
+	 * @throws InvalidArgumentException
1492
+	 * @throws InvalidDataTypeException
1493
+	 * @throws InvalidInterfaceException
1494
+	 * @throws ReflectionException
1495
+	 */
1496
+	public function set_reg_status_buttons_metabox()
1497
+	{
1498
+		$this->_set_registration_object();
1499
+		$change_reg_status_form = $this->_generate_reg_status_change_form();
1500
+		$output                 = $change_reg_status_form->form_open(
1501
+			self::add_query_args_and_nonce(
1502
+				[
1503
+					'action' => 'change_reg_status',
1504
+				],
1505
+				REG_ADMIN_URL
1506
+			)
1507
+		);
1508
+		$output                 .= $change_reg_status_form->get_html();
1509
+		$output                 .= $change_reg_status_form->form_close();
1510
+		echo wp_kses($output, AllowedTags::getWithFormTags());
1511
+	}
1512
+
1513
+
1514
+	/**
1515
+	 * @return EE_Form_Section_Proper
1516
+	 * @throws EE_Error
1517
+	 * @throws InvalidArgumentException
1518
+	 * @throws InvalidDataTypeException
1519
+	 * @throws InvalidInterfaceException
1520
+	 * @throws EntityNotFoundException
1521
+	 * @throws ReflectionException
1522
+	 */
1523
+	protected function _generate_reg_status_change_form()
1524
+	{
1525
+		$reg_status_change_form_array = [
1526
+			'name'            => 'reg_status_change_form',
1527
+			'html_id'         => 'reg-status-change-form',
1528
+			'layout_strategy' => new EE_Admin_Two_Column_Layout(),
1529
+			'subsections'     => [
1530
+				'return'         => new EE_Hidden_Input(
1531
+					[
1532
+						'name'    => 'return',
1533
+						'default' => 'view_registration',
1534
+					]
1535
+				),
1536
+				'REG_ID'         => new EE_Hidden_Input(
1537
+					[
1538
+						'name'    => 'REG_ID',
1539
+						'default' => $this->_registration->ID(),
1540
+					]
1541
+				),
1542
+				'current_status' => new EE_Form_Section_HTML(
1543
+					EEH_HTML::table(
1544
+						EEH_HTML::tr(
1545
+							EEH_HTML::th(
1546
+								EEH_HTML::label(
1547
+									EEH_HTML::strong(
1548
+										esc_html__('Current Registration Status', 'event_espresso')
1549
+									)
1550
+								)
1551
+							)
1552
+							. EEH_HTML::td(
1553
+								EEH_HTML::strong(
1554
+									$this->_registration->pretty_status(),
1555
+									'',
1556
+									'status-' . $this->_registration->status_ID(),
1557
+									'line-height: 1em; font-size: 1.5em; font-weight: bold;'
1558
+								)
1559
+							)
1560
+						)
1561
+					)
1562
+				),
1563
+			],
1564
+		];
1565
+		if (
1566
+			EE_Registry::instance()->CAP->current_user_can(
1567
+				'ee_edit_registration',
1568
+				'toggle_registration_status',
1569
+				$this->_registration->ID()
1570
+			)
1571
+		) {
1572
+			$reg_status_change_form_array['subsections']['reg_status']         = new EE_Select_Input(
1573
+				$this->_get_reg_statuses(),
1574
+				[
1575
+					'html_label_text' => esc_html__('Change Registration Status to', 'event_espresso'),
1576
+					'default'         => $this->_registration->status_ID(),
1577
+				]
1578
+			);
1579
+			$reg_status_change_form_array['subsections']['send_notifications'] = new EE_Yes_No_Input(
1580
+				[
1581
+					'html_label_text' => esc_html__('Send Related Messages', 'event_espresso'),
1582
+					'default'         => false,
1583
+					'html_help_text'  => esc_html__(
1584
+						'If set to "Yes", then the related messages will be sent to the registrant.',
1585
+						'event_espresso'
1586
+					),
1587
+				]
1588
+			);
1589
+			$reg_status_change_form_array['subsections']['submit']             = new EE_Submit_Input(
1590
+				[
1591
+					'html_class'      => 'button-primary',
1592
+					'html_label_text' => '&nbsp;',
1593
+					'default'         => esc_html__('Update Registration Status', 'event_espresso'),
1594
+				]
1595
+			);
1596
+		}
1597
+		return new EE_Form_Section_Proper($reg_status_change_form_array);
1598
+	}
1599
+
1600
+
1601
+	/**
1602
+	 * Returns an array of all the buttons for the various statuses and switch status actions
1603
+	 *
1604
+	 * @return array
1605
+	 * @throws EE_Error
1606
+	 * @throws InvalidArgumentException
1607
+	 * @throws InvalidDataTypeException
1608
+	 * @throws InvalidInterfaceException
1609
+	 * @throws EntityNotFoundException
1610
+	 */
1611
+	protected function _get_reg_statuses()
1612
+	{
1613
+		$reg_status_array = $this->getRegistrationModel()->reg_status_array();
1614
+		unset($reg_status_array[ EEM_Registration::status_id_incomplete ]);
1615
+		// get current reg status
1616
+		$current_status = $this->_registration->status_ID();
1617
+		// is registration for free event? This will determine whether to display the pending payment option
1618
+		if (
1619
+			$current_status !== EEM_Registration::status_id_pending_payment
1620
+			&& EEH_Money::compare_floats($this->_registration->ticket()->price(), 0.00)
1621
+		) {
1622
+			unset($reg_status_array[ EEM_Registration::status_id_pending_payment ]);
1623
+		}
1624
+		return $this->getStatusModel()->localized_status($reg_status_array, false, 'sentence');
1625
+	}
1626
+
1627
+
1628
+	/**
1629
+	 * This method is used when using _REG_ID from request which may or may not be an array of reg_ids.
1630
+	 *
1631
+	 * @param bool $status REG status given for changing registrations to.
1632
+	 * @param bool $notify Whether to send messages notifications or not.
1633
+	 * @return array (array with reg_id(s) updated and whether update was successful.
1634
+	 * @throws DomainException
1635
+	 * @throws EE_Error
1636
+	 * @throws EntityNotFoundException
1637
+	 * @throws InvalidArgumentException
1638
+	 * @throws InvalidDataTypeException
1639
+	 * @throws InvalidInterfaceException
1640
+	 * @throws ReflectionException
1641
+	 * @throws RuntimeException
1642
+	 */
1643
+	protected function _set_registration_status_from_request($status = false, $notify = false)
1644
+	{
1645
+		$REG_IDs = $this->request->requestParamIsSet('reg_status_change_form')
1646
+			? $this->request->getRequestParam('reg_status_change_form[REG_ID]', [], 'int', true)
1647
+			: $this->request->getRequestParam('_REG_ID', [], 'int', true);
1648
+
1649
+		// sanitize $REG_IDs
1650
+		$REG_IDs = array_map('absint', $REG_IDs);
1651
+		// and remove empty entries
1652
+		$REG_IDs = array_filter($REG_IDs);
1653
+
1654
+		$result = $this->_set_registration_status($REG_IDs, $status, $notify);
1655
+
1656
+		/**
1657
+		 * Set and filter $_req_data['_REG_ID'] for any potential future messages notifications.
1658
+		 * Currently this value is used downstream by the _process_resend_registration method.
1659
+		 *
1660
+		 * @param int|array                $registration_ids The registration ids that have had their status changed successfully.
1661
+		 * @param bool                     $status           The status registrations were changed to.
1662
+		 * @param bool                     $success          If the status was changed successfully for all registrations.
1663
+		 * @param Registrations_Admin_Page $admin_page_object
1664
+		 */
1665
+		$REG_ID = apply_filters(
1666
+			'FHEE__Registrations_Admin_Page___set_registration_status_from_request__REG_IDs',
1667
+			$result['REG_ID'],
1668
+			$status,
1669
+			$result['success'],
1670
+			$this
1671
+		);
1672
+		$this->request->setRequestParam('_REG_ID', $REG_ID);
1673
+
1674
+		// notify?
1675
+		if (
1676
+			$notify
1677
+			&& $result['success']
1678
+			&& ! empty($REG_ID)
1679
+			&& EE_Registry::instance()->CAP->current_user_can(
1680
+				'ee_send_message',
1681
+				'espresso_registrations_resend_registration'
1682
+			)
1683
+		) {
1684
+			$this->_process_resend_registration();
1685
+		}
1686
+		return $result;
1687
+	}
1688
+
1689
+
1690
+	/**
1691
+	 * Set the registration status for the given reg_id (which may or may not be an array, it gets typecast to an
1692
+	 * array). Note, this method does NOT take care of possible notifications.  That is required by calling code.
1693
+	 *
1694
+	 * @param array  $REG_IDs
1695
+	 * @param string $status
1696
+	 * @param bool   $notify Used to indicate whether notification was requested or not.  This determines the context
1697
+	 *                       slug sent with setting the registration status.
1698
+	 * @return array (an array with 'success' key representing whether status change was successful, and 'REG_ID' as
1699
+	 * @throws EE_Error
1700
+	 * @throws InvalidArgumentException
1701
+	 * @throws InvalidDataTypeException
1702
+	 * @throws InvalidInterfaceException
1703
+	 * @throws ReflectionException
1704
+	 * @throws RuntimeException
1705
+	 * @throws EntityNotFoundException
1706
+	 * @throws DomainException
1707
+	 */
1708
+	protected function _set_registration_status($REG_IDs = [], $status = '', $notify = false)
1709
+	{
1710
+		$success = false;
1711
+		// typecast $REG_IDs
1712
+		$REG_IDs = (array) $REG_IDs;
1713
+		if (! empty($REG_IDs)) {
1714
+			$success = true;
1715
+			// set default status if none is passed
1716
+			$status         = $status ?: EEM_Registration::status_id_pending_payment;
1717
+			$status_context = $notify
1718
+				? Domain::CONTEXT_REGISTRATION_STATUS_CHANGE_REGISTRATION_ADMIN_NOTIFY
1719
+				: Domain::CONTEXT_REGISTRATION_STATUS_CHANGE_REGISTRATION_ADMIN;
1720
+			// loop through REG_ID's and change status
1721
+			foreach ($REG_IDs as $REG_ID) {
1722
+				$registration = $this->getRegistrationModel()->get_one_by_ID($REG_ID);
1723
+				if ($registration instanceof EE_Registration) {
1724
+					$registration->set_status(
1725
+						$status,
1726
+						false,
1727
+						new Context(
1728
+							$status_context,
1729
+							esc_html__(
1730
+								'Manually triggered status change on a Registration Admin Page route.',
1731
+								'event_espresso'
1732
+							)
1733
+						)
1734
+					);
1735
+					$result = $registration->save();
1736
+					// verifying explicit fails because update *may* just return 0 for 0 rows affected
1737
+					$success = $result !== false ? $success : false;
1738
+				}
1739
+			}
1740
+		}
1741
+
1742
+		// return $success and processed registrations
1743
+		return ['REG_ID' => $REG_IDs, 'success' => $success];
1744
+	}
1745
+
1746
+
1747
+	/**
1748
+	 * Common logic for setting up success message and redirecting to appropriate route
1749
+	 *
1750
+	 * @param string $STS_ID status id for the registration changed to
1751
+	 * @param bool   $notify indicates whether the _set_registration_status_from_request does notifications or not.
1752
+	 * @return void
1753
+	 * @throws DomainException
1754
+	 * @throws EE_Error
1755
+	 * @throws EntityNotFoundException
1756
+	 * @throws InvalidArgumentException
1757
+	 * @throws InvalidDataTypeException
1758
+	 * @throws InvalidInterfaceException
1759
+	 * @throws ReflectionException
1760
+	 * @throws RuntimeException
1761
+	 */
1762
+	protected function _reg_status_change_return($STS_ID, $notify = false)
1763
+	{
1764
+		$result  = ! empty($STS_ID) ? $this->_set_registration_status_from_request($STS_ID, $notify)
1765
+			: ['success' => false];
1766
+		$success = isset($result['success']) && $result['success'];
1767
+		// setup success message
1768
+		if ($success) {
1769
+			if (is_array($result['REG_ID']) && count($result['REG_ID']) === 1) {
1770
+				$msg = sprintf(
1771
+					esc_html__('Registration status has been set to %s', 'event_espresso'),
1772
+					EEH_Template::pretty_status($STS_ID, false, 'lower')
1773
+				);
1774
+			} else {
1775
+				$msg = sprintf(
1776
+					esc_html__('Registrations have been set to %s.', 'event_espresso'),
1777
+					EEH_Template::pretty_status($STS_ID, false, 'lower')
1778
+				);
1779
+			}
1780
+			EE_Error::add_success($msg);
1781
+		} else {
1782
+			EE_Error::add_error(
1783
+				esc_html__(
1784
+					'Something went wrong, and the status was not changed',
1785
+					'event_espresso'
1786
+				),
1787
+				__FILE__,
1788
+				__LINE__,
1789
+				__FUNCTION__
1790
+			);
1791
+		}
1792
+		$return = $this->request->getRequestParam('return');
1793
+		$route  = $return === 'view_registration'
1794
+			? ['action' => 'view_registration', '_REG_ID' => reset($result['REG_ID'])]
1795
+			: ['action' => 'default'];
1796
+		$route  = $this->mergeExistingRequestParamsWithRedirectArgs($route);
1797
+		$this->_redirect_after_action($success, '', '', $route, true);
1798
+	}
1799
+
1800
+
1801
+	/**
1802
+	 * incoming reg status change from reg details page.
1803
+	 *
1804
+	 * @return void
1805
+	 * @throws EE_Error
1806
+	 * @throws EntityNotFoundException
1807
+	 * @throws InvalidArgumentException
1808
+	 * @throws InvalidDataTypeException
1809
+	 * @throws InvalidInterfaceException
1810
+	 * @throws ReflectionException
1811
+	 * @throws RuntimeException
1812
+	 * @throws DomainException
1813
+	 */
1814
+	protected function _change_reg_status()
1815
+	{
1816
+		$this->request->setRequestParam('return', 'view_registration');
1817
+		// set notify based on whether the send notifications toggle is set or not
1818
+		$notify     = $this->request->getRequestParam('reg_status_change_form[send_notifications]', false, 'bool');
1819
+		$reg_status = $this->request->getRequestParam('reg_status_change_form[reg_status]', '');
1820
+		$this->request->setRequestParam('reg_status_change_form[reg_status]', $reg_status);
1821
+		switch ($reg_status) {
1822
+			case EEM_Registration::status_id_approved:
1823
+			case EEH_Template::pretty_status(EEM_Registration::status_id_approved, false, 'sentence'):
1824
+				$this->approve_registration($notify);
1825
+				break;
1826
+			case EEM_Registration::status_id_pending_payment:
1827
+			case EEH_Template::pretty_status(EEM_Registration::status_id_pending_payment, false, 'sentence'):
1828
+				$this->pending_registration($notify);
1829
+				break;
1830
+			case EEM_Registration::status_id_not_approved:
1831
+			case EEH_Template::pretty_status(EEM_Registration::status_id_not_approved, false, 'sentence'):
1832
+				$this->not_approve_registration($notify);
1833
+				break;
1834
+			case EEM_Registration::status_id_declined:
1835
+			case EEH_Template::pretty_status(EEM_Registration::status_id_declined, false, 'sentence'):
1836
+				$this->decline_registration($notify);
1837
+				break;
1838
+			case EEM_Registration::status_id_cancelled:
1839
+			case EEH_Template::pretty_status(EEM_Registration::status_id_cancelled, false, 'sentence'):
1840
+				$this->cancel_registration($notify);
1841
+				break;
1842
+			case EEM_Registration::status_id_wait_list:
1843
+			case EEH_Template::pretty_status(EEM_Registration::status_id_wait_list, false, 'sentence'):
1844
+				$this->wait_list_registration($notify);
1845
+				break;
1846
+			case EEM_Registration::status_id_incomplete:
1847
+			default:
1848
+				$this->request->unSetRequestParam('return');
1849
+				$this->_reg_status_change_return('');
1850
+				break;
1851
+		}
1852
+	}
1853
+
1854
+
1855
+	/**
1856
+	 * Callback for bulk action routes.
1857
+	 * Note: although we could just register the singular route callbacks for each bulk action route as well, this
1858
+	 * method was chosen so there is one central place all the registration status bulk actions are going through.
1859
+	 * Potentially, this provides an easier place to locate logic that is specific to these bulk actions (as opposed to
1860
+	 * when an action is happening on just a single registration).
1861
+	 *
1862
+	 * @param      $action
1863
+	 * @param bool $notify
1864
+	 */
1865
+	protected function bulk_action_on_registrations($action, $notify = false)
1866
+	{
1867
+		do_action(
1868
+			'AHEE__Registrations_Admin_Page__bulk_action_on_registrations__before_execution',
1869
+			$this,
1870
+			$action,
1871
+			$notify
1872
+		);
1873
+		$method = $action . '_registration';
1874
+		if (method_exists($this, $method)) {
1875
+			$this->$method($notify);
1876
+		}
1877
+	}
1878
+
1879
+
1880
+	/**
1881
+	 * approve_registration
1882
+	 *
1883
+	 * @param bool $notify whether or not to notify the registrant about their approval.
1884
+	 * @return void
1885
+	 * @throws EE_Error
1886
+	 * @throws EntityNotFoundException
1887
+	 * @throws InvalidArgumentException
1888
+	 * @throws InvalidDataTypeException
1889
+	 * @throws InvalidInterfaceException
1890
+	 * @throws ReflectionException
1891
+	 * @throws RuntimeException
1892
+	 * @throws DomainException
1893
+	 */
1894
+	protected function approve_registration($notify = false)
1895
+	{
1896
+		$this->_reg_status_change_return(EEM_Registration::status_id_approved, $notify);
1897
+	}
1898
+
1899
+
1900
+	/**
1901
+	 * decline_registration
1902
+	 *
1903
+	 * @param bool $notify whether or not to notify the registrant about their status change.
1904
+	 * @return void
1905
+	 * @throws EE_Error
1906
+	 * @throws EntityNotFoundException
1907
+	 * @throws InvalidArgumentException
1908
+	 * @throws InvalidDataTypeException
1909
+	 * @throws InvalidInterfaceException
1910
+	 * @throws ReflectionException
1911
+	 * @throws RuntimeException
1912
+	 * @throws DomainException
1913
+	 */
1914
+	protected function decline_registration($notify = false)
1915
+	{
1916
+		$this->_reg_status_change_return(EEM_Registration::status_id_declined, $notify);
1917
+	}
1918
+
1919
+
1920
+	/**
1921
+	 * cancel_registration
1922
+	 *
1923
+	 * @param bool $notify whether or not to notify the registrant about their status change.
1924
+	 * @return void
1925
+	 * @throws EE_Error
1926
+	 * @throws EntityNotFoundException
1927
+	 * @throws InvalidArgumentException
1928
+	 * @throws InvalidDataTypeException
1929
+	 * @throws InvalidInterfaceException
1930
+	 * @throws ReflectionException
1931
+	 * @throws RuntimeException
1932
+	 * @throws DomainException
1933
+	 */
1934
+	protected function cancel_registration($notify = false)
1935
+	{
1936
+		$this->_reg_status_change_return(EEM_Registration::status_id_cancelled, $notify);
1937
+	}
1938
+
1939
+
1940
+	/**
1941
+	 * not_approve_registration
1942
+	 *
1943
+	 * @param bool $notify whether or not to notify the registrant about their status change.
1944
+	 * @return void
1945
+	 * @throws EE_Error
1946
+	 * @throws EntityNotFoundException
1947
+	 * @throws InvalidArgumentException
1948
+	 * @throws InvalidDataTypeException
1949
+	 * @throws InvalidInterfaceException
1950
+	 * @throws ReflectionException
1951
+	 * @throws RuntimeException
1952
+	 * @throws DomainException
1953
+	 */
1954
+	protected function not_approve_registration($notify = false)
1955
+	{
1956
+		$this->_reg_status_change_return(EEM_Registration::status_id_not_approved, $notify);
1957
+	}
1958
+
1959
+
1960
+	/**
1961
+	 * decline_registration
1962
+	 *
1963
+	 * @param bool $notify whether or not to notify the registrant about their status change.
1964
+	 * @return void
1965
+	 * @throws EE_Error
1966
+	 * @throws EntityNotFoundException
1967
+	 * @throws InvalidArgumentException
1968
+	 * @throws InvalidDataTypeException
1969
+	 * @throws InvalidInterfaceException
1970
+	 * @throws ReflectionException
1971
+	 * @throws RuntimeException
1972
+	 * @throws DomainException
1973
+	 */
1974
+	protected function pending_registration($notify = false)
1975
+	{
1976
+		$this->_reg_status_change_return(EEM_Registration::status_id_pending_payment, $notify);
1977
+	}
1978
+
1979
+
1980
+	/**
1981
+	 * waitlist_registration
1982
+	 *
1983
+	 * @param bool $notify whether or not to notify the registrant about their status change.
1984
+	 * @return void
1985
+	 * @throws EE_Error
1986
+	 * @throws EntityNotFoundException
1987
+	 * @throws InvalidArgumentException
1988
+	 * @throws InvalidDataTypeException
1989
+	 * @throws InvalidInterfaceException
1990
+	 * @throws ReflectionException
1991
+	 * @throws RuntimeException
1992
+	 * @throws DomainException
1993
+	 */
1994
+	protected function wait_list_registration($notify = false)
1995
+	{
1996
+		$this->_reg_status_change_return(EEM_Registration::status_id_wait_list, $notify);
1997
+	}
1998
+
1999
+
2000
+	/**
2001
+	 * generates HTML for the Registration main meta box
2002
+	 *
2003
+	 * @return void
2004
+	 * @throws DomainException
2005
+	 * @throws EE_Error
2006
+	 * @throws InvalidArgumentException
2007
+	 * @throws InvalidDataTypeException
2008
+	 * @throws InvalidInterfaceException
2009
+	 * @throws ReflectionException
2010
+	 * @throws EntityNotFoundException
2011
+	 */
2012
+	public function _reg_details_meta_box()
2013
+	{
2014
+		EEH_Autoloader::register_line_item_display_autoloaders();
2015
+		EEH_Autoloader::register_line_item_filter_autoloaders();
2016
+		EE_Registry::instance()->load_helper('Line_Item');
2017
+		$transaction    = $this->_registration->transaction() ? $this->_registration->transaction()
2018
+			: EE_Transaction::new_instance();
2019
+		$this->_session = $transaction->session_data();
2020
+		$filters        = new EE_Line_Item_Filter_Collection();
2021
+		$filters->add(new EE_Single_Registration_Line_Item_Filter($this->_registration));
2022
+		$filters->add(new EE_Non_Zero_Line_Item_Filter());
2023
+		$line_item_filter_processor              = new EE_Line_Item_Filter_Processor(
2024
+			$filters,
2025
+			$transaction->total_line_item()
2026
+		);
2027
+		$filtered_line_item_tree                 = $line_item_filter_processor->process();
2028
+		$line_item_display                       = new EE_Line_Item_Display(
2029
+			'reg_admin_table',
2030
+			'EE_Admin_Table_Registration_Line_Item_Display_Strategy'
2031
+		);
2032
+		$this->_template_args['line_item_table'] = $line_item_display->display_line_item(
2033
+			$filtered_line_item_tree,
2034
+			['EE_Registration' => $this->_registration]
2035
+		);
2036
+		$attendee                                = $this->_registration->attendee();
2037
+		if (
2038
+			EE_Registry::instance()->CAP->current_user_can(
2039
+				'ee_read_transaction',
2040
+				'espresso_transactions_view_transaction'
2041
+			)
2042
+		) {
2043
+			$this->_template_args['view_transaction_button'] = EEH_Template::get_button_or_link(
2044
+				EE_Admin_Page::add_query_args_and_nonce(
2045
+					[
2046
+						'action' => 'view_transaction',
2047
+						'TXN_ID' => $transaction->ID(),
2048
+					],
2049
+					TXN_ADMIN_URL
2050
+				),
2051
+				esc_html__(' View Transaction', 'event_espresso'),
2052
+				'button secondary-button right',
2053
+				'dashicons dashicons-cart'
2054
+			);
2055
+		} else {
2056
+			$this->_template_args['view_transaction_button'] = '';
2057
+		}
2058
+		if (
2059
+			$attendee instanceof EE_Attendee
2060
+			&& EE_Registry::instance()->CAP->current_user_can(
2061
+				'ee_send_message',
2062
+				'espresso_registrations_resend_registration'
2063
+			)
2064
+		) {
2065
+			$this->_template_args['resend_registration_button'] = EEH_Template::get_button_or_link(
2066
+				EE_Admin_Page::add_query_args_and_nonce(
2067
+					[
2068
+						'action'      => 'resend_registration',
2069
+						'_REG_ID'     => $this->_registration->ID(),
2070
+						'redirect_to' => 'view_registration',
2071
+					],
2072
+					REG_ADMIN_URL
2073
+				),
2074
+				esc_html__(' Resend Registration', 'event_espresso'),
2075
+				'button secondary-button right',
2076
+				'dashicons dashicons-email-alt'
2077
+			);
2078
+		} else {
2079
+			$this->_template_args['resend_registration_button'] = '';
2080
+		}
2081
+		$this->_template_args['currency_sign'] = EE_Registry::instance()->CFG->currency->sign;
2082
+		$payment                               = $transaction->get_first_related('Payment');
2083
+		$payment                               = ! $payment instanceof EE_Payment
2084
+			? EE_Payment::new_instance()
2085
+			: $payment;
2086
+		$payment_method                        = $payment->get_first_related('Payment_Method');
2087
+		$payment_method                        = ! $payment_method instanceof EE_Payment_Method
2088
+			? EE_Payment_Method::new_instance()
2089
+			: $payment_method;
2090
+		$reg_details                           = [
2091
+			'payment_method'       => $payment_method->name(),
2092
+			'response_msg'         => $payment->gateway_response(),
2093
+			'registration_id'      => $this->_registration->get('REG_code'),
2094
+			'registration_session' => $this->_registration->session_ID(),
2095
+			'ip_address'           => isset($this->_session['ip_address']) ? $this->_session['ip_address'] : '',
2096
+			'user_agent'           => isset($this->_session['user_agent']) ? $this->_session['user_agent'] : '',
2097
+		];
2098
+		if (isset($reg_details['registration_id'])) {
2099
+			$this->_template_args['reg_details']['registration_id']['value'] = $reg_details['registration_id'];
2100
+			$this->_template_args['reg_details']['registration_id']['label'] = esc_html__(
2101
+				'Registration ID',
2102
+				'event_espresso'
2103
+			);
2104
+			$this->_template_args['reg_details']['registration_id']['class'] = 'regular-text';
2105
+		}
2106
+		if (isset($reg_details['payment_method'])) {
2107
+			$this->_template_args['reg_details']['payment_method']['value'] = $reg_details['payment_method'];
2108
+			$this->_template_args['reg_details']['payment_method']['label'] = esc_html__(
2109
+				'Most Recent Payment Method',
2110
+				'event_espresso'
2111
+			);
2112
+			$this->_template_args['reg_details']['payment_method']['class'] = 'regular-text';
2113
+			$this->_template_args['reg_details']['response_msg']['value']   = $reg_details['response_msg'];
2114
+			$this->_template_args['reg_details']['response_msg']['label']   = esc_html__(
2115
+				'Payment method response',
2116
+				'event_espresso'
2117
+			);
2118
+			$this->_template_args['reg_details']['response_msg']['class']   = 'regular-text';
2119
+		}
2120
+		$this->_template_args['reg_details']['registration_session']['value'] = $reg_details['registration_session'];
2121
+		$this->_template_args['reg_details']['registration_session']['label'] = esc_html__(
2122
+			'Registration Session',
2123
+			'event_espresso'
2124
+		);
2125
+		$this->_template_args['reg_details']['registration_session']['class'] = 'regular-text';
2126
+		$this->_template_args['reg_details']['ip_address']['value']           = $reg_details['ip_address'];
2127
+		$this->_template_args['reg_details']['ip_address']['label']           = esc_html__(
2128
+			'Registration placed from IP',
2129
+			'event_espresso'
2130
+		);
2131
+		$this->_template_args['reg_details']['ip_address']['class']           = 'regular-text';
2132
+		$this->_template_args['reg_details']['user_agent']['value']           = $reg_details['user_agent'];
2133
+		$this->_template_args['reg_details']['user_agent']['label']           = esc_html__(
2134
+			'Registrant User Agent',
2135
+			'event_espresso'
2136
+		);
2137
+		$this->_template_args['reg_details']['user_agent']['class']           = 'large-text';
2138
+		$this->_template_args['event_link']                                   = EE_Admin_Page::add_query_args_and_nonce(
2139
+			[
2140
+				'action'   => 'default',
2141
+				'event_id' => $this->_registration->event_ID(),
2142
+			],
2143
+			REG_ADMIN_URL
2144
+		);
2145
+		$this->_template_args['REG_ID']                                       = $this->_registration->ID();
2146
+		$this->_template_args['event_id']                                     = $this->_registration->event_ID();
2147
+		$template_path                                                        =
2148
+			REG_TEMPLATE_PATH . 'reg_admin_details_main_meta_box_reg_details.template.php';
2149
+		EEH_Template::display_template($template_path, $this->_template_args); // already escaped
2150
+	}
2151
+
2152
+
2153
+	/**
2154
+	 * generates HTML for the Registration Questions meta box.
2155
+	 * If pre-4.8.32.rc.000 hooks are used, uses old methods (with its filters),
2156
+	 * otherwise uses new forms system
2157
+	 *
2158
+	 * @return void
2159
+	 * @throws DomainException
2160
+	 * @throws EE_Error
2161
+	 * @throws InvalidArgumentException
2162
+	 * @throws InvalidDataTypeException
2163
+	 * @throws InvalidInterfaceException
2164
+	 * @throws ReflectionException
2165
+	 */
2166
+	public function _reg_questions_meta_box()
2167
+	{
2168
+		// allow someone to override this method entirely
2169
+		if (
2170
+			apply_filters(
2171
+				'FHEE__Registrations_Admin_Page___reg_questions_meta_box__do_default',
2172
+				true,
2173
+				$this,
2174
+				$this->_registration
2175
+			)
2176
+		) {
2177
+			$form                                              = $this->_get_reg_custom_questions_form(
2178
+				$this->_registration->ID()
2179
+			);
2180
+			$this->_template_args['att_questions']             = count($form->subforms()) > 0
2181
+				? $form->get_html_and_js()
2182
+				: '';
2183
+			$this->_template_args['reg_questions_form_action'] = 'edit_registration';
2184
+			$this->_template_args['REG_ID']                    = $this->_registration->ID();
2185
+			$template_path                                     =
2186
+				REG_TEMPLATE_PATH . 'reg_admin_details_main_meta_box_reg_questions.template.php';
2187
+			EEH_Template::display_template($template_path, $this->_template_args);
2188
+		}
2189
+	}
2190
+
2191
+
2192
+	/**
2193
+	 * form_before_question_group
2194
+	 *
2195
+	 * @param string $output
2196
+	 * @return        string
2197
+	 * @deprecated    as of 4.8.32.rc.000
2198
+	 */
2199
+	public function form_before_question_group($output)
2200
+	{
2201
+		EE_Error::doing_it_wrong(
2202
+			__CLASS__ . '::' . __FUNCTION__,
2203
+			esc_html__(
2204
+				'This method would have been protected but was used on a filter callback so needed to be public. Please discontinue usage as it will be removed soon.',
2205
+				'event_espresso'
2206
+			),
2207
+			'4.8.32.rc.000'
2208
+		);
2209
+		return '
2210 2210
 	<table class="form-table ee-width-100">
2211 2211
 		<tbody>
2212 2212
 			';
2213
-    }
2214
-
2215
-
2216
-    /**
2217
-     * form_after_question_group
2218
-     *
2219
-     * @param string $output
2220
-     * @return        string
2221
-     * @deprecated    as of 4.8.32.rc.000
2222
-     */
2223
-    public function form_after_question_group($output)
2224
-    {
2225
-        EE_Error::doing_it_wrong(
2226
-            __CLASS__ . '::' . __FUNCTION__,
2227
-            esc_html__(
2228
-                'This method would have been protected but was used on a filter callback so needed to be public. Please discontinue usage as it will be removed soon.',
2229
-                'event_espresso'
2230
-            ),
2231
-            '4.8.32.rc.000'
2232
-        );
2233
-        return '
2213
+	}
2214
+
2215
+
2216
+	/**
2217
+	 * form_after_question_group
2218
+	 *
2219
+	 * @param string $output
2220
+	 * @return        string
2221
+	 * @deprecated    as of 4.8.32.rc.000
2222
+	 */
2223
+	public function form_after_question_group($output)
2224
+	{
2225
+		EE_Error::doing_it_wrong(
2226
+			__CLASS__ . '::' . __FUNCTION__,
2227
+			esc_html__(
2228
+				'This method would have been protected but was used on a filter callback so needed to be public. Please discontinue usage as it will be removed soon.',
2229
+				'event_espresso'
2230
+			),
2231
+			'4.8.32.rc.000'
2232
+		);
2233
+		return '
2234 2234
 			<tr class="hide-if-no-js">
2235 2235
 				<th> </th>
2236 2236
 				<td class="reg-admin-edit-attendee-question-td">
2237 2237
 					<a class="reg-admin-edit-attendee-question-lnk" href="#" title="'
2238
-               . esc_attr__('click to edit question', 'event_espresso')
2239
-               . '">
2238
+			   . esc_attr__('click to edit question', 'event_espresso')
2239
+			   . '">
2240 2240
 						<span class="reg-admin-edit-question-group-spn lt-grey-txt">'
2241
-               . esc_html__('edit the above question group', 'event_espresso')
2242
-               . '</span>
2241
+			   . esc_html__('edit the above question group', 'event_espresso')
2242
+			   . '</span>
2243 2243
 						<div class="dashicons dashicons-edit"></div>
2244 2244
 					</a>
2245 2245
 				</td>
@@ -2247,641 +2247,641 @@  discard block
 block discarded – undo
2247 2247
 		</tbody>
2248 2248
 	</table>
2249 2249
 ';
2250
-    }
2251
-
2252
-
2253
-    /**
2254
-     * form_form_field_label_wrap
2255
-     *
2256
-     * @param string $label
2257
-     * @return        string
2258
-     * @deprecated    as of 4.8.32.rc.000
2259
-     */
2260
-    public function form_form_field_label_wrap($label)
2261
-    {
2262
-        EE_Error::doing_it_wrong(
2263
-            __CLASS__ . '::' . __FUNCTION__,
2264
-            esc_html__(
2265
-                'This method would have been protected but was used on a filter callback so needed to be public. Please discontinue usage as it will be removed soon.',
2266
-                'event_espresso'
2267
-            ),
2268
-            '4.8.32.rc.000'
2269
-        );
2270
-        return '
2250
+	}
2251
+
2252
+
2253
+	/**
2254
+	 * form_form_field_label_wrap
2255
+	 *
2256
+	 * @param string $label
2257
+	 * @return        string
2258
+	 * @deprecated    as of 4.8.32.rc.000
2259
+	 */
2260
+	public function form_form_field_label_wrap($label)
2261
+	{
2262
+		EE_Error::doing_it_wrong(
2263
+			__CLASS__ . '::' . __FUNCTION__,
2264
+			esc_html__(
2265
+				'This method would have been protected but was used on a filter callback so needed to be public. Please discontinue usage as it will be removed soon.',
2266
+				'event_espresso'
2267
+			),
2268
+			'4.8.32.rc.000'
2269
+		);
2270
+		return '
2271 2271
 			<tr>
2272 2272
 				<th>
2273 2273
 					' . $label . '
2274 2274
 				</th>';
2275
-    }
2276
-
2277
-
2278
-    /**
2279
-     * form_form_field_input__wrap
2280
-     *
2281
-     * @param string $input
2282
-     * @return        string
2283
-     * @deprecated    as of 4.8.32.rc.000
2284
-     */
2285
-    public function form_form_field_input__wrap($input)
2286
-    {
2287
-        EE_Error::doing_it_wrong(
2288
-            __CLASS__ . '::' . __FUNCTION__,
2289
-            esc_html__(
2290
-                'This method would have been protected but was used on a filter callback so needed to be public. Please discontinue usage as it will be removed soon.',
2291
-                'event_espresso'
2292
-            ),
2293
-            '4.8.32.rc.000'
2294
-        );
2295
-        return '
2275
+	}
2276
+
2277
+
2278
+	/**
2279
+	 * form_form_field_input__wrap
2280
+	 *
2281
+	 * @param string $input
2282
+	 * @return        string
2283
+	 * @deprecated    as of 4.8.32.rc.000
2284
+	 */
2285
+	public function form_form_field_input__wrap($input)
2286
+	{
2287
+		EE_Error::doing_it_wrong(
2288
+			__CLASS__ . '::' . __FUNCTION__,
2289
+			esc_html__(
2290
+				'This method would have been protected but was used on a filter callback so needed to be public. Please discontinue usage as it will be removed soon.',
2291
+				'event_espresso'
2292
+			),
2293
+			'4.8.32.rc.000'
2294
+		);
2295
+		return '
2296 2296
 				<td class="reg-admin-attendee-questions-input-td disabled-input">
2297 2297
 					' . $input . '
2298 2298
 				</td>
2299 2299
 			</tr>';
2300
-    }
2301
-
2302
-
2303
-    /**
2304
-     * Updates the registration's custom questions according to the form info, if the form is submitted.
2305
-     * If it's not a post, the "view_registrations" route will be called next on the SAME request
2306
-     * to display the page
2307
-     *
2308
-     * @return void
2309
-     * @throws EE_Error
2310
-     * @throws InvalidArgumentException
2311
-     * @throws InvalidDataTypeException
2312
-     * @throws InvalidInterfaceException
2313
-     * @throws ReflectionException
2314
-     */
2315
-    protected function _update_attendee_registration_form()
2316
-    {
2317
-        do_action('AHEE__Registrations_Admin_Page___update_attendee_registration_form__start', $this);
2318
-        if ($_SERVER['REQUEST_METHOD'] === 'POST') {
2319
-            $REG_ID  = $this->request->getRequestParam('_REG_ID', 0, 'int');
2320
-            $success = $this->_save_reg_custom_questions_form($REG_ID);
2321
-            if ($success) {
2322
-                $what  = esc_html__('Registration Form', 'event_espresso');
2323
-                $route = $REG_ID
2324
-                    ? ['action' => 'view_registration', '_REG_ID' => $REG_ID]
2325
-                    : ['action' => 'default'];
2326
-                $this->_redirect_after_action(true, $what, esc_html__('updated', 'event_espresso'), $route);
2327
-            }
2328
-        }
2329
-    }
2330
-
2331
-
2332
-    /**
2333
-     * Gets the form for saving registrations custom questions (if done
2334
-     * previously retrieves the cached form object, which may have validation errors in it)
2335
-     *
2336
-     * @param int $REG_ID
2337
-     * @return EE_Registration_Custom_Questions_Form
2338
-     * @throws EE_Error
2339
-     * @throws InvalidArgumentException
2340
-     * @throws InvalidDataTypeException
2341
-     * @throws InvalidInterfaceException
2342
-     * @throws ReflectionException
2343
-     */
2344
-    protected function _get_reg_custom_questions_form($REG_ID)
2345
-    {
2346
-        if (! $this->_reg_custom_questions_form) {
2347
-            require_once(REG_ADMIN . 'form_sections/EE_Registration_Custom_Questions_Form.form.php');
2348
-            $this->_reg_custom_questions_form = new EE_Registration_Custom_Questions_Form(
2349
-                $this->getRegistrationModel()->get_one_by_ID($REG_ID)
2350
-            );
2351
-            $this->_reg_custom_questions_form->_construct_finalize(null, null);
2352
-        }
2353
-        return $this->_reg_custom_questions_form;
2354
-    }
2355
-
2356
-
2357
-    /**
2358
-     * Saves
2359
-     *
2360
-     * @param bool $REG_ID
2361
-     * @return bool
2362
-     * @throws EE_Error
2363
-     * @throws InvalidArgumentException
2364
-     * @throws InvalidDataTypeException
2365
-     * @throws InvalidInterfaceException
2366
-     * @throws ReflectionException
2367
-     */
2368
-    private function _save_reg_custom_questions_form($REG_ID = 0)
2369
-    {
2370
-        if (! $REG_ID) {
2371
-            EE_Error::add_error(
2372
-                esc_html__(
2373
-                    'An error occurred. No registration ID was received.',
2374
-                    'event_espresso'
2375
-                ),
2376
-                __FILE__,
2377
-                __FUNCTION__,
2378
-                __LINE__
2379
-            );
2380
-        }
2381
-        $form = $this->_get_reg_custom_questions_form($REG_ID);
2382
-        $form->receive_form_submission($this->request->requestParams());
2383
-        $success = false;
2384
-        if ($form->is_valid()) {
2385
-            foreach ($form->subforms() as $question_group_form) {
2386
-                foreach ($question_group_form->inputs() as $question_id => $input) {
2387
-                    $where_conditions    = [
2388
-                        'QST_ID' => $question_id,
2389
-                        'REG_ID' => $REG_ID,
2390
-                    ];
2391
-                    $possibly_new_values = [
2392
-                        'ANS_value' => $input->normalized_value(),
2393
-                    ];
2394
-                    $answer              = EEM_Answer::instance()->get_one([$where_conditions]);
2395
-                    if ($answer instanceof EE_Answer) {
2396
-                        $success = $answer->save($possibly_new_values);
2397
-                    } else {
2398
-                        // insert it then
2399
-                        $cols_n_vals = array_merge($where_conditions, $possibly_new_values);
2400
-                        $answer      = EE_Answer::new_instance($cols_n_vals);
2401
-                        $success     = $answer->save();
2402
-                    }
2403
-                }
2404
-            }
2405
-        } else {
2406
-            EE_Error::add_error($form->get_validation_error_string(), __FILE__, __FUNCTION__, __LINE__);
2407
-        }
2408
-        return $success;
2409
-    }
2410
-
2411
-
2412
-    /**
2413
-     * generates HTML for the Registration main meta box
2414
-     *
2415
-     * @return void
2416
-     * @throws DomainException
2417
-     * @throws EE_Error
2418
-     * @throws InvalidArgumentException
2419
-     * @throws InvalidDataTypeException
2420
-     * @throws InvalidInterfaceException
2421
-     * @throws ReflectionException
2422
-     */
2423
-    public function _reg_attendees_meta_box()
2424
-    {
2425
-        $REG = $this->getRegistrationModel();
2426
-        // get all other registrations on this transaction, and cache
2427
-        // the attendees for them so we don't have to run another query using force_join
2428
-        $registrations                           = $REG->get_all(
2429
-            [
2430
-                [
2431
-                    'TXN_ID' => $this->_registration->transaction_ID(),
2432
-                    'REG_ID' => ['!=', $this->_registration->ID()],
2433
-                ],
2434
-                'force_join'               => ['Attendee'],
2435
-                'default_where_conditions' => 'other_models_only',
2436
-            ]
2437
-        );
2438
-        $this->_template_args['attendees']       = [];
2439
-        $this->_template_args['attendee_notice'] = '';
2440
-        if (
2441
-            empty($registrations)
2442
-            || (is_array($registrations)
2443
-                && ! EEH_Array::get_one_item_from_array($registrations))
2444
-        ) {
2445
-            EE_Error::add_error(
2446
-                esc_html__(
2447
-                    'There are no records attached to this registration. Something may have gone wrong with the registration',
2448
-                    'event_espresso'
2449
-                ),
2450
-                __FILE__,
2451
-                __FUNCTION__,
2452
-                __LINE__
2453
-            );
2454
-            $this->_template_args['attendee_notice'] = EE_Error::get_notices();
2455
-        } else {
2456
-            $att_nmbr = 1;
2457
-            foreach ($registrations as $registration) {
2458
-                /* @var $registration EE_Registration */
2459
-                $attendee                                                      = $registration->attendee()
2460
-                    ? $registration->attendee()
2461
-                    : $this->getAttendeeModel()->create_default_object();
2462
-                $this->_template_args['attendees'][ $att_nmbr ]['STS_ID']      = $registration->status_ID();
2463
-                $this->_template_args['attendees'][ $att_nmbr ]['fname']       = $attendee->fname();
2464
-                $this->_template_args['attendees'][ $att_nmbr ]['lname']       = $attendee->lname();
2465
-                $this->_template_args['attendees'][ $att_nmbr ]['email']       = $attendee->email();
2466
-                $this->_template_args['attendees'][ $att_nmbr ]['final_price'] = $registration->final_price();
2467
-                $this->_template_args['attendees'][ $att_nmbr ]['address']     = implode(
2468
-                    ', ',
2469
-                    $attendee->full_address_as_array()
2470
-                );
2471
-                $this->_template_args['attendees'][ $att_nmbr ]['att_link']    = self::add_query_args_and_nonce(
2472
-                    [
2473
-                        'action' => 'edit_attendee',
2474
-                        'post'   => $attendee->ID(),
2475
-                    ],
2476
-                    REG_ADMIN_URL
2477
-                );
2478
-                $this->_template_args['attendees'][ $att_nmbr ]['event_name']  =
2479
-                    $registration->event_obj() instanceof EE_Event
2480
-                        ? $registration->event_obj()->name()
2481
-                        : '';
2482
-                $att_nmbr++;
2483
-            }
2484
-            $this->_template_args['currency_sign'] = EE_Registry::instance()->CFG->currency->sign;
2485
-        }
2486
-        $template_path = REG_TEMPLATE_PATH . 'reg_admin_details_main_meta_box_attendees.template.php';
2487
-        EEH_Template::display_template($template_path, $this->_template_args);
2488
-    }
2489
-
2490
-
2491
-    /**
2492
-     * generates HTML for the Edit Registration side meta box
2493
-     *
2494
-     * @return void
2495
-     * @throws DomainException
2496
-     * @throws EE_Error
2497
-     * @throws InvalidArgumentException
2498
-     * @throws InvalidDataTypeException
2499
-     * @throws InvalidInterfaceException
2500
-     * @throws ReflectionException
2501
-     */
2502
-    public function _reg_registrant_side_meta_box()
2503
-    {
2504
-        /*@var $attendee EE_Attendee */
2505
-        $att_check = $this->_registration->attendee();
2506
-        $attendee  = $att_check instanceof EE_Attendee
2507
-            ? $att_check
2508
-            : $this->getAttendeeModel()->create_default_object();
2509
-        // now let's determine if this is not the primary registration.  If it isn't then we set the
2510
-        // primary_registration object for reference BUT ONLY if the Attendee object loaded is not the same as the
2511
-        // primary registration object (that way we know if we need to show create button or not)
2512
-        if (! $this->_registration->is_primary_registrant()) {
2513
-            $primary_registration = $this->_registration->get_primary_registration();
2514
-            $primary_attendee     = $primary_registration instanceof EE_Registration ? $primary_registration->attendee()
2515
-                : null;
2516
-            if (! $primary_attendee instanceof EE_Attendee || $attendee->ID() !== $primary_attendee->ID()) {
2517
-                // in here?  This means the displayed registration is not the primary registrant but ALREADY HAS its own
2518
-                // custom attendee object so let's not worry about the primary reg.
2519
-                $primary_registration = null;
2520
-            }
2521
-        } else {
2522
-            $primary_registration = null;
2523
-        }
2524
-        $this->_template_args['ATT_ID']            = $attendee->ID();
2525
-        $this->_template_args['fname']             = $attendee->fname();
2526
-        $this->_template_args['lname']             = $attendee->lname();
2527
-        $this->_template_args['email']             = $attendee->email();
2528
-        $this->_template_args['phone']             = $attendee->phone();
2529
-        $this->_template_args['formatted_address'] = EEH_Address::format($attendee);
2530
-        // edit link
2531
-        $this->_template_args['att_edit_link']  = EE_Admin_Page::add_query_args_and_nonce(
2532
-            [
2533
-                'action' => 'edit_attendee',
2534
-                'post'   => $attendee->ID(),
2535
-            ],
2536
-            REG_ADMIN_URL
2537
-        );
2538
-        $this->_template_args['att_edit_label'] = esc_html__('View/Edit Contact', 'event_espresso');
2539
-        // create link
2540
-        $this->_template_args['create_link']  = $primary_registration instanceof EE_Registration
2541
-            ? EE_Admin_Page::add_query_args_and_nonce(
2542
-                [
2543
-                    'action'  => 'duplicate_attendee',
2544
-                    '_REG_ID' => $this->_registration->ID(),
2545
-                ],
2546
-                REG_ADMIN_URL
2547
-            ) : '';
2548
-        $this->_template_args['create_label'] = esc_html__('Create Contact', 'event_espresso');
2549
-        $this->_template_args['att_check']    = $att_check;
2550
-        $template_path                        =
2551
-            REG_TEMPLATE_PATH . 'reg_admin_details_side_meta_box_registrant.template.php';
2552
-        EEH_Template::display_template($template_path, $this->_template_args);
2553
-    }
2554
-
2555
-
2556
-    /**
2557
-     * trash or restore registrations
2558
-     *
2559
-     * @param boolean $trash whether to archive or restore
2560
-     * @return void
2561
-     * @throws DomainException
2562
-     * @throws EE_Error
2563
-     * @throws EntityNotFoundException
2564
-     * @throws InvalidArgumentException
2565
-     * @throws InvalidDataTypeException
2566
-     * @throws InvalidInterfaceException
2567
-     * @throws ReflectionException
2568
-     * @throws RuntimeException
2569
-     * @throws UnexpectedEntityException
2570
-     */
2571
-    protected function _trash_or_restore_registrations($trash = true)
2572
-    {
2573
-        // if empty _REG_ID then get out because there's nothing to do
2574
-        $REG_IDs = $this->request->getRequestParam('_REG_ID', [], 'int', true);
2575
-        if (empty($REG_IDs)) {
2576
-            EE_Error::add_error(
2577
-                sprintf(
2578
-                    esc_html__(
2579
-                        'In order to %1$s registrations you must select which ones you wish to %1$s by clicking the checkboxes.',
2580
-                        'event_espresso'
2581
-                    ),
2582
-                    $trash ? 'trash' : 'restore'
2583
-                ),
2584
-                __FILE__,
2585
-                __LINE__,
2586
-                __FUNCTION__
2587
-            );
2588
-            $this->_redirect_after_action(false, '', '', [], true);
2589
-        }
2590
-        $success        = 0;
2591
-        $overwrite_msgs = false;
2592
-        // Checkboxes
2593
-        $reg_count = count($REG_IDs);
2594
-        // cycle thru checkboxes
2595
-        foreach ($REG_IDs as $REG_ID) {
2596
-            /** @var EE_Registration $REG */
2597
-            $REG      = $this->getRegistrationModel()->get_one_by_ID($REG_ID);
2598
-            $payments = $REG->registration_payments();
2599
-            if (! empty($payments)) {
2600
-                $name           = $REG->attendee() instanceof EE_Attendee
2601
-                    ? $REG->attendee()->full_name()
2602
-                    : esc_html__('Unknown Attendee', 'event_espresso');
2603
-                $overwrite_msgs = true;
2604
-                EE_Error::add_error(
2605
-                    sprintf(
2606
-                        esc_html__(
2607
-                            'The registration for %s could not be trashed because it has payments attached to the related transaction.  If you wish to trash this registration you must first delete the payments on the related transaction.',
2608
-                            'event_espresso'
2609
-                        ),
2610
-                        $name
2611
-                    ),
2612
-                    __FILE__,
2613
-                    __FUNCTION__,
2614
-                    __LINE__
2615
-                );
2616
-                // can't trash this registration because it has payments.
2617
-                continue;
2618
-            }
2619
-            $updated = $trash ? $REG->delete(__METHOD__) : $REG->restore(__METHOD__);
2620
-            if ($updated) {
2621
-                $success++;
2622
-            }
2623
-        }
2624
-        $this->_redirect_after_action(
2625
-            $success === $reg_count, // were ALL registrations affected?
2626
-            $success > 1
2627
-                ? esc_html__('Registrations', 'event_espresso')
2628
-                : esc_html__('Registration', 'event_espresso'),
2629
-            $trash
2630
-                ? esc_html__('moved to the trash', 'event_espresso')
2631
-                : esc_html__('restored', 'event_espresso'),
2632
-            $this->mergeExistingRequestParamsWithRedirectArgs(['action' => 'default']),
2633
-            $overwrite_msgs
2634
-        );
2635
-    }
2636
-
2637
-
2638
-    /**
2639
-     * This is used to permanently delete registrations.  Note, this will handle not only deleting permanently the
2640
-     * registration but also.
2641
-     * 1. Removing relations to EE_Attendee
2642
-     * 2. Deleting permanently the related transaction, but ONLY if all related registrations to the transaction are
2643
-     * ALSO trashed.
2644
-     * 3. Deleting permanently any related Line items but only if the above conditions are met.
2645
-     * 4. Removing relationships between all tickets and the related registrations
2646
-     * 5. Deleting permanently any related Answers (and the answers for other related registrations that were deleted.)
2647
-     * 6. Deleting permanently any related Checkins.
2648
-     *
2649
-     * @return void
2650
-     * @throws EE_Error
2651
-     * @throws InvalidArgumentException
2652
-     * @throws InvalidDataTypeException
2653
-     * @throws InvalidInterfaceException
2654
-     * @throws ReflectionException
2655
-     */
2656
-    protected function _delete_registrations()
2657
-    {
2658
-        $REG_MDL = $this->getRegistrationModel();
2659
-        $success = 0;
2660
-        // Checkboxes
2661
-        $REG_IDs = $this->request->getRequestParam('_REG_ID', [], 'int', true);
2662
-
2663
-        if (! empty($REG_IDs)) {
2664
-            // if array has more than one element than success message should be plural
2665
-            $success = count($REG_IDs) > 1 ? 2 : 1;
2666
-            // cycle thru checkboxes
2667
-            foreach ($REG_IDs as $REG_ID) {
2668
-                $REG = $REG_MDL->get_one_by_ID($REG_ID);
2669
-                if (! $REG instanceof EE_Registration) {
2670
-                    continue;
2671
-                }
2672
-                $deleted = $this->_delete_registration($REG);
2673
-                if (! $deleted) {
2674
-                    $success = 0;
2675
-                }
2676
-            }
2677
-        }
2678
-
2679
-        $what        = $success > 1
2680
-            ? esc_html__('Registrations', 'event_espresso')
2681
-            : esc_html__('Registration', 'event_espresso');
2682
-        $action_desc = esc_html__('permanently deleted.', 'event_espresso');
2683
-        $this->_redirect_after_action(
2684
-            $success,
2685
-            $what,
2686
-            $action_desc,
2687
-            $this->mergeExistingRequestParamsWithRedirectArgs(['action' => 'default']),
2688
-            true
2689
-        );
2690
-    }
2691
-
2692
-
2693
-    /**
2694
-     * handles the permanent deletion of a registration.  See comments with _delete_registrations() for details on what
2695
-     * models get affected.
2696
-     *
2697
-     * @param EE_Registration $REG registration to be deleted permanently
2698
-     * @return bool true = successful deletion, false = fail.
2699
-     * @throws EE_Error
2700
-     * @throws InvalidArgumentException
2701
-     * @throws InvalidDataTypeException
2702
-     * @throws InvalidInterfaceException
2703
-     * @throws ReflectionException
2704
-     */
2705
-    protected function _delete_registration(EE_Registration $REG)
2706
-    {
2707
-        // first we start with the transaction... ultimately, we WILL not delete permanently if there are any related
2708
-        // registrations on the transaction that are NOT trashed.
2709
-        $TXN = $REG->get_first_related('Transaction');
2710
-        if (! $TXN instanceof EE_Transaction) {
2711
-            EE_Error::add_error(
2712
-                sprintf(
2713
-                    esc_html__(
2714
-                        'Unable to permanently delete registration %d because its related transaction has already been deleted. If you can restore the related transaction to the database then this registration can be deleted.',
2715
-                        'event_espresso'
2716
-                    ),
2717
-                    $REG->id()
2718
-                ),
2719
-                __FILE__,
2720
-                __FUNCTION__,
2721
-                __LINE__
2722
-            );
2723
-            return false;
2724
-        }
2725
-        $REGS        = $TXN->get_many_related('Registration');
2726
-        $all_trashed = true;
2727
-        foreach ($REGS as $registration) {
2728
-            if (! $registration->get('REG_deleted')) {
2729
-                $all_trashed = false;
2730
-            }
2731
-        }
2732
-        if (! $all_trashed) {
2733
-            EE_Error::add_error(
2734
-                esc_html__(
2735
-                    'Unable to permanently delete this registration. Before this registration can be permanently deleted, all registrations made in the same transaction must be trashed as well.  These registrations will be permanently deleted in the same action.',
2736
-                    'event_espresso'
2737
-                ),
2738
-                __FILE__,
2739
-                __FUNCTION__,
2740
-                __LINE__
2741
-            );
2742
-            return false;
2743
-        }
2744
-        // k made it here so that means we can delete all the related transactions and their answers (but let's do them
2745
-        // separately from THIS one).
2746
-        foreach ($REGS as $registration) {
2747
-            // delete related answers
2748
-            $registration->delete_related_permanently('Answer');
2749
-            // remove relationship to EE_Attendee (but we ALWAYS leave the contact record intact)
2750
-            $attendee = $registration->get_first_related('Attendee');
2751
-            if ($attendee instanceof EE_Attendee) {
2752
-                $registration->_remove_relation_to($attendee, 'Attendee');
2753
-            }
2754
-            // now remove relationships to tickets on this registration.
2755
-            $registration->_remove_relations('Ticket');
2756
-            // now delete permanently the checkins related to this registration.
2757
-            $registration->delete_related_permanently('Checkin');
2758
-            if ($registration->ID() === $REG->ID()) {
2759
-                continue;
2760
-            } //we don't want to delete permanently the existing registration just yet.
2761
-            // remove relation to transaction for these registrations if NOT the existing registrations
2762
-            $registration->_remove_relations('Transaction');
2763
-            // delete permanently any related messages.
2764
-            $registration->delete_related_permanently('Message');
2765
-            // now delete this registration permanently
2766
-            $registration->delete_permanently();
2767
-        }
2768
-        // now all related registrations on the transaction are handled.  So let's just handle this registration itself
2769
-        // (the transaction and line items should be all that's left).
2770
-        // delete the line items related to the transaction for this registration.
2771
-        $TXN->delete_related_permanently('Line_Item');
2772
-        // we need to remove all the relationships on the transaction
2773
-        $TXN->delete_related_permanently('Payment');
2774
-        $TXN->delete_related_permanently('Extra_Meta');
2775
-        $TXN->delete_related_permanently('Message');
2776
-        // now we can delete this REG permanently (and the transaction of course)
2777
-        $REG->delete_related_permanently('Transaction');
2778
-        return $REG->delete_permanently();
2779
-    }
2780
-
2781
-
2782
-    /**
2783
-     *    generates HTML for the Register New Attendee Admin page
2784
-     *
2785
-     * @throws DomainException
2786
-     * @throws EE_Error
2787
-     * @throws InvalidArgumentException
2788
-     * @throws InvalidDataTypeException
2789
-     * @throws InvalidInterfaceException
2790
-     * @throws ReflectionException
2791
-     */
2792
-    public function new_registration()
2793
-    {
2794
-        if (! $this->_set_reg_event()) {
2795
-            throw new EE_Error(
2796
-                esc_html__(
2797
-                    'Unable to continue with registering because there is no Event ID in the request',
2798
-                    'event_espresso'
2799
-                )
2800
-            );
2801
-        }
2802
-        /** @var CurrentPage $current_page */
2803
-        $current_page = $this->loader->getShared(CurrentPage::class);
2804
-        $current_page->setEspressoPage(true);
2805
-        // gotta start with a clean slate if we're not coming here via ajax
2806
-        if (
2807
-            ! $this->request->isAjax()
2808
-            && (
2809
-                ! $this->request->requestParamIsSet('processing_registration')
2810
-                || $this->request->requestParamIsSet('step_error')
2811
-            )
2812
-        ) {
2813
-            EE_Registry::instance()->SSN->clear_session(__CLASS__, __FUNCTION__);
2814
-        }
2815
-        $this->_template_args['event_name'] = '';
2816
-        // event name
2817
-        if ($this->_reg_event) {
2818
-            $this->_template_args['event_name'] = $this->_reg_event->name();
2819
-            $edit_event_url                     = self::add_query_args_and_nonce(
2820
-                [
2821
-                    'action' => 'edit',
2822
-                    'post'   => $this->_reg_event->ID(),
2823
-                ],
2824
-                EVENTS_ADMIN_URL
2825
-            );
2826
-            $edit_event_lnk                     = '<a href="'
2827
-                                                  . $edit_event_url
2828
-                                                  . '" title="'
2829
-                                                  . esc_attr__('Edit ', 'event_espresso')
2830
-                                                  . $this->_reg_event->name()
2831
-                                                  . '">'
2832
-                                                  . esc_html__('Edit Event', 'event_espresso')
2833
-                                                  . '</a>';
2834
-            $this->_template_args['event_name'] .= ' <span class="admin-page-header-edit-lnk not-bold">'
2835
-                                                   . $edit_event_lnk
2836
-                                                   . '</span>';
2837
-        }
2838
-        $this->_template_args['step_content'] = $this->_get_registration_step_content();
2839
-        if ($this->request->isAjax()) {
2840
-            $this->_return_json();
2841
-        }
2842
-        // grab header
2843
-        $template_path                              =
2844
-            REG_TEMPLATE_PATH . 'reg_admin_register_new_attendee.template.php';
2845
-        $this->_template_args['admin_page_content'] = EEH_Template::display_template(
2846
-            $template_path,
2847
-            $this->_template_args,
2848
-            true
2849
-        );
2850
-        // $this->_set_publish_post_box_vars( NULL, FALSE, FALSE, NULL, FALSE );
2851
-        // the details template wrapper
2852
-        $this->display_admin_page_with_sidebar();
2853
-    }
2854
-
2855
-
2856
-    /**
2857
-     * This returns the content for a registration step
2858
-     *
2859
-     * @return string html
2860
-     * @throws DomainException
2861
-     * @throws EE_Error
2862
-     * @throws InvalidArgumentException
2863
-     * @throws InvalidDataTypeException
2864
-     * @throws InvalidInterfaceException
2865
-     * @throws ReflectionException
2866
-     */
2867
-    protected function _get_registration_step_content()
2868
-    {
2869
-        if (isset($_COOKIE['ee_registration_added']) && $_COOKIE['ee_registration_added']) {
2870
-            $warning_msg = sprintf(
2871
-                esc_html__(
2872
-                    '%2$sWARNING!!!%3$s%1$sPlease do not use the back button to return to this page for the purpose of adding another registration.%1$sThis can result in lost and/or corrupted data.%1$sIf you wish to add another registration, then please click the%1$s%7$s"Add Another New Registration to Event"%8$s button%1$son the Transaction details page, after you are redirected.%1$s%1$s%4$s redirecting in %5$s seconds %6$s',
2873
-                    'event_espresso'
2874
-                ),
2875
-                '<br />',
2876
-                '<h3 class="important-notice">',
2877
-                '</h3>',
2878
-                '<div class="float-right">',
2879
-                '<span id="redirect_timer" class="important-notice">30</span>',
2880
-                '</div>',
2881
-                '<b>',
2882
-                '</b>'
2883
-            );
2884
-            return '
2300
+	}
2301
+
2302
+
2303
+	/**
2304
+	 * Updates the registration's custom questions according to the form info, if the form is submitted.
2305
+	 * If it's not a post, the "view_registrations" route will be called next on the SAME request
2306
+	 * to display the page
2307
+	 *
2308
+	 * @return void
2309
+	 * @throws EE_Error
2310
+	 * @throws InvalidArgumentException
2311
+	 * @throws InvalidDataTypeException
2312
+	 * @throws InvalidInterfaceException
2313
+	 * @throws ReflectionException
2314
+	 */
2315
+	protected function _update_attendee_registration_form()
2316
+	{
2317
+		do_action('AHEE__Registrations_Admin_Page___update_attendee_registration_form__start', $this);
2318
+		if ($_SERVER['REQUEST_METHOD'] === 'POST') {
2319
+			$REG_ID  = $this->request->getRequestParam('_REG_ID', 0, 'int');
2320
+			$success = $this->_save_reg_custom_questions_form($REG_ID);
2321
+			if ($success) {
2322
+				$what  = esc_html__('Registration Form', 'event_espresso');
2323
+				$route = $REG_ID
2324
+					? ['action' => 'view_registration', '_REG_ID' => $REG_ID]
2325
+					: ['action' => 'default'];
2326
+				$this->_redirect_after_action(true, $what, esc_html__('updated', 'event_espresso'), $route);
2327
+			}
2328
+		}
2329
+	}
2330
+
2331
+
2332
+	/**
2333
+	 * Gets the form for saving registrations custom questions (if done
2334
+	 * previously retrieves the cached form object, which may have validation errors in it)
2335
+	 *
2336
+	 * @param int $REG_ID
2337
+	 * @return EE_Registration_Custom_Questions_Form
2338
+	 * @throws EE_Error
2339
+	 * @throws InvalidArgumentException
2340
+	 * @throws InvalidDataTypeException
2341
+	 * @throws InvalidInterfaceException
2342
+	 * @throws ReflectionException
2343
+	 */
2344
+	protected function _get_reg_custom_questions_form($REG_ID)
2345
+	{
2346
+		if (! $this->_reg_custom_questions_form) {
2347
+			require_once(REG_ADMIN . 'form_sections/EE_Registration_Custom_Questions_Form.form.php');
2348
+			$this->_reg_custom_questions_form = new EE_Registration_Custom_Questions_Form(
2349
+				$this->getRegistrationModel()->get_one_by_ID($REG_ID)
2350
+			);
2351
+			$this->_reg_custom_questions_form->_construct_finalize(null, null);
2352
+		}
2353
+		return $this->_reg_custom_questions_form;
2354
+	}
2355
+
2356
+
2357
+	/**
2358
+	 * Saves
2359
+	 *
2360
+	 * @param bool $REG_ID
2361
+	 * @return bool
2362
+	 * @throws EE_Error
2363
+	 * @throws InvalidArgumentException
2364
+	 * @throws InvalidDataTypeException
2365
+	 * @throws InvalidInterfaceException
2366
+	 * @throws ReflectionException
2367
+	 */
2368
+	private function _save_reg_custom_questions_form($REG_ID = 0)
2369
+	{
2370
+		if (! $REG_ID) {
2371
+			EE_Error::add_error(
2372
+				esc_html__(
2373
+					'An error occurred. No registration ID was received.',
2374
+					'event_espresso'
2375
+				),
2376
+				__FILE__,
2377
+				__FUNCTION__,
2378
+				__LINE__
2379
+			);
2380
+		}
2381
+		$form = $this->_get_reg_custom_questions_form($REG_ID);
2382
+		$form->receive_form_submission($this->request->requestParams());
2383
+		$success = false;
2384
+		if ($form->is_valid()) {
2385
+			foreach ($form->subforms() as $question_group_form) {
2386
+				foreach ($question_group_form->inputs() as $question_id => $input) {
2387
+					$where_conditions    = [
2388
+						'QST_ID' => $question_id,
2389
+						'REG_ID' => $REG_ID,
2390
+					];
2391
+					$possibly_new_values = [
2392
+						'ANS_value' => $input->normalized_value(),
2393
+					];
2394
+					$answer              = EEM_Answer::instance()->get_one([$where_conditions]);
2395
+					if ($answer instanceof EE_Answer) {
2396
+						$success = $answer->save($possibly_new_values);
2397
+					} else {
2398
+						// insert it then
2399
+						$cols_n_vals = array_merge($where_conditions, $possibly_new_values);
2400
+						$answer      = EE_Answer::new_instance($cols_n_vals);
2401
+						$success     = $answer->save();
2402
+					}
2403
+				}
2404
+			}
2405
+		} else {
2406
+			EE_Error::add_error($form->get_validation_error_string(), __FILE__, __FUNCTION__, __LINE__);
2407
+		}
2408
+		return $success;
2409
+	}
2410
+
2411
+
2412
+	/**
2413
+	 * generates HTML for the Registration main meta box
2414
+	 *
2415
+	 * @return void
2416
+	 * @throws DomainException
2417
+	 * @throws EE_Error
2418
+	 * @throws InvalidArgumentException
2419
+	 * @throws InvalidDataTypeException
2420
+	 * @throws InvalidInterfaceException
2421
+	 * @throws ReflectionException
2422
+	 */
2423
+	public function _reg_attendees_meta_box()
2424
+	{
2425
+		$REG = $this->getRegistrationModel();
2426
+		// get all other registrations on this transaction, and cache
2427
+		// the attendees for them so we don't have to run another query using force_join
2428
+		$registrations                           = $REG->get_all(
2429
+			[
2430
+				[
2431
+					'TXN_ID' => $this->_registration->transaction_ID(),
2432
+					'REG_ID' => ['!=', $this->_registration->ID()],
2433
+				],
2434
+				'force_join'               => ['Attendee'],
2435
+				'default_where_conditions' => 'other_models_only',
2436
+			]
2437
+		);
2438
+		$this->_template_args['attendees']       = [];
2439
+		$this->_template_args['attendee_notice'] = '';
2440
+		if (
2441
+			empty($registrations)
2442
+			|| (is_array($registrations)
2443
+				&& ! EEH_Array::get_one_item_from_array($registrations))
2444
+		) {
2445
+			EE_Error::add_error(
2446
+				esc_html__(
2447
+					'There are no records attached to this registration. Something may have gone wrong with the registration',
2448
+					'event_espresso'
2449
+				),
2450
+				__FILE__,
2451
+				__FUNCTION__,
2452
+				__LINE__
2453
+			);
2454
+			$this->_template_args['attendee_notice'] = EE_Error::get_notices();
2455
+		} else {
2456
+			$att_nmbr = 1;
2457
+			foreach ($registrations as $registration) {
2458
+				/* @var $registration EE_Registration */
2459
+				$attendee                                                      = $registration->attendee()
2460
+					? $registration->attendee()
2461
+					: $this->getAttendeeModel()->create_default_object();
2462
+				$this->_template_args['attendees'][ $att_nmbr ]['STS_ID']      = $registration->status_ID();
2463
+				$this->_template_args['attendees'][ $att_nmbr ]['fname']       = $attendee->fname();
2464
+				$this->_template_args['attendees'][ $att_nmbr ]['lname']       = $attendee->lname();
2465
+				$this->_template_args['attendees'][ $att_nmbr ]['email']       = $attendee->email();
2466
+				$this->_template_args['attendees'][ $att_nmbr ]['final_price'] = $registration->final_price();
2467
+				$this->_template_args['attendees'][ $att_nmbr ]['address']     = implode(
2468
+					', ',
2469
+					$attendee->full_address_as_array()
2470
+				);
2471
+				$this->_template_args['attendees'][ $att_nmbr ]['att_link']    = self::add_query_args_and_nonce(
2472
+					[
2473
+						'action' => 'edit_attendee',
2474
+						'post'   => $attendee->ID(),
2475
+					],
2476
+					REG_ADMIN_URL
2477
+				);
2478
+				$this->_template_args['attendees'][ $att_nmbr ]['event_name']  =
2479
+					$registration->event_obj() instanceof EE_Event
2480
+						? $registration->event_obj()->name()
2481
+						: '';
2482
+				$att_nmbr++;
2483
+			}
2484
+			$this->_template_args['currency_sign'] = EE_Registry::instance()->CFG->currency->sign;
2485
+		}
2486
+		$template_path = REG_TEMPLATE_PATH . 'reg_admin_details_main_meta_box_attendees.template.php';
2487
+		EEH_Template::display_template($template_path, $this->_template_args);
2488
+	}
2489
+
2490
+
2491
+	/**
2492
+	 * generates HTML for the Edit Registration side meta box
2493
+	 *
2494
+	 * @return void
2495
+	 * @throws DomainException
2496
+	 * @throws EE_Error
2497
+	 * @throws InvalidArgumentException
2498
+	 * @throws InvalidDataTypeException
2499
+	 * @throws InvalidInterfaceException
2500
+	 * @throws ReflectionException
2501
+	 */
2502
+	public function _reg_registrant_side_meta_box()
2503
+	{
2504
+		/*@var $attendee EE_Attendee */
2505
+		$att_check = $this->_registration->attendee();
2506
+		$attendee  = $att_check instanceof EE_Attendee
2507
+			? $att_check
2508
+			: $this->getAttendeeModel()->create_default_object();
2509
+		// now let's determine if this is not the primary registration.  If it isn't then we set the
2510
+		// primary_registration object for reference BUT ONLY if the Attendee object loaded is not the same as the
2511
+		// primary registration object (that way we know if we need to show create button or not)
2512
+		if (! $this->_registration->is_primary_registrant()) {
2513
+			$primary_registration = $this->_registration->get_primary_registration();
2514
+			$primary_attendee     = $primary_registration instanceof EE_Registration ? $primary_registration->attendee()
2515
+				: null;
2516
+			if (! $primary_attendee instanceof EE_Attendee || $attendee->ID() !== $primary_attendee->ID()) {
2517
+				// in here?  This means the displayed registration is not the primary registrant but ALREADY HAS its own
2518
+				// custom attendee object so let's not worry about the primary reg.
2519
+				$primary_registration = null;
2520
+			}
2521
+		} else {
2522
+			$primary_registration = null;
2523
+		}
2524
+		$this->_template_args['ATT_ID']            = $attendee->ID();
2525
+		$this->_template_args['fname']             = $attendee->fname();
2526
+		$this->_template_args['lname']             = $attendee->lname();
2527
+		$this->_template_args['email']             = $attendee->email();
2528
+		$this->_template_args['phone']             = $attendee->phone();
2529
+		$this->_template_args['formatted_address'] = EEH_Address::format($attendee);
2530
+		// edit link
2531
+		$this->_template_args['att_edit_link']  = EE_Admin_Page::add_query_args_and_nonce(
2532
+			[
2533
+				'action' => 'edit_attendee',
2534
+				'post'   => $attendee->ID(),
2535
+			],
2536
+			REG_ADMIN_URL
2537
+		);
2538
+		$this->_template_args['att_edit_label'] = esc_html__('View/Edit Contact', 'event_espresso');
2539
+		// create link
2540
+		$this->_template_args['create_link']  = $primary_registration instanceof EE_Registration
2541
+			? EE_Admin_Page::add_query_args_and_nonce(
2542
+				[
2543
+					'action'  => 'duplicate_attendee',
2544
+					'_REG_ID' => $this->_registration->ID(),
2545
+				],
2546
+				REG_ADMIN_URL
2547
+			) : '';
2548
+		$this->_template_args['create_label'] = esc_html__('Create Contact', 'event_espresso');
2549
+		$this->_template_args['att_check']    = $att_check;
2550
+		$template_path                        =
2551
+			REG_TEMPLATE_PATH . 'reg_admin_details_side_meta_box_registrant.template.php';
2552
+		EEH_Template::display_template($template_path, $this->_template_args);
2553
+	}
2554
+
2555
+
2556
+	/**
2557
+	 * trash or restore registrations
2558
+	 *
2559
+	 * @param boolean $trash whether to archive or restore
2560
+	 * @return void
2561
+	 * @throws DomainException
2562
+	 * @throws EE_Error
2563
+	 * @throws EntityNotFoundException
2564
+	 * @throws InvalidArgumentException
2565
+	 * @throws InvalidDataTypeException
2566
+	 * @throws InvalidInterfaceException
2567
+	 * @throws ReflectionException
2568
+	 * @throws RuntimeException
2569
+	 * @throws UnexpectedEntityException
2570
+	 */
2571
+	protected function _trash_or_restore_registrations($trash = true)
2572
+	{
2573
+		// if empty _REG_ID then get out because there's nothing to do
2574
+		$REG_IDs = $this->request->getRequestParam('_REG_ID', [], 'int', true);
2575
+		if (empty($REG_IDs)) {
2576
+			EE_Error::add_error(
2577
+				sprintf(
2578
+					esc_html__(
2579
+						'In order to %1$s registrations you must select which ones you wish to %1$s by clicking the checkboxes.',
2580
+						'event_espresso'
2581
+					),
2582
+					$trash ? 'trash' : 'restore'
2583
+				),
2584
+				__FILE__,
2585
+				__LINE__,
2586
+				__FUNCTION__
2587
+			);
2588
+			$this->_redirect_after_action(false, '', '', [], true);
2589
+		}
2590
+		$success        = 0;
2591
+		$overwrite_msgs = false;
2592
+		// Checkboxes
2593
+		$reg_count = count($REG_IDs);
2594
+		// cycle thru checkboxes
2595
+		foreach ($REG_IDs as $REG_ID) {
2596
+			/** @var EE_Registration $REG */
2597
+			$REG      = $this->getRegistrationModel()->get_one_by_ID($REG_ID);
2598
+			$payments = $REG->registration_payments();
2599
+			if (! empty($payments)) {
2600
+				$name           = $REG->attendee() instanceof EE_Attendee
2601
+					? $REG->attendee()->full_name()
2602
+					: esc_html__('Unknown Attendee', 'event_espresso');
2603
+				$overwrite_msgs = true;
2604
+				EE_Error::add_error(
2605
+					sprintf(
2606
+						esc_html__(
2607
+							'The registration for %s could not be trashed because it has payments attached to the related transaction.  If you wish to trash this registration you must first delete the payments on the related transaction.',
2608
+							'event_espresso'
2609
+						),
2610
+						$name
2611
+					),
2612
+					__FILE__,
2613
+					__FUNCTION__,
2614
+					__LINE__
2615
+				);
2616
+				// can't trash this registration because it has payments.
2617
+				continue;
2618
+			}
2619
+			$updated = $trash ? $REG->delete(__METHOD__) : $REG->restore(__METHOD__);
2620
+			if ($updated) {
2621
+				$success++;
2622
+			}
2623
+		}
2624
+		$this->_redirect_after_action(
2625
+			$success === $reg_count, // were ALL registrations affected?
2626
+			$success > 1
2627
+				? esc_html__('Registrations', 'event_espresso')
2628
+				: esc_html__('Registration', 'event_espresso'),
2629
+			$trash
2630
+				? esc_html__('moved to the trash', 'event_espresso')
2631
+				: esc_html__('restored', 'event_espresso'),
2632
+			$this->mergeExistingRequestParamsWithRedirectArgs(['action' => 'default']),
2633
+			$overwrite_msgs
2634
+		);
2635
+	}
2636
+
2637
+
2638
+	/**
2639
+	 * This is used to permanently delete registrations.  Note, this will handle not only deleting permanently the
2640
+	 * registration but also.
2641
+	 * 1. Removing relations to EE_Attendee
2642
+	 * 2. Deleting permanently the related transaction, but ONLY if all related registrations to the transaction are
2643
+	 * ALSO trashed.
2644
+	 * 3. Deleting permanently any related Line items but only if the above conditions are met.
2645
+	 * 4. Removing relationships between all tickets and the related registrations
2646
+	 * 5. Deleting permanently any related Answers (and the answers for other related registrations that were deleted.)
2647
+	 * 6. Deleting permanently any related Checkins.
2648
+	 *
2649
+	 * @return void
2650
+	 * @throws EE_Error
2651
+	 * @throws InvalidArgumentException
2652
+	 * @throws InvalidDataTypeException
2653
+	 * @throws InvalidInterfaceException
2654
+	 * @throws ReflectionException
2655
+	 */
2656
+	protected function _delete_registrations()
2657
+	{
2658
+		$REG_MDL = $this->getRegistrationModel();
2659
+		$success = 0;
2660
+		// Checkboxes
2661
+		$REG_IDs = $this->request->getRequestParam('_REG_ID', [], 'int', true);
2662
+
2663
+		if (! empty($REG_IDs)) {
2664
+			// if array has more than one element than success message should be plural
2665
+			$success = count($REG_IDs) > 1 ? 2 : 1;
2666
+			// cycle thru checkboxes
2667
+			foreach ($REG_IDs as $REG_ID) {
2668
+				$REG = $REG_MDL->get_one_by_ID($REG_ID);
2669
+				if (! $REG instanceof EE_Registration) {
2670
+					continue;
2671
+				}
2672
+				$deleted = $this->_delete_registration($REG);
2673
+				if (! $deleted) {
2674
+					$success = 0;
2675
+				}
2676
+			}
2677
+		}
2678
+
2679
+		$what        = $success > 1
2680
+			? esc_html__('Registrations', 'event_espresso')
2681
+			: esc_html__('Registration', 'event_espresso');
2682
+		$action_desc = esc_html__('permanently deleted.', 'event_espresso');
2683
+		$this->_redirect_after_action(
2684
+			$success,
2685
+			$what,
2686
+			$action_desc,
2687
+			$this->mergeExistingRequestParamsWithRedirectArgs(['action' => 'default']),
2688
+			true
2689
+		);
2690
+	}
2691
+
2692
+
2693
+	/**
2694
+	 * handles the permanent deletion of a registration.  See comments with _delete_registrations() for details on what
2695
+	 * models get affected.
2696
+	 *
2697
+	 * @param EE_Registration $REG registration to be deleted permanently
2698
+	 * @return bool true = successful deletion, false = fail.
2699
+	 * @throws EE_Error
2700
+	 * @throws InvalidArgumentException
2701
+	 * @throws InvalidDataTypeException
2702
+	 * @throws InvalidInterfaceException
2703
+	 * @throws ReflectionException
2704
+	 */
2705
+	protected function _delete_registration(EE_Registration $REG)
2706
+	{
2707
+		// first we start with the transaction... ultimately, we WILL not delete permanently if there are any related
2708
+		// registrations on the transaction that are NOT trashed.
2709
+		$TXN = $REG->get_first_related('Transaction');
2710
+		if (! $TXN instanceof EE_Transaction) {
2711
+			EE_Error::add_error(
2712
+				sprintf(
2713
+					esc_html__(
2714
+						'Unable to permanently delete registration %d because its related transaction has already been deleted. If you can restore the related transaction to the database then this registration can be deleted.',
2715
+						'event_espresso'
2716
+					),
2717
+					$REG->id()
2718
+				),
2719
+				__FILE__,
2720
+				__FUNCTION__,
2721
+				__LINE__
2722
+			);
2723
+			return false;
2724
+		}
2725
+		$REGS        = $TXN->get_many_related('Registration');
2726
+		$all_trashed = true;
2727
+		foreach ($REGS as $registration) {
2728
+			if (! $registration->get('REG_deleted')) {
2729
+				$all_trashed = false;
2730
+			}
2731
+		}
2732
+		if (! $all_trashed) {
2733
+			EE_Error::add_error(
2734
+				esc_html__(
2735
+					'Unable to permanently delete this registration. Before this registration can be permanently deleted, all registrations made in the same transaction must be trashed as well.  These registrations will be permanently deleted in the same action.',
2736
+					'event_espresso'
2737
+				),
2738
+				__FILE__,
2739
+				__FUNCTION__,
2740
+				__LINE__
2741
+			);
2742
+			return false;
2743
+		}
2744
+		// k made it here so that means we can delete all the related transactions and their answers (but let's do them
2745
+		// separately from THIS one).
2746
+		foreach ($REGS as $registration) {
2747
+			// delete related answers
2748
+			$registration->delete_related_permanently('Answer');
2749
+			// remove relationship to EE_Attendee (but we ALWAYS leave the contact record intact)
2750
+			$attendee = $registration->get_first_related('Attendee');
2751
+			if ($attendee instanceof EE_Attendee) {
2752
+				$registration->_remove_relation_to($attendee, 'Attendee');
2753
+			}
2754
+			// now remove relationships to tickets on this registration.
2755
+			$registration->_remove_relations('Ticket');
2756
+			// now delete permanently the checkins related to this registration.
2757
+			$registration->delete_related_permanently('Checkin');
2758
+			if ($registration->ID() === $REG->ID()) {
2759
+				continue;
2760
+			} //we don't want to delete permanently the existing registration just yet.
2761
+			// remove relation to transaction for these registrations if NOT the existing registrations
2762
+			$registration->_remove_relations('Transaction');
2763
+			// delete permanently any related messages.
2764
+			$registration->delete_related_permanently('Message');
2765
+			// now delete this registration permanently
2766
+			$registration->delete_permanently();
2767
+		}
2768
+		// now all related registrations on the transaction are handled.  So let's just handle this registration itself
2769
+		// (the transaction and line items should be all that's left).
2770
+		// delete the line items related to the transaction for this registration.
2771
+		$TXN->delete_related_permanently('Line_Item');
2772
+		// we need to remove all the relationships on the transaction
2773
+		$TXN->delete_related_permanently('Payment');
2774
+		$TXN->delete_related_permanently('Extra_Meta');
2775
+		$TXN->delete_related_permanently('Message');
2776
+		// now we can delete this REG permanently (and the transaction of course)
2777
+		$REG->delete_related_permanently('Transaction');
2778
+		return $REG->delete_permanently();
2779
+	}
2780
+
2781
+
2782
+	/**
2783
+	 *    generates HTML for the Register New Attendee Admin page
2784
+	 *
2785
+	 * @throws DomainException
2786
+	 * @throws EE_Error
2787
+	 * @throws InvalidArgumentException
2788
+	 * @throws InvalidDataTypeException
2789
+	 * @throws InvalidInterfaceException
2790
+	 * @throws ReflectionException
2791
+	 */
2792
+	public function new_registration()
2793
+	{
2794
+		if (! $this->_set_reg_event()) {
2795
+			throw new EE_Error(
2796
+				esc_html__(
2797
+					'Unable to continue with registering because there is no Event ID in the request',
2798
+					'event_espresso'
2799
+				)
2800
+			);
2801
+		}
2802
+		/** @var CurrentPage $current_page */
2803
+		$current_page = $this->loader->getShared(CurrentPage::class);
2804
+		$current_page->setEspressoPage(true);
2805
+		// gotta start with a clean slate if we're not coming here via ajax
2806
+		if (
2807
+			! $this->request->isAjax()
2808
+			&& (
2809
+				! $this->request->requestParamIsSet('processing_registration')
2810
+				|| $this->request->requestParamIsSet('step_error')
2811
+			)
2812
+		) {
2813
+			EE_Registry::instance()->SSN->clear_session(__CLASS__, __FUNCTION__);
2814
+		}
2815
+		$this->_template_args['event_name'] = '';
2816
+		// event name
2817
+		if ($this->_reg_event) {
2818
+			$this->_template_args['event_name'] = $this->_reg_event->name();
2819
+			$edit_event_url                     = self::add_query_args_and_nonce(
2820
+				[
2821
+					'action' => 'edit',
2822
+					'post'   => $this->_reg_event->ID(),
2823
+				],
2824
+				EVENTS_ADMIN_URL
2825
+			);
2826
+			$edit_event_lnk                     = '<a href="'
2827
+												  . $edit_event_url
2828
+												  . '" title="'
2829
+												  . esc_attr__('Edit ', 'event_espresso')
2830
+												  . $this->_reg_event->name()
2831
+												  . '">'
2832
+												  . esc_html__('Edit Event', 'event_espresso')
2833
+												  . '</a>';
2834
+			$this->_template_args['event_name'] .= ' <span class="admin-page-header-edit-lnk not-bold">'
2835
+												   . $edit_event_lnk
2836
+												   . '</span>';
2837
+		}
2838
+		$this->_template_args['step_content'] = $this->_get_registration_step_content();
2839
+		if ($this->request->isAjax()) {
2840
+			$this->_return_json();
2841
+		}
2842
+		// grab header
2843
+		$template_path                              =
2844
+			REG_TEMPLATE_PATH . 'reg_admin_register_new_attendee.template.php';
2845
+		$this->_template_args['admin_page_content'] = EEH_Template::display_template(
2846
+			$template_path,
2847
+			$this->_template_args,
2848
+			true
2849
+		);
2850
+		// $this->_set_publish_post_box_vars( NULL, FALSE, FALSE, NULL, FALSE );
2851
+		// the details template wrapper
2852
+		$this->display_admin_page_with_sidebar();
2853
+	}
2854
+
2855
+
2856
+	/**
2857
+	 * This returns the content for a registration step
2858
+	 *
2859
+	 * @return string html
2860
+	 * @throws DomainException
2861
+	 * @throws EE_Error
2862
+	 * @throws InvalidArgumentException
2863
+	 * @throws InvalidDataTypeException
2864
+	 * @throws InvalidInterfaceException
2865
+	 * @throws ReflectionException
2866
+	 */
2867
+	protected function _get_registration_step_content()
2868
+	{
2869
+		if (isset($_COOKIE['ee_registration_added']) && $_COOKIE['ee_registration_added']) {
2870
+			$warning_msg = sprintf(
2871
+				esc_html__(
2872
+					'%2$sWARNING!!!%3$s%1$sPlease do not use the back button to return to this page for the purpose of adding another registration.%1$sThis can result in lost and/or corrupted data.%1$sIf you wish to add another registration, then please click the%1$s%7$s"Add Another New Registration to Event"%8$s button%1$son the Transaction details page, after you are redirected.%1$s%1$s%4$s redirecting in %5$s seconds %6$s',
2873
+					'event_espresso'
2874
+				),
2875
+				'<br />',
2876
+				'<h3 class="important-notice">',
2877
+				'</h3>',
2878
+				'<div class="float-right">',
2879
+				'<span id="redirect_timer" class="important-notice">30</span>',
2880
+				'</div>',
2881
+				'<b>',
2882
+				'</b>'
2883
+			);
2884
+			return '
2885 2885
 	<div id="ee-add-reg-back-button-dv"><p>' . $warning_msg . '</p></div>
2886 2886
 	<script >
2887 2887
 		// WHOAH !!! it appears that someone is using the back button from the Transaction admin page
@@ -2894,846 +2894,846 @@  discard block
 block discarded – undo
2894 2894
 	        }
2895 2895
 	    }, 800 );
2896 2896
 	</script >';
2897
-        }
2898
-        $template_args = [
2899
-            'title'                    => '',
2900
-            'content'                  => '',
2901
-            'step_button_text'         => '',
2902
-            'show_notification_toggle' => false,
2903
-        ];
2904
-        // to indicate we're processing a new registration
2905
-        $hidden_fields = [
2906
-            'processing_registration' => [
2907
-                'type'  => 'hidden',
2908
-                'value' => 0,
2909
-            ],
2910
-            'event_id'                => [
2911
-                'type'  => 'hidden',
2912
-                'value' => $this->_reg_event->ID(),
2913
-            ],
2914
-        ];
2915
-        // if the cart is empty then we know we're at step one, so we'll display the ticket selector
2916
-        $cart = EE_Registry::instance()->SSN->cart();
2917
-        $step = ! $cart instanceof EE_Cart ? 'ticket' : 'questions';
2918
-        switch ($step) {
2919
-            case 'ticket':
2920
-                $hidden_fields['processing_registration']['value'] = 1;
2921
-                $template_args['title']                            = esc_html__(
2922
-                    'Step One: Select the Ticket for this registration',
2923
-                    'event_espresso'
2924
-                );
2925
-                $template_args['content']                          =
2926
-                    EED_Ticket_Selector::instance()->display_ticket_selector($this->_reg_event);
2927
-                $template_args['content']                          .= '</div>';
2928
-                $template_args['step_button_text']                 = esc_html__(
2929
-                    'Add Tickets and Continue to Registrant Details',
2930
-                    'event_espresso'
2931
-                );
2932
-                $template_args['show_notification_toggle']         = false;
2933
-                break;
2934
-            case 'questions':
2935
-                $hidden_fields['processing_registration']['value'] = 2;
2936
-                $template_args['title']                            = esc_html__(
2937
-                    'Step Two: Add Registrant Details for this Registration',
2938
-                    'event_espresso'
2939
-                );
2940
-                // in theory, we should be able to run EED_SPCO at this point
2941
-                // because the cart should have been set up properly by the first process_reg_step run.
2942
-                $template_args['content']                  =
2943
-                    EED_Single_Page_Checkout::registration_checkout_for_admin();
2944
-                $template_args['step_button_text']         = esc_html__(
2945
-                    'Save Registration and Continue to Details',
2946
-                    'event_espresso'
2947
-                );
2948
-                $template_args['show_notification_toggle'] = true;
2949
-                break;
2950
-        }
2951
-        // we come back to the process_registration_step route.
2952
-        $this->_set_add_edit_form_tags('process_reg_step', $hidden_fields);
2953
-        return EEH_Template::display_template(
2954
-            REG_TEMPLATE_PATH . 'reg_admin_register_new_attendee_step_content.template.php',
2955
-            $template_args,
2956
-            true
2957
-        );
2958
-    }
2959
-
2960
-
2961
-    /**
2962
-     * set_reg_event
2963
-     *
2964
-     * @return bool
2965
-     * @throws EE_Error
2966
-     * @throws InvalidArgumentException
2967
-     * @throws InvalidDataTypeException
2968
-     * @throws InvalidInterfaceException
2969
-     */
2970
-    private function _set_reg_event()
2971
-    {
2972
-        if (is_object($this->_reg_event)) {
2973
-            return true;
2974
-        }
2975
-
2976
-        $EVT_ID = $this->request->getRequestParam('event_id', 0, 'int');
2977
-        if (! $EVT_ID) {
2978
-            return false;
2979
-        }
2980
-        $this->_reg_event = $this->getEventModel()->get_one_by_ID($EVT_ID);
2981
-        return true;
2982
-    }
2983
-
2984
-
2985
-    /**
2986
-     * process_reg_step
2987
-     *
2988
-     * @return void
2989
-     * @throws DomainException
2990
-     * @throws EE_Error
2991
-     * @throws InvalidArgumentException
2992
-     * @throws InvalidDataTypeException
2993
-     * @throws InvalidInterfaceException
2994
-     * @throws ReflectionException
2995
-     * @throws RuntimeException
2996
-     */
2997
-    public function process_reg_step()
2998
-    {
2999
-        EE_System::do_not_cache();
3000
-        $this->_set_reg_event();
3001
-        /** @var CurrentPage $current_page */
3002
-        $current_page = $this->loader->getShared(CurrentPage::class);
3003
-        $current_page->setEspressoPage(true);
3004
-        $this->request->setRequestParam('uts', time());
3005
-        // what step are we on?
3006
-        $cart = EE_Registry::instance()->SSN->cart();
3007
-        $step = ! $cart instanceof EE_Cart ? 'ticket' : 'questions';
3008
-        // if doing ajax then we need to verify the nonce
3009
-        if ($this->request->isAjax()) {
3010
-            $nonce = $this->request->getRequestParam($this->_req_nonce, '');
3011
-            $this->_verify_nonce($nonce, $this->_req_nonce);
3012
-        }
3013
-        switch ($step) {
3014
-            case 'ticket':
3015
-                // process ticket selection
3016
-                $success = EED_Ticket_Selector::instance()->process_ticket_selections();
3017
-                if ($success) {
3018
-                    EE_Error::add_success(
3019
-                        esc_html__(
3020
-                            'Tickets Selected. Now complete the registration.',
3021
-                            'event_espresso'
3022
-                        )
3023
-                    );
3024
-                } else {
3025
-                    $this->request->setRequestParam('step_error', true);
3026
-                    $query_args['step_error'] = $this->request->getRequestParam('step_error', true, 'bool');
3027
-                }
3028
-                if ($this->request->isAjax()) {
3029
-                    $this->new_registration(); // display next step
3030
-                } else {
3031
-                    $query_args = [
3032
-                        'action'                  => 'new_registration',
3033
-                        'processing_registration' => 1,
3034
-                        'event_id'                => $this->_reg_event->ID(),
3035
-                        'uts'                     => time(),
3036
-                    ];
3037
-                    $this->_redirect_after_action(
3038
-                        false,
3039
-                        '',
3040
-                        '',
3041
-                        $query_args,
3042
-                        true
3043
-                    );
3044
-                }
3045
-                break;
3046
-            case 'questions':
3047
-                if (! $this->request->requestParamIsSet('txn_reg_status_change[send_notifications]')) {
3048
-                    add_filter('FHEE__EED_Messages___maybe_registration__deliver_notifications', '__return_false', 15);
3049
-                }
3050
-                // process registration
3051
-                $transaction = EED_Single_Page_Checkout::instance()->process_registration_from_admin();
3052
-                if ($cart instanceof EE_Cart) {
3053
-                    $grand_total = $cart->get_grand_total();
3054
-                    if ($grand_total instanceof EE_Line_Item) {
3055
-                        $grand_total->save_this_and_descendants_to_txn();
3056
-                    }
3057
-                }
3058
-                if (! $transaction instanceof EE_Transaction) {
3059
-                    $query_args = [
3060
-                        'action'                  => 'new_registration',
3061
-                        'processing_registration' => 2,
3062
-                        'event_id'                => $this->_reg_event->ID(),
3063
-                        'uts'                     => time(),
3064
-                    ];
3065
-                    if ($this->request->isAjax()) {
3066
-                        // display registration form again because there are errors (maybe validation?)
3067
-                        $this->new_registration();
3068
-                        return;
3069
-                    }
3070
-                    $this->_redirect_after_action(
3071
-                        false,
3072
-                        '',
3073
-                        '',
3074
-                        $query_args,
3075
-                        true
3076
-                    );
3077
-                    return;
3078
-                }
3079
-                // maybe update status, and make sure to save transaction if not done already
3080
-                if (! $transaction->update_status_based_on_total_paid()) {
3081
-                    $transaction->save();
3082
-                }
3083
-                EE_Registry::instance()->SSN->clear_session(__CLASS__, __FUNCTION__);
3084
-                $query_args = [
3085
-                    'action'        => 'redirect_to_txn',
3086
-                    'TXN_ID'        => $transaction->ID(),
3087
-                    'EVT_ID'        => $this->_reg_event->ID(),
3088
-                    'event_name'    => urlencode($this->_reg_event->name()),
3089
-                    'redirect_from' => 'new_registration',
3090
-                ];
3091
-                $this->_redirect_after_action(false, '', '', $query_args, true);
3092
-                break;
3093
-        }
3094
-        // what are you looking here for?  Should be nothing to do at this point.
3095
-    }
3096
-
3097
-
3098
-    /**
3099
-     * redirect_to_txn
3100
-     *
3101
-     * @return void
3102
-     * @throws EE_Error
3103
-     * @throws InvalidArgumentException
3104
-     * @throws InvalidDataTypeException
3105
-     * @throws InvalidInterfaceException
3106
-     * @throws ReflectionException
3107
-     */
3108
-    public function redirect_to_txn()
3109
-    {
3110
-        EE_System::do_not_cache();
3111
-        EE_Registry::instance()->SSN->clear_session(__CLASS__, __FUNCTION__);
3112
-        $query_args = [
3113
-            'action' => 'view_transaction',
3114
-            'TXN_ID' => $this->request->getRequestParam('TXN_ID', 0, 'int'),
3115
-            'page'   => 'espresso_transactions',
3116
-        ];
3117
-        if ($this->request->requestParamIsSet('EVT_ID') && $this->request->requestParamIsSet('redirect_from')) {
3118
-            $query_args['EVT_ID']        = $this->request->getRequestParam('EVT_ID', 0, 'int');
3119
-            $query_args['event_name']    = urlencode($this->request->getRequestParam('event_name'));
3120
-            $query_args['redirect_from'] = $this->request->getRequestParam('redirect_from');
3121
-        }
3122
-        EE_Error::add_success(
3123
-            esc_html__(
3124
-                'Registration Created.  Please review the transaction and add any payments as necessary',
3125
-                'event_espresso'
3126
-            )
3127
-        );
3128
-        $this->_redirect_after_action(false, '', '', $query_args, true);
3129
-    }
3130
-
3131
-
3132
-    /**
3133
-     * generates HTML for the Attendee Contact List
3134
-     *
3135
-     * @return void
3136
-     * @throws DomainException
3137
-     * @throws EE_Error
3138
-     */
3139
-    protected function _attendee_contact_list_table()
3140
-    {
3141
-        do_action('AHEE_log', __FILE__, __FUNCTION__, '');
3142
-        $this->_search_btn_label = esc_html__('Contacts', 'event_espresso');
3143
-        $this->display_admin_list_table_page_with_no_sidebar();
3144
-    }
3145
-
3146
-
3147
-    /**
3148
-     * get_attendees
3149
-     *
3150
-     * @param      $per_page
3151
-     * @param bool $count whether to return count or data.
3152
-     * @param bool $trash
3153
-     * @return array|int
3154
-     * @throws EE_Error
3155
-     * @throws InvalidArgumentException
3156
-     * @throws InvalidDataTypeException
3157
-     * @throws InvalidInterfaceException
3158
-     */
3159
-    public function get_attendees($per_page, $count = false, $trash = false)
3160
-    {
3161
-        do_action('AHEE_log', __FILE__, __FUNCTION__, '');
3162
-        require_once(REG_ADMIN . 'EE_Attendee_Contact_List_Table.class.php');
3163
-        $orderby = $this->request->getRequestParam('orderby');
3164
-        switch ($orderby) {
3165
-            case 'ATT_ID':
3166
-            case 'ATT_fname':
3167
-            case 'ATT_email':
3168
-            case 'ATT_city':
3169
-            case 'STA_ID':
3170
-            case 'CNT_ID':
3171
-                break;
3172
-            case 'Registration_Count':
3173
-                $orderby = 'Registration_Count';
3174
-                break;
3175
-            default:
3176
-                $orderby = 'ATT_lname';
3177
-        }
3178
-        $sort         = $this->request->getRequestParam('order', 'ASC');
3179
-        $current_page = $this->request->getRequestParam('paged', 1, 'int');
3180
-        $per_page     = absint($per_page) ? $per_page : 10;
3181
-        $per_page     = $this->request->getRequestParam('perpage', $per_page, 'int');
3182
-        $_where       = [];
3183
-        $search_term  = $this->request->getRequestParam('s');
3184
-        if ($search_term) {
3185
-            $search_term  = '%' . $search_term . '%';
3186
-            $_where['OR'] = [
3187
-                'Registration.Event.EVT_name'       => ['LIKE', $search_term],
3188
-                'Registration.Event.EVT_desc'       => ['LIKE', $search_term],
3189
-                'Registration.Event.EVT_short_desc' => ['LIKE', $search_term],
3190
-                'ATT_fname'                         => ['LIKE', $search_term],
3191
-                'ATT_lname'                         => ['LIKE', $search_term],
3192
-                'ATT_short_bio'                     => ['LIKE', $search_term],
3193
-                'ATT_email'                         => ['LIKE', $search_term],
3194
-                'ATT_address'                       => ['LIKE', $search_term],
3195
-                'ATT_address2'                      => ['LIKE', $search_term],
3196
-                'ATT_city'                          => ['LIKE', $search_term],
3197
-                'Country.CNT_name'                  => ['LIKE', $search_term],
3198
-                'State.STA_name'                    => ['LIKE', $search_term],
3199
-                'ATT_phone'                         => ['LIKE', $search_term],
3200
-                'Registration.REG_final_price'      => ['LIKE', $search_term],
3201
-                'Registration.REG_code'             => ['LIKE', $search_term],
3202
-                'Registration.REG_group_size'       => ['LIKE', $search_term],
3203
-            ];
3204
-        }
3205
-        $offset     = ($current_page - 1) * $per_page;
3206
-        $limit      = $count ? null : [$offset, $per_page];
3207
-        $query_args = [
3208
-            $_where,
3209
-            'extra_selects' => ['Registration_Count' => ['Registration.REG_ID', 'count', '%d']],
3210
-            'limit'         => $limit,
3211
-        ];
3212
-        if (! $count) {
3213
-            $query_args['order_by'] = [$orderby => $sort];
3214
-        }
3215
-        $query_args[0]['status'] = $trash ? ['!=', 'publish'] : ['IN', ['publish']];
3216
-        return $count
3217
-            ? $this->getAttendeeModel()->count($query_args, 'ATT_ID', true)
3218
-            : $this->getAttendeeModel()->get_all($query_args);
3219
-    }
3220
-
3221
-
3222
-    /**
3223
-     * This is just taking care of resending the registration confirmation
3224
-     *
3225
-     * @return void
3226
-     * @throws EE_Error
3227
-     * @throws InvalidArgumentException
3228
-     * @throws InvalidDataTypeException
3229
-     * @throws InvalidInterfaceException
3230
-     * @throws ReflectionException
3231
-     */
3232
-    protected function _resend_registration()
3233
-    {
3234
-        $this->_process_resend_registration();
3235
-        $REG_ID      = $this->request->getRequestParam('_REG_ID', 0, 'int');
3236
-        $redirect_to = $this->request->getRequestParam('redirect_to');
3237
-        $query_args  = $redirect_to
3238
-            ? ['action' => $redirect_to, '_REG_ID' => $REG_ID]
3239
-            : ['action' => 'default'];
3240
-        $this->_redirect_after_action(false, '', '', $query_args, true);
3241
-    }
3242
-
3243
-
3244
-    /**
3245
-     * Creates a registration report, but accepts the name of a method to use for preparing the query parameters
3246
-     * to use when selecting registrations
3247
-     *
3248
-     * @param string $method_name_for_getting_query_params the name of the method (on this class) to use for preparing
3249
-     *                                                     the query parameters from the request
3250
-     * @return void ends the request with a redirect or download
3251
-     */
3252
-    public function _registrations_report_base($method_name_for_getting_query_params)
3253
-    {
3254
-        $EVT_ID = $this->request->requestParamIsSet('EVT_ID')
3255
-            ? $this->request->getRequestParam('EVT_ID', 0, 'int')
3256
-            : null;
3257
-        if (! defined('EE_USE_OLD_CSV_REPORT_CLASS')) {
3258
-            $filters = $this->request->getRequestParam('filters', [], DataType::STRING, true);
3259
-            $report_params  = $this->$method_name_for_getting_query_params($filters);
3260
-            wp_redirect(
3261
-                EE_Admin_Page::add_query_args_and_nonce(
3262
-                    [
3263
-                        'page'        => 'espresso_batch',
3264
-                        'batch'       => 'file',
3265
-                        'EVT_ID'      => $EVT_ID,
3266
-                        'job_handler' => urlencode('EventEspressoBatchRequest\JobHandlers\RegistrationsReport'),
3267
-                        'return_url'  => urlencode($this->request->getRequestParam('return_url', '', DataType::URL)),
3268
-                        'filters'     => urlencode(serialize($report_params)),
3269
-                        'use_filters' => $this->request->getRequestParam('use_filters', false, DataType::BOOL)
3270
-                    ]
3271
-                )
3272
-            );
3273
-        } else {
3274
-            // Pull the current request params
3275
-            $request_args = $this->request->requestParams();
3276
-            // Set the required request_args to be passed to the export
3277
-            $required_request_args = [
3278
-                'export' => 'report',
3279
-                'action' => 'registrations_report_for_event',
3280
-                'EVT_ID' => $EVT_ID,
3281
-            ];
3282
-            // Merge required request args, overriding any currently set
3283
-            $request_args = array_merge($request_args, $required_request_args);
3284
-            if (is_readable(EE_CLASSES . 'EE_Export.class.php')) {
3285
-                require_once(EE_CLASSES . 'EE_Export.class.php');
3286
-                $EE_Export = EE_Export::instance($request_args);
3287
-                $EE_Export->export();
3288
-            }
3289
-        }
3290
-    }
3291
-
3292
-
3293
-    /**
3294
-     * Creates a registration report using only query parameters in the request
3295
-     *
3296
-     * @return void
3297
-     */
3298
-    public function _registrations_report()
3299
-    {
3300
-        $this->_registrations_report_base('_get_registration_query_parameters');
3301
-    }
3302
-
3303
-
3304
-    public function _contact_list_export()
3305
-    {
3306
-        if (is_readable(EE_CLASSES . 'EE_Export.class.php')) {
3307
-            require_once(EE_CLASSES . 'EE_Export.class.php');
3308
-            $EE_Export = EE_Export::instance($this->request->requestParams());
3309
-            $EE_Export->export_attendees();
3310
-        }
3311
-    }
3312
-
3313
-
3314
-    public function _contact_list_report()
3315
-    {
3316
-        if (! defined('EE_USE_OLD_CSV_REPORT_CLASS')) {
3317
-            wp_redirect(
3318
-                EE_Admin_Page::add_query_args_and_nonce(
3319
-                    [
3320
-                        'page'        => 'espresso_batch',
3321
-                        'batch'       => 'file',
3322
-                        'job_handler' => urlencode('EventEspressoBatchRequest\JobHandlers\AttendeesReport'),
3323
-                        'return_url'  => urlencode($this->request->getRequestParam('return_url', '', 'url')),
3324
-                    ]
3325
-                )
3326
-            );
3327
-        } else {
3328
-            if (is_readable(EE_CLASSES . 'EE_Export.class.php')) {
3329
-                require_once(EE_CLASSES . 'EE_Export.class.php');
3330
-                $EE_Export = EE_Export::instance($this->request->requestParams());
3331
-                $EE_Export->report_attendees();
3332
-            }
3333
-        }
3334
-    }
3335
-
3336
-
3337
-
3338
-
3339
-
3340
-    /***************************************        ATTENDEE DETAILS        ***************************************/
3341
-    /**
3342
-     * This duplicates the attendee object for the given incoming registration id and attendee_id.
3343
-     *
3344
-     * @return void
3345
-     * @throws EE_Error
3346
-     * @throws InvalidArgumentException
3347
-     * @throws InvalidDataTypeException
3348
-     * @throws InvalidInterfaceException
3349
-     * @throws ReflectionException
3350
-     */
3351
-    protected function _duplicate_attendee()
3352
-    {
3353
-        $REG_ID = $this->request->getRequestParam('_REG_ID', 0, 'int');
3354
-        $action = $this->request->getRequestParam('return', 'default');
3355
-        // verify we have necessary info
3356
-        if (! $REG_ID) {
3357
-            EE_Error::add_error(
3358
-                esc_html__(
3359
-                    'Unable to create the contact for the registration because the required parameters are not present (_REG_ID )',
3360
-                    'event_espresso'
3361
-                ),
3362
-                __FILE__,
3363
-                __LINE__,
3364
-                __FUNCTION__
3365
-            );
3366
-            $query_args = ['action' => $action];
3367
-            $this->_redirect_after_action('', '', '', $query_args, true);
3368
-        }
3369
-        // okay necessary deets present... let's dupe the incoming attendee and attach to incoming registration.
3370
-        $registration = $this->getRegistrationModel()->get_one_by_ID($REG_ID);
3371
-        if (! $registration instanceof EE_Registration) {
3372
-            throw new RuntimeException(
3373
-                sprintf(
3374
-                    esc_html__(
3375
-                        'Unable to create the contact because a valid registration could not be retrieved for REG ID: %1$d',
3376
-                        'event_espresso'
3377
-                    ),
3378
-                    $REG_ID
3379
-                )
3380
-            );
3381
-        }
3382
-        $attendee = $registration->attendee();
3383
-        // remove relation of existing attendee on registration
3384
-        $registration->_remove_relation_to($attendee, 'Attendee');
3385
-        // new attendee
3386
-        $new_attendee = clone $attendee;
3387
-        $new_attendee->set('ATT_ID', 0);
3388
-        $new_attendee->save();
3389
-        // add new attendee to reg
3390
-        $registration->_add_relation_to($new_attendee, 'Attendee');
3391
-        EE_Error::add_success(
3392
-            esc_html__(
3393
-                'New Contact record created.  Now make any edits you wish to make for this contact.',
3394
-                'event_espresso'
3395
-            )
3396
-        );
3397
-        // redirect to edit page for attendee
3398
-        $query_args = ['post' => $new_attendee->ID(), 'action' => 'edit_attendee'];
3399
-        $this->_redirect_after_action('', '', '', $query_args, true);
3400
-    }
3401
-
3402
-
3403
-    /**
3404
-     * Callback invoked by parent EE_Admin_CPT class hooked in on `save_post` wp hook.
3405
-     *
3406
-     * @param int     $post_id
3407
-     * @param WP_Post $post
3408
-     * @throws DomainException
3409
-     * @throws EE_Error
3410
-     * @throws InvalidArgumentException
3411
-     * @throws InvalidDataTypeException
3412
-     * @throws InvalidInterfaceException
3413
-     * @throws LogicException
3414
-     * @throws InvalidFormSubmissionException
3415
-     * @throws ReflectionException
3416
-     */
3417
-    protected function _insert_update_cpt_item($post_id, $post)
3418
-    {
3419
-        $success  = true;
3420
-        $attendee = $post instanceof WP_Post && $post->post_type === 'espresso_attendees'
3421
-            ? $this->getAttendeeModel()->get_one_by_ID($post_id)
3422
-            : null;
3423
-        // for attendee updates
3424
-        if ($attendee instanceof EE_Attendee) {
3425
-            // note we should only be UPDATING attendees at this point.
3426
-            $fname          = $this->request->getRequestParam('ATT_fname', '');
3427
-            $lname          = $this->request->getRequestParam('ATT_lname', '');
3428
-            $updated_fields = [
3429
-                'ATT_fname'     => $fname,
3430
-                'ATT_lname'     => $lname,
3431
-                'ATT_full_name' => "{$fname} {$lname}",
3432
-                'ATT_address'   => $this->request->getRequestParam('ATT_address', ''),
3433
-                'ATT_address2'  => $this->request->getRequestParam('ATT_address2', ''),
3434
-                'ATT_city'      => $this->request->getRequestParam('ATT_city', ''),
3435
-                'STA_ID'        => $this->request->getRequestParam('STA_ID', ''),
3436
-                'CNT_ISO'       => $this->request->getRequestParam('CNT_ISO', ''),
3437
-                'ATT_zip'       => $this->request->getRequestParam('ATT_zip', ''),
3438
-            ];
3439
-            foreach ($updated_fields as $field => $value) {
3440
-                $attendee->set($field, $value);
3441
-            }
3442
-
3443
-            // process contact details metabox form handler (which will also save the attendee)
3444
-            $contact_details_form = $this->getAttendeeContactDetailsMetaboxFormHandler($attendee);
3445
-            $success              = $contact_details_form->process($this->request->requestParams());
3446
-
3447
-            $attendee_update_callbacks = apply_filters(
3448
-                'FHEE__Registrations_Admin_Page__insert_update_cpt_item__attendee_update',
3449
-                []
3450
-            );
3451
-            foreach ($attendee_update_callbacks as $a_callback) {
3452
-                if (false === call_user_func_array($a_callback, [$attendee, $this->request->requestParams()])) {
3453
-                    throw new EE_Error(
3454
-                        sprintf(
3455
-                            esc_html__(
3456
-                                'The %s callback given for the "FHEE__Registrations_Admin_Page__insert_update_cpt_item__attendee_update" filter is not a valid callback.  Please check the spelling.',
3457
-                                'event_espresso'
3458
-                            ),
3459
-                            $a_callback
3460
-                        )
3461
-                    );
3462
-                }
3463
-            }
3464
-        }
3465
-
3466
-        if ($success === false) {
3467
-            EE_Error::add_error(
3468
-                esc_html__(
3469
-                    'Something went wrong with updating the meta table data for the registration.',
3470
-                    'event_espresso'
3471
-                ),
3472
-                __FILE__,
3473
-                __FUNCTION__,
3474
-                __LINE__
3475
-            );
3476
-        }
3477
-    }
3478
-
3479
-
3480
-    public function trash_cpt_item($post_id)
3481
-    {
3482
-    }
3483
-
3484
-
3485
-    public function delete_cpt_item($post_id)
3486
-    {
3487
-    }
3488
-
3489
-
3490
-    public function restore_cpt_item($post_id)
3491
-    {
3492
-    }
3493
-
3494
-
3495
-    protected function _restore_cpt_item($post_id, $revision_id)
3496
-    {
3497
-    }
3498
-
3499
-
3500
-    /**
3501
-     * @throws EE_Error
3502
-     * @throws ReflectionException
3503
-     * @since 4.10.2.p
3504
-     */
3505
-    public function attendee_editor_metaboxes()
3506
-    {
3507
-        $this->verify_cpt_object();
3508
-        remove_meta_box(
3509
-            'postexcerpt',
3510
-            $this->_cpt_routes[ $this->_req_action ],
3511
-            'normal'
3512
-        );
3513
-        remove_meta_box('commentstatusdiv', $this->_cpt_routes[ $this->_req_action ], 'normal');
3514
-        if (post_type_supports('espresso_attendees', 'excerpt')) {
3515
-            add_meta_box(
3516
-                'postexcerpt',
3517
-                esc_html__('Short Biography', 'event_espresso'),
3518
-                'post_excerpt_meta_box',
3519
-                $this->_cpt_routes[ $this->_req_action ],
3520
-                'normal'
3521
-            );
3522
-        }
3523
-        if (post_type_supports('espresso_attendees', 'comments')) {
3524
-            add_meta_box(
3525
-                'commentsdiv',
3526
-                esc_html__('Notes on the Contact', 'event_espresso'),
3527
-                'post_comment_meta_box',
3528
-                $this->_cpt_routes[ $this->_req_action ],
3529
-                'normal',
3530
-                'core'
3531
-            );
3532
-        }
3533
-        add_meta_box(
3534
-            'attendee_contact_info',
3535
-            esc_html__('Contact Info', 'event_espresso'),
3536
-            [$this, 'attendee_contact_info'],
3537
-            $this->_cpt_routes[ $this->_req_action ],
3538
-            'side',
3539
-            'core'
3540
-        );
3541
-        add_meta_box(
3542
-            'attendee_details_address',
3543
-            esc_html__('Address Details', 'event_espresso'),
3544
-            [$this, 'attendee_address_details'],
3545
-            $this->_cpt_routes[ $this->_req_action ],
3546
-            'normal',
3547
-            'core'
3548
-        );
3549
-        add_meta_box(
3550
-            'attendee_registrations',
3551
-            esc_html__('Registrations for this Contact', 'event_espresso'),
3552
-            [$this, 'attendee_registrations_meta_box'],
3553
-            $this->_cpt_routes[ $this->_req_action ],
3554
-            'normal',
3555
-            'high'
3556
-        );
3557
-    }
3558
-
3559
-
3560
-    /**
3561
-     * Metabox for attendee contact info
3562
-     *
3563
-     * @param WP_Post $post wp post object
3564
-     * @return void attendee contact info ( and form )
3565
-     * @throws EE_Error
3566
-     * @throws InvalidArgumentException
3567
-     * @throws InvalidDataTypeException
3568
-     * @throws InvalidInterfaceException
3569
-     * @throws LogicException
3570
-     * @throws DomainException
3571
-     */
3572
-    public function attendee_contact_info($post)
3573
-    {
3574
-        // get attendee object ( should already have it )
3575
-        $form = $this->getAttendeeContactDetailsMetaboxFormHandler($this->_cpt_model_obj);
3576
-        $form->enqueueStylesAndScripts();
3577
-        echo wp_kses($form->display(), AllowedTags::getWithFormTags());
3578
-    }
3579
-
3580
-
3581
-    /**
3582
-     * Return form handler for the contact details metabox
3583
-     *
3584
-     * @param EE_Attendee $attendee
3585
-     * @return AttendeeContactDetailsMetaboxFormHandler
3586
-     * @throws DomainException
3587
-     * @throws InvalidArgumentException
3588
-     * @throws InvalidDataTypeException
3589
-     * @throws InvalidInterfaceException
3590
-     */
3591
-    protected function getAttendeeContactDetailsMetaboxFormHandler(EE_Attendee $attendee)
3592
-    {
3593
-        return new AttendeeContactDetailsMetaboxFormHandler($attendee, EE_Registry::instance());
3594
-    }
3595
-
3596
-
3597
-    /**
3598
-     * Metabox for attendee details
3599
-     *
3600
-     * @param WP_Post $post wp post object
3601
-     * @throws EE_Error
3602
-     * @throws ReflectionException
3603
-     */
3604
-    public function attendee_address_details($post)
3605
-    {
3606
-        // get attendee object (should already have it)
3607
-        $this->_template_args['attendee']     = $this->_cpt_model_obj;
3608
-        $this->_template_args['state_html']   = EEH_Form_Fields::generate_form_input(
3609
-            new EE_Question_Form_Input(
3610
-                EE_Question::new_instance(
3611
-                    [
3612
-                        'QST_ID'           => 0,
3613
-                        'QST_display_text' => esc_html__('State/Province', 'event_espresso'),
3614
-                        'QST_system'       => 'admin-state',
3615
-                    ]
3616
-                ),
3617
-                EE_Answer::new_instance(
3618
-                    [
3619
-                        'ANS_ID'    => 0,
3620
-                        'ANS_value' => $this->_cpt_model_obj->state_ID(),
3621
-                    ]
3622
-                ),
3623
-                [
3624
-                    'input_id'       => 'STA_ID',
3625
-                    'input_name'     => 'STA_ID',
3626
-                    'input_prefix'   => '',
3627
-                    'append_qstn_id' => false,
3628
-                ]
3629
-            )
3630
-        );
3631
-        $this->_template_args['country_html'] = EEH_Form_Fields::generate_form_input(
3632
-            new EE_Question_Form_Input(
3633
-                EE_Question::new_instance(
3634
-                    [
3635
-                        'QST_ID'           => 0,
3636
-                        'QST_display_text' => esc_html__('Country', 'event_espresso'),
3637
-                        'QST_system'       => 'admin-country',
3638
-                    ]
3639
-                ),
3640
-                EE_Answer::new_instance(
3641
-                    [
3642
-                        'ANS_ID'    => 0,
3643
-                        'ANS_value' => $this->_cpt_model_obj->country_ID(),
3644
-                    ]
3645
-                ),
3646
-                [
3647
-                    'input_id'       => 'CNT_ISO',
3648
-                    'input_name'     => 'CNT_ISO',
3649
-                    'input_prefix'   => '',
3650
-                    'append_qstn_id' => false,
3651
-                ]
3652
-            )
3653
-        );
3654
-        $template                             =
3655
-            REG_TEMPLATE_PATH . 'attendee_address_details_metabox_content.template.php';
3656
-        EEH_Template::display_template($template, $this->_template_args);
3657
-    }
3658
-
3659
-
3660
-    /**
3661
-     * _attendee_details
3662
-     *
3663
-     * @param $post
3664
-     * @return void
3665
-     * @throws DomainException
3666
-     * @throws EE_Error
3667
-     * @throws InvalidArgumentException
3668
-     * @throws InvalidDataTypeException
3669
-     * @throws InvalidInterfaceException
3670
-     * @throws ReflectionException
3671
-     */
3672
-    public function attendee_registrations_meta_box($post)
3673
-    {
3674
-        $this->_template_args['attendee']      = $this->_cpt_model_obj;
3675
-        $this->_template_args['registrations'] = $this->_cpt_model_obj->get_many_related('Registration');
3676
-        $template                              =
3677
-            REG_TEMPLATE_PATH . 'attendee_registrations_main_meta_box.template.php';
3678
-        EEH_Template::display_template($template, $this->_template_args);
3679
-    }
3680
-
3681
-
3682
-    /**
3683
-     * add in the form fields for the attendee edit
3684
-     *
3685
-     * @param WP_Post $post wp post object
3686
-     * @return void echos html for new form.
3687
-     * @throws DomainException
3688
-     */
3689
-    public function after_title_form_fields($post)
3690
-    {
3691
-        if ($post->post_type === 'espresso_attendees') {
3692
-            $template                  = REG_TEMPLATE_PATH . 'attendee_details_after_title_form_fields.template.php';
3693
-            $template_args['attendee'] = $this->_cpt_model_obj;
3694
-            EEH_Template::display_template($template, $template_args);
3695
-        }
3696
-    }
3697
-
3698
-
3699
-    /**
3700
-     * _trash_or_restore_attendee
3701
-     *
3702
-     * @param boolean $trash - whether to move item to trash (TRUE) or restore it (FALSE)
3703
-     * @return void
3704
-     * @throws EE_Error
3705
-     * @throws InvalidArgumentException
3706
-     * @throws InvalidDataTypeException
3707
-     * @throws InvalidInterfaceException
3708
-     */
3709
-    protected function _trash_or_restore_attendees($trash = true)
3710
-    {
3711
-        do_action('AHEE_log', __FILE__, __FUNCTION__, '');
3712
-        $status = $trash ? 'trash' : 'publish';
3713
-        // Checkboxes
3714
-        if ($this->request->requestParamIsSet('checkbox')) {
3715
-            $ATT_IDs = $this->request->getRequestParam('checkbox', [], 'int', true);
3716
-            // if array has more than one element than success message should be plural
3717
-            $success = count($ATT_IDs) > 1 ? 2 : 1;
3718
-            // cycle thru checkboxes
3719
-            foreach ($ATT_IDs as $ATT_ID) {
3720
-                $updated = $this->getAttendeeModel()->update_by_ID(['status' => $status], $ATT_ID);
3721
-                if (! $updated) {
3722
-                    $success = 0;
3723
-                }
3724
-            }
3725
-        } else {
3726
-            // grab single id and delete
3727
-            $ATT_ID = $this->request->getRequestParam('ATT_ID', 0, 'int');
3728
-            // update attendee
3729
-            $success = $this->getAttendeeModel()->update_by_ID(['status' => $status], $ATT_ID) ? 1 : 0;
3730
-        }
3731
-        $what        = $success > 1
3732
-            ? esc_html__('Contacts', 'event_espresso')
3733
-            : esc_html__('Contact', 'event_espresso');
3734
-        $action_desc = $trash
3735
-            ? esc_html__('moved to the trash', 'event_espresso')
3736
-            : esc_html__('restored', 'event_espresso');
3737
-        $this->_redirect_after_action($success, $what, $action_desc, ['action' => 'contact_list']);
3738
-    }
2897
+		}
2898
+		$template_args = [
2899
+			'title'                    => '',
2900
+			'content'                  => '',
2901
+			'step_button_text'         => '',
2902
+			'show_notification_toggle' => false,
2903
+		];
2904
+		// to indicate we're processing a new registration
2905
+		$hidden_fields = [
2906
+			'processing_registration' => [
2907
+				'type'  => 'hidden',
2908
+				'value' => 0,
2909
+			],
2910
+			'event_id'                => [
2911
+				'type'  => 'hidden',
2912
+				'value' => $this->_reg_event->ID(),
2913
+			],
2914
+		];
2915
+		// if the cart is empty then we know we're at step one, so we'll display the ticket selector
2916
+		$cart = EE_Registry::instance()->SSN->cart();
2917
+		$step = ! $cart instanceof EE_Cart ? 'ticket' : 'questions';
2918
+		switch ($step) {
2919
+			case 'ticket':
2920
+				$hidden_fields['processing_registration']['value'] = 1;
2921
+				$template_args['title']                            = esc_html__(
2922
+					'Step One: Select the Ticket for this registration',
2923
+					'event_espresso'
2924
+				);
2925
+				$template_args['content']                          =
2926
+					EED_Ticket_Selector::instance()->display_ticket_selector($this->_reg_event);
2927
+				$template_args['content']                          .= '</div>';
2928
+				$template_args['step_button_text']                 = esc_html__(
2929
+					'Add Tickets and Continue to Registrant Details',
2930
+					'event_espresso'
2931
+				);
2932
+				$template_args['show_notification_toggle']         = false;
2933
+				break;
2934
+			case 'questions':
2935
+				$hidden_fields['processing_registration']['value'] = 2;
2936
+				$template_args['title']                            = esc_html__(
2937
+					'Step Two: Add Registrant Details for this Registration',
2938
+					'event_espresso'
2939
+				);
2940
+				// in theory, we should be able to run EED_SPCO at this point
2941
+				// because the cart should have been set up properly by the first process_reg_step run.
2942
+				$template_args['content']                  =
2943
+					EED_Single_Page_Checkout::registration_checkout_for_admin();
2944
+				$template_args['step_button_text']         = esc_html__(
2945
+					'Save Registration and Continue to Details',
2946
+					'event_espresso'
2947
+				);
2948
+				$template_args['show_notification_toggle'] = true;
2949
+				break;
2950
+		}
2951
+		// we come back to the process_registration_step route.
2952
+		$this->_set_add_edit_form_tags('process_reg_step', $hidden_fields);
2953
+		return EEH_Template::display_template(
2954
+			REG_TEMPLATE_PATH . 'reg_admin_register_new_attendee_step_content.template.php',
2955
+			$template_args,
2956
+			true
2957
+		);
2958
+	}
2959
+
2960
+
2961
+	/**
2962
+	 * set_reg_event
2963
+	 *
2964
+	 * @return bool
2965
+	 * @throws EE_Error
2966
+	 * @throws InvalidArgumentException
2967
+	 * @throws InvalidDataTypeException
2968
+	 * @throws InvalidInterfaceException
2969
+	 */
2970
+	private function _set_reg_event()
2971
+	{
2972
+		if (is_object($this->_reg_event)) {
2973
+			return true;
2974
+		}
2975
+
2976
+		$EVT_ID = $this->request->getRequestParam('event_id', 0, 'int');
2977
+		if (! $EVT_ID) {
2978
+			return false;
2979
+		}
2980
+		$this->_reg_event = $this->getEventModel()->get_one_by_ID($EVT_ID);
2981
+		return true;
2982
+	}
2983
+
2984
+
2985
+	/**
2986
+	 * process_reg_step
2987
+	 *
2988
+	 * @return void
2989
+	 * @throws DomainException
2990
+	 * @throws EE_Error
2991
+	 * @throws InvalidArgumentException
2992
+	 * @throws InvalidDataTypeException
2993
+	 * @throws InvalidInterfaceException
2994
+	 * @throws ReflectionException
2995
+	 * @throws RuntimeException
2996
+	 */
2997
+	public function process_reg_step()
2998
+	{
2999
+		EE_System::do_not_cache();
3000
+		$this->_set_reg_event();
3001
+		/** @var CurrentPage $current_page */
3002
+		$current_page = $this->loader->getShared(CurrentPage::class);
3003
+		$current_page->setEspressoPage(true);
3004
+		$this->request->setRequestParam('uts', time());
3005
+		// what step are we on?
3006
+		$cart = EE_Registry::instance()->SSN->cart();
3007
+		$step = ! $cart instanceof EE_Cart ? 'ticket' : 'questions';
3008
+		// if doing ajax then we need to verify the nonce
3009
+		if ($this->request->isAjax()) {
3010
+			$nonce = $this->request->getRequestParam($this->_req_nonce, '');
3011
+			$this->_verify_nonce($nonce, $this->_req_nonce);
3012
+		}
3013
+		switch ($step) {
3014
+			case 'ticket':
3015
+				// process ticket selection
3016
+				$success = EED_Ticket_Selector::instance()->process_ticket_selections();
3017
+				if ($success) {
3018
+					EE_Error::add_success(
3019
+						esc_html__(
3020
+							'Tickets Selected. Now complete the registration.',
3021
+							'event_espresso'
3022
+						)
3023
+					);
3024
+				} else {
3025
+					$this->request->setRequestParam('step_error', true);
3026
+					$query_args['step_error'] = $this->request->getRequestParam('step_error', true, 'bool');
3027
+				}
3028
+				if ($this->request->isAjax()) {
3029
+					$this->new_registration(); // display next step
3030
+				} else {
3031
+					$query_args = [
3032
+						'action'                  => 'new_registration',
3033
+						'processing_registration' => 1,
3034
+						'event_id'                => $this->_reg_event->ID(),
3035
+						'uts'                     => time(),
3036
+					];
3037
+					$this->_redirect_after_action(
3038
+						false,
3039
+						'',
3040
+						'',
3041
+						$query_args,
3042
+						true
3043
+					);
3044
+				}
3045
+				break;
3046
+			case 'questions':
3047
+				if (! $this->request->requestParamIsSet('txn_reg_status_change[send_notifications]')) {
3048
+					add_filter('FHEE__EED_Messages___maybe_registration__deliver_notifications', '__return_false', 15);
3049
+				}
3050
+				// process registration
3051
+				$transaction = EED_Single_Page_Checkout::instance()->process_registration_from_admin();
3052
+				if ($cart instanceof EE_Cart) {
3053
+					$grand_total = $cart->get_grand_total();
3054
+					if ($grand_total instanceof EE_Line_Item) {
3055
+						$grand_total->save_this_and_descendants_to_txn();
3056
+					}
3057
+				}
3058
+				if (! $transaction instanceof EE_Transaction) {
3059
+					$query_args = [
3060
+						'action'                  => 'new_registration',
3061
+						'processing_registration' => 2,
3062
+						'event_id'                => $this->_reg_event->ID(),
3063
+						'uts'                     => time(),
3064
+					];
3065
+					if ($this->request->isAjax()) {
3066
+						// display registration form again because there are errors (maybe validation?)
3067
+						$this->new_registration();
3068
+						return;
3069
+					}
3070
+					$this->_redirect_after_action(
3071
+						false,
3072
+						'',
3073
+						'',
3074
+						$query_args,
3075
+						true
3076
+					);
3077
+					return;
3078
+				}
3079
+				// maybe update status, and make sure to save transaction if not done already
3080
+				if (! $transaction->update_status_based_on_total_paid()) {
3081
+					$transaction->save();
3082
+				}
3083
+				EE_Registry::instance()->SSN->clear_session(__CLASS__, __FUNCTION__);
3084
+				$query_args = [
3085
+					'action'        => 'redirect_to_txn',
3086
+					'TXN_ID'        => $transaction->ID(),
3087
+					'EVT_ID'        => $this->_reg_event->ID(),
3088
+					'event_name'    => urlencode($this->_reg_event->name()),
3089
+					'redirect_from' => 'new_registration',
3090
+				];
3091
+				$this->_redirect_after_action(false, '', '', $query_args, true);
3092
+				break;
3093
+		}
3094
+		// what are you looking here for?  Should be nothing to do at this point.
3095
+	}
3096
+
3097
+
3098
+	/**
3099
+	 * redirect_to_txn
3100
+	 *
3101
+	 * @return void
3102
+	 * @throws EE_Error
3103
+	 * @throws InvalidArgumentException
3104
+	 * @throws InvalidDataTypeException
3105
+	 * @throws InvalidInterfaceException
3106
+	 * @throws ReflectionException
3107
+	 */
3108
+	public function redirect_to_txn()
3109
+	{
3110
+		EE_System::do_not_cache();
3111
+		EE_Registry::instance()->SSN->clear_session(__CLASS__, __FUNCTION__);
3112
+		$query_args = [
3113
+			'action' => 'view_transaction',
3114
+			'TXN_ID' => $this->request->getRequestParam('TXN_ID', 0, 'int'),
3115
+			'page'   => 'espresso_transactions',
3116
+		];
3117
+		if ($this->request->requestParamIsSet('EVT_ID') && $this->request->requestParamIsSet('redirect_from')) {
3118
+			$query_args['EVT_ID']        = $this->request->getRequestParam('EVT_ID', 0, 'int');
3119
+			$query_args['event_name']    = urlencode($this->request->getRequestParam('event_name'));
3120
+			$query_args['redirect_from'] = $this->request->getRequestParam('redirect_from');
3121
+		}
3122
+		EE_Error::add_success(
3123
+			esc_html__(
3124
+				'Registration Created.  Please review the transaction and add any payments as necessary',
3125
+				'event_espresso'
3126
+			)
3127
+		);
3128
+		$this->_redirect_after_action(false, '', '', $query_args, true);
3129
+	}
3130
+
3131
+
3132
+	/**
3133
+	 * generates HTML for the Attendee Contact List
3134
+	 *
3135
+	 * @return void
3136
+	 * @throws DomainException
3137
+	 * @throws EE_Error
3138
+	 */
3139
+	protected function _attendee_contact_list_table()
3140
+	{
3141
+		do_action('AHEE_log', __FILE__, __FUNCTION__, '');
3142
+		$this->_search_btn_label = esc_html__('Contacts', 'event_espresso');
3143
+		$this->display_admin_list_table_page_with_no_sidebar();
3144
+	}
3145
+
3146
+
3147
+	/**
3148
+	 * get_attendees
3149
+	 *
3150
+	 * @param      $per_page
3151
+	 * @param bool $count whether to return count or data.
3152
+	 * @param bool $trash
3153
+	 * @return array|int
3154
+	 * @throws EE_Error
3155
+	 * @throws InvalidArgumentException
3156
+	 * @throws InvalidDataTypeException
3157
+	 * @throws InvalidInterfaceException
3158
+	 */
3159
+	public function get_attendees($per_page, $count = false, $trash = false)
3160
+	{
3161
+		do_action('AHEE_log', __FILE__, __FUNCTION__, '');
3162
+		require_once(REG_ADMIN . 'EE_Attendee_Contact_List_Table.class.php');
3163
+		$orderby = $this->request->getRequestParam('orderby');
3164
+		switch ($orderby) {
3165
+			case 'ATT_ID':
3166
+			case 'ATT_fname':
3167
+			case 'ATT_email':
3168
+			case 'ATT_city':
3169
+			case 'STA_ID':
3170
+			case 'CNT_ID':
3171
+				break;
3172
+			case 'Registration_Count':
3173
+				$orderby = 'Registration_Count';
3174
+				break;
3175
+			default:
3176
+				$orderby = 'ATT_lname';
3177
+		}
3178
+		$sort         = $this->request->getRequestParam('order', 'ASC');
3179
+		$current_page = $this->request->getRequestParam('paged', 1, 'int');
3180
+		$per_page     = absint($per_page) ? $per_page : 10;
3181
+		$per_page     = $this->request->getRequestParam('perpage', $per_page, 'int');
3182
+		$_where       = [];
3183
+		$search_term  = $this->request->getRequestParam('s');
3184
+		if ($search_term) {
3185
+			$search_term  = '%' . $search_term . '%';
3186
+			$_where['OR'] = [
3187
+				'Registration.Event.EVT_name'       => ['LIKE', $search_term],
3188
+				'Registration.Event.EVT_desc'       => ['LIKE', $search_term],
3189
+				'Registration.Event.EVT_short_desc' => ['LIKE', $search_term],
3190
+				'ATT_fname'                         => ['LIKE', $search_term],
3191
+				'ATT_lname'                         => ['LIKE', $search_term],
3192
+				'ATT_short_bio'                     => ['LIKE', $search_term],
3193
+				'ATT_email'                         => ['LIKE', $search_term],
3194
+				'ATT_address'                       => ['LIKE', $search_term],
3195
+				'ATT_address2'                      => ['LIKE', $search_term],
3196
+				'ATT_city'                          => ['LIKE', $search_term],
3197
+				'Country.CNT_name'                  => ['LIKE', $search_term],
3198
+				'State.STA_name'                    => ['LIKE', $search_term],
3199
+				'ATT_phone'                         => ['LIKE', $search_term],
3200
+				'Registration.REG_final_price'      => ['LIKE', $search_term],
3201
+				'Registration.REG_code'             => ['LIKE', $search_term],
3202
+				'Registration.REG_group_size'       => ['LIKE', $search_term],
3203
+			];
3204
+		}
3205
+		$offset     = ($current_page - 1) * $per_page;
3206
+		$limit      = $count ? null : [$offset, $per_page];
3207
+		$query_args = [
3208
+			$_where,
3209
+			'extra_selects' => ['Registration_Count' => ['Registration.REG_ID', 'count', '%d']],
3210
+			'limit'         => $limit,
3211
+		];
3212
+		if (! $count) {
3213
+			$query_args['order_by'] = [$orderby => $sort];
3214
+		}
3215
+		$query_args[0]['status'] = $trash ? ['!=', 'publish'] : ['IN', ['publish']];
3216
+		return $count
3217
+			? $this->getAttendeeModel()->count($query_args, 'ATT_ID', true)
3218
+			: $this->getAttendeeModel()->get_all($query_args);
3219
+	}
3220
+
3221
+
3222
+	/**
3223
+	 * This is just taking care of resending the registration confirmation
3224
+	 *
3225
+	 * @return void
3226
+	 * @throws EE_Error
3227
+	 * @throws InvalidArgumentException
3228
+	 * @throws InvalidDataTypeException
3229
+	 * @throws InvalidInterfaceException
3230
+	 * @throws ReflectionException
3231
+	 */
3232
+	protected function _resend_registration()
3233
+	{
3234
+		$this->_process_resend_registration();
3235
+		$REG_ID      = $this->request->getRequestParam('_REG_ID', 0, 'int');
3236
+		$redirect_to = $this->request->getRequestParam('redirect_to');
3237
+		$query_args  = $redirect_to
3238
+			? ['action' => $redirect_to, '_REG_ID' => $REG_ID]
3239
+			: ['action' => 'default'];
3240
+		$this->_redirect_after_action(false, '', '', $query_args, true);
3241
+	}
3242
+
3243
+
3244
+	/**
3245
+	 * Creates a registration report, but accepts the name of a method to use for preparing the query parameters
3246
+	 * to use when selecting registrations
3247
+	 *
3248
+	 * @param string $method_name_for_getting_query_params the name of the method (on this class) to use for preparing
3249
+	 *                                                     the query parameters from the request
3250
+	 * @return void ends the request with a redirect or download
3251
+	 */
3252
+	public function _registrations_report_base($method_name_for_getting_query_params)
3253
+	{
3254
+		$EVT_ID = $this->request->requestParamIsSet('EVT_ID')
3255
+			? $this->request->getRequestParam('EVT_ID', 0, 'int')
3256
+			: null;
3257
+		if (! defined('EE_USE_OLD_CSV_REPORT_CLASS')) {
3258
+			$filters = $this->request->getRequestParam('filters', [], DataType::STRING, true);
3259
+			$report_params  = $this->$method_name_for_getting_query_params($filters);
3260
+			wp_redirect(
3261
+				EE_Admin_Page::add_query_args_and_nonce(
3262
+					[
3263
+						'page'        => 'espresso_batch',
3264
+						'batch'       => 'file',
3265
+						'EVT_ID'      => $EVT_ID,
3266
+						'job_handler' => urlencode('EventEspressoBatchRequest\JobHandlers\RegistrationsReport'),
3267
+						'return_url'  => urlencode($this->request->getRequestParam('return_url', '', DataType::URL)),
3268
+						'filters'     => urlencode(serialize($report_params)),
3269
+						'use_filters' => $this->request->getRequestParam('use_filters', false, DataType::BOOL)
3270
+					]
3271
+				)
3272
+			);
3273
+		} else {
3274
+			// Pull the current request params
3275
+			$request_args = $this->request->requestParams();
3276
+			// Set the required request_args to be passed to the export
3277
+			$required_request_args = [
3278
+				'export' => 'report',
3279
+				'action' => 'registrations_report_for_event',
3280
+				'EVT_ID' => $EVT_ID,
3281
+			];
3282
+			// Merge required request args, overriding any currently set
3283
+			$request_args = array_merge($request_args, $required_request_args);
3284
+			if (is_readable(EE_CLASSES . 'EE_Export.class.php')) {
3285
+				require_once(EE_CLASSES . 'EE_Export.class.php');
3286
+				$EE_Export = EE_Export::instance($request_args);
3287
+				$EE_Export->export();
3288
+			}
3289
+		}
3290
+	}
3291
+
3292
+
3293
+	/**
3294
+	 * Creates a registration report using only query parameters in the request
3295
+	 *
3296
+	 * @return void
3297
+	 */
3298
+	public function _registrations_report()
3299
+	{
3300
+		$this->_registrations_report_base('_get_registration_query_parameters');
3301
+	}
3302
+
3303
+
3304
+	public function _contact_list_export()
3305
+	{
3306
+		if (is_readable(EE_CLASSES . 'EE_Export.class.php')) {
3307
+			require_once(EE_CLASSES . 'EE_Export.class.php');
3308
+			$EE_Export = EE_Export::instance($this->request->requestParams());
3309
+			$EE_Export->export_attendees();
3310
+		}
3311
+	}
3312
+
3313
+
3314
+	public function _contact_list_report()
3315
+	{
3316
+		if (! defined('EE_USE_OLD_CSV_REPORT_CLASS')) {
3317
+			wp_redirect(
3318
+				EE_Admin_Page::add_query_args_and_nonce(
3319
+					[
3320
+						'page'        => 'espresso_batch',
3321
+						'batch'       => 'file',
3322
+						'job_handler' => urlencode('EventEspressoBatchRequest\JobHandlers\AttendeesReport'),
3323
+						'return_url'  => urlencode($this->request->getRequestParam('return_url', '', 'url')),
3324
+					]
3325
+				)
3326
+			);
3327
+		} else {
3328
+			if (is_readable(EE_CLASSES . 'EE_Export.class.php')) {
3329
+				require_once(EE_CLASSES . 'EE_Export.class.php');
3330
+				$EE_Export = EE_Export::instance($this->request->requestParams());
3331
+				$EE_Export->report_attendees();
3332
+			}
3333
+		}
3334
+	}
3335
+
3336
+
3337
+
3338
+
3339
+
3340
+	/***************************************        ATTENDEE DETAILS        ***************************************/
3341
+	/**
3342
+	 * This duplicates the attendee object for the given incoming registration id and attendee_id.
3343
+	 *
3344
+	 * @return void
3345
+	 * @throws EE_Error
3346
+	 * @throws InvalidArgumentException
3347
+	 * @throws InvalidDataTypeException
3348
+	 * @throws InvalidInterfaceException
3349
+	 * @throws ReflectionException
3350
+	 */
3351
+	protected function _duplicate_attendee()
3352
+	{
3353
+		$REG_ID = $this->request->getRequestParam('_REG_ID', 0, 'int');
3354
+		$action = $this->request->getRequestParam('return', 'default');
3355
+		// verify we have necessary info
3356
+		if (! $REG_ID) {
3357
+			EE_Error::add_error(
3358
+				esc_html__(
3359
+					'Unable to create the contact for the registration because the required parameters are not present (_REG_ID )',
3360
+					'event_espresso'
3361
+				),
3362
+				__FILE__,
3363
+				__LINE__,
3364
+				__FUNCTION__
3365
+			);
3366
+			$query_args = ['action' => $action];
3367
+			$this->_redirect_after_action('', '', '', $query_args, true);
3368
+		}
3369
+		// okay necessary deets present... let's dupe the incoming attendee and attach to incoming registration.
3370
+		$registration = $this->getRegistrationModel()->get_one_by_ID($REG_ID);
3371
+		if (! $registration instanceof EE_Registration) {
3372
+			throw new RuntimeException(
3373
+				sprintf(
3374
+					esc_html__(
3375
+						'Unable to create the contact because a valid registration could not be retrieved for REG ID: %1$d',
3376
+						'event_espresso'
3377
+					),
3378
+					$REG_ID
3379
+				)
3380
+			);
3381
+		}
3382
+		$attendee = $registration->attendee();
3383
+		// remove relation of existing attendee on registration
3384
+		$registration->_remove_relation_to($attendee, 'Attendee');
3385
+		// new attendee
3386
+		$new_attendee = clone $attendee;
3387
+		$new_attendee->set('ATT_ID', 0);
3388
+		$new_attendee->save();
3389
+		// add new attendee to reg
3390
+		$registration->_add_relation_to($new_attendee, 'Attendee');
3391
+		EE_Error::add_success(
3392
+			esc_html__(
3393
+				'New Contact record created.  Now make any edits you wish to make for this contact.',
3394
+				'event_espresso'
3395
+			)
3396
+		);
3397
+		// redirect to edit page for attendee
3398
+		$query_args = ['post' => $new_attendee->ID(), 'action' => 'edit_attendee'];
3399
+		$this->_redirect_after_action('', '', '', $query_args, true);
3400
+	}
3401
+
3402
+
3403
+	/**
3404
+	 * Callback invoked by parent EE_Admin_CPT class hooked in on `save_post` wp hook.
3405
+	 *
3406
+	 * @param int     $post_id
3407
+	 * @param WP_Post $post
3408
+	 * @throws DomainException
3409
+	 * @throws EE_Error
3410
+	 * @throws InvalidArgumentException
3411
+	 * @throws InvalidDataTypeException
3412
+	 * @throws InvalidInterfaceException
3413
+	 * @throws LogicException
3414
+	 * @throws InvalidFormSubmissionException
3415
+	 * @throws ReflectionException
3416
+	 */
3417
+	protected function _insert_update_cpt_item($post_id, $post)
3418
+	{
3419
+		$success  = true;
3420
+		$attendee = $post instanceof WP_Post && $post->post_type === 'espresso_attendees'
3421
+			? $this->getAttendeeModel()->get_one_by_ID($post_id)
3422
+			: null;
3423
+		// for attendee updates
3424
+		if ($attendee instanceof EE_Attendee) {
3425
+			// note we should only be UPDATING attendees at this point.
3426
+			$fname          = $this->request->getRequestParam('ATT_fname', '');
3427
+			$lname          = $this->request->getRequestParam('ATT_lname', '');
3428
+			$updated_fields = [
3429
+				'ATT_fname'     => $fname,
3430
+				'ATT_lname'     => $lname,
3431
+				'ATT_full_name' => "{$fname} {$lname}",
3432
+				'ATT_address'   => $this->request->getRequestParam('ATT_address', ''),
3433
+				'ATT_address2'  => $this->request->getRequestParam('ATT_address2', ''),
3434
+				'ATT_city'      => $this->request->getRequestParam('ATT_city', ''),
3435
+				'STA_ID'        => $this->request->getRequestParam('STA_ID', ''),
3436
+				'CNT_ISO'       => $this->request->getRequestParam('CNT_ISO', ''),
3437
+				'ATT_zip'       => $this->request->getRequestParam('ATT_zip', ''),
3438
+			];
3439
+			foreach ($updated_fields as $field => $value) {
3440
+				$attendee->set($field, $value);
3441
+			}
3442
+
3443
+			// process contact details metabox form handler (which will also save the attendee)
3444
+			$contact_details_form = $this->getAttendeeContactDetailsMetaboxFormHandler($attendee);
3445
+			$success              = $contact_details_form->process($this->request->requestParams());
3446
+
3447
+			$attendee_update_callbacks = apply_filters(
3448
+				'FHEE__Registrations_Admin_Page__insert_update_cpt_item__attendee_update',
3449
+				[]
3450
+			);
3451
+			foreach ($attendee_update_callbacks as $a_callback) {
3452
+				if (false === call_user_func_array($a_callback, [$attendee, $this->request->requestParams()])) {
3453
+					throw new EE_Error(
3454
+						sprintf(
3455
+							esc_html__(
3456
+								'The %s callback given for the "FHEE__Registrations_Admin_Page__insert_update_cpt_item__attendee_update" filter is not a valid callback.  Please check the spelling.',
3457
+								'event_espresso'
3458
+							),
3459
+							$a_callback
3460
+						)
3461
+					);
3462
+				}
3463
+			}
3464
+		}
3465
+
3466
+		if ($success === false) {
3467
+			EE_Error::add_error(
3468
+				esc_html__(
3469
+					'Something went wrong with updating the meta table data for the registration.',
3470
+					'event_espresso'
3471
+				),
3472
+				__FILE__,
3473
+				__FUNCTION__,
3474
+				__LINE__
3475
+			);
3476
+		}
3477
+	}
3478
+
3479
+
3480
+	public function trash_cpt_item($post_id)
3481
+	{
3482
+	}
3483
+
3484
+
3485
+	public function delete_cpt_item($post_id)
3486
+	{
3487
+	}
3488
+
3489
+
3490
+	public function restore_cpt_item($post_id)
3491
+	{
3492
+	}
3493
+
3494
+
3495
+	protected function _restore_cpt_item($post_id, $revision_id)
3496
+	{
3497
+	}
3498
+
3499
+
3500
+	/**
3501
+	 * @throws EE_Error
3502
+	 * @throws ReflectionException
3503
+	 * @since 4.10.2.p
3504
+	 */
3505
+	public function attendee_editor_metaboxes()
3506
+	{
3507
+		$this->verify_cpt_object();
3508
+		remove_meta_box(
3509
+			'postexcerpt',
3510
+			$this->_cpt_routes[ $this->_req_action ],
3511
+			'normal'
3512
+		);
3513
+		remove_meta_box('commentstatusdiv', $this->_cpt_routes[ $this->_req_action ], 'normal');
3514
+		if (post_type_supports('espresso_attendees', 'excerpt')) {
3515
+			add_meta_box(
3516
+				'postexcerpt',
3517
+				esc_html__('Short Biography', 'event_espresso'),
3518
+				'post_excerpt_meta_box',
3519
+				$this->_cpt_routes[ $this->_req_action ],
3520
+				'normal'
3521
+			);
3522
+		}
3523
+		if (post_type_supports('espresso_attendees', 'comments')) {
3524
+			add_meta_box(
3525
+				'commentsdiv',
3526
+				esc_html__('Notes on the Contact', 'event_espresso'),
3527
+				'post_comment_meta_box',
3528
+				$this->_cpt_routes[ $this->_req_action ],
3529
+				'normal',
3530
+				'core'
3531
+			);
3532
+		}
3533
+		add_meta_box(
3534
+			'attendee_contact_info',
3535
+			esc_html__('Contact Info', 'event_espresso'),
3536
+			[$this, 'attendee_contact_info'],
3537
+			$this->_cpt_routes[ $this->_req_action ],
3538
+			'side',
3539
+			'core'
3540
+		);
3541
+		add_meta_box(
3542
+			'attendee_details_address',
3543
+			esc_html__('Address Details', 'event_espresso'),
3544
+			[$this, 'attendee_address_details'],
3545
+			$this->_cpt_routes[ $this->_req_action ],
3546
+			'normal',
3547
+			'core'
3548
+		);
3549
+		add_meta_box(
3550
+			'attendee_registrations',
3551
+			esc_html__('Registrations for this Contact', 'event_espresso'),
3552
+			[$this, 'attendee_registrations_meta_box'],
3553
+			$this->_cpt_routes[ $this->_req_action ],
3554
+			'normal',
3555
+			'high'
3556
+		);
3557
+	}
3558
+
3559
+
3560
+	/**
3561
+	 * Metabox for attendee contact info
3562
+	 *
3563
+	 * @param WP_Post $post wp post object
3564
+	 * @return void attendee contact info ( and form )
3565
+	 * @throws EE_Error
3566
+	 * @throws InvalidArgumentException
3567
+	 * @throws InvalidDataTypeException
3568
+	 * @throws InvalidInterfaceException
3569
+	 * @throws LogicException
3570
+	 * @throws DomainException
3571
+	 */
3572
+	public function attendee_contact_info($post)
3573
+	{
3574
+		// get attendee object ( should already have it )
3575
+		$form = $this->getAttendeeContactDetailsMetaboxFormHandler($this->_cpt_model_obj);
3576
+		$form->enqueueStylesAndScripts();
3577
+		echo wp_kses($form->display(), AllowedTags::getWithFormTags());
3578
+	}
3579
+
3580
+
3581
+	/**
3582
+	 * Return form handler for the contact details metabox
3583
+	 *
3584
+	 * @param EE_Attendee $attendee
3585
+	 * @return AttendeeContactDetailsMetaboxFormHandler
3586
+	 * @throws DomainException
3587
+	 * @throws InvalidArgumentException
3588
+	 * @throws InvalidDataTypeException
3589
+	 * @throws InvalidInterfaceException
3590
+	 */
3591
+	protected function getAttendeeContactDetailsMetaboxFormHandler(EE_Attendee $attendee)
3592
+	{
3593
+		return new AttendeeContactDetailsMetaboxFormHandler($attendee, EE_Registry::instance());
3594
+	}
3595
+
3596
+
3597
+	/**
3598
+	 * Metabox for attendee details
3599
+	 *
3600
+	 * @param WP_Post $post wp post object
3601
+	 * @throws EE_Error
3602
+	 * @throws ReflectionException
3603
+	 */
3604
+	public function attendee_address_details($post)
3605
+	{
3606
+		// get attendee object (should already have it)
3607
+		$this->_template_args['attendee']     = $this->_cpt_model_obj;
3608
+		$this->_template_args['state_html']   = EEH_Form_Fields::generate_form_input(
3609
+			new EE_Question_Form_Input(
3610
+				EE_Question::new_instance(
3611
+					[
3612
+						'QST_ID'           => 0,
3613
+						'QST_display_text' => esc_html__('State/Province', 'event_espresso'),
3614
+						'QST_system'       => 'admin-state',
3615
+					]
3616
+				),
3617
+				EE_Answer::new_instance(
3618
+					[
3619
+						'ANS_ID'    => 0,
3620
+						'ANS_value' => $this->_cpt_model_obj->state_ID(),
3621
+					]
3622
+				),
3623
+				[
3624
+					'input_id'       => 'STA_ID',
3625
+					'input_name'     => 'STA_ID',
3626
+					'input_prefix'   => '',
3627
+					'append_qstn_id' => false,
3628
+				]
3629
+			)
3630
+		);
3631
+		$this->_template_args['country_html'] = EEH_Form_Fields::generate_form_input(
3632
+			new EE_Question_Form_Input(
3633
+				EE_Question::new_instance(
3634
+					[
3635
+						'QST_ID'           => 0,
3636
+						'QST_display_text' => esc_html__('Country', 'event_espresso'),
3637
+						'QST_system'       => 'admin-country',
3638
+					]
3639
+				),
3640
+				EE_Answer::new_instance(
3641
+					[
3642
+						'ANS_ID'    => 0,
3643
+						'ANS_value' => $this->_cpt_model_obj->country_ID(),
3644
+					]
3645
+				),
3646
+				[
3647
+					'input_id'       => 'CNT_ISO',
3648
+					'input_name'     => 'CNT_ISO',
3649
+					'input_prefix'   => '',
3650
+					'append_qstn_id' => false,
3651
+				]
3652
+			)
3653
+		);
3654
+		$template                             =
3655
+			REG_TEMPLATE_PATH . 'attendee_address_details_metabox_content.template.php';
3656
+		EEH_Template::display_template($template, $this->_template_args);
3657
+	}
3658
+
3659
+
3660
+	/**
3661
+	 * _attendee_details
3662
+	 *
3663
+	 * @param $post
3664
+	 * @return void
3665
+	 * @throws DomainException
3666
+	 * @throws EE_Error
3667
+	 * @throws InvalidArgumentException
3668
+	 * @throws InvalidDataTypeException
3669
+	 * @throws InvalidInterfaceException
3670
+	 * @throws ReflectionException
3671
+	 */
3672
+	public function attendee_registrations_meta_box($post)
3673
+	{
3674
+		$this->_template_args['attendee']      = $this->_cpt_model_obj;
3675
+		$this->_template_args['registrations'] = $this->_cpt_model_obj->get_many_related('Registration');
3676
+		$template                              =
3677
+			REG_TEMPLATE_PATH . 'attendee_registrations_main_meta_box.template.php';
3678
+		EEH_Template::display_template($template, $this->_template_args);
3679
+	}
3680
+
3681
+
3682
+	/**
3683
+	 * add in the form fields for the attendee edit
3684
+	 *
3685
+	 * @param WP_Post $post wp post object
3686
+	 * @return void echos html for new form.
3687
+	 * @throws DomainException
3688
+	 */
3689
+	public function after_title_form_fields($post)
3690
+	{
3691
+		if ($post->post_type === 'espresso_attendees') {
3692
+			$template                  = REG_TEMPLATE_PATH . 'attendee_details_after_title_form_fields.template.php';
3693
+			$template_args['attendee'] = $this->_cpt_model_obj;
3694
+			EEH_Template::display_template($template, $template_args);
3695
+		}
3696
+	}
3697
+
3698
+
3699
+	/**
3700
+	 * _trash_or_restore_attendee
3701
+	 *
3702
+	 * @param boolean $trash - whether to move item to trash (TRUE) or restore it (FALSE)
3703
+	 * @return void
3704
+	 * @throws EE_Error
3705
+	 * @throws InvalidArgumentException
3706
+	 * @throws InvalidDataTypeException
3707
+	 * @throws InvalidInterfaceException
3708
+	 */
3709
+	protected function _trash_or_restore_attendees($trash = true)
3710
+	{
3711
+		do_action('AHEE_log', __FILE__, __FUNCTION__, '');
3712
+		$status = $trash ? 'trash' : 'publish';
3713
+		// Checkboxes
3714
+		if ($this->request->requestParamIsSet('checkbox')) {
3715
+			$ATT_IDs = $this->request->getRequestParam('checkbox', [], 'int', true);
3716
+			// if array has more than one element than success message should be plural
3717
+			$success = count($ATT_IDs) > 1 ? 2 : 1;
3718
+			// cycle thru checkboxes
3719
+			foreach ($ATT_IDs as $ATT_ID) {
3720
+				$updated = $this->getAttendeeModel()->update_by_ID(['status' => $status], $ATT_ID);
3721
+				if (! $updated) {
3722
+					$success = 0;
3723
+				}
3724
+			}
3725
+		} else {
3726
+			// grab single id and delete
3727
+			$ATT_ID = $this->request->getRequestParam('ATT_ID', 0, 'int');
3728
+			// update attendee
3729
+			$success = $this->getAttendeeModel()->update_by_ID(['status' => $status], $ATT_ID) ? 1 : 0;
3730
+		}
3731
+		$what        = $success > 1
3732
+			? esc_html__('Contacts', 'event_espresso')
3733
+			: esc_html__('Contact', 'event_espresso');
3734
+		$action_desc = $trash
3735
+			? esc_html__('moved to the trash', 'event_espresso')
3736
+			: esc_html__('restored', 'event_espresso');
3737
+		$this->_redirect_after_action($success, $what, $action_desc, ['action' => 'contact_list']);
3738
+	}
3739 3739
 }
Please login to merge, or discard this patch.
modules/single_page_checkout/inc/EE_SPCO_JSON_Response.php 1 patch
Indentation   +397 added lines, -397 removed lines patch added patch discarded remove patch
@@ -14,401 +14,401 @@
 block discarded – undo
14 14
  */
15 15
 class EE_SPCO_JSON_Response
16 16
 {
17
-    /**
18
-     * @var string
19
-     */
20
-    protected $_errors = '';
21
-
22
-    /**
23
-     * @var string
24
-     */
25
-    protected $_unexpected_errors = '';
26
-
27
-    /**
28
-     * @var string
29
-     */
30
-    protected $_attention = '';
31
-
32
-    /**
33
-     * @var string
34
-     */
35
-    protected $_success = '';
36
-
37
-    /**
38
-     * @var string
39
-     */
40
-    protected $_plz_select_method_of_payment = '';
41
-
42
-    /**
43
-     * @var string
44
-     */
45
-    protected $_redirect_url = '';
46
-
47
-    /**
48
-     * @var string
49
-     */
50
-    protected $_registration_time_limit = '';
51
-
52
-    /**
53
-     * @var string
54
-     */
55
-    protected $_redirect_form = '';
56
-
57
-    /**
58
-     * @var string
59
-     */
60
-    protected $_reg_step_html = '';
61
-
62
-    /**
63
-     * @var string
64
-     */
65
-    protected $_method_of_payment = '';
66
-
67
-    /**
68
-     * @var float
69
-     */
70
-    protected $_payment_amount;
71
-
72
-    /**
73
-     * @var array
74
-     */
75
-    protected $_return_data = array();
76
-
77
-
78
-    /**
79
-     * @var array
80
-     */
81
-    protected $_validation_rules = array();
82
-
83
-
84
-    /**
85
-     *    class constructor
86
-     */
87
-    public function __construct()
88
-    {
89
-    }
90
-
91
-
92
-    /**
93
-     *    __toString
94
-     *
95
-     *        allows you to simply echo or print an EE_SPCO_JSON_Response object to produce a  JSON encoded string
96
-     *
97
-     * @access    public
98
-     * @return    string
99
-     */
100
-    public function __toString()
101
-    {
102
-        $JSON_response = array();
103
-        // grab notices
104
-        $notices = EE_Error::get_notices(false);
105
-        $this->set_attention(isset($notices['attention']) ? $notices['attention'] : '');
106
-        $this->set_errors(isset($notices['errors']) ? $notices['errors'] : '');
107
-        $this->set_success(isset($notices['success']) ? $notices['success'] : '');
108
-        // add notices to JSON response, but only if they exist
109
-        if ($this->attention()) {
110
-            $JSON_response['attention'] = $this->attention();
111
-        }
112
-        if ($this->errors()) {
113
-            $JSON_response['errors'] = $this->errors();
114
-        }
115
-        if ($this->unexpected_errors()) {
116
-            $JSON_response['unexpected_errors'] = $this->unexpected_errors();
117
-        }
118
-        if ($this->success()) {
119
-            $JSON_response['success'] = $this->success();
120
-        }
121
-        // but if NO notices are set... at least set the "success" as a key so that the JS knows everything worked
122
-        if (! isset($JSON_response['attention']) && ! isset($JSON_response['errors']) && ! isset($JSON_response['success'])) {
123
-            $JSON_response['success'] = null;
124
-        }
125
-        // set redirect_url, IF it exists
126
-        if ($this->redirect_url()) {
127
-            $JSON_response['redirect_url'] = $this->redirect_url();
128
-        }
129
-        // set registration_time_limit, IF it exists
130
-        if ($this->registration_time_limit()) {
131
-            $JSON_response['registration_time_limit'] = $this->registration_time_limit();
132
-        }
133
-        // set payment_amount, IF it exists
134
-        if ($this->payment_amount() !== null) {
135
-            $JSON_response['payment_amount'] = $this->payment_amount();
136
-        }
137
-        // grab generic return data
138
-        $return_data = $this->return_data();
139
-        // add billing form validation rules
140
-        if ($this->validation_rules()) {
141
-            $return_data['validation_rules'] = $this->validation_rules();
142
-        }
143
-        // set reg_step_html, IF it exists
144
-        if ($this->reg_step_html()) {
145
-            $return_data['reg_step_html'] = $this->reg_step_html();
146
-        }
147
-        // set method of payment, IF it exists
148
-        if ($this->method_of_payment()) {
149
-            $return_data['method_of_payment'] = $this->method_of_payment();
150
-        }
151
-        // set "plz_select_method_of_payment" message, IF it exists
152
-        if ($this->plz_select_method_of_payment()) {
153
-            $return_data['plz_select_method_of_payment'] = $this->plz_select_method_of_payment();
154
-        }
155
-        // set redirect_form, IF it exists
156
-        if ($this->redirect_form()) {
157
-            $return_data['redirect_form'] = $this->redirect_form();
158
-        }
159
-        // and finally, add return_data array to main JSON response array, IF it contains anything
160
-        // why did we add some of the above properties to the return data array?
161
-        // because it is easier and cleaner in the Javascript to deal with this way
162
-        if (! empty($return_data)) {
163
-            $JSON_response['return_data'] = $return_data;
164
-        }
165
-        // filter final array
166
-        $JSON_response = apply_filters('FHEE__EE_SPCO_JSON_Response___toString__JSON_response', $JSON_response);
167
-        // return encoded array
168
-        return (string) wp_json_encode($JSON_response);
169
-    }
170
-
171
-
172
-    /**
173
-     * @param string $attention
174
-     */
175
-    public function set_attention($attention)
176
-    {
177
-        $this->_attention = $attention;
178
-    }
179
-
180
-
181
-    /**
182
-     * @return string
183
-     */
184
-    public function attention()
185
-    {
186
-        return $this->_attention;
187
-    }
188
-
189
-
190
-    /**
191
-     * @param string $errors
192
-     */
193
-    public function set_errors($errors)
194
-    {
195
-        $this->_errors = $errors;
196
-    }
197
-
198
-
199
-    /**
200
-     * @return string
201
-     */
202
-    public function errors()
203
-    {
204
-        return $this->_errors;
205
-    }
206
-
207
-
208
-    /**
209
-     * @return string
210
-     */
211
-    public function unexpected_errors()
212
-    {
213
-        return $this->_unexpected_errors;
214
-    }
215
-
216
-
217
-    /**
218
-     * @param string $unexpected_errors
219
-     */
220
-    public function set_unexpected_errors($unexpected_errors)
221
-    {
222
-        $this->_unexpected_errors = $unexpected_errors;
223
-    }
224
-
225
-
226
-    /**
227
-     * @param string $success
228
-     */
229
-    public function set_success($success)
230
-    {
231
-        $this->_success = $success;
232
-    }
233
-
234
-
235
-    /**
236
-     * @return string
237
-     */
238
-    public function success()
239
-    {
240
-        return $this->_success;
241
-    }
242
-
243
-
244
-    /**
245
-     * @param string $method_of_payment
246
-     */
247
-    public function set_method_of_payment($method_of_payment)
248
-    {
249
-        $this->_method_of_payment = $method_of_payment;
250
-    }
251
-
252
-
253
-    /**
254
-     * @return string
255
-     */
256
-    public function method_of_payment()
257
-    {
258
-        return $this->_method_of_payment;
259
-    }
260
-
261
-
262
-    /**
263
-     * @return float
264
-     */
265
-    public function payment_amount()
266
-    {
267
-        return $this->_payment_amount;
268
-    }
269
-
270
-
271
-    /**
272
-     * @param float $payment_amount
273
-     * @throws EE_Error
274
-     */
275
-    public function set_payment_amount($payment_amount)
276
-    {
277
-        $this->_payment_amount = (float) $payment_amount;
278
-    }
279
-
280
-
281
-    /**
282
-     * @param string $next_step_html
283
-     */
284
-    public function set_reg_step_html($next_step_html)
285
-    {
286
-        $this->_reg_step_html = $next_step_html;
287
-    }
288
-
289
-
290
-    /**
291
-     * @return string
292
-     */
293
-    public function reg_step_html()
294
-    {
295
-        return $this->_reg_step_html;
296
-    }
297
-
298
-
299
-    /**
300
-     * @param string $redirect_form
301
-     */
302
-    public function set_redirect_form($redirect_form)
303
-    {
304
-        $this->_redirect_form = $redirect_form;
305
-    }
306
-
307
-
308
-    /**
309
-     * @return string
310
-     */
311
-    public function redirect_form()
312
-    {
313
-        return ! empty($this->_redirect_form) ? $this->_redirect_form : false;
314
-    }
315
-
316
-
317
-    /**
318
-     * @param string $plz_select_method_of_payment
319
-     */
320
-    public function set_plz_select_method_of_payment($plz_select_method_of_payment)
321
-    {
322
-        $this->_plz_select_method_of_payment = $plz_select_method_of_payment;
323
-    }
324
-
325
-
326
-    /**
327
-     * @return string
328
-     */
329
-    public function plz_select_method_of_payment()
330
-    {
331
-        return $this->_plz_select_method_of_payment;
332
-    }
333
-
334
-
335
-    /**
336
-     * @param string $redirect_url
337
-     */
338
-    public function set_redirect_url($redirect_url)
339
-    {
340
-        $this->_redirect_url = $redirect_url;
341
-    }
342
-
343
-
344
-    /**
345
-     * @return string
346
-     */
347
-    public function redirect_url()
348
-    {
349
-        return $this->_redirect_url;
350
-    }
351
-
352
-
353
-    /**
354
-     * @return string
355
-     */
356
-    public function registration_time_limit()
357
-    {
358
-        return $this->_registration_time_limit;
359
-    }
360
-
361
-
362
-    /**
363
-     * @param string $registration_time_limit
364
-     */
365
-    public function set_registration_time_limit($registration_time_limit)
366
-    {
367
-        $this->_registration_time_limit = $registration_time_limit;
368
-    }
369
-
370
-
371
-    /**
372
-     * @param array $return_data
373
-     */
374
-    public function set_return_data($return_data)
375
-    {
376
-        $this->_return_data = array_merge($this->_return_data, $return_data);
377
-    }
378
-
379
-
380
-    /**
381
-     * @return array
382
-     */
383
-    public function return_data()
384
-    {
385
-        return $this->_return_data;
386
-    }
387
-
388
-
389
-    /**
390
-     * @param array $validation_rules
391
-     */
392
-    public function add_validation_rules(array $validation_rules = array())
393
-    {
394
-        if (is_array($validation_rules) && ! empty($validation_rules)) {
395
-            $this->_validation_rules = array_merge($this->_validation_rules, $validation_rules);
396
-        }
397
-    }
398
-
399
-
400
-    /**
401
-     * @return array | bool
402
-     */
403
-    public function validation_rules()
404
-    {
405
-        return ! empty($this->_validation_rules) ? $this->_validation_rules : false;
406
-    }
407
-
408
-
409
-    public function echoAndExit()
410
-    {
411
-        echo ($this);
412
-        exit();
413
-    }
17
+	/**
18
+	 * @var string
19
+	 */
20
+	protected $_errors = '';
21
+
22
+	/**
23
+	 * @var string
24
+	 */
25
+	protected $_unexpected_errors = '';
26
+
27
+	/**
28
+	 * @var string
29
+	 */
30
+	protected $_attention = '';
31
+
32
+	/**
33
+	 * @var string
34
+	 */
35
+	protected $_success = '';
36
+
37
+	/**
38
+	 * @var string
39
+	 */
40
+	protected $_plz_select_method_of_payment = '';
41
+
42
+	/**
43
+	 * @var string
44
+	 */
45
+	protected $_redirect_url = '';
46
+
47
+	/**
48
+	 * @var string
49
+	 */
50
+	protected $_registration_time_limit = '';
51
+
52
+	/**
53
+	 * @var string
54
+	 */
55
+	protected $_redirect_form = '';
56
+
57
+	/**
58
+	 * @var string
59
+	 */
60
+	protected $_reg_step_html = '';
61
+
62
+	/**
63
+	 * @var string
64
+	 */
65
+	protected $_method_of_payment = '';
66
+
67
+	/**
68
+	 * @var float
69
+	 */
70
+	protected $_payment_amount;
71
+
72
+	/**
73
+	 * @var array
74
+	 */
75
+	protected $_return_data = array();
76
+
77
+
78
+	/**
79
+	 * @var array
80
+	 */
81
+	protected $_validation_rules = array();
82
+
83
+
84
+	/**
85
+	 *    class constructor
86
+	 */
87
+	public function __construct()
88
+	{
89
+	}
90
+
91
+
92
+	/**
93
+	 *    __toString
94
+	 *
95
+	 *        allows you to simply echo or print an EE_SPCO_JSON_Response object to produce a  JSON encoded string
96
+	 *
97
+	 * @access    public
98
+	 * @return    string
99
+	 */
100
+	public function __toString()
101
+	{
102
+		$JSON_response = array();
103
+		// grab notices
104
+		$notices = EE_Error::get_notices(false);
105
+		$this->set_attention(isset($notices['attention']) ? $notices['attention'] : '');
106
+		$this->set_errors(isset($notices['errors']) ? $notices['errors'] : '');
107
+		$this->set_success(isset($notices['success']) ? $notices['success'] : '');
108
+		// add notices to JSON response, but only if they exist
109
+		if ($this->attention()) {
110
+			$JSON_response['attention'] = $this->attention();
111
+		}
112
+		if ($this->errors()) {
113
+			$JSON_response['errors'] = $this->errors();
114
+		}
115
+		if ($this->unexpected_errors()) {
116
+			$JSON_response['unexpected_errors'] = $this->unexpected_errors();
117
+		}
118
+		if ($this->success()) {
119
+			$JSON_response['success'] = $this->success();
120
+		}
121
+		// but if NO notices are set... at least set the "success" as a key so that the JS knows everything worked
122
+		if (! isset($JSON_response['attention']) && ! isset($JSON_response['errors']) && ! isset($JSON_response['success'])) {
123
+			$JSON_response['success'] = null;
124
+		}
125
+		// set redirect_url, IF it exists
126
+		if ($this->redirect_url()) {
127
+			$JSON_response['redirect_url'] = $this->redirect_url();
128
+		}
129
+		// set registration_time_limit, IF it exists
130
+		if ($this->registration_time_limit()) {
131
+			$JSON_response['registration_time_limit'] = $this->registration_time_limit();
132
+		}
133
+		// set payment_amount, IF it exists
134
+		if ($this->payment_amount() !== null) {
135
+			$JSON_response['payment_amount'] = $this->payment_amount();
136
+		}
137
+		// grab generic return data
138
+		$return_data = $this->return_data();
139
+		// add billing form validation rules
140
+		if ($this->validation_rules()) {
141
+			$return_data['validation_rules'] = $this->validation_rules();
142
+		}
143
+		// set reg_step_html, IF it exists
144
+		if ($this->reg_step_html()) {
145
+			$return_data['reg_step_html'] = $this->reg_step_html();
146
+		}
147
+		// set method of payment, IF it exists
148
+		if ($this->method_of_payment()) {
149
+			$return_data['method_of_payment'] = $this->method_of_payment();
150
+		}
151
+		// set "plz_select_method_of_payment" message, IF it exists
152
+		if ($this->plz_select_method_of_payment()) {
153
+			$return_data['plz_select_method_of_payment'] = $this->plz_select_method_of_payment();
154
+		}
155
+		// set redirect_form, IF it exists
156
+		if ($this->redirect_form()) {
157
+			$return_data['redirect_form'] = $this->redirect_form();
158
+		}
159
+		// and finally, add return_data array to main JSON response array, IF it contains anything
160
+		// why did we add some of the above properties to the return data array?
161
+		// because it is easier and cleaner in the Javascript to deal with this way
162
+		if (! empty($return_data)) {
163
+			$JSON_response['return_data'] = $return_data;
164
+		}
165
+		// filter final array
166
+		$JSON_response = apply_filters('FHEE__EE_SPCO_JSON_Response___toString__JSON_response', $JSON_response);
167
+		// return encoded array
168
+		return (string) wp_json_encode($JSON_response);
169
+	}
170
+
171
+
172
+	/**
173
+	 * @param string $attention
174
+	 */
175
+	public function set_attention($attention)
176
+	{
177
+		$this->_attention = $attention;
178
+	}
179
+
180
+
181
+	/**
182
+	 * @return string
183
+	 */
184
+	public function attention()
185
+	{
186
+		return $this->_attention;
187
+	}
188
+
189
+
190
+	/**
191
+	 * @param string $errors
192
+	 */
193
+	public function set_errors($errors)
194
+	{
195
+		$this->_errors = $errors;
196
+	}
197
+
198
+
199
+	/**
200
+	 * @return string
201
+	 */
202
+	public function errors()
203
+	{
204
+		return $this->_errors;
205
+	}
206
+
207
+
208
+	/**
209
+	 * @return string
210
+	 */
211
+	public function unexpected_errors()
212
+	{
213
+		return $this->_unexpected_errors;
214
+	}
215
+
216
+
217
+	/**
218
+	 * @param string $unexpected_errors
219
+	 */
220
+	public function set_unexpected_errors($unexpected_errors)
221
+	{
222
+		$this->_unexpected_errors = $unexpected_errors;
223
+	}
224
+
225
+
226
+	/**
227
+	 * @param string $success
228
+	 */
229
+	public function set_success($success)
230
+	{
231
+		$this->_success = $success;
232
+	}
233
+
234
+
235
+	/**
236
+	 * @return string
237
+	 */
238
+	public function success()
239
+	{
240
+		return $this->_success;
241
+	}
242
+
243
+
244
+	/**
245
+	 * @param string $method_of_payment
246
+	 */
247
+	public function set_method_of_payment($method_of_payment)
248
+	{
249
+		$this->_method_of_payment = $method_of_payment;
250
+	}
251
+
252
+
253
+	/**
254
+	 * @return string
255
+	 */
256
+	public function method_of_payment()
257
+	{
258
+		return $this->_method_of_payment;
259
+	}
260
+
261
+
262
+	/**
263
+	 * @return float
264
+	 */
265
+	public function payment_amount()
266
+	{
267
+		return $this->_payment_amount;
268
+	}
269
+
270
+
271
+	/**
272
+	 * @param float $payment_amount
273
+	 * @throws EE_Error
274
+	 */
275
+	public function set_payment_amount($payment_amount)
276
+	{
277
+		$this->_payment_amount = (float) $payment_amount;
278
+	}
279
+
280
+
281
+	/**
282
+	 * @param string $next_step_html
283
+	 */
284
+	public function set_reg_step_html($next_step_html)
285
+	{
286
+		$this->_reg_step_html = $next_step_html;
287
+	}
288
+
289
+
290
+	/**
291
+	 * @return string
292
+	 */
293
+	public function reg_step_html()
294
+	{
295
+		return $this->_reg_step_html;
296
+	}
297
+
298
+
299
+	/**
300
+	 * @param string $redirect_form
301
+	 */
302
+	public function set_redirect_form($redirect_form)
303
+	{
304
+		$this->_redirect_form = $redirect_form;
305
+	}
306
+
307
+
308
+	/**
309
+	 * @return string
310
+	 */
311
+	public function redirect_form()
312
+	{
313
+		return ! empty($this->_redirect_form) ? $this->_redirect_form : false;
314
+	}
315
+
316
+
317
+	/**
318
+	 * @param string $plz_select_method_of_payment
319
+	 */
320
+	public function set_plz_select_method_of_payment($plz_select_method_of_payment)
321
+	{
322
+		$this->_plz_select_method_of_payment = $plz_select_method_of_payment;
323
+	}
324
+
325
+
326
+	/**
327
+	 * @return string
328
+	 */
329
+	public function plz_select_method_of_payment()
330
+	{
331
+		return $this->_plz_select_method_of_payment;
332
+	}
333
+
334
+
335
+	/**
336
+	 * @param string $redirect_url
337
+	 */
338
+	public function set_redirect_url($redirect_url)
339
+	{
340
+		$this->_redirect_url = $redirect_url;
341
+	}
342
+
343
+
344
+	/**
345
+	 * @return string
346
+	 */
347
+	public function redirect_url()
348
+	{
349
+		return $this->_redirect_url;
350
+	}
351
+
352
+
353
+	/**
354
+	 * @return string
355
+	 */
356
+	public function registration_time_limit()
357
+	{
358
+		return $this->_registration_time_limit;
359
+	}
360
+
361
+
362
+	/**
363
+	 * @param string $registration_time_limit
364
+	 */
365
+	public function set_registration_time_limit($registration_time_limit)
366
+	{
367
+		$this->_registration_time_limit = $registration_time_limit;
368
+	}
369
+
370
+
371
+	/**
372
+	 * @param array $return_data
373
+	 */
374
+	public function set_return_data($return_data)
375
+	{
376
+		$this->_return_data = array_merge($this->_return_data, $return_data);
377
+	}
378
+
379
+
380
+	/**
381
+	 * @return array
382
+	 */
383
+	public function return_data()
384
+	{
385
+		return $this->_return_data;
386
+	}
387
+
388
+
389
+	/**
390
+	 * @param array $validation_rules
391
+	 */
392
+	public function add_validation_rules(array $validation_rules = array())
393
+	{
394
+		if (is_array($validation_rules) && ! empty($validation_rules)) {
395
+			$this->_validation_rules = array_merge($this->_validation_rules, $validation_rules);
396
+		}
397
+	}
398
+
399
+
400
+	/**
401
+	 * @return array | bool
402
+	 */
403
+	public function validation_rules()
404
+	{
405
+		return ! empty($this->_validation_rules) ? $this->_validation_rules : false;
406
+	}
407
+
408
+
409
+	public function echoAndExit()
410
+	{
411
+		echo ($this);
412
+		exit();
413
+	}
414 414
 }
Please login to merge, or discard this patch.
core/libraries/messages/messenger/EE_Html_messenger.class.php 1 patch
Indentation   +545 added lines, -545 removed lines patch added patch discarded remove patch
@@ -12,549 +12,549 @@
 block discarded – undo
12 12
  */
13 13
 class EE_Html_messenger extends EE_messenger
14 14
 {
15
-    /**
16
-     * The following are the properties that this messenger requires for displaying the html
17
-     */
18
-    /**
19
-     * This is the html body generated by the template via the message type.
20
-     *
21
-     * @var string
22
-     */
23
-    protected $_content = '';
24
-
25
-    /**
26
-     * This is for the page title that gets displayed.  (Why use "subject"?  Because the "title" tag in html is
27
-     * equivalent to the "subject" of the page.
28
-     *
29
-     * @var string
30
-     */
31
-    protected $_subject = '';
32
-
33
-
34
-    /**
35
-     * EE_Html_messenger constructor.
36
-     */
37
-    public function __construct()
38
-    {
39
-        // set properties
40
-        $this->name                = 'html';
41
-        $this->description         = esc_html__(
42
-            'This messenger outputs a message to a browser for display.',
43
-            'event_espresso'
44
-        );
45
-        $this->label               = [
46
-            'singular' => esc_html__('html', 'event_espresso'),
47
-            'plural'   => esc_html__('html', 'event_espresso'),
48
-        ];
49
-        $this->activate_on_install = true;
50
-        // add the "powered by EE" credit link to the HTML receipt and invoice
51
-        add_filter(
52
-            'FHEE__EE_Html_messenger___send_message__main_body',
53
-            [$this, 'add_powered_by_credit_link_to_receipt_and_invoice'],
54
-            10,
55
-            3
56
-        );
57
-        parent::__construct();
58
-    }
59
-
60
-
61
-    /**
62
-     * HTML Messenger desires execution immediately.
63
-     *
64
-     * @return bool
65
-     * @since  4.9.0
66
-     * @see    parent::send_now() for documentation.
67
-     */
68
-    public function send_now(): bool
69
-    {
70
-        return true;
71
-    }
72
-
73
-
74
-    /**
75
-     * HTML Messenger allows an empty to field.
76
-     *
77
-     * @return bool
78
-     * @since  4.9.0
79
-     * @see    parent::allow_empty_to_field() for documentation
80
-     */
81
-    public function allow_empty_to_field(): bool
82
-    {
83
-        return true;
84
-    }
85
-
86
-
87
-    /**
88
-     * @see abstract declaration in EE_messenger for details.
89
-     */
90
-    protected function _set_admin_pages()
91
-    {
92
-        $this->admin_registered_pages = ['events_edit' => true];
93
-    }
94
-
95
-
96
-    /**
97
-     * @see abstract declaration in EE_messenger for details.
98
-     */
99
-    protected function _set_valid_shortcodes()
100
-    {
101
-        $this->_valid_shortcodes = [];
102
-    }
103
-
104
-
105
-    /**
106
-     * @see abstract declaration in EE_messenger for details.
107
-     */
108
-    protected function _set_validator_config()
109
-    {
110
-        $this->_validator_config = [
111
-            'subject'                       => [
112
-                'shortcodes' => ['organization', 'primary_registration_details', 'email', 'transaction'],
113
-            ],
114
-            'content'                       => [
115
-                'shortcodes' => [
116
-                    'organization',
117
-                    'primary_registration_list',
118
-                    'primary_registration_details',
119
-                    'email',
120
-                    'transaction',
121
-                    'event_list',
122
-                    'payment_list',
123
-                    'venue',
124
-                    'line_item_list',
125
-                    'messenger',
126
-                    'ticket_list',
127
-                ],
128
-            ],
129
-            'event_list'                    => [
130
-                'shortcodes' => [
131
-                    'event',
132
-                    'ticket_list',
133
-                    'venue',
134
-                    'primary_registration_details',
135
-                    'primary_registration_list',
136
-                    'event_author',
137
-                ],
138
-                'required'   => ['[EVENT_LIST]'],
139
-            ],
140
-            'ticket_list'                   => [
141
-                'shortcodes' => [
142
-                    'attendee_list',
143
-                    'ticket',
144
-                    'datetime_list',
145
-                    'primary_registration_details',
146
-                    'line_item_list',
147
-                    'venue',
148
-                ],
149
-                'required'   => ['[TICKET_LIST]'],
150
-            ],
151
-            'ticket_line_item_no_pms'       => [
152
-                'shortcodes' => ['line_item', 'ticket'],
153
-                'required'   => ['[TICKET_LINE_ITEM_LIST]'],
154
-            ],
155
-            'ticket_line_item_pms'          => [
156
-                'shortcodes' => ['line_item', 'ticket', 'line_item_list'],
157
-                'required'   => ['[TICKET_LINE_ITEM_LIST]'],
158
-            ],
159
-            'price_modifier_line_item_list' => [
160
-                'shortcodes' => ['line_item'],
161
-                'required'   => ['[PRICE_MODIFIER_LINE_ITEM_LIST]'],
162
-            ],
163
-            'datetime_list'                 => [
164
-                'shortcodes' => ['datetime'],
165
-                'required'   => ['[DATETIME_LIST]'],
166
-            ],
167
-            'attendee_list'                 => [
168
-                'shortcodes' => ['attendee'],
169
-                'required'   => ['[ATTENDEE_LIST]'],
170
-            ],
171
-            'tax_line_item_list'            => [
172
-                'shortcodes' => ['line_item'],
173
-                'required'   => ['[TAX_LINE_ITEM_LIST]'],
174
-            ],
175
-            'additional_line_item_list'     => [
176
-                'shortcodes' => ['line_item'],
177
-                'required'   => ['[ADDITIONAL_LINE_ITEM_LIST]'],
178
-            ],
179
-            'payment_list'                  => [
180
-                'shortcodes' => ['payment'],
181
-                'required'   => ['[PAYMENT_LIST_*]'],
182
-            ],
183
-        ];
184
-    }
185
-
186
-
187
-    /**
188
-     * This is a method called from EE_messages when this messenger is a generating messenger and the sending messenger
189
-     * is a different messenger.  Child messengers can set hooks for the sending messenger to callback on if necessary
190
-     * (i.e. swap out css files or something else).
191
-     *
192
-     * @param string $sending_messenger_name the name of the sending messenger so we only set the hooks needed.
193
-     * @return void
194
-     * @since 4.5.0
195
-     */
196
-    public function do_secondary_messenger_hooks($sending_messenger_name)
197
-    {
198
-        if ($sending_messenger_name === 'pdf') {
199
-            add_filter('EE_messenger__get_variation__variation', [$this, 'add_html_css'], 10, 8);
200
-        }
201
-    }
202
-
203
-
204
-    /**
205
-     * @param                            $variation_path
206
-     * @param EE_Messages_Template_Pack  $template_pack
207
-     * @param                            $messenger_name
208
-     * @param                            $message_type_name
209
-     * @param                            $url
210
-     * @param                            $type
211
-     * @param                            $variation
212
-     * @param                            $skip_filters
213
-     * @return string
214
-     */
215
-    public function add_html_css(
216
-        $variation_path,
217
-        EE_Messages_Template_Pack $template_pack,
218
-        $messenger_name,
219
-        $message_type_name,
220
-        $url,
221
-        $type,
222
-        $variation,
223
-        $skip_filters
224
-    ): string {
225
-        return $template_pack->get_variation(
226
-            $this->name,
227
-            $message_type_name,
228
-            $type,
229
-            $variation,
230
-            $url,
231
-            '.css',
232
-            $skip_filters
233
-        );
234
-    }
235
-
236
-
237
-    /**
238
-     * Takes care of enqueuing any necessary scripts or styles for the page.  A do_action() so message types using this
239
-     * messenger can add their own js.
240
-     *
241
-     * @return void.
242
-     */
243
-    public function enqueue_scripts_styles()
244
-    {
245
-        parent::enqueue_scripts_styles();
246
-        do_action('AHEE__EE_Html_messenger__enqueue_scripts_styles');
247
-    }
248
-
249
-
250
-    /**
251
-     * _set_template_fields
252
-     * This sets up the fields that a messenger requires for the message to go out.
253
-     *
254
-     * @access  protected
255
-     * @return void
256
-     */
257
-    protected function _set_template_fields()
258
-    {
259
-        // any extra template fields that are NOT used by the messenger
260
-        // but will get used by a messenger field for shortcode replacement
261
-        // get added to the 'extra' key in an associated array
262
-        // indexed by the messenger field they relate to.
263
-        // This is important for the Messages_admin to know what fields to display to the user.
264
-        // Also, notice that the "values" are equal to the field type
265
-        // that messages admin will use to know what kind of field to display.
266
-        // The values ALSO have one index labeled "shortcode".
267
-        // The values in that array indicate which ACTUAL SHORTCODE (i.e. [SHORTCODE])
268
-        // is required in order for this extra field to be displayed.
269
-        //  If the required shortcode isn't part of the shortcodes array
270
-        // then the field is not needed and will not be displayed/parsed.
271
-        $this->_template_fields = [
272
-            'subject' => [
273
-                'input'      => 'text',
274
-                'label'      => esc_html__('Page Title', 'event_espresso'),
275
-                'type'       => 'string',
276
-                'required'   => true,
277
-                'validation' => true,
278
-                'css_class'  => 'large-text',
279
-                'format'     => '%s',
280
-            ],
281
-            'content' => '',
282
-            // left empty b/c it is in the "extra array" but messenger still needs needs to know this is a field.
283
-            'extra'   => [
284
-                'content' => [
285
-                    'main'                          => [
286
-                        'input'      => 'wp_editor',
287
-                        'label'      => esc_html__('Main Content', 'event_espresso'),
288
-                        'type'       => 'string',
289
-                        'required'   => true,
290
-                        'validation' => true,
291
-                        'format'     => '%s',
292
-                        'rows'       => '15',
293
-                    ],
294
-                    'event_list'                    => [
295
-                        'input'               => 'wp_editor',
296
-                        'label'               => '[EVENT_LIST]',
297
-                        'type'                => 'string',
298
-                        'required'            => true,
299
-                        'validation'          => true,
300
-                        'format'              => '%s',
301
-                        'rows'                => '15',
302
-                        'shortcodes_required' => ['[EVENT_LIST]'],
303
-                    ],
304
-                    'ticket_list'                   => [
305
-                        'input'               => 'textarea',
306
-                        'label'               => '[TICKET_LIST]',
307
-                        'type'                => 'string',
308
-                        'required'            => true,
309
-                        'validation'          => true,
310
-                        'format'              => '%s',
311
-                        'css_class'           => 'large-text',
312
-                        'rows'                => '10',
313
-                        'shortcodes_required' => ['[TICKET_LIST]'],
314
-                    ],
315
-                    'ticket_line_item_no_pms'       => [
316
-                        'input'               => 'textarea',
317
-                        'label'               => '[TICKET_LINE_ITEM_LIST] <br>' . esc_html__(
318
-                            'Ticket Line Item List with no Price Modifiers',
319
-                            'event_espresso'
320
-                        ),
321
-                        'type'                => 'string',
322
-                        'required'            => false,
323
-                        'validation'          => true,
324
-                        'format'              => '%s',
325
-                        'css_class'           => 'large-text',
326
-                        'rows'                => '5',
327
-                        'shortcodes_required' => ['[TICKET_LINE_ITEM_LIST]'],
328
-                    ],
329
-                    'ticket_line_item_pms'          => [
330
-                        'input'               => 'textarea',
331
-                        'label'               => '[TICKET_LINE_ITEM_LIST] <br>' . esc_html__(
332
-                            'Ticket Line Item List with Price Modifiers',
333
-                            'event_espresso'
334
-                        ),
335
-                        'type'                => 'string',
336
-                        'required'            => false,
337
-                        'validation'          => true,
338
-                        'format'              => '%s',
339
-                        'css_class'           => 'large-text',
340
-                        'rows'                => '5',
341
-                        'shortcodes_required' => ['[TICKET_LINE_ITEM_LIST]'],
342
-                    ],
343
-                    'price_modifier_line_item_list' => [
344
-                        'input'               => 'textarea',
345
-                        'label'               => '[PRICE_MODIFIER_LINE_ITEM_LIST]',
346
-                        'type'                => 'string',
347
-                        'required'            => false,
348
-                        'validation'          => true,
349
-                        'format'              => '%s',
350
-                        'css_class'           => 'large-text',
351
-                        'rows'                => '5',
352
-                        'shortcodes_required' => ['[PRICE_MODIFIER_LINE_ITEM_LIST]'],
353
-                    ],
354
-                    'datetime_list'                 => [
355
-                        'input'               => 'textarea',
356
-                        'label'               => '[DATETIME_LIST]',
357
-                        'type'                => 'string',
358
-                        'required'            => true,
359
-                        'validation'          => true,
360
-                        'format'              => '%s',
361
-                        'css_class'           => 'large-text',
362
-                        'rows'                => '5',
363
-                        'shortcodes_required' => ['[DATETIME_LIST]'],
364
-                    ],
365
-                    'attendee_list'                 => [
366
-                        'input'               => 'textarea',
367
-                        'label'               => '[ATTENDEE_LIST]',
368
-                        'type'                => 'string',
369
-                        'required'            => true,
370
-                        'validation'          => true,
371
-                        'format'              => '%s',
372
-                        'css_class'           => 'large-text',
373
-                        'rows'                => '5',
374
-                        'shortcodes_required' => ['[ATTENDEE_LIST]'],
375
-                    ],
376
-                    'tax_line_item_list'            => [
377
-                        'input'               => 'textarea',
378
-                        'label'               => '[TAX_LINE_ITEM_LIST]',
379
-                        'type'                => 'string',
380
-                        'required'            => false,
381
-                        'validation'          => true,
382
-                        'format'              => '%s',
383
-                        'css_class'           => 'large-text',
384
-                        'rows'                => '5',
385
-                        'shortcodes_required' => ['[TAX_LINE_ITEM_LIST]'],
386
-                    ],
387
-                    'additional_line_item_list'     => [
388
-                        'input'               => 'textarea',
389
-                        'label'               => '[ADDITIONAL_LINE_ITEM_LIST]',
390
-                        'type'                => 'string',
391
-                        'required'            => false,
392
-                        'validation'          => true,
393
-                        'format'              => '%s',
394
-                        'css_class'           => 'large-text',
395
-                        'rows'                => '5',
396
-                        'shortcodes_required' => ['[ADDITIONAL_LINE_ITEM_LIST]'],
397
-                    ],
398
-                    'payment_list'                  => [
399
-                        'input'               => 'textarea',
400
-                        'label'               => '[PAYMENT_LIST]',
401
-                        'type'                => 'string',
402
-                        'required'            => true,
403
-                        'validation'          => true,
404
-                        'format'              => '%s',
405
-                        'css_class'           => 'large-text',
406
-                        'rows'                => '5',
407
-                        'shortcodes_required' => ['[PAYMENT_LIST_*]'],
408
-                    ],
409
-                ],
410
-            ],
411
-        ];
412
-    }
413
-
414
-
415
-    /**
416
-     * @see   definition of this method in parent
417
-     * @since 4.5.0
418
-     */
419
-    protected function _set_default_message_types()
420
-    {
421
-        $this->_default_message_types = ['receipt', 'invoice'];
422
-    }
423
-
424
-
425
-    /**
426
-     * @see   definition of this method in parent
427
-     * @since 4.5.0
428
-     */
429
-    protected function _set_valid_message_types()
430
-    {
431
-        $this->_valid_message_types = ['receipt', 'invoice'];
432
-    }
433
-
434
-
435
-    /**
436
-     * Displays the message in the browser.
437
-     *
438
-     * @return void.
439
-     * @since 4.5.0
440
-     */
441
-    protected function _send_message()
442
-    {
443
-        $this->_template_args = [
444
-            'page_title' => $this->_subject,
445
-            'base_css'   => $this->get_variation(
446
-                $this->_tmp_pack,
447
-                $this->_incoming_message_type->name,
448
-                true,
449
-                'base',
450
-                $this->_variation
451
-            ),
452
-            'print_css'  => $this->get_variation(
453
-                $this->_tmp_pack,
454
-                $this->_incoming_message_type->name,
455
-                true,
456
-                'print',
457
-                $this->_variation
458
-            ),
459
-            'main_css'   => $this->get_variation(
460
-                $this->_tmp_pack,
461
-                $this->_incoming_message_type->name,
462
-                true,
463
-                'main',
464
-                $this->_variation
465
-            ),
466
-            'main_body'  => wpautop(
467
-                apply_filters(
468
-                    'FHEE__EE_Html_messenger___send_message__main_body',
469
-                    $this->_content,
470
-                    $this->_content,
471
-                    $this->_incoming_message_type
472
-                )
473
-            ),
474
-        ];
475
-        $this->_deregister_wp_hooks();
476
-        add_action('wp_enqueue_scripts', [$this, 'enqueue_scripts_styles']);
477
-        echo ($this->_get_main_template());
478
-        exit();
479
-    }
480
-
481
-
482
-    /**
483
-     * The purpose of this function is to de register all actions hooked into wp_head and wp_footer so that it doesn't
484
-     * interfere with our templates.  If users want to add any custom styles or scripts they must use the
485
-     * AHEE__EE_Html_messenger__enqueue_scripts_styles hook.
486
-     *
487
-     * @return void
488
-     * @since 4.5.0
489
-     */
490
-    protected function _deregister_wp_hooks()
491
-    {
492
-        remove_all_actions('wp_head');
493
-        remove_all_actions('wp_footer');
494
-        remove_all_actions('wp_print_footer_scripts');
495
-        remove_all_actions('wp_enqueue_scripts');
496
-        global $wp_scripts, $wp_styles;
497
-        $wp_scripts = $wp_styles = [];
498
-        // just add back in wp_enqueue_scripts and wp_print_footer_scripts cause that's all we want to load.
499
-        add_action('wp_footer', 'wp_print_footer_scripts');
500
-        add_action('wp_print_footer_scripts', '_wp_footer_scripts');
501
-        add_action('wp_head', 'wp_enqueue_scripts');
502
-    }
503
-
504
-
505
-    /**
506
-     * Overwrite parent _get_main_template for display_html purposes.
507
-     *
508
-     * @param bool $preview
509
-     * @return string
510
-     * @since  4.5.0
511
-     */
512
-    protected function _get_main_template($preview = false): string
513
-    {
514
-        $wrapper_template = $this->_tmp_pack->get_wrapper($this->name);
515
-        // include message type as a template arg
516
-        $this->_template_args['message_type'] = $this->_incoming_message_type;
517
-        return EEH_Template::display_template($wrapper_template, $this->_template_args, true);
518
-    }
519
-
520
-
521
-    /**
522
-     * @return void
523
-     */
524
-    protected function _preview()
525
-    {
526
-        $this->_send_message();
527
-    }
528
-
529
-
530
-    protected function _set_admin_settings_fields()
531
-    {
532
-    }
533
-
534
-
535
-    /**
536
-     * add the "powered by EE" credit link to the HTML receipt and invoice
537
-     *
538
-     * @param string          $content
539
-     * @param string          $content_again
540
-     * @param EE_message_type $incoming_message_type
541
-     * @return string
542
-     */
543
-    public function add_powered_by_credit_link_to_receipt_and_invoice(
544
-        string $content,
545
-        string $content_again,
546
-        EE_message_type $incoming_message_type
547
-    ): string {
548
-        if (
549
-            ($incoming_message_type->name === 'invoice' || $incoming_message_type->name === 'receipt')
550
-            && apply_filters('FHEE_EE_Html_messenger__add_powered_by_credit_link_to_receipt_and_invoice', true)
551
-        ) {
552
-            $content .= EEH_Template::powered_by_event_espresso(
553
-                'aln-cntr',
554
-                '',
555
-                ['utm_content' => 'messages_system']
556
-            ) . EEH_HTML::div(EEH_HTML::p('&nbsp;'));
557
-        }
558
-        return $content;
559
-    }
15
+	/**
16
+	 * The following are the properties that this messenger requires for displaying the html
17
+	 */
18
+	/**
19
+	 * This is the html body generated by the template via the message type.
20
+	 *
21
+	 * @var string
22
+	 */
23
+	protected $_content = '';
24
+
25
+	/**
26
+	 * This is for the page title that gets displayed.  (Why use "subject"?  Because the "title" tag in html is
27
+	 * equivalent to the "subject" of the page.
28
+	 *
29
+	 * @var string
30
+	 */
31
+	protected $_subject = '';
32
+
33
+
34
+	/**
35
+	 * EE_Html_messenger constructor.
36
+	 */
37
+	public function __construct()
38
+	{
39
+		// set properties
40
+		$this->name                = 'html';
41
+		$this->description         = esc_html__(
42
+			'This messenger outputs a message to a browser for display.',
43
+			'event_espresso'
44
+		);
45
+		$this->label               = [
46
+			'singular' => esc_html__('html', 'event_espresso'),
47
+			'plural'   => esc_html__('html', 'event_espresso'),
48
+		];
49
+		$this->activate_on_install = true;
50
+		// add the "powered by EE" credit link to the HTML receipt and invoice
51
+		add_filter(
52
+			'FHEE__EE_Html_messenger___send_message__main_body',
53
+			[$this, 'add_powered_by_credit_link_to_receipt_and_invoice'],
54
+			10,
55
+			3
56
+		);
57
+		parent::__construct();
58
+	}
59
+
60
+
61
+	/**
62
+	 * HTML Messenger desires execution immediately.
63
+	 *
64
+	 * @return bool
65
+	 * @since  4.9.0
66
+	 * @see    parent::send_now() for documentation.
67
+	 */
68
+	public function send_now(): bool
69
+	{
70
+		return true;
71
+	}
72
+
73
+
74
+	/**
75
+	 * HTML Messenger allows an empty to field.
76
+	 *
77
+	 * @return bool
78
+	 * @since  4.9.0
79
+	 * @see    parent::allow_empty_to_field() for documentation
80
+	 */
81
+	public function allow_empty_to_field(): bool
82
+	{
83
+		return true;
84
+	}
85
+
86
+
87
+	/**
88
+	 * @see abstract declaration in EE_messenger for details.
89
+	 */
90
+	protected function _set_admin_pages()
91
+	{
92
+		$this->admin_registered_pages = ['events_edit' => true];
93
+	}
94
+
95
+
96
+	/**
97
+	 * @see abstract declaration in EE_messenger for details.
98
+	 */
99
+	protected function _set_valid_shortcodes()
100
+	{
101
+		$this->_valid_shortcodes = [];
102
+	}
103
+
104
+
105
+	/**
106
+	 * @see abstract declaration in EE_messenger for details.
107
+	 */
108
+	protected function _set_validator_config()
109
+	{
110
+		$this->_validator_config = [
111
+			'subject'                       => [
112
+				'shortcodes' => ['organization', 'primary_registration_details', 'email', 'transaction'],
113
+			],
114
+			'content'                       => [
115
+				'shortcodes' => [
116
+					'organization',
117
+					'primary_registration_list',
118
+					'primary_registration_details',
119
+					'email',
120
+					'transaction',
121
+					'event_list',
122
+					'payment_list',
123
+					'venue',
124
+					'line_item_list',
125
+					'messenger',
126
+					'ticket_list',
127
+				],
128
+			],
129
+			'event_list'                    => [
130
+				'shortcodes' => [
131
+					'event',
132
+					'ticket_list',
133
+					'venue',
134
+					'primary_registration_details',
135
+					'primary_registration_list',
136
+					'event_author',
137
+				],
138
+				'required'   => ['[EVENT_LIST]'],
139
+			],
140
+			'ticket_list'                   => [
141
+				'shortcodes' => [
142
+					'attendee_list',
143
+					'ticket',
144
+					'datetime_list',
145
+					'primary_registration_details',
146
+					'line_item_list',
147
+					'venue',
148
+				],
149
+				'required'   => ['[TICKET_LIST]'],
150
+			],
151
+			'ticket_line_item_no_pms'       => [
152
+				'shortcodes' => ['line_item', 'ticket'],
153
+				'required'   => ['[TICKET_LINE_ITEM_LIST]'],
154
+			],
155
+			'ticket_line_item_pms'          => [
156
+				'shortcodes' => ['line_item', 'ticket', 'line_item_list'],
157
+				'required'   => ['[TICKET_LINE_ITEM_LIST]'],
158
+			],
159
+			'price_modifier_line_item_list' => [
160
+				'shortcodes' => ['line_item'],
161
+				'required'   => ['[PRICE_MODIFIER_LINE_ITEM_LIST]'],
162
+			],
163
+			'datetime_list'                 => [
164
+				'shortcodes' => ['datetime'],
165
+				'required'   => ['[DATETIME_LIST]'],
166
+			],
167
+			'attendee_list'                 => [
168
+				'shortcodes' => ['attendee'],
169
+				'required'   => ['[ATTENDEE_LIST]'],
170
+			],
171
+			'tax_line_item_list'            => [
172
+				'shortcodes' => ['line_item'],
173
+				'required'   => ['[TAX_LINE_ITEM_LIST]'],
174
+			],
175
+			'additional_line_item_list'     => [
176
+				'shortcodes' => ['line_item'],
177
+				'required'   => ['[ADDITIONAL_LINE_ITEM_LIST]'],
178
+			],
179
+			'payment_list'                  => [
180
+				'shortcodes' => ['payment'],
181
+				'required'   => ['[PAYMENT_LIST_*]'],
182
+			],
183
+		];
184
+	}
185
+
186
+
187
+	/**
188
+	 * This is a method called from EE_messages when this messenger is a generating messenger and the sending messenger
189
+	 * is a different messenger.  Child messengers can set hooks for the sending messenger to callback on if necessary
190
+	 * (i.e. swap out css files or something else).
191
+	 *
192
+	 * @param string $sending_messenger_name the name of the sending messenger so we only set the hooks needed.
193
+	 * @return void
194
+	 * @since 4.5.0
195
+	 */
196
+	public function do_secondary_messenger_hooks($sending_messenger_name)
197
+	{
198
+		if ($sending_messenger_name === 'pdf') {
199
+			add_filter('EE_messenger__get_variation__variation', [$this, 'add_html_css'], 10, 8);
200
+		}
201
+	}
202
+
203
+
204
+	/**
205
+	 * @param                            $variation_path
206
+	 * @param EE_Messages_Template_Pack  $template_pack
207
+	 * @param                            $messenger_name
208
+	 * @param                            $message_type_name
209
+	 * @param                            $url
210
+	 * @param                            $type
211
+	 * @param                            $variation
212
+	 * @param                            $skip_filters
213
+	 * @return string
214
+	 */
215
+	public function add_html_css(
216
+		$variation_path,
217
+		EE_Messages_Template_Pack $template_pack,
218
+		$messenger_name,
219
+		$message_type_name,
220
+		$url,
221
+		$type,
222
+		$variation,
223
+		$skip_filters
224
+	): string {
225
+		return $template_pack->get_variation(
226
+			$this->name,
227
+			$message_type_name,
228
+			$type,
229
+			$variation,
230
+			$url,
231
+			'.css',
232
+			$skip_filters
233
+		);
234
+	}
235
+
236
+
237
+	/**
238
+	 * Takes care of enqueuing any necessary scripts or styles for the page.  A do_action() so message types using this
239
+	 * messenger can add their own js.
240
+	 *
241
+	 * @return void.
242
+	 */
243
+	public function enqueue_scripts_styles()
244
+	{
245
+		parent::enqueue_scripts_styles();
246
+		do_action('AHEE__EE_Html_messenger__enqueue_scripts_styles');
247
+	}
248
+
249
+
250
+	/**
251
+	 * _set_template_fields
252
+	 * This sets up the fields that a messenger requires for the message to go out.
253
+	 *
254
+	 * @access  protected
255
+	 * @return void
256
+	 */
257
+	protected function _set_template_fields()
258
+	{
259
+		// any extra template fields that are NOT used by the messenger
260
+		// but will get used by a messenger field for shortcode replacement
261
+		// get added to the 'extra' key in an associated array
262
+		// indexed by the messenger field they relate to.
263
+		// This is important for the Messages_admin to know what fields to display to the user.
264
+		// Also, notice that the "values" are equal to the field type
265
+		// that messages admin will use to know what kind of field to display.
266
+		// The values ALSO have one index labeled "shortcode".
267
+		// The values in that array indicate which ACTUAL SHORTCODE (i.e. [SHORTCODE])
268
+		// is required in order for this extra field to be displayed.
269
+		//  If the required shortcode isn't part of the shortcodes array
270
+		// then the field is not needed and will not be displayed/parsed.
271
+		$this->_template_fields = [
272
+			'subject' => [
273
+				'input'      => 'text',
274
+				'label'      => esc_html__('Page Title', 'event_espresso'),
275
+				'type'       => 'string',
276
+				'required'   => true,
277
+				'validation' => true,
278
+				'css_class'  => 'large-text',
279
+				'format'     => '%s',
280
+			],
281
+			'content' => '',
282
+			// left empty b/c it is in the "extra array" but messenger still needs needs to know this is a field.
283
+			'extra'   => [
284
+				'content' => [
285
+					'main'                          => [
286
+						'input'      => 'wp_editor',
287
+						'label'      => esc_html__('Main Content', 'event_espresso'),
288
+						'type'       => 'string',
289
+						'required'   => true,
290
+						'validation' => true,
291
+						'format'     => '%s',
292
+						'rows'       => '15',
293
+					],
294
+					'event_list'                    => [
295
+						'input'               => 'wp_editor',
296
+						'label'               => '[EVENT_LIST]',
297
+						'type'                => 'string',
298
+						'required'            => true,
299
+						'validation'          => true,
300
+						'format'              => '%s',
301
+						'rows'                => '15',
302
+						'shortcodes_required' => ['[EVENT_LIST]'],
303
+					],
304
+					'ticket_list'                   => [
305
+						'input'               => 'textarea',
306
+						'label'               => '[TICKET_LIST]',
307
+						'type'                => 'string',
308
+						'required'            => true,
309
+						'validation'          => true,
310
+						'format'              => '%s',
311
+						'css_class'           => 'large-text',
312
+						'rows'                => '10',
313
+						'shortcodes_required' => ['[TICKET_LIST]'],
314
+					],
315
+					'ticket_line_item_no_pms'       => [
316
+						'input'               => 'textarea',
317
+						'label'               => '[TICKET_LINE_ITEM_LIST] <br>' . esc_html__(
318
+							'Ticket Line Item List with no Price Modifiers',
319
+							'event_espresso'
320
+						),
321
+						'type'                => 'string',
322
+						'required'            => false,
323
+						'validation'          => true,
324
+						'format'              => '%s',
325
+						'css_class'           => 'large-text',
326
+						'rows'                => '5',
327
+						'shortcodes_required' => ['[TICKET_LINE_ITEM_LIST]'],
328
+					],
329
+					'ticket_line_item_pms'          => [
330
+						'input'               => 'textarea',
331
+						'label'               => '[TICKET_LINE_ITEM_LIST] <br>' . esc_html__(
332
+							'Ticket Line Item List with Price Modifiers',
333
+							'event_espresso'
334
+						),
335
+						'type'                => 'string',
336
+						'required'            => false,
337
+						'validation'          => true,
338
+						'format'              => '%s',
339
+						'css_class'           => 'large-text',
340
+						'rows'                => '5',
341
+						'shortcodes_required' => ['[TICKET_LINE_ITEM_LIST]'],
342
+					],
343
+					'price_modifier_line_item_list' => [
344
+						'input'               => 'textarea',
345
+						'label'               => '[PRICE_MODIFIER_LINE_ITEM_LIST]',
346
+						'type'                => 'string',
347
+						'required'            => false,
348
+						'validation'          => true,
349
+						'format'              => '%s',
350
+						'css_class'           => 'large-text',
351
+						'rows'                => '5',
352
+						'shortcodes_required' => ['[PRICE_MODIFIER_LINE_ITEM_LIST]'],
353
+					],
354
+					'datetime_list'                 => [
355
+						'input'               => 'textarea',
356
+						'label'               => '[DATETIME_LIST]',
357
+						'type'                => 'string',
358
+						'required'            => true,
359
+						'validation'          => true,
360
+						'format'              => '%s',
361
+						'css_class'           => 'large-text',
362
+						'rows'                => '5',
363
+						'shortcodes_required' => ['[DATETIME_LIST]'],
364
+					],
365
+					'attendee_list'                 => [
366
+						'input'               => 'textarea',
367
+						'label'               => '[ATTENDEE_LIST]',
368
+						'type'                => 'string',
369
+						'required'            => true,
370
+						'validation'          => true,
371
+						'format'              => '%s',
372
+						'css_class'           => 'large-text',
373
+						'rows'                => '5',
374
+						'shortcodes_required' => ['[ATTENDEE_LIST]'],
375
+					],
376
+					'tax_line_item_list'            => [
377
+						'input'               => 'textarea',
378
+						'label'               => '[TAX_LINE_ITEM_LIST]',
379
+						'type'                => 'string',
380
+						'required'            => false,
381
+						'validation'          => true,
382
+						'format'              => '%s',
383
+						'css_class'           => 'large-text',
384
+						'rows'                => '5',
385
+						'shortcodes_required' => ['[TAX_LINE_ITEM_LIST]'],
386
+					],
387
+					'additional_line_item_list'     => [
388
+						'input'               => 'textarea',
389
+						'label'               => '[ADDITIONAL_LINE_ITEM_LIST]',
390
+						'type'                => 'string',
391
+						'required'            => false,
392
+						'validation'          => true,
393
+						'format'              => '%s',
394
+						'css_class'           => 'large-text',
395
+						'rows'                => '5',
396
+						'shortcodes_required' => ['[ADDITIONAL_LINE_ITEM_LIST]'],
397
+					],
398
+					'payment_list'                  => [
399
+						'input'               => 'textarea',
400
+						'label'               => '[PAYMENT_LIST]',
401
+						'type'                => 'string',
402
+						'required'            => true,
403
+						'validation'          => true,
404
+						'format'              => '%s',
405
+						'css_class'           => 'large-text',
406
+						'rows'                => '5',
407
+						'shortcodes_required' => ['[PAYMENT_LIST_*]'],
408
+					],
409
+				],
410
+			],
411
+		];
412
+	}
413
+
414
+
415
+	/**
416
+	 * @see   definition of this method in parent
417
+	 * @since 4.5.0
418
+	 */
419
+	protected function _set_default_message_types()
420
+	{
421
+		$this->_default_message_types = ['receipt', 'invoice'];
422
+	}
423
+
424
+
425
+	/**
426
+	 * @see   definition of this method in parent
427
+	 * @since 4.5.0
428
+	 */
429
+	protected function _set_valid_message_types()
430
+	{
431
+		$this->_valid_message_types = ['receipt', 'invoice'];
432
+	}
433
+
434
+
435
+	/**
436
+	 * Displays the message in the browser.
437
+	 *
438
+	 * @return void.
439
+	 * @since 4.5.0
440
+	 */
441
+	protected function _send_message()
442
+	{
443
+		$this->_template_args = [
444
+			'page_title' => $this->_subject,
445
+			'base_css'   => $this->get_variation(
446
+				$this->_tmp_pack,
447
+				$this->_incoming_message_type->name,
448
+				true,
449
+				'base',
450
+				$this->_variation
451
+			),
452
+			'print_css'  => $this->get_variation(
453
+				$this->_tmp_pack,
454
+				$this->_incoming_message_type->name,
455
+				true,
456
+				'print',
457
+				$this->_variation
458
+			),
459
+			'main_css'   => $this->get_variation(
460
+				$this->_tmp_pack,
461
+				$this->_incoming_message_type->name,
462
+				true,
463
+				'main',
464
+				$this->_variation
465
+			),
466
+			'main_body'  => wpautop(
467
+				apply_filters(
468
+					'FHEE__EE_Html_messenger___send_message__main_body',
469
+					$this->_content,
470
+					$this->_content,
471
+					$this->_incoming_message_type
472
+				)
473
+			),
474
+		];
475
+		$this->_deregister_wp_hooks();
476
+		add_action('wp_enqueue_scripts', [$this, 'enqueue_scripts_styles']);
477
+		echo ($this->_get_main_template());
478
+		exit();
479
+	}
480
+
481
+
482
+	/**
483
+	 * The purpose of this function is to de register all actions hooked into wp_head and wp_footer so that it doesn't
484
+	 * interfere with our templates.  If users want to add any custom styles or scripts they must use the
485
+	 * AHEE__EE_Html_messenger__enqueue_scripts_styles hook.
486
+	 *
487
+	 * @return void
488
+	 * @since 4.5.0
489
+	 */
490
+	protected function _deregister_wp_hooks()
491
+	{
492
+		remove_all_actions('wp_head');
493
+		remove_all_actions('wp_footer');
494
+		remove_all_actions('wp_print_footer_scripts');
495
+		remove_all_actions('wp_enqueue_scripts');
496
+		global $wp_scripts, $wp_styles;
497
+		$wp_scripts = $wp_styles = [];
498
+		// just add back in wp_enqueue_scripts and wp_print_footer_scripts cause that's all we want to load.
499
+		add_action('wp_footer', 'wp_print_footer_scripts');
500
+		add_action('wp_print_footer_scripts', '_wp_footer_scripts');
501
+		add_action('wp_head', 'wp_enqueue_scripts');
502
+	}
503
+
504
+
505
+	/**
506
+	 * Overwrite parent _get_main_template for display_html purposes.
507
+	 *
508
+	 * @param bool $preview
509
+	 * @return string
510
+	 * @since  4.5.0
511
+	 */
512
+	protected function _get_main_template($preview = false): string
513
+	{
514
+		$wrapper_template = $this->_tmp_pack->get_wrapper($this->name);
515
+		// include message type as a template arg
516
+		$this->_template_args['message_type'] = $this->_incoming_message_type;
517
+		return EEH_Template::display_template($wrapper_template, $this->_template_args, true);
518
+	}
519
+
520
+
521
+	/**
522
+	 * @return void
523
+	 */
524
+	protected function _preview()
525
+	{
526
+		$this->_send_message();
527
+	}
528
+
529
+
530
+	protected function _set_admin_settings_fields()
531
+	{
532
+	}
533
+
534
+
535
+	/**
536
+	 * add the "powered by EE" credit link to the HTML receipt and invoice
537
+	 *
538
+	 * @param string          $content
539
+	 * @param string          $content_again
540
+	 * @param EE_message_type $incoming_message_type
541
+	 * @return string
542
+	 */
543
+	public function add_powered_by_credit_link_to_receipt_and_invoice(
544
+		string $content,
545
+		string $content_again,
546
+		EE_message_type $incoming_message_type
547
+	): string {
548
+		if (
549
+			($incoming_message_type->name === 'invoice' || $incoming_message_type->name === 'receipt')
550
+			&& apply_filters('FHEE_EE_Html_messenger__add_powered_by_credit_link_to_receipt_and_invoice', true)
551
+		) {
552
+			$content .= EEH_Template::powered_by_event_espresso(
553
+				'aln-cntr',
554
+				'',
555
+				['utm_content' => 'messages_system']
556
+			) . EEH_HTML::div(EEH_HTML::p('&nbsp;'));
557
+		}
558
+		return $content;
559
+	}
560 560
 }
Please login to merge, or discard this patch.
core/libraries/iframe_display/Iframe.php 1 patch
Indentation   +334 added lines, -334 removed lines patch added patch discarded remove patch
@@ -18,373 +18,373 @@
 block discarded – undo
18 18
  */
19 19
 class Iframe
20 20
 {
21
-    /*
21
+	/*
22 22
     * HTML for notices and ajax gif
23 23
     * @var string $title
24 24
     */
25
-    protected $title = '';
25
+	protected $title = '';
26 26
 
27
-    /*
27
+	/*
28 28
     * HTML for the content being displayed
29 29
     * @var string $content
30 30
     */
31
-    protected $content = '';
31
+	protected $content = '';
32 32
 
33
-    /*
33
+	/*
34 34
     * whether or not to call wp_head() and wp_footer()
35 35
     * @var boolean $enqueue_wp_assets
36 36
     */
37
-    protected $enqueue_wp_assets = false;
37
+	protected $enqueue_wp_assets = false;
38 38
 
39
-    /*
39
+	/*
40 40
     * an array of CSS URLs
41 41
     * @var array $css
42 42
     */
43
-    protected $css = array();
43
+	protected $css = array();
44 44
 
45
-    /*
45
+	/*
46 46
     * an array of JS URLs to be set in the HTML header.
47 47
     * @var array $header_js
48 48
     */
49
-    protected $header_js = array();
49
+	protected $header_js = array();
50 50
 
51
-    /*
51
+	/*
52 52
     * an array of additional attributes to be added to <script> tags for header JS
53 53
     * @var array $footer_js
54 54
     */
55
-    protected $header_js_attributes = array();
55
+	protected $header_js_attributes = array();
56 56
 
57
-    /*
57
+	/*
58 58
     * an array of JS URLs to be displayed before the HTML </body> tag
59 59
     * @var array $footer_js
60 60
     */
61
-    protected $footer_js = array();
61
+	protected $footer_js = array();
62 62
 
63
-    /*
63
+	/*
64 64
     * an array of additional attributes to be added to <script> tags for footer JS
65 65
     * @var array $footer_js_attributes
66 66
     */
67
-    protected $footer_js_attributes = array();
67
+	protected $footer_js_attributes = array();
68 68
 
69
-    /*
69
+	/*
70 70
     * an array of JSON vars to be set in the HTML header.
71 71
     * @var array $localized_vars
72 72
     */
73
-    protected $localized_vars = array();
74
-
75
-
76
-    /**
77
-     * Iframe constructor
78
-     *
79
-     * @param string $title
80
-     * @param string $content
81
-     * @throws DomainException
82
-     */
83
-    public function __construct($title, $content)
84
-    {
85
-        global $wp_version;
86
-        if (! defined('EE_IFRAME_DIR_URL')) {
87
-            define('EE_IFRAME_DIR_URL', plugin_dir_url(__FILE__));
88
-        }
89
-        $this->setContent($content);
90
-        $this->setTitle($title);
91
-        $this->addStylesheets(
92
-            apply_filters(
93
-                'FHEE___EventEspresso_core_libraries_iframe_display_Iframe__construct__default_css',
94
-                array(
95
-                    'site_theme'       => get_stylesheet_directory_uri()
96
-                                          . '/style.css?ver=' . EVENT_ESPRESSO_VERSION,
97
-                    'dashicons'        => includes_url('css/dashicons.min.css?ver=' . $wp_version),
98
-                    'espresso_default' => EE_GLOBAL_ASSETS_URL
99
-                                          . 'css/espresso_default.css?ver=' . EVENT_ESPRESSO_VERSION,
100
-                ),
101
-                $this
102
-            )
103
-        );
104
-        $this->addScripts(
105
-            apply_filters(
106
-                'FHEE___EventEspresso_core_libraries_iframe_display_Iframe__construct__default_js',
107
-                array(
108
-                    'jquery'        => includes_url('js/jquery/jquery.js?ver=' . $wp_version),
109
-                    'espresso_core' => EE_GLOBAL_ASSETS_URL
110
-                                       . 'scripts/espresso_core.js?ver=' . EVENT_ESPRESSO_VERSION,
111
-                ),
112
-                $this
113
-            )
114
-        );
115
-        if (
116
-            apply_filters(
117
-                'FHEE___EventEspresso_core_libraries_iframe_display_Iframe__construct__load_default_theme_stylesheet',
118
-                false
119
-            )
120
-        ) {
121
-            $this->addStylesheets(
122
-                apply_filters(
123
-                    'FHEE___EventEspresso_core_libraries_iframe_display_Iframe__construct__default_theme_stylesheet',
124
-                    array('default_theme_stylesheet' => get_stylesheet_uri()),
125
-                    $this
126
-                )
127
-            );
128
-        }
129
-    }
130
-
131
-
132
-    /**
133
-     * @param string $title
134
-     * @throws DomainException
135
-     */
136
-    public function setTitle($title)
137
-    {
138
-        if (empty($title)) {
139
-            throw new DomainException(
140
-                esc_html__('You must provide a page title in order to create an iframe.', 'event_espresso')
141
-            );
142
-        }
143
-        $this->title = $title;
144
-    }
145
-
146
-
147
-    /**
148
-     * @param string $content
149
-     * @throws DomainException
150
-     */
151
-    public function setContent($content)
152
-    {
153
-        if (empty($content)) {
154
-            throw new DomainException(
155
-                esc_html__('You must provide content in order to create an iframe.', 'event_espresso')
156
-            );
157
-        }
158
-        $this->content = $content;
159
-    }
160
-
161
-
162
-    /**
163
-     * @param boolean $enqueue_wp_assets
164
-     */
165
-    public function setEnqueueWpAssets($enqueue_wp_assets)
166
-    {
167
-        $this->enqueue_wp_assets = filter_var($enqueue_wp_assets, FILTER_VALIDATE_BOOLEAN);
168
-    }
169
-
170
-
171
-    /**
172
-     * @param array $stylesheets
173
-     * @throws DomainException
174
-     */
175
-    public function addStylesheets(array $stylesheets)
176
-    {
177
-        if (empty($stylesheets)) {
178
-            throw new DomainException(
179
-                esc_html__(
180
-                    'A non-empty array of URLs, is required to add a CSS stylesheet to an iframe.',
181
-                    'event_espresso'
182
-                )
183
-            );
184
-        }
185
-        foreach ($stylesheets as $handle => $stylesheet) {
186
-            $this->css[ $handle ] = $stylesheet;
187
-        }
188
-    }
189
-
190
-
191
-    /**
192
-     * @param array $scripts
193
-     * @param bool  $add_to_header
194
-     * @throws DomainException
195
-     */
196
-    public function addScripts(array $scripts, $add_to_header = false)
197
-    {
198
-        if (empty($scripts)) {
199
-            throw new DomainException(
200
-                esc_html__(
201
-                    'A non-empty array of URLs, is required to add Javascript to an iframe.',
202
-                    'event_espresso'
203
-                )
204
-            );
205
-        }
206
-        foreach ($scripts as $handle => $script) {
207
-            if ($add_to_header) {
208
-                $this->header_js[ $handle ] = $script;
209
-            } else {
210
-                $this->footer_js[ $handle ] = $script;
211
-            }
212
-        }
213
-    }
214
-
215
-
216
-    /**
217
-     * @param array $script_attributes
218
-     * @param bool  $add_to_header
219
-     * @throws DomainException
220
-     */
221
-    public function addScriptAttributes(array $script_attributes, $add_to_header = false)
222
-    {
223
-        if (empty($script_attributes)) {
224
-            throw new DomainException(
225
-                esc_html__(
226
-                    'A non-empty array of strings, is required to add attributes to iframe Javascript.',
227
-                    'event_espresso'
228
-                )
229
-            );
230
-        }
231
-        foreach ($script_attributes as $handle => $script_attribute) {
232
-            if ($add_to_header) {
233
-                $this->header_js_attributes[ $handle ] = $script_attribute;
234
-            } else {
235
-                $this->footer_js_attributes[ $handle ] = $script_attribute;
236
-            }
237
-        }
238
-    }
239
-
240
-
241
-    /**
242
-     * @param array  $vars
243
-     * @param string $var_name
244
-     * @throws DomainException
245
-     */
246
-    public function addLocalizedVars(array $vars, $var_name = 'eei18n')
247
-    {
248
-        if (empty($vars)) {
249
-            throw new DomainException(
250
-                esc_html__(
251
-                    'A non-empty array of vars, is required to add localized Javascript vars to an iframe.',
252
-                    'event_espresso'
253
-                )
254
-            );
255
-        }
256
-        foreach ($vars as $handle => $var) {
257
-            if ($var_name === 'eei18n') {
258
-                EE_Registry::$i18n_js_strings[ $handle ] = $var;
259
-            } elseif ($var_name === 'eeCAL' && $handle === 'espresso_calendar') {
260
-                $this->localized_vars[ $var_name ] = $var;
261
-            } else {
262
-                if (! isset($this->localized_vars[ $var_name ])) {
263
-                    $this->localized_vars[ $var_name ] = array();
264
-                }
265
-                $this->localized_vars[ $var_name ][ $handle ] = $var;
266
-            }
267
-        }
268
-    }
269
-
270
-
271
-    /**
272
-     * @param string $utm_content
273
-     * @throws DomainException
274
-     */
275
-    public function display($utm_content = '')
276
-    {
277
-        $this->content .= EEH_Template::powered_by_event_espresso(
278
-            '',
279
-            '',
280
-            ! empty($utm_content) ? array('utm_content' => $utm_content) : array()
281
-        );
282
-        EE_System::do_not_cache();
283
-        echo ($this->getTemplate());
284
-        exit;
285
-    }
286
-
287
-
288
-    /**
289
-     * @return string
290
-     * @throws DomainException
291
-     */
292
-    public function getTemplate()
293
-    {
294
-        return EEH_Template::display_template(
295
-            __DIR__ . DIRECTORY_SEPARATOR . 'iframe_wrapper.template.php',
296
-            array(
297
-                'title'                => apply_filters(
298
-                    'FHEE___EventEspresso_core_libraries_iframe_display_Iframe__getTemplate__title',
299
-                    $this->title,
300
-                    $this
301
-                ),
302
-                'content'              => apply_filters(
303
-                    'FHEE___EventEspresso_core_libraries_iframe_display_Iframe__getTemplate__content',
304
-                    $this->content,
305
-                    $this
306
-                ),
307
-                'enqueue_wp_assets'    => apply_filters(
308
-                    'FHEE___EventEspresso_core_libraries_iframe_display_Iframe__getTemplate__enqueue_wp_assets',
309
-                    $this->enqueue_wp_assets,
310
-                    $this
311
-                ),
312
-                'css'                  => (array) apply_filters(
313
-                    'FHEE___EventEspresso_core_libraries_iframe_display_Iframe__getTemplate__css_urls',
314
-                    $this->css,
315
-                    $this
316
-                ),
317
-                'header_js'            => (array) apply_filters(
318
-                    'FHEE___EventEspresso_core_libraries_iframe_display_Iframe__getTemplate__header_js_urls',
319
-                    $this->header_js,
320
-                    $this
321
-                ),
322
-                'header_js_attributes' => (array) apply_filters(
323
-                    'FHEE___EventEspresso_core_libraries_iframe_display_Iframe__getTemplate__header_js_attributes',
324
-                    $this->header_js_attributes,
325
-                    $this
326
-                ),
327
-                'footer_js'            => (array) apply_filters(
328
-                    'FHEE___EventEspresso_core_libraries_iframe_display_Iframe__getTemplate__footer_js_urls',
329
-                    $this->footer_js,
330
-                    $this
331
-                ),
332
-                'footer_js_attributes' => (array) apply_filters(
333
-                    'FHEE___EventEspresso_core_libraries_iframe_display_Iframe__getTemplate__footer_js_attributes',
334
-                    $this->footer_js_attributes,
335
-                    $this
336
-                ),
337
-                'eei18n'               => apply_filters(
338
-                    'FHEE___EventEspresso_core_libraries_iframe_display_Iframe__getTemplate__eei18n_js_strings',
339
-                    EE_Registry::localize_i18n_js_strings() . $this->localizeJsonVars(),
340
-                    $this
341
-                ),
342
-                'notices'              => EEH_Template::display_template(
343
-                    EE_TEMPLATES . 'espresso-ajax-notices.template.php',
344
-                    array(),
345
-                    true
346
-                ),
347
-            ),
348
-            true,
349
-            true
350
-        );
351
-    }
352
-
353
-
354
-    /**
355
-     * localizeJsonVars
356
-     *
357
-     * @return string
358
-     */
359
-    public function localizeJsonVars()
360
-    {
361
-        $JSON = '';
362
-        foreach ($this->localized_vars as $var_name => $vars) {
363
-            $this->localized_vars[ $var_name ] = $this->encodeJsonVars($vars);
364
-            $JSON .= "/* <![CDATA[ */ var {$var_name} = ";
365
-            $JSON .= wp_json_encode($this->localized_vars[ $var_name ]);
366
-            $JSON .= '; /* ]]> */';
367
-        }
368
-        return $JSON;
369
-    }
370
-
371
-
372
-    /**
373
-     * @param bool|int|float|string|array $var
374
-     * @return array|string|null
375
-     */
376
-    public function encodeJsonVars($var)
377
-    {
378
-        if (is_array($var)) {
379
-            $localized_vars = array();
380
-            foreach ((array) $var as $key => $value) {
381
-                $localized_vars[ $key ] = $this->encodeJsonVars($value);
382
-            }
383
-            return $localized_vars;
384
-        }
385
-        if (is_scalar($var)) {
386
-            return html_entity_decode((string) $var, ENT_QUOTES, 'UTF-8');
387
-        }
388
-        return null;
389
-    }
73
+	protected $localized_vars = array();
74
+
75
+
76
+	/**
77
+	 * Iframe constructor
78
+	 *
79
+	 * @param string $title
80
+	 * @param string $content
81
+	 * @throws DomainException
82
+	 */
83
+	public function __construct($title, $content)
84
+	{
85
+		global $wp_version;
86
+		if (! defined('EE_IFRAME_DIR_URL')) {
87
+			define('EE_IFRAME_DIR_URL', plugin_dir_url(__FILE__));
88
+		}
89
+		$this->setContent($content);
90
+		$this->setTitle($title);
91
+		$this->addStylesheets(
92
+			apply_filters(
93
+				'FHEE___EventEspresso_core_libraries_iframe_display_Iframe__construct__default_css',
94
+				array(
95
+					'site_theme'       => get_stylesheet_directory_uri()
96
+										  . '/style.css?ver=' . EVENT_ESPRESSO_VERSION,
97
+					'dashicons'        => includes_url('css/dashicons.min.css?ver=' . $wp_version),
98
+					'espresso_default' => EE_GLOBAL_ASSETS_URL
99
+										  . 'css/espresso_default.css?ver=' . EVENT_ESPRESSO_VERSION,
100
+				),
101
+				$this
102
+			)
103
+		);
104
+		$this->addScripts(
105
+			apply_filters(
106
+				'FHEE___EventEspresso_core_libraries_iframe_display_Iframe__construct__default_js',
107
+				array(
108
+					'jquery'        => includes_url('js/jquery/jquery.js?ver=' . $wp_version),
109
+					'espresso_core' => EE_GLOBAL_ASSETS_URL
110
+									   . 'scripts/espresso_core.js?ver=' . EVENT_ESPRESSO_VERSION,
111
+				),
112
+				$this
113
+			)
114
+		);
115
+		if (
116
+			apply_filters(
117
+				'FHEE___EventEspresso_core_libraries_iframe_display_Iframe__construct__load_default_theme_stylesheet',
118
+				false
119
+			)
120
+		) {
121
+			$this->addStylesheets(
122
+				apply_filters(
123
+					'FHEE___EventEspresso_core_libraries_iframe_display_Iframe__construct__default_theme_stylesheet',
124
+					array('default_theme_stylesheet' => get_stylesheet_uri()),
125
+					$this
126
+				)
127
+			);
128
+		}
129
+	}
130
+
131
+
132
+	/**
133
+	 * @param string $title
134
+	 * @throws DomainException
135
+	 */
136
+	public function setTitle($title)
137
+	{
138
+		if (empty($title)) {
139
+			throw new DomainException(
140
+				esc_html__('You must provide a page title in order to create an iframe.', 'event_espresso')
141
+			);
142
+		}
143
+		$this->title = $title;
144
+	}
145
+
146
+
147
+	/**
148
+	 * @param string $content
149
+	 * @throws DomainException
150
+	 */
151
+	public function setContent($content)
152
+	{
153
+		if (empty($content)) {
154
+			throw new DomainException(
155
+				esc_html__('You must provide content in order to create an iframe.', 'event_espresso')
156
+			);
157
+		}
158
+		$this->content = $content;
159
+	}
160
+
161
+
162
+	/**
163
+	 * @param boolean $enqueue_wp_assets
164
+	 */
165
+	public function setEnqueueWpAssets($enqueue_wp_assets)
166
+	{
167
+		$this->enqueue_wp_assets = filter_var($enqueue_wp_assets, FILTER_VALIDATE_BOOLEAN);
168
+	}
169
+
170
+
171
+	/**
172
+	 * @param array $stylesheets
173
+	 * @throws DomainException
174
+	 */
175
+	public function addStylesheets(array $stylesheets)
176
+	{
177
+		if (empty($stylesheets)) {
178
+			throw new DomainException(
179
+				esc_html__(
180
+					'A non-empty array of URLs, is required to add a CSS stylesheet to an iframe.',
181
+					'event_espresso'
182
+				)
183
+			);
184
+		}
185
+		foreach ($stylesheets as $handle => $stylesheet) {
186
+			$this->css[ $handle ] = $stylesheet;
187
+		}
188
+	}
189
+
190
+
191
+	/**
192
+	 * @param array $scripts
193
+	 * @param bool  $add_to_header
194
+	 * @throws DomainException
195
+	 */
196
+	public function addScripts(array $scripts, $add_to_header = false)
197
+	{
198
+		if (empty($scripts)) {
199
+			throw new DomainException(
200
+				esc_html__(
201
+					'A non-empty array of URLs, is required to add Javascript to an iframe.',
202
+					'event_espresso'
203
+				)
204
+			);
205
+		}
206
+		foreach ($scripts as $handle => $script) {
207
+			if ($add_to_header) {
208
+				$this->header_js[ $handle ] = $script;
209
+			} else {
210
+				$this->footer_js[ $handle ] = $script;
211
+			}
212
+		}
213
+	}
214
+
215
+
216
+	/**
217
+	 * @param array $script_attributes
218
+	 * @param bool  $add_to_header
219
+	 * @throws DomainException
220
+	 */
221
+	public function addScriptAttributes(array $script_attributes, $add_to_header = false)
222
+	{
223
+		if (empty($script_attributes)) {
224
+			throw new DomainException(
225
+				esc_html__(
226
+					'A non-empty array of strings, is required to add attributes to iframe Javascript.',
227
+					'event_espresso'
228
+				)
229
+			);
230
+		}
231
+		foreach ($script_attributes as $handle => $script_attribute) {
232
+			if ($add_to_header) {
233
+				$this->header_js_attributes[ $handle ] = $script_attribute;
234
+			} else {
235
+				$this->footer_js_attributes[ $handle ] = $script_attribute;
236
+			}
237
+		}
238
+	}
239
+
240
+
241
+	/**
242
+	 * @param array  $vars
243
+	 * @param string $var_name
244
+	 * @throws DomainException
245
+	 */
246
+	public function addLocalizedVars(array $vars, $var_name = 'eei18n')
247
+	{
248
+		if (empty($vars)) {
249
+			throw new DomainException(
250
+				esc_html__(
251
+					'A non-empty array of vars, is required to add localized Javascript vars to an iframe.',
252
+					'event_espresso'
253
+				)
254
+			);
255
+		}
256
+		foreach ($vars as $handle => $var) {
257
+			if ($var_name === 'eei18n') {
258
+				EE_Registry::$i18n_js_strings[ $handle ] = $var;
259
+			} elseif ($var_name === 'eeCAL' && $handle === 'espresso_calendar') {
260
+				$this->localized_vars[ $var_name ] = $var;
261
+			} else {
262
+				if (! isset($this->localized_vars[ $var_name ])) {
263
+					$this->localized_vars[ $var_name ] = array();
264
+				}
265
+				$this->localized_vars[ $var_name ][ $handle ] = $var;
266
+			}
267
+		}
268
+	}
269
+
270
+
271
+	/**
272
+	 * @param string $utm_content
273
+	 * @throws DomainException
274
+	 */
275
+	public function display($utm_content = '')
276
+	{
277
+		$this->content .= EEH_Template::powered_by_event_espresso(
278
+			'',
279
+			'',
280
+			! empty($utm_content) ? array('utm_content' => $utm_content) : array()
281
+		);
282
+		EE_System::do_not_cache();
283
+		echo ($this->getTemplate());
284
+		exit;
285
+	}
286
+
287
+
288
+	/**
289
+	 * @return string
290
+	 * @throws DomainException
291
+	 */
292
+	public function getTemplate()
293
+	{
294
+		return EEH_Template::display_template(
295
+			__DIR__ . DIRECTORY_SEPARATOR . 'iframe_wrapper.template.php',
296
+			array(
297
+				'title'                => apply_filters(
298
+					'FHEE___EventEspresso_core_libraries_iframe_display_Iframe__getTemplate__title',
299
+					$this->title,
300
+					$this
301
+				),
302
+				'content'              => apply_filters(
303
+					'FHEE___EventEspresso_core_libraries_iframe_display_Iframe__getTemplate__content',
304
+					$this->content,
305
+					$this
306
+				),
307
+				'enqueue_wp_assets'    => apply_filters(
308
+					'FHEE___EventEspresso_core_libraries_iframe_display_Iframe__getTemplate__enqueue_wp_assets',
309
+					$this->enqueue_wp_assets,
310
+					$this
311
+				),
312
+				'css'                  => (array) apply_filters(
313
+					'FHEE___EventEspresso_core_libraries_iframe_display_Iframe__getTemplate__css_urls',
314
+					$this->css,
315
+					$this
316
+				),
317
+				'header_js'            => (array) apply_filters(
318
+					'FHEE___EventEspresso_core_libraries_iframe_display_Iframe__getTemplate__header_js_urls',
319
+					$this->header_js,
320
+					$this
321
+				),
322
+				'header_js_attributes' => (array) apply_filters(
323
+					'FHEE___EventEspresso_core_libraries_iframe_display_Iframe__getTemplate__header_js_attributes',
324
+					$this->header_js_attributes,
325
+					$this
326
+				),
327
+				'footer_js'            => (array) apply_filters(
328
+					'FHEE___EventEspresso_core_libraries_iframe_display_Iframe__getTemplate__footer_js_urls',
329
+					$this->footer_js,
330
+					$this
331
+				),
332
+				'footer_js_attributes' => (array) apply_filters(
333
+					'FHEE___EventEspresso_core_libraries_iframe_display_Iframe__getTemplate__footer_js_attributes',
334
+					$this->footer_js_attributes,
335
+					$this
336
+				),
337
+				'eei18n'               => apply_filters(
338
+					'FHEE___EventEspresso_core_libraries_iframe_display_Iframe__getTemplate__eei18n_js_strings',
339
+					EE_Registry::localize_i18n_js_strings() . $this->localizeJsonVars(),
340
+					$this
341
+				),
342
+				'notices'              => EEH_Template::display_template(
343
+					EE_TEMPLATES . 'espresso-ajax-notices.template.php',
344
+					array(),
345
+					true
346
+				),
347
+			),
348
+			true,
349
+			true
350
+		);
351
+	}
352
+
353
+
354
+	/**
355
+	 * localizeJsonVars
356
+	 *
357
+	 * @return string
358
+	 */
359
+	public function localizeJsonVars()
360
+	{
361
+		$JSON = '';
362
+		foreach ($this->localized_vars as $var_name => $vars) {
363
+			$this->localized_vars[ $var_name ] = $this->encodeJsonVars($vars);
364
+			$JSON .= "/* <![CDATA[ */ var {$var_name} = ";
365
+			$JSON .= wp_json_encode($this->localized_vars[ $var_name ]);
366
+			$JSON .= '; /* ]]> */';
367
+		}
368
+		return $JSON;
369
+	}
370
+
371
+
372
+	/**
373
+	 * @param bool|int|float|string|array $var
374
+	 * @return array|string|null
375
+	 */
376
+	public function encodeJsonVars($var)
377
+	{
378
+		if (is_array($var)) {
379
+			$localized_vars = array();
380
+			foreach ((array) $var as $key => $value) {
381
+				$localized_vars[ $key ] = $this->encodeJsonVars($value);
382
+			}
383
+			return $localized_vars;
384
+		}
385
+		if (is_scalar($var)) {
386
+			return html_entity_decode((string) $var, ENT_QUOTES, 'UTF-8');
387
+		}
388
+		return null;
389
+	}
390 390
 }
Please login to merge, or discard this patch.
core/libraries/iframe_display/iframe_wrapper.template.php 2 patches
Spacing   +2 added lines, -2 removed lines patch added patch discarded remove patch
@@ -36,7 +36,7 @@  discard block
 block discarded – undo
36 36
                 <?php echo ($eei18n); ?>
37 37
             </script>
38 38
         <?php foreach ($header_js as $key => $url) :?>
39
-            <?php $header_attributes = isset($header_js_attributes[ $key ]) ? $header_js_attributes[ $key ] : ''; ?>
39
+            <?php $header_attributes = isset($header_js_attributes[$key]) ? $header_js_attributes[$key] : ''; ?>
40 40
         <script type="text/javascript" src="<?php echo esc_url_raw($url); ?>" <?php echo AttributesSanitizer::clean($header_attributes, AllowedTags::getAllowedTags(), 'script'); ?>></script>
41 41
         <?php endforeach; ?>
42 42
     <?php endif; ?>
@@ -47,7 +47,7 @@  discard block
 block discarded – undo
47 47
     <?php echo wp_kses($content, AllowedTags::getWithFullTags()); ?>
48 48
 </div>
49 49
 <?php foreach ($footer_js as $key => $url) : ?>
50
-    <?php $footer_attributes = isset($footer_js_attributes[ $key ]) ? $footer_js_attributes[ $key ] : ''; ?>
50
+    <?php $footer_attributes = isset($footer_js_attributes[$key]) ? $footer_js_attributes[$key] : ''; ?>
51 51
     <script type="text/javascript" src="<?php echo esc_url_raw($url); ?>" <?php echo AttributesSanitizer::clean($footer_attributes, AllowedTags::getAllowedTags(), 'script'); ?>></script>
52 52
 <?php endforeach; ?>
53 53
 <?php if ($enqueue_wp_assets) : ?>
Please login to merge, or discard this patch.
Braces   +5 added lines, -2 removed lines patch added patch discarded remove patch
@@ -28,9 +28,12 @@
 block discarded – undo
28 28
     <title><?php echo wp_strip_all_tags($title); ?></title>
29 29
     <?php if ($enqueue_wp_assets) : ?>
30 30
         <?php wp_head(); ?>
31
-    <?php else : ?>
31
+    <?php else {
32
+	: ?>
32 33
         <?php foreach ($css as $url) :?>
33
-    <link rel="stylesheet" type="text/css" href="<?php echo esc_url_raw($url); ?>">
34
+    <link rel="stylesheet" type="text/css" href="<?php echo esc_url_raw($url);
35
+}
36
+?>">
34 37
         <?php endforeach; ?>
35 38
             <script type="text/javascript">
36 39
                 <?php echo ($eei18n); ?>
Please login to merge, or discard this patch.
admin_pages/events/Events_Admin_Page.core.php 1 patch
Indentation   +2799 added lines, -2799 removed lines patch added patch discarded remove patch
@@ -15,2806 +15,2806 @@
 block discarded – undo
15 15
  */
16 16
 class Events_Admin_Page extends EE_Admin_Page_CPT
17 17
 {
18
-    /**
19
-     * This will hold the event object for event_details screen.
18
+	/**
19
+	 * This will hold the event object for event_details screen.
20
+	 *
21
+	 * @var EE_Event $_event
22
+	 */
23
+	protected $_event;
24
+
25
+
26
+	/**
27
+	 * This will hold the category object for category_details screen.
28
+	 *
29
+	 * @var stdClass $_category
30
+	 */
31
+	protected $_category;
32
+
33
+
34
+	/**
35
+	 * This will hold the event model instance
36
+	 *
37
+	 * @var EEM_Event $_event_model
38
+	 */
39
+	protected $_event_model;
40
+
41
+
42
+	/**
43
+	 * @var EE_Event
44
+	 */
45
+	protected $_cpt_model_obj = false;
46
+
47
+
48
+	/**
49
+	 * @var NodeGroupDao
50
+	 */
51
+	protected $model_obj_node_group_persister;
52
+
53
+
54
+	/**
55
+	 * Initialize page props for this admin page group.
56
+	 */
57
+	protected function _init_page_props()
58
+	{
59
+		$this->page_slug        = EVENTS_PG_SLUG;
60
+		$this->page_label       = EVENTS_LABEL;
61
+		$this->_admin_base_url  = EVENTS_ADMIN_URL;
62
+		$this->_admin_base_path = EVENTS_ADMIN;
63
+		$this->_cpt_model_names = [
64
+			'create_new' => 'EEM_Event',
65
+			'edit'       => 'EEM_Event',
66
+		];
67
+		$this->_cpt_edit_routes = [
68
+			'espresso_events' => 'edit',
69
+		];
70
+		add_action(
71
+			'AHEE__EE_Admin_Page_CPT__set_model_object__after_set_object',
72
+			[$this, 'verify_event_edit'],
73
+			10,
74
+			2
75
+		);
76
+	}
77
+
78
+
79
+	/**
80
+	 * Sets the ajax hooks used for this admin page group.
81
+	 */
82
+	protected function _ajax_hooks()
83
+	{
84
+		add_action('wp_ajax_ee_save_timezone_setting', [$this, 'saveTimezoneString']);
85
+	}
86
+
87
+
88
+	/**
89
+	 * Sets the page properties for this admin page group.
90
+	 */
91
+	protected function _define_page_props()
92
+	{
93
+		$this->_admin_page_title = EVENTS_LABEL;
94
+		$this->_labels           = [
95
+			'buttons'      => [
96
+				'add'             => esc_html__('Add New Event', 'event_espresso'),
97
+				'edit'            => esc_html__('Edit Event', 'event_espresso'),
98
+				'delete'          => esc_html__('Delete Event', 'event_espresso'),
99
+				'add_category'    => esc_html__('Add New Category', 'event_espresso'),
100
+				'edit_category'   => esc_html__('Edit Category', 'event_espresso'),
101
+				'delete_category' => esc_html__('Delete Category', 'event_espresso'),
102
+			],
103
+			'editor_title' => [
104
+				'espresso_events' => esc_html__('Enter event title here', 'event_espresso'),
105
+			],
106
+			'publishbox'   => [
107
+				'create_new'        => esc_html__('Save New Event', 'event_espresso'),
108
+				'edit'              => esc_html__('Update Event', 'event_espresso'),
109
+				'add_category'      => esc_html__('Save New Category', 'event_espresso'),
110
+				'edit_category'     => esc_html__('Update Category', 'event_espresso'),
111
+				'template_settings' => esc_html__('Update Settings', 'event_espresso'),
112
+			],
113
+		];
114
+	}
115
+
116
+
117
+	/**
118
+	 * Sets the page routes property for this admin page group.
119
+	 */
120
+	protected function _set_page_routes()
121
+	{
122
+		// load formatter helper
123
+		// load field generator helper
124
+		// is there a evt_id in the request?
125
+		$EVT_ID = $this->request->getRequestParam('EVT_ID', 0, 'int');
126
+		$EVT_ID = $this->request->getRequestParam('post', $EVT_ID, 'int');
127
+
128
+		$this->_page_routes = [
129
+			'default'                       => [
130
+				'func'       => '_events_overview_list_table',
131
+				'capability' => 'ee_read_events',
132
+			],
133
+			'create_new'                    => [
134
+				'func'       => '_create_new_cpt_item',
135
+				'capability' => 'ee_edit_events',
136
+			],
137
+			'edit'                          => [
138
+				'func'       => '_edit_cpt_item',
139
+				'capability' => 'ee_edit_event',
140
+				'obj_id'     => $EVT_ID,
141
+			],
142
+			'copy_event'                    => [
143
+				'func'       => '_copy_events',
144
+				'capability' => 'ee_edit_event',
145
+				'obj_id'     => $EVT_ID,
146
+				'noheader'   => true,
147
+			],
148
+			'trash_event'                   => [
149
+				'func'       => '_trash_or_restore_event',
150
+				'args'       => ['event_status' => 'trash'],
151
+				'capability' => 'ee_delete_event',
152
+				'obj_id'     => $EVT_ID,
153
+				'noheader'   => true,
154
+			],
155
+			'trash_events'                  => [
156
+				'func'       => '_trash_or_restore_events',
157
+				'args'       => ['event_status' => 'trash'],
158
+				'capability' => 'ee_delete_events',
159
+				'noheader'   => true,
160
+			],
161
+			'restore_event'                 => [
162
+				'func'       => '_trash_or_restore_event',
163
+				'args'       => ['event_status' => 'draft'],
164
+				'capability' => 'ee_delete_event',
165
+				'obj_id'     => $EVT_ID,
166
+				'noheader'   => true,
167
+			],
168
+			'restore_events'                => [
169
+				'func'       => '_trash_or_restore_events',
170
+				'args'       => ['event_status' => 'draft'],
171
+				'capability' => 'ee_delete_events',
172
+				'noheader'   => true,
173
+			],
174
+			'delete_event'                  => [
175
+				'func'       => '_delete_event',
176
+				'capability' => 'ee_delete_event',
177
+				'obj_id'     => $EVT_ID,
178
+				'noheader'   => true,
179
+			],
180
+			'delete_events'                 => [
181
+				'func'       => '_delete_events',
182
+				'capability' => 'ee_delete_events',
183
+				'noheader'   => true,
184
+			],
185
+			'view_report'                   => [
186
+				'func'       => '_view_report',
187
+				'capability' => 'ee_edit_events',
188
+			],
189
+			'default_event_settings'        => [
190
+				'func'       => '_default_event_settings',
191
+				'capability' => 'manage_options',
192
+			],
193
+			'update_default_event_settings' => [
194
+				'func'       => '_update_default_event_settings',
195
+				'capability' => 'manage_options',
196
+				'noheader'   => true,
197
+			],
198
+			'template_settings'             => [
199
+				'func'       => '_template_settings',
200
+				'capability' => 'manage_options',
201
+			],
202
+			// event category tab related
203
+			'add_category'                  => [
204
+				'func'       => '_category_details',
205
+				'capability' => 'ee_edit_event_category',
206
+				'args'       => ['add'],
207
+			],
208
+			'edit_category'                 => [
209
+				'func'       => '_category_details',
210
+				'capability' => 'ee_edit_event_category',
211
+				'args'       => ['edit'],
212
+			],
213
+			'delete_categories'             => [
214
+				'func'       => '_delete_categories',
215
+				'capability' => 'ee_delete_event_category',
216
+				'noheader'   => true,
217
+			],
218
+			'delete_category'               => [
219
+				'func'       => '_delete_categories',
220
+				'capability' => 'ee_delete_event_category',
221
+				'noheader'   => true,
222
+			],
223
+			'insert_category'               => [
224
+				'func'       => '_insert_or_update_category',
225
+				'args'       => ['new_category' => true],
226
+				'capability' => 'ee_edit_event_category',
227
+				'noheader'   => true,
228
+			],
229
+			'update_category'               => [
230
+				'func'       => '_insert_or_update_category',
231
+				'args'       => ['new_category' => false],
232
+				'capability' => 'ee_edit_event_category',
233
+				'noheader'   => true,
234
+			],
235
+			'category_list'                 => [
236
+				'func'       => '_category_list_table',
237
+				'capability' => 'ee_manage_event_categories',
238
+			],
239
+			'preview_deletion'              => [
240
+				'func'       => 'previewDeletion',
241
+				'capability' => 'ee_delete_events',
242
+			],
243
+			'confirm_deletion'              => [
244
+				'func'       => 'confirmDeletion',
245
+				'capability' => 'ee_delete_events',
246
+				'noheader'   => true,
247
+			],
248
+		];
249
+	}
250
+
251
+
252
+	/**
253
+	 * Set the _page_config property for this admin page group.
254
+	 */
255
+	protected function _set_page_config()
256
+	{
257
+		$post_id            = $this->request->getRequestParam('post', 0, 'int');
258
+		$EVT_CAT_ID         = $this->request->getRequestParam('EVT_CAT_ID', 0, 'int');
259
+		$this->_page_config = [
260
+			'default'                => [
261
+				'nav'           => [
262
+					'label' => esc_html__('Overview', 'event_espresso'),
263
+					'order' => 10,
264
+				],
265
+				'list_table'    => 'Events_Admin_List_Table',
266
+				'help_tabs'     => [
267
+					'events_overview_help_tab'                       => [
268
+						'title'    => esc_html__('Events Overview', 'event_espresso'),
269
+						'filename' => 'events_overview',
270
+					],
271
+					'events_overview_table_column_headings_help_tab' => [
272
+						'title'    => esc_html__('Events Overview Table Column Headings', 'event_espresso'),
273
+						'filename' => 'events_overview_table_column_headings',
274
+					],
275
+					'events_overview_filters_help_tab'               => [
276
+						'title'    => esc_html__('Events Overview Filters', 'event_espresso'),
277
+						'filename' => 'events_overview_filters',
278
+					],
279
+					'events_overview_view_help_tab'                  => [
280
+						'title'    => esc_html__('Events Overview Views', 'event_espresso'),
281
+						'filename' => 'events_overview_views',
282
+					],
283
+					'events_overview_other_help_tab'                 => [
284
+						'title'    => esc_html__('Events Overview Other', 'event_espresso'),
285
+						'filename' => 'events_overview_other',
286
+					],
287
+				],
288
+				'qtips'         => [
289
+					'EE_Event_List_Table_Tips',
290
+				],
291
+				'require_nonce' => false,
292
+			],
293
+			'create_new'             => [
294
+				'nav'           => [
295
+					'label'      => esc_html__('Add Event', 'event_espresso'),
296
+					'order'      => 5,
297
+					'persistent' => false,
298
+				],
299
+				'metaboxes'     => ['_register_event_editor_meta_boxes'],
300
+				'help_tabs'     => [
301
+					'event_editor_help_tab'                            => [
302
+						'title'    => esc_html__('Event Editor', 'event_espresso'),
303
+						'filename' => 'event_editor',
304
+					],
305
+					'event_editor_title_richtexteditor_help_tab'       => [
306
+						'title'    => esc_html__('Event Title & Rich Text Editor', 'event_espresso'),
307
+						'filename' => 'event_editor_title_richtexteditor',
308
+					],
309
+					'event_editor_venue_details_help_tab'              => [
310
+						'title'    => esc_html__('Event Venue Details', 'event_espresso'),
311
+						'filename' => 'event_editor_venue_details',
312
+					],
313
+					'event_editor_event_datetimes_help_tab'            => [
314
+						'title'    => esc_html__('Event Datetimes', 'event_espresso'),
315
+						'filename' => 'event_editor_event_datetimes',
316
+					],
317
+					'event_editor_event_tickets_help_tab'              => [
318
+						'title'    => esc_html__('Event Tickets', 'event_espresso'),
319
+						'filename' => 'event_editor_event_tickets',
320
+					],
321
+					'event_editor_event_registration_options_help_tab' => [
322
+						'title'    => esc_html__('Event Registration Options', 'event_espresso'),
323
+						'filename' => 'event_editor_event_registration_options',
324
+					],
325
+					'event_editor_tags_categories_help_tab'            => [
326
+						'title'    => esc_html__('Event Tags & Categories', 'event_espresso'),
327
+						'filename' => 'event_editor_tags_categories',
328
+					],
329
+					'event_editor_questions_registrants_help_tab'      => [
330
+						'title'    => esc_html__('Questions for Registrants', 'event_espresso'),
331
+						'filename' => 'event_editor_questions_registrants',
332
+					],
333
+					'event_editor_save_new_event_help_tab'             => [
334
+						'title'    => esc_html__('Save New Event', 'event_espresso'),
335
+						'filename' => 'event_editor_save_new_event',
336
+					],
337
+					'event_editor_other_help_tab'                      => [
338
+						'title'    => esc_html__('Event Other', 'event_espresso'),
339
+						'filename' => 'event_editor_other',
340
+					],
341
+				],
342
+				'qtips'         => ['EE_Event_Editor_Decaf_Tips'],
343
+				'require_nonce' => false,
344
+			],
345
+			'edit'                   => [
346
+				'nav'           => [
347
+					'label'      => esc_html__('Edit Event', 'event_espresso'),
348
+					'order'      => 5,
349
+					'persistent' => false,
350
+					'url'        => $post_id
351
+						? EE_Admin_Page::add_query_args_and_nonce(
352
+							['post' => $post_id, 'action' => 'edit'],
353
+							$this->_current_page_view_url
354
+						)
355
+						: $this->_admin_base_url,
356
+				],
357
+				'metaboxes'     => ['_register_event_editor_meta_boxes'],
358
+				'help_tabs'     => [
359
+					'event_editor_help_tab'                            => [
360
+						'title'    => esc_html__('Event Editor', 'event_espresso'),
361
+						'filename' => 'event_editor',
362
+					],
363
+					'event_editor_title_richtexteditor_help_tab'       => [
364
+						'title'    => esc_html__('Event Title & Rich Text Editor', 'event_espresso'),
365
+						'filename' => 'event_editor_title_richtexteditor',
366
+					],
367
+					'event_editor_venue_details_help_tab'              => [
368
+						'title'    => esc_html__('Event Venue Details', 'event_espresso'),
369
+						'filename' => 'event_editor_venue_details',
370
+					],
371
+					'event_editor_event_datetimes_help_tab'            => [
372
+						'title'    => esc_html__('Event Datetimes', 'event_espresso'),
373
+						'filename' => 'event_editor_event_datetimes',
374
+					],
375
+					'event_editor_event_tickets_help_tab'              => [
376
+						'title'    => esc_html__('Event Tickets', 'event_espresso'),
377
+						'filename' => 'event_editor_event_tickets',
378
+					],
379
+					'event_editor_event_registration_options_help_tab' => [
380
+						'title'    => esc_html__('Event Registration Options', 'event_espresso'),
381
+						'filename' => 'event_editor_event_registration_options',
382
+					],
383
+					'event_editor_tags_categories_help_tab'            => [
384
+						'title'    => esc_html__('Event Tags & Categories', 'event_espresso'),
385
+						'filename' => 'event_editor_tags_categories',
386
+					],
387
+					'event_editor_questions_registrants_help_tab'      => [
388
+						'title'    => esc_html__('Questions for Registrants', 'event_espresso'),
389
+						'filename' => 'event_editor_questions_registrants',
390
+					],
391
+					'event_editor_save_new_event_help_tab'             => [
392
+						'title'    => esc_html__('Save New Event', 'event_espresso'),
393
+						'filename' => 'event_editor_save_new_event',
394
+					],
395
+					'event_editor_other_help_tab'                      => [
396
+						'title'    => esc_html__('Event Other', 'event_espresso'),
397
+						'filename' => 'event_editor_other',
398
+					],
399
+				],
400
+				'qtips'         => ['EE_Event_Editor_Decaf_Tips'],
401
+				'require_nonce' => false,
402
+			],
403
+			'default_event_settings' => [
404
+				'nav'           => [
405
+					'label' => esc_html__('Default Settings', 'event_espresso'),
406
+					'order' => 40,
407
+				],
408
+				'metaboxes'     => array_merge($this->_default_espresso_metaboxes, ['_publish_post_box']),
409
+				'labels'        => [
410
+					'publishbox' => esc_html__('Update Settings', 'event_espresso'),
411
+				],
412
+				'help_tabs'     => [
413
+					'default_settings_help_tab'        => [
414
+						'title'    => esc_html__('Default Event Settings', 'event_espresso'),
415
+						'filename' => 'events_default_settings',
416
+					],
417
+					'default_settings_status_help_tab' => [
418
+						'title'    => esc_html__('Default Registration Status', 'event_espresso'),
419
+						'filename' => 'events_default_settings_status',
420
+					],
421
+					'default_maximum_tickets_help_tab' => [
422
+						'title'    => esc_html__('Default Maximum Tickets Per Order', 'event_espresso'),
423
+						'filename' => 'events_default_settings_max_tickets',
424
+					],
425
+				],
426
+				'require_nonce' => false,
427
+			],
428
+			// template settings
429
+			'template_settings'      => [
430
+				'nav'           => [
431
+					'label' => esc_html__('Templates', 'event_espresso'),
432
+					'order' => 30,
433
+				],
434
+				'metaboxes'     => $this->_default_espresso_metaboxes,
435
+				'help_tabs'     => [
436
+					'general_settings_templates_help_tab' => [
437
+						'title'    => esc_html__('Templates', 'event_espresso'),
438
+						'filename' => 'general_settings_templates',
439
+					],
440
+				],
441
+				'require_nonce' => false,
442
+			],
443
+			// event category stuff
444
+			'add_category'           => [
445
+				'nav'           => [
446
+					'label'      => esc_html__('Add Category', 'event_espresso'),
447
+					'order'      => 15,
448
+					'persistent' => false,
449
+				],
450
+				'help_tabs'     => [
451
+					'add_category_help_tab' => [
452
+						'title'    => esc_html__('Add New Event Category', 'event_espresso'),
453
+						'filename' => 'events_add_category',
454
+					],
455
+				],
456
+				'metaboxes'     => ['_publish_post_box'],
457
+				'require_nonce' => false,
458
+			],
459
+			'edit_category'          => [
460
+				'nav'           => [
461
+					'label'      => esc_html__('Edit Category', 'event_espresso'),
462
+					'order'      => 15,
463
+					'persistent' => false,
464
+					'url'        => $EVT_CAT_ID
465
+						? add_query_arg(
466
+							['EVT_CAT_ID' => $EVT_CAT_ID],
467
+							$this->_current_page_view_url
468
+						)
469
+						: $this->_admin_base_url,
470
+				],
471
+				'help_tabs'     => [
472
+					'edit_category_help_tab' => [
473
+						'title'    => esc_html__('Edit Event Category', 'event_espresso'),
474
+						'filename' => 'events_edit_category',
475
+					],
476
+				],
477
+				'metaboxes'     => ['_publish_post_box'],
478
+				'require_nonce' => false,
479
+			],
480
+			'category_list'          => [
481
+				'nav'           => [
482
+					'label' => esc_html__('Categories', 'event_espresso'),
483
+					'order' => 20,
484
+				],
485
+				'list_table'    => 'Event_Categories_Admin_List_Table',
486
+				'help_tabs'     => [
487
+					'events_categories_help_tab'                       => [
488
+						'title'    => esc_html__('Event Categories', 'event_espresso'),
489
+						'filename' => 'events_categories',
490
+					],
491
+					'events_categories_table_column_headings_help_tab' => [
492
+						'title'    => esc_html__('Event Categories Table Column Headings', 'event_espresso'),
493
+						'filename' => 'events_categories_table_column_headings',
494
+					],
495
+					'events_categories_view_help_tab'                  => [
496
+						'title'    => esc_html__('Event Categories Views', 'event_espresso'),
497
+						'filename' => 'events_categories_views',
498
+					],
499
+					'events_categories_other_help_tab'                 => [
500
+						'title'    => esc_html__('Event Categories Other', 'event_espresso'),
501
+						'filename' => 'events_categories_other',
502
+					],
503
+				],
504
+				'metaboxes'     => $this->_default_espresso_metaboxes,
505
+				'require_nonce' => false,
506
+			],
507
+			'preview_deletion'       => [
508
+				'nav'           => [
509
+					'label'      => esc_html__('Preview Deletion', 'event_espresso'),
510
+					'order'      => 15,
511
+					'persistent' => false,
512
+					'url'        => '',
513
+				],
514
+				'require_nonce' => false,
515
+			],
516
+		];
517
+	}
518
+
519
+
520
+	/**
521
+	 * Used to register any global screen options if necessary for every route in this admin page group.
522
+	 */
523
+	protected function _add_screen_options()
524
+	{
525
+	}
526
+
527
+
528
+	/**
529
+	 * Implementing the screen options for the 'default' route.
530
+	 */
531
+	protected function _add_screen_options_default()
532
+	{
533
+		$this->_per_page_screen_option();
534
+	}
535
+
536
+
537
+	/**
538
+	 * Implementing screen options for the category list route.
539
+	 */
540
+	protected function _add_screen_options_category_list()
541
+	{
542
+		$page_title              = $this->_admin_page_title;
543
+		$this->_admin_page_title = esc_html__('Categories', 'event_espresso');
544
+		$this->_per_page_screen_option();
545
+		$this->_admin_page_title = $page_title;
546
+	}
547
+
548
+
549
+	/**
550
+	 * Used to register any global feature pointers for the admin page group.
551
+	 */
552
+	protected function _add_feature_pointers()
553
+	{
554
+	}
555
+
556
+
557
+	/**
558
+	 * Registers and enqueues any global scripts and styles for the entire admin page group.
559
+	 */
560
+	public function load_scripts_styles()
561
+	{
562
+		wp_register_style(
563
+			'events-admin-css',
564
+			EVENTS_ASSETS_URL . 'events-admin-page.css',
565
+			[],
566
+			EVENT_ESPRESSO_VERSION
567
+		);
568
+		wp_register_style('ee-cat-admin', EVENTS_ASSETS_URL . 'ee-cat-admin.css', [], EVENT_ESPRESSO_VERSION);
569
+		wp_enqueue_style('events-admin-css');
570
+		wp_enqueue_style('ee-cat-admin');
571
+		// todo note: we also need to load_scripts_styles per view (i.e. default/view_report/event_details
572
+		// registers for all views
573
+		// scripts
574
+		wp_register_script(
575
+			'event_editor_js',
576
+			EVENTS_ASSETS_URL . 'event_editor.js',
577
+			['ee_admin_js', 'jquery-ui-slider', 'jquery-ui-timepicker-addon'],
578
+			EVENT_ESPRESSO_VERSION,
579
+			true
580
+		);
581
+	}
582
+
583
+
584
+	/**
585
+	 * Enqueuing scripts and styles specific to this view
586
+	 */
587
+	public function load_scripts_styles_create_new()
588
+	{
589
+		$this->load_scripts_styles_edit();
590
+	}
591
+
592
+
593
+	/**
594
+	 * Enqueuing scripts and styles specific to this view
595
+	 */
596
+	public function load_scripts_styles_edit()
597
+	{
598
+		// styles
599
+		wp_enqueue_style('espresso-ui-theme');
600
+		wp_register_style(
601
+			'event-editor-css',
602
+			EVENTS_ASSETS_URL . 'event-editor.css',
603
+			['ee-admin-css'],
604
+			EVENT_ESPRESSO_VERSION
605
+		);
606
+		wp_enqueue_style('event-editor-css');
607
+		// scripts
608
+		wp_register_script(
609
+			'event-datetime-metabox',
610
+			EVENTS_ASSETS_URL . 'event-datetime-metabox.js',
611
+			['event_editor_js', 'ee-datepicker'],
612
+			EVENT_ESPRESSO_VERSION
613
+		);
614
+		wp_enqueue_script('event-datetime-metabox');
615
+	}
616
+
617
+
618
+	/**
619
+	 * Populating the _views property for the category list table view.
620
+	 */
621
+	protected function _set_list_table_views_category_list()
622
+	{
623
+		$this->_views = [
624
+			'all' => [
625
+				'slug'        => 'all',
626
+				'label'       => esc_html__('All', 'event_espresso'),
627
+				'count'       => 0,
628
+				'bulk_action' => [
629
+					'delete_categories' => esc_html__('Delete Permanently', 'event_espresso'),
630
+				],
631
+			],
632
+		];
633
+	}
634
+
635
+
636
+	/**
637
+	 * For adding anything that fires on the admin_init hook for any route within this admin page group.
638
+	 */
639
+	public function admin_init()
640
+	{
641
+		EE_Registry::$i18n_js_strings['image_confirm'] = esc_html__(
642
+			'Do you really want to delete this image? Please remember to update your event to complete the removal.',
643
+			'event_espresso'
644
+		);
645
+	}
646
+
647
+
648
+	/**
649
+	 * For adding anything that should be triggered on the admin_notices hook for any route within this admin page
650
+	 * group.
651
+	 */
652
+	public function admin_notices()
653
+	{
654
+	}
655
+
656
+
657
+	/**
658
+	 * For adding anything that should be triggered on the `admin_print_footer_scripts` hook for any route within
659
+	 * this admin page group.
660
+	 */
661
+	public function admin_footer_scripts()
662
+	{
663
+	}
664
+
665
+
666
+	/**
667
+	 * Call this function to verify if an event is public and has tickets for sale.  If it does, then we need to show a
668
+	 * warning (via EE_Error::add_error());
669
+	 *
670
+	 * @param EE_Event $event Event object
671
+	 * @param string   $req_type
672
+	 * @return void
673
+	 * @throws EE_Error
674
+	 * @throws ReflectionException
675
+	 */
676
+	public function verify_event_edit($event = null, $req_type = '')
677
+	{
678
+		// don't need to do this when processing
679
+		if (! empty($req_type)) {
680
+			return;
681
+		}
682
+		// no event?
683
+		if (empty($event)) {
684
+			// set event
685
+			$event = $this->_cpt_model_obj;
686
+		}
687
+		// STILL no event?
688
+		if (! $event instanceof EE_Event) {
689
+			return;
690
+		}
691
+		$orig_status = $event->status();
692
+		// first check if event is active.
693
+		if (
694
+			$orig_status === EEM_Event::cancelled
695
+			|| $orig_status === EEM_Event::postponed
696
+			|| $event->is_expired()
697
+			|| $event->is_inactive()
698
+		) {
699
+			return;
700
+		}
701
+		// made it here so it IS active... next check that any of the tickets are sold.
702
+		if ($event->is_sold_out(true)) {
703
+			if ($orig_status !== EEM_Event::sold_out && $event->status() !== $orig_status) {
704
+				EE_Error::add_attention(
705
+					sprintf(
706
+						esc_html__(
707
+							'Please note that the Event Status has automatically been changed to %s because there are no more spaces available for this event.  However, this change is not permanent until you update the event.  You can change the status back to something else before updating if you wish.',
708
+							'event_espresso'
709
+						),
710
+						EEH_Template::pretty_status(EEM_Event::sold_out, false, 'sentence')
711
+					)
712
+				);
713
+			}
714
+			return;
715
+		} elseif ($orig_status === EEM_Event::sold_out) {
716
+			EE_Error::add_attention(
717
+				sprintf(
718
+					esc_html__(
719
+						'Please note that the Event Status has automatically been changed to %s because more spaces have become available for this event, most likely due to abandoned transactions freeing up reserved tickets.  However, this change is not permanent until you update the event. If you wish, you can change the status back to something else before updating.',
720
+						'event_espresso'
721
+					),
722
+					EEH_Template::pretty_status($event->status(), false, 'sentence')
723
+				)
724
+			);
725
+		}
726
+		// now we need to determine if the event has any tickets on sale.  If not then we dont' show the error
727
+		if (! $event->tickets_on_sale()) {
728
+			return;
729
+		}
730
+		// made it here so show warning
731
+		$this->_edit_event_warning();
732
+	}
733
+
734
+
735
+	/**
736
+	 * This is the text used for when an event is being edited that is public and has tickets for sale.
737
+	 * When needed, hook this into a EE_Error::add_error() notice.
738
+	 *
739
+	 * @access protected
740
+	 * @return void
741
+	 */
742
+	protected function _edit_event_warning()
743
+	{
744
+		// we don't want to add warnings during these requests
745
+		if ($this->request->getRequestParam('action') === 'editpost') {
746
+			return;
747
+		}
748
+		EE_Error::add_attention(
749
+			sprintf(
750
+				esc_html__(
751
+					'Your event is open for registration. Making changes may disrupt any transactions in progress. %sLearn more%s',
752
+					'event_espresso'
753
+				),
754
+				'<a class="espresso-help-tab-lnk">',
755
+				'</a>'
756
+			)
757
+		);
758
+	}
759
+
760
+
761
+	/**
762
+	 * When a user is creating a new event, notify them if they haven't set their timezone.
763
+	 * Otherwise, do the normal logic
764
+	 *
765
+	 * @return void
766
+	 * @throws EE_Error
767
+	 */
768
+	protected function _create_new_cpt_item()
769
+	{
770
+		$has_timezone_string = get_option('timezone_string');
771
+		// only nag them about setting their timezone if it's their first event, and they haven't already done it
772
+		if (! $has_timezone_string && ! EEM_Event::instance()->exists([])) {
773
+			EE_Error::add_attention(
774
+				sprintf(
775
+					esc_html__(
776
+						'Your website\'s timezone is currently set to a UTC offset. We recommend updating your timezone to a city or region near you before you create an event. Change your timezone now:%1$s%2$s%3$sChange Timezone%4$s',
777
+						'event_espresso'
778
+					),
779
+					'<br>',
780
+					'<select id="timezone_string" name="timezone_string" aria-describedby="timezone-description">'
781
+					. EEH_DTT_Helper::wp_timezone_choice('', EEH_DTT_Helper::get_user_locale())
782
+					. '</select>',
783
+					'<button class="button button-secondary timezone-submit">',
784
+					'</button><span class="spinner"></span>'
785
+				),
786
+				__FILE__,
787
+				__FUNCTION__,
788
+				__LINE__
789
+			);
790
+		}
791
+		parent::_create_new_cpt_item();
792
+	}
793
+
794
+
795
+	/**
796
+	 * Sets the _views property for the default route in this admin page group.
797
+	 */
798
+	protected function _set_list_table_views_default()
799
+	{
800
+		$this->_views = [
801
+			'all'   => [
802
+				'slug'        => 'all',
803
+				'label'       => esc_html__('View All Events', 'event_espresso'),
804
+				'count'       => 0,
805
+				'bulk_action' => [
806
+					'trash_events' => esc_html__('Move to Trash', 'event_espresso'),
807
+				],
808
+			],
809
+			'draft' => [
810
+				'slug'        => 'draft',
811
+				'label'       => esc_html__('Draft', 'event_espresso'),
812
+				'count'       => 0,
813
+				'bulk_action' => [
814
+					'trash_events' => esc_html__('Move to Trash', 'event_espresso'),
815
+				],
816
+			],
817
+		];
818
+		if (EE_Registry::instance()->CAP->current_user_can('ee_delete_events', 'espresso_events_trash_events')) {
819
+			$this->_views['trash'] = [
820
+				'slug'        => 'trash',
821
+				'label'       => esc_html__('Trash', 'event_espresso'),
822
+				'count'       => 0,
823
+				'bulk_action' => [
824
+					'restore_events' => esc_html__('Restore From Trash', 'event_espresso'),
825
+					'delete_events'  => esc_html__('Delete Permanently', 'event_espresso'),
826
+				],
827
+			];
828
+		}
829
+	}
830
+
831
+
832
+	/**
833
+	 * Provides the legend item array for the default list table view.
834
+	 *
835
+	 * @return array
836
+	 * @throws EE_Error
837
+	 * @throws EE_Error
838
+	 */
839
+	protected function _event_legend_items()
840
+	{
841
+		$items    = [
842
+			'view_details'   => [
843
+				'class' => 'dashicons dashicons-search',
844
+				'desc'  => esc_html__('View Event', 'event_espresso'),
845
+			],
846
+			'edit_event'     => [
847
+				'class' => 'ee-icon ee-icon-calendar-edit',
848
+				'desc'  => esc_html__('Edit Event Details', 'event_espresso'),
849
+			],
850
+			'view_attendees' => [
851
+				'class' => 'dashicons dashicons-groups',
852
+				'desc'  => esc_html__('View Registrations for Event', 'event_espresso'),
853
+			],
854
+		];
855
+		$items    = apply_filters('FHEE__Events_Admin_Page___event_legend_items__items', $items);
856
+		$statuses = [
857
+			'sold_out_status'  => [
858
+				'class' => 'ee-status-legend ee-status-legend-' . EE_Datetime::sold_out,
859
+				'desc'  => EEH_Template::pretty_status(EE_Datetime::sold_out, false, 'sentence'),
860
+			],
861
+			'active_status'    => [
862
+				'class' => 'ee-status-legend ee-status-legend-' . EE_Datetime::active,
863
+				'desc'  => EEH_Template::pretty_status(EE_Datetime::active, false, 'sentence'),
864
+			],
865
+			'upcoming_status'  => [
866
+				'class' => 'ee-status-legend ee-status-legend-' . EE_Datetime::upcoming,
867
+				'desc'  => EEH_Template::pretty_status(EE_Datetime::upcoming, false, 'sentence'),
868
+			],
869
+			'postponed_status' => [
870
+				'class' => 'ee-status-legend ee-status-legend-' . EE_Datetime::postponed,
871
+				'desc'  => EEH_Template::pretty_status(EE_Datetime::postponed, false, 'sentence'),
872
+			],
873
+			'cancelled_status' => [
874
+				'class' => 'ee-status-legend ee-status-legend-' . EE_Datetime::cancelled,
875
+				'desc'  => EEH_Template::pretty_status(EE_Datetime::cancelled, false, 'sentence'),
876
+			],
877
+			'expired_status'   => [
878
+				'class' => 'ee-status-legend ee-status-legend-' . EE_Datetime::expired,
879
+				'desc'  => EEH_Template::pretty_status(EE_Datetime::expired, false, 'sentence'),
880
+			],
881
+			'inactive_status'  => [
882
+				'class' => 'ee-status-legend ee-status-legend-' . EE_Datetime::inactive,
883
+				'desc'  => EEH_Template::pretty_status(EE_Datetime::inactive, false, 'sentence'),
884
+			],
885
+		];
886
+		$statuses = apply_filters('FHEE__Events_Admin_Page__event_legend_items__statuses', $statuses);
887
+		return array_merge($items, $statuses);
888
+	}
889
+
890
+
891
+	/**
892
+	 * @return EEM_Event
893
+	 * @throws EE_Error
894
+	 * @throws ReflectionException
895
+	 */
896
+	private function _event_model()
897
+	{
898
+		if (! $this->_event_model instanceof EEM_Event) {
899
+			$this->_event_model = EE_Registry::instance()->load_model('Event');
900
+		}
901
+		return $this->_event_model;
902
+	}
903
+
904
+
905
+	/**
906
+	 * Adds extra buttons to the WP CPT permalink field row.
907
+	 * Method is called from parent and is hooked into the wp 'get_sample_permalink_html' filter.
908
+	 *
909
+	 * @param string $return    the current html
910
+	 * @param int    $id        the post id for the page
911
+	 * @param string $new_title What the title is
912
+	 * @param string $new_slug  what the slug is
913
+	 * @return string            The new html string for the permalink area
914
+	 */
915
+	public function extra_permalink_field_buttons($return, $id, $new_title, $new_slug)
916
+	{
917
+		// make sure this is only when editing
918
+		if (! empty($id)) {
919
+			$post   = get_post($id);
920
+			$return .= '<a class="button button-small" onclick="prompt(\'Shortcode:\', jQuery(\'#shortcode\').val()); return false;" href="#"  tabindex="-1">'
921
+					   . esc_html__('Shortcode', 'event_espresso')
922
+					   . '</a> ';
923
+			$return .= '<input id="shortcode" type="hidden" value="[ESPRESSO_TICKET_SELECTOR event_id='
924
+					   . $post->ID
925
+					   . ']">';
926
+		}
927
+		return $return;
928
+	}
929
+
930
+
931
+	/**
932
+	 * _events_overview_list_table
933
+	 * This contains the logic for showing the events_overview list
934
+	 *
935
+	 * @access protected
936
+	 * @return void
937
+	 * @throws EE_Error
938
+	 */
939
+	protected function _events_overview_list_table()
940
+	{
941
+		do_action('AHEE_log', __FILE__, __FUNCTION__, '');
942
+		$this->_template_args['after_list_table']                           =
943
+			! empty($this->_template_args['after_list_table'])
944
+				? (array) $this->_template_args['after_list_table']
945
+				: [];
946
+		$this->_template_args['after_list_table']['view_event_list_button'] = EEH_HTML::br()
947
+			. EEH_Template::get_button_or_link(
948
+				get_post_type_archive_link('espresso_events'),
949
+				esc_html__("View Event Archive Page", "event_espresso"),
950
+				'button'
951
+			);
952
+		$this->_template_args['after_list_table']['legend']                 = $this->_display_legend(
953
+			$this->_event_legend_items()
954
+		);
955
+		$this->_admin_page_title                                            .= ' ' . $this->get_action_link_or_button(
956
+			'create_new',
957
+			'add',
958
+			[],
959
+			'add-new-h2'
960
+		);
961
+		$this->display_admin_list_table_page_with_no_sidebar();
962
+	}
963
+
964
+
965
+	/**
966
+	 * this allows for extra misc actions in the default WP publish box
967
+	 *
968
+	 * @return void
969
+	 * @throws EE_Error
970
+	 * @throws ReflectionException
971
+	 */
972
+	public function extra_misc_actions_publish_box()
973
+	{
974
+		$this->_generate_publish_box_extra_content();
975
+	}
976
+
977
+
978
+	/**
979
+	 * This is hooked into the WordPress do_action('save_post') hook and runs after the custom post type has been
980
+	 * saved.
981
+	 * Typically you would use this to save any additional data.
982
+	 * Keep in mind also that "save_post" runs on EVERY post update to the database.
983
+	 * ALSO very important.  When a post transitions from scheduled to published,
984
+	 * the save_post action is fired but you will NOT have any _POST data containing any extra info you may have from
985
+	 * other meta saves. So MAKE sure that you handle this accordingly.
986
+	 *
987
+	 * @access protected
988
+	 * @abstract
989
+	 * @param string $post_id The ID of the cpt that was saved (so you can link relationally)
990
+	 * @param WP_Post $post    The post object of the cpt that was saved.
991
+	 * @return void
992
+	 * @throws EE_Error
993
+	 * @throws ReflectionException
994
+	 */
995
+	protected function _insert_update_cpt_item($post_id, $post)
996
+	{
997
+		if ($post instanceof WP_Post && $post->post_type !== 'espresso_events') {
998
+			// get out we're not processing an event save.
999
+			return;
1000
+		}
1001
+
1002
+		$event_values = [
1003
+			'EVT_display_desc'                => $this->request->getRequestParam('display_desc', false, 'bool'),
1004
+			'EVT_display_ticket_selector'     => $this->request->getRequestParam(
1005
+				'display_ticket_selector',
1006
+				false,
1007
+				'bool'
1008
+			),
1009
+			'EVT_additional_limit'            => min(
1010
+				apply_filters('FHEE__EE_Events_Admin__insert_update_cpt_item__EVT_additional_limit_max', 255),
1011
+				$this->request->getRequestParam('additional_limit', null, 'int')
1012
+			),
1013
+			'EVT_default_registration_status' => $this->request->getRequestParam(
1014
+				'EVT_default_registration_status',
1015
+				EE_Registry::instance()->CFG->registration->default_STS_ID
1016
+			),
1017
+
1018
+			'EVT_member_only'     => $this->request->getRequestParam('member_only', false, 'bool'),
1019
+			'EVT_allow_overflow'  => $this->request->getRequestParam('EVT_allow_overflow', false, 'bool'),
1020
+			'EVT_timezone_string' => $this->request->getRequestParam('timezone_string'),
1021
+			'EVT_external_URL'    => $this->request->getRequestParam('externalURL'),
1022
+			'EVT_phone'           => $this->request->getRequestParam('event_phone'),
1023
+		];
1024
+		// update event
1025
+		$success = $this->_event_model()->update_by_ID($event_values, $post_id);
1026
+		// get event_object for other metaboxes...
1027
+		// though it would seem to make sense to just use $this->_event_model()->get_one_by_ID( $post_id )..
1028
+		// i have to setup where conditions to override the filters in the model
1029
+		// that filter out autodraft and inherit statuses so we GET the inherit id!
1030
+		$event = $this->_event_model()->get_one(
1031
+			[
1032
+				[
1033
+					$this->_event_model()->primary_key_name() => $post_id,
1034
+					'OR'                                      => [
1035
+						'status'   => $post->post_status,
1036
+						// if trying to "Publish" a sold out event, it's status will get switched back to "sold_out" in the db,
1037
+						// but the returned object here has a status of "publish", so use the original post status as well
1038
+						'status*1' => $this->request->getRequestParam('original_post_status'),
1039
+					],
1040
+				],
1041
+			]
1042
+		);
1043
+		// the following are default callbacks for event attachment updates that can be overridden by caffeinated functionality and/or addons.
1044
+		$event_update_callbacks = apply_filters(
1045
+			'FHEE__Events_Admin_Page___insert_update_cpt_item__event_update_callbacks',
1046
+			[
1047
+				[$this, '_default_venue_update'],
1048
+				[$this, '_default_tickets_update'],
1049
+			]
1050
+		);
1051
+		$att_success            = true;
1052
+		foreach ($event_update_callbacks as $e_callback) {
1053
+			$_success = is_callable($e_callback)
1054
+				? call_user_func($e_callback, $event, $this->request->requestParams())
1055
+				: false;
1056
+			// if ANY of these updates fail then we want the appropriate global error message
1057
+			$att_success = ! $att_success ? $att_success : $_success;
1058
+		}
1059
+		// any errors?
1060
+		if ($success && false === $att_success) {
1061
+			EE_Error::add_error(
1062
+				esc_html__(
1063
+					'Event Details saved successfully but something went wrong with saving attachments.',
1064
+					'event_espresso'
1065
+				),
1066
+				__FILE__,
1067
+				__FUNCTION__,
1068
+				__LINE__
1069
+			);
1070
+		} elseif ($success === false) {
1071
+			EE_Error::add_error(
1072
+				esc_html__('Event Details did not save successfully.', 'event_espresso'),
1073
+				__FILE__,
1074
+				__FUNCTION__,
1075
+				__LINE__
1076
+			);
1077
+		}
1078
+	}
1079
+
1080
+
1081
+	/**
1082
+	 * @param int $post_id
1083
+	 * @param int $revision_id
1084
+	 * @throws EE_Error
1085
+	 * @throws EE_Error
1086
+	 * @throws ReflectionException
1087
+	 * @see parent::restore_item()
1088
+	 */
1089
+	protected function _restore_cpt_item($post_id, $revision_id)
1090
+	{
1091
+		// copy existing event meta to new post
1092
+		$post_evt = $this->_event_model()->get_one_by_ID($post_id);
1093
+		if ($post_evt instanceof EE_Event) {
1094
+			// meta revision restore
1095
+			$post_evt->restore_revision($revision_id);
1096
+			// related objs restore
1097
+			$post_evt->restore_revision($revision_id, ['Venue', 'Datetime', 'Price']);
1098
+		}
1099
+	}
1100
+
1101
+
1102
+	/**
1103
+	 * Attach the venue to the Event
1104
+	 *
1105
+	 * @param EE_Event $event Event Object to add the venue to
1106
+	 * @param array    $data  The request data from the form
1107
+	 * @return bool           Success or fail.
1108
+	 * @throws EE_Error
1109
+	 * @throws ReflectionException
1110
+	 */
1111
+	protected function _default_venue_update(EE_Event $event, $data)
1112
+	{
1113
+		require_once(EE_MODELS . 'EEM_Venue.model.php');
1114
+		$venue_model = EE_Registry::instance()->load_model('Venue');
1115
+		$venue_id    = ! empty($data['venue_id']) ? $data['venue_id'] : null;
1116
+		// very important.  If we don't have a venue name...
1117
+		// then we'll get out because not necessary to create empty venue
1118
+		if (empty($data['venue_title'])) {
1119
+			return false;
1120
+		}
1121
+		$venue_array = [
1122
+			'VNU_wp_user'         => $event->get('EVT_wp_user'),
1123
+			'VNU_name'            => $data['venue_title'],
1124
+			'VNU_desc'            => ! empty($data['venue_description']) ? $data['venue_description'] : null,
1125
+			'VNU_identifier'      => ! empty($data['venue_identifier']) ? $data['venue_identifier'] : null,
1126
+			'VNU_short_desc'      => ! empty($data['venue_short_description'])
1127
+				? $data['venue_short_description']
1128
+				: null,
1129
+			'VNU_address'         => ! empty($data['address']) ? $data['address'] : null,
1130
+			'VNU_address2'        => ! empty($data['address2']) ? $data['address2'] : null,
1131
+			'VNU_city'            => ! empty($data['city']) ? $data['city'] : null,
1132
+			'STA_ID'              => ! empty($data['state']) ? $data['state'] : null,
1133
+			'CNT_ISO'             => ! empty($data['countries']) ? $data['countries'] : null,
1134
+			'VNU_zip'             => ! empty($data['zip']) ? $data['zip'] : null,
1135
+			'VNU_phone'           => ! empty($data['venue_phone']) ? $data['venue_phone'] : null,
1136
+			'VNU_capacity'        => ! empty($data['venue_capacity']) ? $data['venue_capacity'] : null,
1137
+			'VNU_url'             => ! empty($data['venue_url']) ? $data['venue_url'] : null,
1138
+			'VNU_virtual_phone'   => ! empty($data['virtual_phone']) ? $data['virtual_phone'] : null,
1139
+			'VNU_virtual_url'     => ! empty($data['virtual_url']) ? $data['virtual_url'] : null,
1140
+			'VNU_enable_for_gmap' => isset($data['enable_for_gmap']) ? 1 : 0,
1141
+			'status'              => 'publish',
1142
+		];
1143
+		// if we've got the venue_id then we're just updating the existing venue so let's do that and then get out.
1144
+		if (! empty($venue_id)) {
1145
+			$update_where  = [$venue_model->primary_key_name() => $venue_id];
1146
+			$rows_affected = $venue_model->update($venue_array, [$update_where]);
1147
+			// we've gotta make sure that the venue is always attached to a revision.. add_relation_to should take care of making sure that the relation is already present.
1148
+			$event->_add_relation_to($venue_id, 'Venue');
1149
+			return $rows_affected > 0;
1150
+		}
1151
+		// we insert the venue
1152
+		$venue_id = $venue_model->insert($venue_array);
1153
+		$event->_add_relation_to($venue_id, 'Venue');
1154
+		return ! empty($venue_id);
1155
+		// when we have the ancestor come in it's already been handled by the revision save.
1156
+	}
1157
+
1158
+
1159
+	/**
1160
+	 * Handles saving everything related to Tickets (datetimes, tickets, prices)
1161
+	 *
1162
+	 * @param EE_Event $event The Event object we're attaching data to
1163
+	 * @param array    $data  The request data from the form
1164
+	 * @return array
1165
+	 * @throws EE_Error
1166
+	 * @throws ReflectionException
1167
+	 * @throws Exception
1168
+	 */
1169
+	protected function _default_tickets_update(EE_Event $event, $data)
1170
+	{
1171
+		$datetime       = null;
1172
+		$saved_tickets  = [];
1173
+		$event_timezone = $event->get_timezone();
1174
+		$date_formats   = ['Y-m-d', 'h:i a'];
1175
+		foreach ($data['edit_event_datetimes'] as $row => $datetime_data) {
1176
+			// trim all values to ensure any excess whitespace is removed.
1177
+			$datetime_data                = array_map('trim', $datetime_data);
1178
+			$datetime_data['DTT_EVT_end'] =
1179
+				isset($datetime_data['DTT_EVT_end']) && ! empty($datetime_data['DTT_EVT_end'])
1180
+					? $datetime_data['DTT_EVT_end']
1181
+					: $datetime_data['DTT_EVT_start'];
1182
+			$datetime_values              = [
1183
+				'DTT_ID'        => ! empty($datetime_data['DTT_ID']) ? $datetime_data['DTT_ID'] : null,
1184
+				'DTT_EVT_start' => $datetime_data['DTT_EVT_start'],
1185
+				'DTT_EVT_end'   => $datetime_data['DTT_EVT_end'],
1186
+				'DTT_reg_limit' => empty($datetime_data['DTT_reg_limit']) ? EE_INF : $datetime_data['DTT_reg_limit'],
1187
+				'DTT_order'     => $row,
1188
+			];
1189
+			// if we have an id then let's get existing object first and then set the new values.
1190
+			//  Otherwise we instantiate a new object for save.
1191
+			if (! empty($datetime_data['DTT_ID'])) {
1192
+				$datetime = EEM_Datetime::instance($event_timezone)->get_one_by_ID($datetime_data['DTT_ID']);
1193
+				if (! $datetime instanceof EE_Datetime) {
1194
+					throw new RuntimeException(
1195
+						sprintf(
1196
+							esc_html__(
1197
+								'Something went wrong! A valid Datetime could not be retrieved from the database using the supplied ID: %1$d',
1198
+								'event_espresso'
1199
+							),
1200
+							$datetime_data['DTT_ID']
1201
+						)
1202
+					);
1203
+				}
1204
+				$datetime->set_date_format($date_formats[0]);
1205
+				$datetime->set_time_format($date_formats[1]);
1206
+				foreach ($datetime_values as $field => $value) {
1207
+					$datetime->set($field, $value);
1208
+				}
1209
+			} else {
1210
+				$datetime = EE_Datetime::new_instance($datetime_values, $event_timezone, $date_formats);
1211
+			}
1212
+			if (! $datetime instanceof EE_Datetime) {
1213
+				throw new RuntimeException(
1214
+					sprintf(
1215
+						esc_html__(
1216
+							'Something went wrong! A valid Datetime could not be generated or retrieved using the supplied data: %1$s',
1217
+							'event_espresso'
1218
+						),
1219
+						print_r($datetime_values, true)
1220
+					)
1221
+				);
1222
+			}
1223
+			// before going any further make sure our dates are setup correctly
1224
+			// so that the end date is always equal or greater than the start date.
1225
+			if ($datetime->get_raw('DTT_EVT_start') > $datetime->get_raw('DTT_EVT_end')) {
1226
+				$datetime->set('DTT_EVT_end', $datetime->get('DTT_EVT_start'));
1227
+				$datetime = EEH_DTT_Helper::date_time_add($datetime, 'DTT_EVT_end', 'days');
1228
+			}
1229
+			$datetime->save();
1230
+			$event->_add_relation_to($datetime, 'Datetime');
1231
+		}
1232
+		// no datetimes get deleted so we don't do any of that logic here.
1233
+		// update tickets next
1234
+		$old_tickets = isset($data['ticket_IDs']) ? explode(',', $data['ticket_IDs']) : [];
1235
+
1236
+		// set up some default start and end dates in case those are not present in the incoming data
1237
+		$default_start_date = new DateTime('now', new DateTimeZone($event->get_timezone()));
1238
+		$default_start_date = $default_start_date->format($date_formats[0] . ' ' . $date_formats[1]);
1239
+		// use the start date of the first datetime for the end date
1240
+		$first_datetime   = $event->first_datetime();
1241
+		$default_end_date = $first_datetime->start_date_and_time($date_formats[0], $date_formats[1]);
1242
+
1243
+		// now process the incoming data
1244
+		foreach ($data['edit_tickets'] as $row => $ticket_data) {
1245
+			$update_prices = false;
1246
+			$ticket_price  = isset($data['edit_prices'][ $row ][1]['PRC_amount'])
1247
+				? $data['edit_prices'][ $row ][1]['PRC_amount']
1248
+				: 0;
1249
+			// trim inputs to ensure any excess whitespace is removed.
1250
+			$ticket_data   = array_map('trim', $ticket_data);
1251
+			$ticket_values = [
1252
+				'TKT_ID'          => ! empty($ticket_data['TKT_ID']) ? $ticket_data['TKT_ID'] : null,
1253
+				'TTM_ID'          => ! empty($ticket_data['TTM_ID']) ? $ticket_data['TTM_ID'] : 0,
1254
+				'TKT_name'        => ! empty($ticket_data['TKT_name']) ? $ticket_data['TKT_name'] : '',
1255
+				'TKT_description' => ! empty($ticket_data['TKT_description']) ? $ticket_data['TKT_description'] : '',
1256
+				'TKT_start_date'  => ! empty($ticket_data['TKT_start_date'])
1257
+					? $ticket_data['TKT_start_date']
1258
+					: $default_start_date,
1259
+				'TKT_end_date'    => ! empty($ticket_data['TKT_end_date'])
1260
+					? $ticket_data['TKT_end_date']
1261
+					: $default_end_date,
1262
+				'TKT_qty'         => ! empty($ticket_data['TKT_qty'])
1263
+									 || (isset($ticket_data['TKT_qty']) && (int) $ticket_data['TKT_qty'] === 0)
1264
+					? $ticket_data['TKT_qty']
1265
+					: EE_INF,
1266
+				'TKT_uses'        => ! empty($ticket_data['TKT_uses'])
1267
+									 || (isset($ticket_data['TKT_uses']) && (int) $ticket_data['TKT_uses'] === 0)
1268
+					? $ticket_data['TKT_uses']
1269
+					: EE_INF,
1270
+				'TKT_min'         => ! empty($ticket_data['TKT_min']) ? $ticket_data['TKT_min'] : 0,
1271
+				'TKT_max'         => ! empty($ticket_data['TKT_max']) ? $ticket_data['TKT_max'] : EE_INF,
1272
+				'TKT_order'       => isset($ticket_data['TKT_order']) ? $ticket_data['TKT_order'] : $row,
1273
+				'TKT_price'       => $ticket_price,
1274
+				'TKT_row'         => $row,
1275
+			];
1276
+			// if this is a default ticket, then we need to set the TKT_ID to 0 and update accordingly,
1277
+			// which means in turn that the prices will become new prices as well.
1278
+			if (isset($ticket_data['TKT_is_default']) && $ticket_data['TKT_is_default']) {
1279
+				$ticket_values['TKT_ID']         = 0;
1280
+				$ticket_values['TKT_is_default'] = 0;
1281
+				$update_prices                   = true;
1282
+			}
1283
+			// if we have a TKT_ID then we need to get that existing TKT_obj and update it
1284
+			// we actually do our saves ahead of adding any relations because its entirely possible that this
1285
+			// ticket didn't get removed or added to any datetime in the session but DID have it's items modified.
1286
+			// keep in mind that if the ticket has been sold (and we have changed pricing information),
1287
+			// then we won't be updating the tkt but instead a new tkt will be created and the old one archived.
1288
+			if (! empty($ticket_data['TKT_ID'])) {
1289
+				$existing_ticket = EEM_Ticket::instance($event_timezone)->get_one_by_ID($ticket_data['TKT_ID']);
1290
+				if (! $existing_ticket instanceof EE_Ticket) {
1291
+					throw new RuntimeException(
1292
+						sprintf(
1293
+							esc_html__(
1294
+								'Something went wrong! A valid Ticket could not be retrieved from the database using the supplied ID: %1$d',
1295
+								'event_espresso'
1296
+							),
1297
+							$ticket_data['TKT_ID']
1298
+						)
1299
+					);
1300
+				}
1301
+				$ticket_sold = $existing_ticket->count_related(
1302
+					'Registration',
1303
+					[
1304
+						[
1305
+							'STS_ID' => [
1306
+								'NOT IN',
1307
+								[EEM_Registration::status_id_incomplete],
1308
+							],
1309
+						],
1310
+					]
1311
+				) > 0;
1312
+				// let's just check the total price for the existing ticket and determine if it matches the new total price.
1313
+				// if they are different then we create a new ticket (if $ticket_sold)
1314
+				// if they aren't different then we go ahead and modify existing ticket.
1315
+				$create_new_ticket = $ticket_sold
1316
+									 && $ticket_price !== $existing_ticket->price()
1317
+									 && ! $existing_ticket->deleted();
1318
+				$existing_ticket->set_date_format($date_formats[0]);
1319
+				$existing_ticket->set_time_format($date_formats[1]);
1320
+				// set new values
1321
+				foreach ($ticket_values as $field => $value) {
1322
+					if ($field == 'TKT_qty') {
1323
+						$existing_ticket->set_qty($value);
1324
+					} elseif ($field == 'TKT_price') {
1325
+						$existing_ticket->set('TKT_price', $ticket_price);
1326
+					} else {
1327
+						$existing_ticket->set($field, $value);
1328
+					}
1329
+				}
1330
+				$ticket = $existing_ticket;
1331
+				// if $create_new_ticket is false then we can safely update the existing ticket.
1332
+				//  Otherwise we have to create a new ticket.
1333
+				if ($create_new_ticket) {
1334
+					// archive the old ticket first
1335
+					$existing_ticket->set('TKT_deleted', 1);
1336
+					$existing_ticket->save();
1337
+					// make sure this ticket is still recorded in our $saved_tickets
1338
+					// so we don't run it through the regular trash routine.
1339
+					$saved_tickets[ $existing_ticket->ID() ] = $existing_ticket;
1340
+					// create new ticket that's a copy of the existing except,
1341
+					// (a new id of course and not archived) AND has the new TKT_price associated with it.
1342
+					$new_ticket = clone $existing_ticket;
1343
+					$new_ticket->set('TKT_ID', 0);
1344
+					$new_ticket->set('TKT_deleted', 0);
1345
+					$new_ticket->set('TKT_sold', 0);
1346
+					// now we need to make sure that $new prices are created as well and attached to new ticket.
1347
+					$update_prices = true;
1348
+					$ticket        = $new_ticket;
1349
+				}
1350
+			} else {
1351
+				// no TKT_id so a new ticket
1352
+				$ticket_values['TKT_price'] = $ticket_price;
1353
+				$ticket                     = EE_Ticket::new_instance($ticket_values, $event_timezone, $date_formats);
1354
+				$update_prices              = true;
1355
+			}
1356
+			if (! $ticket instanceof EE_Ticket) {
1357
+				throw new RuntimeException(
1358
+					sprintf(
1359
+						esc_html__(
1360
+							'Something went wrong! A valid Ticket could not be generated or retrieved using the supplied data: %1$s',
1361
+							'event_espresso'
1362
+						),
1363
+						print_r($ticket_values, true)
1364
+					)
1365
+				);
1366
+			}
1367
+			// cap ticket qty by datetime reg limits
1368
+			$ticket->set_qty(min($ticket->qty(), $ticket->qty('reg_limit')));
1369
+			// update ticket.
1370
+			$ticket->save();
1371
+			// before going any further make sure our dates are setup correctly
1372
+			// so that the end date is always equal or greater than the start date.
1373
+			if ($ticket->get_raw('TKT_start_date') > $ticket->get_raw('TKT_end_date')) {
1374
+				$ticket->set('TKT_end_date', $ticket->get('TKT_start_date'));
1375
+				$ticket = EEH_DTT_Helper::date_time_add($ticket, 'TKT_end_date', 'days');
1376
+				$ticket->save();
1377
+			}
1378
+			// initially let's add the ticket to the datetime
1379
+			$datetime->_add_relation_to($ticket, 'Ticket');
1380
+			$saved_tickets[ $ticket->ID() ] = $ticket;
1381
+			// add prices to ticket
1382
+			$prices_data = isset($data['edit_prices'][ $row ]) && is_array($data['edit_prices'][ $row ])
1383
+				? $data['edit_prices'][ $row ]
1384
+				: [];
1385
+			$this->_add_prices_to_ticket($prices_data, $ticket, $update_prices);
1386
+		}
1387
+		// however now we need to handle permanently deleting tickets via the ui.
1388
+		// Keep in mind that the ui does not allow deleting/archiving tickets that have ticket sold.
1389
+		// However, it does allow for deleting tickets that have no tickets sold,
1390
+		// in which case we want to get rid of permanently because there is no need to save in db.
1391
+		$old_tickets     = isset($old_tickets[0]) && $old_tickets[0] == '' ? [] : $old_tickets;
1392
+		$tickets_removed = array_diff($old_tickets, array_keys($saved_tickets));
1393
+		foreach ($tickets_removed as $id) {
1394
+			$id = absint($id);
1395
+			// get the ticket for this id
1396
+			$ticket_to_remove = EEM_Ticket::instance()->get_one_by_ID($id);
1397
+			if (! $ticket_to_remove instanceof EE_Ticket) {
1398
+				continue;
1399
+			}
1400
+			// need to get all the related datetimes on this ticket and remove from every single one of them
1401
+			// (remember this process can ONLY kick off if there are NO tickets sold)
1402
+			$related_datetimes = $ticket_to_remove->get_many_related('Datetime');
1403
+			foreach ($related_datetimes as $related_datetime) {
1404
+				$ticket_to_remove->_remove_relation_to($related_datetime, 'Datetime');
1405
+			}
1406
+			// need to do the same for prices (except these prices can also be deleted because again,
1407
+			// tickets can only be trashed if they don't have any TKTs sold (otherwise they are just archived))
1408
+			$ticket_to_remove->delete_related_permanently('Price');
1409
+			// finally let's delete this ticket
1410
+			// (which should not be blocked at this point b/c we've removed all our relationships)
1411
+			$ticket_to_remove->delete_permanently();
1412
+		}
1413
+		return [$datetime, $saved_tickets];
1414
+	}
1415
+
1416
+
1417
+	/**
1418
+	 * This attaches a list of given prices to a ticket.
1419
+	 * Note we dont' have to worry about ever removing relationships (or archiving prices) because if there is a change
1420
+	 * in price information on a ticket, a new ticket is created anyways so the archived ticket will retain the old
1421
+	 * price info and prices are automatically "archived" via the ticket.
1422
+	 *
1423
+	 * @access  private
1424
+	 * @param array     $prices_data Array of prices from the form.
1425
+	 * @param EE_Ticket $ticket      EE_Ticket object that prices are being attached to.
1426
+	 * @param bool      $new_prices  Whether attach existing incoming prices or create new ones.
1427
+	 * @return  void
1428
+	 * @throws EE_Error
1429
+	 * @throws ReflectionException
1430
+	 */
1431
+	private function _add_prices_to_ticket($prices_data, EE_Ticket $ticket, $new_prices = false)
1432
+	{
1433
+		$timezone = $ticket->get_timezone();
1434
+		foreach ($prices_data as $row => $price_data) {
1435
+			$price_values = [
1436
+				'PRC_ID'         => ! empty($price_data['PRC_ID']) ? $price_data['PRC_ID'] : null,
1437
+				'PRT_ID'         => ! empty($price_data['PRT_ID']) ? $price_data['PRT_ID'] : null,
1438
+				'PRC_amount'     => ! empty($price_data['PRC_amount']) ? $price_data['PRC_amount'] : 0,
1439
+				'PRC_name'       => ! empty($price_data['PRC_name']) ? $price_data['PRC_name'] : '',
1440
+				'PRC_desc'       => ! empty($price_data['PRC_desc']) ? $price_data['PRC_desc'] : '',
1441
+				'PRC_is_default' => 0, // make sure prices are NOT set as default from this context
1442
+				'PRC_order'      => $row,
1443
+			];
1444
+			if ($new_prices || empty($price_values['PRC_ID'])) {
1445
+				$price_values['PRC_ID'] = 0;
1446
+				$price                  = EE_Price::new_instance($price_values, $timezone);
1447
+			} else {
1448
+				$price = EEM_Price::instance($timezone)->get_one_by_ID($price_data['PRC_ID']);
1449
+				// update this price with new values
1450
+				foreach ($price_values as $field => $new_price) {
1451
+					$price->set($field, $new_price);
1452
+				}
1453
+			}
1454
+			if (! $price instanceof EE_Price) {
1455
+				throw new RuntimeException(
1456
+					sprintf(
1457
+						esc_html__(
1458
+							'Something went wrong! A valid Price could not be generated or retrieved using the supplied data: %1$s',
1459
+							'event_espresso'
1460
+						),
1461
+						print_r($price_values, true)
1462
+					)
1463
+				);
1464
+			}
1465
+			$price->save();
1466
+			$ticket->_add_relation_to($price, 'Price');
1467
+		}
1468
+	}
1469
+
1470
+
1471
+	/**
1472
+	 * Add in our autosave ajax handlers
1473
+	 *
1474
+	 */
1475
+	protected function _ee_autosave_create_new()
1476
+	{
1477
+	}
1478
+
1479
+
1480
+	/**
1481
+	 * More autosave handlers.
1482
+	 */
1483
+	protected function _ee_autosave_edit()
1484
+	{
1485
+		// TEMPORARILY EXITING CAUSE THIS IS A TODO
1486
+	}
1487
+
1488
+
1489
+	/**
1490
+	 * @throws EE_Error
1491
+	 * @throws ReflectionException
1492
+	 */
1493
+	private function _generate_publish_box_extra_content()
1494
+	{
1495
+		// load formatter helper
1496
+		// args for getting related registrations
1497
+		$approved_query_args        = [
1498
+			[
1499
+				'REG_deleted' => 0,
1500
+				'STS_ID'      => EEM_Registration::status_id_approved,
1501
+			],
1502
+		];
1503
+		$not_approved_query_args    = [
1504
+			[
1505
+				'REG_deleted' => 0,
1506
+				'STS_ID'      => EEM_Registration::status_id_not_approved,
1507
+			],
1508
+		];
1509
+		$pending_payment_query_args = [
1510
+			[
1511
+				'REG_deleted' => 0,
1512
+				'STS_ID'      => EEM_Registration::status_id_pending_payment,
1513
+			],
1514
+		];
1515
+		// publish box
1516
+		$publish_box_extra_args = [
1517
+			'view_approved_reg_url'        => add_query_arg(
1518
+				[
1519
+					'action'      => 'default',
1520
+					'event_id'    => $this->_cpt_model_obj->ID(),
1521
+					'_reg_status' => EEM_Registration::status_id_approved,
1522
+					'use_filters' => true,
1523
+				],
1524
+				REG_ADMIN_URL
1525
+			),
1526
+			'view_not_approved_reg_url'    => add_query_arg(
1527
+				[
1528
+					'action'      => 'default',
1529
+					'event_id'    => $this->_cpt_model_obj->ID(),
1530
+					'_reg_status' => EEM_Registration::status_id_not_approved,
1531
+					'use_filters' => true,
1532
+				],
1533
+				REG_ADMIN_URL
1534
+			),
1535
+			'view_pending_payment_reg_url' => add_query_arg(
1536
+				[
1537
+					'action'      => 'default',
1538
+					'event_id'    => $this->_cpt_model_obj->ID(),
1539
+					'_reg_status' => EEM_Registration::status_id_pending_payment,
1540
+					'use_filters' => true,
1541
+				],
1542
+				REG_ADMIN_URL
1543
+			),
1544
+			'approved_regs'                => $this->_cpt_model_obj->count_related(
1545
+				'Registration',
1546
+				$approved_query_args
1547
+			),
1548
+			'not_approved_regs'            => $this->_cpt_model_obj->count_related(
1549
+				'Registration',
1550
+				$not_approved_query_args
1551
+			),
1552
+			'pending_payment_regs'         => $this->_cpt_model_obj->count_related(
1553
+				'Registration',
1554
+				$pending_payment_query_args
1555
+			),
1556
+			'misc_pub_section_class'       => apply_filters(
1557
+				'FHEE_Events_Admin_Page___generate_publish_box_extra_content__misc_pub_section_class',
1558
+				'misc-pub-section'
1559
+			),
1560
+		];
1561
+		ob_start();
1562
+		do_action(
1563
+			'AHEE__Events_Admin_Page___generate_publish_box_extra_content__event_editor_overview_add',
1564
+			$this->_cpt_model_obj
1565
+		);
1566
+		$publish_box_extra_args['event_editor_overview_add'] = ob_get_clean();
1567
+		// load template
1568
+		EEH_Template::display_template(
1569
+			EVENTS_TEMPLATE_PATH . 'event_publish_box_extras.template.php',
1570
+			$publish_box_extra_args
1571
+		);
1572
+	}
1573
+
1574
+
1575
+	/**
1576
+	 * @return EE_Event
1577
+	 */
1578
+	public function get_event_object()
1579
+	{
1580
+		return $this->_cpt_model_obj;
1581
+	}
1582
+
1583
+
1584
+
1585
+
1586
+	/** METABOXES * */
1587
+	/**
1588
+	 * _register_event_editor_meta_boxes
1589
+	 * add all metaboxes related to the event_editor
1590
+	 *
1591
+	 * @return void
1592
+	 * @throws EE_Error
1593
+	 * @throws ReflectionException
1594
+	 */
1595
+	protected function _register_event_editor_meta_boxes()
1596
+	{
1597
+		$this->verify_cpt_object();
1598
+		add_meta_box(
1599
+			'espresso_event_editor_tickets',
1600
+			esc_html__('Event Datetime & Ticket', 'event_espresso'),
1601
+			[$this, 'ticket_metabox'],
1602
+			$this->page_slug,
1603
+			'normal',
1604
+			'high'
1605
+		);
1606
+		add_meta_box(
1607
+			'espresso_event_editor_event_options',
1608
+			esc_html__('Event Registration Options', 'event_espresso'),
1609
+			[$this, 'registration_options_meta_box'],
1610
+			$this->page_slug,
1611
+			'side'
1612
+		);
1613
+		// NOTE: if you're looking for other metaboxes in here,
1614
+		// where a metabox has a related management page in the admin
1615
+		// you will find it setup in the related management page's "_Hooks" file.
1616
+		// i.e. messages metabox is found in "espresso_events_Messages_Hooks.class.php".
1617
+	}
1618
+
1619
+
1620
+	/**
1621
+	 * @throws DomainException
1622
+	 * @throws EE_Error
1623
+	 * @throws ReflectionException
1624
+	 */
1625
+	public function ticket_metabox()
1626
+	{
1627
+		$existing_datetime_ids = $existing_ticket_ids = [];
1628
+		// defaults for template args
1629
+		$template_args = [
1630
+			'existing_datetime_ids'    => '',
1631
+			'event_datetime_help_link' => '',
1632
+			'ticket_options_help_link' => '',
1633
+			'time'                     => null,
1634
+			'ticket_rows'              => '',
1635
+			'existing_ticket_ids'      => '',
1636
+			'total_ticket_rows'        => 1,
1637
+			'ticket_js_structure'      => '',
1638
+			'trash_icon'               => 'ee-lock-icon',
1639
+			'disabled'                 => '',
1640
+		];
1641
+		$event_id      = is_object($this->_cpt_model_obj) ? $this->_cpt_model_obj->ID() : null;
1642
+		/**
1643
+		 * 1. Start with retrieving Datetimes
1644
+		 * 2. Fore each datetime get related tickets
1645
+		 * 3. For each ticket get related prices
1646
+		 */
1647
+		$times          = EEM_Datetime::instance()->get_all_event_dates($event_id);
1648
+		$first_datetime = reset($times);
1649
+		// do we get related tickets?
1650
+		if (
1651
+			$first_datetime instanceof EE_Datetime
1652
+			&& $first_datetime->ID() !== 0
1653
+		) {
1654
+			$existing_datetime_ids[] = $first_datetime->get('DTT_ID');
1655
+			$template_args['time']   = $first_datetime;
1656
+			$related_tickets         = $first_datetime->tickets(
1657
+				[
1658
+					['OR' => ['TKT_deleted' => 1, 'TKT_deleted*' => 0]],
1659
+					'default_where_conditions' => 'none',
1660
+				]
1661
+			);
1662
+			if (! empty($related_tickets)) {
1663
+				$template_args['total_ticket_rows'] = count($related_tickets);
1664
+				$row                                = 0;
1665
+				foreach ($related_tickets as $ticket) {
1666
+					$existing_ticket_ids[]        = $ticket->get('TKT_ID');
1667
+					$template_args['ticket_rows'] .= $this->_get_ticket_row($ticket, false, $row);
1668
+					$row++;
1669
+				}
1670
+			} else {
1671
+				$template_args['total_ticket_rows'] = 1;
1672
+				/** @type EE_Ticket $ticket */
1673
+				$ticket                       = EEM_Ticket::instance()->create_default_object();
1674
+				$template_args['ticket_rows'] .= $this->_get_ticket_row($ticket);
1675
+			}
1676
+		} else {
1677
+			$template_args['time']        = $times[0];
1678
+			$tickets                      = EEM_Ticket::instance()->get_all_default_tickets();
1679
+			$template_args['ticket_rows'] .= $this->_get_ticket_row($tickets[1]);
1680
+			// NOTE: we're just sending the first default row
1681
+			// (decaf can't manage default tickets so this should be sufficient);
1682
+		}
1683
+		$template_args['event_datetime_help_link'] = $this->_get_help_tab_link(
1684
+			'event_editor_event_datetimes_help_tab'
1685
+		);
1686
+		$template_args['ticket_options_help_link'] = $this->_get_help_tab_link('ticket_options_info');
1687
+		$template_args['existing_datetime_ids']    = implode(',', $existing_datetime_ids);
1688
+		$template_args['existing_ticket_ids']      = implode(',', $existing_ticket_ids);
1689
+		$template_args['ticket_js_structure']      = $this->_get_ticket_row(
1690
+			EEM_Ticket::instance()->create_default_object(),
1691
+			true
1692
+		);
1693
+		$template                                  = apply_filters(
1694
+			'FHEE__Events_Admin_Page__ticket_metabox__template',
1695
+			EVENTS_TEMPLATE_PATH . 'event_tickets_metabox_main.template.php'
1696
+		);
1697
+		EEH_Template::display_template($template, $template_args);
1698
+	}
1699
+
1700
+
1701
+	/**
1702
+	 * Setup an individual ticket form for the decaf event editor page
1703
+	 *
1704
+	 * @access private
1705
+	 * @param EE_Ticket $ticket   the ticket object
1706
+	 * @param boolean   $skeleton whether we're generating a skeleton for js manipulation
1707
+	 * @param int       $row
1708
+	 * @return string generated html for the ticket row.
1709
+	 * @throws EE_Error
1710
+	 * @throws ReflectionException
1711
+	 */
1712
+	private function _get_ticket_row($ticket, $skeleton = false, $row = 0)
1713
+	{
1714
+		$template_args = [
1715
+			'tkt_status_class'    => ' tkt-status-' . $ticket->ticket_status(),
1716
+			'tkt_archive_class'   => $ticket->ticket_status() === EE_Ticket::archived && ! $skeleton ? ' tkt-archived'
1717
+				: '',
1718
+			'ticketrow'           => $skeleton ? 'TICKETNUM' : $row,
1719
+			'TKT_ID'              => $ticket->get('TKT_ID'),
1720
+			'TKT_name'            => $ticket->get('TKT_name'),
1721
+			'TKT_start_date'      => $skeleton ? '' : $ticket->get_date('TKT_start_date', 'Y-m-d h:i a'),
1722
+			'TKT_end_date'        => $skeleton ? '' : $ticket->get_date('TKT_end_date', 'Y-m-d h:i a'),
1723
+			'TKT_is_default'      => $ticket->get('TKT_is_default'),
1724
+			'TKT_qty'             => $ticket->get_pretty('TKT_qty', 'input'),
1725
+			'edit_ticketrow_name' => $skeleton ? 'TICKETNAMEATTR' : 'edit_tickets',
1726
+			'TKT_sold'            => $skeleton ? 0 : $ticket->get('TKT_sold'),
1727
+			'trash_icon'          => ($skeleton || (! empty($ticket) && ! $ticket->get('TKT_deleted')))
1728
+									 && (! empty($ticket) && $ticket->get('TKT_sold') === 0)
1729
+				? 'trash-icon dashicons dashicons-post-trash clickable' : 'ee-lock-icon',
1730
+			'disabled'            => $skeleton || (! empty($ticket) && ! $ticket->get('TKT_deleted')) ? ''
1731
+				: ' disabled=disabled',
1732
+		];
1733
+		$price         = $ticket->ID() !== 0
1734
+			? $ticket->get_first_related('Price', ['default_where_conditions' => 'none'])
1735
+			: null;
1736
+		$price         = $price instanceof EE_Price
1737
+			? $price
1738
+			: EEM_Price::instance()->create_default_object();
1739
+		$price_args    = [
1740
+			'price_currency_symbol' => EE_Registry::instance()->CFG->currency->sign,
1741
+			'PRC_amount'            => $price->get('PRC_amount'),
1742
+			'PRT_ID'                => $price->get('PRT_ID'),
1743
+			'PRC_ID'                => $price->get('PRC_ID'),
1744
+			'PRC_is_default'        => $price->get('PRC_is_default'),
1745
+		];
1746
+		// make sure we have default start and end dates if skeleton
1747
+		// handle rows that should NOT be empty
1748
+		if (empty($template_args['TKT_start_date'])) {
1749
+			// if empty then the start date will be now.
1750
+			$template_args['TKT_start_date'] = date('Y-m-d h:i a', current_time('timestamp'));
1751
+		}
1752
+		if (empty($template_args['TKT_end_date'])) {
1753
+			// get the earliest datetime (if present);
1754
+			$earliest_datetime             = $this->_cpt_model_obj->ID() > 0
1755
+				? $this->_cpt_model_obj->get_first_related(
1756
+					'Datetime',
1757
+					['order_by' => ['DTT_EVT_start' => 'ASC']]
1758
+				)
1759
+				: null;
1760
+			$template_args['TKT_end_date'] = $earliest_datetime instanceof EE_Datetime
1761
+				? $earliest_datetime->get_datetime('DTT_EVT_start', 'Y-m-d', 'h:i a')
1762
+				: date('Y-m-d h:i a', mktime(0, 0, 0, date('m'), date('d') + 7, date('Y')));
1763
+		}
1764
+		$template_args = array_merge($template_args, $price_args);
1765
+		$template      = apply_filters(
1766
+			'FHEE__Events_Admin_Page__get_ticket_row__template',
1767
+			EVENTS_TEMPLATE_PATH . 'event_tickets_metabox_ticket_row.template.php',
1768
+			$ticket
1769
+		);
1770
+		return EEH_Template::display_template($template, $template_args, true);
1771
+	}
1772
+
1773
+
1774
+	/**
1775
+	 * @throws EE_Error
1776
+	 * @throws ReflectionException
1777
+	 */
1778
+	public function registration_options_meta_box()
1779
+	{
1780
+		$yes_no_values             = [
1781
+			['id' => true, 'text' => esc_html__('Yes', 'event_espresso')],
1782
+			['id' => false, 'text' => esc_html__('No', 'event_espresso')],
1783
+		];
1784
+		$default_reg_status_values = EEM_Registration::reg_status_array(
1785
+			[
1786
+				EEM_Registration::status_id_cancelled,
1787
+				EEM_Registration::status_id_declined,
1788
+				EEM_Registration::status_id_incomplete,
1789
+			],
1790
+			true
1791
+		);
1792
+		// $template_args['is_active_select'] = EEH_Form_Fields::select_input('is_active', $yes_no_values, $this->_cpt_model_obj->is_active());
1793
+		$template_args['_event']                          = $this->_cpt_model_obj;
1794
+		$template_args['event']                           = $this->_cpt_model_obj;
1795
+		$template_args['active_status']                   = $this->_cpt_model_obj->pretty_active_status(false);
1796
+		$template_args['additional_limit']                = $this->_cpt_model_obj->additional_limit();
1797
+		$template_args['default_registration_status']     = EEH_Form_Fields::select_input(
1798
+			'default_reg_status',
1799
+			$default_reg_status_values,
1800
+			$this->_cpt_model_obj->default_registration_status()
1801
+		);
1802
+		$template_args['display_description']             = EEH_Form_Fields::select_input(
1803
+			'display_desc',
1804
+			$yes_no_values,
1805
+			$this->_cpt_model_obj->display_description()
1806
+		);
1807
+		$template_args['display_ticket_selector']         = EEH_Form_Fields::select_input(
1808
+			'display_ticket_selector',
1809
+			$yes_no_values,
1810
+			$this->_cpt_model_obj->display_ticket_selector(),
1811
+			'',
1812
+			'',
1813
+			false
1814
+		);
1815
+		$template_args['additional_registration_options'] = apply_filters(
1816
+			'FHEE__Events_Admin_Page__registration_options_meta_box__additional_registration_options',
1817
+			'',
1818
+			$template_args,
1819
+			$yes_no_values,
1820
+			$default_reg_status_values
1821
+		);
1822
+		EEH_Template::display_template(
1823
+			EVENTS_TEMPLATE_PATH . 'event_registration_options.template.php',
1824
+			$template_args
1825
+		);
1826
+	}
1827
+
1828
+
1829
+	/**
1830
+	 * _get_events()
1831
+	 * This method simply returns all the events (for the given _view and paging)
1832
+	 *
1833
+	 * @access public
1834
+	 * @param int  $per_page     count of items per page (20 default);
1835
+	 * @param int  $current_page what is the current page being viewed.
1836
+	 * @param bool $count        if TRUE then we just return a count of ALL events matching the given _view.
1837
+	 *                           If FALSE then we return an array of event objects
1838
+	 *                           that match the given _view and paging parameters.
1839
+	 * @return array|int         an array of event objects or a count of them.
1840
+	 * @throws Exception
1841
+	 */
1842
+	public function get_events($per_page = 10, $current_page = 1, $count = false)
1843
+	{
1844
+		$EEM_Event   = $this->_event_model();
1845
+		$offset      = ($current_page - 1) * $per_page;
1846
+		$limit       = $count ? null : $offset . ',' . $per_page;
1847
+		$orderby     = $this->request->getRequestParam('orderby', 'EVT_ID');
1848
+		$order       = $this->request->getRequestParam('order', 'DESC');
1849
+		$month_range = $this->request->getRequestParam('month_range');
1850
+		if ($month_range) {
1851
+			$pieces = explode(' ', $month_range, 3);
1852
+			// simulate the FIRST day of the month, that fixes issues for months like February
1853
+			// where PHP doesn't know what to assume for date.
1854
+			// @see https://events.codebasehq.com/projects/event-espresso/tickets/10437
1855
+			$month_r = ! empty($pieces[0]) ? date('m', EEH_DTT_Helper::first_of_month_timestamp($pieces[0])) : '';
1856
+			$year_r  = ! empty($pieces[1]) ? $pieces[1] : '';
1857
+		}
1858
+		$where  = [];
1859
+		$status = $this->request->getRequestParam('status');
1860
+		// determine what post_status our condition will have for the query.
1861
+		switch ($status) {
1862
+			case 'month':
1863
+			case 'today':
1864
+			case null:
1865
+			case 'all':
1866
+				break;
1867
+			case 'draft':
1868
+				$where['status'] = ['IN', ['draft', 'auto-draft']];
1869
+				break;
1870
+			default:
1871
+				$where['status'] = $status;
1872
+		}
1873
+		// categories? The default for all categories is -1
1874
+		$category = $this->request->getRequestParam('EVT_CAT', -1, 'int');
1875
+		if ($category !== -1) {
1876
+			$where['Term_Taxonomy.taxonomy'] = EEM_CPT_Base::EVENT_CATEGORY_TAXONOMY;
1877
+			$where['Term_Taxonomy.term_id']  = $category;
1878
+		}
1879
+		// date where conditions
1880
+		$start_formats = EEM_Datetime::instance()->get_formats_for('DTT_EVT_start');
1881
+		if ($month_range) {
1882
+			$DateTime = new DateTime(
1883
+				$year_r . '-' . $month_r . '-01 00:00:00',
1884
+				new DateTimeZone('UTC')
1885
+			);
1886
+			$start    = $DateTime->getTimestamp();
1887
+			// set the datetime to be the end of the month
1888
+			$DateTime->setDate(
1889
+				$year_r,
1890
+				$month_r,
1891
+				$DateTime->format('t')
1892
+			)->setTime(23, 59, 59);
1893
+			$end                             = $DateTime->getTimestamp();
1894
+			$where['Datetime.DTT_EVT_start'] = ['BETWEEN', [$start, $end]];
1895
+		} elseif ($status === 'today') {
1896
+			$DateTime                        =
1897
+				new DateTime('now', new DateTimeZone(EEM_Event::instance()->get_timezone()));
1898
+			$start                           = $DateTime->setTime(0, 0)->format(implode(' ', $start_formats));
1899
+			$end                             = $DateTime->setTime(23, 59, 59)->format(implode(' ', $start_formats));
1900
+			$where['Datetime.DTT_EVT_start'] = ['BETWEEN', [$start, $end]];
1901
+		} elseif ($status === 'month') {
1902
+			$now                             = date('Y-m-01');
1903
+			$DateTime                        =
1904
+				new DateTime($now, new DateTimeZone(EEM_Event::instance()->get_timezone()));
1905
+			$start                           = $DateTime->setTime(0, 0)->format(implode(' ', $start_formats));
1906
+			$end                             = $DateTime->setDate(date('Y'), date('m'), $DateTime->format('t'))
1907
+														->setTime(23, 59, 59)
1908
+														->format(implode(' ', $start_formats));
1909
+			$where['Datetime.DTT_EVT_start'] = ['BETWEEN', [$start, $end]];
1910
+		}
1911
+		if (! EE_Registry::instance()->CAP->current_user_can('ee_read_others_events', 'get_events')) {
1912
+			$where['EVT_wp_user'] = get_current_user_id();
1913
+		} else {
1914
+			if (! isset($where['status'])) {
1915
+				if (! EE_Registry::instance()->CAP->current_user_can('ee_read_private_events', 'get_events')) {
1916
+					$where['OR'] = [
1917
+						'status*restrict_private' => ['!=', 'private'],
1918
+						'AND'                     => [
1919
+							'status*inclusive' => ['=', 'private'],
1920
+							'EVT_wp_user'      => get_current_user_id(),
1921
+						],
1922
+					];
1923
+				}
1924
+			}
1925
+		}
1926
+		$wp_user = $this->request->getRequestParam('EVT_wp_user', 0, 'int');
1927
+		if (
1928
+			$wp_user
1929
+			&& $wp_user !== get_current_user_id()
1930
+			&& EE_Registry::instance()->CAP->current_user_can('ee_read_others_events', 'get_events')
1931
+		) {
1932
+			$where['EVT_wp_user'] = $wp_user;
1933
+		}
1934
+		// search query handling
1935
+		$search_term = $this->request->getRequestParam('s');
1936
+		if ($search_term) {
1937
+			$search_term = '%' . $search_term . '%';
1938
+			$where['OR'] = [
1939
+				'EVT_name'       => ['LIKE', $search_term],
1940
+				'EVT_desc'       => ['LIKE', $search_term],
1941
+				'EVT_short_desc' => ['LIKE', $search_term],
1942
+			];
1943
+		}
1944
+		// filter events by venue.
1945
+		$venue = $this->request->getRequestParam('venue', 0, 'int');
1946
+		if ($venue) {
1947
+			$where['Venue.VNU_ID'] = $venue;
1948
+		}
1949
+		$request_params = $this->request->requestParams();
1950
+		$where          = apply_filters('FHEE__Events_Admin_Page__get_events__where', $where, $request_params);
1951
+		$query_params   = apply_filters(
1952
+			'FHEE__Events_Admin_Page__get_events__query_params',
1953
+			[
1954
+				$where,
1955
+				'limit'    => $limit,
1956
+				'order_by' => $orderby,
1957
+				'order'    => $order,
1958
+				'group_by' => 'EVT_ID',
1959
+			],
1960
+			$request_params
1961
+		);
1962
+
1963
+		// let's first check if we have special requests coming in.
1964
+		$active_status = $this->request->getRequestParam('active_status');
1965
+		if ($active_status) {
1966
+			switch ($active_status) {
1967
+				case 'upcoming':
1968
+					return $EEM_Event->get_upcoming_events($query_params, $count);
1969
+				case 'expired':
1970
+					return $EEM_Event->get_expired_events($query_params, $count);
1971
+				case 'active':
1972
+					return $EEM_Event->get_active_events($query_params, $count);
1973
+				case 'inactive':
1974
+					return $EEM_Event->get_inactive_events($query_params, $count);
1975
+			}
1976
+		}
1977
+
1978
+		return $count ? $EEM_Event->count([$where], 'EVT_ID', true) : $EEM_Event->get_all($query_params);
1979
+	}
1980
+
1981
+
1982
+	/**
1983
+	 * handling for WordPress CPT actions (trash, restore, delete)
1984
+	 *
1985
+	 * @param string $post_id
1986
+	 * @throws EE_Error
1987
+	 * @throws ReflectionException
1988
+	 */
1989
+	public function trash_cpt_item($post_id)
1990
+	{
1991
+		$this->request->setRequestParam('EVT_ID', $post_id);
1992
+		$this->_trash_or_restore_event('trash', false);
1993
+	}
1994
+
1995
+
1996
+	/**
1997
+	 * @param string $post_id
1998
+	 * @throws EE_Error
1999
+	 * @throws ReflectionException
2000
+	 */
2001
+	public function restore_cpt_item($post_id)
2002
+	{
2003
+		$this->request->setRequestParam('EVT_ID', $post_id);
2004
+		$this->_trash_or_restore_event('draft', false);
2005
+	}
2006
+
2007
+
2008
+	/**
2009
+	 * @param string $post_id
2010
+	 * @throws EE_Error
2011
+	 * @throws EE_Error
2012
+	 */
2013
+	public function delete_cpt_item($post_id)
2014
+	{
2015
+		throw new EE_Error(
2016
+			esc_html__(
2017
+				'Please contact Event Espresso support with the details of the steps taken to produce this error.',
2018
+				'event_espresso'
2019
+			)
2020
+		);
2021
+		// $this->request->setRequestParam('EVT_ID', $post_id);
2022
+		// $this->_delete_event();
2023
+	}
2024
+
2025
+
2026
+	/**
2027
+	 * _trash_or_restore_event
2028
+	 *
2029
+	 * @access protected
2030
+	 * @param string $event_status
2031
+	 * @param bool   $redirect_after
2032
+	 * @throws EE_Error
2033
+	 * @throws EE_Error
2034
+	 * @throws ReflectionException
2035
+	 */
2036
+	protected function _trash_or_restore_event($event_status = 'trash', $redirect_after = true)
2037
+	{
2038
+		// determine the event id and set to array.
2039
+		$EVT_ID = $this->request->getRequestParam('EVT_ID', 0, 'int');
2040
+		// loop thru events
2041
+		if ($EVT_ID) {
2042
+			// clean status
2043
+			$event_status = sanitize_key($event_status);
2044
+			// grab status
2045
+			if (! empty($event_status)) {
2046
+				$success = $this->_change_event_status($EVT_ID, $event_status);
2047
+			} else {
2048
+				$success = false;
2049
+				$msg     = esc_html__(
2050
+					'An error occurred. The event could not be moved to the trash because a valid event status was not not supplied.',
2051
+					'event_espresso'
2052
+				);
2053
+				EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
2054
+			}
2055
+		} else {
2056
+			$success = false;
2057
+			$msg     = esc_html__(
2058
+				'An error occurred. The event could not be moved to the trash because a valid event ID was not not supplied.',
2059
+				'event_espresso'
2060
+			);
2061
+			EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
2062
+		}
2063
+		$action = $event_status == 'trash' ? 'moved to the trash' : 'restored from the trash';
2064
+		if ($redirect_after) {
2065
+			$this->_redirect_after_action($success, 'Event', $action, ['action' => 'default']);
2066
+		}
2067
+	}
2068
+
2069
+
2070
+	/**
2071
+	 * _trash_or_restore_events
2072
+	 *
2073
+	 * @access protected
2074
+	 * @param string $event_status
2075
+	 * @return void
2076
+	 * @throws EE_Error
2077
+	 * @throws EE_Error
2078
+	 * @throws ReflectionException
2079
+	 */
2080
+	protected function _trash_or_restore_events($event_status = 'trash')
2081
+	{
2082
+		// clean status
2083
+		$event_status = sanitize_key($event_status);
2084
+		// grab status
2085
+		if (! empty($event_status)) {
2086
+			$success = true;
2087
+			// determine the event id and set to array.
2088
+			$EVT_IDs = $this->request->getRequestParam('EVT_IDs', [], 'int', true);
2089
+			// loop thru events
2090
+			foreach ($EVT_IDs as $EVT_ID) {
2091
+				if ($EVT_ID = absint($EVT_ID)) {
2092
+					$results = $this->_change_event_status($EVT_ID, $event_status);
2093
+					$success = $results !== false ? $success : false;
2094
+				} else {
2095
+					$msg = sprintf(
2096
+						esc_html__(
2097
+							'An error occurred. Event #%d could not be moved to the trash because a valid event ID was not not supplied.',
2098
+							'event_espresso'
2099
+						),
2100
+						$EVT_ID
2101
+					);
2102
+					EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
2103
+					$success = false;
2104
+				}
2105
+			}
2106
+		} else {
2107
+			$success = false;
2108
+			$msg     = esc_html__(
2109
+				'An error occurred. The event could not be moved to the trash because a valid event status was not not supplied.',
2110
+				'event_espresso'
2111
+			);
2112
+			EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
2113
+		}
2114
+		// in order to force a pluralized result message we need to send back a success status greater than 1
2115
+		$success = $success ? 2 : false;
2116
+		$action  = $event_status == 'trash' ? 'moved to the trash' : 'restored from the trash';
2117
+		$this->_redirect_after_action($success, 'Events', $action, ['action' => 'default']);
2118
+	}
2119
+
2120
+
2121
+	/**
2122
+	 * @param int    $EVT_ID
2123
+	 * @param string $event_status
2124
+	 * @return bool
2125
+	 * @throws EE_Error
2126
+	 * @throws ReflectionException
2127
+	 */
2128
+	private function _change_event_status($EVT_ID = 0, $event_status = '')
2129
+	{
2130
+		// grab event id
2131
+		if (! $EVT_ID) {
2132
+			$msg = esc_html__(
2133
+				'An error occurred. No Event ID or an invalid Event ID was received.',
2134
+				'event_espresso'
2135
+			);
2136
+			EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
2137
+			return false;
2138
+		}
2139
+		$this->_cpt_model_obj = EEM_Event::instance()->get_one_by_ID($EVT_ID);
2140
+		// clean status
2141
+		$event_status = sanitize_key($event_status);
2142
+		// grab status
2143
+		if (empty($event_status)) {
2144
+			$msg = esc_html__(
2145
+				'An error occurred. No Event Status or an invalid Event Status was received.',
2146
+				'event_espresso'
2147
+			);
2148
+			EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
2149
+			return false;
2150
+		}
2151
+		// was event trashed or restored ?
2152
+		switch ($event_status) {
2153
+			case 'draft':
2154
+				$action = 'restored from the trash';
2155
+				$hook   = 'AHEE_event_restored_from_trash';
2156
+				break;
2157
+			case 'trash':
2158
+				$action = 'moved to the trash';
2159
+				$hook   = 'AHEE_event_moved_to_trash';
2160
+				break;
2161
+			default:
2162
+				$action = 'updated';
2163
+				$hook   = false;
2164
+		}
2165
+		// use class to change status
2166
+		$this->_cpt_model_obj->set_status($event_status);
2167
+		$success = $this->_cpt_model_obj->save();
2168
+		if (! $success) {
2169
+			$msg = sprintf(esc_html__('An error occurred. The event could not be %s.', 'event_espresso'), $action);
2170
+			EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
2171
+			return false;
2172
+		}
2173
+		if ($hook) {
2174
+			do_action($hook);
2175
+		}
2176
+		return true;
2177
+	}
2178
+
2179
+
2180
+	/**
2181
+	 * @param array $event_ids
2182
+	 * @return array
2183
+	 * @since   4.10.23.p
2184
+	 */
2185
+	private function cleanEventIds(array $event_ids)
2186
+	{
2187
+		return array_map('absint', $event_ids);
2188
+	}
2189
+
2190
+
2191
+	/**
2192
+	 * @return array
2193
+	 * @since   4.10.23.p
2194
+	 */
2195
+	private function getEventIdsFromRequest()
2196
+	{
2197
+		if ($this->request->requestParamIsSet('EVT_IDs')) {
2198
+			return $this->request->getRequestParam('EVT_IDs', [], 'int', true);
2199
+		} else {
2200
+			return $this->request->getRequestParam('EVT_ID', [], 'int', true);
2201
+		}
2202
+	}
2203
+
2204
+
2205
+	/**
2206
+	 * @param bool $preview_delete
2207
+	 * @throws EE_Error
2208
+	 */
2209
+	protected function _delete_event($preview_delete = true)
2210
+	{
2211
+		$this->_delete_events($preview_delete);
2212
+	}
2213
+
2214
+
2215
+	/**
2216
+	 * Gets the tree traversal batch persister.
2217
+	 *
2218
+	 * @return NodeGroupDao
2219
+	 * @throws InvalidArgumentException
2220
+	 * @throws InvalidDataTypeException
2221
+	 * @throws InvalidInterfaceException
2222
+	 * @since 4.10.12.p
2223
+	 */
2224
+	protected function getModelObjNodeGroupPersister()
2225
+	{
2226
+		if (! $this->model_obj_node_group_persister instanceof NodeGroupDao) {
2227
+			$this->model_obj_node_group_persister =
2228
+				$this->getLoader()->load('\EventEspresso\core\services\orm\tree_traversal\NodeGroupDao');
2229
+		}
2230
+		return $this->model_obj_node_group_persister;
2231
+	}
2232
+
2233
+
2234
+	/**
2235
+	 * @param bool $preview_delete
2236
+	 * @return void
2237
+	 * @throws EE_Error
2238
+	 */
2239
+	protected function _delete_events($preview_delete = true)
2240
+	{
2241
+		$event_ids = $this->getEventIdsFromRequest();
2242
+		if ($preview_delete) {
2243
+			$this->generateDeletionPreview($event_ids);
2244
+		} else {
2245
+			EEM_Event::instance()->delete_permanently([['EVT_ID' => ['IN', $event_ids]]]);
2246
+		}
2247
+	}
2248
+
2249
+
2250
+	/**
2251
+	 * @param array $event_ids
2252
+	 */
2253
+	protected function generateDeletionPreview(array $event_ids)
2254
+	{
2255
+		$event_ids = $this->cleanEventIds($event_ids);
2256
+		// Set a code we can use to reference this deletion task in the batch jobs and preview page.
2257
+		$deletion_job_code = $this->getModelObjNodeGroupPersister()->generateGroupCode();
2258
+		$return_url        = EE_Admin_Page::add_query_args_and_nonce(
2259
+			[
2260
+				'action'            => 'preview_deletion',
2261
+				'deletion_job_code' => $deletion_job_code,
2262
+			],
2263
+			$this->_admin_base_url
2264
+		);
2265
+		EEH_URL::safeRedirectAndExit(
2266
+			EE_Admin_Page::add_query_args_and_nonce(
2267
+				[
2268
+					'page'              => 'espresso_batch',
2269
+					'batch'             => EED_Batch::batch_job,
2270
+					'EVT_IDs'           => $event_ids,
2271
+					'deletion_job_code' => $deletion_job_code,
2272
+					'job_handler'       => urlencode('EventEspressoBatchRequest\JobHandlers\PreviewEventDeletion'),
2273
+					'return_url'        => urlencode($return_url),
2274
+				],
2275
+				admin_url()
2276
+			)
2277
+		);
2278
+	}
2279
+
2280
+
2281
+	/**
2282
+	 * Checks for a POST submission
2283
+	 *
2284
+	 * @since 4.10.12.p
2285
+	 */
2286
+	protected function confirmDeletion()
2287
+	{
2288
+		$deletion_redirect_logic =
2289
+			$this->getLoader()->getShared('\EventEspresso\core\domain\services\admin\events\data\ConfirmDeletion');
2290
+		$deletion_redirect_logic->handle($this->get_request_data(), $this->admin_base_url());
2291
+	}
2292
+
2293
+
2294
+	/**
2295
+	 * A page for users to preview what exactly will be deleted, and confirm they want to delete it.
2296
+	 *
2297
+	 * @throws EE_Error
2298
+	 * @since 4.10.12.p
2299
+	 */
2300
+	protected function previewDeletion()
2301
+	{
2302
+		$preview_deletion_logic =
2303
+			$this->getLoader()->getShared('\EventEspresso\core\domain\services\admin\events\data\PreviewDeletion');
2304
+		$this->set_template_args($preview_deletion_logic->handle($this->get_request_data(), $this->admin_base_url()));
2305
+		$this->display_admin_page_with_no_sidebar();
2306
+	}
2307
+
2308
+
2309
+	/**
2310
+	 * get total number of events
2311
+	 *
2312
+	 * @access public
2313
+	 * @return int
2314
+	 * @throws EE_Error
2315
+	 * @throws EE_Error
2316
+	 */
2317
+	public function total_events()
2318
+	{
2319
+		return EEM_Event::instance()->count(
2320
+			['caps' => 'read_admin'],
2321
+			'EVT_ID',
2322
+			true
2323
+		);
2324
+	}
2325
+
2326
+
2327
+	/**
2328
+	 * get total number of draft events
2329
+	 *
2330
+	 * @access public
2331
+	 * @return int
2332
+	 * @throws EE_Error
2333
+	 * @throws EE_Error
2334
+	 */
2335
+	public function total_events_draft()
2336
+	{
2337
+		return EEM_Event::instance()->count(
2338
+			[
2339
+				['status' => ['IN', ['draft', 'auto-draft']]],
2340
+				'caps' => 'read_admin',
2341
+			],
2342
+			'EVT_ID',
2343
+			true
2344
+		);
2345
+	}
2346
+
2347
+
2348
+	/**
2349
+	 * get total number of trashed events
2350
+	 *
2351
+	 * @access public
2352
+	 * @return int
2353
+	 * @throws EE_Error
2354
+	 * @throws EE_Error
2355
+	 */
2356
+	public function total_trashed_events()
2357
+	{
2358
+		return EEM_Event::instance()->count(
2359
+			[
2360
+				['status' => 'trash'],
2361
+				'caps' => 'read_admin',
2362
+			],
2363
+			'EVT_ID',
2364
+			true
2365
+		);
2366
+	}
2367
+
2368
+
2369
+	/**
2370
+	 *    _default_event_settings
2371
+	 *    This generates the Default Settings Tab
2372
+	 *
2373
+	 * @return void
2374
+	 * @throws EE_Error
2375
+	 */
2376
+	protected function _default_event_settings()
2377
+	{
2378
+		$this->_set_add_edit_form_tags('update_default_event_settings');
2379
+		$this->_set_publish_post_box_vars(null, false, false, null, false);
2380
+		$this->_template_args['admin_page_content'] = $this->_default_event_settings_form()->get_html();
2381
+		$this->display_admin_page_with_sidebar();
2382
+	}
2383
+
2384
+
2385
+	/**
2386
+	 * Return the form for event settings.
2387
+	 *
2388
+	 * @return EE_Form_Section_Proper
2389
+	 * @throws EE_Error
2390
+	 */
2391
+	protected function _default_event_settings_form()
2392
+	{
2393
+		$registration_config              = EE_Registry::instance()->CFG->registration;
2394
+		$registration_stati_for_selection = EEM_Registration::reg_status_array(
2395
+		// exclude
2396
+			[
2397
+				EEM_Registration::status_id_cancelled,
2398
+				EEM_Registration::status_id_declined,
2399
+				EEM_Registration::status_id_incomplete,
2400
+				EEM_Registration::status_id_wait_list,
2401
+			],
2402
+			true
2403
+		);
2404
+		return new EE_Form_Section_Proper(
2405
+			[
2406
+				'name'            => 'update_default_event_settings',
2407
+				'html_id'         => 'update_default_event_settings',
2408
+				'html_class'      => 'form-table',
2409
+				'layout_strategy' => new EE_Admin_Two_Column_Layout(),
2410
+				'subsections'     => apply_filters(
2411
+					'FHEE__Events_Admin_Page___default_event_settings_form__form_subsections',
2412
+					[
2413
+						'default_reg_status'  => new EE_Select_Input(
2414
+							$registration_stati_for_selection,
2415
+							[
2416
+								'default'         => isset($registration_config->default_STS_ID)
2417
+													 && array_key_exists(
2418
+														 $registration_config->default_STS_ID,
2419
+														 $registration_stati_for_selection
2420
+													 )
2421
+									? sanitize_text_field($registration_config->default_STS_ID)
2422
+									: EEM_Registration::status_id_pending_payment,
2423
+								'html_label_text' => esc_html__('Default Registration Status', 'event_espresso')
2424
+													 . EEH_Template::get_help_tab_link(
2425
+														 'default_settings_status_help_tab'
2426
+													 ),
2427
+								'html_help_text'  => esc_html__(
2428
+									'This setting allows you to preselect what the default registration status setting is when creating an event.  Note that changing this setting does NOT retroactively apply it to existing events.',
2429
+									'event_espresso'
2430
+								),
2431
+							]
2432
+						),
2433
+						'default_max_tickets' => new EE_Integer_Input(
2434
+							[
2435
+								'default'         => isset($registration_config->default_maximum_number_of_tickets)
2436
+									? $registration_config->default_maximum_number_of_tickets
2437
+									: EEM_Event::get_default_additional_limit(),
2438
+								'html_label_text' => esc_html__(
2439
+									'Default Maximum Tickets Allowed Per Order:',
2440
+									'event_espresso'
2441
+								)
2442
+								. EEH_Template::get_help_tab_link(
2443
+									'default_maximum_tickets_help_tab"'
2444
+								),
2445
+								'html_help_text'  => esc_html__(
2446
+									'This setting allows you to indicate what will be the default for the maximum number of tickets per order when creating new events.',
2447
+									'event_espresso'
2448
+								),
2449
+							]
2450
+						),
2451
+					]
2452
+				),
2453
+			]
2454
+		);
2455
+	}
2456
+
2457
+
2458
+	/**
2459
+	 * _update_default_event_settings
2460
+	 *
2461
+	 * @access protected
2462
+	 * @return void
2463
+	 * @throws EE_Error
2464
+	 */
2465
+	protected function _update_default_event_settings()
2466
+	{
2467
+		$registration_config = EE_Registry::instance()->CFG->registration;
2468
+		$form                = $this->_default_event_settings_form();
2469
+		if ($form->was_submitted()) {
2470
+			$form->receive_form_submission();
2471
+			if ($form->is_valid()) {
2472
+				$valid_data = $form->valid_data();
2473
+				if (isset($valid_data['default_reg_status'])) {
2474
+					$registration_config->default_STS_ID = $valid_data['default_reg_status'];
2475
+				}
2476
+				if (isset($valid_data['default_max_tickets'])) {
2477
+					$registration_config->default_maximum_number_of_tickets = $valid_data['default_max_tickets'];
2478
+				}
2479
+				// update because data was valid!
2480
+				EE_Registry::instance()->CFG->update_espresso_config();
2481
+				EE_Error::overwrite_success();
2482
+				EE_Error::add_success(
2483
+					esc_html__('Default Event Settings were updated', 'event_espresso')
2484
+				);
2485
+			}
2486
+		}
2487
+		$this->_redirect_after_action(0, '', '', ['action' => 'default_event_settings'], true);
2488
+	}
2489
+
2490
+
2491
+	/*************        Templates        *************
20 2492
      *
21
-     * @var EE_Event $_event
22
-     */
23
-    protected $_event;
24
-
25
-
26
-    /**
27
-     * This will hold the category object for category_details screen.
28
-     *
29
-     * @var stdClass $_category
30
-     */
31
-    protected $_category;
32
-
33
-
34
-    /**
35
-     * This will hold the event model instance
36
-     *
37
-     * @var EEM_Event $_event_model
38
-     */
39
-    protected $_event_model;
40
-
41
-
42
-    /**
43
-     * @var EE_Event
44
-     */
45
-    protected $_cpt_model_obj = false;
46
-
47
-
48
-    /**
49
-     * @var NodeGroupDao
50
-     */
51
-    protected $model_obj_node_group_persister;
52
-
53
-
54
-    /**
55
-     * Initialize page props for this admin page group.
56
-     */
57
-    protected function _init_page_props()
58
-    {
59
-        $this->page_slug        = EVENTS_PG_SLUG;
60
-        $this->page_label       = EVENTS_LABEL;
61
-        $this->_admin_base_url  = EVENTS_ADMIN_URL;
62
-        $this->_admin_base_path = EVENTS_ADMIN;
63
-        $this->_cpt_model_names = [
64
-            'create_new' => 'EEM_Event',
65
-            'edit'       => 'EEM_Event',
66
-        ];
67
-        $this->_cpt_edit_routes = [
68
-            'espresso_events' => 'edit',
69
-        ];
70
-        add_action(
71
-            'AHEE__EE_Admin_Page_CPT__set_model_object__after_set_object',
72
-            [$this, 'verify_event_edit'],
73
-            10,
74
-            2
75
-        );
76
-    }
77
-
78
-
79
-    /**
80
-     * Sets the ajax hooks used for this admin page group.
81
-     */
82
-    protected function _ajax_hooks()
83
-    {
84
-        add_action('wp_ajax_ee_save_timezone_setting', [$this, 'saveTimezoneString']);
85
-    }
86
-
87
-
88
-    /**
89
-     * Sets the page properties for this admin page group.
90
-     */
91
-    protected function _define_page_props()
92
-    {
93
-        $this->_admin_page_title = EVENTS_LABEL;
94
-        $this->_labels           = [
95
-            'buttons'      => [
96
-                'add'             => esc_html__('Add New Event', 'event_espresso'),
97
-                'edit'            => esc_html__('Edit Event', 'event_espresso'),
98
-                'delete'          => esc_html__('Delete Event', 'event_espresso'),
99
-                'add_category'    => esc_html__('Add New Category', 'event_espresso'),
100
-                'edit_category'   => esc_html__('Edit Category', 'event_espresso'),
101
-                'delete_category' => esc_html__('Delete Category', 'event_espresso'),
102
-            ],
103
-            'editor_title' => [
104
-                'espresso_events' => esc_html__('Enter event title here', 'event_espresso'),
105
-            ],
106
-            'publishbox'   => [
107
-                'create_new'        => esc_html__('Save New Event', 'event_espresso'),
108
-                'edit'              => esc_html__('Update Event', 'event_espresso'),
109
-                'add_category'      => esc_html__('Save New Category', 'event_espresso'),
110
-                'edit_category'     => esc_html__('Update Category', 'event_espresso'),
111
-                'template_settings' => esc_html__('Update Settings', 'event_espresso'),
112
-            ],
113
-        ];
114
-    }
115
-
116
-
117
-    /**
118
-     * Sets the page routes property for this admin page group.
119
-     */
120
-    protected function _set_page_routes()
121
-    {
122
-        // load formatter helper
123
-        // load field generator helper
124
-        // is there a evt_id in the request?
125
-        $EVT_ID = $this->request->getRequestParam('EVT_ID', 0, 'int');
126
-        $EVT_ID = $this->request->getRequestParam('post', $EVT_ID, 'int');
127
-
128
-        $this->_page_routes = [
129
-            'default'                       => [
130
-                'func'       => '_events_overview_list_table',
131
-                'capability' => 'ee_read_events',
132
-            ],
133
-            'create_new'                    => [
134
-                'func'       => '_create_new_cpt_item',
135
-                'capability' => 'ee_edit_events',
136
-            ],
137
-            'edit'                          => [
138
-                'func'       => '_edit_cpt_item',
139
-                'capability' => 'ee_edit_event',
140
-                'obj_id'     => $EVT_ID,
141
-            ],
142
-            'copy_event'                    => [
143
-                'func'       => '_copy_events',
144
-                'capability' => 'ee_edit_event',
145
-                'obj_id'     => $EVT_ID,
146
-                'noheader'   => true,
147
-            ],
148
-            'trash_event'                   => [
149
-                'func'       => '_trash_or_restore_event',
150
-                'args'       => ['event_status' => 'trash'],
151
-                'capability' => 'ee_delete_event',
152
-                'obj_id'     => $EVT_ID,
153
-                'noheader'   => true,
154
-            ],
155
-            'trash_events'                  => [
156
-                'func'       => '_trash_or_restore_events',
157
-                'args'       => ['event_status' => 'trash'],
158
-                'capability' => 'ee_delete_events',
159
-                'noheader'   => true,
160
-            ],
161
-            'restore_event'                 => [
162
-                'func'       => '_trash_or_restore_event',
163
-                'args'       => ['event_status' => 'draft'],
164
-                'capability' => 'ee_delete_event',
165
-                'obj_id'     => $EVT_ID,
166
-                'noheader'   => true,
167
-            ],
168
-            'restore_events'                => [
169
-                'func'       => '_trash_or_restore_events',
170
-                'args'       => ['event_status' => 'draft'],
171
-                'capability' => 'ee_delete_events',
172
-                'noheader'   => true,
173
-            ],
174
-            'delete_event'                  => [
175
-                'func'       => '_delete_event',
176
-                'capability' => 'ee_delete_event',
177
-                'obj_id'     => $EVT_ID,
178
-                'noheader'   => true,
179
-            ],
180
-            'delete_events'                 => [
181
-                'func'       => '_delete_events',
182
-                'capability' => 'ee_delete_events',
183
-                'noheader'   => true,
184
-            ],
185
-            'view_report'                   => [
186
-                'func'       => '_view_report',
187
-                'capability' => 'ee_edit_events',
188
-            ],
189
-            'default_event_settings'        => [
190
-                'func'       => '_default_event_settings',
191
-                'capability' => 'manage_options',
192
-            ],
193
-            'update_default_event_settings' => [
194
-                'func'       => '_update_default_event_settings',
195
-                'capability' => 'manage_options',
196
-                'noheader'   => true,
197
-            ],
198
-            'template_settings'             => [
199
-                'func'       => '_template_settings',
200
-                'capability' => 'manage_options',
201
-            ],
202
-            // event category tab related
203
-            'add_category'                  => [
204
-                'func'       => '_category_details',
205
-                'capability' => 'ee_edit_event_category',
206
-                'args'       => ['add'],
207
-            ],
208
-            'edit_category'                 => [
209
-                'func'       => '_category_details',
210
-                'capability' => 'ee_edit_event_category',
211
-                'args'       => ['edit'],
212
-            ],
213
-            'delete_categories'             => [
214
-                'func'       => '_delete_categories',
215
-                'capability' => 'ee_delete_event_category',
216
-                'noheader'   => true,
217
-            ],
218
-            'delete_category'               => [
219
-                'func'       => '_delete_categories',
220
-                'capability' => 'ee_delete_event_category',
221
-                'noheader'   => true,
222
-            ],
223
-            'insert_category'               => [
224
-                'func'       => '_insert_or_update_category',
225
-                'args'       => ['new_category' => true],
226
-                'capability' => 'ee_edit_event_category',
227
-                'noheader'   => true,
228
-            ],
229
-            'update_category'               => [
230
-                'func'       => '_insert_or_update_category',
231
-                'args'       => ['new_category' => false],
232
-                'capability' => 'ee_edit_event_category',
233
-                'noheader'   => true,
234
-            ],
235
-            'category_list'                 => [
236
-                'func'       => '_category_list_table',
237
-                'capability' => 'ee_manage_event_categories',
238
-            ],
239
-            'preview_deletion'              => [
240
-                'func'       => 'previewDeletion',
241
-                'capability' => 'ee_delete_events',
242
-            ],
243
-            'confirm_deletion'              => [
244
-                'func'       => 'confirmDeletion',
245
-                'capability' => 'ee_delete_events',
246
-                'noheader'   => true,
247
-            ],
248
-        ];
249
-    }
250
-
251
-
252
-    /**
253
-     * Set the _page_config property for this admin page group.
254
-     */
255
-    protected function _set_page_config()
256
-    {
257
-        $post_id            = $this->request->getRequestParam('post', 0, 'int');
258
-        $EVT_CAT_ID         = $this->request->getRequestParam('EVT_CAT_ID', 0, 'int');
259
-        $this->_page_config = [
260
-            'default'                => [
261
-                'nav'           => [
262
-                    'label' => esc_html__('Overview', 'event_espresso'),
263
-                    'order' => 10,
264
-                ],
265
-                'list_table'    => 'Events_Admin_List_Table',
266
-                'help_tabs'     => [
267
-                    'events_overview_help_tab'                       => [
268
-                        'title'    => esc_html__('Events Overview', 'event_espresso'),
269
-                        'filename' => 'events_overview',
270
-                    ],
271
-                    'events_overview_table_column_headings_help_tab' => [
272
-                        'title'    => esc_html__('Events Overview Table Column Headings', 'event_espresso'),
273
-                        'filename' => 'events_overview_table_column_headings',
274
-                    ],
275
-                    'events_overview_filters_help_tab'               => [
276
-                        'title'    => esc_html__('Events Overview Filters', 'event_espresso'),
277
-                        'filename' => 'events_overview_filters',
278
-                    ],
279
-                    'events_overview_view_help_tab'                  => [
280
-                        'title'    => esc_html__('Events Overview Views', 'event_espresso'),
281
-                        'filename' => 'events_overview_views',
282
-                    ],
283
-                    'events_overview_other_help_tab'                 => [
284
-                        'title'    => esc_html__('Events Overview Other', 'event_espresso'),
285
-                        'filename' => 'events_overview_other',
286
-                    ],
287
-                ],
288
-                'qtips'         => [
289
-                    'EE_Event_List_Table_Tips',
290
-                ],
291
-                'require_nonce' => false,
292
-            ],
293
-            'create_new'             => [
294
-                'nav'           => [
295
-                    'label'      => esc_html__('Add Event', 'event_espresso'),
296
-                    'order'      => 5,
297
-                    'persistent' => false,
298
-                ],
299
-                'metaboxes'     => ['_register_event_editor_meta_boxes'],
300
-                'help_tabs'     => [
301
-                    'event_editor_help_tab'                            => [
302
-                        'title'    => esc_html__('Event Editor', 'event_espresso'),
303
-                        'filename' => 'event_editor',
304
-                    ],
305
-                    'event_editor_title_richtexteditor_help_tab'       => [
306
-                        'title'    => esc_html__('Event Title & Rich Text Editor', 'event_espresso'),
307
-                        'filename' => 'event_editor_title_richtexteditor',
308
-                    ],
309
-                    'event_editor_venue_details_help_tab'              => [
310
-                        'title'    => esc_html__('Event Venue Details', 'event_espresso'),
311
-                        'filename' => 'event_editor_venue_details',
312
-                    ],
313
-                    'event_editor_event_datetimes_help_tab'            => [
314
-                        'title'    => esc_html__('Event Datetimes', 'event_espresso'),
315
-                        'filename' => 'event_editor_event_datetimes',
316
-                    ],
317
-                    'event_editor_event_tickets_help_tab'              => [
318
-                        'title'    => esc_html__('Event Tickets', 'event_espresso'),
319
-                        'filename' => 'event_editor_event_tickets',
320
-                    ],
321
-                    'event_editor_event_registration_options_help_tab' => [
322
-                        'title'    => esc_html__('Event Registration Options', 'event_espresso'),
323
-                        'filename' => 'event_editor_event_registration_options',
324
-                    ],
325
-                    'event_editor_tags_categories_help_tab'            => [
326
-                        'title'    => esc_html__('Event Tags & Categories', 'event_espresso'),
327
-                        'filename' => 'event_editor_tags_categories',
328
-                    ],
329
-                    'event_editor_questions_registrants_help_tab'      => [
330
-                        'title'    => esc_html__('Questions for Registrants', 'event_espresso'),
331
-                        'filename' => 'event_editor_questions_registrants',
332
-                    ],
333
-                    'event_editor_save_new_event_help_tab'             => [
334
-                        'title'    => esc_html__('Save New Event', 'event_espresso'),
335
-                        'filename' => 'event_editor_save_new_event',
336
-                    ],
337
-                    'event_editor_other_help_tab'                      => [
338
-                        'title'    => esc_html__('Event Other', 'event_espresso'),
339
-                        'filename' => 'event_editor_other',
340
-                    ],
341
-                ],
342
-                'qtips'         => ['EE_Event_Editor_Decaf_Tips'],
343
-                'require_nonce' => false,
344
-            ],
345
-            'edit'                   => [
346
-                'nav'           => [
347
-                    'label'      => esc_html__('Edit Event', 'event_espresso'),
348
-                    'order'      => 5,
349
-                    'persistent' => false,
350
-                    'url'        => $post_id
351
-                        ? EE_Admin_Page::add_query_args_and_nonce(
352
-                            ['post' => $post_id, 'action' => 'edit'],
353
-                            $this->_current_page_view_url
354
-                        )
355
-                        : $this->_admin_base_url,
356
-                ],
357
-                'metaboxes'     => ['_register_event_editor_meta_boxes'],
358
-                'help_tabs'     => [
359
-                    'event_editor_help_tab'                            => [
360
-                        'title'    => esc_html__('Event Editor', 'event_espresso'),
361
-                        'filename' => 'event_editor',
362
-                    ],
363
-                    'event_editor_title_richtexteditor_help_tab'       => [
364
-                        'title'    => esc_html__('Event Title & Rich Text Editor', 'event_espresso'),
365
-                        'filename' => 'event_editor_title_richtexteditor',
366
-                    ],
367
-                    'event_editor_venue_details_help_tab'              => [
368
-                        'title'    => esc_html__('Event Venue Details', 'event_espresso'),
369
-                        'filename' => 'event_editor_venue_details',
370
-                    ],
371
-                    'event_editor_event_datetimes_help_tab'            => [
372
-                        'title'    => esc_html__('Event Datetimes', 'event_espresso'),
373
-                        'filename' => 'event_editor_event_datetimes',
374
-                    ],
375
-                    'event_editor_event_tickets_help_tab'              => [
376
-                        'title'    => esc_html__('Event Tickets', 'event_espresso'),
377
-                        'filename' => 'event_editor_event_tickets',
378
-                    ],
379
-                    'event_editor_event_registration_options_help_tab' => [
380
-                        'title'    => esc_html__('Event Registration Options', 'event_espresso'),
381
-                        'filename' => 'event_editor_event_registration_options',
382
-                    ],
383
-                    'event_editor_tags_categories_help_tab'            => [
384
-                        'title'    => esc_html__('Event Tags & Categories', 'event_espresso'),
385
-                        'filename' => 'event_editor_tags_categories',
386
-                    ],
387
-                    'event_editor_questions_registrants_help_tab'      => [
388
-                        'title'    => esc_html__('Questions for Registrants', 'event_espresso'),
389
-                        'filename' => 'event_editor_questions_registrants',
390
-                    ],
391
-                    'event_editor_save_new_event_help_tab'             => [
392
-                        'title'    => esc_html__('Save New Event', 'event_espresso'),
393
-                        'filename' => 'event_editor_save_new_event',
394
-                    ],
395
-                    'event_editor_other_help_tab'                      => [
396
-                        'title'    => esc_html__('Event Other', 'event_espresso'),
397
-                        'filename' => 'event_editor_other',
398
-                    ],
399
-                ],
400
-                'qtips'         => ['EE_Event_Editor_Decaf_Tips'],
401
-                'require_nonce' => false,
402
-            ],
403
-            'default_event_settings' => [
404
-                'nav'           => [
405
-                    'label' => esc_html__('Default Settings', 'event_espresso'),
406
-                    'order' => 40,
407
-                ],
408
-                'metaboxes'     => array_merge($this->_default_espresso_metaboxes, ['_publish_post_box']),
409
-                'labels'        => [
410
-                    'publishbox' => esc_html__('Update Settings', 'event_espresso'),
411
-                ],
412
-                'help_tabs'     => [
413
-                    'default_settings_help_tab'        => [
414
-                        'title'    => esc_html__('Default Event Settings', 'event_espresso'),
415
-                        'filename' => 'events_default_settings',
416
-                    ],
417
-                    'default_settings_status_help_tab' => [
418
-                        'title'    => esc_html__('Default Registration Status', 'event_espresso'),
419
-                        'filename' => 'events_default_settings_status',
420
-                    ],
421
-                    'default_maximum_tickets_help_tab' => [
422
-                        'title'    => esc_html__('Default Maximum Tickets Per Order', 'event_espresso'),
423
-                        'filename' => 'events_default_settings_max_tickets',
424
-                    ],
425
-                ],
426
-                'require_nonce' => false,
427
-            ],
428
-            // template settings
429
-            'template_settings'      => [
430
-                'nav'           => [
431
-                    'label' => esc_html__('Templates', 'event_espresso'),
432
-                    'order' => 30,
433
-                ],
434
-                'metaboxes'     => $this->_default_espresso_metaboxes,
435
-                'help_tabs'     => [
436
-                    'general_settings_templates_help_tab' => [
437
-                        'title'    => esc_html__('Templates', 'event_espresso'),
438
-                        'filename' => 'general_settings_templates',
439
-                    ],
440
-                ],
441
-                'require_nonce' => false,
442
-            ],
443
-            // event category stuff
444
-            'add_category'           => [
445
-                'nav'           => [
446
-                    'label'      => esc_html__('Add Category', 'event_espresso'),
447
-                    'order'      => 15,
448
-                    'persistent' => false,
449
-                ],
450
-                'help_tabs'     => [
451
-                    'add_category_help_tab' => [
452
-                        'title'    => esc_html__('Add New Event Category', 'event_espresso'),
453
-                        'filename' => 'events_add_category',
454
-                    ],
455
-                ],
456
-                'metaboxes'     => ['_publish_post_box'],
457
-                'require_nonce' => false,
458
-            ],
459
-            'edit_category'          => [
460
-                'nav'           => [
461
-                    'label'      => esc_html__('Edit Category', 'event_espresso'),
462
-                    'order'      => 15,
463
-                    'persistent' => false,
464
-                    'url'        => $EVT_CAT_ID
465
-                        ? add_query_arg(
466
-                            ['EVT_CAT_ID' => $EVT_CAT_ID],
467
-                            $this->_current_page_view_url
468
-                        )
469
-                        : $this->_admin_base_url,
470
-                ],
471
-                'help_tabs'     => [
472
-                    'edit_category_help_tab' => [
473
-                        'title'    => esc_html__('Edit Event Category', 'event_espresso'),
474
-                        'filename' => 'events_edit_category',
475
-                    ],
476
-                ],
477
-                'metaboxes'     => ['_publish_post_box'],
478
-                'require_nonce' => false,
479
-            ],
480
-            'category_list'          => [
481
-                'nav'           => [
482
-                    'label' => esc_html__('Categories', 'event_espresso'),
483
-                    'order' => 20,
484
-                ],
485
-                'list_table'    => 'Event_Categories_Admin_List_Table',
486
-                'help_tabs'     => [
487
-                    'events_categories_help_tab'                       => [
488
-                        'title'    => esc_html__('Event Categories', 'event_espresso'),
489
-                        'filename' => 'events_categories',
490
-                    ],
491
-                    'events_categories_table_column_headings_help_tab' => [
492
-                        'title'    => esc_html__('Event Categories Table Column Headings', 'event_espresso'),
493
-                        'filename' => 'events_categories_table_column_headings',
494
-                    ],
495
-                    'events_categories_view_help_tab'                  => [
496
-                        'title'    => esc_html__('Event Categories Views', 'event_espresso'),
497
-                        'filename' => 'events_categories_views',
498
-                    ],
499
-                    'events_categories_other_help_tab'                 => [
500
-                        'title'    => esc_html__('Event Categories Other', 'event_espresso'),
501
-                        'filename' => 'events_categories_other',
502
-                    ],
503
-                ],
504
-                'metaboxes'     => $this->_default_espresso_metaboxes,
505
-                'require_nonce' => false,
506
-            ],
507
-            'preview_deletion'       => [
508
-                'nav'           => [
509
-                    'label'      => esc_html__('Preview Deletion', 'event_espresso'),
510
-                    'order'      => 15,
511
-                    'persistent' => false,
512
-                    'url'        => '',
513
-                ],
514
-                'require_nonce' => false,
515
-            ],
516
-        ];
517
-    }
518
-
519
-
520
-    /**
521
-     * Used to register any global screen options if necessary for every route in this admin page group.
522
-     */
523
-    protected function _add_screen_options()
524
-    {
525
-    }
526
-
527
-
528
-    /**
529
-     * Implementing the screen options for the 'default' route.
530
-     */
531
-    protected function _add_screen_options_default()
532
-    {
533
-        $this->_per_page_screen_option();
534
-    }
535
-
536
-
537
-    /**
538
-     * Implementing screen options for the category list route.
539
-     */
540
-    protected function _add_screen_options_category_list()
541
-    {
542
-        $page_title              = $this->_admin_page_title;
543
-        $this->_admin_page_title = esc_html__('Categories', 'event_espresso');
544
-        $this->_per_page_screen_option();
545
-        $this->_admin_page_title = $page_title;
546
-    }
547
-
548
-
549
-    /**
550
-     * Used to register any global feature pointers for the admin page group.
551
-     */
552
-    protected function _add_feature_pointers()
553
-    {
554
-    }
555
-
556
-
557
-    /**
558
-     * Registers and enqueues any global scripts and styles for the entire admin page group.
559
-     */
560
-    public function load_scripts_styles()
561
-    {
562
-        wp_register_style(
563
-            'events-admin-css',
564
-            EVENTS_ASSETS_URL . 'events-admin-page.css',
565
-            [],
566
-            EVENT_ESPRESSO_VERSION
567
-        );
568
-        wp_register_style('ee-cat-admin', EVENTS_ASSETS_URL . 'ee-cat-admin.css', [], EVENT_ESPRESSO_VERSION);
569
-        wp_enqueue_style('events-admin-css');
570
-        wp_enqueue_style('ee-cat-admin');
571
-        // todo note: we also need to load_scripts_styles per view (i.e. default/view_report/event_details
572
-        // registers for all views
573
-        // scripts
574
-        wp_register_script(
575
-            'event_editor_js',
576
-            EVENTS_ASSETS_URL . 'event_editor.js',
577
-            ['ee_admin_js', 'jquery-ui-slider', 'jquery-ui-timepicker-addon'],
578
-            EVENT_ESPRESSO_VERSION,
579
-            true
580
-        );
581
-    }
582
-
583
-
584
-    /**
585
-     * Enqueuing scripts and styles specific to this view
586
-     */
587
-    public function load_scripts_styles_create_new()
588
-    {
589
-        $this->load_scripts_styles_edit();
590
-    }
591
-
592
-
593
-    /**
594
-     * Enqueuing scripts and styles specific to this view
595
-     */
596
-    public function load_scripts_styles_edit()
597
-    {
598
-        // styles
599
-        wp_enqueue_style('espresso-ui-theme');
600
-        wp_register_style(
601
-            'event-editor-css',
602
-            EVENTS_ASSETS_URL . 'event-editor.css',
603
-            ['ee-admin-css'],
604
-            EVENT_ESPRESSO_VERSION
605
-        );
606
-        wp_enqueue_style('event-editor-css');
607
-        // scripts
608
-        wp_register_script(
609
-            'event-datetime-metabox',
610
-            EVENTS_ASSETS_URL . 'event-datetime-metabox.js',
611
-            ['event_editor_js', 'ee-datepicker'],
612
-            EVENT_ESPRESSO_VERSION
613
-        );
614
-        wp_enqueue_script('event-datetime-metabox');
615
-    }
616
-
617
-
618
-    /**
619
-     * Populating the _views property for the category list table view.
620
-     */
621
-    protected function _set_list_table_views_category_list()
622
-    {
623
-        $this->_views = [
624
-            'all' => [
625
-                'slug'        => 'all',
626
-                'label'       => esc_html__('All', 'event_espresso'),
627
-                'count'       => 0,
628
-                'bulk_action' => [
629
-                    'delete_categories' => esc_html__('Delete Permanently', 'event_espresso'),
630
-                ],
631
-            ],
632
-        ];
633
-    }
634
-
635
-
636
-    /**
637
-     * For adding anything that fires on the admin_init hook for any route within this admin page group.
638
-     */
639
-    public function admin_init()
640
-    {
641
-        EE_Registry::$i18n_js_strings['image_confirm'] = esc_html__(
642
-            'Do you really want to delete this image? Please remember to update your event to complete the removal.',
643
-            'event_espresso'
644
-        );
645
-    }
646
-
647
-
648
-    /**
649
-     * For adding anything that should be triggered on the admin_notices hook for any route within this admin page
650
-     * group.
651
-     */
652
-    public function admin_notices()
653
-    {
654
-    }
655
-
656
-
657
-    /**
658
-     * For adding anything that should be triggered on the `admin_print_footer_scripts` hook for any route within
659
-     * this admin page group.
660
-     */
661
-    public function admin_footer_scripts()
662
-    {
663
-    }
664
-
665
-
666
-    /**
667
-     * Call this function to verify if an event is public and has tickets for sale.  If it does, then we need to show a
668
-     * warning (via EE_Error::add_error());
669
-     *
670
-     * @param EE_Event $event Event object
671
-     * @param string   $req_type
672
-     * @return void
673
-     * @throws EE_Error
674
-     * @throws ReflectionException
675
-     */
676
-    public function verify_event_edit($event = null, $req_type = '')
677
-    {
678
-        // don't need to do this when processing
679
-        if (! empty($req_type)) {
680
-            return;
681
-        }
682
-        // no event?
683
-        if (empty($event)) {
684
-            // set event
685
-            $event = $this->_cpt_model_obj;
686
-        }
687
-        // STILL no event?
688
-        if (! $event instanceof EE_Event) {
689
-            return;
690
-        }
691
-        $orig_status = $event->status();
692
-        // first check if event is active.
693
-        if (
694
-            $orig_status === EEM_Event::cancelled
695
-            || $orig_status === EEM_Event::postponed
696
-            || $event->is_expired()
697
-            || $event->is_inactive()
698
-        ) {
699
-            return;
700
-        }
701
-        // made it here so it IS active... next check that any of the tickets are sold.
702
-        if ($event->is_sold_out(true)) {
703
-            if ($orig_status !== EEM_Event::sold_out && $event->status() !== $orig_status) {
704
-                EE_Error::add_attention(
705
-                    sprintf(
706
-                        esc_html__(
707
-                            'Please note that the Event Status has automatically been changed to %s because there are no more spaces available for this event.  However, this change is not permanent until you update the event.  You can change the status back to something else before updating if you wish.',
708
-                            'event_espresso'
709
-                        ),
710
-                        EEH_Template::pretty_status(EEM_Event::sold_out, false, 'sentence')
711
-                    )
712
-                );
713
-            }
714
-            return;
715
-        } elseif ($orig_status === EEM_Event::sold_out) {
716
-            EE_Error::add_attention(
717
-                sprintf(
718
-                    esc_html__(
719
-                        'Please note that the Event Status has automatically been changed to %s because more spaces have become available for this event, most likely due to abandoned transactions freeing up reserved tickets.  However, this change is not permanent until you update the event. If you wish, you can change the status back to something else before updating.',
720
-                        'event_espresso'
721
-                    ),
722
-                    EEH_Template::pretty_status($event->status(), false, 'sentence')
723
-                )
724
-            );
725
-        }
726
-        // now we need to determine if the event has any tickets on sale.  If not then we dont' show the error
727
-        if (! $event->tickets_on_sale()) {
728
-            return;
729
-        }
730
-        // made it here so show warning
731
-        $this->_edit_event_warning();
732
-    }
733
-
734
-
735
-    /**
736
-     * This is the text used for when an event is being edited that is public and has tickets for sale.
737
-     * When needed, hook this into a EE_Error::add_error() notice.
738
-     *
739
-     * @access protected
740
-     * @return void
741
-     */
742
-    protected function _edit_event_warning()
743
-    {
744
-        // we don't want to add warnings during these requests
745
-        if ($this->request->getRequestParam('action') === 'editpost') {
746
-            return;
747
-        }
748
-        EE_Error::add_attention(
749
-            sprintf(
750
-                esc_html__(
751
-                    'Your event is open for registration. Making changes may disrupt any transactions in progress. %sLearn more%s',
752
-                    'event_espresso'
753
-                ),
754
-                '<a class="espresso-help-tab-lnk">',
755
-                '</a>'
756
-            )
757
-        );
758
-    }
759
-
760
-
761
-    /**
762
-     * When a user is creating a new event, notify them if they haven't set their timezone.
763
-     * Otherwise, do the normal logic
764
-     *
765
-     * @return void
766
-     * @throws EE_Error
767
-     */
768
-    protected function _create_new_cpt_item()
769
-    {
770
-        $has_timezone_string = get_option('timezone_string');
771
-        // only nag them about setting their timezone if it's their first event, and they haven't already done it
772
-        if (! $has_timezone_string && ! EEM_Event::instance()->exists([])) {
773
-            EE_Error::add_attention(
774
-                sprintf(
775
-                    esc_html__(
776
-                        'Your website\'s timezone is currently set to a UTC offset. We recommend updating your timezone to a city or region near you before you create an event. Change your timezone now:%1$s%2$s%3$sChange Timezone%4$s',
777
-                        'event_espresso'
778
-                    ),
779
-                    '<br>',
780
-                    '<select id="timezone_string" name="timezone_string" aria-describedby="timezone-description">'
781
-                    . EEH_DTT_Helper::wp_timezone_choice('', EEH_DTT_Helper::get_user_locale())
782
-                    . '</select>',
783
-                    '<button class="button button-secondary timezone-submit">',
784
-                    '</button><span class="spinner"></span>'
785
-                ),
786
-                __FILE__,
787
-                __FUNCTION__,
788
-                __LINE__
789
-            );
790
-        }
791
-        parent::_create_new_cpt_item();
792
-    }
793
-
794
-
795
-    /**
796
-     * Sets the _views property for the default route in this admin page group.
797
-     */
798
-    protected function _set_list_table_views_default()
799
-    {
800
-        $this->_views = [
801
-            'all'   => [
802
-                'slug'        => 'all',
803
-                'label'       => esc_html__('View All Events', 'event_espresso'),
804
-                'count'       => 0,
805
-                'bulk_action' => [
806
-                    'trash_events' => esc_html__('Move to Trash', 'event_espresso'),
807
-                ],
808
-            ],
809
-            'draft' => [
810
-                'slug'        => 'draft',
811
-                'label'       => esc_html__('Draft', 'event_espresso'),
812
-                'count'       => 0,
813
-                'bulk_action' => [
814
-                    'trash_events' => esc_html__('Move to Trash', 'event_espresso'),
815
-                ],
816
-            ],
817
-        ];
818
-        if (EE_Registry::instance()->CAP->current_user_can('ee_delete_events', 'espresso_events_trash_events')) {
819
-            $this->_views['trash'] = [
820
-                'slug'        => 'trash',
821
-                'label'       => esc_html__('Trash', 'event_espresso'),
822
-                'count'       => 0,
823
-                'bulk_action' => [
824
-                    'restore_events' => esc_html__('Restore From Trash', 'event_espresso'),
825
-                    'delete_events'  => esc_html__('Delete Permanently', 'event_espresso'),
826
-                ],
827
-            ];
828
-        }
829
-    }
830
-
831
-
832
-    /**
833
-     * Provides the legend item array for the default list table view.
834
-     *
835
-     * @return array
836
-     * @throws EE_Error
837
-     * @throws EE_Error
838
-     */
839
-    protected function _event_legend_items()
840
-    {
841
-        $items    = [
842
-            'view_details'   => [
843
-                'class' => 'dashicons dashicons-search',
844
-                'desc'  => esc_html__('View Event', 'event_espresso'),
845
-            ],
846
-            'edit_event'     => [
847
-                'class' => 'ee-icon ee-icon-calendar-edit',
848
-                'desc'  => esc_html__('Edit Event Details', 'event_espresso'),
849
-            ],
850
-            'view_attendees' => [
851
-                'class' => 'dashicons dashicons-groups',
852
-                'desc'  => esc_html__('View Registrations for Event', 'event_espresso'),
853
-            ],
854
-        ];
855
-        $items    = apply_filters('FHEE__Events_Admin_Page___event_legend_items__items', $items);
856
-        $statuses = [
857
-            'sold_out_status'  => [
858
-                'class' => 'ee-status-legend ee-status-legend-' . EE_Datetime::sold_out,
859
-                'desc'  => EEH_Template::pretty_status(EE_Datetime::sold_out, false, 'sentence'),
860
-            ],
861
-            'active_status'    => [
862
-                'class' => 'ee-status-legend ee-status-legend-' . EE_Datetime::active,
863
-                'desc'  => EEH_Template::pretty_status(EE_Datetime::active, false, 'sentence'),
864
-            ],
865
-            'upcoming_status'  => [
866
-                'class' => 'ee-status-legend ee-status-legend-' . EE_Datetime::upcoming,
867
-                'desc'  => EEH_Template::pretty_status(EE_Datetime::upcoming, false, 'sentence'),
868
-            ],
869
-            'postponed_status' => [
870
-                'class' => 'ee-status-legend ee-status-legend-' . EE_Datetime::postponed,
871
-                'desc'  => EEH_Template::pretty_status(EE_Datetime::postponed, false, 'sentence'),
872
-            ],
873
-            'cancelled_status' => [
874
-                'class' => 'ee-status-legend ee-status-legend-' . EE_Datetime::cancelled,
875
-                'desc'  => EEH_Template::pretty_status(EE_Datetime::cancelled, false, 'sentence'),
876
-            ],
877
-            'expired_status'   => [
878
-                'class' => 'ee-status-legend ee-status-legend-' . EE_Datetime::expired,
879
-                'desc'  => EEH_Template::pretty_status(EE_Datetime::expired, false, 'sentence'),
880
-            ],
881
-            'inactive_status'  => [
882
-                'class' => 'ee-status-legend ee-status-legend-' . EE_Datetime::inactive,
883
-                'desc'  => EEH_Template::pretty_status(EE_Datetime::inactive, false, 'sentence'),
884
-            ],
885
-        ];
886
-        $statuses = apply_filters('FHEE__Events_Admin_Page__event_legend_items__statuses', $statuses);
887
-        return array_merge($items, $statuses);
888
-    }
889
-
890
-
891
-    /**
892
-     * @return EEM_Event
893
-     * @throws EE_Error
894
-     * @throws ReflectionException
895
-     */
896
-    private function _event_model()
897
-    {
898
-        if (! $this->_event_model instanceof EEM_Event) {
899
-            $this->_event_model = EE_Registry::instance()->load_model('Event');
900
-        }
901
-        return $this->_event_model;
902
-    }
903
-
904
-
905
-    /**
906
-     * Adds extra buttons to the WP CPT permalink field row.
907
-     * Method is called from parent and is hooked into the wp 'get_sample_permalink_html' filter.
908
-     *
909
-     * @param string $return    the current html
910
-     * @param int    $id        the post id for the page
911
-     * @param string $new_title What the title is
912
-     * @param string $new_slug  what the slug is
913
-     * @return string            The new html string for the permalink area
914
-     */
915
-    public function extra_permalink_field_buttons($return, $id, $new_title, $new_slug)
916
-    {
917
-        // make sure this is only when editing
918
-        if (! empty($id)) {
919
-            $post   = get_post($id);
920
-            $return .= '<a class="button button-small" onclick="prompt(\'Shortcode:\', jQuery(\'#shortcode\').val()); return false;" href="#"  tabindex="-1">'
921
-                       . esc_html__('Shortcode', 'event_espresso')
922
-                       . '</a> ';
923
-            $return .= '<input id="shortcode" type="hidden" value="[ESPRESSO_TICKET_SELECTOR event_id='
924
-                       . $post->ID
925
-                       . ']">';
926
-        }
927
-        return $return;
928
-    }
929
-
930
-
931
-    /**
932
-     * _events_overview_list_table
933
-     * This contains the logic for showing the events_overview list
934
-     *
935
-     * @access protected
936
-     * @return void
937
-     * @throws EE_Error
938
-     */
939
-    protected function _events_overview_list_table()
940
-    {
941
-        do_action('AHEE_log', __FILE__, __FUNCTION__, '');
942
-        $this->_template_args['after_list_table']                           =
943
-            ! empty($this->_template_args['after_list_table'])
944
-                ? (array) $this->_template_args['after_list_table']
945
-                : [];
946
-        $this->_template_args['after_list_table']['view_event_list_button'] = EEH_HTML::br()
947
-            . EEH_Template::get_button_or_link(
948
-                get_post_type_archive_link('espresso_events'),
949
-                esc_html__("View Event Archive Page", "event_espresso"),
950
-                'button'
951
-            );
952
-        $this->_template_args['after_list_table']['legend']                 = $this->_display_legend(
953
-            $this->_event_legend_items()
954
-        );
955
-        $this->_admin_page_title                                            .= ' ' . $this->get_action_link_or_button(
956
-            'create_new',
957
-            'add',
958
-            [],
959
-            'add-new-h2'
960
-        );
961
-        $this->display_admin_list_table_page_with_no_sidebar();
962
-    }
963
-
964
-
965
-    /**
966
-     * this allows for extra misc actions in the default WP publish box
967
-     *
968
-     * @return void
969
-     * @throws EE_Error
970
-     * @throws ReflectionException
971
-     */
972
-    public function extra_misc_actions_publish_box()
973
-    {
974
-        $this->_generate_publish_box_extra_content();
975
-    }
976
-
977
-
978
-    /**
979
-     * This is hooked into the WordPress do_action('save_post') hook and runs after the custom post type has been
980
-     * saved.
981
-     * Typically you would use this to save any additional data.
982
-     * Keep in mind also that "save_post" runs on EVERY post update to the database.
983
-     * ALSO very important.  When a post transitions from scheduled to published,
984
-     * the save_post action is fired but you will NOT have any _POST data containing any extra info you may have from
985
-     * other meta saves. So MAKE sure that you handle this accordingly.
986
-     *
987
-     * @access protected
988
-     * @abstract
989
-     * @param string $post_id The ID of the cpt that was saved (so you can link relationally)
990
-     * @param WP_Post $post    The post object of the cpt that was saved.
991
-     * @return void
992
-     * @throws EE_Error
993
-     * @throws ReflectionException
994
-     */
995
-    protected function _insert_update_cpt_item($post_id, $post)
996
-    {
997
-        if ($post instanceof WP_Post && $post->post_type !== 'espresso_events') {
998
-            // get out we're not processing an event save.
999
-            return;
1000
-        }
1001
-
1002
-        $event_values = [
1003
-            'EVT_display_desc'                => $this->request->getRequestParam('display_desc', false, 'bool'),
1004
-            'EVT_display_ticket_selector'     => $this->request->getRequestParam(
1005
-                'display_ticket_selector',
1006
-                false,
1007
-                'bool'
1008
-            ),
1009
-            'EVT_additional_limit'            => min(
1010
-                apply_filters('FHEE__EE_Events_Admin__insert_update_cpt_item__EVT_additional_limit_max', 255),
1011
-                $this->request->getRequestParam('additional_limit', null, 'int')
1012
-            ),
1013
-            'EVT_default_registration_status' => $this->request->getRequestParam(
1014
-                'EVT_default_registration_status',
1015
-                EE_Registry::instance()->CFG->registration->default_STS_ID
1016
-            ),
1017
-
1018
-            'EVT_member_only'     => $this->request->getRequestParam('member_only', false, 'bool'),
1019
-            'EVT_allow_overflow'  => $this->request->getRequestParam('EVT_allow_overflow', false, 'bool'),
1020
-            'EVT_timezone_string' => $this->request->getRequestParam('timezone_string'),
1021
-            'EVT_external_URL'    => $this->request->getRequestParam('externalURL'),
1022
-            'EVT_phone'           => $this->request->getRequestParam('event_phone'),
1023
-        ];
1024
-        // update event
1025
-        $success = $this->_event_model()->update_by_ID($event_values, $post_id);
1026
-        // get event_object for other metaboxes...
1027
-        // though it would seem to make sense to just use $this->_event_model()->get_one_by_ID( $post_id )..
1028
-        // i have to setup where conditions to override the filters in the model
1029
-        // that filter out autodraft and inherit statuses so we GET the inherit id!
1030
-        $event = $this->_event_model()->get_one(
1031
-            [
1032
-                [
1033
-                    $this->_event_model()->primary_key_name() => $post_id,
1034
-                    'OR'                                      => [
1035
-                        'status'   => $post->post_status,
1036
-                        // if trying to "Publish" a sold out event, it's status will get switched back to "sold_out" in the db,
1037
-                        // but the returned object here has a status of "publish", so use the original post status as well
1038
-                        'status*1' => $this->request->getRequestParam('original_post_status'),
1039
-                    ],
1040
-                ],
1041
-            ]
1042
-        );
1043
-        // the following are default callbacks for event attachment updates that can be overridden by caffeinated functionality and/or addons.
1044
-        $event_update_callbacks = apply_filters(
1045
-            'FHEE__Events_Admin_Page___insert_update_cpt_item__event_update_callbacks',
1046
-            [
1047
-                [$this, '_default_venue_update'],
1048
-                [$this, '_default_tickets_update'],
1049
-            ]
1050
-        );
1051
-        $att_success            = true;
1052
-        foreach ($event_update_callbacks as $e_callback) {
1053
-            $_success = is_callable($e_callback)
1054
-                ? call_user_func($e_callback, $event, $this->request->requestParams())
1055
-                : false;
1056
-            // if ANY of these updates fail then we want the appropriate global error message
1057
-            $att_success = ! $att_success ? $att_success : $_success;
1058
-        }
1059
-        // any errors?
1060
-        if ($success && false === $att_success) {
1061
-            EE_Error::add_error(
1062
-                esc_html__(
1063
-                    'Event Details saved successfully but something went wrong with saving attachments.',
1064
-                    'event_espresso'
1065
-                ),
1066
-                __FILE__,
1067
-                __FUNCTION__,
1068
-                __LINE__
1069
-            );
1070
-        } elseif ($success === false) {
1071
-            EE_Error::add_error(
1072
-                esc_html__('Event Details did not save successfully.', 'event_espresso'),
1073
-                __FILE__,
1074
-                __FUNCTION__,
1075
-                __LINE__
1076
-            );
1077
-        }
1078
-    }
1079
-
1080
-
1081
-    /**
1082
-     * @param int $post_id
1083
-     * @param int $revision_id
1084
-     * @throws EE_Error
1085
-     * @throws EE_Error
1086
-     * @throws ReflectionException
1087
-     * @see parent::restore_item()
1088
-     */
1089
-    protected function _restore_cpt_item($post_id, $revision_id)
1090
-    {
1091
-        // copy existing event meta to new post
1092
-        $post_evt = $this->_event_model()->get_one_by_ID($post_id);
1093
-        if ($post_evt instanceof EE_Event) {
1094
-            // meta revision restore
1095
-            $post_evt->restore_revision($revision_id);
1096
-            // related objs restore
1097
-            $post_evt->restore_revision($revision_id, ['Venue', 'Datetime', 'Price']);
1098
-        }
1099
-    }
1100
-
1101
-
1102
-    /**
1103
-     * Attach the venue to the Event
1104
-     *
1105
-     * @param EE_Event $event Event Object to add the venue to
1106
-     * @param array    $data  The request data from the form
1107
-     * @return bool           Success or fail.
1108
-     * @throws EE_Error
1109
-     * @throws ReflectionException
1110
-     */
1111
-    protected function _default_venue_update(EE_Event $event, $data)
1112
-    {
1113
-        require_once(EE_MODELS . 'EEM_Venue.model.php');
1114
-        $venue_model = EE_Registry::instance()->load_model('Venue');
1115
-        $venue_id    = ! empty($data['venue_id']) ? $data['venue_id'] : null;
1116
-        // very important.  If we don't have a venue name...
1117
-        // then we'll get out because not necessary to create empty venue
1118
-        if (empty($data['venue_title'])) {
1119
-            return false;
1120
-        }
1121
-        $venue_array = [
1122
-            'VNU_wp_user'         => $event->get('EVT_wp_user'),
1123
-            'VNU_name'            => $data['venue_title'],
1124
-            'VNU_desc'            => ! empty($data['venue_description']) ? $data['venue_description'] : null,
1125
-            'VNU_identifier'      => ! empty($data['venue_identifier']) ? $data['venue_identifier'] : null,
1126
-            'VNU_short_desc'      => ! empty($data['venue_short_description'])
1127
-                ? $data['venue_short_description']
1128
-                : null,
1129
-            'VNU_address'         => ! empty($data['address']) ? $data['address'] : null,
1130
-            'VNU_address2'        => ! empty($data['address2']) ? $data['address2'] : null,
1131
-            'VNU_city'            => ! empty($data['city']) ? $data['city'] : null,
1132
-            'STA_ID'              => ! empty($data['state']) ? $data['state'] : null,
1133
-            'CNT_ISO'             => ! empty($data['countries']) ? $data['countries'] : null,
1134
-            'VNU_zip'             => ! empty($data['zip']) ? $data['zip'] : null,
1135
-            'VNU_phone'           => ! empty($data['venue_phone']) ? $data['venue_phone'] : null,
1136
-            'VNU_capacity'        => ! empty($data['venue_capacity']) ? $data['venue_capacity'] : null,
1137
-            'VNU_url'             => ! empty($data['venue_url']) ? $data['venue_url'] : null,
1138
-            'VNU_virtual_phone'   => ! empty($data['virtual_phone']) ? $data['virtual_phone'] : null,
1139
-            'VNU_virtual_url'     => ! empty($data['virtual_url']) ? $data['virtual_url'] : null,
1140
-            'VNU_enable_for_gmap' => isset($data['enable_for_gmap']) ? 1 : 0,
1141
-            'status'              => 'publish',
1142
-        ];
1143
-        // if we've got the venue_id then we're just updating the existing venue so let's do that and then get out.
1144
-        if (! empty($venue_id)) {
1145
-            $update_where  = [$venue_model->primary_key_name() => $venue_id];
1146
-            $rows_affected = $venue_model->update($venue_array, [$update_where]);
1147
-            // we've gotta make sure that the venue is always attached to a revision.. add_relation_to should take care of making sure that the relation is already present.
1148
-            $event->_add_relation_to($venue_id, 'Venue');
1149
-            return $rows_affected > 0;
1150
-        }
1151
-        // we insert the venue
1152
-        $venue_id = $venue_model->insert($venue_array);
1153
-        $event->_add_relation_to($venue_id, 'Venue');
1154
-        return ! empty($venue_id);
1155
-        // when we have the ancestor come in it's already been handled by the revision save.
1156
-    }
1157
-
1158
-
1159
-    /**
1160
-     * Handles saving everything related to Tickets (datetimes, tickets, prices)
1161
-     *
1162
-     * @param EE_Event $event The Event object we're attaching data to
1163
-     * @param array    $data  The request data from the form
1164
-     * @return array
1165
-     * @throws EE_Error
1166
-     * @throws ReflectionException
1167
-     * @throws Exception
1168
-     */
1169
-    protected function _default_tickets_update(EE_Event $event, $data)
1170
-    {
1171
-        $datetime       = null;
1172
-        $saved_tickets  = [];
1173
-        $event_timezone = $event->get_timezone();
1174
-        $date_formats   = ['Y-m-d', 'h:i a'];
1175
-        foreach ($data['edit_event_datetimes'] as $row => $datetime_data) {
1176
-            // trim all values to ensure any excess whitespace is removed.
1177
-            $datetime_data                = array_map('trim', $datetime_data);
1178
-            $datetime_data['DTT_EVT_end'] =
1179
-                isset($datetime_data['DTT_EVT_end']) && ! empty($datetime_data['DTT_EVT_end'])
1180
-                    ? $datetime_data['DTT_EVT_end']
1181
-                    : $datetime_data['DTT_EVT_start'];
1182
-            $datetime_values              = [
1183
-                'DTT_ID'        => ! empty($datetime_data['DTT_ID']) ? $datetime_data['DTT_ID'] : null,
1184
-                'DTT_EVT_start' => $datetime_data['DTT_EVT_start'],
1185
-                'DTT_EVT_end'   => $datetime_data['DTT_EVT_end'],
1186
-                'DTT_reg_limit' => empty($datetime_data['DTT_reg_limit']) ? EE_INF : $datetime_data['DTT_reg_limit'],
1187
-                'DTT_order'     => $row,
1188
-            ];
1189
-            // if we have an id then let's get existing object first and then set the new values.
1190
-            //  Otherwise we instantiate a new object for save.
1191
-            if (! empty($datetime_data['DTT_ID'])) {
1192
-                $datetime = EEM_Datetime::instance($event_timezone)->get_one_by_ID($datetime_data['DTT_ID']);
1193
-                if (! $datetime instanceof EE_Datetime) {
1194
-                    throw new RuntimeException(
1195
-                        sprintf(
1196
-                            esc_html__(
1197
-                                'Something went wrong! A valid Datetime could not be retrieved from the database using the supplied ID: %1$d',
1198
-                                'event_espresso'
1199
-                            ),
1200
-                            $datetime_data['DTT_ID']
1201
-                        )
1202
-                    );
1203
-                }
1204
-                $datetime->set_date_format($date_formats[0]);
1205
-                $datetime->set_time_format($date_formats[1]);
1206
-                foreach ($datetime_values as $field => $value) {
1207
-                    $datetime->set($field, $value);
1208
-                }
1209
-            } else {
1210
-                $datetime = EE_Datetime::new_instance($datetime_values, $event_timezone, $date_formats);
1211
-            }
1212
-            if (! $datetime instanceof EE_Datetime) {
1213
-                throw new RuntimeException(
1214
-                    sprintf(
1215
-                        esc_html__(
1216
-                            'Something went wrong! A valid Datetime could not be generated or retrieved using the supplied data: %1$s',
1217
-                            'event_espresso'
1218
-                        ),
1219
-                        print_r($datetime_values, true)
1220
-                    )
1221
-                );
1222
-            }
1223
-            // before going any further make sure our dates are setup correctly
1224
-            // so that the end date is always equal or greater than the start date.
1225
-            if ($datetime->get_raw('DTT_EVT_start') > $datetime->get_raw('DTT_EVT_end')) {
1226
-                $datetime->set('DTT_EVT_end', $datetime->get('DTT_EVT_start'));
1227
-                $datetime = EEH_DTT_Helper::date_time_add($datetime, 'DTT_EVT_end', 'days');
1228
-            }
1229
-            $datetime->save();
1230
-            $event->_add_relation_to($datetime, 'Datetime');
1231
-        }
1232
-        // no datetimes get deleted so we don't do any of that logic here.
1233
-        // update tickets next
1234
-        $old_tickets = isset($data['ticket_IDs']) ? explode(',', $data['ticket_IDs']) : [];
1235
-
1236
-        // set up some default start and end dates in case those are not present in the incoming data
1237
-        $default_start_date = new DateTime('now', new DateTimeZone($event->get_timezone()));
1238
-        $default_start_date = $default_start_date->format($date_formats[0] . ' ' . $date_formats[1]);
1239
-        // use the start date of the first datetime for the end date
1240
-        $first_datetime   = $event->first_datetime();
1241
-        $default_end_date = $first_datetime->start_date_and_time($date_formats[0], $date_formats[1]);
1242
-
1243
-        // now process the incoming data
1244
-        foreach ($data['edit_tickets'] as $row => $ticket_data) {
1245
-            $update_prices = false;
1246
-            $ticket_price  = isset($data['edit_prices'][ $row ][1]['PRC_amount'])
1247
-                ? $data['edit_prices'][ $row ][1]['PRC_amount']
1248
-                : 0;
1249
-            // trim inputs to ensure any excess whitespace is removed.
1250
-            $ticket_data   = array_map('trim', $ticket_data);
1251
-            $ticket_values = [
1252
-                'TKT_ID'          => ! empty($ticket_data['TKT_ID']) ? $ticket_data['TKT_ID'] : null,
1253
-                'TTM_ID'          => ! empty($ticket_data['TTM_ID']) ? $ticket_data['TTM_ID'] : 0,
1254
-                'TKT_name'        => ! empty($ticket_data['TKT_name']) ? $ticket_data['TKT_name'] : '',
1255
-                'TKT_description' => ! empty($ticket_data['TKT_description']) ? $ticket_data['TKT_description'] : '',
1256
-                'TKT_start_date'  => ! empty($ticket_data['TKT_start_date'])
1257
-                    ? $ticket_data['TKT_start_date']
1258
-                    : $default_start_date,
1259
-                'TKT_end_date'    => ! empty($ticket_data['TKT_end_date'])
1260
-                    ? $ticket_data['TKT_end_date']
1261
-                    : $default_end_date,
1262
-                'TKT_qty'         => ! empty($ticket_data['TKT_qty'])
1263
-                                     || (isset($ticket_data['TKT_qty']) && (int) $ticket_data['TKT_qty'] === 0)
1264
-                    ? $ticket_data['TKT_qty']
1265
-                    : EE_INF,
1266
-                'TKT_uses'        => ! empty($ticket_data['TKT_uses'])
1267
-                                     || (isset($ticket_data['TKT_uses']) && (int) $ticket_data['TKT_uses'] === 0)
1268
-                    ? $ticket_data['TKT_uses']
1269
-                    : EE_INF,
1270
-                'TKT_min'         => ! empty($ticket_data['TKT_min']) ? $ticket_data['TKT_min'] : 0,
1271
-                'TKT_max'         => ! empty($ticket_data['TKT_max']) ? $ticket_data['TKT_max'] : EE_INF,
1272
-                'TKT_order'       => isset($ticket_data['TKT_order']) ? $ticket_data['TKT_order'] : $row,
1273
-                'TKT_price'       => $ticket_price,
1274
-                'TKT_row'         => $row,
1275
-            ];
1276
-            // if this is a default ticket, then we need to set the TKT_ID to 0 and update accordingly,
1277
-            // which means in turn that the prices will become new prices as well.
1278
-            if (isset($ticket_data['TKT_is_default']) && $ticket_data['TKT_is_default']) {
1279
-                $ticket_values['TKT_ID']         = 0;
1280
-                $ticket_values['TKT_is_default'] = 0;
1281
-                $update_prices                   = true;
1282
-            }
1283
-            // if we have a TKT_ID then we need to get that existing TKT_obj and update it
1284
-            // we actually do our saves ahead of adding any relations because its entirely possible that this
1285
-            // ticket didn't get removed or added to any datetime in the session but DID have it's items modified.
1286
-            // keep in mind that if the ticket has been sold (and we have changed pricing information),
1287
-            // then we won't be updating the tkt but instead a new tkt will be created and the old one archived.
1288
-            if (! empty($ticket_data['TKT_ID'])) {
1289
-                $existing_ticket = EEM_Ticket::instance($event_timezone)->get_one_by_ID($ticket_data['TKT_ID']);
1290
-                if (! $existing_ticket instanceof EE_Ticket) {
1291
-                    throw new RuntimeException(
1292
-                        sprintf(
1293
-                            esc_html__(
1294
-                                'Something went wrong! A valid Ticket could not be retrieved from the database using the supplied ID: %1$d',
1295
-                                'event_espresso'
1296
-                            ),
1297
-                            $ticket_data['TKT_ID']
1298
-                        )
1299
-                    );
1300
-                }
1301
-                $ticket_sold = $existing_ticket->count_related(
1302
-                    'Registration',
1303
-                    [
1304
-                        [
1305
-                            'STS_ID' => [
1306
-                                'NOT IN',
1307
-                                [EEM_Registration::status_id_incomplete],
1308
-                            ],
1309
-                        ],
1310
-                    ]
1311
-                ) > 0;
1312
-                // let's just check the total price for the existing ticket and determine if it matches the new total price.
1313
-                // if they are different then we create a new ticket (if $ticket_sold)
1314
-                // if they aren't different then we go ahead and modify existing ticket.
1315
-                $create_new_ticket = $ticket_sold
1316
-                                     && $ticket_price !== $existing_ticket->price()
1317
-                                     && ! $existing_ticket->deleted();
1318
-                $existing_ticket->set_date_format($date_formats[0]);
1319
-                $existing_ticket->set_time_format($date_formats[1]);
1320
-                // set new values
1321
-                foreach ($ticket_values as $field => $value) {
1322
-                    if ($field == 'TKT_qty') {
1323
-                        $existing_ticket->set_qty($value);
1324
-                    } elseif ($field == 'TKT_price') {
1325
-                        $existing_ticket->set('TKT_price', $ticket_price);
1326
-                    } else {
1327
-                        $existing_ticket->set($field, $value);
1328
-                    }
1329
-                }
1330
-                $ticket = $existing_ticket;
1331
-                // if $create_new_ticket is false then we can safely update the existing ticket.
1332
-                //  Otherwise we have to create a new ticket.
1333
-                if ($create_new_ticket) {
1334
-                    // archive the old ticket first
1335
-                    $existing_ticket->set('TKT_deleted', 1);
1336
-                    $existing_ticket->save();
1337
-                    // make sure this ticket is still recorded in our $saved_tickets
1338
-                    // so we don't run it through the regular trash routine.
1339
-                    $saved_tickets[ $existing_ticket->ID() ] = $existing_ticket;
1340
-                    // create new ticket that's a copy of the existing except,
1341
-                    // (a new id of course and not archived) AND has the new TKT_price associated with it.
1342
-                    $new_ticket = clone $existing_ticket;
1343
-                    $new_ticket->set('TKT_ID', 0);
1344
-                    $new_ticket->set('TKT_deleted', 0);
1345
-                    $new_ticket->set('TKT_sold', 0);
1346
-                    // now we need to make sure that $new prices are created as well and attached to new ticket.
1347
-                    $update_prices = true;
1348
-                    $ticket        = $new_ticket;
1349
-                }
1350
-            } else {
1351
-                // no TKT_id so a new ticket
1352
-                $ticket_values['TKT_price'] = $ticket_price;
1353
-                $ticket                     = EE_Ticket::new_instance($ticket_values, $event_timezone, $date_formats);
1354
-                $update_prices              = true;
1355
-            }
1356
-            if (! $ticket instanceof EE_Ticket) {
1357
-                throw new RuntimeException(
1358
-                    sprintf(
1359
-                        esc_html__(
1360
-                            'Something went wrong! A valid Ticket could not be generated or retrieved using the supplied data: %1$s',
1361
-                            'event_espresso'
1362
-                        ),
1363
-                        print_r($ticket_values, true)
1364
-                    )
1365
-                );
1366
-            }
1367
-            // cap ticket qty by datetime reg limits
1368
-            $ticket->set_qty(min($ticket->qty(), $ticket->qty('reg_limit')));
1369
-            // update ticket.
1370
-            $ticket->save();
1371
-            // before going any further make sure our dates are setup correctly
1372
-            // so that the end date is always equal or greater than the start date.
1373
-            if ($ticket->get_raw('TKT_start_date') > $ticket->get_raw('TKT_end_date')) {
1374
-                $ticket->set('TKT_end_date', $ticket->get('TKT_start_date'));
1375
-                $ticket = EEH_DTT_Helper::date_time_add($ticket, 'TKT_end_date', 'days');
1376
-                $ticket->save();
1377
-            }
1378
-            // initially let's add the ticket to the datetime
1379
-            $datetime->_add_relation_to($ticket, 'Ticket');
1380
-            $saved_tickets[ $ticket->ID() ] = $ticket;
1381
-            // add prices to ticket
1382
-            $prices_data = isset($data['edit_prices'][ $row ]) && is_array($data['edit_prices'][ $row ])
1383
-                ? $data['edit_prices'][ $row ]
1384
-                : [];
1385
-            $this->_add_prices_to_ticket($prices_data, $ticket, $update_prices);
1386
-        }
1387
-        // however now we need to handle permanently deleting tickets via the ui.
1388
-        // Keep in mind that the ui does not allow deleting/archiving tickets that have ticket sold.
1389
-        // However, it does allow for deleting tickets that have no tickets sold,
1390
-        // in which case we want to get rid of permanently because there is no need to save in db.
1391
-        $old_tickets     = isset($old_tickets[0]) && $old_tickets[0] == '' ? [] : $old_tickets;
1392
-        $tickets_removed = array_diff($old_tickets, array_keys($saved_tickets));
1393
-        foreach ($tickets_removed as $id) {
1394
-            $id = absint($id);
1395
-            // get the ticket for this id
1396
-            $ticket_to_remove = EEM_Ticket::instance()->get_one_by_ID($id);
1397
-            if (! $ticket_to_remove instanceof EE_Ticket) {
1398
-                continue;
1399
-            }
1400
-            // need to get all the related datetimes on this ticket and remove from every single one of them
1401
-            // (remember this process can ONLY kick off if there are NO tickets sold)
1402
-            $related_datetimes = $ticket_to_remove->get_many_related('Datetime');
1403
-            foreach ($related_datetimes as $related_datetime) {
1404
-                $ticket_to_remove->_remove_relation_to($related_datetime, 'Datetime');
1405
-            }
1406
-            // need to do the same for prices (except these prices can also be deleted because again,
1407
-            // tickets can only be trashed if they don't have any TKTs sold (otherwise they are just archived))
1408
-            $ticket_to_remove->delete_related_permanently('Price');
1409
-            // finally let's delete this ticket
1410
-            // (which should not be blocked at this point b/c we've removed all our relationships)
1411
-            $ticket_to_remove->delete_permanently();
1412
-        }
1413
-        return [$datetime, $saved_tickets];
1414
-    }
1415
-
1416
-
1417
-    /**
1418
-     * This attaches a list of given prices to a ticket.
1419
-     * Note we dont' have to worry about ever removing relationships (or archiving prices) because if there is a change
1420
-     * in price information on a ticket, a new ticket is created anyways so the archived ticket will retain the old
1421
-     * price info and prices are automatically "archived" via the ticket.
1422
-     *
1423
-     * @access  private
1424
-     * @param array     $prices_data Array of prices from the form.
1425
-     * @param EE_Ticket $ticket      EE_Ticket object that prices are being attached to.
1426
-     * @param bool      $new_prices  Whether attach existing incoming prices or create new ones.
1427
-     * @return  void
1428
-     * @throws EE_Error
1429
-     * @throws ReflectionException
1430
-     */
1431
-    private function _add_prices_to_ticket($prices_data, EE_Ticket $ticket, $new_prices = false)
1432
-    {
1433
-        $timezone = $ticket->get_timezone();
1434
-        foreach ($prices_data as $row => $price_data) {
1435
-            $price_values = [
1436
-                'PRC_ID'         => ! empty($price_data['PRC_ID']) ? $price_data['PRC_ID'] : null,
1437
-                'PRT_ID'         => ! empty($price_data['PRT_ID']) ? $price_data['PRT_ID'] : null,
1438
-                'PRC_amount'     => ! empty($price_data['PRC_amount']) ? $price_data['PRC_amount'] : 0,
1439
-                'PRC_name'       => ! empty($price_data['PRC_name']) ? $price_data['PRC_name'] : '',
1440
-                'PRC_desc'       => ! empty($price_data['PRC_desc']) ? $price_data['PRC_desc'] : '',
1441
-                'PRC_is_default' => 0, // make sure prices are NOT set as default from this context
1442
-                'PRC_order'      => $row,
1443
-            ];
1444
-            if ($new_prices || empty($price_values['PRC_ID'])) {
1445
-                $price_values['PRC_ID'] = 0;
1446
-                $price                  = EE_Price::new_instance($price_values, $timezone);
1447
-            } else {
1448
-                $price = EEM_Price::instance($timezone)->get_one_by_ID($price_data['PRC_ID']);
1449
-                // update this price with new values
1450
-                foreach ($price_values as $field => $new_price) {
1451
-                    $price->set($field, $new_price);
1452
-                }
1453
-            }
1454
-            if (! $price instanceof EE_Price) {
1455
-                throw new RuntimeException(
1456
-                    sprintf(
1457
-                        esc_html__(
1458
-                            'Something went wrong! A valid Price could not be generated or retrieved using the supplied data: %1$s',
1459
-                            'event_espresso'
1460
-                        ),
1461
-                        print_r($price_values, true)
1462
-                    )
1463
-                );
1464
-            }
1465
-            $price->save();
1466
-            $ticket->_add_relation_to($price, 'Price');
1467
-        }
1468
-    }
1469
-
1470
-
1471
-    /**
1472
-     * Add in our autosave ajax handlers
1473
-     *
1474
-     */
1475
-    protected function _ee_autosave_create_new()
1476
-    {
1477
-    }
1478
-
1479
-
1480
-    /**
1481
-     * More autosave handlers.
1482
-     */
1483
-    protected function _ee_autosave_edit()
1484
-    {
1485
-        // TEMPORARILY EXITING CAUSE THIS IS A TODO
1486
-    }
1487
-
1488
-
1489
-    /**
1490
-     * @throws EE_Error
1491
-     * @throws ReflectionException
1492
-     */
1493
-    private function _generate_publish_box_extra_content()
1494
-    {
1495
-        // load formatter helper
1496
-        // args for getting related registrations
1497
-        $approved_query_args        = [
1498
-            [
1499
-                'REG_deleted' => 0,
1500
-                'STS_ID'      => EEM_Registration::status_id_approved,
1501
-            ],
1502
-        ];
1503
-        $not_approved_query_args    = [
1504
-            [
1505
-                'REG_deleted' => 0,
1506
-                'STS_ID'      => EEM_Registration::status_id_not_approved,
1507
-            ],
1508
-        ];
1509
-        $pending_payment_query_args = [
1510
-            [
1511
-                'REG_deleted' => 0,
1512
-                'STS_ID'      => EEM_Registration::status_id_pending_payment,
1513
-            ],
1514
-        ];
1515
-        // publish box
1516
-        $publish_box_extra_args = [
1517
-            'view_approved_reg_url'        => add_query_arg(
1518
-                [
1519
-                    'action'      => 'default',
1520
-                    'event_id'    => $this->_cpt_model_obj->ID(),
1521
-                    '_reg_status' => EEM_Registration::status_id_approved,
1522
-                    'use_filters' => true,
1523
-                ],
1524
-                REG_ADMIN_URL
1525
-            ),
1526
-            'view_not_approved_reg_url'    => add_query_arg(
1527
-                [
1528
-                    'action'      => 'default',
1529
-                    'event_id'    => $this->_cpt_model_obj->ID(),
1530
-                    '_reg_status' => EEM_Registration::status_id_not_approved,
1531
-                    'use_filters' => true,
1532
-                ],
1533
-                REG_ADMIN_URL
1534
-            ),
1535
-            'view_pending_payment_reg_url' => add_query_arg(
1536
-                [
1537
-                    'action'      => 'default',
1538
-                    'event_id'    => $this->_cpt_model_obj->ID(),
1539
-                    '_reg_status' => EEM_Registration::status_id_pending_payment,
1540
-                    'use_filters' => true,
1541
-                ],
1542
-                REG_ADMIN_URL
1543
-            ),
1544
-            'approved_regs'                => $this->_cpt_model_obj->count_related(
1545
-                'Registration',
1546
-                $approved_query_args
1547
-            ),
1548
-            'not_approved_regs'            => $this->_cpt_model_obj->count_related(
1549
-                'Registration',
1550
-                $not_approved_query_args
1551
-            ),
1552
-            'pending_payment_regs'         => $this->_cpt_model_obj->count_related(
1553
-                'Registration',
1554
-                $pending_payment_query_args
1555
-            ),
1556
-            'misc_pub_section_class'       => apply_filters(
1557
-                'FHEE_Events_Admin_Page___generate_publish_box_extra_content__misc_pub_section_class',
1558
-                'misc-pub-section'
1559
-            ),
1560
-        ];
1561
-        ob_start();
1562
-        do_action(
1563
-            'AHEE__Events_Admin_Page___generate_publish_box_extra_content__event_editor_overview_add',
1564
-            $this->_cpt_model_obj
1565
-        );
1566
-        $publish_box_extra_args['event_editor_overview_add'] = ob_get_clean();
1567
-        // load template
1568
-        EEH_Template::display_template(
1569
-            EVENTS_TEMPLATE_PATH . 'event_publish_box_extras.template.php',
1570
-            $publish_box_extra_args
1571
-        );
1572
-    }
1573
-
1574
-
1575
-    /**
1576
-     * @return EE_Event
1577
-     */
1578
-    public function get_event_object()
1579
-    {
1580
-        return $this->_cpt_model_obj;
1581
-    }
1582
-
1583
-
1584
-
1585
-
1586
-    /** METABOXES * */
1587
-    /**
1588
-     * _register_event_editor_meta_boxes
1589
-     * add all metaboxes related to the event_editor
1590
-     *
1591
-     * @return void
1592
-     * @throws EE_Error
1593
-     * @throws ReflectionException
1594
-     */
1595
-    protected function _register_event_editor_meta_boxes()
1596
-    {
1597
-        $this->verify_cpt_object();
1598
-        add_meta_box(
1599
-            'espresso_event_editor_tickets',
1600
-            esc_html__('Event Datetime & Ticket', 'event_espresso'),
1601
-            [$this, 'ticket_metabox'],
1602
-            $this->page_slug,
1603
-            'normal',
1604
-            'high'
1605
-        );
1606
-        add_meta_box(
1607
-            'espresso_event_editor_event_options',
1608
-            esc_html__('Event Registration Options', 'event_espresso'),
1609
-            [$this, 'registration_options_meta_box'],
1610
-            $this->page_slug,
1611
-            'side'
1612
-        );
1613
-        // NOTE: if you're looking for other metaboxes in here,
1614
-        // where a metabox has a related management page in the admin
1615
-        // you will find it setup in the related management page's "_Hooks" file.
1616
-        // i.e. messages metabox is found in "espresso_events_Messages_Hooks.class.php".
1617
-    }
1618
-
1619
-
1620
-    /**
1621
-     * @throws DomainException
1622
-     * @throws EE_Error
1623
-     * @throws ReflectionException
1624
-     */
1625
-    public function ticket_metabox()
1626
-    {
1627
-        $existing_datetime_ids = $existing_ticket_ids = [];
1628
-        // defaults for template args
1629
-        $template_args = [
1630
-            'existing_datetime_ids'    => '',
1631
-            'event_datetime_help_link' => '',
1632
-            'ticket_options_help_link' => '',
1633
-            'time'                     => null,
1634
-            'ticket_rows'              => '',
1635
-            'existing_ticket_ids'      => '',
1636
-            'total_ticket_rows'        => 1,
1637
-            'ticket_js_structure'      => '',
1638
-            'trash_icon'               => 'ee-lock-icon',
1639
-            'disabled'                 => '',
1640
-        ];
1641
-        $event_id      = is_object($this->_cpt_model_obj) ? $this->_cpt_model_obj->ID() : null;
1642
-        /**
1643
-         * 1. Start with retrieving Datetimes
1644
-         * 2. Fore each datetime get related tickets
1645
-         * 3. For each ticket get related prices
1646
-         */
1647
-        $times          = EEM_Datetime::instance()->get_all_event_dates($event_id);
1648
-        $first_datetime = reset($times);
1649
-        // do we get related tickets?
1650
-        if (
1651
-            $first_datetime instanceof EE_Datetime
1652
-            && $first_datetime->ID() !== 0
1653
-        ) {
1654
-            $existing_datetime_ids[] = $first_datetime->get('DTT_ID');
1655
-            $template_args['time']   = $first_datetime;
1656
-            $related_tickets         = $first_datetime->tickets(
1657
-                [
1658
-                    ['OR' => ['TKT_deleted' => 1, 'TKT_deleted*' => 0]],
1659
-                    'default_where_conditions' => 'none',
1660
-                ]
1661
-            );
1662
-            if (! empty($related_tickets)) {
1663
-                $template_args['total_ticket_rows'] = count($related_tickets);
1664
-                $row                                = 0;
1665
-                foreach ($related_tickets as $ticket) {
1666
-                    $existing_ticket_ids[]        = $ticket->get('TKT_ID');
1667
-                    $template_args['ticket_rows'] .= $this->_get_ticket_row($ticket, false, $row);
1668
-                    $row++;
1669
-                }
1670
-            } else {
1671
-                $template_args['total_ticket_rows'] = 1;
1672
-                /** @type EE_Ticket $ticket */
1673
-                $ticket                       = EEM_Ticket::instance()->create_default_object();
1674
-                $template_args['ticket_rows'] .= $this->_get_ticket_row($ticket);
1675
-            }
1676
-        } else {
1677
-            $template_args['time']        = $times[0];
1678
-            $tickets                      = EEM_Ticket::instance()->get_all_default_tickets();
1679
-            $template_args['ticket_rows'] .= $this->_get_ticket_row($tickets[1]);
1680
-            // NOTE: we're just sending the first default row
1681
-            // (decaf can't manage default tickets so this should be sufficient);
1682
-        }
1683
-        $template_args['event_datetime_help_link'] = $this->_get_help_tab_link(
1684
-            'event_editor_event_datetimes_help_tab'
1685
-        );
1686
-        $template_args['ticket_options_help_link'] = $this->_get_help_tab_link('ticket_options_info');
1687
-        $template_args['existing_datetime_ids']    = implode(',', $existing_datetime_ids);
1688
-        $template_args['existing_ticket_ids']      = implode(',', $existing_ticket_ids);
1689
-        $template_args['ticket_js_structure']      = $this->_get_ticket_row(
1690
-            EEM_Ticket::instance()->create_default_object(),
1691
-            true
1692
-        );
1693
-        $template                                  = apply_filters(
1694
-            'FHEE__Events_Admin_Page__ticket_metabox__template',
1695
-            EVENTS_TEMPLATE_PATH . 'event_tickets_metabox_main.template.php'
1696
-        );
1697
-        EEH_Template::display_template($template, $template_args);
1698
-    }
1699
-
1700
-
1701
-    /**
1702
-     * Setup an individual ticket form for the decaf event editor page
1703
-     *
1704
-     * @access private
1705
-     * @param EE_Ticket $ticket   the ticket object
1706
-     * @param boolean   $skeleton whether we're generating a skeleton for js manipulation
1707
-     * @param int       $row
1708
-     * @return string generated html for the ticket row.
1709
-     * @throws EE_Error
1710
-     * @throws ReflectionException
1711
-     */
1712
-    private function _get_ticket_row($ticket, $skeleton = false, $row = 0)
1713
-    {
1714
-        $template_args = [
1715
-            'tkt_status_class'    => ' tkt-status-' . $ticket->ticket_status(),
1716
-            'tkt_archive_class'   => $ticket->ticket_status() === EE_Ticket::archived && ! $skeleton ? ' tkt-archived'
1717
-                : '',
1718
-            'ticketrow'           => $skeleton ? 'TICKETNUM' : $row,
1719
-            'TKT_ID'              => $ticket->get('TKT_ID'),
1720
-            'TKT_name'            => $ticket->get('TKT_name'),
1721
-            'TKT_start_date'      => $skeleton ? '' : $ticket->get_date('TKT_start_date', 'Y-m-d h:i a'),
1722
-            'TKT_end_date'        => $skeleton ? '' : $ticket->get_date('TKT_end_date', 'Y-m-d h:i a'),
1723
-            'TKT_is_default'      => $ticket->get('TKT_is_default'),
1724
-            'TKT_qty'             => $ticket->get_pretty('TKT_qty', 'input'),
1725
-            'edit_ticketrow_name' => $skeleton ? 'TICKETNAMEATTR' : 'edit_tickets',
1726
-            'TKT_sold'            => $skeleton ? 0 : $ticket->get('TKT_sold'),
1727
-            'trash_icon'          => ($skeleton || (! empty($ticket) && ! $ticket->get('TKT_deleted')))
1728
-                                     && (! empty($ticket) && $ticket->get('TKT_sold') === 0)
1729
-                ? 'trash-icon dashicons dashicons-post-trash clickable' : 'ee-lock-icon',
1730
-            'disabled'            => $skeleton || (! empty($ticket) && ! $ticket->get('TKT_deleted')) ? ''
1731
-                : ' disabled=disabled',
1732
-        ];
1733
-        $price         = $ticket->ID() !== 0
1734
-            ? $ticket->get_first_related('Price', ['default_where_conditions' => 'none'])
1735
-            : null;
1736
-        $price         = $price instanceof EE_Price
1737
-            ? $price
1738
-            : EEM_Price::instance()->create_default_object();
1739
-        $price_args    = [
1740
-            'price_currency_symbol' => EE_Registry::instance()->CFG->currency->sign,
1741
-            'PRC_amount'            => $price->get('PRC_amount'),
1742
-            'PRT_ID'                => $price->get('PRT_ID'),
1743
-            'PRC_ID'                => $price->get('PRC_ID'),
1744
-            'PRC_is_default'        => $price->get('PRC_is_default'),
1745
-        ];
1746
-        // make sure we have default start and end dates if skeleton
1747
-        // handle rows that should NOT be empty
1748
-        if (empty($template_args['TKT_start_date'])) {
1749
-            // if empty then the start date will be now.
1750
-            $template_args['TKT_start_date'] = date('Y-m-d h:i a', current_time('timestamp'));
1751
-        }
1752
-        if (empty($template_args['TKT_end_date'])) {
1753
-            // get the earliest datetime (if present);
1754
-            $earliest_datetime             = $this->_cpt_model_obj->ID() > 0
1755
-                ? $this->_cpt_model_obj->get_first_related(
1756
-                    'Datetime',
1757
-                    ['order_by' => ['DTT_EVT_start' => 'ASC']]
1758
-                )
1759
-                : null;
1760
-            $template_args['TKT_end_date'] = $earliest_datetime instanceof EE_Datetime
1761
-                ? $earliest_datetime->get_datetime('DTT_EVT_start', 'Y-m-d', 'h:i a')
1762
-                : date('Y-m-d h:i a', mktime(0, 0, 0, date('m'), date('d') + 7, date('Y')));
1763
-        }
1764
-        $template_args = array_merge($template_args, $price_args);
1765
-        $template      = apply_filters(
1766
-            'FHEE__Events_Admin_Page__get_ticket_row__template',
1767
-            EVENTS_TEMPLATE_PATH . 'event_tickets_metabox_ticket_row.template.php',
1768
-            $ticket
1769
-        );
1770
-        return EEH_Template::display_template($template, $template_args, true);
1771
-    }
1772
-
1773
-
1774
-    /**
1775
-     * @throws EE_Error
1776
-     * @throws ReflectionException
1777
-     */
1778
-    public function registration_options_meta_box()
1779
-    {
1780
-        $yes_no_values             = [
1781
-            ['id' => true, 'text' => esc_html__('Yes', 'event_espresso')],
1782
-            ['id' => false, 'text' => esc_html__('No', 'event_espresso')],
1783
-        ];
1784
-        $default_reg_status_values = EEM_Registration::reg_status_array(
1785
-            [
1786
-                EEM_Registration::status_id_cancelled,
1787
-                EEM_Registration::status_id_declined,
1788
-                EEM_Registration::status_id_incomplete,
1789
-            ],
1790
-            true
1791
-        );
1792
-        // $template_args['is_active_select'] = EEH_Form_Fields::select_input('is_active', $yes_no_values, $this->_cpt_model_obj->is_active());
1793
-        $template_args['_event']                          = $this->_cpt_model_obj;
1794
-        $template_args['event']                           = $this->_cpt_model_obj;
1795
-        $template_args['active_status']                   = $this->_cpt_model_obj->pretty_active_status(false);
1796
-        $template_args['additional_limit']                = $this->_cpt_model_obj->additional_limit();
1797
-        $template_args['default_registration_status']     = EEH_Form_Fields::select_input(
1798
-            'default_reg_status',
1799
-            $default_reg_status_values,
1800
-            $this->_cpt_model_obj->default_registration_status()
1801
-        );
1802
-        $template_args['display_description']             = EEH_Form_Fields::select_input(
1803
-            'display_desc',
1804
-            $yes_no_values,
1805
-            $this->_cpt_model_obj->display_description()
1806
-        );
1807
-        $template_args['display_ticket_selector']         = EEH_Form_Fields::select_input(
1808
-            'display_ticket_selector',
1809
-            $yes_no_values,
1810
-            $this->_cpt_model_obj->display_ticket_selector(),
1811
-            '',
1812
-            '',
1813
-            false
1814
-        );
1815
-        $template_args['additional_registration_options'] = apply_filters(
1816
-            'FHEE__Events_Admin_Page__registration_options_meta_box__additional_registration_options',
1817
-            '',
1818
-            $template_args,
1819
-            $yes_no_values,
1820
-            $default_reg_status_values
1821
-        );
1822
-        EEH_Template::display_template(
1823
-            EVENTS_TEMPLATE_PATH . 'event_registration_options.template.php',
1824
-            $template_args
1825
-        );
1826
-    }
1827
-
1828
-
1829
-    /**
1830
-     * _get_events()
1831
-     * This method simply returns all the events (for the given _view and paging)
1832
-     *
1833
-     * @access public
1834
-     * @param int  $per_page     count of items per page (20 default);
1835
-     * @param int  $current_page what is the current page being viewed.
1836
-     * @param bool $count        if TRUE then we just return a count of ALL events matching the given _view.
1837
-     *                           If FALSE then we return an array of event objects
1838
-     *                           that match the given _view and paging parameters.
1839
-     * @return array|int         an array of event objects or a count of them.
1840
-     * @throws Exception
1841
-     */
1842
-    public function get_events($per_page = 10, $current_page = 1, $count = false)
1843
-    {
1844
-        $EEM_Event   = $this->_event_model();
1845
-        $offset      = ($current_page - 1) * $per_page;
1846
-        $limit       = $count ? null : $offset . ',' . $per_page;
1847
-        $orderby     = $this->request->getRequestParam('orderby', 'EVT_ID');
1848
-        $order       = $this->request->getRequestParam('order', 'DESC');
1849
-        $month_range = $this->request->getRequestParam('month_range');
1850
-        if ($month_range) {
1851
-            $pieces = explode(' ', $month_range, 3);
1852
-            // simulate the FIRST day of the month, that fixes issues for months like February
1853
-            // where PHP doesn't know what to assume for date.
1854
-            // @see https://events.codebasehq.com/projects/event-espresso/tickets/10437
1855
-            $month_r = ! empty($pieces[0]) ? date('m', EEH_DTT_Helper::first_of_month_timestamp($pieces[0])) : '';
1856
-            $year_r  = ! empty($pieces[1]) ? $pieces[1] : '';
1857
-        }
1858
-        $where  = [];
1859
-        $status = $this->request->getRequestParam('status');
1860
-        // determine what post_status our condition will have for the query.
1861
-        switch ($status) {
1862
-            case 'month':
1863
-            case 'today':
1864
-            case null:
1865
-            case 'all':
1866
-                break;
1867
-            case 'draft':
1868
-                $where['status'] = ['IN', ['draft', 'auto-draft']];
1869
-                break;
1870
-            default:
1871
-                $where['status'] = $status;
1872
-        }
1873
-        // categories? The default for all categories is -1
1874
-        $category = $this->request->getRequestParam('EVT_CAT', -1, 'int');
1875
-        if ($category !== -1) {
1876
-            $where['Term_Taxonomy.taxonomy'] = EEM_CPT_Base::EVENT_CATEGORY_TAXONOMY;
1877
-            $where['Term_Taxonomy.term_id']  = $category;
1878
-        }
1879
-        // date where conditions
1880
-        $start_formats = EEM_Datetime::instance()->get_formats_for('DTT_EVT_start');
1881
-        if ($month_range) {
1882
-            $DateTime = new DateTime(
1883
-                $year_r . '-' . $month_r . '-01 00:00:00',
1884
-                new DateTimeZone('UTC')
1885
-            );
1886
-            $start    = $DateTime->getTimestamp();
1887
-            // set the datetime to be the end of the month
1888
-            $DateTime->setDate(
1889
-                $year_r,
1890
-                $month_r,
1891
-                $DateTime->format('t')
1892
-            )->setTime(23, 59, 59);
1893
-            $end                             = $DateTime->getTimestamp();
1894
-            $where['Datetime.DTT_EVT_start'] = ['BETWEEN', [$start, $end]];
1895
-        } elseif ($status === 'today') {
1896
-            $DateTime                        =
1897
-                new DateTime('now', new DateTimeZone(EEM_Event::instance()->get_timezone()));
1898
-            $start                           = $DateTime->setTime(0, 0)->format(implode(' ', $start_formats));
1899
-            $end                             = $DateTime->setTime(23, 59, 59)->format(implode(' ', $start_formats));
1900
-            $where['Datetime.DTT_EVT_start'] = ['BETWEEN', [$start, $end]];
1901
-        } elseif ($status === 'month') {
1902
-            $now                             = date('Y-m-01');
1903
-            $DateTime                        =
1904
-                new DateTime($now, new DateTimeZone(EEM_Event::instance()->get_timezone()));
1905
-            $start                           = $DateTime->setTime(0, 0)->format(implode(' ', $start_formats));
1906
-            $end                             = $DateTime->setDate(date('Y'), date('m'), $DateTime->format('t'))
1907
-                                                        ->setTime(23, 59, 59)
1908
-                                                        ->format(implode(' ', $start_formats));
1909
-            $where['Datetime.DTT_EVT_start'] = ['BETWEEN', [$start, $end]];
1910
-        }
1911
-        if (! EE_Registry::instance()->CAP->current_user_can('ee_read_others_events', 'get_events')) {
1912
-            $where['EVT_wp_user'] = get_current_user_id();
1913
-        } else {
1914
-            if (! isset($where['status'])) {
1915
-                if (! EE_Registry::instance()->CAP->current_user_can('ee_read_private_events', 'get_events')) {
1916
-                    $where['OR'] = [
1917
-                        'status*restrict_private' => ['!=', 'private'],
1918
-                        'AND'                     => [
1919
-                            'status*inclusive' => ['=', 'private'],
1920
-                            'EVT_wp_user'      => get_current_user_id(),
1921
-                        ],
1922
-                    ];
1923
-                }
1924
-            }
1925
-        }
1926
-        $wp_user = $this->request->getRequestParam('EVT_wp_user', 0, 'int');
1927
-        if (
1928
-            $wp_user
1929
-            && $wp_user !== get_current_user_id()
1930
-            && EE_Registry::instance()->CAP->current_user_can('ee_read_others_events', 'get_events')
1931
-        ) {
1932
-            $where['EVT_wp_user'] = $wp_user;
1933
-        }
1934
-        // search query handling
1935
-        $search_term = $this->request->getRequestParam('s');
1936
-        if ($search_term) {
1937
-            $search_term = '%' . $search_term . '%';
1938
-            $where['OR'] = [
1939
-                'EVT_name'       => ['LIKE', $search_term],
1940
-                'EVT_desc'       => ['LIKE', $search_term],
1941
-                'EVT_short_desc' => ['LIKE', $search_term],
1942
-            ];
1943
-        }
1944
-        // filter events by venue.
1945
-        $venue = $this->request->getRequestParam('venue', 0, 'int');
1946
-        if ($venue) {
1947
-            $where['Venue.VNU_ID'] = $venue;
1948
-        }
1949
-        $request_params = $this->request->requestParams();
1950
-        $where          = apply_filters('FHEE__Events_Admin_Page__get_events__where', $where, $request_params);
1951
-        $query_params   = apply_filters(
1952
-            'FHEE__Events_Admin_Page__get_events__query_params',
1953
-            [
1954
-                $where,
1955
-                'limit'    => $limit,
1956
-                'order_by' => $orderby,
1957
-                'order'    => $order,
1958
-                'group_by' => 'EVT_ID',
1959
-            ],
1960
-            $request_params
1961
-        );
1962
-
1963
-        // let's first check if we have special requests coming in.
1964
-        $active_status = $this->request->getRequestParam('active_status');
1965
-        if ($active_status) {
1966
-            switch ($active_status) {
1967
-                case 'upcoming':
1968
-                    return $EEM_Event->get_upcoming_events($query_params, $count);
1969
-                case 'expired':
1970
-                    return $EEM_Event->get_expired_events($query_params, $count);
1971
-                case 'active':
1972
-                    return $EEM_Event->get_active_events($query_params, $count);
1973
-                case 'inactive':
1974
-                    return $EEM_Event->get_inactive_events($query_params, $count);
1975
-            }
1976
-        }
1977
-
1978
-        return $count ? $EEM_Event->count([$where], 'EVT_ID', true) : $EEM_Event->get_all($query_params);
1979
-    }
1980
-
1981
-
1982
-    /**
1983
-     * handling for WordPress CPT actions (trash, restore, delete)
1984
-     *
1985
-     * @param string $post_id
1986
-     * @throws EE_Error
1987
-     * @throws ReflectionException
1988
-     */
1989
-    public function trash_cpt_item($post_id)
1990
-    {
1991
-        $this->request->setRequestParam('EVT_ID', $post_id);
1992
-        $this->_trash_or_restore_event('trash', false);
1993
-    }
1994
-
1995
-
1996
-    /**
1997
-     * @param string $post_id
1998
-     * @throws EE_Error
1999
-     * @throws ReflectionException
2000
-     */
2001
-    public function restore_cpt_item($post_id)
2002
-    {
2003
-        $this->request->setRequestParam('EVT_ID', $post_id);
2004
-        $this->_trash_or_restore_event('draft', false);
2005
-    }
2006
-
2007
-
2008
-    /**
2009
-     * @param string $post_id
2010
-     * @throws EE_Error
2011
-     * @throws EE_Error
2012
-     */
2013
-    public function delete_cpt_item($post_id)
2014
-    {
2015
-        throw new EE_Error(
2016
-            esc_html__(
2017
-                'Please contact Event Espresso support with the details of the steps taken to produce this error.',
2018
-                'event_espresso'
2019
-            )
2020
-        );
2021
-        // $this->request->setRequestParam('EVT_ID', $post_id);
2022
-        // $this->_delete_event();
2023
-    }
2024
-
2025
-
2026
-    /**
2027
-     * _trash_or_restore_event
2028
-     *
2029
-     * @access protected
2030
-     * @param string $event_status
2031
-     * @param bool   $redirect_after
2032
-     * @throws EE_Error
2033
-     * @throws EE_Error
2034
-     * @throws ReflectionException
2035
-     */
2036
-    protected function _trash_or_restore_event($event_status = 'trash', $redirect_after = true)
2037
-    {
2038
-        // determine the event id and set to array.
2039
-        $EVT_ID = $this->request->getRequestParam('EVT_ID', 0, 'int');
2040
-        // loop thru events
2041
-        if ($EVT_ID) {
2042
-            // clean status
2043
-            $event_status = sanitize_key($event_status);
2044
-            // grab status
2045
-            if (! empty($event_status)) {
2046
-                $success = $this->_change_event_status($EVT_ID, $event_status);
2047
-            } else {
2048
-                $success = false;
2049
-                $msg     = esc_html__(
2050
-                    'An error occurred. The event could not be moved to the trash because a valid event status was not not supplied.',
2051
-                    'event_espresso'
2052
-                );
2053
-                EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
2054
-            }
2055
-        } else {
2056
-            $success = false;
2057
-            $msg     = esc_html__(
2058
-                'An error occurred. The event could not be moved to the trash because a valid event ID was not not supplied.',
2059
-                'event_espresso'
2060
-            );
2061
-            EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
2062
-        }
2063
-        $action = $event_status == 'trash' ? 'moved to the trash' : 'restored from the trash';
2064
-        if ($redirect_after) {
2065
-            $this->_redirect_after_action($success, 'Event', $action, ['action' => 'default']);
2066
-        }
2067
-    }
2068
-
2069
-
2070
-    /**
2071
-     * _trash_or_restore_events
2072
-     *
2073
-     * @access protected
2074
-     * @param string $event_status
2075
-     * @return void
2076
-     * @throws EE_Error
2077
-     * @throws EE_Error
2078
-     * @throws ReflectionException
2079
-     */
2080
-    protected function _trash_or_restore_events($event_status = 'trash')
2081
-    {
2082
-        // clean status
2083
-        $event_status = sanitize_key($event_status);
2084
-        // grab status
2085
-        if (! empty($event_status)) {
2086
-            $success = true;
2087
-            // determine the event id and set to array.
2088
-            $EVT_IDs = $this->request->getRequestParam('EVT_IDs', [], 'int', true);
2089
-            // loop thru events
2090
-            foreach ($EVT_IDs as $EVT_ID) {
2091
-                if ($EVT_ID = absint($EVT_ID)) {
2092
-                    $results = $this->_change_event_status($EVT_ID, $event_status);
2093
-                    $success = $results !== false ? $success : false;
2094
-                } else {
2095
-                    $msg = sprintf(
2096
-                        esc_html__(
2097
-                            'An error occurred. Event #%d could not be moved to the trash because a valid event ID was not not supplied.',
2098
-                            'event_espresso'
2099
-                        ),
2100
-                        $EVT_ID
2101
-                    );
2102
-                    EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
2103
-                    $success = false;
2104
-                }
2105
-            }
2106
-        } else {
2107
-            $success = false;
2108
-            $msg     = esc_html__(
2109
-                'An error occurred. The event could not be moved to the trash because a valid event status was not not supplied.',
2110
-                'event_espresso'
2111
-            );
2112
-            EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
2113
-        }
2114
-        // in order to force a pluralized result message we need to send back a success status greater than 1
2115
-        $success = $success ? 2 : false;
2116
-        $action  = $event_status == 'trash' ? 'moved to the trash' : 'restored from the trash';
2117
-        $this->_redirect_after_action($success, 'Events', $action, ['action' => 'default']);
2118
-    }
2119
-
2120
-
2121
-    /**
2122
-     * @param int    $EVT_ID
2123
-     * @param string $event_status
2124
-     * @return bool
2125
-     * @throws EE_Error
2126
-     * @throws ReflectionException
2127
-     */
2128
-    private function _change_event_status($EVT_ID = 0, $event_status = '')
2129
-    {
2130
-        // grab event id
2131
-        if (! $EVT_ID) {
2132
-            $msg = esc_html__(
2133
-                'An error occurred. No Event ID or an invalid Event ID was received.',
2134
-                'event_espresso'
2135
-            );
2136
-            EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
2137
-            return false;
2138
-        }
2139
-        $this->_cpt_model_obj = EEM_Event::instance()->get_one_by_ID($EVT_ID);
2140
-        // clean status
2141
-        $event_status = sanitize_key($event_status);
2142
-        // grab status
2143
-        if (empty($event_status)) {
2144
-            $msg = esc_html__(
2145
-                'An error occurred. No Event Status or an invalid Event Status was received.',
2146
-                'event_espresso'
2147
-            );
2148
-            EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
2149
-            return false;
2150
-        }
2151
-        // was event trashed or restored ?
2152
-        switch ($event_status) {
2153
-            case 'draft':
2154
-                $action = 'restored from the trash';
2155
-                $hook   = 'AHEE_event_restored_from_trash';
2156
-                break;
2157
-            case 'trash':
2158
-                $action = 'moved to the trash';
2159
-                $hook   = 'AHEE_event_moved_to_trash';
2160
-                break;
2161
-            default:
2162
-                $action = 'updated';
2163
-                $hook   = false;
2164
-        }
2165
-        // use class to change status
2166
-        $this->_cpt_model_obj->set_status($event_status);
2167
-        $success = $this->_cpt_model_obj->save();
2168
-        if (! $success) {
2169
-            $msg = sprintf(esc_html__('An error occurred. The event could not be %s.', 'event_espresso'), $action);
2170
-            EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
2171
-            return false;
2172
-        }
2173
-        if ($hook) {
2174
-            do_action($hook);
2175
-        }
2176
-        return true;
2177
-    }
2178
-
2179
-
2180
-    /**
2181
-     * @param array $event_ids
2182
-     * @return array
2183
-     * @since   4.10.23.p
2184
-     */
2185
-    private function cleanEventIds(array $event_ids)
2186
-    {
2187
-        return array_map('absint', $event_ids);
2188
-    }
2189
-
2190
-
2191
-    /**
2192
-     * @return array
2193
-     * @since   4.10.23.p
2194
-     */
2195
-    private function getEventIdsFromRequest()
2196
-    {
2197
-        if ($this->request->requestParamIsSet('EVT_IDs')) {
2198
-            return $this->request->getRequestParam('EVT_IDs', [], 'int', true);
2199
-        } else {
2200
-            return $this->request->getRequestParam('EVT_ID', [], 'int', true);
2201
-        }
2202
-    }
2203
-
2204
-
2205
-    /**
2206
-     * @param bool $preview_delete
2207
-     * @throws EE_Error
2208
-     */
2209
-    protected function _delete_event($preview_delete = true)
2210
-    {
2211
-        $this->_delete_events($preview_delete);
2212
-    }
2213
-
2214
-
2215
-    /**
2216
-     * Gets the tree traversal batch persister.
2217
-     *
2218
-     * @return NodeGroupDao
2219
-     * @throws InvalidArgumentException
2220
-     * @throws InvalidDataTypeException
2221
-     * @throws InvalidInterfaceException
2222
-     * @since 4.10.12.p
2223
-     */
2224
-    protected function getModelObjNodeGroupPersister()
2225
-    {
2226
-        if (! $this->model_obj_node_group_persister instanceof NodeGroupDao) {
2227
-            $this->model_obj_node_group_persister =
2228
-                $this->getLoader()->load('\EventEspresso\core\services\orm\tree_traversal\NodeGroupDao');
2229
-        }
2230
-        return $this->model_obj_node_group_persister;
2231
-    }
2232
-
2233
-
2234
-    /**
2235
-     * @param bool $preview_delete
2236
-     * @return void
2237
-     * @throws EE_Error
2238
-     */
2239
-    protected function _delete_events($preview_delete = true)
2240
-    {
2241
-        $event_ids = $this->getEventIdsFromRequest();
2242
-        if ($preview_delete) {
2243
-            $this->generateDeletionPreview($event_ids);
2244
-        } else {
2245
-            EEM_Event::instance()->delete_permanently([['EVT_ID' => ['IN', $event_ids]]]);
2246
-        }
2247
-    }
2248
-
2249
-
2250
-    /**
2251
-     * @param array $event_ids
2252
-     */
2253
-    protected function generateDeletionPreview(array $event_ids)
2254
-    {
2255
-        $event_ids = $this->cleanEventIds($event_ids);
2256
-        // Set a code we can use to reference this deletion task in the batch jobs and preview page.
2257
-        $deletion_job_code = $this->getModelObjNodeGroupPersister()->generateGroupCode();
2258
-        $return_url        = EE_Admin_Page::add_query_args_and_nonce(
2259
-            [
2260
-                'action'            => 'preview_deletion',
2261
-                'deletion_job_code' => $deletion_job_code,
2262
-            ],
2263
-            $this->_admin_base_url
2264
-        );
2265
-        EEH_URL::safeRedirectAndExit(
2266
-            EE_Admin_Page::add_query_args_and_nonce(
2267
-                [
2268
-                    'page'              => 'espresso_batch',
2269
-                    'batch'             => EED_Batch::batch_job,
2270
-                    'EVT_IDs'           => $event_ids,
2271
-                    'deletion_job_code' => $deletion_job_code,
2272
-                    'job_handler'       => urlencode('EventEspressoBatchRequest\JobHandlers\PreviewEventDeletion'),
2273
-                    'return_url'        => urlencode($return_url),
2274
-                ],
2275
-                admin_url()
2276
-            )
2277
-        );
2278
-    }
2279
-
2280
-
2281
-    /**
2282
-     * Checks for a POST submission
2283
-     *
2284
-     * @since 4.10.12.p
2285
-     */
2286
-    protected function confirmDeletion()
2287
-    {
2288
-        $deletion_redirect_logic =
2289
-            $this->getLoader()->getShared('\EventEspresso\core\domain\services\admin\events\data\ConfirmDeletion');
2290
-        $deletion_redirect_logic->handle($this->get_request_data(), $this->admin_base_url());
2291
-    }
2292
-
2293
-
2294
-    /**
2295
-     * A page for users to preview what exactly will be deleted, and confirm they want to delete it.
2296
-     *
2297
-     * @throws EE_Error
2298
-     * @since 4.10.12.p
2299
-     */
2300
-    protected function previewDeletion()
2301
-    {
2302
-        $preview_deletion_logic =
2303
-            $this->getLoader()->getShared('\EventEspresso\core\domain\services\admin\events\data\PreviewDeletion');
2304
-        $this->set_template_args($preview_deletion_logic->handle($this->get_request_data(), $this->admin_base_url()));
2305
-        $this->display_admin_page_with_no_sidebar();
2306
-    }
2307
-
2308
-
2309
-    /**
2310
-     * get total number of events
2311
-     *
2312
-     * @access public
2313
-     * @return int
2314
-     * @throws EE_Error
2315
-     * @throws EE_Error
2316
-     */
2317
-    public function total_events()
2318
-    {
2319
-        return EEM_Event::instance()->count(
2320
-            ['caps' => 'read_admin'],
2321
-            'EVT_ID',
2322
-            true
2323
-        );
2324
-    }
2325
-
2326
-
2327
-    /**
2328
-     * get total number of draft events
2329
-     *
2330
-     * @access public
2331
-     * @return int
2332
-     * @throws EE_Error
2333
-     * @throws EE_Error
2334
-     */
2335
-    public function total_events_draft()
2336
-    {
2337
-        return EEM_Event::instance()->count(
2338
-            [
2339
-                ['status' => ['IN', ['draft', 'auto-draft']]],
2340
-                'caps' => 'read_admin',
2341
-            ],
2342
-            'EVT_ID',
2343
-            true
2344
-        );
2345
-    }
2346
-
2347
-
2348
-    /**
2349
-     * get total number of trashed events
2350
-     *
2351
-     * @access public
2352
-     * @return int
2353
-     * @throws EE_Error
2354
-     * @throws EE_Error
2355
-     */
2356
-    public function total_trashed_events()
2357
-    {
2358
-        return EEM_Event::instance()->count(
2359
-            [
2360
-                ['status' => 'trash'],
2361
-                'caps' => 'read_admin',
2362
-            ],
2363
-            'EVT_ID',
2364
-            true
2365
-        );
2366
-    }
2367
-
2368
-
2369
-    /**
2370
-     *    _default_event_settings
2371
-     *    This generates the Default Settings Tab
2372
-     *
2373
-     * @return void
2374
-     * @throws EE_Error
2375
-     */
2376
-    protected function _default_event_settings()
2377
-    {
2378
-        $this->_set_add_edit_form_tags('update_default_event_settings');
2379
-        $this->_set_publish_post_box_vars(null, false, false, null, false);
2380
-        $this->_template_args['admin_page_content'] = $this->_default_event_settings_form()->get_html();
2381
-        $this->display_admin_page_with_sidebar();
2382
-    }
2383
-
2384
-
2385
-    /**
2386
-     * Return the form for event settings.
2387
-     *
2388
-     * @return EE_Form_Section_Proper
2389
-     * @throws EE_Error
2390
-     */
2391
-    protected function _default_event_settings_form()
2392
-    {
2393
-        $registration_config              = EE_Registry::instance()->CFG->registration;
2394
-        $registration_stati_for_selection = EEM_Registration::reg_status_array(
2395
-        // exclude
2396
-            [
2397
-                EEM_Registration::status_id_cancelled,
2398
-                EEM_Registration::status_id_declined,
2399
-                EEM_Registration::status_id_incomplete,
2400
-                EEM_Registration::status_id_wait_list,
2401
-            ],
2402
-            true
2403
-        );
2404
-        return new EE_Form_Section_Proper(
2405
-            [
2406
-                'name'            => 'update_default_event_settings',
2407
-                'html_id'         => 'update_default_event_settings',
2408
-                'html_class'      => 'form-table',
2409
-                'layout_strategy' => new EE_Admin_Two_Column_Layout(),
2410
-                'subsections'     => apply_filters(
2411
-                    'FHEE__Events_Admin_Page___default_event_settings_form__form_subsections',
2412
-                    [
2413
-                        'default_reg_status'  => new EE_Select_Input(
2414
-                            $registration_stati_for_selection,
2415
-                            [
2416
-                                'default'         => isset($registration_config->default_STS_ID)
2417
-                                                     && array_key_exists(
2418
-                                                         $registration_config->default_STS_ID,
2419
-                                                         $registration_stati_for_selection
2420
-                                                     )
2421
-                                    ? sanitize_text_field($registration_config->default_STS_ID)
2422
-                                    : EEM_Registration::status_id_pending_payment,
2423
-                                'html_label_text' => esc_html__('Default Registration Status', 'event_espresso')
2424
-                                                     . EEH_Template::get_help_tab_link(
2425
-                                                         'default_settings_status_help_tab'
2426
-                                                     ),
2427
-                                'html_help_text'  => esc_html__(
2428
-                                    'This setting allows you to preselect what the default registration status setting is when creating an event.  Note that changing this setting does NOT retroactively apply it to existing events.',
2429
-                                    'event_espresso'
2430
-                                ),
2431
-                            ]
2432
-                        ),
2433
-                        'default_max_tickets' => new EE_Integer_Input(
2434
-                            [
2435
-                                'default'         => isset($registration_config->default_maximum_number_of_tickets)
2436
-                                    ? $registration_config->default_maximum_number_of_tickets
2437
-                                    : EEM_Event::get_default_additional_limit(),
2438
-                                'html_label_text' => esc_html__(
2439
-                                    'Default Maximum Tickets Allowed Per Order:',
2440
-                                    'event_espresso'
2441
-                                )
2442
-                                . EEH_Template::get_help_tab_link(
2443
-                                    'default_maximum_tickets_help_tab"'
2444
-                                ),
2445
-                                'html_help_text'  => esc_html__(
2446
-                                    'This setting allows you to indicate what will be the default for the maximum number of tickets per order when creating new events.',
2447
-                                    'event_espresso'
2448
-                                ),
2449
-                            ]
2450
-                        ),
2451
-                    ]
2452
-                ),
2453
-            ]
2454
-        );
2455
-    }
2456
-
2457
-
2458
-    /**
2459
-     * _update_default_event_settings
2460
-     *
2461
-     * @access protected
2462
-     * @return void
2463
-     * @throws EE_Error
2464
-     */
2465
-    protected function _update_default_event_settings()
2466
-    {
2467
-        $registration_config = EE_Registry::instance()->CFG->registration;
2468
-        $form                = $this->_default_event_settings_form();
2469
-        if ($form->was_submitted()) {
2470
-            $form->receive_form_submission();
2471
-            if ($form->is_valid()) {
2472
-                $valid_data = $form->valid_data();
2473
-                if (isset($valid_data['default_reg_status'])) {
2474
-                    $registration_config->default_STS_ID = $valid_data['default_reg_status'];
2475
-                }
2476
-                if (isset($valid_data['default_max_tickets'])) {
2477
-                    $registration_config->default_maximum_number_of_tickets = $valid_data['default_max_tickets'];
2478
-                }
2479
-                // update because data was valid!
2480
-                EE_Registry::instance()->CFG->update_espresso_config();
2481
-                EE_Error::overwrite_success();
2482
-                EE_Error::add_success(
2483
-                    esc_html__('Default Event Settings were updated', 'event_espresso')
2484
-                );
2485
-            }
2486
-        }
2487
-        $this->_redirect_after_action(0, '', '', ['action' => 'default_event_settings'], true);
2488
-    }
2489
-
2490
-
2491
-    /*************        Templates        *************
2492
-     *
2493
-     * @throws EE_Error
2494
-     */
2495
-    protected function _template_settings()
2496
-    {
2497
-        $this->_admin_page_title              = esc_html__('Template Settings (Preview)', 'event_espresso');
2498
-        $this->_template_args['preview_img']  = '<img src="'
2499
-                                                . EVENTS_ASSETS_URL
2500
-                                                . '/images/'
2501
-                                                . 'caffeinated_template_features.jpg" alt="'
2502
-                                                . esc_attr__('Template Settings Preview screenshot', 'event_espresso')
2503
-                                                . '" />';
2504
-        $this->_template_args['preview_text'] = '<strong>'
2505
-                                                . esc_html__(
2506
-                                                    'Template Settings is a feature that is only available in the premium version of Event Espresso 4 which is available with a support license purchase on EventEspresso.com. Template Settings allow you to configure some of the appearance options for both the Event List and Event Details pages.',
2507
-                                                    'event_espresso'
2508
-                                                ) . '</strong>';
2509
-        $this->display_admin_caf_preview_page('template_settings_tab');
2510
-    }
2511
-
2512
-
2513
-    /** Event Category Stuff **/
2514
-    /**
2515
-     * set the _category property with the category object for the loaded page.
2516
-     *
2517
-     * @access private
2518
-     * @return void
2519
-     */
2520
-    private function _set_category_object()
2521
-    {
2522
-        if (isset($this->_category->id) && ! empty($this->_category->id)) {
2523
-            return;
2524
-        } //already have the category object so get out.
2525
-        // set default category object
2526
-        $this->_set_empty_category_object();
2527
-        // only set if we've got an id
2528
-        $category_ID = $this->request->getRequestParam('EVT_CAT_ID', 0, 'int');
2529
-        if (! $category_ID) {
2530
-            return;
2531
-        }
2532
-        $term = get_term($category_ID, EEM_CPT_Base::EVENT_CATEGORY_TAXONOMY);
2533
-        if (! empty($term)) {
2534
-            $this->_category->category_name       = $term->name;
2535
-            $this->_category->category_identifier = $term->slug;
2536
-            $this->_category->category_desc       = $term->description;
2537
-            $this->_category->id                  = $term->term_id;
2538
-            $this->_category->parent              = $term->parent;
2539
-        }
2540
-    }
2541
-
2542
-
2543
-    /**
2544
-     * Clears out category properties.
2545
-     */
2546
-    private function _set_empty_category_object()
2547
-    {
2548
-        $this->_category                = new stdClass();
2549
-        $this->_category->category_name = $this->_category->category_identifier = $this->_category->category_desc = '';
2550
-        $this->_category->id            = $this->_category->parent = 0;
2551
-    }
2552
-
2553
-
2554
-    /**
2555
-     * @throws EE_Error
2556
-     */
2557
-    protected function _category_list_table()
2558
-    {
2559
-        do_action('AHEE_log', __FILE__, __FUNCTION__, '');
2560
-        $this->_search_btn_label = esc_html__('Categories', 'event_espresso');
2561
-        $this->_admin_page_title .= ' ' . $this->get_action_link_or_button(
2562
-            'add_category',
2563
-            'add_category',
2564
-            [],
2565
-            'add-new-h2'
2566
-        );
2567
-        $this->display_admin_list_table_page_with_sidebar();
2568
-    }
2569
-
2570
-
2571
-    /**
2572
-     * Output category details view.
2573
-     *
2574
-     * @throws EE_Error
2575
-     * @throws EE_Error
2576
-     */
2577
-    protected function _category_details($view)
2578
-    {
2579
-        // load formatter helper
2580
-        // load field generator helper
2581
-        $route = $view == 'edit' ? 'update_category' : 'insert_category';
2582
-        $this->_set_add_edit_form_tags($route);
2583
-        $this->_set_category_object();
2584
-        $id            = ! empty($this->_category->id) ? $this->_category->id : '';
2585
-        $delete_action = 'delete_category';
2586
-        // custom redirect
2587
-        $redirect = EE_Admin_Page::add_query_args_and_nonce(
2588
-            ['action' => 'category_list'],
2589
-            $this->_admin_base_url
2590
-        );
2591
-        $this->_set_publish_post_box_vars('EVT_CAT_ID', $id, $delete_action, $redirect);
2592
-        // take care of contents
2593
-        $this->_template_args['admin_page_content'] = $this->_category_details_content();
2594
-        $this->display_admin_page_with_sidebar();
2595
-    }
2596
-
2597
-
2598
-    /**
2599
-     * Output category details content.
2600
-     */
2601
-    protected function _category_details_content()
2602
-    {
2603
-        $editor_args['category_desc'] = [
2604
-            'type'          => 'wp_editor',
2605
-            'value'         => EEH_Formatter::admin_format_content($this->_category->category_desc),
2606
-            'class'         => 'my_editor_custom',
2607
-            'wpeditor_args' => ['media_buttons' => false],
2608
-        ];
2609
-        $_wp_editor                   = $this->_generate_admin_form_fields($editor_args, 'array');
2610
-        $all_terms                    = get_terms(
2611
-            [EEM_CPT_Base::EVENT_CATEGORY_TAXONOMY],
2612
-            ['hide_empty' => 0, 'exclude' => [$this->_category->id]]
2613
-        );
2614
-        // setup category select for term parents.
2615
-        $category_select_values[] = [
2616
-            'text' => esc_html__('No Parent', 'event_espresso'),
2617
-            'id'   => 0,
2618
-        ];
2619
-        foreach ($all_terms as $term) {
2620
-            $category_select_values[] = [
2621
-                'text' => $term->name,
2622
-                'id'   => $term->term_id,
2623
-            ];
2624
-        }
2625
-        $category_select = EEH_Form_Fields::select_input(
2626
-            'category_parent',
2627
-            $category_select_values,
2628
-            $this->_category->parent
2629
-        );
2630
-        $template_args   = [
2631
-            'category'                 => $this->_category,
2632
-            'category_select'          => $category_select,
2633
-            'unique_id_info_help_link' => $this->_get_help_tab_link('unique_id_info'),
2634
-            'category_desc_editor'     => $_wp_editor['category_desc']['field'],
2635
-            'disable'                  => '',
2636
-            'disabled_message'         => false,
2637
-        ];
2638
-        $template        = EVENTS_TEMPLATE_PATH . 'event_category_details.template.php';
2639
-        return EEH_Template::display_template($template, $template_args, true);
2640
-    }
2641
-
2642
-
2643
-    /**
2644
-     * Handles deleting categories.
2645
-     *
2646
-     * @throws EE_Error
2647
-     */
2648
-    protected function _delete_categories()
2649
-    {
2650
-        $category_IDs = $this->request->getRequestParam('EVT_CAT_ID', 0, 'int', true);
2651
-        foreach ($category_IDs as $category_ID) {
2652
-            $this->_delete_category($category_ID);
2653
-        }
2654
-        // doesn't matter what page we're coming from... we're going to the same place after delete.
2655
-        $query_args = [
2656
-            'action' => 'category_list',
2657
-        ];
2658
-        $this->_redirect_after_action(0, '', '', $query_args);
2659
-    }
2660
-
2661
-
2662
-    /**
2663
-     * Handles deleting specific category.
2664
-     *
2665
-     * @param int $cat_id
2666
-     */
2667
-    protected function _delete_category($cat_id)
2668
-    {
2669
-        $cat_id = absint($cat_id);
2670
-        wp_delete_term($cat_id, EEM_CPT_Base::EVENT_CATEGORY_TAXONOMY);
2671
-    }
2672
-
2673
-
2674
-    /**
2675
-     * Handles triggering the update or insertion of a new category.
2676
-     *
2677
-     * @param bool $new_category true means we're triggering the insert of a new category.
2678
-     * @throws EE_Error
2679
-     * @throws EE_Error
2680
-     */
2681
-    protected function _insert_or_update_category($new_category)
2682
-    {
2683
-        $cat_id  = $new_category ? $this->_insert_category() : $this->_insert_category(true);
2684
-        $success = 0; // we already have a success message so lets not send another.
2685
-        if ($cat_id) {
2686
-            $query_args = [
2687
-                'action'     => 'edit_category',
2688
-                'EVT_CAT_ID' => $cat_id,
2689
-            ];
2690
-        } else {
2691
-            $query_args = ['action' => 'add_category'];
2692
-        }
2693
-        $this->_redirect_after_action($success, '', '', $query_args, true);
2694
-    }
2695
-
2696
-
2697
-    /**
2698
-     * Inserts or updates category
2699
-     *
2700
-     * @param bool $update (true indicates we're updating a category).
2701
-     * @return bool|mixed|string
2702
-     */
2703
-    private function _insert_category($update = false)
2704
-    {
2705
-        $category_ID         = $update ? $this->request->getRequestParam('EVT_CAT_ID', 0, 'int') : 0;
2706
-        $category_name       = $this->request->getRequestParam('category_name', '');
2707
-        $category_desc       = $this->request->getRequestParam('category_desc', '');
2708
-        $category_parent     = $this->request->getRequestParam('category_parent', 0, 'int');
2709
-        $category_identifier = $this->request->getRequestParam('category_identifier', '');
2710
-
2711
-        if (empty($category_name)) {
2712
-            $msg = esc_html__('You must add a name for the category.', 'event_espresso');
2713
-            EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
2714
-            return false;
2715
-        }
2716
-        $term_args = [
2717
-            'name'        => $category_name,
2718
-            'description' => $category_desc,
2719
-            'parent'      => $category_parent,
2720
-        ];
2721
-        // was the category_identifier input disabled?
2722
-        if ($category_identifier) {
2723
-            $term_args['slug'] = $category_identifier;
2724
-        }
2725
-        $insert_ids = $update
2726
-            ? wp_update_term($category_ID, EEM_CPT_Base::EVENT_CATEGORY_TAXONOMY, $term_args)
2727
-            : wp_insert_term($category_name, EEM_CPT_Base::EVENT_CATEGORY_TAXONOMY, $term_args);
2728
-        if (! is_array($insert_ids)) {
2729
-            $msg = esc_html__(
2730
-                'An error occurred and the category has not been saved to the database.',
2731
-                'event_espresso'
2732
-            );
2733
-            EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
2734
-        } else {
2735
-            $category_ID = $insert_ids['term_id'];
2736
-            $msg         =
2737
-                sprintf(esc_html__('The category %s was successfully saved', 'event_espresso'), $category_name);
2738
-            EE_Error::add_success($msg);
2739
-        }
2740
-        return $category_ID;
2741
-    }
2742
-
2743
-
2744
-    /**
2745
-     * Gets categories or count of categories matching the arguments in the request.
2746
-     *
2747
-     * @param int  $per_page
2748
-     * @param int  $current_page
2749
-     * @param bool $count
2750
-     * @return EE_Term_Taxonomy[]|int
2751
-     * @throws EE_Error
2752
-     * @throws EE_Error
2753
-     */
2754
-    public function get_categories($per_page = 10, $current_page = 1, $count = false)
2755
-    {
2756
-        // testing term stuff
2757
-        $orderby     = $this->request->getRequestParam('orderby', 'Term.term_id');
2758
-        $order       = $this->request->getRequestParam('order', 'DESC');
2759
-        $limit       = ($current_page - 1) * $per_page;
2760
-        $where       = ['taxonomy' => EEM_CPT_Base::EVENT_CATEGORY_TAXONOMY];
2761
-        $search_term = $this->request->getRequestParam('s');
2762
-        if ($search_term) {
2763
-            $search_term = '%' . $search_term . '%';
2764
-            $where['OR'] = [
2765
-                'Term.name'   => ['LIKE', $search_term],
2766
-                'description' => ['LIKE', $search_term],
2767
-            ];
2768
-        }
2769
-        $query_params = [
2770
-            $where,
2771
-            'order_by'   => [$orderby => $order],
2772
-            'limit'      => $limit . ',' . $per_page,
2773
-            'force_join' => ['Term'],
2774
-        ];
2775
-        return $count
2776
-            ? EEM_Term_Taxonomy::instance()->count($query_params, 'term_id')
2777
-            : EEM_Term_Taxonomy::instance()->get_all($query_params);
2778
-    }
2779
-
2780
-    /* end category stuff */
2781
-    /**************/
2782
-
2783
-
2784
-    /**
2785
-     * Callback for the `ee_save_timezone_setting` ajax action.
2786
-     *
2787
-     * @throws EE_Error
2788
-     */
2789
-    public function saveTimezoneString()
2790
-    {
2791
-        $timezone_string = $this->request->getRequestParam('timezone_selected');
2792
-        if (empty($timezone_string) || ! EEH_DTT_Helper::validate_timezone($timezone_string, false)) {
2793
-            EE_Error::add_error(
2794
-                esc_html__('An invalid timezone string submitted.', 'event_espresso'),
2795
-                __FILE__,
2796
-                __FUNCTION__,
2797
-                __LINE__
2798
-            );
2799
-            $this->_template_args['error'] = true;
2800
-            $this->_return_json();
2801
-        }
2802
-
2803
-        update_option('timezone_string', $timezone_string);
2804
-        EE_Error::add_success(
2805
-            esc_html__('Your timezone string was updated.', 'event_espresso')
2806
-        );
2807
-        $this->_template_args['success'] = true;
2808
-        $this->_return_json(true, ['action' => 'create_new']);
2809
-    }
2810
-
2811
-
2812
-    /**
2813 2493
      * @throws EE_Error
2814
-     * @deprecated 4.10.25.p
2815 2494
      */
2816
-    public function save_timezonestring_setting()
2817
-    {
2818
-        $this->saveTimezoneString();
2819
-    }
2495
+	protected function _template_settings()
2496
+	{
2497
+		$this->_admin_page_title              = esc_html__('Template Settings (Preview)', 'event_espresso');
2498
+		$this->_template_args['preview_img']  = '<img src="'
2499
+												. EVENTS_ASSETS_URL
2500
+												. '/images/'
2501
+												. 'caffeinated_template_features.jpg" alt="'
2502
+												. esc_attr__('Template Settings Preview screenshot', 'event_espresso')
2503
+												. '" />';
2504
+		$this->_template_args['preview_text'] = '<strong>'
2505
+												. esc_html__(
2506
+													'Template Settings is a feature that is only available in the premium version of Event Espresso 4 which is available with a support license purchase on EventEspresso.com. Template Settings allow you to configure some of the appearance options for both the Event List and Event Details pages.',
2507
+													'event_espresso'
2508
+												) . '</strong>';
2509
+		$this->display_admin_caf_preview_page('template_settings_tab');
2510
+	}
2511
+
2512
+
2513
+	/** Event Category Stuff **/
2514
+	/**
2515
+	 * set the _category property with the category object for the loaded page.
2516
+	 *
2517
+	 * @access private
2518
+	 * @return void
2519
+	 */
2520
+	private function _set_category_object()
2521
+	{
2522
+		if (isset($this->_category->id) && ! empty($this->_category->id)) {
2523
+			return;
2524
+		} //already have the category object so get out.
2525
+		// set default category object
2526
+		$this->_set_empty_category_object();
2527
+		// only set if we've got an id
2528
+		$category_ID = $this->request->getRequestParam('EVT_CAT_ID', 0, 'int');
2529
+		if (! $category_ID) {
2530
+			return;
2531
+		}
2532
+		$term = get_term($category_ID, EEM_CPT_Base::EVENT_CATEGORY_TAXONOMY);
2533
+		if (! empty($term)) {
2534
+			$this->_category->category_name       = $term->name;
2535
+			$this->_category->category_identifier = $term->slug;
2536
+			$this->_category->category_desc       = $term->description;
2537
+			$this->_category->id                  = $term->term_id;
2538
+			$this->_category->parent              = $term->parent;
2539
+		}
2540
+	}
2541
+
2542
+
2543
+	/**
2544
+	 * Clears out category properties.
2545
+	 */
2546
+	private function _set_empty_category_object()
2547
+	{
2548
+		$this->_category                = new stdClass();
2549
+		$this->_category->category_name = $this->_category->category_identifier = $this->_category->category_desc = '';
2550
+		$this->_category->id            = $this->_category->parent = 0;
2551
+	}
2552
+
2553
+
2554
+	/**
2555
+	 * @throws EE_Error
2556
+	 */
2557
+	protected function _category_list_table()
2558
+	{
2559
+		do_action('AHEE_log', __FILE__, __FUNCTION__, '');
2560
+		$this->_search_btn_label = esc_html__('Categories', 'event_espresso');
2561
+		$this->_admin_page_title .= ' ' . $this->get_action_link_or_button(
2562
+			'add_category',
2563
+			'add_category',
2564
+			[],
2565
+			'add-new-h2'
2566
+		);
2567
+		$this->display_admin_list_table_page_with_sidebar();
2568
+	}
2569
+
2570
+
2571
+	/**
2572
+	 * Output category details view.
2573
+	 *
2574
+	 * @throws EE_Error
2575
+	 * @throws EE_Error
2576
+	 */
2577
+	protected function _category_details($view)
2578
+	{
2579
+		// load formatter helper
2580
+		// load field generator helper
2581
+		$route = $view == 'edit' ? 'update_category' : 'insert_category';
2582
+		$this->_set_add_edit_form_tags($route);
2583
+		$this->_set_category_object();
2584
+		$id            = ! empty($this->_category->id) ? $this->_category->id : '';
2585
+		$delete_action = 'delete_category';
2586
+		// custom redirect
2587
+		$redirect = EE_Admin_Page::add_query_args_and_nonce(
2588
+			['action' => 'category_list'],
2589
+			$this->_admin_base_url
2590
+		);
2591
+		$this->_set_publish_post_box_vars('EVT_CAT_ID', $id, $delete_action, $redirect);
2592
+		// take care of contents
2593
+		$this->_template_args['admin_page_content'] = $this->_category_details_content();
2594
+		$this->display_admin_page_with_sidebar();
2595
+	}
2596
+
2597
+
2598
+	/**
2599
+	 * Output category details content.
2600
+	 */
2601
+	protected function _category_details_content()
2602
+	{
2603
+		$editor_args['category_desc'] = [
2604
+			'type'          => 'wp_editor',
2605
+			'value'         => EEH_Formatter::admin_format_content($this->_category->category_desc),
2606
+			'class'         => 'my_editor_custom',
2607
+			'wpeditor_args' => ['media_buttons' => false],
2608
+		];
2609
+		$_wp_editor                   = $this->_generate_admin_form_fields($editor_args, 'array');
2610
+		$all_terms                    = get_terms(
2611
+			[EEM_CPT_Base::EVENT_CATEGORY_TAXONOMY],
2612
+			['hide_empty' => 0, 'exclude' => [$this->_category->id]]
2613
+		);
2614
+		// setup category select for term parents.
2615
+		$category_select_values[] = [
2616
+			'text' => esc_html__('No Parent', 'event_espresso'),
2617
+			'id'   => 0,
2618
+		];
2619
+		foreach ($all_terms as $term) {
2620
+			$category_select_values[] = [
2621
+				'text' => $term->name,
2622
+				'id'   => $term->term_id,
2623
+			];
2624
+		}
2625
+		$category_select = EEH_Form_Fields::select_input(
2626
+			'category_parent',
2627
+			$category_select_values,
2628
+			$this->_category->parent
2629
+		);
2630
+		$template_args   = [
2631
+			'category'                 => $this->_category,
2632
+			'category_select'          => $category_select,
2633
+			'unique_id_info_help_link' => $this->_get_help_tab_link('unique_id_info'),
2634
+			'category_desc_editor'     => $_wp_editor['category_desc']['field'],
2635
+			'disable'                  => '',
2636
+			'disabled_message'         => false,
2637
+		];
2638
+		$template        = EVENTS_TEMPLATE_PATH . 'event_category_details.template.php';
2639
+		return EEH_Template::display_template($template, $template_args, true);
2640
+	}
2641
+
2642
+
2643
+	/**
2644
+	 * Handles deleting categories.
2645
+	 *
2646
+	 * @throws EE_Error
2647
+	 */
2648
+	protected function _delete_categories()
2649
+	{
2650
+		$category_IDs = $this->request->getRequestParam('EVT_CAT_ID', 0, 'int', true);
2651
+		foreach ($category_IDs as $category_ID) {
2652
+			$this->_delete_category($category_ID);
2653
+		}
2654
+		// doesn't matter what page we're coming from... we're going to the same place after delete.
2655
+		$query_args = [
2656
+			'action' => 'category_list',
2657
+		];
2658
+		$this->_redirect_after_action(0, '', '', $query_args);
2659
+	}
2660
+
2661
+
2662
+	/**
2663
+	 * Handles deleting specific category.
2664
+	 *
2665
+	 * @param int $cat_id
2666
+	 */
2667
+	protected function _delete_category($cat_id)
2668
+	{
2669
+		$cat_id = absint($cat_id);
2670
+		wp_delete_term($cat_id, EEM_CPT_Base::EVENT_CATEGORY_TAXONOMY);
2671
+	}
2672
+
2673
+
2674
+	/**
2675
+	 * Handles triggering the update or insertion of a new category.
2676
+	 *
2677
+	 * @param bool $new_category true means we're triggering the insert of a new category.
2678
+	 * @throws EE_Error
2679
+	 * @throws EE_Error
2680
+	 */
2681
+	protected function _insert_or_update_category($new_category)
2682
+	{
2683
+		$cat_id  = $new_category ? $this->_insert_category() : $this->_insert_category(true);
2684
+		$success = 0; // we already have a success message so lets not send another.
2685
+		if ($cat_id) {
2686
+			$query_args = [
2687
+				'action'     => 'edit_category',
2688
+				'EVT_CAT_ID' => $cat_id,
2689
+			];
2690
+		} else {
2691
+			$query_args = ['action' => 'add_category'];
2692
+		}
2693
+		$this->_redirect_after_action($success, '', '', $query_args, true);
2694
+	}
2695
+
2696
+
2697
+	/**
2698
+	 * Inserts or updates category
2699
+	 *
2700
+	 * @param bool $update (true indicates we're updating a category).
2701
+	 * @return bool|mixed|string
2702
+	 */
2703
+	private function _insert_category($update = false)
2704
+	{
2705
+		$category_ID         = $update ? $this->request->getRequestParam('EVT_CAT_ID', 0, 'int') : 0;
2706
+		$category_name       = $this->request->getRequestParam('category_name', '');
2707
+		$category_desc       = $this->request->getRequestParam('category_desc', '');
2708
+		$category_parent     = $this->request->getRequestParam('category_parent', 0, 'int');
2709
+		$category_identifier = $this->request->getRequestParam('category_identifier', '');
2710
+
2711
+		if (empty($category_name)) {
2712
+			$msg = esc_html__('You must add a name for the category.', 'event_espresso');
2713
+			EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
2714
+			return false;
2715
+		}
2716
+		$term_args = [
2717
+			'name'        => $category_name,
2718
+			'description' => $category_desc,
2719
+			'parent'      => $category_parent,
2720
+		];
2721
+		// was the category_identifier input disabled?
2722
+		if ($category_identifier) {
2723
+			$term_args['slug'] = $category_identifier;
2724
+		}
2725
+		$insert_ids = $update
2726
+			? wp_update_term($category_ID, EEM_CPT_Base::EVENT_CATEGORY_TAXONOMY, $term_args)
2727
+			: wp_insert_term($category_name, EEM_CPT_Base::EVENT_CATEGORY_TAXONOMY, $term_args);
2728
+		if (! is_array($insert_ids)) {
2729
+			$msg = esc_html__(
2730
+				'An error occurred and the category has not been saved to the database.',
2731
+				'event_espresso'
2732
+			);
2733
+			EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
2734
+		} else {
2735
+			$category_ID = $insert_ids['term_id'];
2736
+			$msg         =
2737
+				sprintf(esc_html__('The category %s was successfully saved', 'event_espresso'), $category_name);
2738
+			EE_Error::add_success($msg);
2739
+		}
2740
+		return $category_ID;
2741
+	}
2742
+
2743
+
2744
+	/**
2745
+	 * Gets categories or count of categories matching the arguments in the request.
2746
+	 *
2747
+	 * @param int  $per_page
2748
+	 * @param int  $current_page
2749
+	 * @param bool $count
2750
+	 * @return EE_Term_Taxonomy[]|int
2751
+	 * @throws EE_Error
2752
+	 * @throws EE_Error
2753
+	 */
2754
+	public function get_categories($per_page = 10, $current_page = 1, $count = false)
2755
+	{
2756
+		// testing term stuff
2757
+		$orderby     = $this->request->getRequestParam('orderby', 'Term.term_id');
2758
+		$order       = $this->request->getRequestParam('order', 'DESC');
2759
+		$limit       = ($current_page - 1) * $per_page;
2760
+		$where       = ['taxonomy' => EEM_CPT_Base::EVENT_CATEGORY_TAXONOMY];
2761
+		$search_term = $this->request->getRequestParam('s');
2762
+		if ($search_term) {
2763
+			$search_term = '%' . $search_term . '%';
2764
+			$where['OR'] = [
2765
+				'Term.name'   => ['LIKE', $search_term],
2766
+				'description' => ['LIKE', $search_term],
2767
+			];
2768
+		}
2769
+		$query_params = [
2770
+			$where,
2771
+			'order_by'   => [$orderby => $order],
2772
+			'limit'      => $limit . ',' . $per_page,
2773
+			'force_join' => ['Term'],
2774
+		];
2775
+		return $count
2776
+			? EEM_Term_Taxonomy::instance()->count($query_params, 'term_id')
2777
+			: EEM_Term_Taxonomy::instance()->get_all($query_params);
2778
+	}
2779
+
2780
+	/* end category stuff */
2781
+	/**************/
2782
+
2783
+
2784
+	/**
2785
+	 * Callback for the `ee_save_timezone_setting` ajax action.
2786
+	 *
2787
+	 * @throws EE_Error
2788
+	 */
2789
+	public function saveTimezoneString()
2790
+	{
2791
+		$timezone_string = $this->request->getRequestParam('timezone_selected');
2792
+		if (empty($timezone_string) || ! EEH_DTT_Helper::validate_timezone($timezone_string, false)) {
2793
+			EE_Error::add_error(
2794
+				esc_html__('An invalid timezone string submitted.', 'event_espresso'),
2795
+				__FILE__,
2796
+				__FUNCTION__,
2797
+				__LINE__
2798
+			);
2799
+			$this->_template_args['error'] = true;
2800
+			$this->_return_json();
2801
+		}
2802
+
2803
+		update_option('timezone_string', $timezone_string);
2804
+		EE_Error::add_success(
2805
+			esc_html__('Your timezone string was updated.', 'event_espresso')
2806
+		);
2807
+		$this->_template_args['success'] = true;
2808
+		$this->_return_json(true, ['action' => 'create_new']);
2809
+	}
2810
+
2811
+
2812
+	/**
2813
+	 * @throws EE_Error
2814
+	 * @deprecated 4.10.25.p
2815
+	 */
2816
+	public function save_timezonestring_setting()
2817
+	{
2818
+		$this->saveTimezoneString();
2819
+	}
2820 2820
 }
Please login to merge, or discard this patch.
modules/single_page_checkout/EED_Single_Page_Checkout.module.php 1 patch
Indentation   +1788 added lines, -1788 removed lines patch added patch discarded remove patch
@@ -19,1792 +19,1792 @@
 block discarded – undo
19 19
  */
20 20
 class EED_Single_Page_Checkout extends EED_Module
21 21
 {
22
-    /**
23
-     * $_initialized - has the SPCO controller already been initialized ?
24
-     *
25
-     * @var bool $_initialized
26
-     */
27
-    private static $_initialized = false;
28
-
29
-
30
-    /**
31
-     * $_checkout_verified - is the EE_Checkout verified as correct for this request ?
32
-     *
33
-     * @var bool $_valid_checkout
34
-     */
35
-    private static $_checkout_verified = true;
36
-
37
-    /**
38
-     *    $_reg_steps_array - holds initial array of reg steps
39
-     *
40
-     * @var array $_reg_steps_array
41
-     */
42
-    private static $_reg_steps_array = array();
43
-
44
-    /**
45
-     *    $checkout - EE_Checkout object for handling the properties of the current checkout process
46
-     *
47
-     * @var EE_Checkout $checkout
48
-     */
49
-    public $checkout;
50
-
51
-    /**
52
-     * @var RequestInterface $request
53
-     */
54
-    protected $request;
55
-
56
-
57
-    /**
58
-     * @return EED_Single_Page_Checkout|EED_Module
59
-     * @throws EE_Error
60
-     * @throws ReflectionException
61
-     */
62
-    public static function instance()
63
-    {
64
-        add_filter('EED_Single_Page_Checkout__SPCO_active', '__return_true');
65
-        return parent::get_instance(__CLASS__);
66
-    }
67
-
68
-
69
-    /**
70
-     * @return EE_CART
71
-     */
72
-    public function cart()
73
-    {
74
-        return $this->checkout->cart;
75
-    }
76
-
77
-
78
-    /**
79
-     * @return RequestInterface
80
-     * @since   4.10.14.p
81
-     */
82
-    public static function getRequest()
83
-    {
84
-        return LoaderFactory::getLoader()->getShared(RequestInterface::class);
85
-    }
86
-
87
-
88
-    /**
89
-     * @return EE_Transaction
90
-     */
91
-    public function transaction()
92
-    {
93
-        return $this->checkout->transaction;
94
-    }
95
-
96
-
97
-    /**
98
-     *    set_hooks - for hooking into EE Core, other modules, etc
99
-     *
100
-     * @return    void
101
-     * @throws EE_Error
102
-     */
103
-    public static function set_hooks()
104
-    {
105
-        EED_Single_Page_Checkout::set_definitions();
106
-    }
107
-
108
-
109
-    /**
110
-     *    set_hooks_admin - for hooking into EE Admin Core, other modules, etc
111
-     *
112
-     * @return    void
113
-     * @throws EE_Error
114
-     */
115
-    public static function set_hooks_admin()
116
-    {
117
-        EED_Single_Page_Checkout::set_definitions();
118
-        if (! (defined('DOING_AJAX') && DOING_AJAX)) {
119
-            return;
120
-        }
121
-        // going to start an output buffer in case anything gets accidentally output
122
-        // that might disrupt our JSON response
123
-        ob_start();
124
-        EED_Single_Page_Checkout::load_reg_steps();
125
-        // set ajax hooks
126
-        add_action('wp_ajax_process_reg_step', array('EED_Single_Page_Checkout', 'process_reg_step'));
127
-        add_action('wp_ajax_nopriv_process_reg_step', array('EED_Single_Page_Checkout', 'process_reg_step'));
128
-        add_action('wp_ajax_display_spco_reg_step', array('EED_Single_Page_Checkout', 'display_reg_step'));
129
-        add_action('wp_ajax_nopriv_display_spco_reg_step', array('EED_Single_Page_Checkout', 'display_reg_step'));
130
-        add_action('wp_ajax_update_reg_step', array('EED_Single_Page_Checkout', 'update_reg_step'));
131
-        add_action('wp_ajax_nopriv_update_reg_step', array('EED_Single_Page_Checkout', 'update_reg_step'));
132
-    }
133
-
134
-
135
-    /**
136
-     *    process ajax request
137
-     *
138
-     * @param string $ajax_action
139
-     * @throws EE_Error
140
-     * @throws ReflectionException
141
-     */
142
-    public static function process_ajax_request($ajax_action)
143
-    {
144
-        $request = EED_Single_Page_Checkout::getRequest();
145
-        $request->setRequestParam('action', $ajax_action);
146
-        EED_Single_Page_Checkout::instance()->_initialize();
147
-    }
148
-
149
-
150
-    /**
151
-     * ajax display registration step
152
-     *
153
-     * @throws EE_Error
154
-     * @throws ReflectionException
155
-     */
156
-    public static function display_reg_step()
157
-    {
158
-        EED_Single_Page_Checkout::process_ajax_request('display_spco_reg_step');
159
-    }
160
-
161
-
162
-    /**
163
-     * ajax process registration step
164
-     *
165
-     * @throws EE_Error
166
-     * @throws ReflectionException
167
-     */
168
-    public static function process_reg_step()
169
-    {
170
-        EED_Single_Page_Checkout::process_ajax_request('process_reg_step');
171
-    }
172
-
173
-
174
-    /**
175
-     * ajax process registration step
176
-     *
177
-     * @throws EE_Error
178
-     * @throws ReflectionException
179
-     */
180
-    public static function update_reg_step()
181
-    {
182
-        EED_Single_Page_Checkout::process_ajax_request('update_reg_step');
183
-    }
184
-
185
-
186
-    /**
187
-     * update_checkout
188
-     *
189
-     * @return void
190
-     * @throws ReflectionException
191
-     * @throws EE_Error
192
-     */
193
-    public static function update_checkout()
194
-    {
195
-        EED_Single_Page_Checkout::process_ajax_request('update_checkout');
196
-    }
197
-
198
-
199
-    /**
200
-     * @return void
201
-     * @deprecated 4.10.14.p
202
-     */
203
-    public static function load_request_handler()
204
-    {
205
-    }
206
-
207
-
208
-    /**
209
-     *    set_definitions
210
-     *
211
-     * @return    void
212
-     * @throws EE_Error
213
-     */
214
-    public static function set_definitions()
215
-    {
216
-        if (defined('SPCO_BASE_PATH')) {
217
-            return;
218
-        }
219
-        define(
220
-            'SPCO_BASE_PATH',
221
-            rtrim(str_replace(array('\\', '/'), '/', plugin_dir_path(__FILE__)), '/') . '/'
222
-        );
223
-        define('SPCO_CSS_URL', plugin_dir_url(__FILE__) . 'css/');
224
-        define('SPCO_IMG_URL', plugin_dir_url(__FILE__) . 'img/');
225
-        define('SPCO_JS_URL', plugin_dir_url(__FILE__) . 'js/');
226
-        define('SPCO_INC_PATH', SPCO_BASE_PATH . 'inc/');
227
-        define('SPCO_REG_STEPS_PATH', SPCO_BASE_PATH . 'reg_steps/');
228
-        define('SPCO_TEMPLATES_PATH', SPCO_BASE_PATH . 'templates/');
229
-        EEH_Autoloader::register_autoloaders_for_each_file_in_folder(SPCO_BASE_PATH, true);
230
-        EE_Registry::$i18n_js_strings['registration_expiration_notice'] = EED_Single_Page_Checkout::getRegistrationExpirationNotice(
231
-        );
232
-    }
233
-
234
-
235
-    /**
236
-     * load_reg_steps
237
-     * loads and instantiates each reg step based on the EE_Registry::instance()->CFG->registration->reg_steps array
238
-     *
239
-     * @throws EE_Error
240
-     */
241
-    public static function load_reg_steps()
242
-    {
243
-        static $reg_steps_loaded = false;
244
-        if ($reg_steps_loaded) {
245
-            return;
246
-        }
247
-        // filter list of reg_steps
248
-        $reg_steps_to_load = (array) apply_filters(
249
-            'AHEE__SPCO__load_reg_steps__reg_steps_to_load',
250
-            EED_Single_Page_Checkout::get_reg_steps()
251
-        );
252
-        // sort by key (order)
253
-        ksort($reg_steps_to_load);
254
-        // loop through folders
255
-        foreach ($reg_steps_to_load as $order => $reg_step) {
256
-            // we need a
257
-            if (isset($reg_step['file_path'], $reg_step['class_name'], $reg_step['slug'])) {
258
-                // copy over to the reg_steps_array
259
-                EED_Single_Page_Checkout::$_reg_steps_array[ $order ] = $reg_step;
260
-                // register custom key route for each reg step
261
-                // ie: step=>"slug" - this is the entire reason we load the reg steps array now
262
-                EE_Config::register_route(
263
-                    $reg_step['slug'],
264
-                    'EED_Single_Page_Checkout',
265
-                    'run',
266
-                    'step'
267
-                );
268
-                // add AJAX or other hooks
269
-                if (isset($reg_step['has_hooks']) && $reg_step['has_hooks']) {
270
-                    // setup autoloaders if necessary
271
-                    if (! class_exists($reg_step['class_name'])) {
272
-                        EEH_Autoloader::register_autoloaders_for_each_file_in_folder(
273
-                            $reg_step['file_path'],
274
-                            true
275
-                        );
276
-                    }
277
-                    if (is_callable($reg_step['class_name'], 'set_hooks')) {
278
-                        call_user_func(array($reg_step['class_name'], 'set_hooks'));
279
-                    }
280
-                }
281
-            }
282
-        }
283
-        $reg_steps_loaded = true;
284
-    }
285
-
286
-
287
-    /**
288
-     *    get_reg_steps
289
-     *
290
-     * @return    array
291
-     */
292
-    public static function get_reg_steps()
293
-    {
294
-        $reg_steps = EE_Registry::instance()->CFG->registration->reg_steps;
295
-        if (empty($reg_steps)) {
296
-            $reg_steps = array(
297
-                10  => array(
298
-                    'file_path'  => SPCO_REG_STEPS_PATH . 'attendee_information',
299
-                    'class_name' => 'EE_SPCO_Reg_Step_Attendee_Information',
300
-                    'slug'       => 'attendee_information',
301
-                    'has_hooks'  => false,
302
-                ),
303
-                30  => array(
304
-                    'file_path'  => SPCO_REG_STEPS_PATH . 'payment_options',
305
-                    'class_name' => 'EE_SPCO_Reg_Step_Payment_Options',
306
-                    'slug'       => 'payment_options',
307
-                    'has_hooks'  => true,
308
-                ),
309
-                999 => array(
310
-                    'file_path'  => SPCO_REG_STEPS_PATH . 'finalize_registration',
311
-                    'class_name' => 'EE_SPCO_Reg_Step_Finalize_Registration',
312
-                    'slug'       => 'finalize_registration',
313
-                    'has_hooks'  => false,
314
-                ),
315
-            );
316
-        }
317
-        return $reg_steps;
318
-    }
319
-
320
-
321
-    /**
322
-     *    registration_checkout_for_admin
323
-     *
324
-     * @return    string
325
-     * @throws EE_Error
326
-     * @throws ReflectionException
327
-     */
328
-    public static function registration_checkout_for_admin()
329
-    {
330
-        $request = EED_Single_Page_Checkout::getRequest();
331
-        $request->setRequestParam('step', 'attendee_information');
332
-        $request->setRequestParam('action', 'display_spco_reg_step');
333
-        $request->setRequestParam('process_form_submission', false);
334
-        EED_Single_Page_Checkout::instance()->_initialize();
335
-        EED_Single_Page_Checkout::instance()->_display_spco_reg_form();
336
-        return EED_Single_Page_Checkout::getResponse()->getOutput();
337
-    }
338
-
339
-
340
-    /**
341
-     * process_registration_from_admin
342
-     *
343
-     * @return EE_Transaction
344
-     * @throws EE_Error
345
-     * @throws ReflectionException
346
-     */
347
-    public static function process_registration_from_admin()
348
-    {
349
-        $request = EED_Single_Page_Checkout::getRequest();
350
-        $request->setRequestParam('step', 'attendee_information');
351
-        $request->setRequestParam('action', 'process_reg_step');
352
-        $request->setRequestParam('process_form_submission', true);
353
-        EED_Single_Page_Checkout::instance()->_initialize();
354
-        if (EED_Single_Page_Checkout::instance()->checkout->current_step->completed()) {
355
-            $final_reg_step = end(EED_Single_Page_Checkout::instance()->checkout->reg_steps);
356
-            if ($final_reg_step instanceof EE_SPCO_Reg_Step_Finalize_Registration) {
357
-                EED_Single_Page_Checkout::instance()->checkout->set_reg_step_initiated($final_reg_step);
358
-                if ($final_reg_step->process_reg_step()) {
359
-                    $final_reg_step->set_completed();
360
-                    EED_Single_Page_Checkout::instance()->checkout->update_txn_reg_steps_array();
361
-                    return EED_Single_Page_Checkout::instance()->checkout->transaction;
362
-                }
363
-            }
364
-        }
365
-        return null;
366
-    }
367
-
368
-
369
-    /**
370
-     *    run
371
-     *
372
-     * @param WP_Query $WP
373
-     * @return    void
374
-     */
375
-    public function run($WP)
376
-    {
377
-        if (
378
-            $WP instanceof WP_Query
379
-            && $WP->is_main_query()
380
-            && apply_filters('FHEE__EED_Single_Page_Checkout__run', true)
381
-            && $this->_is_reg_checkout()
382
-        ) {
383
-            $this->_initialize();
384
-        }
385
-    }
386
-
387
-
388
-    /**
389
-     * determines whether current url matches reg page url
390
-     *
391
-     * @return bool
392
-     */
393
-    protected function _is_reg_checkout()
394
-    {
395
-        // get current permalink for reg page without any extra query args
396
-        $reg_page_url = get_permalink(EE_Config::instance()->core->reg_page_id);
397
-        // get request URI for current request, but without the scheme or host
398
-        $current_request_uri = EEH_URL::filter_input_server_url();
399
-        $current_request_uri = html_entity_decode($current_request_uri);
400
-        // get array of query args from the current request URI
401
-        $query_args = EEH_URL::get_query_string($current_request_uri);
402
-        // grab page id if it is set
403
-        $page_id = isset($query_args['page_id']) ? absint($query_args['page_id']) : 0;
404
-        // and remove the page id from the query args (we will re-add it later)
405
-        unset($query_args['page_id']);
406
-        // now strip all query args from current request URI
407
-        $current_request_uri = remove_query_arg(array_keys($query_args), $current_request_uri);
408
-        // and re-add the page id if it was set
409
-        if ($page_id) {
410
-            $current_request_uri = add_query_arg('page_id', $page_id, $current_request_uri);
411
-        }
412
-        // remove slashes and ?
413
-        $current_request_uri = trim($current_request_uri, '?/');
414
-        // is current request URI part of the known full reg page URL ?
415
-        return ! empty($current_request_uri) && strpos($reg_page_url, $current_request_uri) !== false;
416
-    }
417
-
418
-
419
-    /**
420
-     * @param WP_Query $wp_query
421
-     * @return    void
422
-     * @throws EE_Error
423
-     * @throws ReflectionException
424
-     */
425
-    public static function init($wp_query)
426
-    {
427
-        EED_Single_Page_Checkout::instance()->run($wp_query);
428
-    }
429
-
430
-
431
-    /**
432
-     *    _initialize - initial module setup
433
-     *
434
-     * @return    void
435
-     */
436
-    private function _initialize()
437
-    {
438
-        // ensure SPCO doesn't run twice
439
-        if (EED_Single_Page_Checkout::$_initialized) {
440
-            return;
441
-        }
442
-        try {
443
-            $this->request = EED_Single_Page_Checkout::getRequest();
444
-            EED_Single_Page_Checkout::load_reg_steps();
445
-            $this->_verify_session();
446
-            // setup the EE_Checkout object
447
-            $this->checkout = $this->_initialize_checkout();
448
-            // filter checkout
449
-            $this->checkout = apply_filters('FHEE__EED_Single_Page_Checkout___initialize__checkout', $this->checkout);
450
-            // get the $_GET
451
-            $this->_get_request_vars();
452
-            if ($this->_block_bots()) {
453
-                return;
454
-            }
455
-            // filter continue_reg
456
-            $this->checkout->continue_reg = apply_filters(
457
-                'FHEE__EED_Single_Page_Checkout__init___continue_reg',
458
-                true,
459
-                $this->checkout
460
-            );
461
-            // load the reg steps array
462
-            if (! $this->_load_and_instantiate_reg_steps()) {
463
-                EED_Single_Page_Checkout::$_initialized = true;
464
-                return;
465
-            }
466
-            // set the current step
467
-            $this->checkout->set_current_step($this->checkout->step);
468
-            // and the next step
469
-            $this->checkout->set_next_step();
470
-            // verify that everything has been setup correctly
471
-            if (! ($this->_verify_transaction_and_get_registrations() && $this->_final_verifications())) {
472
-                EED_Single_Page_Checkout::$_initialized = true;
473
-                return;
474
-            }
475
-            // lock the transaction
476
-            $this->checkout->transaction->lock();
477
-            // make sure all of our cached objects are added to their respective model entity mappers
478
-            $this->checkout->refresh_all_entities();
479
-            // set amount owing
480
-            $this->checkout->amount_owing = $this->checkout->transaction->remaining();
481
-            // initialize each reg step, which gives them the chance to potentially alter the process
482
-            $this->_initialize_reg_steps();
483
-            // DEBUG LOG
484
-            // $this->checkout->log( __CLASS__, __FUNCTION__, __LINE__ );
485
-            // get reg form
486
-            if (! $this->_check_form_submission()) {
487
-                EED_Single_Page_Checkout::$_initialized = true;
488
-                return;
489
-            }
490
-            // checkout the action!!!
491
-            $this->_process_form_action();
492
-            // add some style and make it dance
493
-            $this->add_styles_and_scripts($this);
494
-            // kk... SPCO has successfully run
495
-            EED_Single_Page_Checkout::$_initialized = true;
496
-            // set no cache headers and constants
497
-            EE_System::do_not_cache();
498
-            // add anchor
499
-            add_action('loop_start', array($this, 'set_checkout_anchor'), 1);
500
-            // remove transaction lock
501
-            add_action('shutdown', array($this, 'unlock_transaction'), 1);
502
-        } catch (Exception $e) {
503
-            EE_Error::add_error($e->getMessage(), __FILE__, __FUNCTION__, __LINE__);
504
-        }
505
-    }
506
-
507
-
508
-    /**
509
-     *    _verify_session
510
-     * checks that the session is valid and not expired
511
-     *
512
-     * @throws EE_Error
513
-     * @throws ReflectionException
514
-     */
515
-    private function _verify_session()
516
-    {
517
-        if (! EE_Registry::instance()->SSN instanceof EE_Session) {
518
-            throw new EE_Error(esc_html__('The EE_Session class could not be loaded.', 'event_espresso'));
519
-        }
520
-        $clear_session_requested = $this->request->getRequestParam('clear_session', false, 'bool');
521
-        // is session still valid ?
522
-        if (
523
-            $clear_session_requested
524
-            || (
525
-                EE_Registry::instance()->SSN->expired()
526
-                && $this->request->getRequestParam('e_reg_url_link') === ''
527
-            )
528
-        ) {
529
-            $this->checkout = new EE_Checkout();
530
-            EE_Registry::instance()->SSN->clear_session(__CLASS__, __FUNCTION__);
531
-            // EE_Registry::instance()->SSN->reset_cart();
532
-            // EE_Registry::instance()->SSN->reset_checkout();
533
-            // EE_Registry::instance()->SSN->reset_transaction();
534
-            if (! $clear_session_requested) {
535
-                EE_Error::add_attention(
536
-                    EE_Registry::$i18n_js_strings['registration_expiration_notice'],
537
-                    __FILE__,
538
-                    __FUNCTION__,
539
-                    __LINE__
540
-                );
541
-            }
542
-            // EE_Registry::instance()->SSN->reset_expired();
543
-        }
544
-    }
545
-
546
-
547
-    /**
548
-     *    _initialize_checkout
549
-     * loads and instantiates EE_Checkout
550
-     *
551
-     * @return EE_Checkout
552
-     * @throws EE_Error
553
-     * @throws ReflectionException
554
-     */
555
-    private function _initialize_checkout()
556
-    {
557
-        // look in session for existing checkout
558
-        /** @type EE_Checkout $checkout */
559
-        $checkout = EE_Registry::instance()->SSN->checkout();
560
-        // verify
561
-        if (! $checkout instanceof EE_Checkout) {
562
-            // instantiate EE_Checkout object for handling the properties of the current checkout process
563
-            $checkout = EE_Registry::instance()->load_file(
564
-                SPCO_INC_PATH,
565
-                'EE_Checkout',
566
-                'class',
567
-                array(),
568
-                false
569
-            );
570
-        } else {
571
-            if ($checkout->current_step->is_final_step() && $checkout->exit_spco() === true) {
572
-                $this->unlock_transaction();
573
-                wp_safe_redirect($checkout->redirect_url);
574
-                exit();
575
-            }
576
-        }
577
-        $checkout = apply_filters('FHEE__EED_Single_Page_Checkout___initialize_checkout__checkout', $checkout);
578
-        // verify again
579
-        if (! $checkout instanceof EE_Checkout) {
580
-            throw new EE_Error(esc_html__('The EE_Checkout class could not be loaded.', 'event_espresso'));
581
-        }
582
-        // reset anything that needs a clean slate for each request
583
-        $checkout->reset_for_current_request();
584
-        return $checkout;
585
-    }
586
-
587
-
588
-    /**
589
-     *    _get_request_vars
590
-     *
591
-     * @return    void
592
-     */
593
-    private function _get_request_vars()
594
-    {
595
-        // make sure this request is marked as belonging to EE
596
-        /** @var CurrentPage $current_page */
597
-        $current_page = LoaderFactory::getLoader()->getShared(CurrentPage::class);
598
-        $current_page->setEspressoPage(true);
599
-        // which step is being requested ?
600
-        $this->checkout->step = $this->request->getRequestParam('step', $this->_get_first_step());
601
-        // which step is being edited ?
602
-        $this->checkout->edit_step = $this->request->getRequestParam('edit_step');
603
-        // and what we're doing on the current step
604
-        $this->checkout->action = $this->request->getRequestParam('action', 'display_spco_reg_step');
605
-        // timestamp
606
-        $this->checkout->uts = $this->request->getRequestParam('uts', 0, 'int');
607
-        // returning to edit ?
608
-        $this->checkout->reg_url_link = $this->request->getRequestParam('e_reg_url_link');
609
-        // add reg url link to registration query params
610
-        if ($this->checkout->reg_url_link && strpos($this->checkout->reg_url_link, '1-') !== 0) {
611
-            $this->checkout->reg_cache_where_params[0]['REG_url_link'] = $this->checkout->reg_url_link;
612
-        }
613
-        // or some other kind of revisit ?
614
-        $this->checkout->revisit = $this->request->getRequestParam('revisit', false, 'bool');
615
-        // and whether or not to generate a reg form for this request
616
-        $this->checkout->generate_reg_form = $this->request->getRequestParam('generate_reg_form', true, 'bool');
617
-        // and whether or not to process a reg form submission for this request
618
-        $this->checkout->process_form_submission = $this->request->getRequestParam(
619
-            'process_form_submission',
620
-            $this->checkout->action === 'process_reg_step',
621
-            'bool'
622
-        );
623
-        $this->checkout->process_form_submission = filter_var(
624
-            $this->checkout->action !== 'display_spco_reg_step'
625
-                ? $this->checkout->process_form_submission
626
-                : false,
627
-            FILTER_VALIDATE_BOOLEAN
628
-        );
629
-        // $this->_display_request_vars();
630
-    }
631
-
632
-
633
-    /**
634
-     *  _display_request_vars
635
-     *
636
-     * @return    void
637
-     */
638
-    protected function _display_request_vars()
639
-    {
640
-        if (! WP_DEBUG) {
641
-            return;
642
-        }
643
-        EEH_Debug_Tools::printr($this->request->requestParams(), 'requestParams', __FILE__, __LINE__);
644
-        EEH_Debug_Tools::printr($this->checkout->step, '$this->checkout->step', __FILE__, __LINE__);
645
-        EEH_Debug_Tools::printr($this->checkout->edit_step, '$this->checkout->edit_step', __FILE__, __LINE__);
646
-        EEH_Debug_Tools::printr($this->checkout->action, '$this->checkout->action', __FILE__, __LINE__);
647
-        EEH_Debug_Tools::printr($this->checkout->reg_url_link, '$this->checkout->reg_url_link', __FILE__, __LINE__);
648
-        EEH_Debug_Tools::printr($this->checkout->revisit, '$this->checkout->revisit', __FILE__, __LINE__);
649
-        EEH_Debug_Tools::printr(
650
-            $this->checkout->generate_reg_form,
651
-            '$this->checkout->generate_reg_form',
652
-            __FILE__,
653
-            __LINE__
654
-        );
655
-        EEH_Debug_Tools::printr(
656
-            $this->checkout->process_form_submission,
657
-            '$this->checkout->process_form_submission',
658
-            __FILE__,
659
-            __LINE__
660
-        );
661
-    }
662
-
663
-
664
-    /**
665
-     * _block_bots
666
-     * checks that the incoming request has either of the following set:
667
-     *  a uts (unix timestamp) which indicates that the request was redirected from the Ticket Selector
668
-     *  a REG URL Link, which indicates that the request is a return visit to SPCO for a valid TXN
669
-     * so if you're not coming from the Ticket Selector nor returning for a valid IP...
670
-     * then where you coming from man?
671
-     *
672
-     * @return boolean
673
-     */
674
-    private function _block_bots()
675
-    {
676
-        $invalid_checkout_access = EED_Invalid_Checkout_Access::getInvalidCheckoutAccess();
677
-        if ($invalid_checkout_access->checkoutAccessIsInvalid($this->checkout)) {
678
-            return true;
679
-        }
680
-        return false;
681
-    }
682
-
683
-
684
-    /**
685
-     *    _get_first_step
686
-     *  gets slug for first step in $_reg_steps_array
687
-     *
688
-     * @return    string
689
-     */
690
-    private function _get_first_step()
691
-    {
692
-        $first_step = reset(EED_Single_Page_Checkout::$_reg_steps_array);
693
-        return isset($first_step['slug']) ? $first_step['slug'] : 'attendee_information';
694
-    }
695
-
696
-
697
-    /**
698
-     * instantiates each reg step based on the loaded reg_steps array
699
-     *
700
-     * @return    bool
701
-     * @throws EE_Error
702
-     * @throws InvalidArgumentException
703
-     * @throws InvalidDataTypeException
704
-     * @throws InvalidInterfaceException
705
-     * @throws ReflectionException
706
-     */
707
-    private function _load_and_instantiate_reg_steps()
708
-    {
709
-        do_action('AHEE__Single_Page_Checkout___load_and_instantiate_reg_steps__start', $this->checkout);
710
-        // have reg_steps already been instantiated ?
711
-        if (
712
-            empty($this->checkout->reg_steps)
713
-            || apply_filters('FHEE__Single_Page_Checkout__load_reg_steps__reload_reg_steps', false, $this->checkout)
714
-        ) {
715
-            // if not, then loop through raw reg steps array
716
-            foreach (EED_Single_Page_Checkout::$_reg_steps_array as $order => $reg_step) {
717
-                if (! $this->_load_and_instantiate_reg_step($reg_step, $order)) {
718
-                    return false;
719
-                }
720
-            }
721
-            if (isset($this->checkout->reg_steps['registration_confirmation'])) {
722
-                // skip the registration_confirmation page ?
723
-                if (EE_Registry::instance()->CFG->registration->skip_reg_confirmation) {
724
-                    // just remove it from the reg steps array
725
-                    $this->checkout->remove_reg_step('registration_confirmation', false);
726
-                } elseif (
727
-                    EE_Registry::instance()->CFG->registration->reg_confirmation_last
728
-                ) {
729
-                    // set the order to something big like 100
730
-                    $this->checkout->set_reg_step_order('registration_confirmation');
731
-                }
732
-            }
733
-            // filter the array for good luck
734
-            $this->checkout->reg_steps = apply_filters(
735
-                'FHEE__Single_Page_Checkout__load_reg_steps__reg_steps',
736
-                $this->checkout->reg_steps
737
-            );
738
-            // finally re-sort based on the reg step class order properties
739
-            $this->checkout->sort_reg_steps();
740
-        } else {
741
-            foreach ($this->checkout->reg_steps as $reg_step) {
742
-                // set all current step stati to FALSE
743
-                $reg_step->set_is_current_step(false);
744
-            }
745
-        }
746
-        if (empty($this->checkout->reg_steps)) {
747
-            EE_Error::add_error(
748
-                esc_html__('No Reg Steps were loaded..', 'event_espresso'),
749
-                __FILE__,
750
-                __FUNCTION__,
751
-                __LINE__
752
-            );
753
-            return false;
754
-        }
755
-        // make reg step details available to JS
756
-        $this->checkout->set_reg_step_JSON_info();
757
-        return true;
758
-    }
759
-
760
-
761
-    /**
762
-     *     _load_and_instantiate_reg_step
763
-     *
764
-     * @param array $reg_step
765
-     * @param int   $order
766
-     * @return bool
767
-     * @throws EE_Error
768
-     * @throws ReflectionException
769
-     */
770
-    private function _load_and_instantiate_reg_step($reg_step = array(), $order = 0)
771
-    {
772
-        // we need a file_path, class_name, and slug to add a reg step
773
-        if (isset($reg_step['file_path'], $reg_step['class_name'], $reg_step['slug'])) {
774
-            // if editing a specific step, but this is NOT that step... (and it's not the 'finalize_registration' step)
775
-            if (
776
-                $this->checkout->reg_url_link
777
-                && $this->checkout->step !== $reg_step['slug']
778
-                && $reg_step['slug'] !== 'finalize_registration'
779
-                // normally at this point we would NOT load the reg step, but this filter can change that
780
-                && apply_filters(
781
-                    'FHEE__Single_Page_Checkout___load_and_instantiate_reg_step__bypass_reg_step',
782
-                    true,
783
-                    $reg_step,
784
-                    $this->checkout
785
-                )
786
-            ) {
787
-                return true;
788
-            }
789
-
790
-            // instantiate step class using file path and class name
791
-            $reg_step_obj = EE_Registry::instance()->load_file(
792
-                $reg_step['file_path'],
793
-                $reg_step['class_name'],
794
-                'class',
795
-                [$this->checkout],
796
-                false
797
-            );
798
-            // did we gets the goods ?
799
-            if ($reg_step_obj instanceof EE_SPCO_Reg_Step) {
800
-                // set reg step order based on config
801
-                $reg_step_obj->set_order($order);
802
-                // add instantiated reg step object to the master reg steps array
803
-                $this->checkout->add_reg_step($reg_step_obj);
804
-            } else {
805
-                EE_Error::add_error(
806
-                    esc_html__('The current step could not be set.', 'event_espresso'),
807
-                    __FILE__,
808
-                    __FUNCTION__,
809
-                    __LINE__
810
-                );
811
-                return false;
812
-            }
813
-        } else {
814
-            if (WP_DEBUG) {
815
-                EE_Error::add_error(
816
-                    sprintf(
817
-                        esc_html__(
818
-                            'A registration step could not be loaded. One or more of the following data points is invalid:%4$s%5$sFile Path: %1$s%6$s%5$sClass Name: %2$s%6$s%5$sSlug: %3$s%6$s%7$s',
819
-                            'event_espresso'
820
-                        ),
821
-                        isset($reg_step['file_path']) ? $reg_step['file_path'] : '',
822
-                        isset($reg_step['class_name']) ? $reg_step['class_name'] : '',
823
-                        isset($reg_step['slug']) ? $reg_step['slug'] : '',
824
-                        '<ul>',
825
-                        '<li>',
826
-                        '</li>',
827
-                        '</ul>'
828
-                    ),
829
-                    __FILE__,
830
-                    __FUNCTION__,
831
-                    __LINE__
832
-                );
833
-            }
834
-            return false;
835
-        }
836
-        return true;
837
-    }
838
-
839
-
840
-    /**
841
-     * _verify_transaction_and_get_registrations
842
-     *
843
-     * @return bool
844
-     * @throws EE_Error
845
-     * @throws ReflectionException
846
-     */
847
-    private function _verify_transaction_and_get_registrations()
848
-    {
849
-        // was there already a valid transaction in the checkout from the session ?
850
-        if (! $this->checkout->transaction instanceof EE_Transaction) {
851
-            // get transaction from db or session
852
-            $this->checkout->transaction = $this->checkout->reg_url_link && ! is_admin()
853
-                ? $this->_get_transaction_and_cart_for_previous_visit()
854
-                : $this->_get_cart_for_current_session_and_setup_new_transaction();
855
-            if (! $this->checkout->transaction instanceof EE_Transaction) {
856
-                EE_Error::add_error(
857
-                    esc_html__(
858
-                        'Your Registration and Transaction information could not be retrieved from the db.',
859
-                        'event_espresso'
860
-                    ),
861
-                    __FILE__,
862
-                    __FUNCTION__,
863
-                    __LINE__
864
-                );
865
-                $this->checkout->transaction = EE_Transaction::new_instance();
866
-                // add some style and make it dance
867
-                $this->add_styles_and_scripts($this);
868
-                EED_Single_Page_Checkout::$_initialized = true;
869
-                return false;
870
-            }
871
-            // and the registrations for the transaction
872
-            $this->_get_registrations($this->checkout->transaction);
873
-        }
874
-        return true;
875
-    }
876
-
877
-
878
-    /**
879
-     * _get_transaction_and_cart_for_previous_visit
880
-     *
881
-     * @return EE_Transaction|null
882
-     * @throws EE_Error
883
-     * @throws ReflectionException
884
-     */
885
-    private function _get_transaction_and_cart_for_previous_visit()
886
-    {
887
-        /** @var $TXN_model EEM_Transaction */
888
-        $TXN_model = EE_Registry::instance()->load_model('Transaction');
889
-        // because the reg_url_link is present in the request,
890
-        // this is a return visit to SPCO, so we'll get the transaction data from the db
891
-        $transaction = $TXN_model->get_transaction_from_reg_url_link($this->checkout->reg_url_link);
892
-        // verify transaction
893
-        if ($transaction instanceof EE_Transaction) {
894
-            // and get the cart that was used for that transaction
895
-            $this->checkout->cart = $this->_get_cart_for_transaction($transaction);
896
-            return $transaction;
897
-        }
898
-        EE_Error::add_error(
899
-            esc_html__('Your Registration and Transaction information could not be retrieved from the db.', 'event_espresso'),
900
-            __FILE__,
901
-            __FUNCTION__,
902
-            __LINE__
903
-        );
904
-        return null;
905
-    }
906
-
907
-
908
-    /**
909
-     * _get_cart_for_transaction
910
-     *
911
-     * @param EE_Transaction $transaction
912
-     * @return EE_Cart
913
-     */
914
-    private function _get_cart_for_transaction($transaction)
915
-    {
916
-        return $this->checkout->get_cart_for_transaction($transaction);
917
-    }
918
-
919
-
920
-    /**
921
-     * get_cart_for_transaction
922
-     *
923
-     * @param EE_Transaction $transaction
924
-     * @return EE_Cart
925
-     */
926
-    public function get_cart_for_transaction(EE_Transaction $transaction)
927
-    {
928
-        return $this->checkout->get_cart_for_transaction($transaction);
929
-    }
930
-
931
-
932
-    /**
933
-     * _get_transaction_and_cart_for_current_session
934
-     *    generates a new EE_Transaction object and adds it to the $_transaction property.
935
-     *
936
-     * @return EE_Transaction
937
-     * @throws EE_Error
938
-     * @throws ReflectionException
939
-     */
940
-    private function _get_cart_for_current_session_and_setup_new_transaction()
941
-    {
942
-        //  if there's no transaction, then this is the FIRST visit to SPCO
943
-        // so load up the cart ( passing nothing for the TXN because it doesn't exist yet )
944
-        $this->checkout->cart = $this->_get_cart_for_transaction(null);
945
-        // and then create a new transaction
946
-        $transaction = $this->_initialize_transaction();
947
-        // verify transaction
948
-        if ($transaction instanceof EE_Transaction) {
949
-            // save it so that we have an ID for other objects to use
950
-            $transaction->save();
951
-            // and save TXN data to the cart
952
-            $this->checkout->cart->get_grand_total()->save_this_and_descendants_to_txn($transaction->ID());
953
-        } else {
954
-            EE_Error::add_error(
955
-                esc_html__('A Valid Transaction could not be initialized.', 'event_espresso'),
956
-                __FILE__,
957
-                __FUNCTION__,
958
-                __LINE__
959
-            );
960
-        }
961
-        return $transaction;
962
-    }
963
-
964
-
965
-    /**
966
-     *    generates a new EE_Transaction object and adds it to the $_transaction property.
967
-     *
968
-     * @return EE_Transaction|null
969
-     */
970
-    private function _initialize_transaction()
971
-    {
972
-        try {
973
-            // ensure cart totals have been calculated
974
-            $this->checkout->cart->get_grand_total()->recalculate_total_including_taxes();
975
-            // grab the cart grand total
976
-            $cart_total = $this->checkout->cart->get_cart_grand_total();
977
-            // create new TXN
978
-            $transaction = EE_Transaction::new_instance(
979
-                array(
980
-                    'TXN_reg_steps' => $this->checkout->initialize_txn_reg_steps_array(),
981
-                    'TXN_total'     => $cart_total > 0 ? $cart_total : 0,
982
-                    'TXN_paid'      => 0,
983
-                    'STS_ID'        => EEM_Transaction::failed_status_code,
984
-                )
985
-            );
986
-            // save it so that we have an ID for other objects to use
987
-            $transaction->save();
988
-            // set cron job for following up on TXNs after their session has expired
989
-            EE_Cron_Tasks::schedule_expired_transaction_check(
990
-                EE_Registry::instance()->SSN->expiration() + 1,
991
-                $transaction->ID()
992
-            );
993
-            return $transaction;
994
-        } catch (Exception $e) {
995
-            EE_Error::add_error($e->getMessage(), __FILE__, __FUNCTION__, __LINE__);
996
-        }
997
-        return null;
998
-    }
999
-
1000
-
1001
-    /**
1002
-     * _get_registrations
1003
-     *
1004
-     * @param EE_Transaction $transaction
1005
-     * @return void
1006
-     * @throws EE_Error
1007
-     * @throws ReflectionException
1008
-     */
1009
-    private function _get_registrations(EE_Transaction $transaction)
1010
-    {
1011
-        // first step: grab the registrants  { : o
1012
-        $registrations = $transaction->registrations($this->checkout->reg_cache_where_params);
1013
-        $this->checkout->total_ticket_count = count($registrations);
1014
-        // verify registrations have been set
1015
-        if (empty($registrations)) {
1016
-            // if no cached registrations, then check the db
1017
-            $registrations = $transaction->registrations($this->checkout->reg_cache_where_params);
1018
-            // still nothing ? well as long as this isn't a revisit
1019
-            if (empty($registrations) && ! $this->checkout->revisit) {
1020
-                // generate new registrations from scratch
1021
-                $registrations = $this->_initialize_registrations($transaction);
1022
-            }
1023
-        }
1024
-        // sort by their original registration order
1025
-        usort($registrations, array('EED_Single_Page_Checkout', 'sort_registrations_by_REG_count'));
1026
-        // then loop thru the array
1027
-        foreach ($registrations as $registration) {
1028
-            // verify each registration
1029
-            if ($registration instanceof EE_Registration) {
1030
-                // we display all attendee info for the primary registrant
1031
-                if (
1032
-                    $this->checkout->reg_url_link === $registration->reg_url_link()
1033
-                    && $registration->is_primary_registrant()
1034
-                ) {
1035
-                    $this->checkout->primary_revisit = true;
1036
-                    break;
1037
-                }
1038
-                if ($this->checkout->revisit && $this->checkout->reg_url_link !== $registration->reg_url_link()) {
1039
-                    // but hide info if it doesn't belong to you
1040
-                    $transaction->clear_cache('Registration', $registration->ID());
1041
-                    $this->checkout->total_ticket_count--;
1042
-                }
1043
-                $this->checkout->set_reg_status_updated($registration->ID(), false);
1044
-            }
1045
-        }
1046
-    }
1047
-
1048
-
1049
-    /**
1050
-     *    adds related EE_Registration objects for each ticket in the cart to the current EE_Transaction object
1051
-     *
1052
-     * @param EE_Transaction $transaction
1053
-     * @return    array
1054
-     * @throws EE_Error
1055
-     * @throws ReflectionException
1056
-     */
1057
-    private function _initialize_registrations(EE_Transaction $transaction)
1058
-    {
1059
-        $att_nmbr = 0;
1060
-        $registrations = array();
1061
-        if ($transaction instanceof EE_Transaction) {
1062
-            /** @type EE_Registration_Processor $registration_processor */
1063
-            $registration_processor = EE_Registry::instance()->load_class('Registration_Processor');
1064
-            $this->checkout->total_ticket_count = $this->checkout->cart->all_ticket_quantity_count();
1065
-            // now let's add the cart items to the $transaction
1066
-            foreach ($this->checkout->cart->get_tickets() as $line_item) {
1067
-                // do the following for each ticket of this type they selected
1068
-                for ($x = 1; $x <= $line_item->quantity(); $x++) {
1069
-                    $att_nmbr++;
1070
-                    /** @var CreateRegistrationCommand $CreateRegistrationCommand */
1071
-                    $CreateRegistrationCommand = EE_Registry::instance()->create(
1072
-                        CreateRegistrationCommand::class,
1073
-                        [
1074
-                            $transaction,
1075
-                            $line_item,
1076
-                            $att_nmbr,
1077
-                            $this->checkout->total_ticket_count,
1078
-                        ]
1079
-                    );
1080
-                    // override capabilities for frontend registrations
1081
-                    if ($this->request->isFrontend()) {
1082
-                        $CreateRegistrationCommand->setCapCheck(
1083
-                            new PublicCapabilities('', 'create_new_registration')
1084
-                        );
1085
-                    }
1086
-                    $registration = EE_Registry::instance()->BUS->execute($CreateRegistrationCommand);
1087
-                    if (! $registration instanceof EE_Registration) {
1088
-                        throw new InvalidEntityException($registration, 'EE_Registration');
1089
-                    }
1090
-                    $registrations[ $registration->ID() ] = $registration;
1091
-                }
1092
-            }
1093
-            $registration_processor->fix_reg_final_price_rounding_issue($transaction);
1094
-        }
1095
-        return $registrations;
1096
-    }
1097
-
1098
-
1099
-    /**
1100
-     * sorts registrations by REG_count
1101
-     *
1102
-     * @param EE_Registration $reg_A
1103
-     * @param EE_Registration $reg_B
1104
-     * @return int
1105
-     */
1106
-    public static function sort_registrations_by_REG_count(EE_Registration $reg_A, EE_Registration $reg_B)
1107
-    {
1108
-        // this shouldn't ever happen within the same TXN, but oh well
1109
-        if ($reg_A->count() === $reg_B->count()) {
1110
-            return 0;
1111
-        }
1112
-        return ($reg_A->count() > $reg_B->count()) ? 1 : -1;
1113
-    }
1114
-
1115
-
1116
-    /**
1117
-     *    _final_verifications
1118
-     * just makes sure that everything is set up correctly before proceeding
1119
-     *
1120
-     * @return    bool
1121
-     * @throws EE_Error
1122
-     * @throws ReflectionException
1123
-     */
1124
-    private function _final_verifications()
1125
-    {
1126
-        // filter checkout
1127
-        $this->checkout = apply_filters(
1128
-            'FHEE__EED_Single_Page_Checkout___final_verifications__checkout',
1129
-            $this->checkout
1130
-        );
1131
-        // verify that current step is still set correctly
1132
-        if (! $this->checkout->current_step instanceof EE_SPCO_Reg_Step) {
1133
-            EE_Error::add_error(
1134
-                esc_html__(
1135
-                    'We\'re sorry but the registration process can not proceed because one or more registration steps were not setup correctly. Please refresh the page and try again or contact support.',
1136
-                    'event_espresso'
1137
-                ),
1138
-                __FILE__,
1139
-                __FUNCTION__,
1140
-                __LINE__
1141
-            );
1142
-            return false;
1143
-        }
1144
-        // if returning to SPCO, then verify that primary registrant is set
1145
-        if (! empty($this->checkout->reg_url_link)) {
1146
-            $valid_registrant = $this->checkout->transaction->primary_registration();
1147
-            if (! $valid_registrant instanceof EE_Registration) {
1148
-                EE_Error::add_error(
1149
-                    esc_html__(
1150
-                        'We\'re sorry but there appears to be an error with the "reg_url_link" or the primary registrant for this transaction. Please refresh the page and try again or contact support.',
1151
-                        'event_espresso'
1152
-                    ),
1153
-                    __FILE__,
1154
-                    __FUNCTION__,
1155
-                    __LINE__
1156
-                );
1157
-                return false;
1158
-            }
1159
-            $valid_registrant = null;
1160
-            foreach ($this->checkout->transaction->registrations($this->checkout->reg_cache_where_params) as $registration) {
1161
-                if (
1162
-                    $registration instanceof EE_Registration
1163
-                    && $registration->reg_url_link() === $this->checkout->reg_url_link
1164
-                ) {
1165
-                    $valid_registrant = $registration;
1166
-                }
1167
-            }
1168
-            if (! $valid_registrant instanceof EE_Registration) {
1169
-                // hmmm... maybe we have the wrong session because the user is opening multiple tabs ?
1170
-                if (EED_Single_Page_Checkout::$_checkout_verified) {
1171
-                    // clear the session, mark the checkout as unverified, and try again
1172
-                    EE_Registry::instance()->SSN->clear_session(__CLASS__, __FUNCTION__);
1173
-                    EED_Single_Page_Checkout::$_initialized = false;
1174
-                    EED_Single_Page_Checkout::$_checkout_verified = false;
1175
-                    $this->_initialize();
1176
-                    EE_Error::reset_notices();
1177
-                    return false;
1178
-                }
1179
-                EE_Error::add_error(
1180
-                    esc_html__(
1181
-                        'We\'re sorry but there appears to be an error with the "reg_url_link" or the transaction itself. Please refresh the page and try again or contact support.',
1182
-                        'event_espresso'
1183
-                    ),
1184
-                    __FILE__,
1185
-                    __FUNCTION__,
1186
-                    __LINE__
1187
-                );
1188
-                return false;
1189
-            }
1190
-        }
1191
-        // now that things have been kinda sufficiently verified,
1192
-        // let's add the checkout to the session so that it's available to other systems
1193
-        EE_Registry::instance()->SSN->set_checkout($this->checkout);
1194
-        return true;
1195
-    }
1196
-
1197
-
1198
-    /**
1199
-     *    _initialize_reg_steps
1200
-     * first makes sure that EE_Transaction_Processor::set_reg_step_initiated() is called as required
1201
-     * then loops thru all of the active reg steps and calls the initialize_reg_step() method
1202
-     *
1203
-     * @param bool $reinitializing
1204
-     * @throws EE_Error
1205
-     */
1206
-    private function _initialize_reg_steps($reinitializing = false)
1207
-    {
1208
-        $this->checkout->set_reg_step_initiated($this->checkout->current_step);
1209
-        // loop thru all steps to call their individual "initialize" methods and set i18n strings for JS
1210
-        foreach ($this->checkout->reg_steps as $reg_step) {
1211
-            if (! $reg_step->initialize_reg_step()) {
1212
-                // if not initialized then maybe this step is being removed...
1213
-                if (! $reinitializing && $reg_step->is_current_step()) {
1214
-                    // if it was the current step, then we need to start over here
1215
-                    $this->_initialize_reg_steps(true);
1216
-                    return;
1217
-                }
1218
-                continue;
1219
-            }
1220
-            // add css and JS for current step
1221
-            $this->add_styles_and_scripts($reg_step);
1222
-            if ($reg_step->is_current_step()) {
1223
-                // the text that appears on the reg step form submit button
1224
-                $reg_step->set_submit_button_text();
1225
-            }
1226
-        }
1227
-        // dynamically creates hook point like: AHEE__Single_Page_Checkout___initialize_reg_step__attendee_information
1228
-        do_action(
1229
-            "AHEE__Single_Page_Checkout___initialize_reg_step__{$this->checkout->current_step->slug()}",
1230
-            $this->checkout->current_step
1231
-        );
1232
-    }
1233
-
1234
-
1235
-    /**
1236
-     * _check_form_submission
1237
-     *
1238
-     * @return boolean
1239
-     * @throws EE_Error
1240
-     * @throws ReflectionException
1241
-     */
1242
-    private function _check_form_submission()
1243
-    {
1244
-        // does this request require the reg form to be generated ?
1245
-        if ($this->checkout->generate_reg_form) {
1246
-            // ever heard that song by Blue Rodeo ?
1247
-            try {
1248
-                $this->checkout->current_step->reg_form = $this->checkout->current_step->generate_reg_form();
1249
-                // if not displaying a form, then check for form submission
1250
-                if (
1251
-                    $this->checkout->process_form_submission
1252
-                    && $this->checkout->current_step->reg_form->was_submitted()
1253
-                ) {
1254
-                    // clear out any old data in case this step is being run again
1255
-                    $this->checkout->current_step->set_valid_data(array());
1256
-                    // capture submitted form data
1257
-                    $request_data = $this->request->requestParams();
1258
-                    $this->checkout->current_step->reg_form->receive_form_submission(
1259
-                        (array) apply_filters(
1260
-                            'FHEE__Single_Page_Checkout___check_form_submission__request_params',
1261
-                            $request_data,
1262
-                            $this->checkout
1263
-                        )
1264
-                    );
1265
-                    // validate submitted form data
1266
-                    if (! $this->checkout->continue_reg || ! $this->checkout->current_step->reg_form->is_valid()) {
1267
-                        // thou shall not pass !!!
1268
-                        $this->checkout->continue_reg = false;
1269
-                        // any form validation errors?
1270
-                        if ($this->checkout->current_step->reg_form->submission_error_message() !== '') {
1271
-                            EE_Error::add_error(
1272
-                                $this->checkout->current_step->reg_form->submission_error_message(),
1273
-                                __FILE__,
1274
-                                __FUNCTION__,
1275
-                                __LINE__
1276
-                            );
1277
-                        }
1278
-                        // well not really... what will happen is
1279
-                        // we'll just get redirected back to redo the current step
1280
-                        $this->go_to_next_step();
1281
-                        return false;
1282
-                    }
1283
-                }
1284
-            } catch (EE_Error $e) {
1285
-                $e->get_error();
1286
-            }
1287
-        }
1288
-        return true;
1289
-    }
1290
-
1291
-
1292
-    /**
1293
-     * _process_action
1294
-     *
1295
-     * @return void
1296
-     * @throws EE_Error
1297
-     * @throws ReflectionException
1298
-     */
1299
-    private function _process_form_action()
1300
-    {
1301
-        // what cha wanna do?
1302
-        switch ($this->checkout->action) {
1303
-            // AJAX next step reg form
1304
-            case 'display_spco_reg_step':
1305
-                $this->checkout->redirect = false;
1306
-                if ($this->request->isAjax()) {
1307
-                    $this->checkout->json_response->set_reg_step_html(
1308
-                        $this->checkout->current_step->display_reg_form()
1309
-                    );
1310
-                }
1311
-                break;
1312
-            default:
1313
-                // meh... do one of those other steps first
1314
-                if (
1315
-                    ! empty($this->checkout->action)
1316
-                    && is_callable(array($this->checkout->current_step, $this->checkout->action))
1317
-                ) {
1318
-                    // dynamically creates hook point like:
1319
-                    //   AHEE__Single_Page_Checkout__before_attendee_information__process_reg_step
1320
-                    do_action(
1321
-                        "AHEE__Single_Page_Checkout__before_{$this->checkout->current_step->slug()}__{$this->checkout->action}",
1322
-                        $this->checkout->current_step
1323
-                    );
1324
-                    $process_reg_step = apply_filters(
1325
-                        "AHEE__Single_Page_Checkout__process_reg_step__{$this->checkout->current_step->slug()}__{$this->checkout->action}",
1326
-                        true,
1327
-                        $this->checkout->current_step,
1328
-                        $this
1329
-                    );
1330
-                    // call action on current step
1331
-                    if ($process_reg_step && call_user_func([$this->checkout->current_step, $this->checkout->action])) {
1332
-                        // good registrant, you get to proceed
1333
-                        if (
1334
-                            $this->checkout->current_step->success_message() !== ''
1335
-                            && apply_filters(
1336
-                                'FHEE__Single_Page_Checkout___process_form_action__display_success',
1337
-                                false
1338
-                            )
1339
-                        ) {
1340
-                            EE_Error::add_success(
1341
-                                $this->checkout->current_step->success_message()
1342
-                                . '<br />' . $this->checkout->next_step->_instructions()
1343
-                            );
1344
-                        }
1345
-                        // pack it up, pack it in...
1346
-                        $this->_setup_redirect();
1347
-                    }
1348
-                    // dynamically creates hook point like:
1349
-                    //  AHEE__Single_Page_Checkout__after_payment_options__process_reg_step
1350
-                    do_action(
1351
-                        "AHEE__Single_Page_Checkout__after_{$this->checkout->current_step->slug()}__{$this->checkout->action}",
1352
-                        $this->checkout->current_step
1353
-                    );
1354
-                } else {
1355
-                    EE_Error::add_error(
1356
-                        sprintf(
1357
-                            esc_html__(
1358
-                                'The requested form action "%s" does not exist for the current "%s" registration step.',
1359
-                                'event_espresso'
1360
-                            ),
1361
-                            $this->checkout->action,
1362
-                            $this->checkout->current_step->name()
1363
-                        ),
1364
-                        __FILE__,
1365
-                        __FUNCTION__,
1366
-                        __LINE__
1367
-                    );
1368
-                }
1369
-            // end default
1370
-        }
1371
-        // store our progress so far
1372
-        $this->checkout->stash_transaction_and_checkout();
1373
-        // advance to the next step! If you pass GO, collect $200
1374
-        $this->go_to_next_step();
1375
-    }
1376
-
1377
-
1378
-    /**
1379
-     * @param EED_Single_Page_Checkout|EE_SPCO_Reg_Step $target an object with the method `translate_js_strings` and
1380
-     *                                                          `enqueue_styles_and_scripts`.
1381
-     * @return        void
1382
-     */
1383
-    public function add_styles_and_scripts($target)
1384
-    {
1385
-        // i18n
1386
-        $target->translate_js_strings();
1387
-        if ($this->checkout->admin_request) {
1388
-            add_action('admin_enqueue_scripts', array($target, 'enqueue_styles_and_scripts'), 10);
1389
-        } else {
1390
-            add_action('wp_enqueue_scripts', array($target, 'enqueue_styles_and_scripts'), 10);
1391
-        }
1392
-    }
1393
-
1394
-    /**
1395
-     *        translate_js_strings
1396
-     *
1397
-     * @return        void
1398
-     */
1399
-    public function translate_js_strings()
1400
-    {
1401
-        EE_Registry::$i18n_js_strings['revisit'] = $this->checkout->revisit;
1402
-        EE_Registry::$i18n_js_strings['e_reg_url_link'] = $this->checkout->reg_url_link;
1403
-        EE_Registry::$i18n_js_strings['server_error'] = esc_html__(
1404
-            'An unknown error occurred on the server while attempting to process your request. Please refresh the page and try again or contact support.',
1405
-            'event_espresso'
1406
-        );
1407
-        EE_Registry::$i18n_js_strings['invalid_json_response'] = esc_html__(
1408
-            'An invalid response was returned from the server while attempting to process your request. Please refresh the page and try again or contact support.',
1409
-            'event_espresso'
1410
-        );
1411
-        EE_Registry::$i18n_js_strings['validation_error'] = esc_html__(
1412
-            'There appears to be a problem with the form validation configuration! Please check the admin settings or contact support.',
1413
-            'event_espresso'
1414
-        );
1415
-        EE_Registry::$i18n_js_strings['invalid_payment_method'] = esc_html__(
1416
-            'There appears to be a problem with the payment method configuration! Please refresh the page and try again or contact support.',
1417
-            'event_espresso'
1418
-        );
1419
-        EE_Registry::$i18n_js_strings['reg_step_error'] = esc_html__(
1420
-            'This registration step could not be completed. Please refresh the page and try again.',
1421
-            'event_espresso'
1422
-        );
1423
-        EE_Registry::$i18n_js_strings['invalid_coupon'] = esc_html__(
1424
-            'We\'re sorry but that coupon code does not appear to be valid. If this is incorrect, please contact the site administrator.',
1425
-            'event_espresso'
1426
-        );
1427
-        EE_Registry::$i18n_js_strings['process_registration'] = sprintf(
1428
-            esc_html__(
1429
-                'Please wait while we process your registration.%sDo not refresh the page or navigate away while this is happening.%sThank you for your patience.',
1430
-                'event_espresso'
1431
-            ),
1432
-            '<br/>',
1433
-            '<br/>'
1434
-        );
1435
-        EE_Registry::$i18n_js_strings['language'] = get_bloginfo('language');
1436
-        EE_Registry::$i18n_js_strings['EESID'] = EE_Registry::instance()->SSN->id();
1437
-        EE_Registry::$i18n_js_strings['currency'] = EE_Registry::instance()->CFG->currency;
1438
-        EE_Registry::$i18n_js_strings['datepicker_yearRange'] = '-150:+20';
1439
-        EE_Registry::$i18n_js_strings['timer_years'] = esc_html__('years', 'event_espresso');
1440
-        EE_Registry::$i18n_js_strings['timer_months'] = esc_html__('months', 'event_espresso');
1441
-        EE_Registry::$i18n_js_strings['timer_weeks'] = esc_html__('weeks', 'event_espresso');
1442
-        EE_Registry::$i18n_js_strings['timer_days'] = esc_html__('days', 'event_espresso');
1443
-        EE_Registry::$i18n_js_strings['timer_hours'] = esc_html__('hours', 'event_espresso');
1444
-        EE_Registry::$i18n_js_strings['timer_minutes'] = esc_html__('minutes', 'event_espresso');
1445
-        EE_Registry::$i18n_js_strings['timer_seconds'] = esc_html__('seconds', 'event_espresso');
1446
-        EE_Registry::$i18n_js_strings['timer_year'] = esc_html__('year', 'event_espresso');
1447
-        EE_Registry::$i18n_js_strings['timer_month'] = esc_html__('month', 'event_espresso');
1448
-        EE_Registry::$i18n_js_strings['timer_week'] = esc_html__('week', 'event_espresso');
1449
-        EE_Registry::$i18n_js_strings['timer_day'] = esc_html__('day', 'event_espresso');
1450
-        EE_Registry::$i18n_js_strings['timer_hour'] = esc_html__('hour', 'event_espresso');
1451
-        EE_Registry::$i18n_js_strings['timer_minute'] = esc_html__('minute', 'event_espresso');
1452
-        EE_Registry::$i18n_js_strings['timer_second'] = esc_html__('second', 'event_espresso');
1453
-        EE_Registry::$i18n_js_strings['registration_expiration_notice'] = EED_Single_Page_Checkout::getRegistrationExpirationNotice(
1454
-        );
1455
-        EE_Registry::$i18n_js_strings['ajax_submit'] = apply_filters(
1456
-            'FHEE__Single_Page_Checkout__translate_js_strings__ajax_submit',
1457
-            true
1458
-        );
1459
-        EE_Registry::$i18n_js_strings['session_extension'] = absint(
1460
-            apply_filters('FHEE__EE_Session__extend_expiration__seconds_added', 10 * MINUTE_IN_SECONDS)
1461
-        );
1462
-        EE_Registry::$i18n_js_strings['session_expiration'] = gmdate(
1463
-            'M d, Y H:i:s',
1464
-            EE_Registry::instance()->SSN->expiration() + (get_option('gmt_offset') * HOUR_IN_SECONDS)
1465
-        );
1466
-    }
1467
-
1468
-
1469
-    /**
1470
-     *    enqueue_styles_and_scripts
1471
-     *
1472
-     * @return        void
1473
-     * @throws EE_Error
1474
-     */
1475
-    public function enqueue_styles_and_scripts()
1476
-    {
1477
-        // load css
1478
-        wp_register_style(
1479
-            'single_page_checkout',
1480
-            SPCO_CSS_URL . 'single_page_checkout.css',
1481
-            array('espresso_default'),
1482
-            EVENT_ESPRESSO_VERSION
1483
-        );
1484
-        wp_enqueue_style('single_page_checkout');
1485
-        // load JS
1486
-        wp_register_script(
1487
-            'jquery_plugin',
1488
-            EE_THIRD_PARTY_URL . 'jquery	.plugin.min.js',
1489
-            array('jquery'),
1490
-            '1.0.1',
1491
-            true
1492
-        );
1493
-        wp_register_script(
1494
-            'jquery_countdown',
1495
-            EE_THIRD_PARTY_URL . 'jquery	.countdown.min.js',
1496
-            array('jquery_plugin'),
1497
-            '2.1.0',
1498
-            true
1499
-        );
1500
-        wp_register_script(
1501
-            'single_page_checkout',
1502
-            SPCO_JS_URL . 'single_page_checkout.js',
1503
-            array('espresso_core', 'underscore', 'ee_form_section_validation'),
1504
-            EVENT_ESPRESSO_VERSION,
1505
-            true
1506
-        );
1507
-        if ($this->checkout->registration_form instanceof EE_Form_Section_Proper) {
1508
-            $this->checkout->registration_form->enqueue_js();
1509
-        }
1510
-        if ($this->checkout->current_step->reg_form instanceof EE_Form_Section_Proper) {
1511
-            $this->checkout->current_step->reg_form->enqueue_js();
1512
-        }
1513
-        wp_enqueue_script('single_page_checkout');
1514
-        if (apply_filters('FHEE__registration_page_wrapper_template__display_time_limit', false)) {
1515
-            wp_enqueue_script('jquery_countdown');
1516
-        }
1517
-        /**
1518
-         * global action hook for enqueueing styles and scripts with
1519
-         * spco calls.
1520
-         */
1521
-        do_action('AHEE__EED_Single_Page_Checkout__enqueue_styles_and_scripts', $this);
1522
-        /**
1523
-         * dynamic action hook for enqueueing styles and scripts with spco calls.
1524
-         * The hook will end up being something like:
1525
-         *      AHEE__EED_Single_Page_Checkout__enqueue_styles_and_scripts__attendee_information
1526
-         */
1527
-        do_action(
1528
-            'AHEE__EED_Single_Page_Checkout__enqueue_styles_and_scripts__' . $this->checkout->current_step->slug(),
1529
-            $this
1530
-        );
1531
-    }
1532
-
1533
-
1534
-    /**
1535
-     *    display the Registration Single Page Checkout Form
1536
-     *
1537
-     * @return    void
1538
-     * @throws EE_Error
1539
-     * @throws ReflectionException
1540
-     */
1541
-    private function _display_spco_reg_form()
1542
-    {
1543
-        // if registering via the admin, just display the reg form for the current step
1544
-        if ($this->checkout->admin_request) {
1545
-            EED_Single_Page_Checkout::getResponse()->addOutput($this->checkout->current_step->display_reg_form());
1546
-        } else {
1547
-            // add powered by EE msg
1548
-            add_action('AHEE__SPCO__reg_form_footer', array('EED_Single_Page_Checkout', 'display_registration_footer'));
1549
-            $empty_cart = count(
1550
-                $this->checkout->transaction->registrations($this->checkout->reg_cache_where_params)
1551
-            ) < 1;
1552
-            EE_Registry::$i18n_js_strings['empty_cart'] = $empty_cart;
1553
-            $cookies_not_set_msg = '';
1554
-            if ($empty_cart) {
1555
-                $cookies_not_set_msg = apply_filters(
1556
-                    'FHEE__Single_Page_Checkout__display_spco_reg_form__cookies_not_set_msg',
1557
-                    sprintf(
1558
-                        esc_html__(
1559
-                            '%1$s%3$sIt appears your browser is not currently set to accept Cookies%4$s%5$sIn order to register for events, you need to enable cookies.%7$sIf you require assistance, then click the following link to learn how to %8$senable cookies%9$s%6$s%2$s',
1560
-                            'event_espresso'
1561
-                        ),
1562
-                        '<div class="ee-attention hidden" id="ee-cookies-not-set-msg">',
1563
-                        '</div>',
1564
-                        '<h6 class="important-notice">',
1565
-                        '</h6>',
1566
-                        '<p>',
1567
-                        '</p>',
1568
-                        '<br />',
1569
-                        '<a href="https://www.whatismybrowser.com/guides/how-to-enable-cookies/" target="_blank" rel="noopener noreferrer">',
1570
-                        '</a>'
1571
-                    )
1572
-                );
1573
-            }
1574
-            $this->checkout->registration_form = new EE_Form_Section_Proper(
1575
-                array(
1576
-                    'name'            => 'single-page-checkout',
1577
-                    'html_id'         => 'ee-single-page-checkout-dv',
1578
-                    'layout_strategy' =>
1579
-                        new EE_Template_Layout(
1580
-                            array(
1581
-                                'layout_template_file' => SPCO_TEMPLATES_PATH . 'registration_page_wrapper.template.php',
1582
-                                'template_args'        => array(
1583
-                                    'empty_cart'              => $empty_cart,
1584
-                                    'revisit'                 => $this->checkout->revisit,
1585
-                                    'reg_steps'               => $this->checkout->reg_steps,
1586
-                                    'next_step'               => $this->checkout->next_step instanceof EE_SPCO_Reg_Step
1587
-                                        ? $this->checkout->next_step->slug()
1588
-                                        : '',
1589
-                                    'empty_msg'               => apply_filters(
1590
-                                        'FHEE__Single_Page_Checkout__display_spco_reg_form__empty_msg',
1591
-                                        sprintf(
1592
-                                            esc_html__(
1593
-                                                'You need to %1$sReturn to Events list%2$sselect at least one event%3$s before you can proceed with the registration process.',
1594
-                                                'event_espresso'
1595
-                                            ),
1596
-                                            '<a href="'
1597
-                                            . get_post_type_archive_link('espresso_events')
1598
-                                            . '" title="',
1599
-                                            '">',
1600
-                                            '</a>'
1601
-                                        )
1602
-                                    ),
1603
-                                    'cookies_not_set_msg'     => $cookies_not_set_msg,
1604
-                                    'registration_time_limit' => $this->checkout->get_registration_time_limit(),
1605
-                                    'session_expiration'      => gmdate(
1606
-                                        'M d, Y H:i:s',
1607
-                                        EE_Registry::instance()->SSN->expiration()
1608
-                                        + (get_option('gmt_offset') * HOUR_IN_SECONDS)
1609
-                                    ),
1610
-                                ),
1611
-                            )
1612
-                        ),
1613
-                )
1614
-            );
1615
-            // load template and add to output sent that gets filtered into the_content()
1616
-            EED_Single_Page_Checkout::getResponse()->addOutput($this->checkout->registration_form->get_html());
1617
-        }
1618
-    }
1619
-
1620
-
1621
-    /**
1622
-     *    add_extra_finalize_registration_inputs
1623
-     *
1624
-     * @param $next_step
1625
-     * @internal  param string $label
1626
-     * @return void
1627
-     */
1628
-    public function add_extra_finalize_registration_inputs($next_step)
1629
-    {
1630
-        if ($next_step === 'finalize_registration') {
1631
-            echo '<div id="spco-extra-finalize_registration-inputs-dv"></div>';
1632
-        }
1633
-    }
1634
-
1635
-
1636
-    /**
1637
-     *    display_registration_footer
1638
-     *
1639
-     * @return    string
1640
-     */
1641
-    public static function display_registration_footer()
1642
-    {
1643
-        if (
1644
-            apply_filters(
1645
-                'FHEE__EE_Front__Controller__show_reg_footer',
1646
-                EE_Registry::instance()->CFG->admin->show_reg_footer
1647
-            )
1648
-        ) {
1649
-            add_filter(
1650
-                'FHEE__EEH_Template__powered_by_event_espresso__url',
1651
-                function ($url) {
1652
-                    return apply_filters('FHEE__EE_Front_Controller__registration_footer__url', $url);
1653
-                }
1654
-            );
1655
-            echo apply_filters(
1656
-                'FHEE__EE_Front_Controller__display_registration_footer',
1657
-                EEH_Template::powered_by_event_espresso(
1658
-                    '',
1659
-                    'espresso-registration-footer-dv',
1660
-                    array('utm_content' => 'registration_checkout')
1661
-                )
1662
-            );
1663
-        }
1664
-        return '';
1665
-    }
1666
-
1667
-
1668
-    /**
1669
-     *    unlock_transaction
1670
-     *
1671
-     * @return    void
1672
-     * @throws EE_Error
1673
-     * @throws ReflectionException
1674
-     */
1675
-    public function unlock_transaction()
1676
-    {
1677
-        if ($this->checkout->transaction instanceof EE_Transaction) {
1678
-            $this->checkout->transaction->unlock();
1679
-        }
1680
-    }
1681
-
1682
-
1683
-    /**
1684
-     *        _setup_redirect
1685
-     *
1686
-     * @return void
1687
-     */
1688
-    private function _setup_redirect()
1689
-    {
1690
-        if ($this->checkout->continue_reg && $this->checkout->next_step instanceof EE_SPCO_Reg_Step) {
1691
-            $this->checkout->redirect = true;
1692
-            if (empty($this->checkout->redirect_url)) {
1693
-                $this->checkout->redirect_url = $this->checkout->next_step->reg_step_url();
1694
-            }
1695
-            $this->checkout->redirect_url = apply_filters(
1696
-                'FHEE__EED_Single_Page_Checkout___setup_redirect__checkout_redirect_url',
1697
-                $this->checkout->redirect_url,
1698
-                $this->checkout
1699
-            );
1700
-        }
1701
-    }
1702
-
1703
-
1704
-    /**
1705
-     *   handle ajax message responses and redirects
1706
-     *
1707
-     * @return void
1708
-     * @throws EE_Error
1709
-     * @throws ReflectionException
1710
-     */
1711
-    public function go_to_next_step()
1712
-    {
1713
-        if ($this->request->isAjax()) {
1714
-            // capture contents of output buffer we started earlier in the request, and insert into JSON response
1715
-            $this->checkout->json_response->set_unexpected_errors(ob_get_clean());
1716
-        }
1717
-        $this->unlock_transaction();
1718
-        // just return for these conditions
1719
-        if (
1720
-            $this->checkout->admin_request
1721
-            || $this->checkout->action === 'redirect_form'
1722
-            || $this->checkout->action === 'update_checkout'
1723
-        ) {
1724
-            return;
1725
-        }
1726
-        // AJAX response
1727
-        $this->_handle_json_response();
1728
-        // redirect to next step or the Thank You page
1729
-        $this->_handle_html_redirects();
1730
-        // hmmm... must be something wrong, so let's just display the form again !
1731
-        $this->_display_spco_reg_form();
1732
-    }
1733
-
1734
-
1735
-    /**
1736
-     *   _handle_json_response
1737
-     *
1738
-     * @return void
1739
-     * @throws EE_Error
1740
-     */
1741
-    protected function _handle_json_response()
1742
-    {
1743
-        // if this is an ajax request
1744
-        if ($this->request->isAjax()) {
1745
-            $this->checkout->json_response->set_registration_time_limit(
1746
-                $this->checkout->get_registration_time_limit()
1747
-            );
1748
-            $this->checkout->json_response->set_payment_amount($this->checkout->amount_owing);
1749
-            // just send the ajax (
1750
-            $json_response = apply_filters(
1751
-                'FHEE__EE_Single_Page_Checkout__JSON_response',
1752
-                $this->checkout->json_response
1753
-            );
1754
-            echo ($json_response);
1755
-            exit();
1756
-        }
1757
-    }
1758
-
1759
-
1760
-    /**
1761
-     *   _handle_redirects
1762
-     *
1763
-     * @return void
1764
-     */
1765
-    protected function _handle_html_redirects()
1766
-    {
1767
-        // going somewhere ?
1768
-        if ($this->checkout->redirect && ! empty($this->checkout->redirect_url)) {
1769
-            // store notices in a transient
1770
-            EE_Error::get_notices(false, true);
1771
-            wp_safe_redirect($this->checkout->redirect_url);
1772
-            exit();
1773
-        }
1774
-    }
1775
-
1776
-
1777
-    /**
1778
-     *   set_checkout_anchor
1779
-     *
1780
-     * @return void
1781
-     */
1782
-    public function set_checkout_anchor()
1783
-    {
1784
-        echo '<a id="checkout" style="float: left; margin-left: -999em;"></a>';
1785
-    }
1786
-
1787
-    /**
1788
-     *    getRegistrationExpirationNotice
1789
-     *
1790
-     * @since     4.9.59.p
1791
-     * @return    string
1792
-     */
1793
-    public static function getRegistrationExpirationNotice()
1794
-    {
1795
-        return sprintf(
1796
-            esc_html__(
1797
-                '%1$sWe\'re sorry, but your registration time has expired.%2$s%3$s%4$sIf you still wish to complete your registration, please return to the %5$sEvent List%6$sEvent List%7$s and reselect your tickets if available. Please accept our apologies for any inconvenience this may have caused.%8$s',
1798
-                'event_espresso'
1799
-            ),
1800
-            '<h4 class="important-notice">',
1801
-            '</h4>',
1802
-            '<br />',
1803
-            '<p>',
1804
-            '<a href="' . get_post_type_archive_link('espresso_events') . '" title="',
1805
-            '">',
1806
-            '</a>',
1807
-            '</p>'
1808
-        );
1809
-    }
22
+	/**
23
+	 * $_initialized - has the SPCO controller already been initialized ?
24
+	 *
25
+	 * @var bool $_initialized
26
+	 */
27
+	private static $_initialized = false;
28
+
29
+
30
+	/**
31
+	 * $_checkout_verified - is the EE_Checkout verified as correct for this request ?
32
+	 *
33
+	 * @var bool $_valid_checkout
34
+	 */
35
+	private static $_checkout_verified = true;
36
+
37
+	/**
38
+	 *    $_reg_steps_array - holds initial array of reg steps
39
+	 *
40
+	 * @var array $_reg_steps_array
41
+	 */
42
+	private static $_reg_steps_array = array();
43
+
44
+	/**
45
+	 *    $checkout - EE_Checkout object for handling the properties of the current checkout process
46
+	 *
47
+	 * @var EE_Checkout $checkout
48
+	 */
49
+	public $checkout;
50
+
51
+	/**
52
+	 * @var RequestInterface $request
53
+	 */
54
+	protected $request;
55
+
56
+
57
+	/**
58
+	 * @return EED_Single_Page_Checkout|EED_Module
59
+	 * @throws EE_Error
60
+	 * @throws ReflectionException
61
+	 */
62
+	public static function instance()
63
+	{
64
+		add_filter('EED_Single_Page_Checkout__SPCO_active', '__return_true');
65
+		return parent::get_instance(__CLASS__);
66
+	}
67
+
68
+
69
+	/**
70
+	 * @return EE_CART
71
+	 */
72
+	public function cart()
73
+	{
74
+		return $this->checkout->cart;
75
+	}
76
+
77
+
78
+	/**
79
+	 * @return RequestInterface
80
+	 * @since   4.10.14.p
81
+	 */
82
+	public static function getRequest()
83
+	{
84
+		return LoaderFactory::getLoader()->getShared(RequestInterface::class);
85
+	}
86
+
87
+
88
+	/**
89
+	 * @return EE_Transaction
90
+	 */
91
+	public function transaction()
92
+	{
93
+		return $this->checkout->transaction;
94
+	}
95
+
96
+
97
+	/**
98
+	 *    set_hooks - for hooking into EE Core, other modules, etc
99
+	 *
100
+	 * @return    void
101
+	 * @throws EE_Error
102
+	 */
103
+	public static function set_hooks()
104
+	{
105
+		EED_Single_Page_Checkout::set_definitions();
106
+	}
107
+
108
+
109
+	/**
110
+	 *    set_hooks_admin - for hooking into EE Admin Core, other modules, etc
111
+	 *
112
+	 * @return    void
113
+	 * @throws EE_Error
114
+	 */
115
+	public static function set_hooks_admin()
116
+	{
117
+		EED_Single_Page_Checkout::set_definitions();
118
+		if (! (defined('DOING_AJAX') && DOING_AJAX)) {
119
+			return;
120
+		}
121
+		// going to start an output buffer in case anything gets accidentally output
122
+		// that might disrupt our JSON response
123
+		ob_start();
124
+		EED_Single_Page_Checkout::load_reg_steps();
125
+		// set ajax hooks
126
+		add_action('wp_ajax_process_reg_step', array('EED_Single_Page_Checkout', 'process_reg_step'));
127
+		add_action('wp_ajax_nopriv_process_reg_step', array('EED_Single_Page_Checkout', 'process_reg_step'));
128
+		add_action('wp_ajax_display_spco_reg_step', array('EED_Single_Page_Checkout', 'display_reg_step'));
129
+		add_action('wp_ajax_nopriv_display_spco_reg_step', array('EED_Single_Page_Checkout', 'display_reg_step'));
130
+		add_action('wp_ajax_update_reg_step', array('EED_Single_Page_Checkout', 'update_reg_step'));
131
+		add_action('wp_ajax_nopriv_update_reg_step', array('EED_Single_Page_Checkout', 'update_reg_step'));
132
+	}
133
+
134
+
135
+	/**
136
+	 *    process ajax request
137
+	 *
138
+	 * @param string $ajax_action
139
+	 * @throws EE_Error
140
+	 * @throws ReflectionException
141
+	 */
142
+	public static function process_ajax_request($ajax_action)
143
+	{
144
+		$request = EED_Single_Page_Checkout::getRequest();
145
+		$request->setRequestParam('action', $ajax_action);
146
+		EED_Single_Page_Checkout::instance()->_initialize();
147
+	}
148
+
149
+
150
+	/**
151
+	 * ajax display registration step
152
+	 *
153
+	 * @throws EE_Error
154
+	 * @throws ReflectionException
155
+	 */
156
+	public static function display_reg_step()
157
+	{
158
+		EED_Single_Page_Checkout::process_ajax_request('display_spco_reg_step');
159
+	}
160
+
161
+
162
+	/**
163
+	 * ajax process registration step
164
+	 *
165
+	 * @throws EE_Error
166
+	 * @throws ReflectionException
167
+	 */
168
+	public static function process_reg_step()
169
+	{
170
+		EED_Single_Page_Checkout::process_ajax_request('process_reg_step');
171
+	}
172
+
173
+
174
+	/**
175
+	 * ajax process registration step
176
+	 *
177
+	 * @throws EE_Error
178
+	 * @throws ReflectionException
179
+	 */
180
+	public static function update_reg_step()
181
+	{
182
+		EED_Single_Page_Checkout::process_ajax_request('update_reg_step');
183
+	}
184
+
185
+
186
+	/**
187
+	 * update_checkout
188
+	 *
189
+	 * @return void
190
+	 * @throws ReflectionException
191
+	 * @throws EE_Error
192
+	 */
193
+	public static function update_checkout()
194
+	{
195
+		EED_Single_Page_Checkout::process_ajax_request('update_checkout');
196
+	}
197
+
198
+
199
+	/**
200
+	 * @return void
201
+	 * @deprecated 4.10.14.p
202
+	 */
203
+	public static function load_request_handler()
204
+	{
205
+	}
206
+
207
+
208
+	/**
209
+	 *    set_definitions
210
+	 *
211
+	 * @return    void
212
+	 * @throws EE_Error
213
+	 */
214
+	public static function set_definitions()
215
+	{
216
+		if (defined('SPCO_BASE_PATH')) {
217
+			return;
218
+		}
219
+		define(
220
+			'SPCO_BASE_PATH',
221
+			rtrim(str_replace(array('\\', '/'), '/', plugin_dir_path(__FILE__)), '/') . '/'
222
+		);
223
+		define('SPCO_CSS_URL', plugin_dir_url(__FILE__) . 'css/');
224
+		define('SPCO_IMG_URL', plugin_dir_url(__FILE__) . 'img/');
225
+		define('SPCO_JS_URL', plugin_dir_url(__FILE__) . 'js/');
226
+		define('SPCO_INC_PATH', SPCO_BASE_PATH . 'inc/');
227
+		define('SPCO_REG_STEPS_PATH', SPCO_BASE_PATH . 'reg_steps/');
228
+		define('SPCO_TEMPLATES_PATH', SPCO_BASE_PATH . 'templates/');
229
+		EEH_Autoloader::register_autoloaders_for_each_file_in_folder(SPCO_BASE_PATH, true);
230
+		EE_Registry::$i18n_js_strings['registration_expiration_notice'] = EED_Single_Page_Checkout::getRegistrationExpirationNotice(
231
+		);
232
+	}
233
+
234
+
235
+	/**
236
+	 * load_reg_steps
237
+	 * loads and instantiates each reg step based on the EE_Registry::instance()->CFG->registration->reg_steps array
238
+	 *
239
+	 * @throws EE_Error
240
+	 */
241
+	public static function load_reg_steps()
242
+	{
243
+		static $reg_steps_loaded = false;
244
+		if ($reg_steps_loaded) {
245
+			return;
246
+		}
247
+		// filter list of reg_steps
248
+		$reg_steps_to_load = (array) apply_filters(
249
+			'AHEE__SPCO__load_reg_steps__reg_steps_to_load',
250
+			EED_Single_Page_Checkout::get_reg_steps()
251
+		);
252
+		// sort by key (order)
253
+		ksort($reg_steps_to_load);
254
+		// loop through folders
255
+		foreach ($reg_steps_to_load as $order => $reg_step) {
256
+			// we need a
257
+			if (isset($reg_step['file_path'], $reg_step['class_name'], $reg_step['slug'])) {
258
+				// copy over to the reg_steps_array
259
+				EED_Single_Page_Checkout::$_reg_steps_array[ $order ] = $reg_step;
260
+				// register custom key route for each reg step
261
+				// ie: step=>"slug" - this is the entire reason we load the reg steps array now
262
+				EE_Config::register_route(
263
+					$reg_step['slug'],
264
+					'EED_Single_Page_Checkout',
265
+					'run',
266
+					'step'
267
+				);
268
+				// add AJAX or other hooks
269
+				if (isset($reg_step['has_hooks']) && $reg_step['has_hooks']) {
270
+					// setup autoloaders if necessary
271
+					if (! class_exists($reg_step['class_name'])) {
272
+						EEH_Autoloader::register_autoloaders_for_each_file_in_folder(
273
+							$reg_step['file_path'],
274
+							true
275
+						);
276
+					}
277
+					if (is_callable($reg_step['class_name'], 'set_hooks')) {
278
+						call_user_func(array($reg_step['class_name'], 'set_hooks'));
279
+					}
280
+				}
281
+			}
282
+		}
283
+		$reg_steps_loaded = true;
284
+	}
285
+
286
+
287
+	/**
288
+	 *    get_reg_steps
289
+	 *
290
+	 * @return    array
291
+	 */
292
+	public static function get_reg_steps()
293
+	{
294
+		$reg_steps = EE_Registry::instance()->CFG->registration->reg_steps;
295
+		if (empty($reg_steps)) {
296
+			$reg_steps = array(
297
+				10  => array(
298
+					'file_path'  => SPCO_REG_STEPS_PATH . 'attendee_information',
299
+					'class_name' => 'EE_SPCO_Reg_Step_Attendee_Information',
300
+					'slug'       => 'attendee_information',
301
+					'has_hooks'  => false,
302
+				),
303
+				30  => array(
304
+					'file_path'  => SPCO_REG_STEPS_PATH . 'payment_options',
305
+					'class_name' => 'EE_SPCO_Reg_Step_Payment_Options',
306
+					'slug'       => 'payment_options',
307
+					'has_hooks'  => true,
308
+				),
309
+				999 => array(
310
+					'file_path'  => SPCO_REG_STEPS_PATH . 'finalize_registration',
311
+					'class_name' => 'EE_SPCO_Reg_Step_Finalize_Registration',
312
+					'slug'       => 'finalize_registration',
313
+					'has_hooks'  => false,
314
+				),
315
+			);
316
+		}
317
+		return $reg_steps;
318
+	}
319
+
320
+
321
+	/**
322
+	 *    registration_checkout_for_admin
323
+	 *
324
+	 * @return    string
325
+	 * @throws EE_Error
326
+	 * @throws ReflectionException
327
+	 */
328
+	public static function registration_checkout_for_admin()
329
+	{
330
+		$request = EED_Single_Page_Checkout::getRequest();
331
+		$request->setRequestParam('step', 'attendee_information');
332
+		$request->setRequestParam('action', 'display_spco_reg_step');
333
+		$request->setRequestParam('process_form_submission', false);
334
+		EED_Single_Page_Checkout::instance()->_initialize();
335
+		EED_Single_Page_Checkout::instance()->_display_spco_reg_form();
336
+		return EED_Single_Page_Checkout::getResponse()->getOutput();
337
+	}
338
+
339
+
340
+	/**
341
+	 * process_registration_from_admin
342
+	 *
343
+	 * @return EE_Transaction
344
+	 * @throws EE_Error
345
+	 * @throws ReflectionException
346
+	 */
347
+	public static function process_registration_from_admin()
348
+	{
349
+		$request = EED_Single_Page_Checkout::getRequest();
350
+		$request->setRequestParam('step', 'attendee_information');
351
+		$request->setRequestParam('action', 'process_reg_step');
352
+		$request->setRequestParam('process_form_submission', true);
353
+		EED_Single_Page_Checkout::instance()->_initialize();
354
+		if (EED_Single_Page_Checkout::instance()->checkout->current_step->completed()) {
355
+			$final_reg_step = end(EED_Single_Page_Checkout::instance()->checkout->reg_steps);
356
+			if ($final_reg_step instanceof EE_SPCO_Reg_Step_Finalize_Registration) {
357
+				EED_Single_Page_Checkout::instance()->checkout->set_reg_step_initiated($final_reg_step);
358
+				if ($final_reg_step->process_reg_step()) {
359
+					$final_reg_step->set_completed();
360
+					EED_Single_Page_Checkout::instance()->checkout->update_txn_reg_steps_array();
361
+					return EED_Single_Page_Checkout::instance()->checkout->transaction;
362
+				}
363
+			}
364
+		}
365
+		return null;
366
+	}
367
+
368
+
369
+	/**
370
+	 *    run
371
+	 *
372
+	 * @param WP_Query $WP
373
+	 * @return    void
374
+	 */
375
+	public function run($WP)
376
+	{
377
+		if (
378
+			$WP instanceof WP_Query
379
+			&& $WP->is_main_query()
380
+			&& apply_filters('FHEE__EED_Single_Page_Checkout__run', true)
381
+			&& $this->_is_reg_checkout()
382
+		) {
383
+			$this->_initialize();
384
+		}
385
+	}
386
+
387
+
388
+	/**
389
+	 * determines whether current url matches reg page url
390
+	 *
391
+	 * @return bool
392
+	 */
393
+	protected function _is_reg_checkout()
394
+	{
395
+		// get current permalink for reg page without any extra query args
396
+		$reg_page_url = get_permalink(EE_Config::instance()->core->reg_page_id);
397
+		// get request URI for current request, but without the scheme or host
398
+		$current_request_uri = EEH_URL::filter_input_server_url();
399
+		$current_request_uri = html_entity_decode($current_request_uri);
400
+		// get array of query args from the current request URI
401
+		$query_args = EEH_URL::get_query_string($current_request_uri);
402
+		// grab page id if it is set
403
+		$page_id = isset($query_args['page_id']) ? absint($query_args['page_id']) : 0;
404
+		// and remove the page id from the query args (we will re-add it later)
405
+		unset($query_args['page_id']);
406
+		// now strip all query args from current request URI
407
+		$current_request_uri = remove_query_arg(array_keys($query_args), $current_request_uri);
408
+		// and re-add the page id if it was set
409
+		if ($page_id) {
410
+			$current_request_uri = add_query_arg('page_id', $page_id, $current_request_uri);
411
+		}
412
+		// remove slashes and ?
413
+		$current_request_uri = trim($current_request_uri, '?/');
414
+		// is current request URI part of the known full reg page URL ?
415
+		return ! empty($current_request_uri) && strpos($reg_page_url, $current_request_uri) !== false;
416
+	}
417
+
418
+
419
+	/**
420
+	 * @param WP_Query $wp_query
421
+	 * @return    void
422
+	 * @throws EE_Error
423
+	 * @throws ReflectionException
424
+	 */
425
+	public static function init($wp_query)
426
+	{
427
+		EED_Single_Page_Checkout::instance()->run($wp_query);
428
+	}
429
+
430
+
431
+	/**
432
+	 *    _initialize - initial module setup
433
+	 *
434
+	 * @return    void
435
+	 */
436
+	private function _initialize()
437
+	{
438
+		// ensure SPCO doesn't run twice
439
+		if (EED_Single_Page_Checkout::$_initialized) {
440
+			return;
441
+		}
442
+		try {
443
+			$this->request = EED_Single_Page_Checkout::getRequest();
444
+			EED_Single_Page_Checkout::load_reg_steps();
445
+			$this->_verify_session();
446
+			// setup the EE_Checkout object
447
+			$this->checkout = $this->_initialize_checkout();
448
+			// filter checkout
449
+			$this->checkout = apply_filters('FHEE__EED_Single_Page_Checkout___initialize__checkout', $this->checkout);
450
+			// get the $_GET
451
+			$this->_get_request_vars();
452
+			if ($this->_block_bots()) {
453
+				return;
454
+			}
455
+			// filter continue_reg
456
+			$this->checkout->continue_reg = apply_filters(
457
+				'FHEE__EED_Single_Page_Checkout__init___continue_reg',
458
+				true,
459
+				$this->checkout
460
+			);
461
+			// load the reg steps array
462
+			if (! $this->_load_and_instantiate_reg_steps()) {
463
+				EED_Single_Page_Checkout::$_initialized = true;
464
+				return;
465
+			}
466
+			// set the current step
467
+			$this->checkout->set_current_step($this->checkout->step);
468
+			// and the next step
469
+			$this->checkout->set_next_step();
470
+			// verify that everything has been setup correctly
471
+			if (! ($this->_verify_transaction_and_get_registrations() && $this->_final_verifications())) {
472
+				EED_Single_Page_Checkout::$_initialized = true;
473
+				return;
474
+			}
475
+			// lock the transaction
476
+			$this->checkout->transaction->lock();
477
+			// make sure all of our cached objects are added to their respective model entity mappers
478
+			$this->checkout->refresh_all_entities();
479
+			// set amount owing
480
+			$this->checkout->amount_owing = $this->checkout->transaction->remaining();
481
+			// initialize each reg step, which gives them the chance to potentially alter the process
482
+			$this->_initialize_reg_steps();
483
+			// DEBUG LOG
484
+			// $this->checkout->log( __CLASS__, __FUNCTION__, __LINE__ );
485
+			// get reg form
486
+			if (! $this->_check_form_submission()) {
487
+				EED_Single_Page_Checkout::$_initialized = true;
488
+				return;
489
+			}
490
+			// checkout the action!!!
491
+			$this->_process_form_action();
492
+			// add some style and make it dance
493
+			$this->add_styles_and_scripts($this);
494
+			// kk... SPCO has successfully run
495
+			EED_Single_Page_Checkout::$_initialized = true;
496
+			// set no cache headers and constants
497
+			EE_System::do_not_cache();
498
+			// add anchor
499
+			add_action('loop_start', array($this, 'set_checkout_anchor'), 1);
500
+			// remove transaction lock
501
+			add_action('shutdown', array($this, 'unlock_transaction'), 1);
502
+		} catch (Exception $e) {
503
+			EE_Error::add_error($e->getMessage(), __FILE__, __FUNCTION__, __LINE__);
504
+		}
505
+	}
506
+
507
+
508
+	/**
509
+	 *    _verify_session
510
+	 * checks that the session is valid and not expired
511
+	 *
512
+	 * @throws EE_Error
513
+	 * @throws ReflectionException
514
+	 */
515
+	private function _verify_session()
516
+	{
517
+		if (! EE_Registry::instance()->SSN instanceof EE_Session) {
518
+			throw new EE_Error(esc_html__('The EE_Session class could not be loaded.', 'event_espresso'));
519
+		}
520
+		$clear_session_requested = $this->request->getRequestParam('clear_session', false, 'bool');
521
+		// is session still valid ?
522
+		if (
523
+			$clear_session_requested
524
+			|| (
525
+				EE_Registry::instance()->SSN->expired()
526
+				&& $this->request->getRequestParam('e_reg_url_link') === ''
527
+			)
528
+		) {
529
+			$this->checkout = new EE_Checkout();
530
+			EE_Registry::instance()->SSN->clear_session(__CLASS__, __FUNCTION__);
531
+			// EE_Registry::instance()->SSN->reset_cart();
532
+			// EE_Registry::instance()->SSN->reset_checkout();
533
+			// EE_Registry::instance()->SSN->reset_transaction();
534
+			if (! $clear_session_requested) {
535
+				EE_Error::add_attention(
536
+					EE_Registry::$i18n_js_strings['registration_expiration_notice'],
537
+					__FILE__,
538
+					__FUNCTION__,
539
+					__LINE__
540
+				);
541
+			}
542
+			// EE_Registry::instance()->SSN->reset_expired();
543
+		}
544
+	}
545
+
546
+
547
+	/**
548
+	 *    _initialize_checkout
549
+	 * loads and instantiates EE_Checkout
550
+	 *
551
+	 * @return EE_Checkout
552
+	 * @throws EE_Error
553
+	 * @throws ReflectionException
554
+	 */
555
+	private function _initialize_checkout()
556
+	{
557
+		// look in session for existing checkout
558
+		/** @type EE_Checkout $checkout */
559
+		$checkout = EE_Registry::instance()->SSN->checkout();
560
+		// verify
561
+		if (! $checkout instanceof EE_Checkout) {
562
+			// instantiate EE_Checkout object for handling the properties of the current checkout process
563
+			$checkout = EE_Registry::instance()->load_file(
564
+				SPCO_INC_PATH,
565
+				'EE_Checkout',
566
+				'class',
567
+				array(),
568
+				false
569
+			);
570
+		} else {
571
+			if ($checkout->current_step->is_final_step() && $checkout->exit_spco() === true) {
572
+				$this->unlock_transaction();
573
+				wp_safe_redirect($checkout->redirect_url);
574
+				exit();
575
+			}
576
+		}
577
+		$checkout = apply_filters('FHEE__EED_Single_Page_Checkout___initialize_checkout__checkout', $checkout);
578
+		// verify again
579
+		if (! $checkout instanceof EE_Checkout) {
580
+			throw new EE_Error(esc_html__('The EE_Checkout class could not be loaded.', 'event_espresso'));
581
+		}
582
+		// reset anything that needs a clean slate for each request
583
+		$checkout->reset_for_current_request();
584
+		return $checkout;
585
+	}
586
+
587
+
588
+	/**
589
+	 *    _get_request_vars
590
+	 *
591
+	 * @return    void
592
+	 */
593
+	private function _get_request_vars()
594
+	{
595
+		// make sure this request is marked as belonging to EE
596
+		/** @var CurrentPage $current_page */
597
+		$current_page = LoaderFactory::getLoader()->getShared(CurrentPage::class);
598
+		$current_page->setEspressoPage(true);
599
+		// which step is being requested ?
600
+		$this->checkout->step = $this->request->getRequestParam('step', $this->_get_first_step());
601
+		// which step is being edited ?
602
+		$this->checkout->edit_step = $this->request->getRequestParam('edit_step');
603
+		// and what we're doing on the current step
604
+		$this->checkout->action = $this->request->getRequestParam('action', 'display_spco_reg_step');
605
+		// timestamp
606
+		$this->checkout->uts = $this->request->getRequestParam('uts', 0, 'int');
607
+		// returning to edit ?
608
+		$this->checkout->reg_url_link = $this->request->getRequestParam('e_reg_url_link');
609
+		// add reg url link to registration query params
610
+		if ($this->checkout->reg_url_link && strpos($this->checkout->reg_url_link, '1-') !== 0) {
611
+			$this->checkout->reg_cache_where_params[0]['REG_url_link'] = $this->checkout->reg_url_link;
612
+		}
613
+		// or some other kind of revisit ?
614
+		$this->checkout->revisit = $this->request->getRequestParam('revisit', false, 'bool');
615
+		// and whether or not to generate a reg form for this request
616
+		$this->checkout->generate_reg_form = $this->request->getRequestParam('generate_reg_form', true, 'bool');
617
+		// and whether or not to process a reg form submission for this request
618
+		$this->checkout->process_form_submission = $this->request->getRequestParam(
619
+			'process_form_submission',
620
+			$this->checkout->action === 'process_reg_step',
621
+			'bool'
622
+		);
623
+		$this->checkout->process_form_submission = filter_var(
624
+			$this->checkout->action !== 'display_spco_reg_step'
625
+				? $this->checkout->process_form_submission
626
+				: false,
627
+			FILTER_VALIDATE_BOOLEAN
628
+		);
629
+		// $this->_display_request_vars();
630
+	}
631
+
632
+
633
+	/**
634
+	 *  _display_request_vars
635
+	 *
636
+	 * @return    void
637
+	 */
638
+	protected function _display_request_vars()
639
+	{
640
+		if (! WP_DEBUG) {
641
+			return;
642
+		}
643
+		EEH_Debug_Tools::printr($this->request->requestParams(), 'requestParams', __FILE__, __LINE__);
644
+		EEH_Debug_Tools::printr($this->checkout->step, '$this->checkout->step', __FILE__, __LINE__);
645
+		EEH_Debug_Tools::printr($this->checkout->edit_step, '$this->checkout->edit_step', __FILE__, __LINE__);
646
+		EEH_Debug_Tools::printr($this->checkout->action, '$this->checkout->action', __FILE__, __LINE__);
647
+		EEH_Debug_Tools::printr($this->checkout->reg_url_link, '$this->checkout->reg_url_link', __FILE__, __LINE__);
648
+		EEH_Debug_Tools::printr($this->checkout->revisit, '$this->checkout->revisit', __FILE__, __LINE__);
649
+		EEH_Debug_Tools::printr(
650
+			$this->checkout->generate_reg_form,
651
+			'$this->checkout->generate_reg_form',
652
+			__FILE__,
653
+			__LINE__
654
+		);
655
+		EEH_Debug_Tools::printr(
656
+			$this->checkout->process_form_submission,
657
+			'$this->checkout->process_form_submission',
658
+			__FILE__,
659
+			__LINE__
660
+		);
661
+	}
662
+
663
+
664
+	/**
665
+	 * _block_bots
666
+	 * checks that the incoming request has either of the following set:
667
+	 *  a uts (unix timestamp) which indicates that the request was redirected from the Ticket Selector
668
+	 *  a REG URL Link, which indicates that the request is a return visit to SPCO for a valid TXN
669
+	 * so if you're not coming from the Ticket Selector nor returning for a valid IP...
670
+	 * then where you coming from man?
671
+	 *
672
+	 * @return boolean
673
+	 */
674
+	private function _block_bots()
675
+	{
676
+		$invalid_checkout_access = EED_Invalid_Checkout_Access::getInvalidCheckoutAccess();
677
+		if ($invalid_checkout_access->checkoutAccessIsInvalid($this->checkout)) {
678
+			return true;
679
+		}
680
+		return false;
681
+	}
682
+
683
+
684
+	/**
685
+	 *    _get_first_step
686
+	 *  gets slug for first step in $_reg_steps_array
687
+	 *
688
+	 * @return    string
689
+	 */
690
+	private function _get_first_step()
691
+	{
692
+		$first_step = reset(EED_Single_Page_Checkout::$_reg_steps_array);
693
+		return isset($first_step['slug']) ? $first_step['slug'] : 'attendee_information';
694
+	}
695
+
696
+
697
+	/**
698
+	 * instantiates each reg step based on the loaded reg_steps array
699
+	 *
700
+	 * @return    bool
701
+	 * @throws EE_Error
702
+	 * @throws InvalidArgumentException
703
+	 * @throws InvalidDataTypeException
704
+	 * @throws InvalidInterfaceException
705
+	 * @throws ReflectionException
706
+	 */
707
+	private function _load_and_instantiate_reg_steps()
708
+	{
709
+		do_action('AHEE__Single_Page_Checkout___load_and_instantiate_reg_steps__start', $this->checkout);
710
+		// have reg_steps already been instantiated ?
711
+		if (
712
+			empty($this->checkout->reg_steps)
713
+			|| apply_filters('FHEE__Single_Page_Checkout__load_reg_steps__reload_reg_steps', false, $this->checkout)
714
+		) {
715
+			// if not, then loop through raw reg steps array
716
+			foreach (EED_Single_Page_Checkout::$_reg_steps_array as $order => $reg_step) {
717
+				if (! $this->_load_and_instantiate_reg_step($reg_step, $order)) {
718
+					return false;
719
+				}
720
+			}
721
+			if (isset($this->checkout->reg_steps['registration_confirmation'])) {
722
+				// skip the registration_confirmation page ?
723
+				if (EE_Registry::instance()->CFG->registration->skip_reg_confirmation) {
724
+					// just remove it from the reg steps array
725
+					$this->checkout->remove_reg_step('registration_confirmation', false);
726
+				} elseif (
727
+					EE_Registry::instance()->CFG->registration->reg_confirmation_last
728
+				) {
729
+					// set the order to something big like 100
730
+					$this->checkout->set_reg_step_order('registration_confirmation');
731
+				}
732
+			}
733
+			// filter the array for good luck
734
+			$this->checkout->reg_steps = apply_filters(
735
+				'FHEE__Single_Page_Checkout__load_reg_steps__reg_steps',
736
+				$this->checkout->reg_steps
737
+			);
738
+			// finally re-sort based on the reg step class order properties
739
+			$this->checkout->sort_reg_steps();
740
+		} else {
741
+			foreach ($this->checkout->reg_steps as $reg_step) {
742
+				// set all current step stati to FALSE
743
+				$reg_step->set_is_current_step(false);
744
+			}
745
+		}
746
+		if (empty($this->checkout->reg_steps)) {
747
+			EE_Error::add_error(
748
+				esc_html__('No Reg Steps were loaded..', 'event_espresso'),
749
+				__FILE__,
750
+				__FUNCTION__,
751
+				__LINE__
752
+			);
753
+			return false;
754
+		}
755
+		// make reg step details available to JS
756
+		$this->checkout->set_reg_step_JSON_info();
757
+		return true;
758
+	}
759
+
760
+
761
+	/**
762
+	 *     _load_and_instantiate_reg_step
763
+	 *
764
+	 * @param array $reg_step
765
+	 * @param int   $order
766
+	 * @return bool
767
+	 * @throws EE_Error
768
+	 * @throws ReflectionException
769
+	 */
770
+	private function _load_and_instantiate_reg_step($reg_step = array(), $order = 0)
771
+	{
772
+		// we need a file_path, class_name, and slug to add a reg step
773
+		if (isset($reg_step['file_path'], $reg_step['class_name'], $reg_step['slug'])) {
774
+			// if editing a specific step, but this is NOT that step... (and it's not the 'finalize_registration' step)
775
+			if (
776
+				$this->checkout->reg_url_link
777
+				&& $this->checkout->step !== $reg_step['slug']
778
+				&& $reg_step['slug'] !== 'finalize_registration'
779
+				// normally at this point we would NOT load the reg step, but this filter can change that
780
+				&& apply_filters(
781
+					'FHEE__Single_Page_Checkout___load_and_instantiate_reg_step__bypass_reg_step',
782
+					true,
783
+					$reg_step,
784
+					$this->checkout
785
+				)
786
+			) {
787
+				return true;
788
+			}
789
+
790
+			// instantiate step class using file path and class name
791
+			$reg_step_obj = EE_Registry::instance()->load_file(
792
+				$reg_step['file_path'],
793
+				$reg_step['class_name'],
794
+				'class',
795
+				[$this->checkout],
796
+				false
797
+			);
798
+			// did we gets the goods ?
799
+			if ($reg_step_obj instanceof EE_SPCO_Reg_Step) {
800
+				// set reg step order based on config
801
+				$reg_step_obj->set_order($order);
802
+				// add instantiated reg step object to the master reg steps array
803
+				$this->checkout->add_reg_step($reg_step_obj);
804
+			} else {
805
+				EE_Error::add_error(
806
+					esc_html__('The current step could not be set.', 'event_espresso'),
807
+					__FILE__,
808
+					__FUNCTION__,
809
+					__LINE__
810
+				);
811
+				return false;
812
+			}
813
+		} else {
814
+			if (WP_DEBUG) {
815
+				EE_Error::add_error(
816
+					sprintf(
817
+						esc_html__(
818
+							'A registration step could not be loaded. One or more of the following data points is invalid:%4$s%5$sFile Path: %1$s%6$s%5$sClass Name: %2$s%6$s%5$sSlug: %3$s%6$s%7$s',
819
+							'event_espresso'
820
+						),
821
+						isset($reg_step['file_path']) ? $reg_step['file_path'] : '',
822
+						isset($reg_step['class_name']) ? $reg_step['class_name'] : '',
823
+						isset($reg_step['slug']) ? $reg_step['slug'] : '',
824
+						'<ul>',
825
+						'<li>',
826
+						'</li>',
827
+						'</ul>'
828
+					),
829
+					__FILE__,
830
+					__FUNCTION__,
831
+					__LINE__
832
+				);
833
+			}
834
+			return false;
835
+		}
836
+		return true;
837
+	}
838
+
839
+
840
+	/**
841
+	 * _verify_transaction_and_get_registrations
842
+	 *
843
+	 * @return bool
844
+	 * @throws EE_Error
845
+	 * @throws ReflectionException
846
+	 */
847
+	private function _verify_transaction_and_get_registrations()
848
+	{
849
+		// was there already a valid transaction in the checkout from the session ?
850
+		if (! $this->checkout->transaction instanceof EE_Transaction) {
851
+			// get transaction from db or session
852
+			$this->checkout->transaction = $this->checkout->reg_url_link && ! is_admin()
853
+				? $this->_get_transaction_and_cart_for_previous_visit()
854
+				: $this->_get_cart_for_current_session_and_setup_new_transaction();
855
+			if (! $this->checkout->transaction instanceof EE_Transaction) {
856
+				EE_Error::add_error(
857
+					esc_html__(
858
+						'Your Registration and Transaction information could not be retrieved from the db.',
859
+						'event_espresso'
860
+					),
861
+					__FILE__,
862
+					__FUNCTION__,
863
+					__LINE__
864
+				);
865
+				$this->checkout->transaction = EE_Transaction::new_instance();
866
+				// add some style and make it dance
867
+				$this->add_styles_and_scripts($this);
868
+				EED_Single_Page_Checkout::$_initialized = true;
869
+				return false;
870
+			}
871
+			// and the registrations for the transaction
872
+			$this->_get_registrations($this->checkout->transaction);
873
+		}
874
+		return true;
875
+	}
876
+
877
+
878
+	/**
879
+	 * _get_transaction_and_cart_for_previous_visit
880
+	 *
881
+	 * @return EE_Transaction|null
882
+	 * @throws EE_Error
883
+	 * @throws ReflectionException
884
+	 */
885
+	private function _get_transaction_and_cart_for_previous_visit()
886
+	{
887
+		/** @var $TXN_model EEM_Transaction */
888
+		$TXN_model = EE_Registry::instance()->load_model('Transaction');
889
+		// because the reg_url_link is present in the request,
890
+		// this is a return visit to SPCO, so we'll get the transaction data from the db
891
+		$transaction = $TXN_model->get_transaction_from_reg_url_link($this->checkout->reg_url_link);
892
+		// verify transaction
893
+		if ($transaction instanceof EE_Transaction) {
894
+			// and get the cart that was used for that transaction
895
+			$this->checkout->cart = $this->_get_cart_for_transaction($transaction);
896
+			return $transaction;
897
+		}
898
+		EE_Error::add_error(
899
+			esc_html__('Your Registration and Transaction information could not be retrieved from the db.', 'event_espresso'),
900
+			__FILE__,
901
+			__FUNCTION__,
902
+			__LINE__
903
+		);
904
+		return null;
905
+	}
906
+
907
+
908
+	/**
909
+	 * _get_cart_for_transaction
910
+	 *
911
+	 * @param EE_Transaction $transaction
912
+	 * @return EE_Cart
913
+	 */
914
+	private function _get_cart_for_transaction($transaction)
915
+	{
916
+		return $this->checkout->get_cart_for_transaction($transaction);
917
+	}
918
+
919
+
920
+	/**
921
+	 * get_cart_for_transaction
922
+	 *
923
+	 * @param EE_Transaction $transaction
924
+	 * @return EE_Cart
925
+	 */
926
+	public function get_cart_for_transaction(EE_Transaction $transaction)
927
+	{
928
+		return $this->checkout->get_cart_for_transaction($transaction);
929
+	}
930
+
931
+
932
+	/**
933
+	 * _get_transaction_and_cart_for_current_session
934
+	 *    generates a new EE_Transaction object and adds it to the $_transaction property.
935
+	 *
936
+	 * @return EE_Transaction
937
+	 * @throws EE_Error
938
+	 * @throws ReflectionException
939
+	 */
940
+	private function _get_cart_for_current_session_and_setup_new_transaction()
941
+	{
942
+		//  if there's no transaction, then this is the FIRST visit to SPCO
943
+		// so load up the cart ( passing nothing for the TXN because it doesn't exist yet )
944
+		$this->checkout->cart = $this->_get_cart_for_transaction(null);
945
+		// and then create a new transaction
946
+		$transaction = $this->_initialize_transaction();
947
+		// verify transaction
948
+		if ($transaction instanceof EE_Transaction) {
949
+			// save it so that we have an ID for other objects to use
950
+			$transaction->save();
951
+			// and save TXN data to the cart
952
+			$this->checkout->cart->get_grand_total()->save_this_and_descendants_to_txn($transaction->ID());
953
+		} else {
954
+			EE_Error::add_error(
955
+				esc_html__('A Valid Transaction could not be initialized.', 'event_espresso'),
956
+				__FILE__,
957
+				__FUNCTION__,
958
+				__LINE__
959
+			);
960
+		}
961
+		return $transaction;
962
+	}
963
+
964
+
965
+	/**
966
+	 *    generates a new EE_Transaction object and adds it to the $_transaction property.
967
+	 *
968
+	 * @return EE_Transaction|null
969
+	 */
970
+	private function _initialize_transaction()
971
+	{
972
+		try {
973
+			// ensure cart totals have been calculated
974
+			$this->checkout->cart->get_grand_total()->recalculate_total_including_taxes();
975
+			// grab the cart grand total
976
+			$cart_total = $this->checkout->cart->get_cart_grand_total();
977
+			// create new TXN
978
+			$transaction = EE_Transaction::new_instance(
979
+				array(
980
+					'TXN_reg_steps' => $this->checkout->initialize_txn_reg_steps_array(),
981
+					'TXN_total'     => $cart_total > 0 ? $cart_total : 0,
982
+					'TXN_paid'      => 0,
983
+					'STS_ID'        => EEM_Transaction::failed_status_code,
984
+				)
985
+			);
986
+			// save it so that we have an ID for other objects to use
987
+			$transaction->save();
988
+			// set cron job for following up on TXNs after their session has expired
989
+			EE_Cron_Tasks::schedule_expired_transaction_check(
990
+				EE_Registry::instance()->SSN->expiration() + 1,
991
+				$transaction->ID()
992
+			);
993
+			return $transaction;
994
+		} catch (Exception $e) {
995
+			EE_Error::add_error($e->getMessage(), __FILE__, __FUNCTION__, __LINE__);
996
+		}
997
+		return null;
998
+	}
999
+
1000
+
1001
+	/**
1002
+	 * _get_registrations
1003
+	 *
1004
+	 * @param EE_Transaction $transaction
1005
+	 * @return void
1006
+	 * @throws EE_Error
1007
+	 * @throws ReflectionException
1008
+	 */
1009
+	private function _get_registrations(EE_Transaction $transaction)
1010
+	{
1011
+		// first step: grab the registrants  { : o
1012
+		$registrations = $transaction->registrations($this->checkout->reg_cache_where_params);
1013
+		$this->checkout->total_ticket_count = count($registrations);
1014
+		// verify registrations have been set
1015
+		if (empty($registrations)) {
1016
+			// if no cached registrations, then check the db
1017
+			$registrations = $transaction->registrations($this->checkout->reg_cache_where_params);
1018
+			// still nothing ? well as long as this isn't a revisit
1019
+			if (empty($registrations) && ! $this->checkout->revisit) {
1020
+				// generate new registrations from scratch
1021
+				$registrations = $this->_initialize_registrations($transaction);
1022
+			}
1023
+		}
1024
+		// sort by their original registration order
1025
+		usort($registrations, array('EED_Single_Page_Checkout', 'sort_registrations_by_REG_count'));
1026
+		// then loop thru the array
1027
+		foreach ($registrations as $registration) {
1028
+			// verify each registration
1029
+			if ($registration instanceof EE_Registration) {
1030
+				// we display all attendee info for the primary registrant
1031
+				if (
1032
+					$this->checkout->reg_url_link === $registration->reg_url_link()
1033
+					&& $registration->is_primary_registrant()
1034
+				) {
1035
+					$this->checkout->primary_revisit = true;
1036
+					break;
1037
+				}
1038
+				if ($this->checkout->revisit && $this->checkout->reg_url_link !== $registration->reg_url_link()) {
1039
+					// but hide info if it doesn't belong to you
1040
+					$transaction->clear_cache('Registration', $registration->ID());
1041
+					$this->checkout->total_ticket_count--;
1042
+				}
1043
+				$this->checkout->set_reg_status_updated($registration->ID(), false);
1044
+			}
1045
+		}
1046
+	}
1047
+
1048
+
1049
+	/**
1050
+	 *    adds related EE_Registration objects for each ticket in the cart to the current EE_Transaction object
1051
+	 *
1052
+	 * @param EE_Transaction $transaction
1053
+	 * @return    array
1054
+	 * @throws EE_Error
1055
+	 * @throws ReflectionException
1056
+	 */
1057
+	private function _initialize_registrations(EE_Transaction $transaction)
1058
+	{
1059
+		$att_nmbr = 0;
1060
+		$registrations = array();
1061
+		if ($transaction instanceof EE_Transaction) {
1062
+			/** @type EE_Registration_Processor $registration_processor */
1063
+			$registration_processor = EE_Registry::instance()->load_class('Registration_Processor');
1064
+			$this->checkout->total_ticket_count = $this->checkout->cart->all_ticket_quantity_count();
1065
+			// now let's add the cart items to the $transaction
1066
+			foreach ($this->checkout->cart->get_tickets() as $line_item) {
1067
+				// do the following for each ticket of this type they selected
1068
+				for ($x = 1; $x <= $line_item->quantity(); $x++) {
1069
+					$att_nmbr++;
1070
+					/** @var CreateRegistrationCommand $CreateRegistrationCommand */
1071
+					$CreateRegistrationCommand = EE_Registry::instance()->create(
1072
+						CreateRegistrationCommand::class,
1073
+						[
1074
+							$transaction,
1075
+							$line_item,
1076
+							$att_nmbr,
1077
+							$this->checkout->total_ticket_count,
1078
+						]
1079
+					);
1080
+					// override capabilities for frontend registrations
1081
+					if ($this->request->isFrontend()) {
1082
+						$CreateRegistrationCommand->setCapCheck(
1083
+							new PublicCapabilities('', 'create_new_registration')
1084
+						);
1085
+					}
1086
+					$registration = EE_Registry::instance()->BUS->execute($CreateRegistrationCommand);
1087
+					if (! $registration instanceof EE_Registration) {
1088
+						throw new InvalidEntityException($registration, 'EE_Registration');
1089
+					}
1090
+					$registrations[ $registration->ID() ] = $registration;
1091
+				}
1092
+			}
1093
+			$registration_processor->fix_reg_final_price_rounding_issue($transaction);
1094
+		}
1095
+		return $registrations;
1096
+	}
1097
+
1098
+
1099
+	/**
1100
+	 * sorts registrations by REG_count
1101
+	 *
1102
+	 * @param EE_Registration $reg_A
1103
+	 * @param EE_Registration $reg_B
1104
+	 * @return int
1105
+	 */
1106
+	public static function sort_registrations_by_REG_count(EE_Registration $reg_A, EE_Registration $reg_B)
1107
+	{
1108
+		// this shouldn't ever happen within the same TXN, but oh well
1109
+		if ($reg_A->count() === $reg_B->count()) {
1110
+			return 0;
1111
+		}
1112
+		return ($reg_A->count() > $reg_B->count()) ? 1 : -1;
1113
+	}
1114
+
1115
+
1116
+	/**
1117
+	 *    _final_verifications
1118
+	 * just makes sure that everything is set up correctly before proceeding
1119
+	 *
1120
+	 * @return    bool
1121
+	 * @throws EE_Error
1122
+	 * @throws ReflectionException
1123
+	 */
1124
+	private function _final_verifications()
1125
+	{
1126
+		// filter checkout
1127
+		$this->checkout = apply_filters(
1128
+			'FHEE__EED_Single_Page_Checkout___final_verifications__checkout',
1129
+			$this->checkout
1130
+		);
1131
+		// verify that current step is still set correctly
1132
+		if (! $this->checkout->current_step instanceof EE_SPCO_Reg_Step) {
1133
+			EE_Error::add_error(
1134
+				esc_html__(
1135
+					'We\'re sorry but the registration process can not proceed because one or more registration steps were not setup correctly. Please refresh the page and try again or contact support.',
1136
+					'event_espresso'
1137
+				),
1138
+				__FILE__,
1139
+				__FUNCTION__,
1140
+				__LINE__
1141
+			);
1142
+			return false;
1143
+		}
1144
+		// if returning to SPCO, then verify that primary registrant is set
1145
+		if (! empty($this->checkout->reg_url_link)) {
1146
+			$valid_registrant = $this->checkout->transaction->primary_registration();
1147
+			if (! $valid_registrant instanceof EE_Registration) {
1148
+				EE_Error::add_error(
1149
+					esc_html__(
1150
+						'We\'re sorry but there appears to be an error with the "reg_url_link" or the primary registrant for this transaction. Please refresh the page and try again or contact support.',
1151
+						'event_espresso'
1152
+					),
1153
+					__FILE__,
1154
+					__FUNCTION__,
1155
+					__LINE__
1156
+				);
1157
+				return false;
1158
+			}
1159
+			$valid_registrant = null;
1160
+			foreach ($this->checkout->transaction->registrations($this->checkout->reg_cache_where_params) as $registration) {
1161
+				if (
1162
+					$registration instanceof EE_Registration
1163
+					&& $registration->reg_url_link() === $this->checkout->reg_url_link
1164
+				) {
1165
+					$valid_registrant = $registration;
1166
+				}
1167
+			}
1168
+			if (! $valid_registrant instanceof EE_Registration) {
1169
+				// hmmm... maybe we have the wrong session because the user is opening multiple tabs ?
1170
+				if (EED_Single_Page_Checkout::$_checkout_verified) {
1171
+					// clear the session, mark the checkout as unverified, and try again
1172
+					EE_Registry::instance()->SSN->clear_session(__CLASS__, __FUNCTION__);
1173
+					EED_Single_Page_Checkout::$_initialized = false;
1174
+					EED_Single_Page_Checkout::$_checkout_verified = false;
1175
+					$this->_initialize();
1176
+					EE_Error::reset_notices();
1177
+					return false;
1178
+				}
1179
+				EE_Error::add_error(
1180
+					esc_html__(
1181
+						'We\'re sorry but there appears to be an error with the "reg_url_link" or the transaction itself. Please refresh the page and try again or contact support.',
1182
+						'event_espresso'
1183
+					),
1184
+					__FILE__,
1185
+					__FUNCTION__,
1186
+					__LINE__
1187
+				);
1188
+				return false;
1189
+			}
1190
+		}
1191
+		// now that things have been kinda sufficiently verified,
1192
+		// let's add the checkout to the session so that it's available to other systems
1193
+		EE_Registry::instance()->SSN->set_checkout($this->checkout);
1194
+		return true;
1195
+	}
1196
+
1197
+
1198
+	/**
1199
+	 *    _initialize_reg_steps
1200
+	 * first makes sure that EE_Transaction_Processor::set_reg_step_initiated() is called as required
1201
+	 * then loops thru all of the active reg steps and calls the initialize_reg_step() method
1202
+	 *
1203
+	 * @param bool $reinitializing
1204
+	 * @throws EE_Error
1205
+	 */
1206
+	private function _initialize_reg_steps($reinitializing = false)
1207
+	{
1208
+		$this->checkout->set_reg_step_initiated($this->checkout->current_step);
1209
+		// loop thru all steps to call their individual "initialize" methods and set i18n strings for JS
1210
+		foreach ($this->checkout->reg_steps as $reg_step) {
1211
+			if (! $reg_step->initialize_reg_step()) {
1212
+				// if not initialized then maybe this step is being removed...
1213
+				if (! $reinitializing && $reg_step->is_current_step()) {
1214
+					// if it was the current step, then we need to start over here
1215
+					$this->_initialize_reg_steps(true);
1216
+					return;
1217
+				}
1218
+				continue;
1219
+			}
1220
+			// add css and JS for current step
1221
+			$this->add_styles_and_scripts($reg_step);
1222
+			if ($reg_step->is_current_step()) {
1223
+				// the text that appears on the reg step form submit button
1224
+				$reg_step->set_submit_button_text();
1225
+			}
1226
+		}
1227
+		// dynamically creates hook point like: AHEE__Single_Page_Checkout___initialize_reg_step__attendee_information
1228
+		do_action(
1229
+			"AHEE__Single_Page_Checkout___initialize_reg_step__{$this->checkout->current_step->slug()}",
1230
+			$this->checkout->current_step
1231
+		);
1232
+	}
1233
+
1234
+
1235
+	/**
1236
+	 * _check_form_submission
1237
+	 *
1238
+	 * @return boolean
1239
+	 * @throws EE_Error
1240
+	 * @throws ReflectionException
1241
+	 */
1242
+	private function _check_form_submission()
1243
+	{
1244
+		// does this request require the reg form to be generated ?
1245
+		if ($this->checkout->generate_reg_form) {
1246
+			// ever heard that song by Blue Rodeo ?
1247
+			try {
1248
+				$this->checkout->current_step->reg_form = $this->checkout->current_step->generate_reg_form();
1249
+				// if not displaying a form, then check for form submission
1250
+				if (
1251
+					$this->checkout->process_form_submission
1252
+					&& $this->checkout->current_step->reg_form->was_submitted()
1253
+				) {
1254
+					// clear out any old data in case this step is being run again
1255
+					$this->checkout->current_step->set_valid_data(array());
1256
+					// capture submitted form data
1257
+					$request_data = $this->request->requestParams();
1258
+					$this->checkout->current_step->reg_form->receive_form_submission(
1259
+						(array) apply_filters(
1260
+							'FHEE__Single_Page_Checkout___check_form_submission__request_params',
1261
+							$request_data,
1262
+							$this->checkout
1263
+						)
1264
+					);
1265
+					// validate submitted form data
1266
+					if (! $this->checkout->continue_reg || ! $this->checkout->current_step->reg_form->is_valid()) {
1267
+						// thou shall not pass !!!
1268
+						$this->checkout->continue_reg = false;
1269
+						// any form validation errors?
1270
+						if ($this->checkout->current_step->reg_form->submission_error_message() !== '') {
1271
+							EE_Error::add_error(
1272
+								$this->checkout->current_step->reg_form->submission_error_message(),
1273
+								__FILE__,
1274
+								__FUNCTION__,
1275
+								__LINE__
1276
+							);
1277
+						}
1278
+						// well not really... what will happen is
1279
+						// we'll just get redirected back to redo the current step
1280
+						$this->go_to_next_step();
1281
+						return false;
1282
+					}
1283
+				}
1284
+			} catch (EE_Error $e) {
1285
+				$e->get_error();
1286
+			}
1287
+		}
1288
+		return true;
1289
+	}
1290
+
1291
+
1292
+	/**
1293
+	 * _process_action
1294
+	 *
1295
+	 * @return void
1296
+	 * @throws EE_Error
1297
+	 * @throws ReflectionException
1298
+	 */
1299
+	private function _process_form_action()
1300
+	{
1301
+		// what cha wanna do?
1302
+		switch ($this->checkout->action) {
1303
+			// AJAX next step reg form
1304
+			case 'display_spco_reg_step':
1305
+				$this->checkout->redirect = false;
1306
+				if ($this->request->isAjax()) {
1307
+					$this->checkout->json_response->set_reg_step_html(
1308
+						$this->checkout->current_step->display_reg_form()
1309
+					);
1310
+				}
1311
+				break;
1312
+			default:
1313
+				// meh... do one of those other steps first
1314
+				if (
1315
+					! empty($this->checkout->action)
1316
+					&& is_callable(array($this->checkout->current_step, $this->checkout->action))
1317
+				) {
1318
+					// dynamically creates hook point like:
1319
+					//   AHEE__Single_Page_Checkout__before_attendee_information__process_reg_step
1320
+					do_action(
1321
+						"AHEE__Single_Page_Checkout__before_{$this->checkout->current_step->slug()}__{$this->checkout->action}",
1322
+						$this->checkout->current_step
1323
+					);
1324
+					$process_reg_step = apply_filters(
1325
+						"AHEE__Single_Page_Checkout__process_reg_step__{$this->checkout->current_step->slug()}__{$this->checkout->action}",
1326
+						true,
1327
+						$this->checkout->current_step,
1328
+						$this
1329
+					);
1330
+					// call action on current step
1331
+					if ($process_reg_step && call_user_func([$this->checkout->current_step, $this->checkout->action])) {
1332
+						// good registrant, you get to proceed
1333
+						if (
1334
+							$this->checkout->current_step->success_message() !== ''
1335
+							&& apply_filters(
1336
+								'FHEE__Single_Page_Checkout___process_form_action__display_success',
1337
+								false
1338
+							)
1339
+						) {
1340
+							EE_Error::add_success(
1341
+								$this->checkout->current_step->success_message()
1342
+								. '<br />' . $this->checkout->next_step->_instructions()
1343
+							);
1344
+						}
1345
+						// pack it up, pack it in...
1346
+						$this->_setup_redirect();
1347
+					}
1348
+					// dynamically creates hook point like:
1349
+					//  AHEE__Single_Page_Checkout__after_payment_options__process_reg_step
1350
+					do_action(
1351
+						"AHEE__Single_Page_Checkout__after_{$this->checkout->current_step->slug()}__{$this->checkout->action}",
1352
+						$this->checkout->current_step
1353
+					);
1354
+				} else {
1355
+					EE_Error::add_error(
1356
+						sprintf(
1357
+							esc_html__(
1358
+								'The requested form action "%s" does not exist for the current "%s" registration step.',
1359
+								'event_espresso'
1360
+							),
1361
+							$this->checkout->action,
1362
+							$this->checkout->current_step->name()
1363
+						),
1364
+						__FILE__,
1365
+						__FUNCTION__,
1366
+						__LINE__
1367
+					);
1368
+				}
1369
+			// end default
1370
+		}
1371
+		// store our progress so far
1372
+		$this->checkout->stash_transaction_and_checkout();
1373
+		// advance to the next step! If you pass GO, collect $200
1374
+		$this->go_to_next_step();
1375
+	}
1376
+
1377
+
1378
+	/**
1379
+	 * @param EED_Single_Page_Checkout|EE_SPCO_Reg_Step $target an object with the method `translate_js_strings` and
1380
+	 *                                                          `enqueue_styles_and_scripts`.
1381
+	 * @return        void
1382
+	 */
1383
+	public function add_styles_and_scripts($target)
1384
+	{
1385
+		// i18n
1386
+		$target->translate_js_strings();
1387
+		if ($this->checkout->admin_request) {
1388
+			add_action('admin_enqueue_scripts', array($target, 'enqueue_styles_and_scripts'), 10);
1389
+		} else {
1390
+			add_action('wp_enqueue_scripts', array($target, 'enqueue_styles_and_scripts'), 10);
1391
+		}
1392
+	}
1393
+
1394
+	/**
1395
+	 *        translate_js_strings
1396
+	 *
1397
+	 * @return        void
1398
+	 */
1399
+	public function translate_js_strings()
1400
+	{
1401
+		EE_Registry::$i18n_js_strings['revisit'] = $this->checkout->revisit;
1402
+		EE_Registry::$i18n_js_strings['e_reg_url_link'] = $this->checkout->reg_url_link;
1403
+		EE_Registry::$i18n_js_strings['server_error'] = esc_html__(
1404
+			'An unknown error occurred on the server while attempting to process your request. Please refresh the page and try again or contact support.',
1405
+			'event_espresso'
1406
+		);
1407
+		EE_Registry::$i18n_js_strings['invalid_json_response'] = esc_html__(
1408
+			'An invalid response was returned from the server while attempting to process your request. Please refresh the page and try again or contact support.',
1409
+			'event_espresso'
1410
+		);
1411
+		EE_Registry::$i18n_js_strings['validation_error'] = esc_html__(
1412
+			'There appears to be a problem with the form validation configuration! Please check the admin settings or contact support.',
1413
+			'event_espresso'
1414
+		);
1415
+		EE_Registry::$i18n_js_strings['invalid_payment_method'] = esc_html__(
1416
+			'There appears to be a problem with the payment method configuration! Please refresh the page and try again or contact support.',
1417
+			'event_espresso'
1418
+		);
1419
+		EE_Registry::$i18n_js_strings['reg_step_error'] = esc_html__(
1420
+			'This registration step could not be completed. Please refresh the page and try again.',
1421
+			'event_espresso'
1422
+		);
1423
+		EE_Registry::$i18n_js_strings['invalid_coupon'] = esc_html__(
1424
+			'We\'re sorry but that coupon code does not appear to be valid. If this is incorrect, please contact the site administrator.',
1425
+			'event_espresso'
1426
+		);
1427
+		EE_Registry::$i18n_js_strings['process_registration'] = sprintf(
1428
+			esc_html__(
1429
+				'Please wait while we process your registration.%sDo not refresh the page or navigate away while this is happening.%sThank you for your patience.',
1430
+				'event_espresso'
1431
+			),
1432
+			'<br/>',
1433
+			'<br/>'
1434
+		);
1435
+		EE_Registry::$i18n_js_strings['language'] = get_bloginfo('language');
1436
+		EE_Registry::$i18n_js_strings['EESID'] = EE_Registry::instance()->SSN->id();
1437
+		EE_Registry::$i18n_js_strings['currency'] = EE_Registry::instance()->CFG->currency;
1438
+		EE_Registry::$i18n_js_strings['datepicker_yearRange'] = '-150:+20';
1439
+		EE_Registry::$i18n_js_strings['timer_years'] = esc_html__('years', 'event_espresso');
1440
+		EE_Registry::$i18n_js_strings['timer_months'] = esc_html__('months', 'event_espresso');
1441
+		EE_Registry::$i18n_js_strings['timer_weeks'] = esc_html__('weeks', 'event_espresso');
1442
+		EE_Registry::$i18n_js_strings['timer_days'] = esc_html__('days', 'event_espresso');
1443
+		EE_Registry::$i18n_js_strings['timer_hours'] = esc_html__('hours', 'event_espresso');
1444
+		EE_Registry::$i18n_js_strings['timer_minutes'] = esc_html__('minutes', 'event_espresso');
1445
+		EE_Registry::$i18n_js_strings['timer_seconds'] = esc_html__('seconds', 'event_espresso');
1446
+		EE_Registry::$i18n_js_strings['timer_year'] = esc_html__('year', 'event_espresso');
1447
+		EE_Registry::$i18n_js_strings['timer_month'] = esc_html__('month', 'event_espresso');
1448
+		EE_Registry::$i18n_js_strings['timer_week'] = esc_html__('week', 'event_espresso');
1449
+		EE_Registry::$i18n_js_strings['timer_day'] = esc_html__('day', 'event_espresso');
1450
+		EE_Registry::$i18n_js_strings['timer_hour'] = esc_html__('hour', 'event_espresso');
1451
+		EE_Registry::$i18n_js_strings['timer_minute'] = esc_html__('minute', 'event_espresso');
1452
+		EE_Registry::$i18n_js_strings['timer_second'] = esc_html__('second', 'event_espresso');
1453
+		EE_Registry::$i18n_js_strings['registration_expiration_notice'] = EED_Single_Page_Checkout::getRegistrationExpirationNotice(
1454
+		);
1455
+		EE_Registry::$i18n_js_strings['ajax_submit'] = apply_filters(
1456
+			'FHEE__Single_Page_Checkout__translate_js_strings__ajax_submit',
1457
+			true
1458
+		);
1459
+		EE_Registry::$i18n_js_strings['session_extension'] = absint(
1460
+			apply_filters('FHEE__EE_Session__extend_expiration__seconds_added', 10 * MINUTE_IN_SECONDS)
1461
+		);
1462
+		EE_Registry::$i18n_js_strings['session_expiration'] = gmdate(
1463
+			'M d, Y H:i:s',
1464
+			EE_Registry::instance()->SSN->expiration() + (get_option('gmt_offset') * HOUR_IN_SECONDS)
1465
+		);
1466
+	}
1467
+
1468
+
1469
+	/**
1470
+	 *    enqueue_styles_and_scripts
1471
+	 *
1472
+	 * @return        void
1473
+	 * @throws EE_Error
1474
+	 */
1475
+	public function enqueue_styles_and_scripts()
1476
+	{
1477
+		// load css
1478
+		wp_register_style(
1479
+			'single_page_checkout',
1480
+			SPCO_CSS_URL . 'single_page_checkout.css',
1481
+			array('espresso_default'),
1482
+			EVENT_ESPRESSO_VERSION
1483
+		);
1484
+		wp_enqueue_style('single_page_checkout');
1485
+		// load JS
1486
+		wp_register_script(
1487
+			'jquery_plugin',
1488
+			EE_THIRD_PARTY_URL . 'jquery	.plugin.min.js',
1489
+			array('jquery'),
1490
+			'1.0.1',
1491
+			true
1492
+		);
1493
+		wp_register_script(
1494
+			'jquery_countdown',
1495
+			EE_THIRD_PARTY_URL . 'jquery	.countdown.min.js',
1496
+			array('jquery_plugin'),
1497
+			'2.1.0',
1498
+			true
1499
+		);
1500
+		wp_register_script(
1501
+			'single_page_checkout',
1502
+			SPCO_JS_URL . 'single_page_checkout.js',
1503
+			array('espresso_core', 'underscore', 'ee_form_section_validation'),
1504
+			EVENT_ESPRESSO_VERSION,
1505
+			true
1506
+		);
1507
+		if ($this->checkout->registration_form instanceof EE_Form_Section_Proper) {
1508
+			$this->checkout->registration_form->enqueue_js();
1509
+		}
1510
+		if ($this->checkout->current_step->reg_form instanceof EE_Form_Section_Proper) {
1511
+			$this->checkout->current_step->reg_form->enqueue_js();
1512
+		}
1513
+		wp_enqueue_script('single_page_checkout');
1514
+		if (apply_filters('FHEE__registration_page_wrapper_template__display_time_limit', false)) {
1515
+			wp_enqueue_script('jquery_countdown');
1516
+		}
1517
+		/**
1518
+		 * global action hook for enqueueing styles and scripts with
1519
+		 * spco calls.
1520
+		 */
1521
+		do_action('AHEE__EED_Single_Page_Checkout__enqueue_styles_and_scripts', $this);
1522
+		/**
1523
+		 * dynamic action hook for enqueueing styles and scripts with spco calls.
1524
+		 * The hook will end up being something like:
1525
+		 *      AHEE__EED_Single_Page_Checkout__enqueue_styles_and_scripts__attendee_information
1526
+		 */
1527
+		do_action(
1528
+			'AHEE__EED_Single_Page_Checkout__enqueue_styles_and_scripts__' . $this->checkout->current_step->slug(),
1529
+			$this
1530
+		);
1531
+	}
1532
+
1533
+
1534
+	/**
1535
+	 *    display the Registration Single Page Checkout Form
1536
+	 *
1537
+	 * @return    void
1538
+	 * @throws EE_Error
1539
+	 * @throws ReflectionException
1540
+	 */
1541
+	private function _display_spco_reg_form()
1542
+	{
1543
+		// if registering via the admin, just display the reg form for the current step
1544
+		if ($this->checkout->admin_request) {
1545
+			EED_Single_Page_Checkout::getResponse()->addOutput($this->checkout->current_step->display_reg_form());
1546
+		} else {
1547
+			// add powered by EE msg
1548
+			add_action('AHEE__SPCO__reg_form_footer', array('EED_Single_Page_Checkout', 'display_registration_footer'));
1549
+			$empty_cart = count(
1550
+				$this->checkout->transaction->registrations($this->checkout->reg_cache_where_params)
1551
+			) < 1;
1552
+			EE_Registry::$i18n_js_strings['empty_cart'] = $empty_cart;
1553
+			$cookies_not_set_msg = '';
1554
+			if ($empty_cart) {
1555
+				$cookies_not_set_msg = apply_filters(
1556
+					'FHEE__Single_Page_Checkout__display_spco_reg_form__cookies_not_set_msg',
1557
+					sprintf(
1558
+						esc_html__(
1559
+							'%1$s%3$sIt appears your browser is not currently set to accept Cookies%4$s%5$sIn order to register for events, you need to enable cookies.%7$sIf you require assistance, then click the following link to learn how to %8$senable cookies%9$s%6$s%2$s',
1560
+							'event_espresso'
1561
+						),
1562
+						'<div class="ee-attention hidden" id="ee-cookies-not-set-msg">',
1563
+						'</div>',
1564
+						'<h6 class="important-notice">',
1565
+						'</h6>',
1566
+						'<p>',
1567
+						'</p>',
1568
+						'<br />',
1569
+						'<a href="https://www.whatismybrowser.com/guides/how-to-enable-cookies/" target="_blank" rel="noopener noreferrer">',
1570
+						'</a>'
1571
+					)
1572
+				);
1573
+			}
1574
+			$this->checkout->registration_form = new EE_Form_Section_Proper(
1575
+				array(
1576
+					'name'            => 'single-page-checkout',
1577
+					'html_id'         => 'ee-single-page-checkout-dv',
1578
+					'layout_strategy' =>
1579
+						new EE_Template_Layout(
1580
+							array(
1581
+								'layout_template_file' => SPCO_TEMPLATES_PATH . 'registration_page_wrapper.template.php',
1582
+								'template_args'        => array(
1583
+									'empty_cart'              => $empty_cart,
1584
+									'revisit'                 => $this->checkout->revisit,
1585
+									'reg_steps'               => $this->checkout->reg_steps,
1586
+									'next_step'               => $this->checkout->next_step instanceof EE_SPCO_Reg_Step
1587
+										? $this->checkout->next_step->slug()
1588
+										: '',
1589
+									'empty_msg'               => apply_filters(
1590
+										'FHEE__Single_Page_Checkout__display_spco_reg_form__empty_msg',
1591
+										sprintf(
1592
+											esc_html__(
1593
+												'You need to %1$sReturn to Events list%2$sselect at least one event%3$s before you can proceed with the registration process.',
1594
+												'event_espresso'
1595
+											),
1596
+											'<a href="'
1597
+											. get_post_type_archive_link('espresso_events')
1598
+											. '" title="',
1599
+											'">',
1600
+											'</a>'
1601
+										)
1602
+									),
1603
+									'cookies_not_set_msg'     => $cookies_not_set_msg,
1604
+									'registration_time_limit' => $this->checkout->get_registration_time_limit(),
1605
+									'session_expiration'      => gmdate(
1606
+										'M d, Y H:i:s',
1607
+										EE_Registry::instance()->SSN->expiration()
1608
+										+ (get_option('gmt_offset') * HOUR_IN_SECONDS)
1609
+									),
1610
+								),
1611
+							)
1612
+						),
1613
+				)
1614
+			);
1615
+			// load template and add to output sent that gets filtered into the_content()
1616
+			EED_Single_Page_Checkout::getResponse()->addOutput($this->checkout->registration_form->get_html());
1617
+		}
1618
+	}
1619
+
1620
+
1621
+	/**
1622
+	 *    add_extra_finalize_registration_inputs
1623
+	 *
1624
+	 * @param $next_step
1625
+	 * @internal  param string $label
1626
+	 * @return void
1627
+	 */
1628
+	public function add_extra_finalize_registration_inputs($next_step)
1629
+	{
1630
+		if ($next_step === 'finalize_registration') {
1631
+			echo '<div id="spco-extra-finalize_registration-inputs-dv"></div>';
1632
+		}
1633
+	}
1634
+
1635
+
1636
+	/**
1637
+	 *    display_registration_footer
1638
+	 *
1639
+	 * @return    string
1640
+	 */
1641
+	public static function display_registration_footer()
1642
+	{
1643
+		if (
1644
+			apply_filters(
1645
+				'FHEE__EE_Front__Controller__show_reg_footer',
1646
+				EE_Registry::instance()->CFG->admin->show_reg_footer
1647
+			)
1648
+		) {
1649
+			add_filter(
1650
+				'FHEE__EEH_Template__powered_by_event_espresso__url',
1651
+				function ($url) {
1652
+					return apply_filters('FHEE__EE_Front_Controller__registration_footer__url', $url);
1653
+				}
1654
+			);
1655
+			echo apply_filters(
1656
+				'FHEE__EE_Front_Controller__display_registration_footer',
1657
+				EEH_Template::powered_by_event_espresso(
1658
+					'',
1659
+					'espresso-registration-footer-dv',
1660
+					array('utm_content' => 'registration_checkout')
1661
+				)
1662
+			);
1663
+		}
1664
+		return '';
1665
+	}
1666
+
1667
+
1668
+	/**
1669
+	 *    unlock_transaction
1670
+	 *
1671
+	 * @return    void
1672
+	 * @throws EE_Error
1673
+	 * @throws ReflectionException
1674
+	 */
1675
+	public function unlock_transaction()
1676
+	{
1677
+		if ($this->checkout->transaction instanceof EE_Transaction) {
1678
+			$this->checkout->transaction->unlock();
1679
+		}
1680
+	}
1681
+
1682
+
1683
+	/**
1684
+	 *        _setup_redirect
1685
+	 *
1686
+	 * @return void
1687
+	 */
1688
+	private function _setup_redirect()
1689
+	{
1690
+		if ($this->checkout->continue_reg && $this->checkout->next_step instanceof EE_SPCO_Reg_Step) {
1691
+			$this->checkout->redirect = true;
1692
+			if (empty($this->checkout->redirect_url)) {
1693
+				$this->checkout->redirect_url = $this->checkout->next_step->reg_step_url();
1694
+			}
1695
+			$this->checkout->redirect_url = apply_filters(
1696
+				'FHEE__EED_Single_Page_Checkout___setup_redirect__checkout_redirect_url',
1697
+				$this->checkout->redirect_url,
1698
+				$this->checkout
1699
+			);
1700
+		}
1701
+	}
1702
+
1703
+
1704
+	/**
1705
+	 *   handle ajax message responses and redirects
1706
+	 *
1707
+	 * @return void
1708
+	 * @throws EE_Error
1709
+	 * @throws ReflectionException
1710
+	 */
1711
+	public function go_to_next_step()
1712
+	{
1713
+		if ($this->request->isAjax()) {
1714
+			// capture contents of output buffer we started earlier in the request, and insert into JSON response
1715
+			$this->checkout->json_response->set_unexpected_errors(ob_get_clean());
1716
+		}
1717
+		$this->unlock_transaction();
1718
+		// just return for these conditions
1719
+		if (
1720
+			$this->checkout->admin_request
1721
+			|| $this->checkout->action === 'redirect_form'
1722
+			|| $this->checkout->action === 'update_checkout'
1723
+		) {
1724
+			return;
1725
+		}
1726
+		// AJAX response
1727
+		$this->_handle_json_response();
1728
+		// redirect to next step or the Thank You page
1729
+		$this->_handle_html_redirects();
1730
+		// hmmm... must be something wrong, so let's just display the form again !
1731
+		$this->_display_spco_reg_form();
1732
+	}
1733
+
1734
+
1735
+	/**
1736
+	 *   _handle_json_response
1737
+	 *
1738
+	 * @return void
1739
+	 * @throws EE_Error
1740
+	 */
1741
+	protected function _handle_json_response()
1742
+	{
1743
+		// if this is an ajax request
1744
+		if ($this->request->isAjax()) {
1745
+			$this->checkout->json_response->set_registration_time_limit(
1746
+				$this->checkout->get_registration_time_limit()
1747
+			);
1748
+			$this->checkout->json_response->set_payment_amount($this->checkout->amount_owing);
1749
+			// just send the ajax (
1750
+			$json_response = apply_filters(
1751
+				'FHEE__EE_Single_Page_Checkout__JSON_response',
1752
+				$this->checkout->json_response
1753
+			);
1754
+			echo ($json_response);
1755
+			exit();
1756
+		}
1757
+	}
1758
+
1759
+
1760
+	/**
1761
+	 *   _handle_redirects
1762
+	 *
1763
+	 * @return void
1764
+	 */
1765
+	protected function _handle_html_redirects()
1766
+	{
1767
+		// going somewhere ?
1768
+		if ($this->checkout->redirect && ! empty($this->checkout->redirect_url)) {
1769
+			// store notices in a transient
1770
+			EE_Error::get_notices(false, true);
1771
+			wp_safe_redirect($this->checkout->redirect_url);
1772
+			exit();
1773
+		}
1774
+	}
1775
+
1776
+
1777
+	/**
1778
+	 *   set_checkout_anchor
1779
+	 *
1780
+	 * @return void
1781
+	 */
1782
+	public function set_checkout_anchor()
1783
+	{
1784
+		echo '<a id="checkout" style="float: left; margin-left: -999em;"></a>';
1785
+	}
1786
+
1787
+	/**
1788
+	 *    getRegistrationExpirationNotice
1789
+	 *
1790
+	 * @since     4.9.59.p
1791
+	 * @return    string
1792
+	 */
1793
+	public static function getRegistrationExpirationNotice()
1794
+	{
1795
+		return sprintf(
1796
+			esc_html__(
1797
+				'%1$sWe\'re sorry, but your registration time has expired.%2$s%3$s%4$sIf you still wish to complete your registration, please return to the %5$sEvent List%6$sEvent List%7$s and reselect your tickets if available. Please accept our apologies for any inconvenience this may have caused.%8$s',
1798
+				'event_espresso'
1799
+			),
1800
+			'<h4 class="important-notice">',
1801
+			'</h4>',
1802
+			'<br />',
1803
+			'<p>',
1804
+			'<a href="' . get_post_type_archive_link('espresso_events') . '" title="',
1805
+			'">',
1806
+			'</a>',
1807
+			'</p>'
1808
+		);
1809
+	}
1810 1810
 }
Please login to merge, or discard this patch.