Completed
Pull Request — master (#331)
by Darren
24:13 queued 10:33
created
modules/ticket_sales_monitor/EED_Ticket_Sales_Monitor.module.php 2 patches
Indentation   +1043 added lines, -1043 removed lines patch added patch discarded remove patch
@@ -20,1048 +20,1048 @@
 block discarded – undo
20 20
 class EED_Ticket_Sales_Monitor extends EED_Module
21 21
 {
22 22
 
23
-    const debug = false;
24
-
25
-    private static $nl = '';
26
-
27
-    /**
28
-     * an array of raw ticket data from EED_Ticket_Selector
29
-     *
30
-     * @var array $ticket_selections
31
-     */
32
-    protected $ticket_selections = array();
33
-
34
-    /**
35
-     * the raw ticket data from EED_Ticket_Selector is organized in rows
36
-     * according to how they are displayed in the actual Ticket_Selector
37
-     * this tracks the current row being processed
38
-     *
39
-     * @var int $current_row
40
-     */
41
-    protected $current_row = 0;
42
-
43
-    /**
44
-     * an array for tracking names of tickets that have sold out
45
-     *
46
-     * @var array $sold_out_tickets
47
-     */
48
-    protected $sold_out_tickets = array();
49
-
50
-    /**
51
-     * an array for tracking names of tickets that have had their quantities reduced
52
-     *
53
-     * @var array $decremented_tickets
54
-     */
55
-    protected $decremented_tickets = array();
56
-
57
-
58
-    /**
59
-     * set_hooks - for hooking into EE Core, other modules, etc
60
-     *
61
-     * @return    void
62
-     */
63
-    public static function set_hooks()
64
-    {
65
-        self::$nl = defined('EE_TESTS_DIR') ? "\n" : '<br />';
66
-        // release tickets for expired carts
67
-        add_action(
68
-            'EED_Ticket_Selector__process_ticket_selections__before',
69
-            array('EED_Ticket_Sales_Monitor', 'release_tickets_for_expired_carts'),
70
-            1
71
-        );
72
-        // check ticket reserves AFTER MER does it's check (hence priority 20)
73
-        add_filter(
74
-            'FHEE__EE_Ticket_Selector___add_ticket_to_cart__ticket_qty',
75
-            array('EED_Ticket_Sales_Monitor', 'validate_ticket_sale'),
76
-            20,
77
-            3
78
-        );
79
-        // add notices for sold out tickets
80
-        add_action(
81
-            'AHEE__EE_Ticket_Selector__process_ticket_selections__after_tickets_added_to_cart',
82
-            array('EED_Ticket_Sales_Monitor', 'post_notices'),
83
-            10
84
-        );
85
-
86
-        // handle tickets deleted from cart
87
-        add_action(
88
-            'FHEE__EED_Multi_Event_Registration__delete_ticket__ticket_removed_from_cart',
89
-            array('EED_Ticket_Sales_Monitor', 'ticket_removed_from_cart'),
90
-            10,
91
-            2
92
-        );
93
-        // handle emptied carts
94
-        add_action(
95
-            'AHEE__EE_Session__reset_cart__before_reset',
96
-            array('EED_Ticket_Sales_Monitor', 'session_cart_reset'),
97
-            10,
98
-            1
99
-        );
100
-        add_action(
101
-            'AHEE__EED_Multi_Event_Registration__empty_event_cart__before_delete_cart',
102
-            array('EED_Ticket_Sales_Monitor', 'session_cart_reset'),
103
-            10,
104
-            1
105
-        );
106
-        // handle cancelled registrations
107
-        add_action(
108
-            'AHEE__EE_Session__reset_checkout__before_reset',
109
-            array('EED_Ticket_Sales_Monitor', 'session_checkout_reset'),
110
-            10,
111
-            1
112
-        );
113
-        // cron tasks
114
-        add_action(
115
-            'AHEE__EE_Cron_Tasks__process_expired_transactions__abandoned_transaction',
116
-            array('EED_Ticket_Sales_Monitor', 'process_abandoned_transactions'),
117
-            10,
118
-            1
119
-        );
120
-        add_action(
121
-            'AHEE__EE_Cron_Tasks__process_expired_transactions__incomplete_transaction',
122
-            array('EED_Ticket_Sales_Monitor', 'process_abandoned_transactions'),
123
-            10,
124
-            1
125
-        );
126
-        add_action(
127
-            'AHEE__EE_Cron_Tasks__process_expired_transactions__failed_transaction',
128
-            array('EED_Ticket_Sales_Monitor', 'process_failed_transactions'),
129
-            10,
130
-            1
131
-        );
132
-    }
133
-
134
-
135
-    /**
136
-     * set_hooks_admin - for hooking into EE Admin Core, other modules, etc
137
-     *
138
-     * @return void
139
-     */
140
-    public static function set_hooks_admin()
141
-    {
142
-        EED_Ticket_Sales_Monitor::set_hooks();
143
-    }
144
-
145
-
146
-    /**
147
-     * @return EED_Ticket_Sales_Monitor|EED_Module
148
-     */
149
-    public static function instance()
150
-    {
151
-        return parent::get_instance(__CLASS__);
152
-    }
153
-
154
-
155
-    /**
156
-     * @param WP_Query $WP_Query
157
-     * @return    void
158
-     */
159
-    public function run($WP_Query)
160
-    {
161
-    }
162
-
163
-
164
-
165
-    /********************************** PRE_TICKET_SALES  **********************************/
166
-
167
-
168
-    /**
169
-     * Retrieves grand totals from the line items that have no TXN ID
170
-     * and timestamps less than the current time minus the session lifespan.
171
-     * These are carts that have been abandoned before the "registrant" even attempted to checkout.
172
-     * We're going to release the tickets for these line items before attempting to add more to the cart.
173
-     *
174
-     * @return void
175
-     * @throws DomainException
176
-     * @throws EE_Error
177
-     * @throws InvalidArgumentException
178
-     * @throws InvalidDataTypeException
179
-     * @throws InvalidInterfaceException
180
-     * @throws UnexpectedEntityException
181
-     */
182
-    public static function release_tickets_for_expired_carts()
183
-    {
184
-        if (self::debug) {
185
-            echo self::$nl . self::$nl . __LINE__ . ') ' . __METHOD__ . '()';
186
-        }
187
-        do_action('AHEE__EED_Ticket_Sales_Monitor__release_tickets_for_expired_carts__begin');
188
-        $expired_ticket_IDs = array();
189
-        /** @var EventEspresso\core\domain\values\session\SessionLifespan $session_lifespan */
190
-        $session_lifespan = LoaderFactory::getLoader()->getShared(
191
-            'EventEspresso\core\domain\values\session\SessionLifespan'
192
-        );
193
-        $timestamp = $session_lifespan->expiration();
194
-        $expired_ticket_line_items = EEM_Line_Item::instance()->getTicketLineItemsForExpiredCarts($timestamp);
195
-        if (self::debug) {
196
-            echo self::$nl . ' . time(): ' . time();
197
-            echo self::$nl . ' . time() as date: ' . date('Y-m-d H:i a');
198
-            echo self::$nl . ' . session expiration: ' . $session_lifespan->expiration();
199
-            echo self::$nl . ' . session expiration as date: ' . date('Y-m-d H:i a', $session_lifespan->expiration());
200
-            echo self::$nl . ' . timestamp: ' . $timestamp;
201
-            echo self::$nl . ' . $expired_ticket_line_items: ' . count($expired_ticket_line_items);
202
-        }
203
-        if (! empty($expired_ticket_line_items)) {
204
-            foreach ($expired_ticket_line_items as $expired_ticket_line_item) {
205
-                if (! $expired_ticket_line_item instanceof EE_Line_Item) {
206
-                    continue;
207
-                }
208
-                $expired_ticket_IDs[ $expired_ticket_line_item->OBJ_ID() ] = $expired_ticket_line_item->OBJ_ID();
209
-                if (self::debug) {
210
-                    echo self::$nl . ' . $expired_ticket_line_item->OBJ_ID(): ' . $expired_ticket_line_item->OBJ_ID();
211
-                    echo self::$nl . ' . $expired_ticket_line_item->timestamp(): '
212
-                         . date(
213
-                             'Y-m-d h:i a',
214
-                             $expired_ticket_line_item->timestamp(true)
215
-                         );
216
-                }
217
-            }
218
-            if (! empty($expired_ticket_IDs)) {
219
-                EED_Ticket_Sales_Monitor::release_reservations_for_tickets(
220
-                    \EEM_Ticket::instance()->get_tickets_with_IDs($expired_ticket_IDs),
221
-                    array(),
222
-                    __FUNCTION__
223
-                );
224
-                // now  let's get rid of expired line items so that they can't interfere with tracking
225
-                EED_Ticket_Sales_Monitor::clear_expired_line_items_with_no_transaction($timestamp);
226
-            }
227
-        }
228
-        do_action(
229
-            'AHEE__EED_Ticket_Sales_Monitor__release_tickets_for_expired_carts__end',
230
-            $expired_ticket_IDs,
231
-            $expired_ticket_line_items
232
-        );
233
-    }
234
-
235
-
236
-
237
-    /********************************** VALIDATE_TICKET_SALE  **********************************/
238
-
239
-
240
-    /**
241
-     * callback for 'FHEE__EED_Ticket_Selector__process_ticket_selections__valid_post_data'
242
-     *
243
-     * @param int       $qty
244
-     * @param EE_Ticket $ticket
245
-     * @return bool
246
-     * @throws UnexpectedEntityException
247
-     * @throws EE_Error
248
-     */
249
-    public static function validate_ticket_sale($qty = 1, EE_Ticket $ticket)
250
-    {
251
-        $qty = absint($qty);
252
-        if ($qty > 0) {
253
-            $qty = EED_Ticket_Sales_Monitor::instance()->_validate_ticket_sale($ticket, $qty);
254
-        }
255
-        if (self::debug) {
256
-            echo self::$nl . self::$nl . __LINE__ . ') ' . __METHOD__ . '()';
257
-            echo self::$nl . self::$nl . '<b> RETURNED QTY: ' . $qty . '</b>';
258
-        }
259
-        return $qty;
260
-    }
261
-
262
-
263
-    /**
264
-     * checks whether an individual ticket is available for purchase based on datetime, and ticket details
265
-     *
266
-     * @param   EE_Ticket $ticket
267
-     * @param int         $qty
268
-     * @return int
269
-     * @throws UnexpectedEntityException
270
-     * @throws EE_Error
271
-     */
272
-    protected function _validate_ticket_sale(EE_Ticket $ticket, $qty = 1)
273
-    {
274
-        if (self::debug) {
275
-            echo self::$nl . self::$nl . __LINE__ . ') ' . __METHOD__ . '() ';
276
-        }
277
-        if (! $ticket instanceof EE_Ticket) {
278
-            return 0;
279
-        }
280
-        if (self::debug) {
281
-            echo self::$nl . '<b> . ticket->ID: ' . $ticket->ID() . '</b>';
282
-            echo self::$nl . ' . original ticket->reserved: ' . $ticket->reserved();
283
-        }
284
-        $ticket->refresh_from_db();
285
-        // first let's determine the ticket availability based on sales
286
-        $available = $ticket->qty('saleable');
287
-        if (self::debug) {
288
-            echo self::$nl . ' . . . ticket->qty: ' . $ticket->qty();
289
-            echo self::$nl . ' . . . ticket->sold: ' . $ticket->sold();
290
-            echo self::$nl . ' . . . ticket->reserved: ' . $ticket->reserved();
291
-            echo self::$nl . ' . . . ticket->qty(saleable): ' . $ticket->qty('saleable');
292
-            echo self::$nl . ' . . . available: ' . $available;
293
-        }
294
-        if ($available < 1) {
295
-            $this->_ticket_sold_out($ticket);
296
-            return 0;
297
-        }
298
-        if (self::debug) {
299
-            echo self::$nl . ' . . . qty: ' . $qty;
300
-        }
301
-        if ($available < $qty) {
302
-            $qty = $available;
303
-            if (self::debug) {
304
-                echo self::$nl . ' . . . QTY ADJUSTED: ' . $qty;
305
-            }
306
-            $this->_ticket_quantity_decremented($ticket);
307
-        }
308
-        $this->_reserve_ticket($ticket, $qty);
309
-        return $qty;
310
-    }
311
-
312
-
313
-    /**
314
-     * increments ticket reserved based on quantity passed
315
-     *
316
-     * @param    EE_Ticket $ticket
317
-     * @param int          $quantity
318
-     * @return bool
319
-     * @throws EE_Error
320
-     */
321
-    protected function _reserve_ticket(EE_Ticket $ticket, $quantity = 1)
322
-    {
323
-        if (self::debug) {
324
-            echo self::$nl . self::$nl . ' . . . INCREASE RESERVED: ' . $quantity;
325
-        }
326
-        $ticket->increase_reserved($quantity, 'TicketSalesMonitor:' . __LINE__);
327
-        return $ticket->save();
328
-    }
329
-
330
-
331
-    /**
332
-     * @param  EE_Ticket $ticket
333
-     * @param  int       $quantity
334
-     * @return bool
335
-     * @throws EE_Error
336
-     */
337
-    protected function _release_reserved_ticket(EE_Ticket $ticket, $quantity = 1)
338
-    {
339
-        if (self::debug) {
340
-            echo self::$nl . ' . . . ticket->ID: ' . $ticket->ID();
341
-            echo self::$nl . ' . . . ticket->reserved before: ' . $ticket->reserved();
342
-        }
343
-        $ticket->decrease_reserved($quantity, true, 'TicketSalesMonitor:' . __LINE__);
344
-        if (self::debug) {
345
-            echo self::$nl . ' . . . ticket->reserved after: ' . $ticket->reserved();
346
-        }
347
-        return $ticket->save() ? 1 : 0;
348
-    }
349
-
350
-
351
-    /**
352
-     * removes quantities within the ticket selector based on zero ticket availability
353
-     *
354
-     * @param    EE_Ticket $ticket
355
-     * @return    void
356
-     * @throws UnexpectedEntityException
357
-     * @throws EE_Error
358
-     */
359
-    protected function _ticket_sold_out(EE_Ticket $ticket)
360
-    {
361
-        if (self::debug) {
362
-            echo self::$nl . self::$nl . __LINE__ . ') ' . __METHOD__ . '() ';
363
-            echo self::$nl . ' . . ticket->name: ' . $this->_get_ticket_and_event_name($ticket);
364
-        }
365
-        $this->sold_out_tickets[] = $this->_get_ticket_and_event_name($ticket);
366
-    }
367
-
368
-
369
-    /**
370
-     * adjusts quantities within the ticket selector based on decreased ticket availability
371
-     *
372
-     * @param    EE_Ticket $ticket
373
-     * @return void
374
-     * @throws UnexpectedEntityException
375
-     * @throws EE_Error
376
-     */
377
-    protected function _ticket_quantity_decremented(EE_Ticket $ticket)
378
-    {
379
-        if (self::debug) {
380
-            echo self::$nl . self::$nl . __LINE__ . ') ' . __METHOD__ . '() ';
381
-            echo self::$nl . ' . . ticket->name: ' . $this->_get_ticket_and_event_name($ticket);
382
-        }
383
-        $this->decremented_tickets[] = $this->_get_ticket_and_event_name($ticket);
384
-    }
385
-
386
-
387
-    /**
388
-     * builds string out of ticket and event name
389
-     *
390
-     * @param    EE_Ticket $ticket
391
-     * @return string
392
-     * @throws UnexpectedEntityException
393
-     * @throws EE_Error
394
-     */
395
-    protected function _get_ticket_and_event_name(EE_Ticket $ticket)
396
-    {
397
-        $event = $ticket->get_related_event();
398
-        if ($event instanceof EE_Event) {
399
-            $ticket_name = sprintf(
400
-                _x('%1$s for %2$s', 'ticket name for event name', 'event_espresso'),
401
-                $ticket->name(),
402
-                $event->name()
403
-            );
404
-        } else {
405
-            $ticket_name = $ticket->name();
406
-        }
407
-        return $ticket_name;
408
-    }
409
-
410
-
411
-
412
-    /********************************** EVENT CART  **********************************/
413
-
414
-
415
-    /**
416
-     * releases or reserves ticket(s) based on quantity passed
417
-     *
418
-     * @param  EE_Line_Item $line_item
419
-     * @param  int          $quantity
420
-     * @return void
421
-     * @throws EE_Error
422
-     * @throws InvalidArgumentException
423
-     * @throws InvalidDataTypeException
424
-     * @throws InvalidInterfaceException
425
-     */
426
-    public static function ticket_quantity_updated(EE_Line_Item $line_item, $quantity = 1)
427
-    {
428
-        $ticket = EEM_Ticket::instance()->get_one_by_ID(absint($line_item->OBJ_ID()));
429
-        if ($ticket instanceof EE_Ticket) {
430
-            $ticket->add_extra_meta(
431
-                EE_Ticket::META_KEY_TICKET_RESERVATIONS,
432
-                __LINE__ . ') ' . __METHOD__ . '()'
433
-            );
434
-            if ($quantity > 0) {
435
-                EED_Ticket_Sales_Monitor::instance()->_reserve_ticket($ticket, $quantity);
436
-            } else {
437
-                EED_Ticket_Sales_Monitor::instance()->_release_reserved_ticket($ticket, $quantity);
438
-            }
439
-        }
440
-    }
441
-
442
-
443
-    /**
444
-     * releases reserved ticket(s) based on quantity passed
445
-     *
446
-     * @param  EE_Ticket $ticket
447
-     * @param  int       $quantity
448
-     * @return void
449
-     * @throws EE_Error
450
-     */
451
-    public static function ticket_removed_from_cart(EE_Ticket $ticket, $quantity = 1)
452
-    {
453
-        $ticket->add_extra_meta(
454
-            EE_Ticket::META_KEY_TICKET_RESERVATIONS,
455
-            __LINE__ . ') ' . __METHOD__ . '()'
456
-        );
457
-        EED_Ticket_Sales_Monitor::instance()->_release_reserved_ticket($ticket, $quantity);
458
-    }
459
-
460
-
461
-
462
-    /********************************** POST_NOTICES  **********************************/
463
-
464
-
465
-    /**
466
-     * @return void
467
-     * @throws EE_Error
468
-     * @throws InvalidArgumentException
469
-     * @throws ReflectionException
470
-     * @throws InvalidDataTypeException
471
-     * @throws InvalidInterfaceException
472
-     */
473
-    public static function post_notices()
474
-    {
475
-        EED_Ticket_Sales_Monitor::instance()->_post_notices();
476
-    }
477
-
478
-
479
-    /**
480
-     * @return void
481
-     * @throws EE_Error
482
-     * @throws InvalidArgumentException
483
-     * @throws ReflectionException
484
-     * @throws InvalidDataTypeException
485
-     * @throws InvalidInterfaceException
486
-     */
487
-    protected function _post_notices()
488
-    {
489
-        if (self::debug) {
490
-            echo self::$nl . self::$nl . __LINE__ . ') ' . __METHOD__ . '() ';
491
-        }
492
-        $refresh_msg = '';
493
-        $none_added_msg = '';
494
-        if (defined('DOING_AJAX') && DOING_AJAX) {
495
-            $refresh_msg = __(
496
-                'Please refresh the page to view updated ticket quantities.',
497
-                'event_espresso'
498
-            );
499
-            $none_added_msg = __('No tickets were added for the event.', 'event_espresso');
500
-        }
501
-        if (! empty($this->sold_out_tickets)) {
502
-            EE_Error::add_attention(
503
-                sprintf(
504
-                    apply_filters(
505
-                        'FHEE__EED_Ticket_Sales_Monitor___post_notices__sold_out_tickets_notice',
506
-                        __(
507
-                            'We\'re sorry...%1$sThe following items have sold out since you first viewed this page, and can no longer be registered for:%1$s%1$s%2$s%1$s%1$sPlease note that availability can change at any time due to cancellations, so please check back again later if registration for this event(s) is important to you.%1$s%1$s%3$s%1$s%4$s%1$s',
508
-                            'event_espresso'
509
-                        )
510
-                    ),
511
-                    '<br />',
512
-                    implode('<br />', $this->sold_out_tickets),
513
-                    $none_added_msg,
514
-                    $refresh_msg
515
-                )
516
-            );
517
-            // alter code flow in the Ticket Selector for better UX
518
-            add_filter('FHEE__EED_Ticket_Selector__process_ticket_selections__tckts_slctd', '__return_true');
519
-            add_filter('FHEE__EED_Ticket_Selector__process_ticket_selections__success', '__return_false');
520
-            $this->sold_out_tickets = array();
521
-            // and reset the cart
522
-            EED_Ticket_Sales_Monitor::session_cart_reset(EE_Registry::instance()->SSN);
523
-        }
524
-        if (! empty($this->decremented_tickets)) {
525
-            EE_Error::add_attention(
526
-                sprintf(
527
-                    apply_filters(
528
-                        'FHEE__EED_Ticket_Sales_Monitor___ticket_quantity_decremented__notice',
529
-                        __(
530
-                            'We\'re sorry...%1$sDue to sales that have occurred since you first viewed the last page, the following items have had their quantities adjusted to match the current available amount:%1$s%1$s%2$s%1$s%1$sPlease note that availability can change at any time due to cancellations, so please check back again later if registration for this event(s) is important to you.%1$s%1$s%3$s%1$s%4$s%1$s',
531
-                            'event_espresso'
532
-                        )
533
-                    ),
534
-                    '<br />',
535
-                    implode('<br />', $this->decremented_tickets),
536
-                    $none_added_msg,
537
-                    $refresh_msg
538
-                )
539
-            );
540
-            $this->decremented_tickets = array();
541
-        }
542
-    }
543
-
544
-
545
-
546
-    /********************************** RELEASE_ALL_RESERVED_TICKETS_FOR_TRANSACTION  **********************************/
547
-
548
-
549
-    /**
550
-     * releases reserved tickets for all registrations of an EE_Transaction
551
-     * by default, will NOT release tickets for finalized transactions
552
-     *
553
-     * @param    EE_Transaction $transaction
554
-     * @return int
555
-     * @throws EE_Error
556
-     * @throws InvalidSessionDataException
557
-     */
558
-    protected function _release_all_reserved_tickets_for_transaction(EE_Transaction $transaction)
559
-    {
560
-        if (self::debug) {
561
-            echo self::$nl . self::$nl . __LINE__ . ') ' . __METHOD__ . '() ';
562
-            echo self::$nl . ' . transaction->ID: ' . $transaction->ID();
563
-            echo self::$nl . ' . TXN status_ID: ' . $transaction->status_ID();
564
-        }
565
-        // check if 'finalize_registration' step has been completed...
566
-        $finalized = $transaction->reg_step_completed('finalize_registration');
567
-        if (self::debug) {
568
-            // DEBUG LOG
569
-            EEH_Debug_Tools::log(
570
-                __CLASS__,
571
-                __FUNCTION__,
572
-                __LINE__,
573
-                array('finalized' => $finalized),
574
-                false,
575
-                'EE_Transaction: ' . $transaction->ID()
576
-            );
577
-        }
578
-        // how many tickets were released
579
-        $count = 0;
580
-        if (self::debug) {
581
-            echo self::$nl . ' . . . TXN finalized: ' . $finalized;
582
-        }
583
-        $release_tickets_with_TXN_status = array(
584
-            EEM_Transaction::failed_status_code,
585
-            EEM_Transaction::abandoned_status_code,
586
-            EEM_Transaction::incomplete_status_code,
587
-        );
588
-        $events = array();
589
-        // if the session is getting cleared BEFORE the TXN has been finalized or the transaction is not completed
590
-        if (! $finalized || in_array($transaction->status_ID(), $release_tickets_with_TXN_status, true)) {
591
-            // cancel any reserved tickets for registrations that were not approved
592
-            $registrations = $transaction->registrations();
593
-            if (self::debug) {
594
-                echo self::$nl . ' . . . # registrations: ' . count($registrations);
595
-                $reg = reset($registrations);
596
-                $ticket = $reg->ticket();
597
-                if ($ticket instanceof EE_Ticket) {
598
-                    $ticket->add_extra_meta(
599
-                        EE_Ticket::META_KEY_TICKET_RESERVATIONS,
600
-                        __LINE__ . ') Release All Tickets TXN:' . $transaction->ID()
601
-                    );
602
-                }
603
-            }
604
-            if (! empty($registrations)) {
605
-                foreach ($registrations as $registration) {
606
-                    if ($registration instanceof EE_Registration
607
-                        && $this->_release_reserved_ticket_for_registration($registration, $transaction)
608
-                    ) {
609
-                        $count++;
610
-                        $events[ $registration->event_ID() ] = $registration->event();
611
-                    }
612
-                }
613
-            }
614
-        }
615
-        if ($events !== array()) {
616
-            foreach ($events as $event) {
617
-                /** @var EE_Event $event */
618
-                $event->perform_sold_out_status_check();
619
-            }
620
-        }
621
-        return $count;
622
-    }
623
-
624
-
625
-    /**
626
-     * releases reserved tickets for an EE_Registration
627
-     * by default, will NOT release tickets for APPROVED registrations
628
-     *
629
-     * @param EE_Registration $registration
630
-     * @param EE_Transaction  $transaction
631
-     * @return int
632
-     * @throws EE_Error
633
-     */
634
-    protected function _release_reserved_ticket_for_registration(
635
-        EE_Registration $registration,
636
-        EE_Transaction $transaction
637
-    ) {
638
-        $STS_ID = $transaction->status_ID();
639
-        if (self::debug) {
640
-            echo self::$nl . self::$nl . __LINE__ . ') ' . __METHOD__ . '() ';
641
-            echo self::$nl . ' . . registration->ID: ' . $registration->ID();
642
-            echo self::$nl . ' . . registration->status_ID: ' . $registration->status_ID();
643
-            echo self::$nl . ' . . transaction->status_ID(): ' . $STS_ID;
644
-        }
645
-        if (// release Tickets for Failed Transactions and Abandoned Transactions
646
-            $STS_ID === EEM_Transaction::failed_status_code
647
-            || $STS_ID === EEM_Transaction::abandoned_status_code
648
-            || (
649
-                // also release Tickets for Incomplete Transactions, but ONLY if the Registrations are NOT Approved
650
-                $STS_ID === EEM_Transaction::incomplete_status_code
651
-                && $registration->status_ID() !== EEM_Registration::status_id_approved
652
-            )
653
-        ) {
654
-            if (self::debug) {
655
-                echo self::$nl . self::$nl . ' . . RELEASE RESERVED TICKET';
656
-                $rsrvd = $registration->get_extra_meta(EE_Registration::HAS_RESERVED_TICKET_KEY, true);
657
-                echo self::$nl . ' . . . registration HAS_RESERVED_TICKET_KEY: ';
658
-                var_dump($rsrvd);
659
-            }
660
-            $registration->release_reserved_ticket(true, 'TicketSalesMonitor:' . __LINE__);
661
-            return 1;
662
-        }
663
-        return 0;
664
-    }
665
-
666
-
667
-
668
-    /********************************** SESSION_CART_RESET  **********************************/
669
-
670
-
671
-    /**
672
-     * callback hooked into 'AHEE__EE_Session__reset_cart__before_reset'
673
-     *
674
-     * @param EE_Session $session
675
-     * @return void
676
-     * @throws EE_Error
677
-     * @throws InvalidArgumentException
678
-     * @throws ReflectionException
679
-     * @throws InvalidDataTypeException
680
-     * @throws InvalidInterfaceException
681
-     */
682
-    public static function session_cart_reset(EE_Session $session)
683
-    {
684
-        // don't release tickets if checkout was already reset
685
-        if (did_action('AHEE__EE_Session__reset_checkout__before_reset')) {
686
-            return;
687
-        }
688
-        if (self::debug) {
689
-            echo self::$nl . self::$nl . __LINE__ . ') ' . __METHOD__ . '() ';
690
-        }
691
-        // first check of the session has a valid Checkout object
692
-        $checkout = $session->checkout();
693
-        if ($checkout instanceof EE_Checkout) {
694
-            // and use that to clear ticket reservations because it will update the associated registration meta data
695
-            EED_Ticket_Sales_Monitor::instance()->_session_checkout_reset($checkout);
696
-            return;
697
-        }
698
-        $cart = $session->cart();
699
-        if ($cart instanceof EE_Cart) {
700
-            if (self::debug) {
701
-                echo self::$nl . self::$nl . ' cart instance of EE_Cart: ';
702
-            }
703
-            EED_Ticket_Sales_Monitor::instance()->_session_cart_reset($cart, $session);
704
-        } else {
705
-            if (self::debug) {
706
-                echo self::$nl . self::$nl . ' invalid EE_Cart: ';
707
-                var_export($cart, true);
708
-            }
709
-        }
710
-    }
711
-
712
-
713
-    /**
714
-     * releases reserved tickets in the EE_Cart
715
-     *
716
-     * @param EE_Cart $cart
717
-     * @return void
718
-     * @throws EE_Error
719
-     * @throws InvalidArgumentException
720
-     * @throws ReflectionException
721
-     * @throws InvalidDataTypeException
722
-     * @throws InvalidInterfaceException
723
-     */
724
-    protected function _session_cart_reset(EE_Cart $cart, EE_Session $session)
725
-    {
726
-        if (self::debug) {
727
-            echo self::$nl . self::$nl . __LINE__ . ') ' . __METHOD__ . '() ';
728
-        }
729
-        $ticket_line_items = $cart->get_tickets();
730
-        if (empty($ticket_line_items)) {
731
-            return;
732
-        }
733
-        if (self::debug) {
734
-            echo '<br /> . ticket_line_item count: ' . count($ticket_line_items);
735
-        }
736
-        foreach ($ticket_line_items as $ticket_line_item) {
737
-            if (self::debug) {
738
-                echo self::$nl . ' . ticket_line_item->ID(): ' . $ticket_line_item->ID();
739
-            }
740
-            if ($ticket_line_item instanceof EE_Line_Item && $ticket_line_item->OBJ_type() === 'Ticket') {
741
-                if (self::debug) {
742
-                    echo self::$nl . ' . . ticket_line_item->OBJ_ID(): ' . $ticket_line_item->OBJ_ID();
743
-                }
744
-                $ticket = EEM_Ticket::instance()->get_one_by_ID($ticket_line_item->OBJ_ID());
745
-                if ($ticket instanceof EE_Ticket) {
746
-                    if (self::debug) {
747
-                        echo self::$nl . ' . . ticket->ID(): ' . $ticket->ID();
748
-                        echo self::$nl . ' . . ticket_line_item->quantity(): ' . $ticket_line_item->quantity();
749
-                    }
750
-                    $ticket->add_extra_meta(
751
-                        EE_Ticket::META_KEY_TICKET_RESERVATIONS,
752
-                        __LINE__ . ') ' . __METHOD__ . '() SID = ' . $session->id()
753
-                    );
754
-                    $this->_release_reserved_ticket($ticket, $ticket_line_item->quantity());
755
-                }
756
-            }
757
-        }
758
-        if (self::debug) {
759
-            echo self::$nl . self::$nl . ' RESET COMPLETED ';
760
-        }
761
-    }
762
-
763
-
764
-
765
-    /********************************** SESSION_CHECKOUT_RESET  **********************************/
766
-
767
-
768
-    /**
769
-     * callback hooked into 'AHEE__EE_Session__reset_checkout__before_reset'
770
-     *
771
-     * @param EE_Session $session
772
-     * @return void
773
-     * @throws EE_Error
774
-     * @throws InvalidSessionDataException
775
-     */
776
-    public static function session_checkout_reset(EE_Session $session)
777
-    {
778
-        // don't release tickets if cart was already reset
779
-        if (did_action('AHEE__EE_Session__reset_cart__before_reset')) {
780
-            return;
781
-        }
782
-        $checkout = $session->checkout();
783
-        if ($checkout instanceof EE_Checkout) {
784
-            EED_Ticket_Sales_Monitor::instance()->_session_checkout_reset($checkout);
785
-        }
786
-    }
787
-
788
-
789
-    /**
790
-     * releases reserved tickets for the EE_Checkout->transaction
791
-     *
792
-     * @param EE_Checkout $checkout
793
-     * @return void
794
-     * @throws EE_Error
795
-     * @throws InvalidSessionDataException
796
-     */
797
-    protected function _session_checkout_reset(EE_Checkout $checkout)
798
-    {
799
-        if (self::debug) {
800
-            echo self::$nl . self::$nl . __LINE__ . ') ' . __METHOD__ . '() ';
801
-        }
802
-        // we want to release the each registration's reserved tickets if the session was cleared, but not if this is a revisit
803
-        if ($checkout->revisit || ! $checkout->transaction instanceof EE_Transaction) {
804
-            return;
805
-        }
806
-        $this->_release_all_reserved_tickets_for_transaction($checkout->transaction);
807
-    }
808
-
809
-
810
-
811
-    /********************************** SESSION_EXPIRED_RESET  **********************************/
812
-
813
-
814
-    /**
815
-     * @param    EE_Session $session
816
-     * @return    void
817
-     */
818
-    public static function session_expired_reset(EE_Session $session)
819
-    {
820
-    }
821
-
822
-
823
-
824
-    /********************************** PROCESS_ABANDONED_TRANSACTIONS  **********************************/
825
-
826
-
827
-    /**
828
-     * releases reserved tickets for all registrations of an ABANDONED EE_Transaction
829
-     * by default, will NOT release tickets for free transactions, or any that have received a payment
830
-     *
831
-     * @param EE_Transaction $transaction
832
-     * @return void
833
-     * @throws EE_Error
834
-     * @throws InvalidSessionDataException
835
-     */
836
-    public static function process_abandoned_transactions(EE_Transaction $transaction)
837
-    {
838
-        // is this TXN free or has any money been paid towards this TXN? If so, then leave it alone
839
-        if ($transaction->is_free() || $transaction->paid() > 0) {
840
-            if (self::debug) {
841
-                // DEBUG LOG
842
-                EEH_Debug_Tools::log(
843
-                    __CLASS__,
844
-                    __FUNCTION__,
845
-                    __LINE__,
846
-                    array($transaction),
847
-                    false,
848
-                    'EE_Transaction: ' . $transaction->ID()
849
-                );
850
-            }
851
-            return;
852
-        }
853
-        // have their been any successful payments made ?
854
-        $payments = $transaction->payments();
855
-        foreach ($payments as $payment) {
856
-            if ($payment instanceof EE_Payment && $payment->status() === EEM_Payment::status_id_approved) {
857
-                if (self::debug) {
858
-                    // DEBUG LOG
859
-                    EEH_Debug_Tools::log(
860
-                        __CLASS__,
861
-                        __FUNCTION__,
862
-                        __LINE__,
863
-                        array($payment),
864
-                        false,
865
-                        'EE_Transaction: ' . $transaction->ID()
866
-                    );
867
-                }
868
-                return;
869
-            }
870
-        }
871
-        // since you haven't even attempted to pay for your ticket...
872
-        EED_Ticket_Sales_Monitor::instance()->_release_all_reserved_tickets_for_transaction($transaction);
873
-    }
874
-
875
-
876
-
877
-    /********************************** PROCESS_FAILED_TRANSACTIONS  **********************************/
878
-
879
-
880
-    /**
881
-     * releases reserved tickets for absolutely ALL registrations of a FAILED EE_Transaction
882
-     *
883
-     * @param EE_Transaction $transaction
884
-     * @return void
885
-     * @throws EE_Error
886
-     * @throws InvalidSessionDataException
887
-     */
888
-    public static function process_failed_transactions(EE_Transaction $transaction)
889
-    {
890
-        // since you haven't even attempted to pay for your ticket...
891
-        EED_Ticket_Sales_Monitor::instance()->_release_all_reserved_tickets_for_transaction($transaction);
892
-    }
893
-
894
-
895
-
896
-    /********************************** RESET RESERVATION COUNTS  *********************************/
897
-
898
-
899
-    /**
900
-     * Resets all ticket and datetime reserved counts to zero
901
-     * Tickets that are currently associated with a Transaction that is in progress
902
-     *
903
-     * @throws EE_Error
904
-     * @throws DomainException
905
-     * @throws InvalidDataTypeException
906
-     * @throws InvalidInterfaceException
907
-     * @throws InvalidArgumentException
908
-     * @throws UnexpectedEntityException
909
-     */
910
-    public static function reset_reservation_counts()
911
-    {
912
-        /** @var EE_Line_Item[] $valid_reserved_tickets */
913
-        $valid_reserved_tickets = array();
914
-        /** @var EE_Transaction[] $transactions_not_in_progress */
915
-        $transactions_not_in_progress = EEM_Transaction::instance()->get_transactions_not_in_progress();
916
-        foreach ($transactions_not_in_progress as $transaction) {
917
-            // if this TXN has been fully completed, then skip it
918
-            if ($transaction->reg_step_completed('finalize_registration')) {
919
-                continue;
920
-            }
921
-            $total_line_item = $transaction->total_line_item();
922
-            // $transaction_in_progress->line
923
-            if (! $total_line_item instanceof EE_Line_Item) {
924
-                throw new DomainException(
925
-                    esc_html__(
926
-                        'Transaction does not have a valid Total Line Item associated with it.',
927
-                        'event_espresso'
928
-                    )
929
-                );
930
-            }
931
-            $valid_reserved_tickets += EED_Ticket_Sales_Monitor::get_ticket_line_items_for_grand_total(
932
-                $total_line_item
933
-            );
934
-        }
935
-        $total_line_items = EEM_Line_Item::instance()->get_total_line_items_for_active_carts();
936
-        foreach ($total_line_items as $total_line_item) {
937
-            $valid_reserved_tickets += EED_Ticket_Sales_Monitor::get_ticket_line_items_for_grand_total(
938
-                $total_line_item
939
-            );
940
-        }
941
-        $tickets_with_reservations = EEM_Ticket::instance()->get_tickets_with_reservations();
942
-        return EED_Ticket_Sales_Monitor::release_reservations_for_tickets(
943
-            $tickets_with_reservations,
944
-            $valid_reserved_tickets,
945
-            __FUNCTION__
946
-        );
947
-    }
948
-
949
-
950
-    /**
951
-     * @param EE_Line_Item $total_line_item
952
-     * @return EE_Line_Item[]
953
-     */
954
-    private static function get_ticket_line_items_for_grand_total(EE_Line_Item $total_line_item)
955
-    {
956
-        /** @var EE_Line_Item[] $valid_reserved_tickets */
957
-        $valid_reserved_tickets = array();
958
-        $ticket_line_items = EEH_Line_Item::get_ticket_line_items($total_line_item);
959
-        foreach ($ticket_line_items as $ticket_line_item) {
960
-            if ($ticket_line_item instanceof EE_Line_Item) {
961
-                $valid_reserved_tickets[] = $ticket_line_item;
962
-            }
963
-        }
964
-        return $valid_reserved_tickets;
965
-    }
966
-
967
-
968
-    /**
969
-     * @param EE_Ticket[]    $tickets_with_reservations
970
-     * @param EE_Line_Item[] $valid_reserved_ticket_line_items
971
-     * @return int
972
-     * @throws UnexpectedEntityException
973
-     * @throws DomainException
974
-     * @throws EE_Error
975
-     */
976
-    private static function release_reservations_for_tickets(
977
-        array $tickets_with_reservations,
978
-        array $valid_reserved_ticket_line_items = array(),
979
-        $source
980
-    ) {
981
-        if (self::debug) {
982
-            echo self::$nl . self::$nl . __LINE__ . ') ' . __METHOD__ . '()';
983
-        }
984
-        $total_tickets_released = 0;
985
-        $sold_out_events = array();
986
-        foreach ($tickets_with_reservations as $ticket_with_reservations) {
987
-            if (! $ticket_with_reservations instanceof EE_Ticket) {
988
-                continue;
989
-            }
990
-            $reserved_qty = $ticket_with_reservations->reserved();
991
-            if (self::debug) {
992
-                echo self::$nl . ' . $ticket_with_reservations->ID(): ' . $ticket_with_reservations->ID();
993
-                echo self::$nl . ' . $reserved_qty: ' . $reserved_qty;
994
-            }
995
-            foreach ($valid_reserved_ticket_line_items as $valid_reserved_ticket_line_item) {
996
-                if ($valid_reserved_ticket_line_item instanceof EE_Line_Item
997
-                    && $valid_reserved_ticket_line_item->OBJ_ID() === $ticket_with_reservations->ID()
998
-                ) {
999
-                    if (self::debug) {
1000
-                        echo self::$nl . ' . $valid_reserved_ticket_line_item->quantity(): '
1001
-                             . $valid_reserved_ticket_line_item->quantity();
1002
-                    }
1003
-                    $reserved_qty -= $valid_reserved_ticket_line_item->quantity();
1004
-                }
1005
-            }
1006
-            if ($reserved_qty > 0) {
1007
-                $ticket_with_reservations->add_extra_meta(
1008
-                    EE_Ticket::META_KEY_TICKET_RESERVATIONS,
1009
-                    __LINE__ . ') ' . $source . '()'
1010
-                );
1011
-                $ticket_with_reservations->decrease_reserved($reserved_qty, true, 'TicketSalesMonitor:' . __LINE__);
1012
-                $ticket_with_reservations->save();
1013
-                $total_tickets_released += $reserved_qty;
1014
-                $event = $ticket_with_reservations->get_related_event();
1015
-                // track sold out events
1016
-                if ($event instanceof EE_Event && $event->is_sold_out()) {
1017
-                    $sold_out_events[] = $event;
1018
-                }
1019
-            }
1020
-        }
1021
-        if (self::debug) {
1022
-            echo self::$nl . ' . $total_tickets_released: ' . $total_tickets_released;
1023
-        }
1024
-        // double check whether sold out events should remain sold out after releasing tickets
1025
-        if ($sold_out_events !== array()) {
1026
-            foreach ($sold_out_events as $sold_out_event) {
1027
-                /** @var EE_Event $sold_out_event */
1028
-                $sold_out_event->perform_sold_out_status_check();
1029
-            }
1030
-        }
1031
-        return $total_tickets_released;
1032
-    }
1033
-
1034
-
1035
-
1036
-    /********************************** SHUTDOWN  **********************************/
1037
-
1038
-
1039
-    /**
1040
-     * @param int $timestamp
1041
-     * @return false|int
1042
-     * @throws EE_Error
1043
-     * @throws InvalidArgumentException
1044
-     * @throws InvalidDataTypeException
1045
-     * @throws InvalidInterfaceException
1046
-     */
1047
-    public static function clear_expired_line_items_with_no_transaction($timestamp = 0)
1048
-    {
1049
-        /** @type WPDB $wpdb */
1050
-        global $wpdb;
1051
-        if (! absint($timestamp)) {
1052
-            /** @var EventEspresso\core\domain\values\session\SessionLifespan $session_lifespan */
1053
-            $session_lifespan = LoaderFactory::getLoader()->getShared(
1054
-                'EventEspresso\core\domain\values\session\SessionLifespan'
1055
-            );
1056
-            $timestamp = $session_lifespan->expiration();
1057
-        }
1058
-        return $wpdb->query(
1059
-            $wpdb->prepare(
1060
-                'DELETE FROM ' . EEM_Line_Item::instance()->table() . '
23
+	const debug = false;
24
+
25
+	private static $nl = '';
26
+
27
+	/**
28
+	 * an array of raw ticket data from EED_Ticket_Selector
29
+	 *
30
+	 * @var array $ticket_selections
31
+	 */
32
+	protected $ticket_selections = array();
33
+
34
+	/**
35
+	 * the raw ticket data from EED_Ticket_Selector is organized in rows
36
+	 * according to how they are displayed in the actual Ticket_Selector
37
+	 * this tracks the current row being processed
38
+	 *
39
+	 * @var int $current_row
40
+	 */
41
+	protected $current_row = 0;
42
+
43
+	/**
44
+	 * an array for tracking names of tickets that have sold out
45
+	 *
46
+	 * @var array $sold_out_tickets
47
+	 */
48
+	protected $sold_out_tickets = array();
49
+
50
+	/**
51
+	 * an array for tracking names of tickets that have had their quantities reduced
52
+	 *
53
+	 * @var array $decremented_tickets
54
+	 */
55
+	protected $decremented_tickets = array();
56
+
57
+
58
+	/**
59
+	 * set_hooks - for hooking into EE Core, other modules, etc
60
+	 *
61
+	 * @return    void
62
+	 */
63
+	public static function set_hooks()
64
+	{
65
+		self::$nl = defined('EE_TESTS_DIR') ? "\n" : '<br />';
66
+		// release tickets for expired carts
67
+		add_action(
68
+			'EED_Ticket_Selector__process_ticket_selections__before',
69
+			array('EED_Ticket_Sales_Monitor', 'release_tickets_for_expired_carts'),
70
+			1
71
+		);
72
+		// check ticket reserves AFTER MER does it's check (hence priority 20)
73
+		add_filter(
74
+			'FHEE__EE_Ticket_Selector___add_ticket_to_cart__ticket_qty',
75
+			array('EED_Ticket_Sales_Monitor', 'validate_ticket_sale'),
76
+			20,
77
+			3
78
+		);
79
+		// add notices for sold out tickets
80
+		add_action(
81
+			'AHEE__EE_Ticket_Selector__process_ticket_selections__after_tickets_added_to_cart',
82
+			array('EED_Ticket_Sales_Monitor', 'post_notices'),
83
+			10
84
+		);
85
+
86
+		// handle tickets deleted from cart
87
+		add_action(
88
+			'FHEE__EED_Multi_Event_Registration__delete_ticket__ticket_removed_from_cart',
89
+			array('EED_Ticket_Sales_Monitor', 'ticket_removed_from_cart'),
90
+			10,
91
+			2
92
+		);
93
+		// handle emptied carts
94
+		add_action(
95
+			'AHEE__EE_Session__reset_cart__before_reset',
96
+			array('EED_Ticket_Sales_Monitor', 'session_cart_reset'),
97
+			10,
98
+			1
99
+		);
100
+		add_action(
101
+			'AHEE__EED_Multi_Event_Registration__empty_event_cart__before_delete_cart',
102
+			array('EED_Ticket_Sales_Monitor', 'session_cart_reset'),
103
+			10,
104
+			1
105
+		);
106
+		// handle cancelled registrations
107
+		add_action(
108
+			'AHEE__EE_Session__reset_checkout__before_reset',
109
+			array('EED_Ticket_Sales_Monitor', 'session_checkout_reset'),
110
+			10,
111
+			1
112
+		);
113
+		// cron tasks
114
+		add_action(
115
+			'AHEE__EE_Cron_Tasks__process_expired_transactions__abandoned_transaction',
116
+			array('EED_Ticket_Sales_Monitor', 'process_abandoned_transactions'),
117
+			10,
118
+			1
119
+		);
120
+		add_action(
121
+			'AHEE__EE_Cron_Tasks__process_expired_transactions__incomplete_transaction',
122
+			array('EED_Ticket_Sales_Monitor', 'process_abandoned_transactions'),
123
+			10,
124
+			1
125
+		);
126
+		add_action(
127
+			'AHEE__EE_Cron_Tasks__process_expired_transactions__failed_transaction',
128
+			array('EED_Ticket_Sales_Monitor', 'process_failed_transactions'),
129
+			10,
130
+			1
131
+		);
132
+	}
133
+
134
+
135
+	/**
136
+	 * set_hooks_admin - for hooking into EE Admin Core, other modules, etc
137
+	 *
138
+	 * @return void
139
+	 */
140
+	public static function set_hooks_admin()
141
+	{
142
+		EED_Ticket_Sales_Monitor::set_hooks();
143
+	}
144
+
145
+
146
+	/**
147
+	 * @return EED_Ticket_Sales_Monitor|EED_Module
148
+	 */
149
+	public static function instance()
150
+	{
151
+		return parent::get_instance(__CLASS__);
152
+	}
153
+
154
+
155
+	/**
156
+	 * @param WP_Query $WP_Query
157
+	 * @return    void
158
+	 */
159
+	public function run($WP_Query)
160
+	{
161
+	}
162
+
163
+
164
+
165
+	/********************************** PRE_TICKET_SALES  **********************************/
166
+
167
+
168
+	/**
169
+	 * Retrieves grand totals from the line items that have no TXN ID
170
+	 * and timestamps less than the current time minus the session lifespan.
171
+	 * These are carts that have been abandoned before the "registrant" even attempted to checkout.
172
+	 * We're going to release the tickets for these line items before attempting to add more to the cart.
173
+	 *
174
+	 * @return void
175
+	 * @throws DomainException
176
+	 * @throws EE_Error
177
+	 * @throws InvalidArgumentException
178
+	 * @throws InvalidDataTypeException
179
+	 * @throws InvalidInterfaceException
180
+	 * @throws UnexpectedEntityException
181
+	 */
182
+	public static function release_tickets_for_expired_carts()
183
+	{
184
+		if (self::debug) {
185
+			echo self::$nl . self::$nl . __LINE__ . ') ' . __METHOD__ . '()';
186
+		}
187
+		do_action('AHEE__EED_Ticket_Sales_Monitor__release_tickets_for_expired_carts__begin');
188
+		$expired_ticket_IDs = array();
189
+		/** @var EventEspresso\core\domain\values\session\SessionLifespan $session_lifespan */
190
+		$session_lifespan = LoaderFactory::getLoader()->getShared(
191
+			'EventEspresso\core\domain\values\session\SessionLifespan'
192
+		);
193
+		$timestamp = $session_lifespan->expiration();
194
+		$expired_ticket_line_items = EEM_Line_Item::instance()->getTicketLineItemsForExpiredCarts($timestamp);
195
+		if (self::debug) {
196
+			echo self::$nl . ' . time(): ' . time();
197
+			echo self::$nl . ' . time() as date: ' . date('Y-m-d H:i a');
198
+			echo self::$nl . ' . session expiration: ' . $session_lifespan->expiration();
199
+			echo self::$nl . ' . session expiration as date: ' . date('Y-m-d H:i a', $session_lifespan->expiration());
200
+			echo self::$nl . ' . timestamp: ' . $timestamp;
201
+			echo self::$nl . ' . $expired_ticket_line_items: ' . count($expired_ticket_line_items);
202
+		}
203
+		if (! empty($expired_ticket_line_items)) {
204
+			foreach ($expired_ticket_line_items as $expired_ticket_line_item) {
205
+				if (! $expired_ticket_line_item instanceof EE_Line_Item) {
206
+					continue;
207
+				}
208
+				$expired_ticket_IDs[ $expired_ticket_line_item->OBJ_ID() ] = $expired_ticket_line_item->OBJ_ID();
209
+				if (self::debug) {
210
+					echo self::$nl . ' . $expired_ticket_line_item->OBJ_ID(): ' . $expired_ticket_line_item->OBJ_ID();
211
+					echo self::$nl . ' . $expired_ticket_line_item->timestamp(): '
212
+						 . date(
213
+							 'Y-m-d h:i a',
214
+							 $expired_ticket_line_item->timestamp(true)
215
+						 );
216
+				}
217
+			}
218
+			if (! empty($expired_ticket_IDs)) {
219
+				EED_Ticket_Sales_Monitor::release_reservations_for_tickets(
220
+					\EEM_Ticket::instance()->get_tickets_with_IDs($expired_ticket_IDs),
221
+					array(),
222
+					__FUNCTION__
223
+				);
224
+				// now  let's get rid of expired line items so that they can't interfere with tracking
225
+				EED_Ticket_Sales_Monitor::clear_expired_line_items_with_no_transaction($timestamp);
226
+			}
227
+		}
228
+		do_action(
229
+			'AHEE__EED_Ticket_Sales_Monitor__release_tickets_for_expired_carts__end',
230
+			$expired_ticket_IDs,
231
+			$expired_ticket_line_items
232
+		);
233
+	}
234
+
235
+
236
+
237
+	/********************************** VALIDATE_TICKET_SALE  **********************************/
238
+
239
+
240
+	/**
241
+	 * callback for 'FHEE__EED_Ticket_Selector__process_ticket_selections__valid_post_data'
242
+	 *
243
+	 * @param int       $qty
244
+	 * @param EE_Ticket $ticket
245
+	 * @return bool
246
+	 * @throws UnexpectedEntityException
247
+	 * @throws EE_Error
248
+	 */
249
+	public static function validate_ticket_sale($qty = 1, EE_Ticket $ticket)
250
+	{
251
+		$qty = absint($qty);
252
+		if ($qty > 0) {
253
+			$qty = EED_Ticket_Sales_Monitor::instance()->_validate_ticket_sale($ticket, $qty);
254
+		}
255
+		if (self::debug) {
256
+			echo self::$nl . self::$nl . __LINE__ . ') ' . __METHOD__ . '()';
257
+			echo self::$nl . self::$nl . '<b> RETURNED QTY: ' . $qty . '</b>';
258
+		}
259
+		return $qty;
260
+	}
261
+
262
+
263
+	/**
264
+	 * checks whether an individual ticket is available for purchase based on datetime, and ticket details
265
+	 *
266
+	 * @param   EE_Ticket $ticket
267
+	 * @param int         $qty
268
+	 * @return int
269
+	 * @throws UnexpectedEntityException
270
+	 * @throws EE_Error
271
+	 */
272
+	protected function _validate_ticket_sale(EE_Ticket $ticket, $qty = 1)
273
+	{
274
+		if (self::debug) {
275
+			echo self::$nl . self::$nl . __LINE__ . ') ' . __METHOD__ . '() ';
276
+		}
277
+		if (! $ticket instanceof EE_Ticket) {
278
+			return 0;
279
+		}
280
+		if (self::debug) {
281
+			echo self::$nl . '<b> . ticket->ID: ' . $ticket->ID() . '</b>';
282
+			echo self::$nl . ' . original ticket->reserved: ' . $ticket->reserved();
283
+		}
284
+		$ticket->refresh_from_db();
285
+		// first let's determine the ticket availability based on sales
286
+		$available = $ticket->qty('saleable');
287
+		if (self::debug) {
288
+			echo self::$nl . ' . . . ticket->qty: ' . $ticket->qty();
289
+			echo self::$nl . ' . . . ticket->sold: ' . $ticket->sold();
290
+			echo self::$nl . ' . . . ticket->reserved: ' . $ticket->reserved();
291
+			echo self::$nl . ' . . . ticket->qty(saleable): ' . $ticket->qty('saleable');
292
+			echo self::$nl . ' . . . available: ' . $available;
293
+		}
294
+		if ($available < 1) {
295
+			$this->_ticket_sold_out($ticket);
296
+			return 0;
297
+		}
298
+		if (self::debug) {
299
+			echo self::$nl . ' . . . qty: ' . $qty;
300
+		}
301
+		if ($available < $qty) {
302
+			$qty = $available;
303
+			if (self::debug) {
304
+				echo self::$nl . ' . . . QTY ADJUSTED: ' . $qty;
305
+			}
306
+			$this->_ticket_quantity_decremented($ticket);
307
+		}
308
+		$this->_reserve_ticket($ticket, $qty);
309
+		return $qty;
310
+	}
311
+
312
+
313
+	/**
314
+	 * increments ticket reserved based on quantity passed
315
+	 *
316
+	 * @param    EE_Ticket $ticket
317
+	 * @param int          $quantity
318
+	 * @return bool
319
+	 * @throws EE_Error
320
+	 */
321
+	protected function _reserve_ticket(EE_Ticket $ticket, $quantity = 1)
322
+	{
323
+		if (self::debug) {
324
+			echo self::$nl . self::$nl . ' . . . INCREASE RESERVED: ' . $quantity;
325
+		}
326
+		$ticket->increase_reserved($quantity, 'TicketSalesMonitor:' . __LINE__);
327
+		return $ticket->save();
328
+	}
329
+
330
+
331
+	/**
332
+	 * @param  EE_Ticket $ticket
333
+	 * @param  int       $quantity
334
+	 * @return bool
335
+	 * @throws EE_Error
336
+	 */
337
+	protected function _release_reserved_ticket(EE_Ticket $ticket, $quantity = 1)
338
+	{
339
+		if (self::debug) {
340
+			echo self::$nl . ' . . . ticket->ID: ' . $ticket->ID();
341
+			echo self::$nl . ' . . . ticket->reserved before: ' . $ticket->reserved();
342
+		}
343
+		$ticket->decrease_reserved($quantity, true, 'TicketSalesMonitor:' . __LINE__);
344
+		if (self::debug) {
345
+			echo self::$nl . ' . . . ticket->reserved after: ' . $ticket->reserved();
346
+		}
347
+		return $ticket->save() ? 1 : 0;
348
+	}
349
+
350
+
351
+	/**
352
+	 * removes quantities within the ticket selector based on zero ticket availability
353
+	 *
354
+	 * @param    EE_Ticket $ticket
355
+	 * @return    void
356
+	 * @throws UnexpectedEntityException
357
+	 * @throws EE_Error
358
+	 */
359
+	protected function _ticket_sold_out(EE_Ticket $ticket)
360
+	{
361
+		if (self::debug) {
362
+			echo self::$nl . self::$nl . __LINE__ . ') ' . __METHOD__ . '() ';
363
+			echo self::$nl . ' . . ticket->name: ' . $this->_get_ticket_and_event_name($ticket);
364
+		}
365
+		$this->sold_out_tickets[] = $this->_get_ticket_and_event_name($ticket);
366
+	}
367
+
368
+
369
+	/**
370
+	 * adjusts quantities within the ticket selector based on decreased ticket availability
371
+	 *
372
+	 * @param    EE_Ticket $ticket
373
+	 * @return void
374
+	 * @throws UnexpectedEntityException
375
+	 * @throws EE_Error
376
+	 */
377
+	protected function _ticket_quantity_decremented(EE_Ticket $ticket)
378
+	{
379
+		if (self::debug) {
380
+			echo self::$nl . self::$nl . __LINE__ . ') ' . __METHOD__ . '() ';
381
+			echo self::$nl . ' . . ticket->name: ' . $this->_get_ticket_and_event_name($ticket);
382
+		}
383
+		$this->decremented_tickets[] = $this->_get_ticket_and_event_name($ticket);
384
+	}
385
+
386
+
387
+	/**
388
+	 * builds string out of ticket and event name
389
+	 *
390
+	 * @param    EE_Ticket $ticket
391
+	 * @return string
392
+	 * @throws UnexpectedEntityException
393
+	 * @throws EE_Error
394
+	 */
395
+	protected function _get_ticket_and_event_name(EE_Ticket $ticket)
396
+	{
397
+		$event = $ticket->get_related_event();
398
+		if ($event instanceof EE_Event) {
399
+			$ticket_name = sprintf(
400
+				_x('%1$s for %2$s', 'ticket name for event name', 'event_espresso'),
401
+				$ticket->name(),
402
+				$event->name()
403
+			);
404
+		} else {
405
+			$ticket_name = $ticket->name();
406
+		}
407
+		return $ticket_name;
408
+	}
409
+
410
+
411
+
412
+	/********************************** EVENT CART  **********************************/
413
+
414
+
415
+	/**
416
+	 * releases or reserves ticket(s) based on quantity passed
417
+	 *
418
+	 * @param  EE_Line_Item $line_item
419
+	 * @param  int          $quantity
420
+	 * @return void
421
+	 * @throws EE_Error
422
+	 * @throws InvalidArgumentException
423
+	 * @throws InvalidDataTypeException
424
+	 * @throws InvalidInterfaceException
425
+	 */
426
+	public static function ticket_quantity_updated(EE_Line_Item $line_item, $quantity = 1)
427
+	{
428
+		$ticket = EEM_Ticket::instance()->get_one_by_ID(absint($line_item->OBJ_ID()));
429
+		if ($ticket instanceof EE_Ticket) {
430
+			$ticket->add_extra_meta(
431
+				EE_Ticket::META_KEY_TICKET_RESERVATIONS,
432
+				__LINE__ . ') ' . __METHOD__ . '()'
433
+			);
434
+			if ($quantity > 0) {
435
+				EED_Ticket_Sales_Monitor::instance()->_reserve_ticket($ticket, $quantity);
436
+			} else {
437
+				EED_Ticket_Sales_Monitor::instance()->_release_reserved_ticket($ticket, $quantity);
438
+			}
439
+		}
440
+	}
441
+
442
+
443
+	/**
444
+	 * releases reserved ticket(s) based on quantity passed
445
+	 *
446
+	 * @param  EE_Ticket $ticket
447
+	 * @param  int       $quantity
448
+	 * @return void
449
+	 * @throws EE_Error
450
+	 */
451
+	public static function ticket_removed_from_cart(EE_Ticket $ticket, $quantity = 1)
452
+	{
453
+		$ticket->add_extra_meta(
454
+			EE_Ticket::META_KEY_TICKET_RESERVATIONS,
455
+			__LINE__ . ') ' . __METHOD__ . '()'
456
+		);
457
+		EED_Ticket_Sales_Monitor::instance()->_release_reserved_ticket($ticket, $quantity);
458
+	}
459
+
460
+
461
+
462
+	/********************************** POST_NOTICES  **********************************/
463
+
464
+
465
+	/**
466
+	 * @return void
467
+	 * @throws EE_Error
468
+	 * @throws InvalidArgumentException
469
+	 * @throws ReflectionException
470
+	 * @throws InvalidDataTypeException
471
+	 * @throws InvalidInterfaceException
472
+	 */
473
+	public static function post_notices()
474
+	{
475
+		EED_Ticket_Sales_Monitor::instance()->_post_notices();
476
+	}
477
+
478
+
479
+	/**
480
+	 * @return void
481
+	 * @throws EE_Error
482
+	 * @throws InvalidArgumentException
483
+	 * @throws ReflectionException
484
+	 * @throws InvalidDataTypeException
485
+	 * @throws InvalidInterfaceException
486
+	 */
487
+	protected function _post_notices()
488
+	{
489
+		if (self::debug) {
490
+			echo self::$nl . self::$nl . __LINE__ . ') ' . __METHOD__ . '() ';
491
+		}
492
+		$refresh_msg = '';
493
+		$none_added_msg = '';
494
+		if (defined('DOING_AJAX') && DOING_AJAX) {
495
+			$refresh_msg = __(
496
+				'Please refresh the page to view updated ticket quantities.',
497
+				'event_espresso'
498
+			);
499
+			$none_added_msg = __('No tickets were added for the event.', 'event_espresso');
500
+		}
501
+		if (! empty($this->sold_out_tickets)) {
502
+			EE_Error::add_attention(
503
+				sprintf(
504
+					apply_filters(
505
+						'FHEE__EED_Ticket_Sales_Monitor___post_notices__sold_out_tickets_notice',
506
+						__(
507
+							'We\'re sorry...%1$sThe following items have sold out since you first viewed this page, and can no longer be registered for:%1$s%1$s%2$s%1$s%1$sPlease note that availability can change at any time due to cancellations, so please check back again later if registration for this event(s) is important to you.%1$s%1$s%3$s%1$s%4$s%1$s',
508
+							'event_espresso'
509
+						)
510
+					),
511
+					'<br />',
512
+					implode('<br />', $this->sold_out_tickets),
513
+					$none_added_msg,
514
+					$refresh_msg
515
+				)
516
+			);
517
+			// alter code flow in the Ticket Selector for better UX
518
+			add_filter('FHEE__EED_Ticket_Selector__process_ticket_selections__tckts_slctd', '__return_true');
519
+			add_filter('FHEE__EED_Ticket_Selector__process_ticket_selections__success', '__return_false');
520
+			$this->sold_out_tickets = array();
521
+			// and reset the cart
522
+			EED_Ticket_Sales_Monitor::session_cart_reset(EE_Registry::instance()->SSN);
523
+		}
524
+		if (! empty($this->decremented_tickets)) {
525
+			EE_Error::add_attention(
526
+				sprintf(
527
+					apply_filters(
528
+						'FHEE__EED_Ticket_Sales_Monitor___ticket_quantity_decremented__notice',
529
+						__(
530
+							'We\'re sorry...%1$sDue to sales that have occurred since you first viewed the last page, the following items have had their quantities adjusted to match the current available amount:%1$s%1$s%2$s%1$s%1$sPlease note that availability can change at any time due to cancellations, so please check back again later if registration for this event(s) is important to you.%1$s%1$s%3$s%1$s%4$s%1$s',
531
+							'event_espresso'
532
+						)
533
+					),
534
+					'<br />',
535
+					implode('<br />', $this->decremented_tickets),
536
+					$none_added_msg,
537
+					$refresh_msg
538
+				)
539
+			);
540
+			$this->decremented_tickets = array();
541
+		}
542
+	}
543
+
544
+
545
+
546
+	/********************************** RELEASE_ALL_RESERVED_TICKETS_FOR_TRANSACTION  **********************************/
547
+
548
+
549
+	/**
550
+	 * releases reserved tickets for all registrations of an EE_Transaction
551
+	 * by default, will NOT release tickets for finalized transactions
552
+	 *
553
+	 * @param    EE_Transaction $transaction
554
+	 * @return int
555
+	 * @throws EE_Error
556
+	 * @throws InvalidSessionDataException
557
+	 */
558
+	protected function _release_all_reserved_tickets_for_transaction(EE_Transaction $transaction)
559
+	{
560
+		if (self::debug) {
561
+			echo self::$nl . self::$nl . __LINE__ . ') ' . __METHOD__ . '() ';
562
+			echo self::$nl . ' . transaction->ID: ' . $transaction->ID();
563
+			echo self::$nl . ' . TXN status_ID: ' . $transaction->status_ID();
564
+		}
565
+		// check if 'finalize_registration' step has been completed...
566
+		$finalized = $transaction->reg_step_completed('finalize_registration');
567
+		if (self::debug) {
568
+			// DEBUG LOG
569
+			EEH_Debug_Tools::log(
570
+				__CLASS__,
571
+				__FUNCTION__,
572
+				__LINE__,
573
+				array('finalized' => $finalized),
574
+				false,
575
+				'EE_Transaction: ' . $transaction->ID()
576
+			);
577
+		}
578
+		// how many tickets were released
579
+		$count = 0;
580
+		if (self::debug) {
581
+			echo self::$nl . ' . . . TXN finalized: ' . $finalized;
582
+		}
583
+		$release_tickets_with_TXN_status = array(
584
+			EEM_Transaction::failed_status_code,
585
+			EEM_Transaction::abandoned_status_code,
586
+			EEM_Transaction::incomplete_status_code,
587
+		);
588
+		$events = array();
589
+		// if the session is getting cleared BEFORE the TXN has been finalized or the transaction is not completed
590
+		if (! $finalized || in_array($transaction->status_ID(), $release_tickets_with_TXN_status, true)) {
591
+			// cancel any reserved tickets for registrations that were not approved
592
+			$registrations = $transaction->registrations();
593
+			if (self::debug) {
594
+				echo self::$nl . ' . . . # registrations: ' . count($registrations);
595
+				$reg = reset($registrations);
596
+				$ticket = $reg->ticket();
597
+				if ($ticket instanceof EE_Ticket) {
598
+					$ticket->add_extra_meta(
599
+						EE_Ticket::META_KEY_TICKET_RESERVATIONS,
600
+						__LINE__ . ') Release All Tickets TXN:' . $transaction->ID()
601
+					);
602
+				}
603
+			}
604
+			if (! empty($registrations)) {
605
+				foreach ($registrations as $registration) {
606
+					if ($registration instanceof EE_Registration
607
+						&& $this->_release_reserved_ticket_for_registration($registration, $transaction)
608
+					) {
609
+						$count++;
610
+						$events[ $registration->event_ID() ] = $registration->event();
611
+					}
612
+				}
613
+			}
614
+		}
615
+		if ($events !== array()) {
616
+			foreach ($events as $event) {
617
+				/** @var EE_Event $event */
618
+				$event->perform_sold_out_status_check();
619
+			}
620
+		}
621
+		return $count;
622
+	}
623
+
624
+
625
+	/**
626
+	 * releases reserved tickets for an EE_Registration
627
+	 * by default, will NOT release tickets for APPROVED registrations
628
+	 *
629
+	 * @param EE_Registration $registration
630
+	 * @param EE_Transaction  $transaction
631
+	 * @return int
632
+	 * @throws EE_Error
633
+	 */
634
+	protected function _release_reserved_ticket_for_registration(
635
+		EE_Registration $registration,
636
+		EE_Transaction $transaction
637
+	) {
638
+		$STS_ID = $transaction->status_ID();
639
+		if (self::debug) {
640
+			echo self::$nl . self::$nl . __LINE__ . ') ' . __METHOD__ . '() ';
641
+			echo self::$nl . ' . . registration->ID: ' . $registration->ID();
642
+			echo self::$nl . ' . . registration->status_ID: ' . $registration->status_ID();
643
+			echo self::$nl . ' . . transaction->status_ID(): ' . $STS_ID;
644
+		}
645
+		if (// release Tickets for Failed Transactions and Abandoned Transactions
646
+			$STS_ID === EEM_Transaction::failed_status_code
647
+			|| $STS_ID === EEM_Transaction::abandoned_status_code
648
+			|| (
649
+				// also release Tickets for Incomplete Transactions, but ONLY if the Registrations are NOT Approved
650
+				$STS_ID === EEM_Transaction::incomplete_status_code
651
+				&& $registration->status_ID() !== EEM_Registration::status_id_approved
652
+			)
653
+		) {
654
+			if (self::debug) {
655
+				echo self::$nl . self::$nl . ' . . RELEASE RESERVED TICKET';
656
+				$rsrvd = $registration->get_extra_meta(EE_Registration::HAS_RESERVED_TICKET_KEY, true);
657
+				echo self::$nl . ' . . . registration HAS_RESERVED_TICKET_KEY: ';
658
+				var_dump($rsrvd);
659
+			}
660
+			$registration->release_reserved_ticket(true, 'TicketSalesMonitor:' . __LINE__);
661
+			return 1;
662
+		}
663
+		return 0;
664
+	}
665
+
666
+
667
+
668
+	/********************************** SESSION_CART_RESET  **********************************/
669
+
670
+
671
+	/**
672
+	 * callback hooked into 'AHEE__EE_Session__reset_cart__before_reset'
673
+	 *
674
+	 * @param EE_Session $session
675
+	 * @return void
676
+	 * @throws EE_Error
677
+	 * @throws InvalidArgumentException
678
+	 * @throws ReflectionException
679
+	 * @throws InvalidDataTypeException
680
+	 * @throws InvalidInterfaceException
681
+	 */
682
+	public static function session_cart_reset(EE_Session $session)
683
+	{
684
+		// don't release tickets if checkout was already reset
685
+		if (did_action('AHEE__EE_Session__reset_checkout__before_reset')) {
686
+			return;
687
+		}
688
+		if (self::debug) {
689
+			echo self::$nl . self::$nl . __LINE__ . ') ' . __METHOD__ . '() ';
690
+		}
691
+		// first check of the session has a valid Checkout object
692
+		$checkout = $session->checkout();
693
+		if ($checkout instanceof EE_Checkout) {
694
+			// and use that to clear ticket reservations because it will update the associated registration meta data
695
+			EED_Ticket_Sales_Monitor::instance()->_session_checkout_reset($checkout);
696
+			return;
697
+		}
698
+		$cart = $session->cart();
699
+		if ($cart instanceof EE_Cart) {
700
+			if (self::debug) {
701
+				echo self::$nl . self::$nl . ' cart instance of EE_Cart: ';
702
+			}
703
+			EED_Ticket_Sales_Monitor::instance()->_session_cart_reset($cart, $session);
704
+		} else {
705
+			if (self::debug) {
706
+				echo self::$nl . self::$nl . ' invalid EE_Cart: ';
707
+				var_export($cart, true);
708
+			}
709
+		}
710
+	}
711
+
712
+
713
+	/**
714
+	 * releases reserved tickets in the EE_Cart
715
+	 *
716
+	 * @param EE_Cart $cart
717
+	 * @return void
718
+	 * @throws EE_Error
719
+	 * @throws InvalidArgumentException
720
+	 * @throws ReflectionException
721
+	 * @throws InvalidDataTypeException
722
+	 * @throws InvalidInterfaceException
723
+	 */
724
+	protected function _session_cart_reset(EE_Cart $cart, EE_Session $session)
725
+	{
726
+		if (self::debug) {
727
+			echo self::$nl . self::$nl . __LINE__ . ') ' . __METHOD__ . '() ';
728
+		}
729
+		$ticket_line_items = $cart->get_tickets();
730
+		if (empty($ticket_line_items)) {
731
+			return;
732
+		}
733
+		if (self::debug) {
734
+			echo '<br /> . ticket_line_item count: ' . count($ticket_line_items);
735
+		}
736
+		foreach ($ticket_line_items as $ticket_line_item) {
737
+			if (self::debug) {
738
+				echo self::$nl . ' . ticket_line_item->ID(): ' . $ticket_line_item->ID();
739
+			}
740
+			if ($ticket_line_item instanceof EE_Line_Item && $ticket_line_item->OBJ_type() === 'Ticket') {
741
+				if (self::debug) {
742
+					echo self::$nl . ' . . ticket_line_item->OBJ_ID(): ' . $ticket_line_item->OBJ_ID();
743
+				}
744
+				$ticket = EEM_Ticket::instance()->get_one_by_ID($ticket_line_item->OBJ_ID());
745
+				if ($ticket instanceof EE_Ticket) {
746
+					if (self::debug) {
747
+						echo self::$nl . ' . . ticket->ID(): ' . $ticket->ID();
748
+						echo self::$nl . ' . . ticket_line_item->quantity(): ' . $ticket_line_item->quantity();
749
+					}
750
+					$ticket->add_extra_meta(
751
+						EE_Ticket::META_KEY_TICKET_RESERVATIONS,
752
+						__LINE__ . ') ' . __METHOD__ . '() SID = ' . $session->id()
753
+					);
754
+					$this->_release_reserved_ticket($ticket, $ticket_line_item->quantity());
755
+				}
756
+			}
757
+		}
758
+		if (self::debug) {
759
+			echo self::$nl . self::$nl . ' RESET COMPLETED ';
760
+		}
761
+	}
762
+
763
+
764
+
765
+	/********************************** SESSION_CHECKOUT_RESET  **********************************/
766
+
767
+
768
+	/**
769
+	 * callback hooked into 'AHEE__EE_Session__reset_checkout__before_reset'
770
+	 *
771
+	 * @param EE_Session $session
772
+	 * @return void
773
+	 * @throws EE_Error
774
+	 * @throws InvalidSessionDataException
775
+	 */
776
+	public static function session_checkout_reset(EE_Session $session)
777
+	{
778
+		// don't release tickets if cart was already reset
779
+		if (did_action('AHEE__EE_Session__reset_cart__before_reset')) {
780
+			return;
781
+		}
782
+		$checkout = $session->checkout();
783
+		if ($checkout instanceof EE_Checkout) {
784
+			EED_Ticket_Sales_Monitor::instance()->_session_checkout_reset($checkout);
785
+		}
786
+	}
787
+
788
+
789
+	/**
790
+	 * releases reserved tickets for the EE_Checkout->transaction
791
+	 *
792
+	 * @param EE_Checkout $checkout
793
+	 * @return void
794
+	 * @throws EE_Error
795
+	 * @throws InvalidSessionDataException
796
+	 */
797
+	protected function _session_checkout_reset(EE_Checkout $checkout)
798
+	{
799
+		if (self::debug) {
800
+			echo self::$nl . self::$nl . __LINE__ . ') ' . __METHOD__ . '() ';
801
+		}
802
+		// we want to release the each registration's reserved tickets if the session was cleared, but not if this is a revisit
803
+		if ($checkout->revisit || ! $checkout->transaction instanceof EE_Transaction) {
804
+			return;
805
+		}
806
+		$this->_release_all_reserved_tickets_for_transaction($checkout->transaction);
807
+	}
808
+
809
+
810
+
811
+	/********************************** SESSION_EXPIRED_RESET  **********************************/
812
+
813
+
814
+	/**
815
+	 * @param    EE_Session $session
816
+	 * @return    void
817
+	 */
818
+	public static function session_expired_reset(EE_Session $session)
819
+	{
820
+	}
821
+
822
+
823
+
824
+	/********************************** PROCESS_ABANDONED_TRANSACTIONS  **********************************/
825
+
826
+
827
+	/**
828
+	 * releases reserved tickets for all registrations of an ABANDONED EE_Transaction
829
+	 * by default, will NOT release tickets for free transactions, or any that have received a payment
830
+	 *
831
+	 * @param EE_Transaction $transaction
832
+	 * @return void
833
+	 * @throws EE_Error
834
+	 * @throws InvalidSessionDataException
835
+	 */
836
+	public static function process_abandoned_transactions(EE_Transaction $transaction)
837
+	{
838
+		// is this TXN free or has any money been paid towards this TXN? If so, then leave it alone
839
+		if ($transaction->is_free() || $transaction->paid() > 0) {
840
+			if (self::debug) {
841
+				// DEBUG LOG
842
+				EEH_Debug_Tools::log(
843
+					__CLASS__,
844
+					__FUNCTION__,
845
+					__LINE__,
846
+					array($transaction),
847
+					false,
848
+					'EE_Transaction: ' . $transaction->ID()
849
+				);
850
+			}
851
+			return;
852
+		}
853
+		// have their been any successful payments made ?
854
+		$payments = $transaction->payments();
855
+		foreach ($payments as $payment) {
856
+			if ($payment instanceof EE_Payment && $payment->status() === EEM_Payment::status_id_approved) {
857
+				if (self::debug) {
858
+					// DEBUG LOG
859
+					EEH_Debug_Tools::log(
860
+						__CLASS__,
861
+						__FUNCTION__,
862
+						__LINE__,
863
+						array($payment),
864
+						false,
865
+						'EE_Transaction: ' . $transaction->ID()
866
+					);
867
+				}
868
+				return;
869
+			}
870
+		}
871
+		// since you haven't even attempted to pay for your ticket...
872
+		EED_Ticket_Sales_Monitor::instance()->_release_all_reserved_tickets_for_transaction($transaction);
873
+	}
874
+
875
+
876
+
877
+	/********************************** PROCESS_FAILED_TRANSACTIONS  **********************************/
878
+
879
+
880
+	/**
881
+	 * releases reserved tickets for absolutely ALL registrations of a FAILED EE_Transaction
882
+	 *
883
+	 * @param EE_Transaction $transaction
884
+	 * @return void
885
+	 * @throws EE_Error
886
+	 * @throws InvalidSessionDataException
887
+	 */
888
+	public static function process_failed_transactions(EE_Transaction $transaction)
889
+	{
890
+		// since you haven't even attempted to pay for your ticket...
891
+		EED_Ticket_Sales_Monitor::instance()->_release_all_reserved_tickets_for_transaction($transaction);
892
+	}
893
+
894
+
895
+
896
+	/********************************** RESET RESERVATION COUNTS  *********************************/
897
+
898
+
899
+	/**
900
+	 * Resets all ticket and datetime reserved counts to zero
901
+	 * Tickets that are currently associated with a Transaction that is in progress
902
+	 *
903
+	 * @throws EE_Error
904
+	 * @throws DomainException
905
+	 * @throws InvalidDataTypeException
906
+	 * @throws InvalidInterfaceException
907
+	 * @throws InvalidArgumentException
908
+	 * @throws UnexpectedEntityException
909
+	 */
910
+	public static function reset_reservation_counts()
911
+	{
912
+		/** @var EE_Line_Item[] $valid_reserved_tickets */
913
+		$valid_reserved_tickets = array();
914
+		/** @var EE_Transaction[] $transactions_not_in_progress */
915
+		$transactions_not_in_progress = EEM_Transaction::instance()->get_transactions_not_in_progress();
916
+		foreach ($transactions_not_in_progress as $transaction) {
917
+			// if this TXN has been fully completed, then skip it
918
+			if ($transaction->reg_step_completed('finalize_registration')) {
919
+				continue;
920
+			}
921
+			$total_line_item = $transaction->total_line_item();
922
+			// $transaction_in_progress->line
923
+			if (! $total_line_item instanceof EE_Line_Item) {
924
+				throw new DomainException(
925
+					esc_html__(
926
+						'Transaction does not have a valid Total Line Item associated with it.',
927
+						'event_espresso'
928
+					)
929
+				);
930
+			}
931
+			$valid_reserved_tickets += EED_Ticket_Sales_Monitor::get_ticket_line_items_for_grand_total(
932
+				$total_line_item
933
+			);
934
+		}
935
+		$total_line_items = EEM_Line_Item::instance()->get_total_line_items_for_active_carts();
936
+		foreach ($total_line_items as $total_line_item) {
937
+			$valid_reserved_tickets += EED_Ticket_Sales_Monitor::get_ticket_line_items_for_grand_total(
938
+				$total_line_item
939
+			);
940
+		}
941
+		$tickets_with_reservations = EEM_Ticket::instance()->get_tickets_with_reservations();
942
+		return EED_Ticket_Sales_Monitor::release_reservations_for_tickets(
943
+			$tickets_with_reservations,
944
+			$valid_reserved_tickets,
945
+			__FUNCTION__
946
+		);
947
+	}
948
+
949
+
950
+	/**
951
+	 * @param EE_Line_Item $total_line_item
952
+	 * @return EE_Line_Item[]
953
+	 */
954
+	private static function get_ticket_line_items_for_grand_total(EE_Line_Item $total_line_item)
955
+	{
956
+		/** @var EE_Line_Item[] $valid_reserved_tickets */
957
+		$valid_reserved_tickets = array();
958
+		$ticket_line_items = EEH_Line_Item::get_ticket_line_items($total_line_item);
959
+		foreach ($ticket_line_items as $ticket_line_item) {
960
+			if ($ticket_line_item instanceof EE_Line_Item) {
961
+				$valid_reserved_tickets[] = $ticket_line_item;
962
+			}
963
+		}
964
+		return $valid_reserved_tickets;
965
+	}
966
+
967
+
968
+	/**
969
+	 * @param EE_Ticket[]    $tickets_with_reservations
970
+	 * @param EE_Line_Item[] $valid_reserved_ticket_line_items
971
+	 * @return int
972
+	 * @throws UnexpectedEntityException
973
+	 * @throws DomainException
974
+	 * @throws EE_Error
975
+	 */
976
+	private static function release_reservations_for_tickets(
977
+		array $tickets_with_reservations,
978
+		array $valid_reserved_ticket_line_items = array(),
979
+		$source
980
+	) {
981
+		if (self::debug) {
982
+			echo self::$nl . self::$nl . __LINE__ . ') ' . __METHOD__ . '()';
983
+		}
984
+		$total_tickets_released = 0;
985
+		$sold_out_events = array();
986
+		foreach ($tickets_with_reservations as $ticket_with_reservations) {
987
+			if (! $ticket_with_reservations instanceof EE_Ticket) {
988
+				continue;
989
+			}
990
+			$reserved_qty = $ticket_with_reservations->reserved();
991
+			if (self::debug) {
992
+				echo self::$nl . ' . $ticket_with_reservations->ID(): ' . $ticket_with_reservations->ID();
993
+				echo self::$nl . ' . $reserved_qty: ' . $reserved_qty;
994
+			}
995
+			foreach ($valid_reserved_ticket_line_items as $valid_reserved_ticket_line_item) {
996
+				if ($valid_reserved_ticket_line_item instanceof EE_Line_Item
997
+					&& $valid_reserved_ticket_line_item->OBJ_ID() === $ticket_with_reservations->ID()
998
+				) {
999
+					if (self::debug) {
1000
+						echo self::$nl . ' . $valid_reserved_ticket_line_item->quantity(): '
1001
+							 . $valid_reserved_ticket_line_item->quantity();
1002
+					}
1003
+					$reserved_qty -= $valid_reserved_ticket_line_item->quantity();
1004
+				}
1005
+			}
1006
+			if ($reserved_qty > 0) {
1007
+				$ticket_with_reservations->add_extra_meta(
1008
+					EE_Ticket::META_KEY_TICKET_RESERVATIONS,
1009
+					__LINE__ . ') ' . $source . '()'
1010
+				);
1011
+				$ticket_with_reservations->decrease_reserved($reserved_qty, true, 'TicketSalesMonitor:' . __LINE__);
1012
+				$ticket_with_reservations->save();
1013
+				$total_tickets_released += $reserved_qty;
1014
+				$event = $ticket_with_reservations->get_related_event();
1015
+				// track sold out events
1016
+				if ($event instanceof EE_Event && $event->is_sold_out()) {
1017
+					$sold_out_events[] = $event;
1018
+				}
1019
+			}
1020
+		}
1021
+		if (self::debug) {
1022
+			echo self::$nl . ' . $total_tickets_released: ' . $total_tickets_released;
1023
+		}
1024
+		// double check whether sold out events should remain sold out after releasing tickets
1025
+		if ($sold_out_events !== array()) {
1026
+			foreach ($sold_out_events as $sold_out_event) {
1027
+				/** @var EE_Event $sold_out_event */
1028
+				$sold_out_event->perform_sold_out_status_check();
1029
+			}
1030
+		}
1031
+		return $total_tickets_released;
1032
+	}
1033
+
1034
+
1035
+
1036
+	/********************************** SHUTDOWN  **********************************/
1037
+
1038
+
1039
+	/**
1040
+	 * @param int $timestamp
1041
+	 * @return false|int
1042
+	 * @throws EE_Error
1043
+	 * @throws InvalidArgumentException
1044
+	 * @throws InvalidDataTypeException
1045
+	 * @throws InvalidInterfaceException
1046
+	 */
1047
+	public static function clear_expired_line_items_with_no_transaction($timestamp = 0)
1048
+	{
1049
+		/** @type WPDB $wpdb */
1050
+		global $wpdb;
1051
+		if (! absint($timestamp)) {
1052
+			/** @var EventEspresso\core\domain\values\session\SessionLifespan $session_lifespan */
1053
+			$session_lifespan = LoaderFactory::getLoader()->getShared(
1054
+				'EventEspresso\core\domain\values\session\SessionLifespan'
1055
+			);
1056
+			$timestamp = $session_lifespan->expiration();
1057
+		}
1058
+		return $wpdb->query(
1059
+			$wpdb->prepare(
1060
+				'DELETE FROM ' . EEM_Line_Item::instance()->table() . '
1061 1061
                 WHERE TXN_ID = 0 AND LIN_timestamp <= %s',
1062
-                // use GMT time because that's what LIN_timestamps are in
1063
-                date('Y-m-d H:i:s', $timestamp)
1064
-            )
1065
-        );
1066
-    }
1062
+				// use GMT time because that's what LIN_timestamps are in
1063
+				date('Y-m-d H:i:s', $timestamp)
1064
+			)
1065
+		);
1066
+	}
1067 1067
 }
Please login to merge, or discard this patch.
Spacing   +83 added lines, -83 removed lines patch added patch discarded remove patch
@@ -182,7 +182,7 @@  discard block
 block discarded – undo
182 182
     public static function release_tickets_for_expired_carts()
183 183
     {
184 184
         if (self::debug) {
185
-            echo self::$nl . self::$nl . __LINE__ . ') ' . __METHOD__ . '()';
185
+            echo self::$nl.self::$nl.__LINE__.') '.__METHOD__.'()';
186 186
         }
187 187
         do_action('AHEE__EED_Ticket_Sales_Monitor__release_tickets_for_expired_carts__begin');
188 188
         $expired_ticket_IDs = array();
@@ -193,29 +193,29 @@  discard block
 block discarded – undo
193 193
         $timestamp = $session_lifespan->expiration();
194 194
         $expired_ticket_line_items = EEM_Line_Item::instance()->getTicketLineItemsForExpiredCarts($timestamp);
195 195
         if (self::debug) {
196
-            echo self::$nl . ' . time(): ' . time();
197
-            echo self::$nl . ' . time() as date: ' . date('Y-m-d H:i a');
198
-            echo self::$nl . ' . session expiration: ' . $session_lifespan->expiration();
199
-            echo self::$nl . ' . session expiration as date: ' . date('Y-m-d H:i a', $session_lifespan->expiration());
200
-            echo self::$nl . ' . timestamp: ' . $timestamp;
201
-            echo self::$nl . ' . $expired_ticket_line_items: ' . count($expired_ticket_line_items);
196
+            echo self::$nl.' . time(): '.time();
197
+            echo self::$nl.' . time() as date: '.date('Y-m-d H:i a');
198
+            echo self::$nl.' . session expiration: '.$session_lifespan->expiration();
199
+            echo self::$nl.' . session expiration as date: '.date('Y-m-d H:i a', $session_lifespan->expiration());
200
+            echo self::$nl.' . timestamp: '.$timestamp;
201
+            echo self::$nl.' . $expired_ticket_line_items: '.count($expired_ticket_line_items);
202 202
         }
203
-        if (! empty($expired_ticket_line_items)) {
203
+        if ( ! empty($expired_ticket_line_items)) {
204 204
             foreach ($expired_ticket_line_items as $expired_ticket_line_item) {
205
-                if (! $expired_ticket_line_item instanceof EE_Line_Item) {
205
+                if ( ! $expired_ticket_line_item instanceof EE_Line_Item) {
206 206
                     continue;
207 207
                 }
208
-                $expired_ticket_IDs[ $expired_ticket_line_item->OBJ_ID() ] = $expired_ticket_line_item->OBJ_ID();
208
+                $expired_ticket_IDs[$expired_ticket_line_item->OBJ_ID()] = $expired_ticket_line_item->OBJ_ID();
209 209
                 if (self::debug) {
210
-                    echo self::$nl . ' . $expired_ticket_line_item->OBJ_ID(): ' . $expired_ticket_line_item->OBJ_ID();
211
-                    echo self::$nl . ' . $expired_ticket_line_item->timestamp(): '
210
+                    echo self::$nl.' . $expired_ticket_line_item->OBJ_ID(): '.$expired_ticket_line_item->OBJ_ID();
211
+                    echo self::$nl.' . $expired_ticket_line_item->timestamp(): '
212 212
                          . date(
213 213
                              'Y-m-d h:i a',
214 214
                              $expired_ticket_line_item->timestamp(true)
215 215
                          );
216 216
                 }
217 217
             }
218
-            if (! empty($expired_ticket_IDs)) {
218
+            if ( ! empty($expired_ticket_IDs)) {
219 219
                 EED_Ticket_Sales_Monitor::release_reservations_for_tickets(
220 220
                     \EEM_Ticket::instance()->get_tickets_with_IDs($expired_ticket_IDs),
221 221
                     array(),
@@ -253,8 +253,8 @@  discard block
 block discarded – undo
253 253
             $qty = EED_Ticket_Sales_Monitor::instance()->_validate_ticket_sale($ticket, $qty);
254 254
         }
255 255
         if (self::debug) {
256
-            echo self::$nl . self::$nl . __LINE__ . ') ' . __METHOD__ . '()';
257
-            echo self::$nl . self::$nl . '<b> RETURNED QTY: ' . $qty . '</b>';
256
+            echo self::$nl.self::$nl.__LINE__.') '.__METHOD__.'()';
257
+            echo self::$nl.self::$nl.'<b> RETURNED QTY: '.$qty.'</b>';
258 258
         }
259 259
         return $qty;
260 260
     }
@@ -272,36 +272,36 @@  discard block
 block discarded – undo
272 272
     protected function _validate_ticket_sale(EE_Ticket $ticket, $qty = 1)
273 273
     {
274 274
         if (self::debug) {
275
-            echo self::$nl . self::$nl . __LINE__ . ') ' . __METHOD__ . '() ';
275
+            echo self::$nl.self::$nl.__LINE__.') '.__METHOD__.'() ';
276 276
         }
277
-        if (! $ticket instanceof EE_Ticket) {
277
+        if ( ! $ticket instanceof EE_Ticket) {
278 278
             return 0;
279 279
         }
280 280
         if (self::debug) {
281
-            echo self::$nl . '<b> . ticket->ID: ' . $ticket->ID() . '</b>';
282
-            echo self::$nl . ' . original ticket->reserved: ' . $ticket->reserved();
281
+            echo self::$nl.'<b> . ticket->ID: '.$ticket->ID().'</b>';
282
+            echo self::$nl.' . original ticket->reserved: '.$ticket->reserved();
283 283
         }
284 284
         $ticket->refresh_from_db();
285 285
         // first let's determine the ticket availability based on sales
286 286
         $available = $ticket->qty('saleable');
287 287
         if (self::debug) {
288
-            echo self::$nl . ' . . . ticket->qty: ' . $ticket->qty();
289
-            echo self::$nl . ' . . . ticket->sold: ' . $ticket->sold();
290
-            echo self::$nl . ' . . . ticket->reserved: ' . $ticket->reserved();
291
-            echo self::$nl . ' . . . ticket->qty(saleable): ' . $ticket->qty('saleable');
292
-            echo self::$nl . ' . . . available: ' . $available;
288
+            echo self::$nl.' . . . ticket->qty: '.$ticket->qty();
289
+            echo self::$nl.' . . . ticket->sold: '.$ticket->sold();
290
+            echo self::$nl.' . . . ticket->reserved: '.$ticket->reserved();
291
+            echo self::$nl.' . . . ticket->qty(saleable): '.$ticket->qty('saleable');
292
+            echo self::$nl.' . . . available: '.$available;
293 293
         }
294 294
         if ($available < 1) {
295 295
             $this->_ticket_sold_out($ticket);
296 296
             return 0;
297 297
         }
298 298
         if (self::debug) {
299
-            echo self::$nl . ' . . . qty: ' . $qty;
299
+            echo self::$nl.' . . . qty: '.$qty;
300 300
         }
301 301
         if ($available < $qty) {
302 302
             $qty = $available;
303 303
             if (self::debug) {
304
-                echo self::$nl . ' . . . QTY ADJUSTED: ' . $qty;
304
+                echo self::$nl.' . . . QTY ADJUSTED: '.$qty;
305 305
             }
306 306
             $this->_ticket_quantity_decremented($ticket);
307 307
         }
@@ -321,9 +321,9 @@  discard block
 block discarded – undo
321 321
     protected function _reserve_ticket(EE_Ticket $ticket, $quantity = 1)
322 322
     {
323 323
         if (self::debug) {
324
-            echo self::$nl . self::$nl . ' . . . INCREASE RESERVED: ' . $quantity;
324
+            echo self::$nl.self::$nl.' . . . INCREASE RESERVED: '.$quantity;
325 325
         }
326
-        $ticket->increase_reserved($quantity, 'TicketSalesMonitor:' . __LINE__);
326
+        $ticket->increase_reserved($quantity, 'TicketSalesMonitor:'.__LINE__);
327 327
         return $ticket->save();
328 328
     }
329 329
 
@@ -337,12 +337,12 @@  discard block
 block discarded – undo
337 337
     protected function _release_reserved_ticket(EE_Ticket $ticket, $quantity = 1)
338 338
     {
339 339
         if (self::debug) {
340
-            echo self::$nl . ' . . . ticket->ID: ' . $ticket->ID();
341
-            echo self::$nl . ' . . . ticket->reserved before: ' . $ticket->reserved();
340
+            echo self::$nl.' . . . ticket->ID: '.$ticket->ID();
341
+            echo self::$nl.' . . . ticket->reserved before: '.$ticket->reserved();
342 342
         }
343
-        $ticket->decrease_reserved($quantity, true, 'TicketSalesMonitor:' . __LINE__);
343
+        $ticket->decrease_reserved($quantity, true, 'TicketSalesMonitor:'.__LINE__);
344 344
         if (self::debug) {
345
-            echo self::$nl . ' . . . ticket->reserved after: ' . $ticket->reserved();
345
+            echo self::$nl.' . . . ticket->reserved after: '.$ticket->reserved();
346 346
         }
347 347
         return $ticket->save() ? 1 : 0;
348 348
     }
@@ -359,8 +359,8 @@  discard block
 block discarded – undo
359 359
     protected function _ticket_sold_out(EE_Ticket $ticket)
360 360
     {
361 361
         if (self::debug) {
362
-            echo self::$nl . self::$nl . __LINE__ . ') ' . __METHOD__ . '() ';
363
-            echo self::$nl . ' . . ticket->name: ' . $this->_get_ticket_and_event_name($ticket);
362
+            echo self::$nl.self::$nl.__LINE__.') '.__METHOD__.'() ';
363
+            echo self::$nl.' . . ticket->name: '.$this->_get_ticket_and_event_name($ticket);
364 364
         }
365 365
         $this->sold_out_tickets[] = $this->_get_ticket_and_event_name($ticket);
366 366
     }
@@ -377,8 +377,8 @@  discard block
 block discarded – undo
377 377
     protected function _ticket_quantity_decremented(EE_Ticket $ticket)
378 378
     {
379 379
         if (self::debug) {
380
-            echo self::$nl . self::$nl . __LINE__ . ') ' . __METHOD__ . '() ';
381
-            echo self::$nl . ' . . ticket->name: ' . $this->_get_ticket_and_event_name($ticket);
380
+            echo self::$nl.self::$nl.__LINE__.') '.__METHOD__.'() ';
381
+            echo self::$nl.' . . ticket->name: '.$this->_get_ticket_and_event_name($ticket);
382 382
         }
383 383
         $this->decremented_tickets[] = $this->_get_ticket_and_event_name($ticket);
384 384
     }
@@ -429,7 +429,7 @@  discard block
 block discarded – undo
429 429
         if ($ticket instanceof EE_Ticket) {
430 430
             $ticket->add_extra_meta(
431 431
                 EE_Ticket::META_KEY_TICKET_RESERVATIONS,
432
-                __LINE__ . ') ' . __METHOD__ . '()'
432
+                __LINE__.') '.__METHOD__.'()'
433 433
             );
434 434
             if ($quantity > 0) {
435 435
                 EED_Ticket_Sales_Monitor::instance()->_reserve_ticket($ticket, $quantity);
@@ -452,7 +452,7 @@  discard block
 block discarded – undo
452 452
     {
453 453
         $ticket->add_extra_meta(
454 454
             EE_Ticket::META_KEY_TICKET_RESERVATIONS,
455
-            __LINE__ . ') ' . __METHOD__ . '()'
455
+            __LINE__.') '.__METHOD__.'()'
456 456
         );
457 457
         EED_Ticket_Sales_Monitor::instance()->_release_reserved_ticket($ticket, $quantity);
458 458
     }
@@ -487,7 +487,7 @@  discard block
 block discarded – undo
487 487
     protected function _post_notices()
488 488
     {
489 489
         if (self::debug) {
490
-            echo self::$nl . self::$nl . __LINE__ . ') ' . __METHOD__ . '() ';
490
+            echo self::$nl.self::$nl.__LINE__.') '.__METHOD__.'() ';
491 491
         }
492 492
         $refresh_msg = '';
493 493
         $none_added_msg = '';
@@ -498,7 +498,7 @@  discard block
 block discarded – undo
498 498
             );
499 499
             $none_added_msg = __('No tickets were added for the event.', 'event_espresso');
500 500
         }
501
-        if (! empty($this->sold_out_tickets)) {
501
+        if ( ! empty($this->sold_out_tickets)) {
502 502
             EE_Error::add_attention(
503 503
                 sprintf(
504 504
                     apply_filters(
@@ -521,7 +521,7 @@  discard block
 block discarded – undo
521 521
             // and reset the cart
522 522
             EED_Ticket_Sales_Monitor::session_cart_reset(EE_Registry::instance()->SSN);
523 523
         }
524
-        if (! empty($this->decremented_tickets)) {
524
+        if ( ! empty($this->decremented_tickets)) {
525 525
             EE_Error::add_attention(
526 526
                 sprintf(
527 527
                     apply_filters(
@@ -558,9 +558,9 @@  discard block
 block discarded – undo
558 558
     protected function _release_all_reserved_tickets_for_transaction(EE_Transaction $transaction)
559 559
     {
560 560
         if (self::debug) {
561
-            echo self::$nl . self::$nl . __LINE__ . ') ' . __METHOD__ . '() ';
562
-            echo self::$nl . ' . transaction->ID: ' . $transaction->ID();
563
-            echo self::$nl . ' . TXN status_ID: ' . $transaction->status_ID();
561
+            echo self::$nl.self::$nl.__LINE__.') '.__METHOD__.'() ';
562
+            echo self::$nl.' . transaction->ID: '.$transaction->ID();
563
+            echo self::$nl.' . TXN status_ID: '.$transaction->status_ID();
564 564
         }
565 565
         // check if 'finalize_registration' step has been completed...
566 566
         $finalized = $transaction->reg_step_completed('finalize_registration');
@@ -572,13 +572,13 @@  discard block
 block discarded – undo
572 572
                 __LINE__,
573 573
                 array('finalized' => $finalized),
574 574
                 false,
575
-                'EE_Transaction: ' . $transaction->ID()
575
+                'EE_Transaction: '.$transaction->ID()
576 576
             );
577 577
         }
578 578
         // how many tickets were released
579 579
         $count = 0;
580 580
         if (self::debug) {
581
-            echo self::$nl . ' . . . TXN finalized: ' . $finalized;
581
+            echo self::$nl.' . . . TXN finalized: '.$finalized;
582 582
         }
583 583
         $release_tickets_with_TXN_status = array(
584 584
             EEM_Transaction::failed_status_code,
@@ -587,27 +587,27 @@  discard block
 block discarded – undo
587 587
         );
588 588
         $events = array();
589 589
         // if the session is getting cleared BEFORE the TXN has been finalized or the transaction is not completed
590
-        if (! $finalized || in_array($transaction->status_ID(), $release_tickets_with_TXN_status, true)) {
590
+        if ( ! $finalized || in_array($transaction->status_ID(), $release_tickets_with_TXN_status, true)) {
591 591
             // cancel any reserved tickets for registrations that were not approved
592 592
             $registrations = $transaction->registrations();
593 593
             if (self::debug) {
594
-                echo self::$nl . ' . . . # registrations: ' . count($registrations);
594
+                echo self::$nl.' . . . # registrations: '.count($registrations);
595 595
                 $reg = reset($registrations);
596 596
                 $ticket = $reg->ticket();
597 597
                 if ($ticket instanceof EE_Ticket) {
598 598
                     $ticket->add_extra_meta(
599 599
                         EE_Ticket::META_KEY_TICKET_RESERVATIONS,
600
-                        __LINE__ . ') Release All Tickets TXN:' . $transaction->ID()
600
+                        __LINE__.') Release All Tickets TXN:'.$transaction->ID()
601 601
                     );
602 602
                 }
603 603
             }
604
-            if (! empty($registrations)) {
604
+            if ( ! empty($registrations)) {
605 605
                 foreach ($registrations as $registration) {
606 606
                     if ($registration instanceof EE_Registration
607 607
                         && $this->_release_reserved_ticket_for_registration($registration, $transaction)
608 608
                     ) {
609 609
                         $count++;
610
-                        $events[ $registration->event_ID() ] = $registration->event();
610
+                        $events[$registration->event_ID()] = $registration->event();
611 611
                     }
612 612
                 }
613 613
             }
@@ -637,10 +637,10 @@  discard block
 block discarded – undo
637 637
     ) {
638 638
         $STS_ID = $transaction->status_ID();
639 639
         if (self::debug) {
640
-            echo self::$nl . self::$nl . __LINE__ . ') ' . __METHOD__ . '() ';
641
-            echo self::$nl . ' . . registration->ID: ' . $registration->ID();
642
-            echo self::$nl . ' . . registration->status_ID: ' . $registration->status_ID();
643
-            echo self::$nl . ' . . transaction->status_ID(): ' . $STS_ID;
640
+            echo self::$nl.self::$nl.__LINE__.') '.__METHOD__.'() ';
641
+            echo self::$nl.' . . registration->ID: '.$registration->ID();
642
+            echo self::$nl.' . . registration->status_ID: '.$registration->status_ID();
643
+            echo self::$nl.' . . transaction->status_ID(): '.$STS_ID;
644 644
         }
645 645
         if (// release Tickets for Failed Transactions and Abandoned Transactions
646 646
             $STS_ID === EEM_Transaction::failed_status_code
@@ -652,12 +652,12 @@  discard block
 block discarded – undo
652 652
             )
653 653
         ) {
654 654
             if (self::debug) {
655
-                echo self::$nl . self::$nl . ' . . RELEASE RESERVED TICKET';
655
+                echo self::$nl.self::$nl.' . . RELEASE RESERVED TICKET';
656 656
                 $rsrvd = $registration->get_extra_meta(EE_Registration::HAS_RESERVED_TICKET_KEY, true);
657
-                echo self::$nl . ' . . . registration HAS_RESERVED_TICKET_KEY: ';
657
+                echo self::$nl.' . . . registration HAS_RESERVED_TICKET_KEY: ';
658 658
                 var_dump($rsrvd);
659 659
             }
660
-            $registration->release_reserved_ticket(true, 'TicketSalesMonitor:' . __LINE__);
660
+            $registration->release_reserved_ticket(true, 'TicketSalesMonitor:'.__LINE__);
661 661
             return 1;
662 662
         }
663 663
         return 0;
@@ -686,7 +686,7 @@  discard block
 block discarded – undo
686 686
             return;
687 687
         }
688 688
         if (self::debug) {
689
-            echo self::$nl . self::$nl . __LINE__ . ') ' . __METHOD__ . '() ';
689
+            echo self::$nl.self::$nl.__LINE__.') '.__METHOD__.'() ';
690 690
         }
691 691
         // first check of the session has a valid Checkout object
692 692
         $checkout = $session->checkout();
@@ -698,12 +698,12 @@  discard block
 block discarded – undo
698 698
         $cart = $session->cart();
699 699
         if ($cart instanceof EE_Cart) {
700 700
             if (self::debug) {
701
-                echo self::$nl . self::$nl . ' cart instance of EE_Cart: ';
701
+                echo self::$nl.self::$nl.' cart instance of EE_Cart: ';
702 702
             }
703 703
             EED_Ticket_Sales_Monitor::instance()->_session_cart_reset($cart, $session);
704 704
         } else {
705 705
             if (self::debug) {
706
-                echo self::$nl . self::$nl . ' invalid EE_Cart: ';
706
+                echo self::$nl.self::$nl.' invalid EE_Cart: ';
707 707
                 var_export($cart, true);
708 708
             }
709 709
         }
@@ -724,39 +724,39 @@  discard block
 block discarded – undo
724 724
     protected function _session_cart_reset(EE_Cart $cart, EE_Session $session)
725 725
     {
726 726
         if (self::debug) {
727
-            echo self::$nl . self::$nl . __LINE__ . ') ' . __METHOD__ . '() ';
727
+            echo self::$nl.self::$nl.__LINE__.') '.__METHOD__.'() ';
728 728
         }
729 729
         $ticket_line_items = $cart->get_tickets();
730 730
         if (empty($ticket_line_items)) {
731 731
             return;
732 732
         }
733 733
         if (self::debug) {
734
-            echo '<br /> . ticket_line_item count: ' . count($ticket_line_items);
734
+            echo '<br /> . ticket_line_item count: '.count($ticket_line_items);
735 735
         }
736 736
         foreach ($ticket_line_items as $ticket_line_item) {
737 737
             if (self::debug) {
738
-                echo self::$nl . ' . ticket_line_item->ID(): ' . $ticket_line_item->ID();
738
+                echo self::$nl.' . ticket_line_item->ID(): '.$ticket_line_item->ID();
739 739
             }
740 740
             if ($ticket_line_item instanceof EE_Line_Item && $ticket_line_item->OBJ_type() === 'Ticket') {
741 741
                 if (self::debug) {
742
-                    echo self::$nl . ' . . ticket_line_item->OBJ_ID(): ' . $ticket_line_item->OBJ_ID();
742
+                    echo self::$nl.' . . ticket_line_item->OBJ_ID(): '.$ticket_line_item->OBJ_ID();
743 743
                 }
744 744
                 $ticket = EEM_Ticket::instance()->get_one_by_ID($ticket_line_item->OBJ_ID());
745 745
                 if ($ticket instanceof EE_Ticket) {
746 746
                     if (self::debug) {
747
-                        echo self::$nl . ' . . ticket->ID(): ' . $ticket->ID();
748
-                        echo self::$nl . ' . . ticket_line_item->quantity(): ' . $ticket_line_item->quantity();
747
+                        echo self::$nl.' . . ticket->ID(): '.$ticket->ID();
748
+                        echo self::$nl.' . . ticket_line_item->quantity(): '.$ticket_line_item->quantity();
749 749
                     }
750 750
                     $ticket->add_extra_meta(
751 751
                         EE_Ticket::META_KEY_TICKET_RESERVATIONS,
752
-                        __LINE__ . ') ' . __METHOD__ . '() SID = ' . $session->id()
752
+                        __LINE__.') '.__METHOD__.'() SID = '.$session->id()
753 753
                     );
754 754
                     $this->_release_reserved_ticket($ticket, $ticket_line_item->quantity());
755 755
                 }
756 756
             }
757 757
         }
758 758
         if (self::debug) {
759
-            echo self::$nl . self::$nl . ' RESET COMPLETED ';
759
+            echo self::$nl.self::$nl.' RESET COMPLETED ';
760 760
         }
761 761
     }
762 762
 
@@ -797,7 +797,7 @@  discard block
 block discarded – undo
797 797
     protected function _session_checkout_reset(EE_Checkout $checkout)
798 798
     {
799 799
         if (self::debug) {
800
-            echo self::$nl . self::$nl . __LINE__ . ') ' . __METHOD__ . '() ';
800
+            echo self::$nl.self::$nl.__LINE__.') '.__METHOD__.'() ';
801 801
         }
802 802
         // we want to release the each registration's reserved tickets if the session was cleared, but not if this is a revisit
803 803
         if ($checkout->revisit || ! $checkout->transaction instanceof EE_Transaction) {
@@ -845,7 +845,7 @@  discard block
 block discarded – undo
845 845
                     __LINE__,
846 846
                     array($transaction),
847 847
                     false,
848
-                    'EE_Transaction: ' . $transaction->ID()
848
+                    'EE_Transaction: '.$transaction->ID()
849 849
                 );
850 850
             }
851 851
             return;
@@ -862,7 +862,7 @@  discard block
 block discarded – undo
862 862
                         __LINE__,
863 863
                         array($payment),
864 864
                         false,
865
-                        'EE_Transaction: ' . $transaction->ID()
865
+                        'EE_Transaction: '.$transaction->ID()
866 866
                     );
867 867
                 }
868 868
                 return;
@@ -920,7 +920,7 @@  discard block
 block discarded – undo
920 920
             }
921 921
             $total_line_item = $transaction->total_line_item();
922 922
             // $transaction_in_progress->line
923
-            if (! $total_line_item instanceof EE_Line_Item) {
923
+            if ( ! $total_line_item instanceof EE_Line_Item) {
924 924
                 throw new DomainException(
925 925
                     esc_html__(
926 926
                         'Transaction does not have a valid Total Line Item associated with it.',
@@ -979,25 +979,25 @@  discard block
 block discarded – undo
979 979
         $source
980 980
     ) {
981 981
         if (self::debug) {
982
-            echo self::$nl . self::$nl . __LINE__ . ') ' . __METHOD__ . '()';
982
+            echo self::$nl.self::$nl.__LINE__.') '.__METHOD__.'()';
983 983
         }
984 984
         $total_tickets_released = 0;
985 985
         $sold_out_events = array();
986 986
         foreach ($tickets_with_reservations as $ticket_with_reservations) {
987
-            if (! $ticket_with_reservations instanceof EE_Ticket) {
987
+            if ( ! $ticket_with_reservations instanceof EE_Ticket) {
988 988
                 continue;
989 989
             }
990 990
             $reserved_qty = $ticket_with_reservations->reserved();
991 991
             if (self::debug) {
992
-                echo self::$nl . ' . $ticket_with_reservations->ID(): ' . $ticket_with_reservations->ID();
993
-                echo self::$nl . ' . $reserved_qty: ' . $reserved_qty;
992
+                echo self::$nl.' . $ticket_with_reservations->ID(): '.$ticket_with_reservations->ID();
993
+                echo self::$nl.' . $reserved_qty: '.$reserved_qty;
994 994
             }
995 995
             foreach ($valid_reserved_ticket_line_items as $valid_reserved_ticket_line_item) {
996 996
                 if ($valid_reserved_ticket_line_item instanceof EE_Line_Item
997 997
                     && $valid_reserved_ticket_line_item->OBJ_ID() === $ticket_with_reservations->ID()
998 998
                 ) {
999 999
                     if (self::debug) {
1000
-                        echo self::$nl . ' . $valid_reserved_ticket_line_item->quantity(): '
1000
+                        echo self::$nl.' . $valid_reserved_ticket_line_item->quantity(): '
1001 1001
                              . $valid_reserved_ticket_line_item->quantity();
1002 1002
                     }
1003 1003
                     $reserved_qty -= $valid_reserved_ticket_line_item->quantity();
@@ -1006,9 +1006,9 @@  discard block
 block discarded – undo
1006 1006
             if ($reserved_qty > 0) {
1007 1007
                 $ticket_with_reservations->add_extra_meta(
1008 1008
                     EE_Ticket::META_KEY_TICKET_RESERVATIONS,
1009
-                    __LINE__ . ') ' . $source . '()'
1009
+                    __LINE__.') '.$source.'()'
1010 1010
                 );
1011
-                $ticket_with_reservations->decrease_reserved($reserved_qty, true, 'TicketSalesMonitor:' . __LINE__);
1011
+                $ticket_with_reservations->decrease_reserved($reserved_qty, true, 'TicketSalesMonitor:'.__LINE__);
1012 1012
                 $ticket_with_reservations->save();
1013 1013
                 $total_tickets_released += $reserved_qty;
1014 1014
                 $event = $ticket_with_reservations->get_related_event();
@@ -1019,7 +1019,7 @@  discard block
 block discarded – undo
1019 1019
             }
1020 1020
         }
1021 1021
         if (self::debug) {
1022
-            echo self::$nl . ' . $total_tickets_released: ' . $total_tickets_released;
1022
+            echo self::$nl.' . $total_tickets_released: '.$total_tickets_released;
1023 1023
         }
1024 1024
         // double check whether sold out events should remain sold out after releasing tickets
1025 1025
         if ($sold_out_events !== array()) {
@@ -1048,7 +1048,7 @@  discard block
 block discarded – undo
1048 1048
     {
1049 1049
         /** @type WPDB $wpdb */
1050 1050
         global $wpdb;
1051
-        if (! absint($timestamp)) {
1051
+        if ( ! absint($timestamp)) {
1052 1052
             /** @var EventEspresso\core\domain\values\session\SessionLifespan $session_lifespan */
1053 1053
             $session_lifespan = LoaderFactory::getLoader()->getShared(
1054 1054
                 'EventEspresso\core\domain\values\session\SessionLifespan'
@@ -1057,7 +1057,7 @@  discard block
 block discarded – undo
1057 1057
         }
1058 1058
         return $wpdb->query(
1059 1059
             $wpdb->prepare(
1060
-                'DELETE FROM ' . EEM_Line_Item::instance()->table() . '
1060
+                'DELETE FROM '.EEM_Line_Item::instance()->table().'
1061 1061
                 WHERE TXN_ID = 0 AND LIN_timestamp <= %s',
1062 1062
                 // use GMT time because that's what LIN_timestamps are in
1063 1063
                 date('Y-m-d H:i:s', $timestamp)
Please login to merge, or discard this patch.
admin/extend/registration_form/Extend_Registration_Form_Admin_Page.core.php 3 patches
Doc Comments   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -566,7 +566,7 @@
 block discarded – undo
566 566
      *
567 567
      * @param int                  $id
568 568
      * @param EEM_Soft_Delete_Base $model
569
-     * @return boolean
569
+     * @return integer
570 570
      * @throws EE_Error
571 571
      * @throws InvalidArgumentException
572 572
      * @throws InvalidDataTypeException
Please login to merge, or discard this patch.
Indentation   +1295 added lines, -1295 removed lines patch added patch discarded remove patch
@@ -14,1299 +14,1299 @@
 block discarded – undo
14 14
 class Extend_Registration_Form_Admin_Page extends Registration_Form_Admin_Page
15 15
 {
16 16
 
17
-    /**
18
-     * @param bool $routing indicate whether we want to just load the object and handle routing or just load the object.
19
-     */
20
-    public function __construct($routing = true)
21
-    {
22
-        define('REGISTRATION_FORM_CAF_ADMIN', EE_CORE_CAF_ADMIN_EXTEND . 'registration_form' . DS);
23
-        define('REGISTRATION_FORM_CAF_ASSETS_PATH', REGISTRATION_FORM_CAF_ADMIN . 'assets' . DS);
24
-        define('REGISTRATION_FORM_CAF_ASSETS_URL', EE_CORE_CAF_ADMIN_EXTEND_URL . 'registration_form/assets/');
25
-        define('REGISTRATION_FORM_CAF_TEMPLATE_PATH', REGISTRATION_FORM_CAF_ADMIN . 'templates' . DS);
26
-        define('REGISTRATION_FORM_CAF_TEMPLATE_URL', EE_CORE_CAF_ADMIN_EXTEND_URL . 'registration_form/templates/');
27
-        parent::__construct($routing);
28
-    }
29
-
30
-
31
-    /**
32
-     * @return void
33
-     */
34
-    protected function _extend_page_config()
35
-    {
36
-        $this->_admin_base_path = REGISTRATION_FORM_CAF_ADMIN;
37
-        $qst_id = ! empty($this->_req_data['QST_ID']) && ! is_array($this->_req_data['QST_ID'])
38
-            ? $this->_req_data['QST_ID'] : 0;
39
-        $qsg_id = ! empty($this->_req_data['QSG_ID']) && ! is_array($this->_req_data['QSG_ID'])
40
-            ? $this->_req_data['QSG_ID'] : 0;
41
-
42
-        $new_page_routes = array(
43
-            'question_groups'    => array(
44
-                'func'       => '_question_groups_overview_list_table',
45
-                'capability' => 'ee_read_question_groups',
46
-            ),
47
-            'add_question'       => array(
48
-                'func'       => '_edit_question',
49
-                'capability' => 'ee_edit_questions',
50
-            ),
51
-            'insert_question'    => array(
52
-                'func'       => '_insert_or_update_question',
53
-                'args'       => array('new_question' => true),
54
-                'capability' => 'ee_edit_questions',
55
-                'noheader'   => true,
56
-            ),
57
-            'duplicate_question' => array(
58
-                'func'       => '_duplicate_question',
59
-                'capability' => 'ee_edit_questions',
60
-                'noheader'   => true,
61
-            ),
62
-            'trash_question'     => array(
63
-                'func'       => '_trash_question',
64
-                'capability' => 'ee_delete_question',
65
-                'obj_id'     => $qst_id,
66
-                'noheader'   => true,
67
-            ),
68
-
69
-            'restore_question' => array(
70
-                'func'       => '_trash_or_restore_questions',
71
-                'capability' => 'ee_delete_question',
72
-                'obj_id'     => $qst_id,
73
-                'args'       => array('trash' => false),
74
-                'noheader'   => true,
75
-            ),
76
-
77
-            'delete_question' => array(
78
-                'func'       => '_delete_question',
79
-                'capability' => 'ee_delete_question',
80
-                'obj_id'     => $qst_id,
81
-                'noheader'   => true,
82
-            ),
83
-
84
-            'trash_questions' => array(
85
-                'func'       => '_trash_or_restore_questions',
86
-                'capability' => 'ee_delete_questions',
87
-                'args'       => array('trash' => true),
88
-                'noheader'   => true,
89
-            ),
90
-
91
-            'restore_questions' => array(
92
-                'func'       => '_trash_or_restore_questions',
93
-                'capability' => 'ee_delete_questions',
94
-                'args'       => array('trash' => false),
95
-                'noheader'   => true,
96
-            ),
97
-
98
-            'delete_questions' => array(
99
-                'func'       => '_delete_questions',
100
-                'args'       => array(),
101
-                'capability' => 'ee_delete_questions',
102
-                'noheader'   => true,
103
-            ),
104
-
105
-            'add_question_group' => array(
106
-                'func'       => '_edit_question_group',
107
-                'capability' => 'ee_edit_question_groups',
108
-            ),
109
-
110
-            'edit_question_group' => array(
111
-                'func'       => '_edit_question_group',
112
-                'capability' => 'ee_edit_question_group',
113
-                'obj_id'     => $qsg_id,
114
-                'args'       => array('edit'),
115
-            ),
116
-
117
-            'delete_question_groups' => array(
118
-                'func'       => '_delete_question_groups',
119
-                'capability' => 'ee_delete_question_groups',
120
-                'noheader'   => true,
121
-            ),
122
-
123
-            'delete_question_group' => array(
124
-                'func'       => '_delete_question_groups',
125
-                'capability' => 'ee_delete_question_group',
126
-                'obj_id'     => $qsg_id,
127
-                'noheader'   => true,
128
-            ),
129
-
130
-            'trash_question_group' => array(
131
-                'func'       => '_trash_or_restore_question_groups',
132
-                'args'       => array('trash' => true),
133
-                'capability' => 'ee_delete_question_group',
134
-                'obj_id'     => $qsg_id,
135
-                'noheader'   => true,
136
-            ),
137
-
138
-            'restore_question_group' => array(
139
-                'func'       => '_trash_or_restore_question_groups',
140
-                'args'       => array('trash' => false),
141
-                'capability' => 'ee_delete_question_group',
142
-                'obj_id'     => $qsg_id,
143
-                'noheader'   => true,
144
-            ),
145
-
146
-            'insert_question_group' => array(
147
-                'func'       => '_insert_or_update_question_group',
148
-                'args'       => array('new_question_group' => true),
149
-                'capability' => 'ee_edit_question_groups',
150
-                'noheader'   => true,
151
-            ),
152
-
153
-            'update_question_group' => array(
154
-                'func'       => '_insert_or_update_question_group',
155
-                'args'       => array('new_question_group' => false),
156
-                'capability' => 'ee_edit_question_group',
157
-                'obj_id'     => $qsg_id,
158
-                'noheader'   => true,
159
-            ),
160
-
161
-            'trash_question_groups' => array(
162
-                'func'       => '_trash_or_restore_question_groups',
163
-                'args'       => array('trash' => true),
164
-                'capability' => 'ee_delete_question_groups',
165
-                'noheader'   => array('trash' => false),
166
-            ),
167
-
168
-            'restore_question_groups' => array(
169
-                'func'       => '_trash_or_restore_question_groups',
170
-                'args'       => array('trash' => false),
171
-                'capability' => 'ee_delete_question_groups',
172
-                'noheader'   => true,
173
-            ),
174
-
175
-
176
-            'espresso_update_question_group_order' => array(
177
-                'func'       => 'update_question_group_order',
178
-                'capability' => 'ee_edit_question_groups',
179
-                'noheader'   => true,
180
-            ),
181
-
182
-            'view_reg_form_settings' => array(
183
-                'func'       => '_reg_form_settings',
184
-                'capability' => 'manage_options',
185
-            ),
186
-
187
-            'update_reg_form_settings' => array(
188
-                'func'       => '_update_reg_form_settings',
189
-                'capability' => 'manage_options',
190
-                'noheader'   => true,
191
-            ),
192
-        );
193
-        $this->_page_routes = array_merge($this->_page_routes, $new_page_routes);
194
-
195
-        $new_page_config = array(
196
-
197
-            'question_groups' => array(
198
-                'nav'           => array(
199
-                    'label' => esc_html__('Question Groups', 'event_espresso'),
200
-                    'order' => 20,
201
-                ),
202
-                'list_table'    => 'Registration_Form_Question_Groups_Admin_List_Table',
203
-                'help_tabs'     => array(
204
-                    'registration_form_question_groups_help_tab'                           => array(
205
-                        'title'    => esc_html__('Question Groups', 'event_espresso'),
206
-                        'filename' => 'registration_form_question_groups',
207
-                    ),
208
-                    'registration_form_question_groups_table_column_headings_help_tab'     => array(
209
-                        'title'    => esc_html__('Question Groups Table Column Headings', 'event_espresso'),
210
-                        'filename' => 'registration_form_question_groups_table_column_headings',
211
-                    ),
212
-                    'registration_form_question_groups_views_bulk_actions_search_help_tab' => array(
213
-                        'title'    => esc_html__('Question Groups Views & Bulk Actions & Search', 'event_espresso'),
214
-                        'filename' => 'registration_form_question_groups_views_bulk_actions_search',
215
-                    ),
216
-                ),
217
-                'help_tour'     => array('Registration_Form_Question_Groups_Help_Tour'),
218
-                'metaboxes'     => $this->_default_espresso_metaboxes,
219
-                'require_nonce' => false,
220
-                'qtips'         => array(
221
-                    'EE_Registration_Form_Tips',
222
-                ),
223
-            ),
224
-
225
-            'add_question' => array(
226
-                'nav'           => array(
227
-                    'label'      => esc_html__('Add Question', 'event_espresso'),
228
-                    'order'      => 5,
229
-                    'persistent' => false,
230
-                ),
231
-                'metaboxes'     => array_merge($this->_default_espresso_metaboxes, array('_publish_post_box')),
232
-                'help_tabs'     => array(
233
-                    'registration_form_add_question_help_tab' => array(
234
-                        'title'    => esc_html__('Add Question', 'event_espresso'),
235
-                        'filename' => 'registration_form_add_question',
236
-                    ),
237
-                ),
238
-                'help_tour'     => array('Registration_Form_Add_Question_Help_Tour'),
239
-                'require_nonce' => false,
240
-            ),
241
-
242
-            'add_question_group' => array(
243
-                'nav'           => array(
244
-                    'label'      => esc_html__('Add Question Group', 'event_espresso'),
245
-                    'order'      => 5,
246
-                    'persistent' => false,
247
-                ),
248
-                'metaboxes'     => array_merge($this->_default_espresso_metaboxes, array('_publish_post_box')),
249
-                'help_tabs'     => array(
250
-                    'registration_form_add_question_group_help_tab' => array(
251
-                        'title'    => esc_html__('Add Question Group', 'event_espresso'),
252
-                        'filename' => 'registration_form_add_question_group',
253
-                    ),
254
-                ),
255
-                'help_tour'     => array('Registration_Form_Add_Question_Group_Help_Tour'),
256
-                'require_nonce' => false,
257
-            ),
258
-
259
-            'edit_question_group' => array(
260
-                'nav'           => array(
261
-                    'label'      => esc_html__('Edit Question Group', 'event_espresso'),
262
-                    'order'      => 5,
263
-                    'persistent' => false,
264
-                    'url'        => isset($this->_req_data['question_group_id']) ? add_query_arg(
265
-                        array('question_group_id' => $this->_req_data['question_group_id']),
266
-                        $this->_current_page_view_url
267
-                    ) : $this->_admin_base_url,
268
-                ),
269
-                'metaboxes'     => array_merge($this->_default_espresso_metaboxes, array('_publish_post_box')),
270
-                'help_tabs'     => array(
271
-                    'registration_form_edit_question_group_help_tab' => array(
272
-                        'title'    => esc_html__('Edit Question Group', 'event_espresso'),
273
-                        'filename' => 'registration_form_edit_question_group',
274
-                    ),
275
-                ),
276
-                'help_tour'     => array('Registration_Form_Edit_Question_Group_Help_Tour'),
277
-                'require_nonce' => false,
278
-            ),
279
-
280
-            'view_reg_form_settings' => array(
281
-                'nav'           => array(
282
-                    'label' => esc_html__('Reg Form Settings', 'event_espresso'),
283
-                    'order' => 40,
284
-                ),
285
-                'labels'        => array(
286
-                    'publishbox' => esc_html__('Update Settings', 'event_espresso'),
287
-                ),
288
-                'metaboxes'     => array_merge($this->_default_espresso_metaboxes, array('_publish_post_box')),
289
-                'help_tabs'     => array(
290
-                    'registration_form_reg_form_settings_help_tab' => array(
291
-                        'title'    => esc_html__('Registration Form Settings', 'event_espresso'),
292
-                        'filename' => 'registration_form_reg_form_settings',
293
-                    ),
294
-                ),
295
-                'help_tour'     => array('Registration_Form_Settings_Help_Tour'),
296
-                'require_nonce' => false,
297
-            ),
298
-
299
-        );
300
-        $this->_page_config = array_merge($this->_page_config, $new_page_config);
301
-
302
-        // change the list table we're going to use so it's the NEW list table!
303
-        $this->_page_config['default']['list_table'] = 'Extend_Registration_Form_Questions_Admin_List_Table';
304
-
305
-
306
-        // additional labels
307
-        $new_labels = array(
308
-            'add_question'          => esc_html__('Add New Question', 'event_espresso'),
309
-            'delete_question'       => esc_html__('Delete Question', 'event_espresso'),
310
-            'add_question_group'    => esc_html__('Add New Question Group', 'event_espresso'),
311
-            'edit_question_group'   => esc_html__('Edit Question Group', 'event_espresso'),
312
-            'delete_question_group' => esc_html__('Delete Question Group', 'event_espresso'),
313
-        );
314
-        $this->_labels['buttons'] = array_merge($this->_labels['buttons'], $new_labels);
315
-    }
316
-
317
-
318
-    /**
319
-     * @return void
320
-     */
321
-    protected function _ajax_hooks()
322
-    {
323
-        add_action('wp_ajax_espresso_update_question_group_order', array($this, 'update_question_group_order'));
324
-    }
325
-
326
-
327
-    /**
328
-     * @return void
329
-     */
330
-    public function load_scripts_styles_question_groups()
331
-    {
332
-        wp_enqueue_script('espresso_ajax_table_sorting');
333
-    }
334
-
335
-
336
-    /**
337
-     * @return void
338
-     */
339
-    public function load_scripts_styles_add_question_group()
340
-    {
341
-        $this->load_scripts_styles_forms();
342
-        $this->load_sortable_question_script();
343
-    }
344
-
345
-
346
-    /**
347
-     * @return void
348
-     */
349
-    public function load_scripts_styles_edit_question_group()
350
-    {
351
-        $this->load_scripts_styles_forms();
352
-        $this->load_sortable_question_script();
353
-    }
354
-
355
-
356
-    /**
357
-     * registers and enqueues script for questions
358
-     *
359
-     * @return void
360
-     */
361
-    public function load_sortable_question_script()
362
-    {
363
-        wp_register_script(
364
-            'ee-question-sortable',
365
-            REGISTRATION_FORM_CAF_ASSETS_URL . 'ee_question_order.js',
366
-            array('jquery-ui-sortable'),
367
-            EVENT_ESPRESSO_VERSION,
368
-            true
369
-        );
370
-        wp_enqueue_script('ee-question-sortable');
371
-    }
372
-
373
-
374
-    /**
375
-     * @return void
376
-     */
377
-    protected function _set_list_table_views_default()
378
-    {
379
-        $this->_views = array(
380
-            'all' => array(
381
-                'slug'        => 'all',
382
-                'label'       => esc_html__('View All Questions', 'event_espresso'),
383
-                'count'       => 0,
384
-                'bulk_action' => array(
385
-                    'trash_questions' => esc_html__('Trash', 'event_espresso'),
386
-                ),
387
-            ),
388
-        );
389
-
390
-        if (EE_Registry::instance()->CAP->current_user_can(
391
-            'ee_delete_questions',
392
-            'espresso_registration_form_trash_questions'
393
-        )
394
-        ) {
395
-            $this->_views['trash'] = array(
396
-                'slug'        => 'trash',
397
-                'label'       => esc_html__('Trash', 'event_espresso'),
398
-                'count'       => 0,
399
-                'bulk_action' => array(
400
-                    'delete_questions'  => esc_html__('Delete Permanently', 'event_espresso'),
401
-                    'restore_questions' => esc_html__('Restore', 'event_espresso'),
402
-                ),
403
-            );
404
-        }
405
-    }
406
-
407
-
408
-    /**
409
-     * @return void
410
-     */
411
-    protected function _set_list_table_views_question_groups()
412
-    {
413
-        $this->_views = array(
414
-            'all' => array(
415
-                'slug'        => 'all',
416
-                'label'       => esc_html__('All', 'event_espresso'),
417
-                'count'       => 0,
418
-                'bulk_action' => array(
419
-                    'trash_question_groups' => esc_html__('Trash', 'event_espresso'),
420
-                ),
421
-            ),
422
-        );
423
-
424
-        if (EE_Registry::instance()->CAP->current_user_can(
425
-            'ee_delete_question_groups',
426
-            'espresso_registration_form_trash_question_groups'
427
-        )
428
-        ) {
429
-            $this->_views['trash'] = array(
430
-                'slug'        => 'trash',
431
-                'label'       => esc_html__('Trash', 'event_espresso'),
432
-                'count'       => 0,
433
-                'bulk_action' => array(
434
-                    'delete_question_groups'  => esc_html__('Delete Permanently', 'event_espresso'),
435
-                    'restore_question_groups' => esc_html__('Restore', 'event_espresso'),
436
-                ),
437
-            );
438
-        }
439
-    }
440
-
441
-
442
-    /**
443
-     * @return void
444
-     * @throws EE_Error
445
-     * @throws InvalidArgumentException
446
-     * @throws InvalidDataTypeException
447
-     * @throws InvalidInterfaceException
448
-     */
449
-    protected function _questions_overview_list_table()
450
-    {
451
-        $this->_admin_page_title .= ' ' . $this->get_action_link_or_button(
452
-            'add_question',
453
-            'add_question',
454
-            array(),
455
-            'add-new-h2'
456
-        );
457
-        parent::_questions_overview_list_table();
458
-    }
459
-
460
-
461
-    /**
462
-     * @return void
463
-     * @throws DomainException
464
-     * @throws EE_Error
465
-     * @throws InvalidArgumentException
466
-     * @throws InvalidDataTypeException
467
-     * @throws InvalidInterfaceException
468
-     */
469
-    protected function _question_groups_overview_list_table()
470
-    {
471
-        $this->_search_btn_label = esc_html__('Question Groups', 'event_espresso');
472
-        $this->_admin_page_title .= ' ' . $this->get_action_link_or_button(
473
-            'add_question_group',
474
-            'add_question_group',
475
-            array(),
476
-            'add-new-h2'
477
-        );
478
-        $this->display_admin_list_table_page_with_sidebar();
479
-    }
480
-
481
-
482
-    /**
483
-     * @return void
484
-     * @throws EE_Error
485
-     * @throws InvalidArgumentException
486
-     * @throws InvalidDataTypeException
487
-     * @throws InvalidInterfaceException
488
-     */
489
-    protected function _delete_question()
490
-    {
491
-        $success = $this->_delete_items($this->_question_model);
492
-        $this->_redirect_after_action(
493
-            $success,
494
-            $this->_question_model->item_name($success),
495
-            'deleted',
496
-            array('action' => 'default', 'status' => 'all')
497
-        );
498
-    }
499
-
500
-
501
-    /**
502
-     * @return void
503
-     * @throws EE_Error
504
-     * @throws InvalidArgumentException
505
-     * @throws InvalidDataTypeException
506
-     * @throws InvalidInterfaceException
507
-     */
508
-    protected function _delete_questions()
509
-    {
510
-        $success = $this->_delete_items($this->_question_model);
511
-        $this->_redirect_after_action(
512
-            $success,
513
-            $this->_question_model->item_name($success),
514
-            'deleted permanently',
515
-            array('action' => 'default', 'status' => 'trash')
516
-        );
517
-    }
518
-
519
-
520
-    /**
521
-     * Performs the deletion of a single or multiple questions or question groups.
522
-     *
523
-     * @param EEM_Soft_Delete_Base $model
524
-     * @return int number of items deleted permanently
525
-     * @throws EE_Error
526
-     * @throws InvalidArgumentException
527
-     * @throws InvalidDataTypeException
528
-     * @throws InvalidInterfaceException
529
-     */
530
-    private function _delete_items(EEM_Soft_Delete_Base $model)
531
-    {
532
-        $success = 0;
533
-        do_action('AHEE_log', __FILE__, __FUNCTION__, '');
534
-        if (! empty($this->_req_data['checkbox']) && is_array($this->_req_data['checkbox'])) {
535
-            // if array has more than one element than success message should be plural
536
-            $success = count($this->_req_data['checkbox']) > 1 ? 2 : 1;
537
-            // cycle thru bulk action checkboxes
538
-            while (list($ID, $value) = each($this->_req_data['checkbox'])) {
539
-                if (! $this->_delete_item($ID, $model)) {
540
-                    $success = 0;
541
-                }
542
-            }
543
-        } elseif (! empty($this->_req_data['QSG_ID'])) {
544
-            $success = $this->_delete_item($this->_req_data['QSG_ID'], $model);
545
-        } elseif (! empty($this->_req_data['QST_ID'])) {
546
-            $success = $this->_delete_item($this->_req_data['QST_ID'], $model);
547
-        } else {
548
-            EE_Error::add_error(
549
-                sprintf(
550
-                    esc_html__(
551
-                        "No Questions or Question Groups were selected for deleting. This error usually shows when you've attempted to delete via bulk action but there were no selections.",
552
-                        "event_espresso"
553
-                    )
554
-                ),
555
-                __FILE__,
556
-                __FUNCTION__,
557
-                __LINE__
558
-            );
559
-        }
560
-        return $success;
561
-    }
562
-
563
-
564
-    /**
565
-     * Deletes the specified question (and its associated question options) or question group
566
-     *
567
-     * @param int                  $id
568
-     * @param EEM_Soft_Delete_Base $model
569
-     * @return boolean
570
-     * @throws EE_Error
571
-     * @throws InvalidArgumentException
572
-     * @throws InvalidDataTypeException
573
-     * @throws InvalidInterfaceException
574
-     */
575
-    protected function _delete_item($id, $model)
576
-    {
577
-        if ($model instanceof EEM_Question) {
578
-            EEM_Question_Option::instance()->delete_permanently(array(array('QST_ID' => absint($id))));
579
-        }
580
-        return $model->delete_permanently_by_ID(absint($id));
581
-    }
582
-
583
-
584
-    /******************************    QUESTION GROUPS    ******************************/
585
-
586
-
587
-    /**
588
-     * @param string $type
589
-     * @return void
590
-     * @throws DomainException
591
-     * @throws EE_Error
592
-     * @throws InvalidArgumentException
593
-     * @throws InvalidDataTypeException
594
-     * @throws InvalidInterfaceException
595
-     */
596
-    protected function _edit_question_group($type = 'add')
597
-    {
598
-        do_action('AHEE_log', __FILE__, __FUNCTION__, '');
599
-        $ID = isset($this->_req_data['QSG_ID']) && ! empty($this->_req_data['QSG_ID'])
600
-            ? absint($this->_req_data['QSG_ID'])
601
-            : false;
602
-
603
-        switch ($this->_req_action) {
604
-            case 'add_question_group':
605
-                $this->_admin_page_title = esc_html__('Add Question Group', 'event_espresso');
606
-                break;
607
-            case 'edit_question_group':
608
-                $this->_admin_page_title = esc_html__('Edit Question Group', 'event_espresso');
609
-                break;
610
-            default:
611
-                $this->_admin_page_title = ucwords(str_replace('_', ' ', $this->_req_action));
612
-        }
613
-        // add ID to title if editing
614
-        $this->_admin_page_title = $ID ? $this->_admin_page_title . ' # ' . $ID : $this->_admin_page_title;
615
-        if ($ID) {
616
-            /** @var EE_Question_Group $questionGroup */
617
-            $questionGroup = $this->_question_group_model->get_one_by_ID($ID);
618
-            $additional_hidden_fields = array('QSG_ID' => array('type' => 'hidden', 'value' => $ID));
619
-            $this->_set_add_edit_form_tags('update_question_group', $additional_hidden_fields);
620
-        } else {
621
-            /** @var EE_Question_Group $questionGroup */
622
-            $questionGroup = EEM_Question_Group::instance()->create_default_object();
623
-            $questionGroup->set_order_to_latest();
624
-            $this->_set_add_edit_form_tags('insert_question_group');
625
-        }
626
-        $this->_template_args['values'] = $this->_yes_no_values;
627
-        $this->_template_args['all_questions'] = $questionGroup->questions_in_and_not_in_group();
628
-        $this->_template_args['QSG_ID'] = $ID ? $ID : true;
629
-        $this->_template_args['question_group'] = $questionGroup;
630
-
631
-        $redirect_URL = add_query_arg(array('action' => 'question_groups'), $this->_admin_base_url);
632
-        $this->_set_publish_post_box_vars('id', $ID, false, $redirect_URL);
633
-        $this->_template_args['admin_page_content'] = EEH_Template::display_template(
634
-            REGISTRATION_FORM_CAF_TEMPLATE_PATH . 'question_groups_main_meta_box.template.php',
635
-            $this->_template_args,
636
-            true
637
-        );
638
-
639
-        // the details template wrapper
640
-        $this->display_admin_page_with_sidebar();
641
-    }
642
-
643
-
644
-    /**
645
-     * @return void
646
-     * @throws EE_Error
647
-     * @throws InvalidArgumentException
648
-     * @throws InvalidDataTypeException
649
-     * @throws InvalidInterfaceException
650
-     */
651
-    protected function _delete_question_groups()
652
-    {
653
-        $success = $this->_delete_items($this->_question_group_model);
654
-        $this->_redirect_after_action(
655
-            $success,
656
-            $this->_question_group_model->item_name($success),
657
-            'deleted permanently',
658
-            array('action' => 'question_groups', 'status' => 'trash')
659
-        );
660
-    }
661
-
662
-
663
-    /**
664
-     * @param bool $new_question_group
665
-     * @throws EE_Error
666
-     * @throws InvalidArgumentException
667
-     * @throws InvalidDataTypeException
668
-     * @throws InvalidInterfaceException
669
-     */
670
-    protected function _insert_or_update_question_group($new_question_group = true)
671
-    {
672
-        do_action('AHEE_log', __FILE__, __FUNCTION__, '');
673
-        $set_column_values = $this->_set_column_values_for($this->_question_group_model);
674
-        if ($new_question_group) {
675
-            $QSG_ID = $this->_question_group_model->insert($set_column_values);
676
-            $success = $QSG_ID ? 1 : 0;
677
-        } else {
678
-            $QSG_ID = absint($this->_req_data['QSG_ID']);
679
-            unset($set_column_values['QSG_ID']);
680
-            $success = $this->_question_group_model->update($set_column_values, array(array('QSG_ID' => $QSG_ID)));
681
-        }
682
-        $phone_question_id = EEM_Question::instance()->get_Question_ID_from_system_string(
683
-            EEM_Attendee::system_question_phone
684
-        );
685
-        // update the existing related questions
686
-        // BUT FIRST...  delete the phone question from the Question_Group_Question
687
-        // if it is being added to this question group (therefore removed from the existing group)
688
-        if (isset($this->_req_data['questions'], $this->_req_data['questions'][ $phone_question_id ])) {
689
-            // delete where QST ID = system phone question ID and Question Group ID is NOT this group
690
-            EEM_Question_Group_Question::instance()->delete(
691
-                array(
692
-                    array(
693
-                        'QST_ID' => $phone_question_id,
694
-                        'QSG_ID' => array('!=', $QSG_ID),
695
-                    ),
696
-                )
697
-            );
698
-        }
699
-        /** @type EE_Question_Group $question_group */
700
-        $question_group = $this->_question_group_model->get_one_by_ID($QSG_ID);
701
-        $questions = $question_group->questions();
702
-        // make sure system phone question is added to list of questions for this group
703
-        if (! isset($questions[ $phone_question_id ])) {
704
-            $questions[ $phone_question_id ] = EEM_Question::instance()->get_one_by_ID($phone_question_id);
705
-        }
706
-
707
-        foreach ($questions as $question_ID => $question) {
708
-            // first we always check for order.
709
-            if (! empty($this->_req_data['question_orders'][ $question_ID ])) {
710
-                // update question order
711
-                $question_group->update_question_order(
712
-                    $question_ID,
713
-                    $this->_req_data['question_orders'][ $question_ID ]
714
-                );
715
-            }
716
-
717
-            // then we always check if adding or removing.
718
-            if (isset($this->_req_data['questions'], $this->_req_data['questions'][ $question_ID ])) {
719
-                $question_group->add_question($question_ID);
720
-            } else {
721
-                // not found, remove it (but only if not a system question for the personal group
722
-                // with the exception of lname system question - we allow removal of it)
723
-                if (in_array(
724
-                    $question->system_ID(),
725
-                    EEM_Question::instance()->required_system_questions_in_system_question_group(
726
-                        $question_group->system_group()
727
-                    )
728
-                )) {
729
-                    continue;
730
-                } else {
731
-                    $question_group->remove_question($question_ID);
732
-                }
733
-            }
734
-        }
735
-        // save new related questions
736
-        if (isset($this->_req_data['questions'])) {
737
-            foreach ($this->_req_data['questions'] as $QST_ID) {
738
-                $question_group->add_question($QST_ID);
739
-                if (isset($this->_req_data['question_orders'][ $QST_ID ])) {
740
-                    $question_group->update_question_order($QST_ID, $this->_req_data['question_orders'][ $QST_ID ]);
741
-                }
742
-            }
743
-        }
744
-
745
-        if ($success !== false) {
746
-            $msg = $new_question_group
747
-                ? sprintf(
748
-                    esc_html__('The %s has been created', 'event_espresso'),
749
-                    $this->_question_group_model->item_name()
750
-                )
751
-                : sprintf(
752
-                    esc_html__(
753
-                        'The %s has been updated',
754
-                        'event_espresso'
755
-                    ),
756
-                    $this->_question_group_model->item_name()
757
-                );
758
-            EE_Error::add_success($msg);
759
-        }
760
-        $this->_redirect_after_action(
761
-            false,
762
-            '',
763
-            '',
764
-            array('action' => 'edit_question_group', 'QSG_ID' => $QSG_ID),
765
-            true
766
-        );
767
-    }
768
-
769
-
770
-    /**
771
-     * duplicates a question and all its question options and redirects to the new question.
772
-     *
773
-     * @return void
774
-     * @throws EE_Error
775
-     * @throws InvalidArgumentException
776
-     * @throws ReflectionException
777
-     * @throws InvalidDataTypeException
778
-     * @throws InvalidInterfaceException
779
-     */
780
-    public function _duplicate_question()
781
-    {
782
-        $question_ID = (int) $this->_req_data['QST_ID'];
783
-        $question = EEM_Question::instance()->get_one_by_ID($question_ID);
784
-        if ($question instanceof EE_Question) {
785
-            $new_question = $question->duplicate();
786
-            if ($new_question instanceof EE_Question) {
787
-                $this->_redirect_after_action(
788
-                    true,
789
-                    esc_html__('Question', 'event_espresso'),
790
-                    esc_html__('Duplicated', 'event_espresso'),
791
-                    array('action' => 'edit_question', 'QST_ID' => $new_question->ID()),
792
-                    true
793
-                );
794
-            } else {
795
-                global $wpdb;
796
-                EE_Error::add_error(
797
-                    sprintf(
798
-                        esc_html__(
799
-                            'Could not duplicate question with ID %1$d because: %2$s',
800
-                            'event_espresso'
801
-                        ),
802
-                        $question_ID,
803
-                        $wpdb->last_error
804
-                    ),
805
-                    __FILE__,
806
-                    __FUNCTION__,
807
-                    __LINE__
808
-                );
809
-                $this->_redirect_after_action(false, '', '', array('action' => 'default'), false);
810
-            }
811
-        } else {
812
-            EE_Error::add_error(
813
-                sprintf(
814
-                    esc_html__(
815
-                        'Could not duplicate question with ID %d because it didn\'t exist!',
816
-                        'event_espresso'
817
-                    ),
818
-                    $question_ID
819
-                ),
820
-                __FILE__,
821
-                __FUNCTION__,
822
-                __LINE__
823
-            );
824
-            $this->_redirect_after_action(false, '', '', array('action' => 'default'), false);
825
-        }
826
-    }
827
-
828
-
829
-    /**
830
-     * @param bool $trash
831
-     * @throws EE_Error
832
-     */
833
-    protected function _trash_or_restore_question_groups($trash = true)
834
-    {
835
-        $this->_trash_or_restore_items($this->_question_group_model, $trash);
836
-    }
837
-
838
-
839
-    /**
840
-     *_trash_question
841
-     *
842
-     * @return void
843
-     * @throws EE_Error
844
-     */
845
-    protected function _trash_question()
846
-    {
847
-        $success = $this->_question_model->delete_by_ID((int) $this->_req_data['QST_ID']);
848
-        $query_args = array('action' => 'default', 'status' => 'all');
849
-        $this->_redirect_after_action($success, $this->_question_model->item_name($success), 'trashed', $query_args);
850
-    }
851
-
852
-
853
-    /**
854
-     * @param bool $trash
855
-     * @throws EE_Error
856
-     */
857
-    protected function _trash_or_restore_questions($trash = true)
858
-    {
859
-        $this->_trash_or_restore_items($this->_question_model, $trash);
860
-    }
861
-
862
-
863
-    /**
864
-     * Internally used to delete or restore items, using the request data. Meant to be
865
-     * flexible between question or question groups
866
-     *
867
-     * @param EEM_Soft_Delete_Base $model
868
-     * @param boolean              $trash whether to trash or restore
869
-     * @throws EE_Error
870
-     */
871
-    private function _trash_or_restore_items(EEM_Soft_Delete_Base $model, $trash = true)
872
-    {
873
-
874
-        do_action('AHEE_log', __FILE__, __FUNCTION__, '');
875
-
876
-        $success = 1;
877
-        // Checkboxes
878
-        // echo "trash $trash";
879
-        // var_dump($this->_req_data['checkbox']);die;
880
-        if (isset($this->_req_data['checkbox'])) {
881
-            if (! empty($this->_req_data['checkbox']) && is_array($this->_req_data['checkbox'])) {
882
-                // if array has more than one element than success message should be plural
883
-                $success = count($this->_req_data['checkbox']) > 1 ? 2 : 1;
884
-                // cycle thru bulk action checkboxes
885
-                while (list($ID, $value) = each($this->_req_data['checkbox'])) {
886
-                    if (! $model->delete_or_restore_by_ID($trash, absint($ID))) {
887
-                        $success = 0;
888
-                    }
889
-                }
890
-            } else {
891
-                // grab single id and delete
892
-                $ID = absint($this->_req_data['checkbox']);
893
-                if (! $model->delete_or_restore_by_ID($trash, $ID)) {
894
-                    $success = 0;
895
-                }
896
-            }
897
-        } else {
898
-            // delete via trash link
899
-            // grab single id and delete
900
-            $ID = absint($this->_req_data[ $model->primary_key_name() ]);
901
-            if (! $model->delete_or_restore_by_ID($trash, $ID)) {
902
-                $success = 0;
903
-            }
904
-        }
905
-
906
-
907
-        $action = $model instanceof EEM_Question ? 'default' : 'question_groups';// strtolower( $model->item_name(2) );
908
-        // echo "action :$action";
909
-        // $action = 'questions' ? 'default' : $action;
910
-        if ($trash) {
911
-            $action_desc = 'trashed';
912
-            $status = 'trash';
913
-        } else {
914
-            $action_desc = 'restored';
915
-            $status = 'all';
916
-        }
917
-        $this->_redirect_after_action(
918
-            $success,
919
-            $model->item_name($success),
920
-            $action_desc,
921
-            array('action' => $action, 'status' => $status)
922
-        );
923
-    }
924
-
925
-
926
-    /**
927
-     * @param            $per_page
928
-     * @param int        $current_page
929
-     * @param bool|false $count
930
-     * @return EE_Soft_Delete_Base_Class[]|int
931
-     * @throws EE_Error
932
-     * @throws InvalidArgumentException
933
-     * @throws InvalidDataTypeException
934
-     * @throws InvalidInterfaceException
935
-     */
936
-    public function get_trashed_questions($per_page, $current_page = 1, $count = false)
937
-    {
938
-        $query_params = $this->get_query_params(EEM_Question::instance(), $per_page, $current_page);
939
-
940
-        if ($count) {
941
-            // note: this a subclass of EEM_Soft_Delete_Base, so this is actually only getting non-trashed items
942
-            $where = isset($query_params[0]) ? array($query_params[0]) : array();
943
-            $results = $this->_question_model->count_deleted($where);
944
-        } else {
945
-            // note: this a subclass of EEM_Soft_Delete_Base, so this is actually only getting non-trashed items
946
-            $results = $this->_question_model->get_all_deleted($query_params);
947
-        }
948
-        return $results;
949
-    }
950
-
951
-
952
-    /**
953
-     * @param            $per_page
954
-     * @param int        $current_page
955
-     * @param bool|false $count
956
-     * @return EE_Soft_Delete_Base_Class[]|int
957
-     * @throws EE_Error
958
-     * @throws InvalidArgumentException
959
-     * @throws InvalidDataTypeException
960
-     * @throws InvalidInterfaceException
961
-     */
962
-    public function get_question_groups($per_page, $current_page = 1, $count = false)
963
-    {
964
-        $questionGroupModel = EEM_Question_Group::instance();
965
-        $query_params = $this->get_query_params($questionGroupModel, $per_page, $current_page);
966
-        if ($count) {
967
-            $where = isset($query_params[0]) ? array($query_params[0]) : array();
968
-            $results = $questionGroupModel->count($where);
969
-        } else {
970
-            $results = $questionGroupModel->get_all($query_params);
971
-        }
972
-        return $results;
973
-    }
974
-
975
-
976
-    /**
977
-     * @param      $per_page
978
-     * @param int  $current_page
979
-     * @param bool $count
980
-     * @return EE_Soft_Delete_Base_Class[]|int
981
-     * @throws EE_Error
982
-     * @throws InvalidArgumentException
983
-     * @throws InvalidDataTypeException
984
-     * @throws InvalidInterfaceException
985
-     */
986
-    public function get_trashed_question_groups($per_page, $current_page = 1, $count = false)
987
-    {
988
-        $questionGroupModel = EEM_Question_Group::instance();
989
-        $query_params = $this->get_query_params($questionGroupModel, $per_page, $current_page);
990
-        if ($count) {
991
-            $where = isset($query_params[0]) ? array($query_params[0]) : array();
992
-            $query_params['limit'] = null;
993
-            $results = $questionGroupModel->count_deleted($where);
994
-        } else {
995
-            $results = $questionGroupModel->get_all_deleted($query_params);
996
-        }
997
-        return $results;
998
-    }
999
-
1000
-
1001
-    /**
1002
-     * method for performing updates to question order
1003
-     *
1004
-     * @return void results array
1005
-     * @throws EE_Error
1006
-     * @throws InvalidArgumentException
1007
-     * @throws InvalidDataTypeException
1008
-     * @throws InvalidInterfaceException
1009
-     */
1010
-    public function update_question_group_order()
1011
-    {
1012
-
1013
-        $success = esc_html__('Question group order was updated successfully.', 'event_espresso');
1014
-
1015
-        // grab our row IDs
1016
-        $row_ids = isset($this->_req_data['row_ids']) && ! empty($this->_req_data['row_ids'])
1017
-            ? explode(',', rtrim($this->_req_data['row_ids'], ','))
1018
-            : array();
1019
-
1020
-        $perpage = ! empty($this->_req_data['perpage'])
1021
-            ? (int) $this->_req_data['perpage']
1022
-            : null;
1023
-        $curpage = ! empty($this->_req_data['curpage'])
1024
-            ? (int) $this->_req_data['curpage']
1025
-            : null;
1026
-
1027
-        if (! empty($row_ids)) {
1028
-            // figure out where we start the row_id count at for the current page.
1029
-            $qsgcount = empty($curpage) ? 0 : ($curpage - 1) * $perpage;
1030
-
1031
-            $row_count = count($row_ids);
1032
-            for ($i = 0; $i < $row_count; $i++) {
1033
-                // Update the questions when re-ordering
1034
-                $updated = EEM_Question_Group::instance()->update(
1035
-                    array('QSG_order' => $qsgcount),
1036
-                    array(array('QSG_ID' => $row_ids[ $i ]))
1037
-                );
1038
-                if ($updated === false) {
1039
-                    $success = false;
1040
-                }
1041
-                $qsgcount++;
1042
-            }
1043
-        } else {
1044
-            $success = false;
1045
-        }
1046
-
1047
-        $errors = ! $success
1048
-            ? esc_html__('An error occurred. The question group order was not updated.', 'event_espresso')
1049
-            : false;
1050
-
1051
-        echo wp_json_encode(array('return_data' => false, 'success' => $success, 'errors' => $errors));
1052
-        die();
1053
-    }
1054
-
1055
-
1056
-
1057
-    /***************************************       REGISTRATION SETTINGS       ***************************************/
1058
-
1059
-
1060
-    /**
1061
-     * @throws DomainException
1062
-     * @throws EE_Error
1063
-     * @throws InvalidArgumentException
1064
-     * @throws InvalidDataTypeException
1065
-     * @throws InvalidInterfaceException
1066
-     */
1067
-    protected function _reg_form_settings()
1068
-    {
1069
-        $this->_template_args['values'] = $this->_yes_no_values;
1070
-        add_action(
1071
-            'AHEE__Extend_Registration_Form_Admin_Page___reg_form_settings_template',
1072
-            array($this, 'email_validation_settings_form'),
1073
-            2
1074
-        );
1075
-        $this->_template_args = (array) apply_filters(
1076
-            'FHEE__Extend_Registration_Form_Admin_Page___reg_form_settings___template_args',
1077
-            $this->_template_args
1078
-        );
1079
-        $this->_set_add_edit_form_tags('update_reg_form_settings');
1080
-        $this->_set_publish_post_box_vars(null, false, false, null, false);
1081
-        $this->_template_args['admin_page_content'] = EEH_Template::display_template(
1082
-            REGISTRATION_FORM_CAF_TEMPLATE_PATH . 'reg_form_settings.template.php',
1083
-            $this->_template_args,
1084
-            true
1085
-        );
1086
-        $this->display_admin_page_with_sidebar();
1087
-    }
1088
-
1089
-
1090
-    /**
1091
-     * @return void
1092
-     * @throws EE_Error
1093
-     * @throws InvalidArgumentException
1094
-     * @throws ReflectionException
1095
-     * @throws InvalidDataTypeException
1096
-     * @throws InvalidInterfaceException
1097
-     */
1098
-    protected function _update_reg_form_settings()
1099
-    {
1100
-        EE_Registry::instance()->CFG->registration = $this->update_email_validation_settings_form(
1101
-            EE_Registry::instance()->CFG->registration
1102
-        );
1103
-        EE_Registry::instance()->CFG->registration = apply_filters(
1104
-            'FHEE__Extend_Registration_Form_Admin_Page___update_reg_form_settings__CFG_registration',
1105
-            EE_Registry::instance()->CFG->registration
1106
-        );
1107
-        $success = $this->_update_espresso_configuration(
1108
-            esc_html__('Registration Form Options', 'event_espresso'),
1109
-            EE_Registry::instance()->CFG,
1110
-            __FILE__,
1111
-            __FUNCTION__,
1112
-            __LINE__
1113
-        );
1114
-        $this->_redirect_after_action(
1115
-            $success,
1116
-            esc_html__('Registration Form Options', 'event_espresso'),
1117
-            'updated',
1118
-            array('action' => 'view_reg_form_settings')
1119
-        );
1120
-    }
1121
-
1122
-
1123
-    /**
1124
-     * @return void
1125
-     * @throws EE_Error
1126
-     * @throws InvalidArgumentException
1127
-     * @throws InvalidDataTypeException
1128
-     * @throws InvalidInterfaceException
1129
-     */
1130
-    public function email_validation_settings_form()
1131
-    {
1132
-        echo $this->_email_validation_settings_form()->get_html();
1133
-    }
1134
-
1135
-
1136
-    /**
1137
-     * _email_validation_settings_form
1138
-     *
1139
-     * @access protected
1140
-     * @return EE_Form_Section_Proper
1141
-     * @throws \EE_Error
1142
-     */
1143
-    protected function _email_validation_settings_form()
1144
-    {
1145
-        return new EE_Form_Section_Proper(
1146
-            array(
1147
-                'name'            => 'email_validation_settings',
1148
-                'html_id'         => 'email_validation_settings',
1149
-                'layout_strategy' => new EE_Admin_Two_Column_Layout(),
1150
-                'subsections'     => apply_filters(
1151
-                    'FHEE__Extend_Registration_Form_Admin_Page___email_validation_settings_form__form_subsections',
1152
-                    array(
1153
-                        'email_validation_hdr'   => new EE_Form_Section_HTML(
1154
-                            EEH_HTML::h2(esc_html__('Email Validation Settings', 'event_espresso'))
1155
-                        ),
1156
-                        'email_validation_level' => new EE_Select_Input(
1157
-                            array(
1158
-                                'basic'      => esc_html__('Basic', 'event_espresso'),
1159
-                                'wp_default' => esc_html__('WordPress Default', 'event_espresso'),
1160
-                                'i18n'       => esc_html__('International', 'event_espresso'),
1161
-                                'i18n_dns'   => esc_html__('International + DNS Check', 'event_espresso'),
1162
-                            ),
1163
-                            array(
1164
-                                'html_label_text' => esc_html__('Email Validation Level', 'event_espresso')
1165
-                                                     . EEH_Template::get_help_tab_link('email_validation_info'),
1166
-                                'html_help_text'  => esc_html__(
1167
-                                    'These levels range from basic validation ( ie: [email protected] ) to more advanced checks against international email addresses (ie: üñîçøðé@example.com ) with additional MX and A record checks to confirm the domain actually exists. More information on on each level can be found within the help section.',
1168
-                                    'event_espresso'
1169
-                                ),
1170
-                                'default'         => isset(
1171
-                                    EE_Registry::instance()->CFG->registration->email_validation_level
1172
-                                )
1173
-                                    ? EE_Registry::instance()->CFG->registration->email_validation_level
1174
-                                    : 'wp_default',
1175
-                                'required'        => false,
1176
-                            )
1177
-                        ),
1178
-                    )
1179
-                ),
1180
-            )
1181
-        );
1182
-    }
1183
-
1184
-
1185
-    /**
1186
-     * @param EE_Registration_Config $EE_Registration_Config
1187
-     * @return EE_Registration_Config
1188
-     * @throws EE_Error
1189
-     * @throws InvalidArgumentException
1190
-     * @throws ReflectionException
1191
-     * @throws InvalidDataTypeException
1192
-     * @throws InvalidInterfaceException
1193
-     */
1194
-    public function update_email_validation_settings_form(EE_Registration_Config $EE_Registration_Config)
1195
-    {
1196
-        $prev_email_validation_level = $EE_Registration_Config->email_validation_level;
1197
-        try {
1198
-            $email_validation_settings_form = $this->_email_validation_settings_form();
1199
-            // if not displaying a form, then check for form submission
1200
-            if ($email_validation_settings_form->was_submitted()) {
1201
-                // capture form data
1202
-                $email_validation_settings_form->receive_form_submission();
1203
-                // validate form data
1204
-                if ($email_validation_settings_form->is_valid()) {
1205
-                    // grab validated data from form
1206
-                    $valid_data = $email_validation_settings_form->valid_data();
1207
-                    if (isset($valid_data['email_validation_level'])) {
1208
-                        $email_validation_level = $valid_data['email_validation_level'];
1209
-                        // now if they want to use international email addresses
1210
-                        if ($email_validation_level === 'i18n' || $email_validation_level === 'i18n_dns') {
1211
-                            // in case we need to reset their email validation level,
1212
-                            // make sure that the previous value wasn't already set to one of the i18n options.
1213
-                            if ($prev_email_validation_level === 'i18n' || $prev_email_validation_level === 'i18n_dns') {
1214
-                                // if so, then reset it back to "basic" since that is the only other option that,
1215
-                                // despite offering poor validation, supports i18n email addresses
1216
-                                $prev_email_validation_level = 'basic';
1217
-                            }
1218
-                            // confirm our i18n email validation will work on the server
1219
-                            if (! $this->_verify_pcre_support($EE_Registration_Config, $email_validation_level)) {
1220
-                                // or reset email validation level to previous value
1221
-                                $email_validation_level = $prev_email_validation_level;
1222
-                            }
1223
-                        }
1224
-                        $EE_Registration_Config->email_validation_level = $email_validation_level;
1225
-                    } else {
1226
-                        EE_Error::add_error(
1227
-                            esc_html__(
1228
-                                'Invalid or missing Email Validation settings. Please refresh the form and try again.',
1229
-                                'event_espresso'
1230
-                            ),
1231
-                            __FILE__,
1232
-                            __FUNCTION__,
1233
-                            __LINE__
1234
-                        );
1235
-                    }
1236
-                } else {
1237
-                    if ($email_validation_settings_form->submission_error_message() !== '') {
1238
-                        EE_Error::add_error(
1239
-                            $email_validation_settings_form->submission_error_message(),
1240
-                            __FILE__,
1241
-                            __FUNCTION__,
1242
-                            __LINE__
1243
-                        );
1244
-                    }
1245
-                }
1246
-            }
1247
-        } catch (EE_Error $e) {
1248
-            $e->get_error();
1249
-        }
1250
-        return $EE_Registration_Config;
1251
-    }
1252
-
1253
-
1254
-    /**
1255
-     * confirms that the server's PHP version has the PCRE module enabled,
1256
-     * and that the PCRE version works with our i18n email validation
1257
-     *
1258
-     * @param EE_Registration_Config $EE_Registration_Config
1259
-     * @param string                 $email_validation_level
1260
-     * @return bool
1261
-     */
1262
-    private function _verify_pcre_support(EE_Registration_Config $EE_Registration_Config, $email_validation_level)
1263
-    {
1264
-        // first check that PCRE is enabled
1265
-        if (! defined('PREG_BAD_UTF8_ERROR')) {
1266
-            EE_Error::add_error(
1267
-                sprintf(
1268
-                    esc_html__(
1269
-                        'We\'re sorry, but it appears that your server\'s version of PHP was not compiled with PCRE unicode support.%1$sPlease contact your hosting company and ask them whether the PCRE compiled with your version of PHP on your server can be been built with the "--enable-unicode-properties" and "--enable-utf8" configuration switches to enable more complex regex expressions.%1$sIf they are unable, or unwilling to do so, then your server will not support international email addresses using UTF-8 unicode characters. This means you will either have to lower your email validation level to "Basic" or "WordPress Default", or switch to a hosting company that has/can enable PCRE unicode support on the server.',
1270
-                        'event_espresso'
1271
-                    ),
1272
-                    '<br />'
1273
-                ),
1274
-                __FILE__,
1275
-                __FUNCTION__,
1276
-                __LINE__
1277
-            );
1278
-            return false;
1279
-        } else {
1280
-            // PCRE support is enabled, but let's still
1281
-            // perform a test to see if the server will support it.
1282
-            // but first, save the updated validation level to the config,
1283
-            // so that the validation strategy picks it up.
1284
-            // this will get bumped back down if it doesn't work
1285
-            $EE_Registration_Config->email_validation_level = $email_validation_level;
1286
-            try {
1287
-                $email_validator = new EE_Email_Validation_Strategy();
1288
-                $i18n_email_address = apply_filters(
1289
-                    'FHEE__Extend_Registration_Form_Admin_Page__update_email_validation_settings_form__i18n_email_address',
1290
-                    'jägerjü[email protected]'
1291
-                );
1292
-                $email_validator->validate($i18n_email_address);
1293
-            } catch (Exception $e) {
1294
-                EE_Error::add_error(
1295
-                    sprintf(
1296
-                        esc_html__(
1297
-                            'We\'re sorry, but it appears that your server\'s configuration will not support the "International" or "International + DNS Check" email validation levels.%1$sTo correct this issue, please consult with your hosting company regarding your server\'s PCRE settings.%1$sIt is recommended that your PHP version be configured to use PCRE 8.10 or newer.%1$sMore information regarding PCRE versions and installation can be found here: %2$s',
1298
-                            'event_espresso'
1299
-                        ),
1300
-                        '<br />',
1301
-                        '<a href="http://php.net/manual/en/pcre.installation.php" target="_blank">http://php.net/manual/en/pcre.installation.php</a>'
1302
-                    ),
1303
-                    __FILE__,
1304
-                    __FUNCTION__,
1305
-                    __LINE__
1306
-                );
1307
-                return false;
1308
-            }
1309
-        }
1310
-        return true;
1311
-    }
17
+	/**
18
+	 * @param bool $routing indicate whether we want to just load the object and handle routing or just load the object.
19
+	 */
20
+	public function __construct($routing = true)
21
+	{
22
+		define('REGISTRATION_FORM_CAF_ADMIN', EE_CORE_CAF_ADMIN_EXTEND . 'registration_form' . DS);
23
+		define('REGISTRATION_FORM_CAF_ASSETS_PATH', REGISTRATION_FORM_CAF_ADMIN . 'assets' . DS);
24
+		define('REGISTRATION_FORM_CAF_ASSETS_URL', EE_CORE_CAF_ADMIN_EXTEND_URL . 'registration_form/assets/');
25
+		define('REGISTRATION_FORM_CAF_TEMPLATE_PATH', REGISTRATION_FORM_CAF_ADMIN . 'templates' . DS);
26
+		define('REGISTRATION_FORM_CAF_TEMPLATE_URL', EE_CORE_CAF_ADMIN_EXTEND_URL . 'registration_form/templates/');
27
+		parent::__construct($routing);
28
+	}
29
+
30
+
31
+	/**
32
+	 * @return void
33
+	 */
34
+	protected function _extend_page_config()
35
+	{
36
+		$this->_admin_base_path = REGISTRATION_FORM_CAF_ADMIN;
37
+		$qst_id = ! empty($this->_req_data['QST_ID']) && ! is_array($this->_req_data['QST_ID'])
38
+			? $this->_req_data['QST_ID'] : 0;
39
+		$qsg_id = ! empty($this->_req_data['QSG_ID']) && ! is_array($this->_req_data['QSG_ID'])
40
+			? $this->_req_data['QSG_ID'] : 0;
41
+
42
+		$new_page_routes = array(
43
+			'question_groups'    => array(
44
+				'func'       => '_question_groups_overview_list_table',
45
+				'capability' => 'ee_read_question_groups',
46
+			),
47
+			'add_question'       => array(
48
+				'func'       => '_edit_question',
49
+				'capability' => 'ee_edit_questions',
50
+			),
51
+			'insert_question'    => array(
52
+				'func'       => '_insert_or_update_question',
53
+				'args'       => array('new_question' => true),
54
+				'capability' => 'ee_edit_questions',
55
+				'noheader'   => true,
56
+			),
57
+			'duplicate_question' => array(
58
+				'func'       => '_duplicate_question',
59
+				'capability' => 'ee_edit_questions',
60
+				'noheader'   => true,
61
+			),
62
+			'trash_question'     => array(
63
+				'func'       => '_trash_question',
64
+				'capability' => 'ee_delete_question',
65
+				'obj_id'     => $qst_id,
66
+				'noheader'   => true,
67
+			),
68
+
69
+			'restore_question' => array(
70
+				'func'       => '_trash_or_restore_questions',
71
+				'capability' => 'ee_delete_question',
72
+				'obj_id'     => $qst_id,
73
+				'args'       => array('trash' => false),
74
+				'noheader'   => true,
75
+			),
76
+
77
+			'delete_question' => array(
78
+				'func'       => '_delete_question',
79
+				'capability' => 'ee_delete_question',
80
+				'obj_id'     => $qst_id,
81
+				'noheader'   => true,
82
+			),
83
+
84
+			'trash_questions' => array(
85
+				'func'       => '_trash_or_restore_questions',
86
+				'capability' => 'ee_delete_questions',
87
+				'args'       => array('trash' => true),
88
+				'noheader'   => true,
89
+			),
90
+
91
+			'restore_questions' => array(
92
+				'func'       => '_trash_or_restore_questions',
93
+				'capability' => 'ee_delete_questions',
94
+				'args'       => array('trash' => false),
95
+				'noheader'   => true,
96
+			),
97
+
98
+			'delete_questions' => array(
99
+				'func'       => '_delete_questions',
100
+				'args'       => array(),
101
+				'capability' => 'ee_delete_questions',
102
+				'noheader'   => true,
103
+			),
104
+
105
+			'add_question_group' => array(
106
+				'func'       => '_edit_question_group',
107
+				'capability' => 'ee_edit_question_groups',
108
+			),
109
+
110
+			'edit_question_group' => array(
111
+				'func'       => '_edit_question_group',
112
+				'capability' => 'ee_edit_question_group',
113
+				'obj_id'     => $qsg_id,
114
+				'args'       => array('edit'),
115
+			),
116
+
117
+			'delete_question_groups' => array(
118
+				'func'       => '_delete_question_groups',
119
+				'capability' => 'ee_delete_question_groups',
120
+				'noheader'   => true,
121
+			),
122
+
123
+			'delete_question_group' => array(
124
+				'func'       => '_delete_question_groups',
125
+				'capability' => 'ee_delete_question_group',
126
+				'obj_id'     => $qsg_id,
127
+				'noheader'   => true,
128
+			),
129
+
130
+			'trash_question_group' => array(
131
+				'func'       => '_trash_or_restore_question_groups',
132
+				'args'       => array('trash' => true),
133
+				'capability' => 'ee_delete_question_group',
134
+				'obj_id'     => $qsg_id,
135
+				'noheader'   => true,
136
+			),
137
+
138
+			'restore_question_group' => array(
139
+				'func'       => '_trash_or_restore_question_groups',
140
+				'args'       => array('trash' => false),
141
+				'capability' => 'ee_delete_question_group',
142
+				'obj_id'     => $qsg_id,
143
+				'noheader'   => true,
144
+			),
145
+
146
+			'insert_question_group' => array(
147
+				'func'       => '_insert_or_update_question_group',
148
+				'args'       => array('new_question_group' => true),
149
+				'capability' => 'ee_edit_question_groups',
150
+				'noheader'   => true,
151
+			),
152
+
153
+			'update_question_group' => array(
154
+				'func'       => '_insert_or_update_question_group',
155
+				'args'       => array('new_question_group' => false),
156
+				'capability' => 'ee_edit_question_group',
157
+				'obj_id'     => $qsg_id,
158
+				'noheader'   => true,
159
+			),
160
+
161
+			'trash_question_groups' => array(
162
+				'func'       => '_trash_or_restore_question_groups',
163
+				'args'       => array('trash' => true),
164
+				'capability' => 'ee_delete_question_groups',
165
+				'noheader'   => array('trash' => false),
166
+			),
167
+
168
+			'restore_question_groups' => array(
169
+				'func'       => '_trash_or_restore_question_groups',
170
+				'args'       => array('trash' => false),
171
+				'capability' => 'ee_delete_question_groups',
172
+				'noheader'   => true,
173
+			),
174
+
175
+
176
+			'espresso_update_question_group_order' => array(
177
+				'func'       => 'update_question_group_order',
178
+				'capability' => 'ee_edit_question_groups',
179
+				'noheader'   => true,
180
+			),
181
+
182
+			'view_reg_form_settings' => array(
183
+				'func'       => '_reg_form_settings',
184
+				'capability' => 'manage_options',
185
+			),
186
+
187
+			'update_reg_form_settings' => array(
188
+				'func'       => '_update_reg_form_settings',
189
+				'capability' => 'manage_options',
190
+				'noheader'   => true,
191
+			),
192
+		);
193
+		$this->_page_routes = array_merge($this->_page_routes, $new_page_routes);
194
+
195
+		$new_page_config = array(
196
+
197
+			'question_groups' => array(
198
+				'nav'           => array(
199
+					'label' => esc_html__('Question Groups', 'event_espresso'),
200
+					'order' => 20,
201
+				),
202
+				'list_table'    => 'Registration_Form_Question_Groups_Admin_List_Table',
203
+				'help_tabs'     => array(
204
+					'registration_form_question_groups_help_tab'                           => array(
205
+						'title'    => esc_html__('Question Groups', 'event_espresso'),
206
+						'filename' => 'registration_form_question_groups',
207
+					),
208
+					'registration_form_question_groups_table_column_headings_help_tab'     => array(
209
+						'title'    => esc_html__('Question Groups Table Column Headings', 'event_espresso'),
210
+						'filename' => 'registration_form_question_groups_table_column_headings',
211
+					),
212
+					'registration_form_question_groups_views_bulk_actions_search_help_tab' => array(
213
+						'title'    => esc_html__('Question Groups Views & Bulk Actions & Search', 'event_espresso'),
214
+						'filename' => 'registration_form_question_groups_views_bulk_actions_search',
215
+					),
216
+				),
217
+				'help_tour'     => array('Registration_Form_Question_Groups_Help_Tour'),
218
+				'metaboxes'     => $this->_default_espresso_metaboxes,
219
+				'require_nonce' => false,
220
+				'qtips'         => array(
221
+					'EE_Registration_Form_Tips',
222
+				),
223
+			),
224
+
225
+			'add_question' => array(
226
+				'nav'           => array(
227
+					'label'      => esc_html__('Add Question', 'event_espresso'),
228
+					'order'      => 5,
229
+					'persistent' => false,
230
+				),
231
+				'metaboxes'     => array_merge($this->_default_espresso_metaboxes, array('_publish_post_box')),
232
+				'help_tabs'     => array(
233
+					'registration_form_add_question_help_tab' => array(
234
+						'title'    => esc_html__('Add Question', 'event_espresso'),
235
+						'filename' => 'registration_form_add_question',
236
+					),
237
+				),
238
+				'help_tour'     => array('Registration_Form_Add_Question_Help_Tour'),
239
+				'require_nonce' => false,
240
+			),
241
+
242
+			'add_question_group' => array(
243
+				'nav'           => array(
244
+					'label'      => esc_html__('Add Question Group', 'event_espresso'),
245
+					'order'      => 5,
246
+					'persistent' => false,
247
+				),
248
+				'metaboxes'     => array_merge($this->_default_espresso_metaboxes, array('_publish_post_box')),
249
+				'help_tabs'     => array(
250
+					'registration_form_add_question_group_help_tab' => array(
251
+						'title'    => esc_html__('Add Question Group', 'event_espresso'),
252
+						'filename' => 'registration_form_add_question_group',
253
+					),
254
+				),
255
+				'help_tour'     => array('Registration_Form_Add_Question_Group_Help_Tour'),
256
+				'require_nonce' => false,
257
+			),
258
+
259
+			'edit_question_group' => array(
260
+				'nav'           => array(
261
+					'label'      => esc_html__('Edit Question Group', 'event_espresso'),
262
+					'order'      => 5,
263
+					'persistent' => false,
264
+					'url'        => isset($this->_req_data['question_group_id']) ? add_query_arg(
265
+						array('question_group_id' => $this->_req_data['question_group_id']),
266
+						$this->_current_page_view_url
267
+					) : $this->_admin_base_url,
268
+				),
269
+				'metaboxes'     => array_merge($this->_default_espresso_metaboxes, array('_publish_post_box')),
270
+				'help_tabs'     => array(
271
+					'registration_form_edit_question_group_help_tab' => array(
272
+						'title'    => esc_html__('Edit Question Group', 'event_espresso'),
273
+						'filename' => 'registration_form_edit_question_group',
274
+					),
275
+				),
276
+				'help_tour'     => array('Registration_Form_Edit_Question_Group_Help_Tour'),
277
+				'require_nonce' => false,
278
+			),
279
+
280
+			'view_reg_form_settings' => array(
281
+				'nav'           => array(
282
+					'label' => esc_html__('Reg Form Settings', 'event_espresso'),
283
+					'order' => 40,
284
+				),
285
+				'labels'        => array(
286
+					'publishbox' => esc_html__('Update Settings', 'event_espresso'),
287
+				),
288
+				'metaboxes'     => array_merge($this->_default_espresso_metaboxes, array('_publish_post_box')),
289
+				'help_tabs'     => array(
290
+					'registration_form_reg_form_settings_help_tab' => array(
291
+						'title'    => esc_html__('Registration Form Settings', 'event_espresso'),
292
+						'filename' => 'registration_form_reg_form_settings',
293
+					),
294
+				),
295
+				'help_tour'     => array('Registration_Form_Settings_Help_Tour'),
296
+				'require_nonce' => false,
297
+			),
298
+
299
+		);
300
+		$this->_page_config = array_merge($this->_page_config, $new_page_config);
301
+
302
+		// change the list table we're going to use so it's the NEW list table!
303
+		$this->_page_config['default']['list_table'] = 'Extend_Registration_Form_Questions_Admin_List_Table';
304
+
305
+
306
+		// additional labels
307
+		$new_labels = array(
308
+			'add_question'          => esc_html__('Add New Question', 'event_espresso'),
309
+			'delete_question'       => esc_html__('Delete Question', 'event_espresso'),
310
+			'add_question_group'    => esc_html__('Add New Question Group', 'event_espresso'),
311
+			'edit_question_group'   => esc_html__('Edit Question Group', 'event_espresso'),
312
+			'delete_question_group' => esc_html__('Delete Question Group', 'event_espresso'),
313
+		);
314
+		$this->_labels['buttons'] = array_merge($this->_labels['buttons'], $new_labels);
315
+	}
316
+
317
+
318
+	/**
319
+	 * @return void
320
+	 */
321
+	protected function _ajax_hooks()
322
+	{
323
+		add_action('wp_ajax_espresso_update_question_group_order', array($this, 'update_question_group_order'));
324
+	}
325
+
326
+
327
+	/**
328
+	 * @return void
329
+	 */
330
+	public function load_scripts_styles_question_groups()
331
+	{
332
+		wp_enqueue_script('espresso_ajax_table_sorting');
333
+	}
334
+
335
+
336
+	/**
337
+	 * @return void
338
+	 */
339
+	public function load_scripts_styles_add_question_group()
340
+	{
341
+		$this->load_scripts_styles_forms();
342
+		$this->load_sortable_question_script();
343
+	}
344
+
345
+
346
+	/**
347
+	 * @return void
348
+	 */
349
+	public function load_scripts_styles_edit_question_group()
350
+	{
351
+		$this->load_scripts_styles_forms();
352
+		$this->load_sortable_question_script();
353
+	}
354
+
355
+
356
+	/**
357
+	 * registers and enqueues script for questions
358
+	 *
359
+	 * @return void
360
+	 */
361
+	public function load_sortable_question_script()
362
+	{
363
+		wp_register_script(
364
+			'ee-question-sortable',
365
+			REGISTRATION_FORM_CAF_ASSETS_URL . 'ee_question_order.js',
366
+			array('jquery-ui-sortable'),
367
+			EVENT_ESPRESSO_VERSION,
368
+			true
369
+		);
370
+		wp_enqueue_script('ee-question-sortable');
371
+	}
372
+
373
+
374
+	/**
375
+	 * @return void
376
+	 */
377
+	protected function _set_list_table_views_default()
378
+	{
379
+		$this->_views = array(
380
+			'all' => array(
381
+				'slug'        => 'all',
382
+				'label'       => esc_html__('View All Questions', 'event_espresso'),
383
+				'count'       => 0,
384
+				'bulk_action' => array(
385
+					'trash_questions' => esc_html__('Trash', 'event_espresso'),
386
+				),
387
+			),
388
+		);
389
+
390
+		if (EE_Registry::instance()->CAP->current_user_can(
391
+			'ee_delete_questions',
392
+			'espresso_registration_form_trash_questions'
393
+		)
394
+		) {
395
+			$this->_views['trash'] = array(
396
+				'slug'        => 'trash',
397
+				'label'       => esc_html__('Trash', 'event_espresso'),
398
+				'count'       => 0,
399
+				'bulk_action' => array(
400
+					'delete_questions'  => esc_html__('Delete Permanently', 'event_espresso'),
401
+					'restore_questions' => esc_html__('Restore', 'event_espresso'),
402
+				),
403
+			);
404
+		}
405
+	}
406
+
407
+
408
+	/**
409
+	 * @return void
410
+	 */
411
+	protected function _set_list_table_views_question_groups()
412
+	{
413
+		$this->_views = array(
414
+			'all' => array(
415
+				'slug'        => 'all',
416
+				'label'       => esc_html__('All', 'event_espresso'),
417
+				'count'       => 0,
418
+				'bulk_action' => array(
419
+					'trash_question_groups' => esc_html__('Trash', 'event_espresso'),
420
+				),
421
+			),
422
+		);
423
+
424
+		if (EE_Registry::instance()->CAP->current_user_can(
425
+			'ee_delete_question_groups',
426
+			'espresso_registration_form_trash_question_groups'
427
+		)
428
+		) {
429
+			$this->_views['trash'] = array(
430
+				'slug'        => 'trash',
431
+				'label'       => esc_html__('Trash', 'event_espresso'),
432
+				'count'       => 0,
433
+				'bulk_action' => array(
434
+					'delete_question_groups'  => esc_html__('Delete Permanently', 'event_espresso'),
435
+					'restore_question_groups' => esc_html__('Restore', 'event_espresso'),
436
+				),
437
+			);
438
+		}
439
+	}
440
+
441
+
442
+	/**
443
+	 * @return void
444
+	 * @throws EE_Error
445
+	 * @throws InvalidArgumentException
446
+	 * @throws InvalidDataTypeException
447
+	 * @throws InvalidInterfaceException
448
+	 */
449
+	protected function _questions_overview_list_table()
450
+	{
451
+		$this->_admin_page_title .= ' ' . $this->get_action_link_or_button(
452
+			'add_question',
453
+			'add_question',
454
+			array(),
455
+			'add-new-h2'
456
+		);
457
+		parent::_questions_overview_list_table();
458
+	}
459
+
460
+
461
+	/**
462
+	 * @return void
463
+	 * @throws DomainException
464
+	 * @throws EE_Error
465
+	 * @throws InvalidArgumentException
466
+	 * @throws InvalidDataTypeException
467
+	 * @throws InvalidInterfaceException
468
+	 */
469
+	protected function _question_groups_overview_list_table()
470
+	{
471
+		$this->_search_btn_label = esc_html__('Question Groups', 'event_espresso');
472
+		$this->_admin_page_title .= ' ' . $this->get_action_link_or_button(
473
+			'add_question_group',
474
+			'add_question_group',
475
+			array(),
476
+			'add-new-h2'
477
+		);
478
+		$this->display_admin_list_table_page_with_sidebar();
479
+	}
480
+
481
+
482
+	/**
483
+	 * @return void
484
+	 * @throws EE_Error
485
+	 * @throws InvalidArgumentException
486
+	 * @throws InvalidDataTypeException
487
+	 * @throws InvalidInterfaceException
488
+	 */
489
+	protected function _delete_question()
490
+	{
491
+		$success = $this->_delete_items($this->_question_model);
492
+		$this->_redirect_after_action(
493
+			$success,
494
+			$this->_question_model->item_name($success),
495
+			'deleted',
496
+			array('action' => 'default', 'status' => 'all')
497
+		);
498
+	}
499
+
500
+
501
+	/**
502
+	 * @return void
503
+	 * @throws EE_Error
504
+	 * @throws InvalidArgumentException
505
+	 * @throws InvalidDataTypeException
506
+	 * @throws InvalidInterfaceException
507
+	 */
508
+	protected function _delete_questions()
509
+	{
510
+		$success = $this->_delete_items($this->_question_model);
511
+		$this->_redirect_after_action(
512
+			$success,
513
+			$this->_question_model->item_name($success),
514
+			'deleted permanently',
515
+			array('action' => 'default', 'status' => 'trash')
516
+		);
517
+	}
518
+
519
+
520
+	/**
521
+	 * Performs the deletion of a single or multiple questions or question groups.
522
+	 *
523
+	 * @param EEM_Soft_Delete_Base $model
524
+	 * @return int number of items deleted permanently
525
+	 * @throws EE_Error
526
+	 * @throws InvalidArgumentException
527
+	 * @throws InvalidDataTypeException
528
+	 * @throws InvalidInterfaceException
529
+	 */
530
+	private function _delete_items(EEM_Soft_Delete_Base $model)
531
+	{
532
+		$success = 0;
533
+		do_action('AHEE_log', __FILE__, __FUNCTION__, '');
534
+		if (! empty($this->_req_data['checkbox']) && is_array($this->_req_data['checkbox'])) {
535
+			// if array has more than one element than success message should be plural
536
+			$success = count($this->_req_data['checkbox']) > 1 ? 2 : 1;
537
+			// cycle thru bulk action checkboxes
538
+			while (list($ID, $value) = each($this->_req_data['checkbox'])) {
539
+				if (! $this->_delete_item($ID, $model)) {
540
+					$success = 0;
541
+				}
542
+			}
543
+		} elseif (! empty($this->_req_data['QSG_ID'])) {
544
+			$success = $this->_delete_item($this->_req_data['QSG_ID'], $model);
545
+		} elseif (! empty($this->_req_data['QST_ID'])) {
546
+			$success = $this->_delete_item($this->_req_data['QST_ID'], $model);
547
+		} else {
548
+			EE_Error::add_error(
549
+				sprintf(
550
+					esc_html__(
551
+						"No Questions or Question Groups were selected for deleting. This error usually shows when you've attempted to delete via bulk action but there were no selections.",
552
+						"event_espresso"
553
+					)
554
+				),
555
+				__FILE__,
556
+				__FUNCTION__,
557
+				__LINE__
558
+			);
559
+		}
560
+		return $success;
561
+	}
562
+
563
+
564
+	/**
565
+	 * Deletes the specified question (and its associated question options) or question group
566
+	 *
567
+	 * @param int                  $id
568
+	 * @param EEM_Soft_Delete_Base $model
569
+	 * @return boolean
570
+	 * @throws EE_Error
571
+	 * @throws InvalidArgumentException
572
+	 * @throws InvalidDataTypeException
573
+	 * @throws InvalidInterfaceException
574
+	 */
575
+	protected function _delete_item($id, $model)
576
+	{
577
+		if ($model instanceof EEM_Question) {
578
+			EEM_Question_Option::instance()->delete_permanently(array(array('QST_ID' => absint($id))));
579
+		}
580
+		return $model->delete_permanently_by_ID(absint($id));
581
+	}
582
+
583
+
584
+	/******************************    QUESTION GROUPS    ******************************/
585
+
586
+
587
+	/**
588
+	 * @param string $type
589
+	 * @return void
590
+	 * @throws DomainException
591
+	 * @throws EE_Error
592
+	 * @throws InvalidArgumentException
593
+	 * @throws InvalidDataTypeException
594
+	 * @throws InvalidInterfaceException
595
+	 */
596
+	protected function _edit_question_group($type = 'add')
597
+	{
598
+		do_action('AHEE_log', __FILE__, __FUNCTION__, '');
599
+		$ID = isset($this->_req_data['QSG_ID']) && ! empty($this->_req_data['QSG_ID'])
600
+			? absint($this->_req_data['QSG_ID'])
601
+			: false;
602
+
603
+		switch ($this->_req_action) {
604
+			case 'add_question_group':
605
+				$this->_admin_page_title = esc_html__('Add Question Group', 'event_espresso');
606
+				break;
607
+			case 'edit_question_group':
608
+				$this->_admin_page_title = esc_html__('Edit Question Group', 'event_espresso');
609
+				break;
610
+			default:
611
+				$this->_admin_page_title = ucwords(str_replace('_', ' ', $this->_req_action));
612
+		}
613
+		// add ID to title if editing
614
+		$this->_admin_page_title = $ID ? $this->_admin_page_title . ' # ' . $ID : $this->_admin_page_title;
615
+		if ($ID) {
616
+			/** @var EE_Question_Group $questionGroup */
617
+			$questionGroup = $this->_question_group_model->get_one_by_ID($ID);
618
+			$additional_hidden_fields = array('QSG_ID' => array('type' => 'hidden', 'value' => $ID));
619
+			$this->_set_add_edit_form_tags('update_question_group', $additional_hidden_fields);
620
+		} else {
621
+			/** @var EE_Question_Group $questionGroup */
622
+			$questionGroup = EEM_Question_Group::instance()->create_default_object();
623
+			$questionGroup->set_order_to_latest();
624
+			$this->_set_add_edit_form_tags('insert_question_group');
625
+		}
626
+		$this->_template_args['values'] = $this->_yes_no_values;
627
+		$this->_template_args['all_questions'] = $questionGroup->questions_in_and_not_in_group();
628
+		$this->_template_args['QSG_ID'] = $ID ? $ID : true;
629
+		$this->_template_args['question_group'] = $questionGroup;
630
+
631
+		$redirect_URL = add_query_arg(array('action' => 'question_groups'), $this->_admin_base_url);
632
+		$this->_set_publish_post_box_vars('id', $ID, false, $redirect_URL);
633
+		$this->_template_args['admin_page_content'] = EEH_Template::display_template(
634
+			REGISTRATION_FORM_CAF_TEMPLATE_PATH . 'question_groups_main_meta_box.template.php',
635
+			$this->_template_args,
636
+			true
637
+		);
638
+
639
+		// the details template wrapper
640
+		$this->display_admin_page_with_sidebar();
641
+	}
642
+
643
+
644
+	/**
645
+	 * @return void
646
+	 * @throws EE_Error
647
+	 * @throws InvalidArgumentException
648
+	 * @throws InvalidDataTypeException
649
+	 * @throws InvalidInterfaceException
650
+	 */
651
+	protected function _delete_question_groups()
652
+	{
653
+		$success = $this->_delete_items($this->_question_group_model);
654
+		$this->_redirect_after_action(
655
+			$success,
656
+			$this->_question_group_model->item_name($success),
657
+			'deleted permanently',
658
+			array('action' => 'question_groups', 'status' => 'trash')
659
+		);
660
+	}
661
+
662
+
663
+	/**
664
+	 * @param bool $new_question_group
665
+	 * @throws EE_Error
666
+	 * @throws InvalidArgumentException
667
+	 * @throws InvalidDataTypeException
668
+	 * @throws InvalidInterfaceException
669
+	 */
670
+	protected function _insert_or_update_question_group($new_question_group = true)
671
+	{
672
+		do_action('AHEE_log', __FILE__, __FUNCTION__, '');
673
+		$set_column_values = $this->_set_column_values_for($this->_question_group_model);
674
+		if ($new_question_group) {
675
+			$QSG_ID = $this->_question_group_model->insert($set_column_values);
676
+			$success = $QSG_ID ? 1 : 0;
677
+		} else {
678
+			$QSG_ID = absint($this->_req_data['QSG_ID']);
679
+			unset($set_column_values['QSG_ID']);
680
+			$success = $this->_question_group_model->update($set_column_values, array(array('QSG_ID' => $QSG_ID)));
681
+		}
682
+		$phone_question_id = EEM_Question::instance()->get_Question_ID_from_system_string(
683
+			EEM_Attendee::system_question_phone
684
+		);
685
+		// update the existing related questions
686
+		// BUT FIRST...  delete the phone question from the Question_Group_Question
687
+		// if it is being added to this question group (therefore removed from the existing group)
688
+		if (isset($this->_req_data['questions'], $this->_req_data['questions'][ $phone_question_id ])) {
689
+			// delete where QST ID = system phone question ID and Question Group ID is NOT this group
690
+			EEM_Question_Group_Question::instance()->delete(
691
+				array(
692
+					array(
693
+						'QST_ID' => $phone_question_id,
694
+						'QSG_ID' => array('!=', $QSG_ID),
695
+					),
696
+				)
697
+			);
698
+		}
699
+		/** @type EE_Question_Group $question_group */
700
+		$question_group = $this->_question_group_model->get_one_by_ID($QSG_ID);
701
+		$questions = $question_group->questions();
702
+		// make sure system phone question is added to list of questions for this group
703
+		if (! isset($questions[ $phone_question_id ])) {
704
+			$questions[ $phone_question_id ] = EEM_Question::instance()->get_one_by_ID($phone_question_id);
705
+		}
706
+
707
+		foreach ($questions as $question_ID => $question) {
708
+			// first we always check for order.
709
+			if (! empty($this->_req_data['question_orders'][ $question_ID ])) {
710
+				// update question order
711
+				$question_group->update_question_order(
712
+					$question_ID,
713
+					$this->_req_data['question_orders'][ $question_ID ]
714
+				);
715
+			}
716
+
717
+			// then we always check if adding or removing.
718
+			if (isset($this->_req_data['questions'], $this->_req_data['questions'][ $question_ID ])) {
719
+				$question_group->add_question($question_ID);
720
+			} else {
721
+				// not found, remove it (but only if not a system question for the personal group
722
+				// with the exception of lname system question - we allow removal of it)
723
+				if (in_array(
724
+					$question->system_ID(),
725
+					EEM_Question::instance()->required_system_questions_in_system_question_group(
726
+						$question_group->system_group()
727
+					)
728
+				)) {
729
+					continue;
730
+				} else {
731
+					$question_group->remove_question($question_ID);
732
+				}
733
+			}
734
+		}
735
+		// save new related questions
736
+		if (isset($this->_req_data['questions'])) {
737
+			foreach ($this->_req_data['questions'] as $QST_ID) {
738
+				$question_group->add_question($QST_ID);
739
+				if (isset($this->_req_data['question_orders'][ $QST_ID ])) {
740
+					$question_group->update_question_order($QST_ID, $this->_req_data['question_orders'][ $QST_ID ]);
741
+				}
742
+			}
743
+		}
744
+
745
+		if ($success !== false) {
746
+			$msg = $new_question_group
747
+				? sprintf(
748
+					esc_html__('The %s has been created', 'event_espresso'),
749
+					$this->_question_group_model->item_name()
750
+				)
751
+				: sprintf(
752
+					esc_html__(
753
+						'The %s has been updated',
754
+						'event_espresso'
755
+					),
756
+					$this->_question_group_model->item_name()
757
+				);
758
+			EE_Error::add_success($msg);
759
+		}
760
+		$this->_redirect_after_action(
761
+			false,
762
+			'',
763
+			'',
764
+			array('action' => 'edit_question_group', 'QSG_ID' => $QSG_ID),
765
+			true
766
+		);
767
+	}
768
+
769
+
770
+	/**
771
+	 * duplicates a question and all its question options and redirects to the new question.
772
+	 *
773
+	 * @return void
774
+	 * @throws EE_Error
775
+	 * @throws InvalidArgumentException
776
+	 * @throws ReflectionException
777
+	 * @throws InvalidDataTypeException
778
+	 * @throws InvalidInterfaceException
779
+	 */
780
+	public function _duplicate_question()
781
+	{
782
+		$question_ID = (int) $this->_req_data['QST_ID'];
783
+		$question = EEM_Question::instance()->get_one_by_ID($question_ID);
784
+		if ($question instanceof EE_Question) {
785
+			$new_question = $question->duplicate();
786
+			if ($new_question instanceof EE_Question) {
787
+				$this->_redirect_after_action(
788
+					true,
789
+					esc_html__('Question', 'event_espresso'),
790
+					esc_html__('Duplicated', 'event_espresso'),
791
+					array('action' => 'edit_question', 'QST_ID' => $new_question->ID()),
792
+					true
793
+				);
794
+			} else {
795
+				global $wpdb;
796
+				EE_Error::add_error(
797
+					sprintf(
798
+						esc_html__(
799
+							'Could not duplicate question with ID %1$d because: %2$s',
800
+							'event_espresso'
801
+						),
802
+						$question_ID,
803
+						$wpdb->last_error
804
+					),
805
+					__FILE__,
806
+					__FUNCTION__,
807
+					__LINE__
808
+				);
809
+				$this->_redirect_after_action(false, '', '', array('action' => 'default'), false);
810
+			}
811
+		} else {
812
+			EE_Error::add_error(
813
+				sprintf(
814
+					esc_html__(
815
+						'Could not duplicate question with ID %d because it didn\'t exist!',
816
+						'event_espresso'
817
+					),
818
+					$question_ID
819
+				),
820
+				__FILE__,
821
+				__FUNCTION__,
822
+				__LINE__
823
+			);
824
+			$this->_redirect_after_action(false, '', '', array('action' => 'default'), false);
825
+		}
826
+	}
827
+
828
+
829
+	/**
830
+	 * @param bool $trash
831
+	 * @throws EE_Error
832
+	 */
833
+	protected function _trash_or_restore_question_groups($trash = true)
834
+	{
835
+		$this->_trash_or_restore_items($this->_question_group_model, $trash);
836
+	}
837
+
838
+
839
+	/**
840
+	 *_trash_question
841
+	 *
842
+	 * @return void
843
+	 * @throws EE_Error
844
+	 */
845
+	protected function _trash_question()
846
+	{
847
+		$success = $this->_question_model->delete_by_ID((int) $this->_req_data['QST_ID']);
848
+		$query_args = array('action' => 'default', 'status' => 'all');
849
+		$this->_redirect_after_action($success, $this->_question_model->item_name($success), 'trashed', $query_args);
850
+	}
851
+
852
+
853
+	/**
854
+	 * @param bool $trash
855
+	 * @throws EE_Error
856
+	 */
857
+	protected function _trash_or_restore_questions($trash = true)
858
+	{
859
+		$this->_trash_or_restore_items($this->_question_model, $trash);
860
+	}
861
+
862
+
863
+	/**
864
+	 * Internally used to delete or restore items, using the request data. Meant to be
865
+	 * flexible between question or question groups
866
+	 *
867
+	 * @param EEM_Soft_Delete_Base $model
868
+	 * @param boolean              $trash whether to trash or restore
869
+	 * @throws EE_Error
870
+	 */
871
+	private function _trash_or_restore_items(EEM_Soft_Delete_Base $model, $trash = true)
872
+	{
873
+
874
+		do_action('AHEE_log', __FILE__, __FUNCTION__, '');
875
+
876
+		$success = 1;
877
+		// Checkboxes
878
+		// echo "trash $trash";
879
+		// var_dump($this->_req_data['checkbox']);die;
880
+		if (isset($this->_req_data['checkbox'])) {
881
+			if (! empty($this->_req_data['checkbox']) && is_array($this->_req_data['checkbox'])) {
882
+				// if array has more than one element than success message should be plural
883
+				$success = count($this->_req_data['checkbox']) > 1 ? 2 : 1;
884
+				// cycle thru bulk action checkboxes
885
+				while (list($ID, $value) = each($this->_req_data['checkbox'])) {
886
+					if (! $model->delete_or_restore_by_ID($trash, absint($ID))) {
887
+						$success = 0;
888
+					}
889
+				}
890
+			} else {
891
+				// grab single id and delete
892
+				$ID = absint($this->_req_data['checkbox']);
893
+				if (! $model->delete_or_restore_by_ID($trash, $ID)) {
894
+					$success = 0;
895
+				}
896
+			}
897
+		} else {
898
+			// delete via trash link
899
+			// grab single id and delete
900
+			$ID = absint($this->_req_data[ $model->primary_key_name() ]);
901
+			if (! $model->delete_or_restore_by_ID($trash, $ID)) {
902
+				$success = 0;
903
+			}
904
+		}
905
+
906
+
907
+		$action = $model instanceof EEM_Question ? 'default' : 'question_groups';// strtolower( $model->item_name(2) );
908
+		// echo "action :$action";
909
+		// $action = 'questions' ? 'default' : $action;
910
+		if ($trash) {
911
+			$action_desc = 'trashed';
912
+			$status = 'trash';
913
+		} else {
914
+			$action_desc = 'restored';
915
+			$status = 'all';
916
+		}
917
+		$this->_redirect_after_action(
918
+			$success,
919
+			$model->item_name($success),
920
+			$action_desc,
921
+			array('action' => $action, 'status' => $status)
922
+		);
923
+	}
924
+
925
+
926
+	/**
927
+	 * @param            $per_page
928
+	 * @param int        $current_page
929
+	 * @param bool|false $count
930
+	 * @return EE_Soft_Delete_Base_Class[]|int
931
+	 * @throws EE_Error
932
+	 * @throws InvalidArgumentException
933
+	 * @throws InvalidDataTypeException
934
+	 * @throws InvalidInterfaceException
935
+	 */
936
+	public function get_trashed_questions($per_page, $current_page = 1, $count = false)
937
+	{
938
+		$query_params = $this->get_query_params(EEM_Question::instance(), $per_page, $current_page);
939
+
940
+		if ($count) {
941
+			// note: this a subclass of EEM_Soft_Delete_Base, so this is actually only getting non-trashed items
942
+			$where = isset($query_params[0]) ? array($query_params[0]) : array();
943
+			$results = $this->_question_model->count_deleted($where);
944
+		} else {
945
+			// note: this a subclass of EEM_Soft_Delete_Base, so this is actually only getting non-trashed items
946
+			$results = $this->_question_model->get_all_deleted($query_params);
947
+		}
948
+		return $results;
949
+	}
950
+
951
+
952
+	/**
953
+	 * @param            $per_page
954
+	 * @param int        $current_page
955
+	 * @param bool|false $count
956
+	 * @return EE_Soft_Delete_Base_Class[]|int
957
+	 * @throws EE_Error
958
+	 * @throws InvalidArgumentException
959
+	 * @throws InvalidDataTypeException
960
+	 * @throws InvalidInterfaceException
961
+	 */
962
+	public function get_question_groups($per_page, $current_page = 1, $count = false)
963
+	{
964
+		$questionGroupModel = EEM_Question_Group::instance();
965
+		$query_params = $this->get_query_params($questionGroupModel, $per_page, $current_page);
966
+		if ($count) {
967
+			$where = isset($query_params[0]) ? array($query_params[0]) : array();
968
+			$results = $questionGroupModel->count($where);
969
+		} else {
970
+			$results = $questionGroupModel->get_all($query_params);
971
+		}
972
+		return $results;
973
+	}
974
+
975
+
976
+	/**
977
+	 * @param      $per_page
978
+	 * @param int  $current_page
979
+	 * @param bool $count
980
+	 * @return EE_Soft_Delete_Base_Class[]|int
981
+	 * @throws EE_Error
982
+	 * @throws InvalidArgumentException
983
+	 * @throws InvalidDataTypeException
984
+	 * @throws InvalidInterfaceException
985
+	 */
986
+	public function get_trashed_question_groups($per_page, $current_page = 1, $count = false)
987
+	{
988
+		$questionGroupModel = EEM_Question_Group::instance();
989
+		$query_params = $this->get_query_params($questionGroupModel, $per_page, $current_page);
990
+		if ($count) {
991
+			$where = isset($query_params[0]) ? array($query_params[0]) : array();
992
+			$query_params['limit'] = null;
993
+			$results = $questionGroupModel->count_deleted($where);
994
+		} else {
995
+			$results = $questionGroupModel->get_all_deleted($query_params);
996
+		}
997
+		return $results;
998
+	}
999
+
1000
+
1001
+	/**
1002
+	 * method for performing updates to question order
1003
+	 *
1004
+	 * @return void results array
1005
+	 * @throws EE_Error
1006
+	 * @throws InvalidArgumentException
1007
+	 * @throws InvalidDataTypeException
1008
+	 * @throws InvalidInterfaceException
1009
+	 */
1010
+	public function update_question_group_order()
1011
+	{
1012
+
1013
+		$success = esc_html__('Question group order was updated successfully.', 'event_espresso');
1014
+
1015
+		// grab our row IDs
1016
+		$row_ids = isset($this->_req_data['row_ids']) && ! empty($this->_req_data['row_ids'])
1017
+			? explode(',', rtrim($this->_req_data['row_ids'], ','))
1018
+			: array();
1019
+
1020
+		$perpage = ! empty($this->_req_data['perpage'])
1021
+			? (int) $this->_req_data['perpage']
1022
+			: null;
1023
+		$curpage = ! empty($this->_req_data['curpage'])
1024
+			? (int) $this->_req_data['curpage']
1025
+			: null;
1026
+
1027
+		if (! empty($row_ids)) {
1028
+			// figure out where we start the row_id count at for the current page.
1029
+			$qsgcount = empty($curpage) ? 0 : ($curpage - 1) * $perpage;
1030
+
1031
+			$row_count = count($row_ids);
1032
+			for ($i = 0; $i < $row_count; $i++) {
1033
+				// Update the questions when re-ordering
1034
+				$updated = EEM_Question_Group::instance()->update(
1035
+					array('QSG_order' => $qsgcount),
1036
+					array(array('QSG_ID' => $row_ids[ $i ]))
1037
+				);
1038
+				if ($updated === false) {
1039
+					$success = false;
1040
+				}
1041
+				$qsgcount++;
1042
+			}
1043
+		} else {
1044
+			$success = false;
1045
+		}
1046
+
1047
+		$errors = ! $success
1048
+			? esc_html__('An error occurred. The question group order was not updated.', 'event_espresso')
1049
+			: false;
1050
+
1051
+		echo wp_json_encode(array('return_data' => false, 'success' => $success, 'errors' => $errors));
1052
+		die();
1053
+	}
1054
+
1055
+
1056
+
1057
+	/***************************************       REGISTRATION SETTINGS       ***************************************/
1058
+
1059
+
1060
+	/**
1061
+	 * @throws DomainException
1062
+	 * @throws EE_Error
1063
+	 * @throws InvalidArgumentException
1064
+	 * @throws InvalidDataTypeException
1065
+	 * @throws InvalidInterfaceException
1066
+	 */
1067
+	protected function _reg_form_settings()
1068
+	{
1069
+		$this->_template_args['values'] = $this->_yes_no_values;
1070
+		add_action(
1071
+			'AHEE__Extend_Registration_Form_Admin_Page___reg_form_settings_template',
1072
+			array($this, 'email_validation_settings_form'),
1073
+			2
1074
+		);
1075
+		$this->_template_args = (array) apply_filters(
1076
+			'FHEE__Extend_Registration_Form_Admin_Page___reg_form_settings___template_args',
1077
+			$this->_template_args
1078
+		);
1079
+		$this->_set_add_edit_form_tags('update_reg_form_settings');
1080
+		$this->_set_publish_post_box_vars(null, false, false, null, false);
1081
+		$this->_template_args['admin_page_content'] = EEH_Template::display_template(
1082
+			REGISTRATION_FORM_CAF_TEMPLATE_PATH . 'reg_form_settings.template.php',
1083
+			$this->_template_args,
1084
+			true
1085
+		);
1086
+		$this->display_admin_page_with_sidebar();
1087
+	}
1088
+
1089
+
1090
+	/**
1091
+	 * @return void
1092
+	 * @throws EE_Error
1093
+	 * @throws InvalidArgumentException
1094
+	 * @throws ReflectionException
1095
+	 * @throws InvalidDataTypeException
1096
+	 * @throws InvalidInterfaceException
1097
+	 */
1098
+	protected function _update_reg_form_settings()
1099
+	{
1100
+		EE_Registry::instance()->CFG->registration = $this->update_email_validation_settings_form(
1101
+			EE_Registry::instance()->CFG->registration
1102
+		);
1103
+		EE_Registry::instance()->CFG->registration = apply_filters(
1104
+			'FHEE__Extend_Registration_Form_Admin_Page___update_reg_form_settings__CFG_registration',
1105
+			EE_Registry::instance()->CFG->registration
1106
+		);
1107
+		$success = $this->_update_espresso_configuration(
1108
+			esc_html__('Registration Form Options', 'event_espresso'),
1109
+			EE_Registry::instance()->CFG,
1110
+			__FILE__,
1111
+			__FUNCTION__,
1112
+			__LINE__
1113
+		);
1114
+		$this->_redirect_after_action(
1115
+			$success,
1116
+			esc_html__('Registration Form Options', 'event_espresso'),
1117
+			'updated',
1118
+			array('action' => 'view_reg_form_settings')
1119
+		);
1120
+	}
1121
+
1122
+
1123
+	/**
1124
+	 * @return void
1125
+	 * @throws EE_Error
1126
+	 * @throws InvalidArgumentException
1127
+	 * @throws InvalidDataTypeException
1128
+	 * @throws InvalidInterfaceException
1129
+	 */
1130
+	public function email_validation_settings_form()
1131
+	{
1132
+		echo $this->_email_validation_settings_form()->get_html();
1133
+	}
1134
+
1135
+
1136
+	/**
1137
+	 * _email_validation_settings_form
1138
+	 *
1139
+	 * @access protected
1140
+	 * @return EE_Form_Section_Proper
1141
+	 * @throws \EE_Error
1142
+	 */
1143
+	protected function _email_validation_settings_form()
1144
+	{
1145
+		return new EE_Form_Section_Proper(
1146
+			array(
1147
+				'name'            => 'email_validation_settings',
1148
+				'html_id'         => 'email_validation_settings',
1149
+				'layout_strategy' => new EE_Admin_Two_Column_Layout(),
1150
+				'subsections'     => apply_filters(
1151
+					'FHEE__Extend_Registration_Form_Admin_Page___email_validation_settings_form__form_subsections',
1152
+					array(
1153
+						'email_validation_hdr'   => new EE_Form_Section_HTML(
1154
+							EEH_HTML::h2(esc_html__('Email Validation Settings', 'event_espresso'))
1155
+						),
1156
+						'email_validation_level' => new EE_Select_Input(
1157
+							array(
1158
+								'basic'      => esc_html__('Basic', 'event_espresso'),
1159
+								'wp_default' => esc_html__('WordPress Default', 'event_espresso'),
1160
+								'i18n'       => esc_html__('International', 'event_espresso'),
1161
+								'i18n_dns'   => esc_html__('International + DNS Check', 'event_espresso'),
1162
+							),
1163
+							array(
1164
+								'html_label_text' => esc_html__('Email Validation Level', 'event_espresso')
1165
+													 . EEH_Template::get_help_tab_link('email_validation_info'),
1166
+								'html_help_text'  => esc_html__(
1167
+									'These levels range from basic validation ( ie: [email protected] ) to more advanced checks against international email addresses (ie: üñîçøðé@example.com ) with additional MX and A record checks to confirm the domain actually exists. More information on on each level can be found within the help section.',
1168
+									'event_espresso'
1169
+								),
1170
+								'default'         => isset(
1171
+									EE_Registry::instance()->CFG->registration->email_validation_level
1172
+								)
1173
+									? EE_Registry::instance()->CFG->registration->email_validation_level
1174
+									: 'wp_default',
1175
+								'required'        => false,
1176
+							)
1177
+						),
1178
+					)
1179
+				),
1180
+			)
1181
+		);
1182
+	}
1183
+
1184
+
1185
+	/**
1186
+	 * @param EE_Registration_Config $EE_Registration_Config
1187
+	 * @return EE_Registration_Config
1188
+	 * @throws EE_Error
1189
+	 * @throws InvalidArgumentException
1190
+	 * @throws ReflectionException
1191
+	 * @throws InvalidDataTypeException
1192
+	 * @throws InvalidInterfaceException
1193
+	 */
1194
+	public function update_email_validation_settings_form(EE_Registration_Config $EE_Registration_Config)
1195
+	{
1196
+		$prev_email_validation_level = $EE_Registration_Config->email_validation_level;
1197
+		try {
1198
+			$email_validation_settings_form = $this->_email_validation_settings_form();
1199
+			// if not displaying a form, then check for form submission
1200
+			if ($email_validation_settings_form->was_submitted()) {
1201
+				// capture form data
1202
+				$email_validation_settings_form->receive_form_submission();
1203
+				// validate form data
1204
+				if ($email_validation_settings_form->is_valid()) {
1205
+					// grab validated data from form
1206
+					$valid_data = $email_validation_settings_form->valid_data();
1207
+					if (isset($valid_data['email_validation_level'])) {
1208
+						$email_validation_level = $valid_data['email_validation_level'];
1209
+						// now if they want to use international email addresses
1210
+						if ($email_validation_level === 'i18n' || $email_validation_level === 'i18n_dns') {
1211
+							// in case we need to reset their email validation level,
1212
+							// make sure that the previous value wasn't already set to one of the i18n options.
1213
+							if ($prev_email_validation_level === 'i18n' || $prev_email_validation_level === 'i18n_dns') {
1214
+								// if so, then reset it back to "basic" since that is the only other option that,
1215
+								// despite offering poor validation, supports i18n email addresses
1216
+								$prev_email_validation_level = 'basic';
1217
+							}
1218
+							// confirm our i18n email validation will work on the server
1219
+							if (! $this->_verify_pcre_support($EE_Registration_Config, $email_validation_level)) {
1220
+								// or reset email validation level to previous value
1221
+								$email_validation_level = $prev_email_validation_level;
1222
+							}
1223
+						}
1224
+						$EE_Registration_Config->email_validation_level = $email_validation_level;
1225
+					} else {
1226
+						EE_Error::add_error(
1227
+							esc_html__(
1228
+								'Invalid or missing Email Validation settings. Please refresh the form and try again.',
1229
+								'event_espresso'
1230
+							),
1231
+							__FILE__,
1232
+							__FUNCTION__,
1233
+							__LINE__
1234
+						);
1235
+					}
1236
+				} else {
1237
+					if ($email_validation_settings_form->submission_error_message() !== '') {
1238
+						EE_Error::add_error(
1239
+							$email_validation_settings_form->submission_error_message(),
1240
+							__FILE__,
1241
+							__FUNCTION__,
1242
+							__LINE__
1243
+						);
1244
+					}
1245
+				}
1246
+			}
1247
+		} catch (EE_Error $e) {
1248
+			$e->get_error();
1249
+		}
1250
+		return $EE_Registration_Config;
1251
+	}
1252
+
1253
+
1254
+	/**
1255
+	 * confirms that the server's PHP version has the PCRE module enabled,
1256
+	 * and that the PCRE version works with our i18n email validation
1257
+	 *
1258
+	 * @param EE_Registration_Config $EE_Registration_Config
1259
+	 * @param string                 $email_validation_level
1260
+	 * @return bool
1261
+	 */
1262
+	private function _verify_pcre_support(EE_Registration_Config $EE_Registration_Config, $email_validation_level)
1263
+	{
1264
+		// first check that PCRE is enabled
1265
+		if (! defined('PREG_BAD_UTF8_ERROR')) {
1266
+			EE_Error::add_error(
1267
+				sprintf(
1268
+					esc_html__(
1269
+						'We\'re sorry, but it appears that your server\'s version of PHP was not compiled with PCRE unicode support.%1$sPlease contact your hosting company and ask them whether the PCRE compiled with your version of PHP on your server can be been built with the "--enable-unicode-properties" and "--enable-utf8" configuration switches to enable more complex regex expressions.%1$sIf they are unable, or unwilling to do so, then your server will not support international email addresses using UTF-8 unicode characters. This means you will either have to lower your email validation level to "Basic" or "WordPress Default", or switch to a hosting company that has/can enable PCRE unicode support on the server.',
1270
+						'event_espresso'
1271
+					),
1272
+					'<br />'
1273
+				),
1274
+				__FILE__,
1275
+				__FUNCTION__,
1276
+				__LINE__
1277
+			);
1278
+			return false;
1279
+		} else {
1280
+			// PCRE support is enabled, but let's still
1281
+			// perform a test to see if the server will support it.
1282
+			// but first, save the updated validation level to the config,
1283
+			// so that the validation strategy picks it up.
1284
+			// this will get bumped back down if it doesn't work
1285
+			$EE_Registration_Config->email_validation_level = $email_validation_level;
1286
+			try {
1287
+				$email_validator = new EE_Email_Validation_Strategy();
1288
+				$i18n_email_address = apply_filters(
1289
+					'FHEE__Extend_Registration_Form_Admin_Page__update_email_validation_settings_form__i18n_email_address',
1290
+					'jägerjü[email protected]'
1291
+				);
1292
+				$email_validator->validate($i18n_email_address);
1293
+			} catch (Exception $e) {
1294
+				EE_Error::add_error(
1295
+					sprintf(
1296
+						esc_html__(
1297
+							'We\'re sorry, but it appears that your server\'s configuration will not support the "International" or "International + DNS Check" email validation levels.%1$sTo correct this issue, please consult with your hosting company regarding your server\'s PCRE settings.%1$sIt is recommended that your PHP version be configured to use PCRE 8.10 or newer.%1$sMore information regarding PCRE versions and installation can be found here: %2$s',
1298
+							'event_espresso'
1299
+						),
1300
+						'<br />',
1301
+						'<a href="http://php.net/manual/en/pcre.installation.php" target="_blank">http://php.net/manual/en/pcre.installation.php</a>'
1302
+					),
1303
+					__FILE__,
1304
+					__FUNCTION__,
1305
+					__LINE__
1306
+				);
1307
+				return false;
1308
+			}
1309
+		}
1310
+		return true;
1311
+	}
1312 1312
 }
Please login to merge, or discard this patch.
Spacing   +33 added lines, -33 removed lines patch added patch discarded remove patch
@@ -19,11 +19,11 @@  discard block
 block discarded – undo
19 19
      */
20 20
     public function __construct($routing = true)
21 21
     {
22
-        define('REGISTRATION_FORM_CAF_ADMIN', EE_CORE_CAF_ADMIN_EXTEND . 'registration_form' . DS);
23
-        define('REGISTRATION_FORM_CAF_ASSETS_PATH', REGISTRATION_FORM_CAF_ADMIN . 'assets' . DS);
24
-        define('REGISTRATION_FORM_CAF_ASSETS_URL', EE_CORE_CAF_ADMIN_EXTEND_URL . 'registration_form/assets/');
25
-        define('REGISTRATION_FORM_CAF_TEMPLATE_PATH', REGISTRATION_FORM_CAF_ADMIN . 'templates' . DS);
26
-        define('REGISTRATION_FORM_CAF_TEMPLATE_URL', EE_CORE_CAF_ADMIN_EXTEND_URL . 'registration_form/templates/');
22
+        define('REGISTRATION_FORM_CAF_ADMIN', EE_CORE_CAF_ADMIN_EXTEND.'registration_form'.DS);
23
+        define('REGISTRATION_FORM_CAF_ASSETS_PATH', REGISTRATION_FORM_CAF_ADMIN.'assets'.DS);
24
+        define('REGISTRATION_FORM_CAF_ASSETS_URL', EE_CORE_CAF_ADMIN_EXTEND_URL.'registration_form/assets/');
25
+        define('REGISTRATION_FORM_CAF_TEMPLATE_PATH', REGISTRATION_FORM_CAF_ADMIN.'templates'.DS);
26
+        define('REGISTRATION_FORM_CAF_TEMPLATE_URL', EE_CORE_CAF_ADMIN_EXTEND_URL.'registration_form/templates/');
27 27
         parent::__construct($routing);
28 28
     }
29 29
 
@@ -362,7 +362,7 @@  discard block
 block discarded – undo
362 362
     {
363 363
         wp_register_script(
364 364
             'ee-question-sortable',
365
-            REGISTRATION_FORM_CAF_ASSETS_URL . 'ee_question_order.js',
365
+            REGISTRATION_FORM_CAF_ASSETS_URL.'ee_question_order.js',
366 366
             array('jquery-ui-sortable'),
367 367
             EVENT_ESPRESSO_VERSION,
368 368
             true
@@ -448,7 +448,7 @@  discard block
 block discarded – undo
448 448
      */
449 449
     protected function _questions_overview_list_table()
450 450
     {
451
-        $this->_admin_page_title .= ' ' . $this->get_action_link_or_button(
451
+        $this->_admin_page_title .= ' '.$this->get_action_link_or_button(
452 452
             'add_question',
453 453
             'add_question',
454 454
             array(),
@@ -469,7 +469,7 @@  discard block
 block discarded – undo
469 469
     protected function _question_groups_overview_list_table()
470 470
     {
471 471
         $this->_search_btn_label = esc_html__('Question Groups', 'event_espresso');
472
-        $this->_admin_page_title .= ' ' . $this->get_action_link_or_button(
472
+        $this->_admin_page_title .= ' '.$this->get_action_link_or_button(
473 473
             'add_question_group',
474 474
             'add_question_group',
475 475
             array(),
@@ -531,18 +531,18 @@  discard block
 block discarded – undo
531 531
     {
532 532
         $success = 0;
533 533
         do_action('AHEE_log', __FILE__, __FUNCTION__, '');
534
-        if (! empty($this->_req_data['checkbox']) && is_array($this->_req_data['checkbox'])) {
534
+        if ( ! empty($this->_req_data['checkbox']) && is_array($this->_req_data['checkbox'])) {
535 535
             // if array has more than one element than success message should be plural
536 536
             $success = count($this->_req_data['checkbox']) > 1 ? 2 : 1;
537 537
             // cycle thru bulk action checkboxes
538 538
             while (list($ID, $value) = each($this->_req_data['checkbox'])) {
539
-                if (! $this->_delete_item($ID, $model)) {
539
+                if ( ! $this->_delete_item($ID, $model)) {
540 540
                     $success = 0;
541 541
                 }
542 542
             }
543
-        } elseif (! empty($this->_req_data['QSG_ID'])) {
543
+        } elseif ( ! empty($this->_req_data['QSG_ID'])) {
544 544
             $success = $this->_delete_item($this->_req_data['QSG_ID'], $model);
545
-        } elseif (! empty($this->_req_data['QST_ID'])) {
545
+        } elseif ( ! empty($this->_req_data['QST_ID'])) {
546 546
             $success = $this->_delete_item($this->_req_data['QST_ID'], $model);
547 547
         } else {
548 548
             EE_Error::add_error(
@@ -611,7 +611,7 @@  discard block
 block discarded – undo
611 611
                 $this->_admin_page_title = ucwords(str_replace('_', ' ', $this->_req_action));
612 612
         }
613 613
         // add ID to title if editing
614
-        $this->_admin_page_title = $ID ? $this->_admin_page_title . ' # ' . $ID : $this->_admin_page_title;
614
+        $this->_admin_page_title = $ID ? $this->_admin_page_title.' # '.$ID : $this->_admin_page_title;
615 615
         if ($ID) {
616 616
             /** @var EE_Question_Group $questionGroup */
617 617
             $questionGroup = $this->_question_group_model->get_one_by_ID($ID);
@@ -631,7 +631,7 @@  discard block
 block discarded – undo
631 631
         $redirect_URL = add_query_arg(array('action' => 'question_groups'), $this->_admin_base_url);
632 632
         $this->_set_publish_post_box_vars('id', $ID, false, $redirect_URL);
633 633
         $this->_template_args['admin_page_content'] = EEH_Template::display_template(
634
-            REGISTRATION_FORM_CAF_TEMPLATE_PATH . 'question_groups_main_meta_box.template.php',
634
+            REGISTRATION_FORM_CAF_TEMPLATE_PATH.'question_groups_main_meta_box.template.php',
635 635
             $this->_template_args,
636 636
             true
637 637
         );
@@ -685,7 +685,7 @@  discard block
 block discarded – undo
685 685
         // update the existing related questions
686 686
         // BUT FIRST...  delete the phone question from the Question_Group_Question
687 687
         // if it is being added to this question group (therefore removed from the existing group)
688
-        if (isset($this->_req_data['questions'], $this->_req_data['questions'][ $phone_question_id ])) {
688
+        if (isset($this->_req_data['questions'], $this->_req_data['questions'][$phone_question_id])) {
689 689
             // delete where QST ID = system phone question ID and Question Group ID is NOT this group
690 690
             EEM_Question_Group_Question::instance()->delete(
691 691
                 array(
@@ -700,22 +700,22 @@  discard block
 block discarded – undo
700 700
         $question_group = $this->_question_group_model->get_one_by_ID($QSG_ID);
701 701
         $questions = $question_group->questions();
702 702
         // make sure system phone question is added to list of questions for this group
703
-        if (! isset($questions[ $phone_question_id ])) {
704
-            $questions[ $phone_question_id ] = EEM_Question::instance()->get_one_by_ID($phone_question_id);
703
+        if ( ! isset($questions[$phone_question_id])) {
704
+            $questions[$phone_question_id] = EEM_Question::instance()->get_one_by_ID($phone_question_id);
705 705
         }
706 706
 
707 707
         foreach ($questions as $question_ID => $question) {
708 708
             // first we always check for order.
709
-            if (! empty($this->_req_data['question_orders'][ $question_ID ])) {
709
+            if ( ! empty($this->_req_data['question_orders'][$question_ID])) {
710 710
                 // update question order
711 711
                 $question_group->update_question_order(
712 712
                     $question_ID,
713
-                    $this->_req_data['question_orders'][ $question_ID ]
713
+                    $this->_req_data['question_orders'][$question_ID]
714 714
                 );
715 715
             }
716 716
 
717 717
             // then we always check if adding or removing.
718
-            if (isset($this->_req_data['questions'], $this->_req_data['questions'][ $question_ID ])) {
718
+            if (isset($this->_req_data['questions'], $this->_req_data['questions'][$question_ID])) {
719 719
                 $question_group->add_question($question_ID);
720 720
             } else {
721 721
                 // not found, remove it (but only if not a system question for the personal group
@@ -736,8 +736,8 @@  discard block
 block discarded – undo
736 736
         if (isset($this->_req_data['questions'])) {
737 737
             foreach ($this->_req_data['questions'] as $QST_ID) {
738 738
                 $question_group->add_question($QST_ID);
739
-                if (isset($this->_req_data['question_orders'][ $QST_ID ])) {
740
-                    $question_group->update_question_order($QST_ID, $this->_req_data['question_orders'][ $QST_ID ]);
739
+                if (isset($this->_req_data['question_orders'][$QST_ID])) {
740
+                    $question_group->update_question_order($QST_ID, $this->_req_data['question_orders'][$QST_ID]);
741 741
                 }
742 742
             }
743 743
         }
@@ -878,33 +878,33 @@  discard block
 block discarded – undo
878 878
         // echo "trash $trash";
879 879
         // var_dump($this->_req_data['checkbox']);die;
880 880
         if (isset($this->_req_data['checkbox'])) {
881
-            if (! empty($this->_req_data['checkbox']) && is_array($this->_req_data['checkbox'])) {
881
+            if ( ! empty($this->_req_data['checkbox']) && is_array($this->_req_data['checkbox'])) {
882 882
                 // if array has more than one element than success message should be plural
883 883
                 $success = count($this->_req_data['checkbox']) > 1 ? 2 : 1;
884 884
                 // cycle thru bulk action checkboxes
885 885
                 while (list($ID, $value) = each($this->_req_data['checkbox'])) {
886
-                    if (! $model->delete_or_restore_by_ID($trash, absint($ID))) {
886
+                    if ( ! $model->delete_or_restore_by_ID($trash, absint($ID))) {
887 887
                         $success = 0;
888 888
                     }
889 889
                 }
890 890
             } else {
891 891
                 // grab single id and delete
892 892
                 $ID = absint($this->_req_data['checkbox']);
893
-                if (! $model->delete_or_restore_by_ID($trash, $ID)) {
893
+                if ( ! $model->delete_or_restore_by_ID($trash, $ID)) {
894 894
                     $success = 0;
895 895
                 }
896 896
             }
897 897
         } else {
898 898
             // delete via trash link
899 899
             // grab single id and delete
900
-            $ID = absint($this->_req_data[ $model->primary_key_name() ]);
901
-            if (! $model->delete_or_restore_by_ID($trash, $ID)) {
900
+            $ID = absint($this->_req_data[$model->primary_key_name()]);
901
+            if ( ! $model->delete_or_restore_by_ID($trash, $ID)) {
902 902
                 $success = 0;
903 903
             }
904 904
         }
905 905
 
906 906
 
907
-        $action = $model instanceof EEM_Question ? 'default' : 'question_groups';// strtolower( $model->item_name(2) );
907
+        $action = $model instanceof EEM_Question ? 'default' : 'question_groups'; // strtolower( $model->item_name(2) );
908 908
         // echo "action :$action";
909 909
         // $action = 'questions' ? 'default' : $action;
910 910
         if ($trash) {
@@ -1024,7 +1024,7 @@  discard block
 block discarded – undo
1024 1024
             ? (int) $this->_req_data['curpage']
1025 1025
             : null;
1026 1026
 
1027
-        if (! empty($row_ids)) {
1027
+        if ( ! empty($row_ids)) {
1028 1028
             // figure out where we start the row_id count at for the current page.
1029 1029
             $qsgcount = empty($curpage) ? 0 : ($curpage - 1) * $perpage;
1030 1030
 
@@ -1033,7 +1033,7 @@  discard block
 block discarded – undo
1033 1033
                 // Update the questions when re-ordering
1034 1034
                 $updated = EEM_Question_Group::instance()->update(
1035 1035
                     array('QSG_order' => $qsgcount),
1036
-                    array(array('QSG_ID' => $row_ids[ $i ]))
1036
+                    array(array('QSG_ID' => $row_ids[$i]))
1037 1037
                 );
1038 1038
                 if ($updated === false) {
1039 1039
                     $success = false;
@@ -1079,7 +1079,7 @@  discard block
 block discarded – undo
1079 1079
         $this->_set_add_edit_form_tags('update_reg_form_settings');
1080 1080
         $this->_set_publish_post_box_vars(null, false, false, null, false);
1081 1081
         $this->_template_args['admin_page_content'] = EEH_Template::display_template(
1082
-            REGISTRATION_FORM_CAF_TEMPLATE_PATH . 'reg_form_settings.template.php',
1082
+            REGISTRATION_FORM_CAF_TEMPLATE_PATH.'reg_form_settings.template.php',
1083 1083
             $this->_template_args,
1084 1084
             true
1085 1085
         );
@@ -1216,7 +1216,7 @@  discard block
 block discarded – undo
1216 1216
                                 $prev_email_validation_level = 'basic';
1217 1217
                             }
1218 1218
                             // confirm our i18n email validation will work on the server
1219
-                            if (! $this->_verify_pcre_support($EE_Registration_Config, $email_validation_level)) {
1219
+                            if ( ! $this->_verify_pcre_support($EE_Registration_Config, $email_validation_level)) {
1220 1220
                                 // or reset email validation level to previous value
1221 1221
                                 $email_validation_level = $prev_email_validation_level;
1222 1222
                             }
@@ -1262,7 +1262,7 @@  discard block
 block discarded – undo
1262 1262
     private function _verify_pcre_support(EE_Registration_Config $EE_Registration_Config, $email_validation_level)
1263 1263
     {
1264 1264
         // first check that PCRE is enabled
1265
-        if (! defined('PREG_BAD_UTF8_ERROR')) {
1265
+        if ( ! defined('PREG_BAD_UTF8_ERROR')) {
1266 1266
             EE_Error::add_error(
1267 1267
                 sprintf(
1268 1268
                     esc_html__(
Please login to merge, or discard this patch.
espresso.php 1 patch
Indentation   +80 added lines, -80 removed lines patch added patch discarded remove patch
@@ -38,103 +38,103 @@
 block discarded – undo
38 38
  * @since           4.0
39 39
  */
40 40
 if (function_exists('espresso_version')) {
41
-    if (! function_exists('espresso_duplicate_plugin_error')) {
42
-        /**
43
-         *    espresso_duplicate_plugin_error
44
-         *    displays if more than one version of EE is activated at the same time
45
-         */
46
-        function espresso_duplicate_plugin_error()
47
-        {
48
-            ?>
41
+	if (! function_exists('espresso_duplicate_plugin_error')) {
42
+		/**
43
+		 *    espresso_duplicate_plugin_error
44
+		 *    displays if more than one version of EE is activated at the same time
45
+		 */
46
+		function espresso_duplicate_plugin_error()
47
+		{
48
+			?>
49 49
             <div class="error">
50 50
                 <p>
51 51
                     <?php
52
-                    echo esc_html__(
53
-                        'Can not run multiple versions of Event Espresso! One version has been automatically deactivated. Please verify that you have the correct version you want still active.',
54
-                        'event_espresso'
55
-                    ); ?>
52
+					echo esc_html__(
53
+						'Can not run multiple versions of Event Espresso! One version has been automatically deactivated. Please verify that you have the correct version you want still active.',
54
+						'event_espresso'
55
+					); ?>
56 56
                 </p>
57 57
             </div>
58 58
             <?php
59
-            espresso_deactivate_plugin(plugin_basename(__FILE__));
60
-        }
61
-    }
62
-    add_action('admin_notices', 'espresso_duplicate_plugin_error', 1);
59
+			espresso_deactivate_plugin(plugin_basename(__FILE__));
60
+		}
61
+	}
62
+	add_action('admin_notices', 'espresso_duplicate_plugin_error', 1);
63 63
 } else {
64
-    define('EE_MIN_PHP_VER_REQUIRED', '5.4.0');
65
-    if (! version_compare(PHP_VERSION, EE_MIN_PHP_VER_REQUIRED, '>=')) {
66
-        /**
67
-         * espresso_minimum_php_version_error
68
-         *
69
-         * @return void
70
-         */
71
-        function espresso_minimum_php_version_error()
72
-        {
73
-            ?>
64
+	define('EE_MIN_PHP_VER_REQUIRED', '5.4.0');
65
+	if (! version_compare(PHP_VERSION, EE_MIN_PHP_VER_REQUIRED, '>=')) {
66
+		/**
67
+		 * espresso_minimum_php_version_error
68
+		 *
69
+		 * @return void
70
+		 */
71
+		function espresso_minimum_php_version_error()
72
+		{
73
+			?>
74 74
             <div class="error">
75 75
                 <p>
76 76
                     <?php
77
-                    printf(
78
-                        esc_html__(
79
-                            'We\'re sorry, but Event Espresso requires PHP version %1$s or greater in order to operate. You are currently running version %2$s.%3$sIn order to update your version of PHP, you will need to contact your current hosting provider.%3$sFor information on stable PHP versions, please go to %4$s.',
80
-                            'event_espresso'
81
-                        ),
82
-                        EE_MIN_PHP_VER_REQUIRED,
83
-                        PHP_VERSION,
84
-                        '<br/>',
85
-                        '<a href="http://php.net/downloads.php">http://php.net/downloads.php</a>'
86
-                    );
87
-                    ?>
77
+					printf(
78
+						esc_html__(
79
+							'We\'re sorry, but Event Espresso requires PHP version %1$s or greater in order to operate. You are currently running version %2$s.%3$sIn order to update your version of PHP, you will need to contact your current hosting provider.%3$sFor information on stable PHP versions, please go to %4$s.',
80
+							'event_espresso'
81
+						),
82
+						EE_MIN_PHP_VER_REQUIRED,
83
+						PHP_VERSION,
84
+						'<br/>',
85
+						'<a href="http://php.net/downloads.php">http://php.net/downloads.php</a>'
86
+					);
87
+					?>
88 88
                 </p>
89 89
             </div>
90 90
             <?php
91
-            espresso_deactivate_plugin(plugin_basename(__FILE__));
92
-        }
91
+			espresso_deactivate_plugin(plugin_basename(__FILE__));
92
+		}
93 93
 
94
-        add_action('admin_notices', 'espresso_minimum_php_version_error', 1);
95
-    } else {
96
-        define('EVENT_ESPRESSO_MAIN_FILE', __FILE__);
97
-        /**
98
-         * espresso_version
99
-         * Returns the plugin version
100
-         *
101
-         * @return string
102
-         */
103
-        function espresso_version()
104
-        {
105
-            return apply_filters('FHEE__espresso__espresso_version', '4.9.62.rc.042');
106
-        }
94
+		add_action('admin_notices', 'espresso_minimum_php_version_error', 1);
95
+	} else {
96
+		define('EVENT_ESPRESSO_MAIN_FILE', __FILE__);
97
+		/**
98
+		 * espresso_version
99
+		 * Returns the plugin version
100
+		 *
101
+		 * @return string
102
+		 */
103
+		function espresso_version()
104
+		{
105
+			return apply_filters('FHEE__espresso__espresso_version', '4.9.62.rc.042');
106
+		}
107 107
 
108
-        /**
109
-         * espresso_plugin_activation
110
-         * adds a wp-option to indicate that EE has been activated via the WP admin plugins page
111
-         */
112
-        function espresso_plugin_activation()
113
-        {
114
-            update_option('ee_espresso_activation', true);
115
-        }
108
+		/**
109
+		 * espresso_plugin_activation
110
+		 * adds a wp-option to indicate that EE has been activated via the WP admin plugins page
111
+		 */
112
+		function espresso_plugin_activation()
113
+		{
114
+			update_option('ee_espresso_activation', true);
115
+		}
116 116
 
117
-        register_activation_hook(EVENT_ESPRESSO_MAIN_FILE, 'espresso_plugin_activation');
117
+		register_activation_hook(EVENT_ESPRESSO_MAIN_FILE, 'espresso_plugin_activation');
118 118
 
119
-        require_once __DIR__ . '/core/bootstrap_espresso.php';
120
-        bootstrap_espresso();
121
-    }
119
+		require_once __DIR__ . '/core/bootstrap_espresso.php';
120
+		bootstrap_espresso();
121
+	}
122 122
 }
123 123
 if (! function_exists('espresso_deactivate_plugin')) {
124
-    /**
125
-     *    deactivate_plugin
126
-     * usage:  espresso_deactivate_plugin( plugin_basename( __FILE__ ));
127
-     *
128
-     * @access public
129
-     * @param string $plugin_basename - the results of plugin_basename( __FILE__ ) for the plugin's main file
130
-     * @return    void
131
-     */
132
-    function espresso_deactivate_plugin($plugin_basename = '')
133
-    {
134
-        if (! function_exists('deactivate_plugins')) {
135
-            require_once ABSPATH . 'wp-admin/includes/plugin.php';
136
-        }
137
-        unset($_GET['activate'], $_REQUEST['activate']);
138
-        deactivate_plugins($plugin_basename);
139
-    }
124
+	/**
125
+	 *    deactivate_plugin
126
+	 * usage:  espresso_deactivate_plugin( plugin_basename( __FILE__ ));
127
+	 *
128
+	 * @access public
129
+	 * @param string $plugin_basename - the results of plugin_basename( __FILE__ ) for the plugin's main file
130
+	 * @return    void
131
+	 */
132
+	function espresso_deactivate_plugin($plugin_basename = '')
133
+	{
134
+		if (! function_exists('deactivate_plugins')) {
135
+			require_once ABSPATH . 'wp-admin/includes/plugin.php';
136
+		}
137
+		unset($_GET['activate'], $_REQUEST['activate']);
138
+		deactivate_plugins($plugin_basename);
139
+	}
140 140
 }
Please login to merge, or discard this patch.
core/EE_System.core.php 1 patch
Indentation   +1256 added lines, -1256 removed lines patch added patch discarded remove patch
@@ -28,1260 +28,1260 @@
 block discarded – undo
28 28
 {
29 29
 
30 30
 
31
-    /**
32
-     * indicates this is a 'normal' request. Ie, not activation, nor upgrade, nor activation.
33
-     * So examples of this would be a normal GET request on the frontend or backend, or a POST, etc
34
-     */
35
-    const req_type_normal = 0;
36
-
37
-    /**
38
-     * Indicates this is a brand new installation of EE so we should install
39
-     * tables and default data etc
40
-     */
41
-    const req_type_new_activation = 1;
42
-
43
-    /**
44
-     * we've detected that EE has been reactivated (or EE was activated during maintenance mode,
45
-     * and we just exited maintenance mode). We MUST check the database is setup properly
46
-     * and that default data is setup too
47
-     */
48
-    const req_type_reactivation = 2;
49
-
50
-    /**
51
-     * indicates that EE has been upgraded since its previous request.
52
-     * We may have data migration scripts to call and will want to trigger maintenance mode
53
-     */
54
-    const req_type_upgrade = 3;
55
-
56
-    /**
57
-     * TODO  will detect that EE has been DOWNGRADED. We probably don't want to run in this case...
58
-     */
59
-    const req_type_downgrade = 4;
60
-
61
-    /**
62
-     * @deprecated since version 4.6.0.dev.006
63
-     * Now whenever a new_activation is detected the request type is still just
64
-     * new_activation (same for reactivation, upgrade, downgrade etc), but if we'r ein maintenance mode
65
-     * EE_System::initialize_db_if_no_migrations_required and EE_Addon::initialize_db_if_no_migrations_required
66
-     * will instead enqueue that EE plugin's db initialization for when we're taken out of maintenance mode.
67
-     * (Specifically, when the migration manager indicates migrations are finished
68
-     * EE_Data_Migration_Manager::initialize_db_for_enqueued_ee_plugins() will be called)
69
-     */
70
-    const req_type_activation_but_not_installed = 5;
71
-
72
-    /**
73
-     * option prefix for recording the activation history (like core's "espresso_db_update") of addons
74
-     */
75
-    const addon_activation_history_option_prefix = 'ee_addon_activation_history_';
76
-
77
-
78
-    /**
79
-     * @var EE_System $_instance
80
-     */
81
-    private static $_instance;
82
-
83
-    /**
84
-     * @var EE_Registry $registry
85
-     */
86
-    private $registry;
87
-
88
-    /**
89
-     * @var LoaderInterface $loader
90
-     */
91
-    private $loader;
92
-
93
-    /**
94
-     * @var EE_Capabilities $capabilities
95
-     */
96
-    private $capabilities;
97
-
98
-    /**
99
-     * @var RequestInterface $request
100
-     */
101
-    private $request;
102
-
103
-    /**
104
-     * @var EE_Maintenance_Mode $maintenance_mode
105
-     */
106
-    private $maintenance_mode;
107
-
108
-    /**
109
-     * Stores which type of request this is, options being one of the constants on EE_System starting with req_type_*.
110
-     * It can be a brand-new activation, a reactivation, an upgrade, a downgrade, or a normal request.
111
-     *
112
-     * @var int $_req_type
113
-     */
114
-    private $_req_type;
115
-
116
-    /**
117
-     * Whether or not there was a non-micro version change in EE core version during this request
118
-     *
119
-     * @var boolean $_major_version_change
120
-     */
121
-    private $_major_version_change = false;
122
-
123
-    /**
124
-     * A Context DTO dedicated solely to identifying the current request type.
125
-     *
126
-     * @var RequestTypeContextCheckerInterface $request_type
127
-     */
128
-    private $request_type;
129
-
130
-
131
-    /**
132
-     * @singleton method used to instantiate class object
133
-     * @param EE_Registry|null         $registry
134
-     * @param LoaderInterface|null     $loader
135
-     * @param RequestInterface|null    $request
136
-     * @param EE_Maintenance_Mode|null $maintenance_mode
137
-     * @return EE_System
138
-     */
139
-    public static function instance(
140
-        EE_Registry $registry = null,
141
-        LoaderInterface $loader = null,
142
-        RequestInterface $request = null,
143
-        EE_Maintenance_Mode $maintenance_mode = null
144
-    ) {
145
-        // check if class object is instantiated
146
-        if (! self::$_instance instanceof EE_System) {
147
-            self::$_instance = new self($registry, $loader, $request, $maintenance_mode);
148
-        }
149
-        return self::$_instance;
150
-    }
151
-
152
-
153
-    /**
154
-     * resets the instance and returns it
155
-     *
156
-     * @return EE_System
157
-     */
158
-    public static function reset()
159
-    {
160
-        self::$_instance->_req_type = null;
161
-        // make sure none of the old hooks are left hanging around
162
-        remove_all_actions('AHEE__EE_System__perform_activations_upgrades_and_migrations');
163
-        // we need to reset the migration manager in order for it to detect DMSs properly
164
-        EE_Data_Migration_Manager::reset();
165
-        self::instance()->detect_activations_or_upgrades();
166
-        self::instance()->perform_activations_upgrades_and_migrations();
167
-        return self::instance();
168
-    }
169
-
170
-
171
-    /**
172
-     * sets hooks for running rest of system
173
-     * provides "AHEE__EE_System__construct__complete" hook for EE Addons to use as their starting point
174
-     * starting EE Addons from any other point may lead to problems
175
-     *
176
-     * @param EE_Registry         $registry
177
-     * @param LoaderInterface     $loader
178
-     * @param RequestInterface    $request
179
-     * @param EE_Maintenance_Mode $maintenance_mode
180
-     */
181
-    private function __construct(
182
-        EE_Registry $registry,
183
-        LoaderInterface $loader,
184
-        RequestInterface $request,
185
-        EE_Maintenance_Mode $maintenance_mode
186
-    ) {
187
-        $this->registry = $registry;
188
-        $this->loader = $loader;
189
-        $this->request = $request;
190
-        $this->maintenance_mode = $maintenance_mode;
191
-        do_action('AHEE__EE_System__construct__begin', $this);
192
-        add_action(
193
-            'AHEE__EE_Bootstrap__load_espresso_addons',
194
-            array($this, 'loadCapabilities'),
195
-            5
196
-        );
197
-        add_action(
198
-            'AHEE__EE_Bootstrap__load_espresso_addons',
199
-            array($this, 'loadCommandBus'),
200
-            7
201
-        );
202
-        add_action(
203
-            'AHEE__EE_Bootstrap__load_espresso_addons',
204
-            array($this, 'loadPluginApi'),
205
-            9
206
-        );
207
-        // allow addons to load first so that they can register autoloaders, set hooks for running DMS's, etc
208
-        add_action(
209
-            'AHEE__EE_Bootstrap__load_espresso_addons',
210
-            array($this, 'load_espresso_addons')
211
-        );
212
-        // when an ee addon is activated, we want to call the core hook(s) again
213
-        // because the newly-activated addon didn't get a chance to run at all
214
-        add_action('activate_plugin', array($this, 'load_espresso_addons'), 1);
215
-        // detect whether install or upgrade
216
-        add_action(
217
-            'AHEE__EE_Bootstrap__detect_activations_or_upgrades',
218
-            array($this, 'detect_activations_or_upgrades'),
219
-            3
220
-        );
221
-        // load EE_Config, EE_Textdomain, etc
222
-        add_action(
223
-            'AHEE__EE_Bootstrap__load_core_configuration',
224
-            array($this, 'load_core_configuration'),
225
-            5
226
-        );
227
-        // load EE_Config, EE_Textdomain, etc
228
-        add_action(
229
-            'AHEE__EE_Bootstrap__register_shortcodes_modules_and_widgets',
230
-            array($this, 'register_shortcodes_modules_and_widgets'),
231
-            7
232
-        );
233
-        // you wanna get going? I wanna get going... let's get going!
234
-        add_action(
235
-            'AHEE__EE_Bootstrap__brew_espresso',
236
-            array($this, 'brew_espresso'),
237
-            9
238
-        );
239
-        // other housekeeping
240
-        // exclude EE critical pages from wp_list_pages
241
-        add_filter(
242
-            'wp_list_pages_excludes',
243
-            array($this, 'remove_pages_from_wp_list_pages'),
244
-            10
245
-        );
246
-        // ALL EE Addons should use the following hook point to attach their initial setup too
247
-        // it's extremely important for EE Addons to register any class autoloaders so that they can be available when the EE_Config loads
248
-        do_action('AHEE__EE_System__construct__complete', $this);
249
-    }
250
-
251
-
252
-    /**
253
-     * load and setup EE_Capabilities
254
-     *
255
-     * @return void
256
-     * @throws EE_Error
257
-     */
258
-    public function loadCapabilities()
259
-    {
260
-        $this->capabilities = $this->loader->getShared('EE_Capabilities');
261
-        add_action(
262
-            'AHEE__EE_Capabilities__init_caps__before_initialization',
263
-            function () {
264
-                LoaderFactory::getLoader()->getShared('EE_Payment_Method_Manager');
265
-            }
266
-        );
267
-    }
268
-
269
-
270
-    /**
271
-     * create and cache the CommandBus, and also add middleware
272
-     * The CapChecker middleware requires the use of EE_Capabilities
273
-     * which is why we need to load the CommandBus after Caps are set up
274
-     *
275
-     * @return void
276
-     * @throws EE_Error
277
-     */
278
-    public function loadCommandBus()
279
-    {
280
-        $this->loader->getShared(
281
-            'CommandBusInterface',
282
-            array(
283
-                null,
284
-                apply_filters(
285
-                    'FHEE__EE_Load_Espresso_Core__handle_request__CommandBus_middleware',
286
-                    array(
287
-                        $this->loader->getShared('EventEspresso\core\services\commands\middleware\CapChecker'),
288
-                        $this->loader->getShared('EventEspresso\core\services\commands\middleware\AddActionHook'),
289
-                    )
290
-                ),
291
-            )
292
-        );
293
-    }
294
-
295
-
296
-    /**
297
-     * @return void
298
-     * @throws EE_Error
299
-     */
300
-    public function loadPluginApi()
301
-    {
302
-        // set autoloaders for all of the classes implementing EEI_Plugin_API
303
-        // which provide helpers for EE plugin authors to more easily register certain components with EE.
304
-        EEH_Autoloader::instance()->register_autoloaders_for_each_file_in_folder(EE_LIBRARIES . 'plugin_api');
305
-        $this->loader->getShared('EE_Request_Handler');
306
-    }
307
-
308
-
309
-    /**
310
-     * @param string $addon_name
311
-     * @param string $version_constant
312
-     * @param string $min_version_required
313
-     * @param string $load_callback
314
-     * @param string $plugin_file_constant
315
-     * @return void
316
-     */
317
-    private function deactivateIncompatibleAddon(
318
-        $addon_name,
319
-        $version_constant,
320
-        $min_version_required,
321
-        $load_callback,
322
-        $plugin_file_constant
323
-    ) {
324
-        if (! defined($version_constant)) {
325
-            return;
326
-        }
327
-        $addon_version = constant($version_constant);
328
-        if ($addon_version && version_compare($addon_version, $min_version_required, '<')) {
329
-            remove_action('AHEE__EE_System__load_espresso_addons', $load_callback);
330
-            if (! function_exists('deactivate_plugins')) {
331
-                require_once ABSPATH . 'wp-admin/includes/plugin.php';
332
-            }
333
-            deactivate_plugins(plugin_basename(constant($plugin_file_constant)));
334
-            unset($_GET['activate'], $_REQUEST['activate'], $_GET['activate-multi'], $_REQUEST['activate-multi']);
335
-            EE_Error::add_error(
336
-                sprintf(
337
-                    esc_html__(
338
-                        'We\'re sorry, but the Event Espresso %1$s addon was deactivated because version %2$s or higher is required with this version of Event Espresso core.',
339
-                        'event_espresso'
340
-                    ),
341
-                    $addon_name,
342
-                    $min_version_required
343
-                ),
344
-                __FILE__,
345
-                __FUNCTION__ . "({$addon_name})",
346
-                __LINE__
347
-            );
348
-            EE_Error::get_notices(false, true);
349
-        }
350
-    }
351
-
352
-
353
-    /**
354
-     * load_espresso_addons
355
-     * allow addons to load first so that they can set hooks for running DMS's, etc
356
-     * this is hooked into both:
357
-     *    'AHEE__EE_Bootstrap__load_core_configuration'
358
-     *        which runs during the WP 'plugins_loaded' action at priority 5
359
-     *    and the WP 'activate_plugin' hook point
360
-     *
361
-     * @access public
362
-     * @return void
363
-     */
364
-    public function load_espresso_addons()
365
-    {
366
-        $this->deactivateIncompatibleAddon(
367
-            'Wait Lists',
368
-            'EE_WAIT_LISTS_VERSION',
369
-            '1.0.0.beta.074',
370
-            'load_espresso_wait_lists',
371
-            'EE_WAIT_LISTS_PLUGIN_FILE'
372
-        );
373
-        $this->deactivateIncompatibleAddon(
374
-            'Automated Upcoming Event Notifications',
375
-            'EE_AUTOMATED_UPCOMING_EVENT_NOTIFICATION_VERSION',
376
-            '1.0.0.beta.091',
377
-            'load_espresso_automated_upcoming_event_notification',
378
-            'EE_AUTOMATED_UPCOMING_EVENT_NOTIFICATION_PLUGIN_FILE'
379
-        );
380
-        do_action('AHEE__EE_System__load_espresso_addons');
381
-        // if the WP API basic auth plugin isn't already loaded, load it now.
382
-        // We want it for mobile apps. Just include the entire plugin
383
-        // also, don't load the basic auth when a plugin is getting activated, because
384
-        // it could be the basic auth plugin, and it doesn't check if its methods are already defined
385
-        // and causes a fatal error
386
-        if ($this->request->getRequestParam('activate') !== 'true'
387
-            && ! function_exists('json_basic_auth_handler')
388
-            && ! function_exists('json_basic_auth_error')
389
-            && ! in_array(
390
-                $this->request->getRequestParam('action'),
391
-                array('activate', 'activate-selected'),
392
-                true
393
-            )
394
-        ) {
395
-            include_once EE_THIRD_PARTY . 'wp-api-basic-auth' . DS . 'basic-auth.php';
396
-        }
397
-        do_action('AHEE__EE_System__load_espresso_addons__complete');
398
-    }
399
-
400
-
401
-    /**
402
-     * detect_activations_or_upgrades
403
-     * Checks for activation or upgrade of core first;
404
-     * then also checks if any registered addons have been activated or upgraded
405
-     * This is hooked into 'AHEE__EE_Bootstrap__detect_activations_or_upgrades'
406
-     * which runs during the WP 'plugins_loaded' action at priority 3
407
-     *
408
-     * @access public
409
-     * @return void
410
-     */
411
-    public function detect_activations_or_upgrades()
412
-    {
413
-        // first off: let's make sure to handle core
414
-        $this->detect_if_activation_or_upgrade();
415
-        foreach ($this->registry->addons as $addon) {
416
-            if ($addon instanceof EE_Addon) {
417
-                // detect teh request type for that addon
418
-                $addon->detect_activation_or_upgrade();
419
-            }
420
-        }
421
-    }
422
-
423
-
424
-    /**
425
-     * detect_if_activation_or_upgrade
426
-     * Takes care of detecting whether this is a brand new install or code upgrade,
427
-     * and either setting up the DB or setting up maintenance mode etc.
428
-     *
429
-     * @access public
430
-     * @return void
431
-     */
432
-    public function detect_if_activation_or_upgrade()
433
-    {
434
-        do_action('AHEE__EE_System___detect_if_activation_or_upgrade__begin');
435
-        // check if db has been updated, or if its a brand-new installation
436
-        $espresso_db_update = $this->fix_espresso_db_upgrade_option();
437
-        $request_type = $this->detect_req_type($espresso_db_update);
438
-        // EEH_Debug_Tools::printr( $request_type, '$request_type', __FILE__, __LINE__ );
439
-        switch ($request_type) {
440
-            case EE_System::req_type_new_activation:
441
-                do_action('AHEE__EE_System__detect_if_activation_or_upgrade__new_activation');
442
-                $this->_handle_core_version_change($espresso_db_update);
443
-                break;
444
-            case EE_System::req_type_reactivation:
445
-                do_action('AHEE__EE_System__detect_if_activation_or_upgrade__reactivation');
446
-                $this->_handle_core_version_change($espresso_db_update);
447
-                break;
448
-            case EE_System::req_type_upgrade:
449
-                do_action('AHEE__EE_System__detect_if_activation_or_upgrade__upgrade');
450
-                // migrations may be required now that we've upgraded
451
-                $this->maintenance_mode->set_maintenance_mode_if_db_old();
452
-                $this->_handle_core_version_change($espresso_db_update);
453
-                break;
454
-            case EE_System::req_type_downgrade:
455
-                do_action('AHEE__EE_System__detect_if_activation_or_upgrade__downgrade');
456
-                // its possible migrations are no longer required
457
-                $this->maintenance_mode->set_maintenance_mode_if_db_old();
458
-                $this->_handle_core_version_change($espresso_db_update);
459
-                break;
460
-            case EE_System::req_type_normal:
461
-            default:
462
-                break;
463
-        }
464
-        do_action('AHEE__EE_System__detect_if_activation_or_upgrade__complete');
465
-    }
466
-
467
-
468
-    /**
469
-     * Updates the list of installed versions and sets hooks for
470
-     * initializing the database later during the request
471
-     *
472
-     * @param array $espresso_db_update
473
-     */
474
-    private function _handle_core_version_change($espresso_db_update)
475
-    {
476
-        $this->update_list_of_installed_versions($espresso_db_update);
477
-        // get ready to verify the DB is ok (provided we aren't in maintenance mode, of course)
478
-        add_action(
479
-            'AHEE__EE_System__perform_activations_upgrades_and_migrations',
480
-            array($this, 'initialize_db_if_no_migrations_required')
481
-        );
482
-    }
483
-
484
-
485
-    /**
486
-     * standardizes the wp option 'espresso_db_upgrade' which actually stores
487
-     * information about what versions of EE have been installed and activated,
488
-     * NOT necessarily the state of the database
489
-     *
490
-     * @param mixed $espresso_db_update           the value of the WordPress option.
491
-     *                                            If not supplied, fetches it from the options table
492
-     * @return array the correct value of 'espresso_db_upgrade', after saving it, if it needed correction
493
-     */
494
-    private function fix_espresso_db_upgrade_option($espresso_db_update = null)
495
-    {
496
-        do_action('FHEE__EE_System__manage_fix_espresso_db_upgrade_option__begin', $espresso_db_update);
497
-        if (! $espresso_db_update) {
498
-            $espresso_db_update = get_option('espresso_db_update');
499
-        }
500
-        // check that option is an array
501
-        if (! is_array($espresso_db_update)) {
502
-            // if option is FALSE, then it never existed
503
-            if ($espresso_db_update === false) {
504
-                // make $espresso_db_update an array and save option with autoload OFF
505
-                $espresso_db_update = array();
506
-                add_option('espresso_db_update', $espresso_db_update, '', 'no');
507
-            } else {
508
-                // option is NOT FALSE but also is NOT an array, so make it an array and save it
509
-                $espresso_db_update = array($espresso_db_update => array());
510
-                update_option('espresso_db_update', $espresso_db_update);
511
-            }
512
-        } else {
513
-            $corrected_db_update = array();
514
-            // if IS an array, but is it an array where KEYS are version numbers, and values are arrays?
515
-            foreach ($espresso_db_update as $should_be_version_string => $should_be_array) {
516
-                if (is_int($should_be_version_string) && ! is_array($should_be_array)) {
517
-                    // the key is an int, and the value IS NOT an array
518
-                    // so it must be numerically-indexed, where values are versions installed...
519
-                    // fix it!
520
-                    $version_string = $should_be_array;
521
-                    $corrected_db_update[ $version_string ] = array('unknown-date');
522
-                } else {
523
-                    // ok it checks out
524
-                    $corrected_db_update[ $should_be_version_string ] = $should_be_array;
525
-                }
526
-            }
527
-            $espresso_db_update = $corrected_db_update;
528
-            update_option('espresso_db_update', $espresso_db_update);
529
-        }
530
-        do_action('FHEE__EE_System__manage_fix_espresso_db_upgrade_option__complete', $espresso_db_update);
531
-        return $espresso_db_update;
532
-    }
533
-
534
-
535
-    /**
536
-     * Does the traditional work of setting up the plugin's database and adding default data.
537
-     * If migration script/process did not exist, this is what would happen on every activation/reactivation/upgrade.
538
-     * NOTE: if we're in maintenance mode (which would be the case if we detect there are data
539
-     * migration scripts that need to be run and a version change happens), enqueues core for database initialization,
540
-     * so that it will be done when migrations are finished
541
-     *
542
-     * @param boolean $initialize_addons_too if true, we double-check addons' database tables etc too;
543
-     * @param boolean $verify_schema         if true will re-check the database tables have the correct schema.
544
-     *                                       This is a resource-intensive job
545
-     *                                       so we prefer to only do it when necessary
546
-     * @return void
547
-     * @throws EE_Error
548
-     */
549
-    public function initialize_db_if_no_migrations_required($initialize_addons_too = false, $verify_schema = true)
550
-    {
551
-        $request_type = $this->detect_req_type();
552
-        // only initialize system if we're not in maintenance mode.
553
-        if ($this->maintenance_mode->level() !== EE_Maintenance_Mode::level_2_complete_maintenance) {
554
-            /** @var EventEspresso\core\domain\services\custom_post_types\RewriteRules $rewrite_rules */
555
-            $rewrite_rules = $this->loader->getShared(
556
-                'EventEspresso\core\domain\services\custom_post_types\RewriteRules'
557
-            );
558
-            $rewrite_rules->flush();
559
-            if ($verify_schema) {
560
-                EEH_Activation::initialize_db_and_folders();
561
-            }
562
-            EEH_Activation::initialize_db_content();
563
-            EEH_Activation::system_initialization();
564
-            if ($initialize_addons_too) {
565
-                $this->initialize_addons();
566
-            }
567
-        } else {
568
-            EE_Data_Migration_Manager::instance()->enqueue_db_initialization_for('Core');
569
-        }
570
-        if ($request_type === EE_System::req_type_new_activation
571
-            || $request_type === EE_System::req_type_reactivation
572
-            || (
573
-                $request_type === EE_System::req_type_upgrade
574
-                && $this->is_major_version_change()
575
-            )
576
-        ) {
577
-            add_action('AHEE__EE_System__initialize_last', array($this, 'redirect_to_about_ee'), 9);
578
-        }
579
-    }
580
-
581
-
582
-    /**
583
-     * Initializes the db for all registered addons
584
-     *
585
-     * @throws EE_Error
586
-     */
587
-    public function initialize_addons()
588
-    {
589
-        // foreach registered addon, make sure its db is up-to-date too
590
-        foreach ($this->registry->addons as $addon) {
591
-            if ($addon instanceof EE_Addon) {
592
-                $addon->initialize_db_if_no_migrations_required();
593
-            }
594
-        }
595
-    }
596
-
597
-
598
-    /**
599
-     * Adds the current code version to the saved wp option which stores a list of all ee versions ever installed.
600
-     *
601
-     * @param    array  $version_history
602
-     * @param    string $current_version_to_add version to be added to the version history
603
-     * @return    boolean success as to whether or not this option was changed
604
-     */
605
-    public function update_list_of_installed_versions($version_history = null, $current_version_to_add = null)
606
-    {
607
-        if (! $version_history) {
608
-            $version_history = $this->fix_espresso_db_upgrade_option($version_history);
609
-        }
610
-        if ($current_version_to_add === null) {
611
-            $current_version_to_add = espresso_version();
612
-        }
613
-        $version_history[ $current_version_to_add ][] = date('Y-m-d H:i:s', time());
614
-        // re-save
615
-        return update_option('espresso_db_update', $version_history);
616
-    }
617
-
618
-
619
-    /**
620
-     * Detects if the current version indicated in the has existed in the list of
621
-     * previously-installed versions of EE (espresso_db_update). Does NOT modify it (ie, no side-effect)
622
-     *
623
-     * @param array $espresso_db_update array from the wp option stored under the name 'espresso_db_update'.
624
-     *                                  If not supplied, fetches it from the options table.
625
-     *                                  Also, caches its result so later parts of the code can also know whether
626
-     *                                  there's been an update or not. This way we can add the current version to
627
-     *                                  espresso_db_update, but still know if this is a new install or not
628
-     * @return int one of the constants on EE_System::req_type_
629
-     */
630
-    public function detect_req_type($espresso_db_update = null)
631
-    {
632
-        if ($this->_req_type === null) {
633
-            $espresso_db_update = ! empty($espresso_db_update)
634
-                ? $espresso_db_update
635
-                : $this->fix_espresso_db_upgrade_option();
636
-            $this->_req_type = EE_System::detect_req_type_given_activation_history(
637
-                $espresso_db_update,
638
-                'ee_espresso_activation',
639
-                espresso_version()
640
-            );
641
-            $this->_major_version_change = $this->_detect_major_version_change($espresso_db_update);
642
-            $this->request->setIsActivation($this->_req_type !== EE_System::req_type_normal);
643
-        }
644
-        return $this->_req_type;
645
-    }
646
-
647
-
648
-    /**
649
-     * Returns whether or not there was a non-micro version change (ie, change in either
650
-     * the first or second number in the version. Eg 4.9.0.rc.001 to 4.10.0.rc.000,
651
-     * but not 4.9.0.rc.0001 to 4.9.1.rc.0001
652
-     *
653
-     * @param $activation_history
654
-     * @return bool
655
-     */
656
-    private function _detect_major_version_change($activation_history)
657
-    {
658
-        $previous_version = EE_System::_get_most_recently_active_version_from_activation_history($activation_history);
659
-        $previous_version_parts = explode('.', $previous_version);
660
-        $current_version_parts = explode('.', espresso_version());
661
-        return isset($previous_version_parts[0], $previous_version_parts[1], $current_version_parts[0], $current_version_parts[1])
662
-               && ($previous_version_parts[0] !== $current_version_parts[0]
663
-                   || $previous_version_parts[1] !== $current_version_parts[1]
664
-               );
665
-    }
666
-
667
-
668
-    /**
669
-     * Returns true if either the major or minor version of EE changed during this request.
670
-     * Eg 4.9.0.rc.001 to 4.10.0.rc.000, but not 4.9.0.rc.0001 to 4.9.1.rc.0001
671
-     *
672
-     * @return bool
673
-     */
674
-    public function is_major_version_change()
675
-    {
676
-        return $this->_major_version_change;
677
-    }
678
-
679
-
680
-    /**
681
-     * Determines the request type for any ee addon, given three piece of info: the current array of activation
682
-     * histories (for core that' 'espresso_db_update' wp option); the name of the WordPress option which is temporarily
683
-     * set upon activation of the plugin (for core it's 'ee_espresso_activation'); and the version that this plugin was
684
-     * just activated to (for core that will always be espresso_version())
685
-     *
686
-     * @param array  $activation_history_for_addon     the option's value which stores the activation history for this
687
-     *                                                 ee plugin. for core that's 'espresso_db_update'
688
-     * @param string $activation_indicator_option_name the name of the WordPress option that is temporarily set to
689
-     *                                                 indicate that this plugin was just activated
690
-     * @param string $version_to_upgrade_to            the version that was just upgraded to (for core that will be
691
-     *                                                 espresso_version())
692
-     * @return int one of the constants on EE_System::req_type_*
693
-     */
694
-    public static function detect_req_type_given_activation_history(
695
-        $activation_history_for_addon,
696
-        $activation_indicator_option_name,
697
-        $version_to_upgrade_to
698
-    ) {
699
-        $version_is_higher = self::_new_version_is_higher($activation_history_for_addon, $version_to_upgrade_to);
700
-        if ($activation_history_for_addon) {
701
-            // it exists, so this isn't a completely new install
702
-            // check if this version already in that list of previously installed versions
703
-            if (! isset($activation_history_for_addon[ $version_to_upgrade_to ])) {
704
-                // it a version we haven't seen before
705
-                if ($version_is_higher === 1) {
706
-                    $req_type = EE_System::req_type_upgrade;
707
-                } else {
708
-                    $req_type = EE_System::req_type_downgrade;
709
-                }
710
-                delete_option($activation_indicator_option_name);
711
-            } else {
712
-                // its not an update. maybe a reactivation?
713
-                if (get_option($activation_indicator_option_name, false)) {
714
-                    if ($version_is_higher === -1) {
715
-                        $req_type = EE_System::req_type_downgrade;
716
-                    } elseif ($version_is_higher === 0) {
717
-                        // we've seen this version before, but it's an activation. must be a reactivation
718
-                        $req_type = EE_System::req_type_reactivation;
719
-                    } else {// $version_is_higher === 1
720
-                        $req_type = EE_System::req_type_upgrade;
721
-                    }
722
-                    delete_option($activation_indicator_option_name);
723
-                } else {
724
-                    // we've seen this version before and the activation indicate doesn't show it was just activated
725
-                    if ($version_is_higher === -1) {
726
-                        $req_type = EE_System::req_type_downgrade;
727
-                    } elseif ($version_is_higher === 0) {
728
-                        // we've seen this version before and it's not an activation. its normal request
729
-                        $req_type = EE_System::req_type_normal;
730
-                    } else {// $version_is_higher === 1
731
-                        $req_type = EE_System::req_type_upgrade;
732
-                    }
733
-                }
734
-            }
735
-        } else {
736
-            // brand new install
737
-            $req_type = EE_System::req_type_new_activation;
738
-            delete_option($activation_indicator_option_name);
739
-        }
740
-        return $req_type;
741
-    }
742
-
743
-
744
-    /**
745
-     * Detects if the $version_to_upgrade_to is higher than the most recent version in
746
-     * the $activation_history_for_addon
747
-     *
748
-     * @param array  $activation_history_for_addon (keys are versions, values are arrays of times activated,
749
-     *                                             sometimes containing 'unknown-date'
750
-     * @param string $version_to_upgrade_to        (current version)
751
-     * @return int results of version_compare( $version_to_upgrade_to, $most_recently_active_version ).
752
-     *                                             ie, -1 if $version_to_upgrade_to is LOWER (downgrade);
753
-     *                                             0 if $version_to_upgrade_to MATCHES (reactivation or normal request);
754
-     *                                             1 if $version_to_upgrade_to is HIGHER (upgrade) ;
755
-     */
756
-    private static function _new_version_is_higher($activation_history_for_addon, $version_to_upgrade_to)
757
-    {
758
-        // find the most recently-activated version
759
-        $most_recently_active_version =
760
-            EE_System::_get_most_recently_active_version_from_activation_history($activation_history_for_addon);
761
-        return version_compare($version_to_upgrade_to, $most_recently_active_version);
762
-    }
763
-
764
-
765
-    /**
766
-     * Gets the most recently active version listed in the activation history,
767
-     * and if none are found (ie, it's a brand new install) returns '0.0.0.dev.000'.
768
-     *
769
-     * @param array $activation_history  (keys are versions, values are arrays of times activated,
770
-     *                                   sometimes containing 'unknown-date'
771
-     * @return string
772
-     */
773
-    private static function _get_most_recently_active_version_from_activation_history($activation_history)
774
-    {
775
-        $most_recently_active_version_activation = '1970-01-01 00:00:00';
776
-        $most_recently_active_version = '0.0.0.dev.000';
777
-        if (is_array($activation_history)) {
778
-            foreach ($activation_history as $version => $times_activated) {
779
-                // check there is a record of when this version was activated. Otherwise,
780
-                // mark it as unknown
781
-                if (! $times_activated) {
782
-                    $times_activated = array('unknown-date');
783
-                }
784
-                if (is_string($times_activated)) {
785
-                    $times_activated = array($times_activated);
786
-                }
787
-                foreach ($times_activated as $an_activation) {
788
-                    if ($an_activation !== 'unknown-date'
789
-                        && $an_activation
790
-                           > $most_recently_active_version_activation) {
791
-                        $most_recently_active_version = $version;
792
-                        $most_recently_active_version_activation = $an_activation === 'unknown-date'
793
-                            ? '1970-01-01 00:00:00'
794
-                            : $an_activation;
795
-                    }
796
-                }
797
-            }
798
-        }
799
-        return $most_recently_active_version;
800
-    }
801
-
802
-
803
-    /**
804
-     * This redirects to the about EE page after activation
805
-     *
806
-     * @return void
807
-     */
808
-    public function redirect_to_about_ee()
809
-    {
810
-        $notices = EE_Error::get_notices(false);
811
-        // if current user is an admin and it's not an ajax or rest request
812
-        if (! isset($notices['errors'])
813
-            && $this->request->isAdmin()
814
-            && apply_filters(
815
-                'FHEE__EE_System__redirect_to_about_ee__do_redirect',
816
-                $this->capabilities->current_user_can('manage_options', 'espresso_about_default')
817
-            )
818
-        ) {
819
-            $query_params = array('page' => 'espresso_about');
820
-            if (EE_System::instance()->detect_req_type() === EE_System::req_type_new_activation) {
821
-                $query_params['new_activation'] = true;
822
-            }
823
-            if (EE_System::instance()->detect_req_type() === EE_System::req_type_reactivation) {
824
-                $query_params['reactivation'] = true;
825
-            }
826
-            $url = add_query_arg($query_params, admin_url('admin.php'));
827
-            wp_safe_redirect($url);
828
-            exit();
829
-        }
830
-    }
831
-
832
-
833
-    /**
834
-     * load_core_configuration
835
-     * this is hooked into 'AHEE__EE_Bootstrap__load_core_configuration'
836
-     * which runs during the WP 'plugins_loaded' action at priority 5
837
-     *
838
-     * @return void
839
-     * @throws ReflectionException
840
-     */
841
-    public function load_core_configuration()
842
-    {
843
-        do_action('AHEE__EE_System__load_core_configuration__begin', $this);
844
-        $this->loader->getShared('EE_Load_Textdomain');
845
-        // load textdomain
846
-        EE_Load_Textdomain::load_textdomain();
847
-        // load and setup EE_Config and EE_Network_Config
848
-        $config = $this->loader->getShared('EE_Config');
849
-        $this->loader->getShared('EE_Network_Config');
850
-        // setup autoloaders
851
-        // enable logging?
852
-        if ($config->admin->use_full_logging) {
853
-            $this->loader->getShared('EE_Log');
854
-        }
855
-        // check for activation errors
856
-        $activation_errors = get_option('ee_plugin_activation_errors', false);
857
-        if ($activation_errors) {
858
-            EE_Error::add_error($activation_errors, __FILE__, __FUNCTION__, __LINE__);
859
-            update_option('ee_plugin_activation_errors', false);
860
-        }
861
-        // get model names
862
-        $this->_parse_model_names();
863
-        // load caf stuff a chance to play during the activation process too.
864
-        $this->_maybe_brew_regular();
865
-        // configure custom post type definitions
866
-        $this->loader->getShared('EventEspresso\core\domain\entities\custom_post_types\CustomTaxonomyDefinitions');
867
-        $this->loader->getShared('EventEspresso\core\domain\entities\custom_post_types\CustomPostTypeDefinitions');
868
-        do_action('AHEE__EE_System__load_core_configuration__complete', $this);
869
-    }
870
-
871
-
872
-    /**
873
-     * cycles through all of the models/*.model.php files, and assembles an array of model names
874
-     *
875
-     * @return void
876
-     * @throws ReflectionException
877
-     */
878
-    private function _parse_model_names()
879
-    {
880
-        // get all the files in the EE_MODELS folder that end in .model.php
881
-        $models = glob(EE_MODELS . '*.model.php');
882
-        $model_names = array();
883
-        $non_abstract_db_models = array();
884
-        foreach ($models as $model) {
885
-            // get model classname
886
-            $classname = EEH_File::get_classname_from_filepath_with_standard_filename($model);
887
-            $short_name = str_replace('EEM_', '', $classname);
888
-            $reflectionClass = new ReflectionClass($classname);
889
-            if ($reflectionClass->isSubclassOf('EEM_Base') && ! $reflectionClass->isAbstract()) {
890
-                $non_abstract_db_models[ $short_name ] = $classname;
891
-            }
892
-            $model_names[ $short_name ] = $classname;
893
-        }
894
-        $this->registry->models = apply_filters('FHEE__EE_System__parse_model_names', $model_names);
895
-        $this->registry->non_abstract_db_models = apply_filters(
896
-            'FHEE__EE_System__parse_implemented_model_names',
897
-            $non_abstract_db_models
898
-        );
899
-    }
900
-
901
-
902
-    /**
903
-     * The purpose of this method is to simply check for a file named "caffeinated/brewing_regular.php" for any hooks
904
-     * that need to be setup before our EE_System launches.
905
-     *
906
-     * @return void
907
-     * @throws DomainException
908
-     * @throws InvalidArgumentException
909
-     * @throws InvalidDataTypeException
910
-     * @throws InvalidInterfaceException
911
-     * @throws InvalidClassException
912
-     * @throws InvalidFilePathException
913
-     */
914
-    private function _maybe_brew_regular()
915
-    {
916
-        /** @var Domain $domain */
917
-        $domain = DomainFactory::getShared(
918
-            new FullyQualifiedName(
919
-                'EventEspresso\core\domain\Domain'
920
-            ),
921
-            array(
922
-                new FilePath(EVENT_ESPRESSO_MAIN_FILE),
923
-                Version::fromString(espresso_version()),
924
-            )
925
-        );
926
-        if ($domain->isCaffeinated()) {
927
-            require_once EE_CAFF_PATH . 'brewing_regular.php';
928
-        }
929
-    }
930
-
931
-
932
-    /**
933
-     * register_shortcodes_modules_and_widgets
934
-     * generate lists of shortcodes and modules, then verify paths and classes
935
-     * This is hooked into 'AHEE__EE_Bootstrap__register_shortcodes_modules_and_widgets'
936
-     * which runs during the WP 'plugins_loaded' action at priority 7
937
-     *
938
-     * @access public
939
-     * @return void
940
-     * @throws Exception
941
-     */
942
-    public function register_shortcodes_modules_and_widgets()
943
-    {
944
-        try {
945
-            // load, register, and add shortcodes the new way
946
-            if ($this->request->isFrontend() || $this->request->isIframe()) {
947
-                $this->loader->getShared(
948
-                    'EventEspresso\core\services\shortcodes\ShortcodesManager',
949
-                    array(
950
-                        // and the old way, but we'll put it under control of the new system
951
-                        EE_Config::getLegacyShortcodesManager(),
952
-                    )
953
-                );
954
-            }
955
-        } catch (Exception $exception) {
956
-            new ExceptionStackTraceDisplay($exception);
957
-        }
958
-        do_action('AHEE__EE_System__register_shortcodes_modules_and_widgets');
959
-        // check for addons using old hook point
960
-        if (has_action('AHEE__EE_System__register_shortcodes_modules_and_addons')) {
961
-            $this->_incompatible_addon_error();
962
-        }
963
-    }
964
-
965
-
966
-    /**
967
-     * _incompatible_addon_error
968
-     *
969
-     * @access public
970
-     * @return void
971
-     */
972
-    private function _incompatible_addon_error()
973
-    {
974
-        // get array of classes hooking into here
975
-        $class_names = EEH_Class_Tools::get_class_names_for_all_callbacks_on_hook(
976
-            'AHEE__EE_System__register_shortcodes_modules_and_addons'
977
-        );
978
-        if (! empty($class_names)) {
979
-            $msg = __(
980
-                'The following plugins, addons, or modules appear to be incompatible with this version of Event Espresso and were automatically deactivated to avoid fatal errors:',
981
-                'event_espresso'
982
-            );
983
-            $msg .= '<ul>';
984
-            foreach ($class_names as $class_name) {
985
-                $msg .= '<li><b>Event Espresso - '
986
-                        . str_replace(
987
-                            array('EE_', 'EEM_', 'EED_', 'EES_', 'EEW_'),
988
-                            '',
989
-                            $class_name
990
-                        ) . '</b></li>';
991
-            }
992
-            $msg .= '</ul>';
993
-            $msg .= __(
994
-                'Compatibility issues can be avoided and/or resolved by keeping addons and plugins updated to the latest version.',
995
-                'event_espresso'
996
-            );
997
-            // save list of incompatible addons to wp-options for later use
998
-            add_option('ee_incompatible_addons', $class_names, '', 'no');
999
-            if (is_admin()) {
1000
-                EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
1001
-            }
1002
-        }
1003
-    }
1004
-
1005
-
1006
-    /**
1007
-     * brew_espresso
1008
-     * begins the process of setting hooks for initializing EE in the correct order
1009
-     * This is happening on the 'AHEE__EE_Bootstrap__brew_espresso' hook point
1010
-     * which runs during the WP 'plugins_loaded' action at priority 9
1011
-     *
1012
-     * @return void
1013
-     */
1014
-    public function brew_espresso()
1015
-    {
1016
-        do_action('AHEE__EE_System__brew_espresso__begin', $this);
1017
-        // load some final core systems
1018
-        add_action('init', array($this, 'set_hooks_for_core'), 1);
1019
-        add_action('init', array($this, 'perform_activations_upgrades_and_migrations'), 3);
1020
-        add_action('init', array($this, 'load_CPTs_and_session'), 5);
1021
-        add_action('init', array($this, 'load_controllers'), 7);
1022
-        add_action('init', array($this, 'core_loaded_and_ready'), 9);
1023
-        add_action('init', array($this, 'initialize'), 10);
1024
-        add_action('init', array($this, 'initialize_last'), 100);
1025
-        if (is_admin() && apply_filters('FHEE__EE_System__brew_espresso__load_pue', true)) {
1026
-            // pew pew pew
1027
-            $this->loader->getShared('EventEspresso\core\services\licensing\LicenseService');
1028
-            do_action('AHEE__EE_System__brew_espresso__after_pue_init');
1029
-        }
1030
-        do_action('AHEE__EE_System__brew_espresso__complete', $this);
1031
-    }
1032
-
1033
-
1034
-    /**
1035
-     *    set_hooks_for_core
1036
-     *
1037
-     * @access public
1038
-     * @return    void
1039
-     * @throws EE_Error
1040
-     */
1041
-    public function set_hooks_for_core()
1042
-    {
1043
-        $this->_deactivate_incompatible_addons();
1044
-        do_action('AHEE__EE_System__set_hooks_for_core');
1045
-        $this->loader->getShared('EventEspresso\core\domain\values\session\SessionLifespan');
1046
-        // caps need to be initialized on every request so that capability maps are set.
1047
-        // @see https://events.codebasehq.com/projects/event-espresso/tickets/8674
1048
-        $this->registry->CAP->init_caps();
1049
-    }
1050
-
1051
-
1052
-    /**
1053
-     * Using the information gathered in EE_System::_incompatible_addon_error,
1054
-     * deactivates any addons considered incompatible with the current version of EE
1055
-     */
1056
-    private function _deactivate_incompatible_addons()
1057
-    {
1058
-        $incompatible_addons = get_option('ee_incompatible_addons', array());
1059
-        if (! empty($incompatible_addons)) {
1060
-            $active_plugins = get_option('active_plugins', array());
1061
-            foreach ($active_plugins as $active_plugin) {
1062
-                foreach ($incompatible_addons as $incompatible_addon) {
1063
-                    if (strpos($active_plugin, $incompatible_addon) !== false) {
1064
-                        unset($_GET['activate']);
1065
-                        espresso_deactivate_plugin($active_plugin);
1066
-                    }
1067
-                }
1068
-            }
1069
-        }
1070
-    }
1071
-
1072
-
1073
-    /**
1074
-     *    perform_activations_upgrades_and_migrations
1075
-     *
1076
-     * @access public
1077
-     * @return    void
1078
-     */
1079
-    public function perform_activations_upgrades_and_migrations()
1080
-    {
1081
-        do_action('AHEE__EE_System__perform_activations_upgrades_and_migrations');
1082
-    }
1083
-
1084
-
1085
-    /**
1086
-     * @return void
1087
-     * @throws DomainException
1088
-     */
1089
-    public function load_CPTs_and_session()
1090
-    {
1091
-        do_action('AHEE__EE_System__load_CPTs_and_session__start');
1092
-        /** @var EventEspresso\core\domain\services\custom_post_types\RegisterCustomTaxonomies $register_custom_taxonomies */
1093
-        $register_custom_taxonomies = $this->loader->getShared(
1094
-            'EventEspresso\core\domain\services\custom_post_types\RegisterCustomTaxonomies'
1095
-        );
1096
-        $register_custom_taxonomies->registerCustomTaxonomies();
1097
-        /** @var EventEspresso\core\domain\services\custom_post_types\RegisterCustomPostTypes $register_custom_post_types */
1098
-        $register_custom_post_types = $this->loader->getShared(
1099
-            'EventEspresso\core\domain\services\custom_post_types\RegisterCustomPostTypes'
1100
-        );
1101
-        $register_custom_post_types->registerCustomPostTypes();
1102
-        /** @var EventEspresso\core\domain\services\custom_post_types\RegisterCustomTaxonomyTerms $register_custom_taxonomy_terms */
1103
-        $register_custom_taxonomy_terms = $this->loader->getShared(
1104
-            'EventEspresso\core\domain\services\custom_post_types\RegisterCustomTaxonomyTerms'
1105
-        );
1106
-        $register_custom_taxonomy_terms->registerCustomTaxonomyTerms();
1107
-        // load legacy Custom Post Types and Taxonomies
1108
-        $this->loader->getShared('EE_Register_CPTs');
1109
-        do_action('AHEE__EE_System__load_CPTs_and_session__complete');
1110
-    }
1111
-
1112
-
1113
-    /**
1114
-     * load_controllers
1115
-     * this is the best place to load any additional controllers that needs access to EE core.
1116
-     * it is expected that all basic core EE systems, that are not dependant on the current request are loaded at this
1117
-     * time
1118
-     *
1119
-     * @access public
1120
-     * @return void
1121
-     */
1122
-    public function load_controllers()
1123
-    {
1124
-        do_action('AHEE__EE_System__load_controllers__start');
1125
-        // let's get it started
1126
-        if (! $this->maintenance_mode->level()
1127
-            && ($this->request->isFrontend() || $this->request->isFrontAjax())
1128
-        ) {
1129
-            do_action('AHEE__EE_System__load_controllers__load_front_controllers');
1130
-            $this->loader->getShared('EE_Front_Controller');
1131
-        } elseif ($this->request->isAdmin() || $this->request->isAdminAjax()) {
1132
-            do_action('AHEE__EE_System__load_controllers__load_admin_controllers');
1133
-            $this->loader->getShared('EE_Admin');
1134
-        }
1135
-        do_action('AHEE__EE_System__load_controllers__complete');
1136
-    }
1137
-
1138
-
1139
-    /**
1140
-     * core_loaded_and_ready
1141
-     * all of the basic EE core should be loaded at this point and available regardless of M-Mode
1142
-     *
1143
-     * @access public
1144
-     * @return void
1145
-     */
1146
-    public function core_loaded_and_ready()
1147
-    {
1148
-        if ($this->request->isAdmin()
1149
-            || $this->request->isEeAjax()
1150
-            || $this->request->isFrontend()
1151
-        ) {
1152
-            $this->loader->getShared('EE_Session');
1153
-        }
1154
-        do_action('AHEE__EE_System__core_loaded_and_ready');
1155
-        // load_espresso_template_tags
1156
-        if (is_readable(EE_PUBLIC . 'template_tags.php')
1157
-            && (
1158
-                $this->request->isFrontend()
1159
-                || $this->request->isAdmin()
1160
-                || $this->request->isIframe()
1161
-                || $this->request->isFeed()
1162
-            )
1163
-        ) {
1164
-            require_once EE_PUBLIC . 'template_tags.php';
1165
-        }
1166
-        do_action('AHEE__EE_System__set_hooks_for_shortcodes_modules_and_addons');
1167
-        if ($this->request->isAdmin() || $this->request->isFrontend() || $this->request->isIframe()) {
1168
-            $this->loader->getShared('EventEspresso\core\services\assets\Registry');
1169
-        }
1170
-    }
1171
-
1172
-
1173
-    /**
1174
-     * initialize
1175
-     * this is the best place to begin initializing client code
1176
-     *
1177
-     * @access public
1178
-     * @return void
1179
-     */
1180
-    public function initialize()
1181
-    {
1182
-        do_action('AHEE__EE_System__initialize');
1183
-    }
1184
-
1185
-
1186
-    /**
1187
-     * initialize_last
1188
-     * this is run really late during the WP init hook point, and ensures that mostly everything else that needs to
1189
-     * initialize has done so
1190
-     *
1191
-     * @access public
1192
-     * @return void
1193
-     */
1194
-    public function initialize_last()
1195
-    {
1196
-        do_action('AHEE__EE_System__initialize_last');
1197
-        /** @var EventEspresso\core\domain\services\custom_post_types\RewriteRules $rewrite_rules */
1198
-        $rewrite_rules = $this->loader->getShared(
1199
-            'EventEspresso\core\domain\services\custom_post_types\RewriteRules'
1200
-        );
1201
-        $rewrite_rules->flushRewriteRules();
1202
-        add_action('admin_bar_init', array($this, 'addEspressoToolbar'));
1203
-    }
1204
-
1205
-
1206
-    /**
1207
-     * @return void
1208
-     * @throws EE_Error
1209
-     */
1210
-    public function addEspressoToolbar()
1211
-    {
1212
-        $this->loader->getShared(
1213
-            'EventEspresso\core\domain\services\admin\AdminToolBar',
1214
-            array($this->registry->CAP)
1215
-        );
1216
-    }
1217
-
1218
-
1219
-    /**
1220
-     * do_not_cache
1221
-     * sets no cache headers and defines no cache constants for WP plugins
1222
-     *
1223
-     * @access public
1224
-     * @return void
1225
-     */
1226
-    public static function do_not_cache()
1227
-    {
1228
-        // set no cache constants
1229
-        if (! defined('DONOTCACHEPAGE')) {
1230
-            define('DONOTCACHEPAGE', true);
1231
-        }
1232
-        if (! defined('DONOTCACHCEOBJECT')) {
1233
-            define('DONOTCACHCEOBJECT', true);
1234
-        }
1235
-        if (! defined('DONOTCACHEDB')) {
1236
-            define('DONOTCACHEDB', true);
1237
-        }
1238
-        // add no cache headers
1239
-        add_action('send_headers', array('EE_System', 'nocache_headers'), 10);
1240
-        // plus a little extra for nginx and Google Chrome
1241
-        add_filter('nocache_headers', array('EE_System', 'extra_nocache_headers'), 10, 1);
1242
-        // prevent browsers from prefetching of the rel='next' link, because it may contain content that interferes with the registration process
1243
-        remove_action('wp_head', 'adjacent_posts_rel_link_wp_head');
1244
-    }
1245
-
1246
-
1247
-    /**
1248
-     *    extra_nocache_headers
1249
-     *
1250
-     * @access    public
1251
-     * @param $headers
1252
-     * @return    array
1253
-     */
1254
-    public static function extra_nocache_headers($headers)
1255
-    {
1256
-        // for NGINX
1257
-        $headers['X-Accel-Expires'] = 0;
1258
-        // plus extra for Google Chrome since it doesn't seem to respect "no-cache", but WILL respect "no-store"
1259
-        $headers['Cache-Control'] = 'no-store, no-cache, must-revalidate, max-age=0';
1260
-        return $headers;
1261
-    }
1262
-
1263
-
1264
-    /**
1265
-     *    nocache_headers
1266
-     *
1267
-     * @access    public
1268
-     * @return    void
1269
-     */
1270
-    public static function nocache_headers()
1271
-    {
1272
-        nocache_headers();
1273
-    }
1274
-
1275
-
1276
-    /**
1277
-     * simply hooks into "wp_list_pages_exclude" filter (for wp_list_pages method) and makes sure EE critical pages are
1278
-     * never returned with the function.
1279
-     *
1280
-     * @param  array $exclude_array any existing pages being excluded are in this array.
1281
-     * @return array
1282
-     */
1283
-    public function remove_pages_from_wp_list_pages($exclude_array)
1284
-    {
1285
-        return array_merge($exclude_array, $this->registry->CFG->core->get_critical_pages_array());
1286
-    }
31
+	/**
32
+	 * indicates this is a 'normal' request. Ie, not activation, nor upgrade, nor activation.
33
+	 * So examples of this would be a normal GET request on the frontend or backend, or a POST, etc
34
+	 */
35
+	const req_type_normal = 0;
36
+
37
+	/**
38
+	 * Indicates this is a brand new installation of EE so we should install
39
+	 * tables and default data etc
40
+	 */
41
+	const req_type_new_activation = 1;
42
+
43
+	/**
44
+	 * we've detected that EE has been reactivated (or EE was activated during maintenance mode,
45
+	 * and we just exited maintenance mode). We MUST check the database is setup properly
46
+	 * and that default data is setup too
47
+	 */
48
+	const req_type_reactivation = 2;
49
+
50
+	/**
51
+	 * indicates that EE has been upgraded since its previous request.
52
+	 * We may have data migration scripts to call and will want to trigger maintenance mode
53
+	 */
54
+	const req_type_upgrade = 3;
55
+
56
+	/**
57
+	 * TODO  will detect that EE has been DOWNGRADED. We probably don't want to run in this case...
58
+	 */
59
+	const req_type_downgrade = 4;
60
+
61
+	/**
62
+	 * @deprecated since version 4.6.0.dev.006
63
+	 * Now whenever a new_activation is detected the request type is still just
64
+	 * new_activation (same for reactivation, upgrade, downgrade etc), but if we'r ein maintenance mode
65
+	 * EE_System::initialize_db_if_no_migrations_required and EE_Addon::initialize_db_if_no_migrations_required
66
+	 * will instead enqueue that EE plugin's db initialization for when we're taken out of maintenance mode.
67
+	 * (Specifically, when the migration manager indicates migrations are finished
68
+	 * EE_Data_Migration_Manager::initialize_db_for_enqueued_ee_plugins() will be called)
69
+	 */
70
+	const req_type_activation_but_not_installed = 5;
71
+
72
+	/**
73
+	 * option prefix for recording the activation history (like core's "espresso_db_update") of addons
74
+	 */
75
+	const addon_activation_history_option_prefix = 'ee_addon_activation_history_';
76
+
77
+
78
+	/**
79
+	 * @var EE_System $_instance
80
+	 */
81
+	private static $_instance;
82
+
83
+	/**
84
+	 * @var EE_Registry $registry
85
+	 */
86
+	private $registry;
87
+
88
+	/**
89
+	 * @var LoaderInterface $loader
90
+	 */
91
+	private $loader;
92
+
93
+	/**
94
+	 * @var EE_Capabilities $capabilities
95
+	 */
96
+	private $capabilities;
97
+
98
+	/**
99
+	 * @var RequestInterface $request
100
+	 */
101
+	private $request;
102
+
103
+	/**
104
+	 * @var EE_Maintenance_Mode $maintenance_mode
105
+	 */
106
+	private $maintenance_mode;
107
+
108
+	/**
109
+	 * Stores which type of request this is, options being one of the constants on EE_System starting with req_type_*.
110
+	 * It can be a brand-new activation, a reactivation, an upgrade, a downgrade, or a normal request.
111
+	 *
112
+	 * @var int $_req_type
113
+	 */
114
+	private $_req_type;
115
+
116
+	/**
117
+	 * Whether or not there was a non-micro version change in EE core version during this request
118
+	 *
119
+	 * @var boolean $_major_version_change
120
+	 */
121
+	private $_major_version_change = false;
122
+
123
+	/**
124
+	 * A Context DTO dedicated solely to identifying the current request type.
125
+	 *
126
+	 * @var RequestTypeContextCheckerInterface $request_type
127
+	 */
128
+	private $request_type;
129
+
130
+
131
+	/**
132
+	 * @singleton method used to instantiate class object
133
+	 * @param EE_Registry|null         $registry
134
+	 * @param LoaderInterface|null     $loader
135
+	 * @param RequestInterface|null    $request
136
+	 * @param EE_Maintenance_Mode|null $maintenance_mode
137
+	 * @return EE_System
138
+	 */
139
+	public static function instance(
140
+		EE_Registry $registry = null,
141
+		LoaderInterface $loader = null,
142
+		RequestInterface $request = null,
143
+		EE_Maintenance_Mode $maintenance_mode = null
144
+	) {
145
+		// check if class object is instantiated
146
+		if (! self::$_instance instanceof EE_System) {
147
+			self::$_instance = new self($registry, $loader, $request, $maintenance_mode);
148
+		}
149
+		return self::$_instance;
150
+	}
151
+
152
+
153
+	/**
154
+	 * resets the instance and returns it
155
+	 *
156
+	 * @return EE_System
157
+	 */
158
+	public static function reset()
159
+	{
160
+		self::$_instance->_req_type = null;
161
+		// make sure none of the old hooks are left hanging around
162
+		remove_all_actions('AHEE__EE_System__perform_activations_upgrades_and_migrations');
163
+		// we need to reset the migration manager in order for it to detect DMSs properly
164
+		EE_Data_Migration_Manager::reset();
165
+		self::instance()->detect_activations_or_upgrades();
166
+		self::instance()->perform_activations_upgrades_and_migrations();
167
+		return self::instance();
168
+	}
169
+
170
+
171
+	/**
172
+	 * sets hooks for running rest of system
173
+	 * provides "AHEE__EE_System__construct__complete" hook for EE Addons to use as their starting point
174
+	 * starting EE Addons from any other point may lead to problems
175
+	 *
176
+	 * @param EE_Registry         $registry
177
+	 * @param LoaderInterface     $loader
178
+	 * @param RequestInterface    $request
179
+	 * @param EE_Maintenance_Mode $maintenance_mode
180
+	 */
181
+	private function __construct(
182
+		EE_Registry $registry,
183
+		LoaderInterface $loader,
184
+		RequestInterface $request,
185
+		EE_Maintenance_Mode $maintenance_mode
186
+	) {
187
+		$this->registry = $registry;
188
+		$this->loader = $loader;
189
+		$this->request = $request;
190
+		$this->maintenance_mode = $maintenance_mode;
191
+		do_action('AHEE__EE_System__construct__begin', $this);
192
+		add_action(
193
+			'AHEE__EE_Bootstrap__load_espresso_addons',
194
+			array($this, 'loadCapabilities'),
195
+			5
196
+		);
197
+		add_action(
198
+			'AHEE__EE_Bootstrap__load_espresso_addons',
199
+			array($this, 'loadCommandBus'),
200
+			7
201
+		);
202
+		add_action(
203
+			'AHEE__EE_Bootstrap__load_espresso_addons',
204
+			array($this, 'loadPluginApi'),
205
+			9
206
+		);
207
+		// allow addons to load first so that they can register autoloaders, set hooks for running DMS's, etc
208
+		add_action(
209
+			'AHEE__EE_Bootstrap__load_espresso_addons',
210
+			array($this, 'load_espresso_addons')
211
+		);
212
+		// when an ee addon is activated, we want to call the core hook(s) again
213
+		// because the newly-activated addon didn't get a chance to run at all
214
+		add_action('activate_plugin', array($this, 'load_espresso_addons'), 1);
215
+		// detect whether install or upgrade
216
+		add_action(
217
+			'AHEE__EE_Bootstrap__detect_activations_or_upgrades',
218
+			array($this, 'detect_activations_or_upgrades'),
219
+			3
220
+		);
221
+		// load EE_Config, EE_Textdomain, etc
222
+		add_action(
223
+			'AHEE__EE_Bootstrap__load_core_configuration',
224
+			array($this, 'load_core_configuration'),
225
+			5
226
+		);
227
+		// load EE_Config, EE_Textdomain, etc
228
+		add_action(
229
+			'AHEE__EE_Bootstrap__register_shortcodes_modules_and_widgets',
230
+			array($this, 'register_shortcodes_modules_and_widgets'),
231
+			7
232
+		);
233
+		// you wanna get going? I wanna get going... let's get going!
234
+		add_action(
235
+			'AHEE__EE_Bootstrap__brew_espresso',
236
+			array($this, 'brew_espresso'),
237
+			9
238
+		);
239
+		// other housekeeping
240
+		// exclude EE critical pages from wp_list_pages
241
+		add_filter(
242
+			'wp_list_pages_excludes',
243
+			array($this, 'remove_pages_from_wp_list_pages'),
244
+			10
245
+		);
246
+		// ALL EE Addons should use the following hook point to attach their initial setup too
247
+		// it's extremely important for EE Addons to register any class autoloaders so that they can be available when the EE_Config loads
248
+		do_action('AHEE__EE_System__construct__complete', $this);
249
+	}
250
+
251
+
252
+	/**
253
+	 * load and setup EE_Capabilities
254
+	 *
255
+	 * @return void
256
+	 * @throws EE_Error
257
+	 */
258
+	public function loadCapabilities()
259
+	{
260
+		$this->capabilities = $this->loader->getShared('EE_Capabilities');
261
+		add_action(
262
+			'AHEE__EE_Capabilities__init_caps__before_initialization',
263
+			function () {
264
+				LoaderFactory::getLoader()->getShared('EE_Payment_Method_Manager');
265
+			}
266
+		);
267
+	}
268
+
269
+
270
+	/**
271
+	 * create and cache the CommandBus, and also add middleware
272
+	 * The CapChecker middleware requires the use of EE_Capabilities
273
+	 * which is why we need to load the CommandBus after Caps are set up
274
+	 *
275
+	 * @return void
276
+	 * @throws EE_Error
277
+	 */
278
+	public function loadCommandBus()
279
+	{
280
+		$this->loader->getShared(
281
+			'CommandBusInterface',
282
+			array(
283
+				null,
284
+				apply_filters(
285
+					'FHEE__EE_Load_Espresso_Core__handle_request__CommandBus_middleware',
286
+					array(
287
+						$this->loader->getShared('EventEspresso\core\services\commands\middleware\CapChecker'),
288
+						$this->loader->getShared('EventEspresso\core\services\commands\middleware\AddActionHook'),
289
+					)
290
+				),
291
+			)
292
+		);
293
+	}
294
+
295
+
296
+	/**
297
+	 * @return void
298
+	 * @throws EE_Error
299
+	 */
300
+	public function loadPluginApi()
301
+	{
302
+		// set autoloaders for all of the classes implementing EEI_Plugin_API
303
+		// which provide helpers for EE plugin authors to more easily register certain components with EE.
304
+		EEH_Autoloader::instance()->register_autoloaders_for_each_file_in_folder(EE_LIBRARIES . 'plugin_api');
305
+		$this->loader->getShared('EE_Request_Handler');
306
+	}
307
+
308
+
309
+	/**
310
+	 * @param string $addon_name
311
+	 * @param string $version_constant
312
+	 * @param string $min_version_required
313
+	 * @param string $load_callback
314
+	 * @param string $plugin_file_constant
315
+	 * @return void
316
+	 */
317
+	private function deactivateIncompatibleAddon(
318
+		$addon_name,
319
+		$version_constant,
320
+		$min_version_required,
321
+		$load_callback,
322
+		$plugin_file_constant
323
+	) {
324
+		if (! defined($version_constant)) {
325
+			return;
326
+		}
327
+		$addon_version = constant($version_constant);
328
+		if ($addon_version && version_compare($addon_version, $min_version_required, '<')) {
329
+			remove_action('AHEE__EE_System__load_espresso_addons', $load_callback);
330
+			if (! function_exists('deactivate_plugins')) {
331
+				require_once ABSPATH . 'wp-admin/includes/plugin.php';
332
+			}
333
+			deactivate_plugins(plugin_basename(constant($plugin_file_constant)));
334
+			unset($_GET['activate'], $_REQUEST['activate'], $_GET['activate-multi'], $_REQUEST['activate-multi']);
335
+			EE_Error::add_error(
336
+				sprintf(
337
+					esc_html__(
338
+						'We\'re sorry, but the Event Espresso %1$s addon was deactivated because version %2$s or higher is required with this version of Event Espresso core.',
339
+						'event_espresso'
340
+					),
341
+					$addon_name,
342
+					$min_version_required
343
+				),
344
+				__FILE__,
345
+				__FUNCTION__ . "({$addon_name})",
346
+				__LINE__
347
+			);
348
+			EE_Error::get_notices(false, true);
349
+		}
350
+	}
351
+
352
+
353
+	/**
354
+	 * load_espresso_addons
355
+	 * allow addons to load first so that they can set hooks for running DMS's, etc
356
+	 * this is hooked into both:
357
+	 *    'AHEE__EE_Bootstrap__load_core_configuration'
358
+	 *        which runs during the WP 'plugins_loaded' action at priority 5
359
+	 *    and the WP 'activate_plugin' hook point
360
+	 *
361
+	 * @access public
362
+	 * @return void
363
+	 */
364
+	public function load_espresso_addons()
365
+	{
366
+		$this->deactivateIncompatibleAddon(
367
+			'Wait Lists',
368
+			'EE_WAIT_LISTS_VERSION',
369
+			'1.0.0.beta.074',
370
+			'load_espresso_wait_lists',
371
+			'EE_WAIT_LISTS_PLUGIN_FILE'
372
+		);
373
+		$this->deactivateIncompatibleAddon(
374
+			'Automated Upcoming Event Notifications',
375
+			'EE_AUTOMATED_UPCOMING_EVENT_NOTIFICATION_VERSION',
376
+			'1.0.0.beta.091',
377
+			'load_espresso_automated_upcoming_event_notification',
378
+			'EE_AUTOMATED_UPCOMING_EVENT_NOTIFICATION_PLUGIN_FILE'
379
+		);
380
+		do_action('AHEE__EE_System__load_espresso_addons');
381
+		// if the WP API basic auth plugin isn't already loaded, load it now.
382
+		// We want it for mobile apps. Just include the entire plugin
383
+		// also, don't load the basic auth when a plugin is getting activated, because
384
+		// it could be the basic auth plugin, and it doesn't check if its methods are already defined
385
+		// and causes a fatal error
386
+		if ($this->request->getRequestParam('activate') !== 'true'
387
+			&& ! function_exists('json_basic_auth_handler')
388
+			&& ! function_exists('json_basic_auth_error')
389
+			&& ! in_array(
390
+				$this->request->getRequestParam('action'),
391
+				array('activate', 'activate-selected'),
392
+				true
393
+			)
394
+		) {
395
+			include_once EE_THIRD_PARTY . 'wp-api-basic-auth' . DS . 'basic-auth.php';
396
+		}
397
+		do_action('AHEE__EE_System__load_espresso_addons__complete');
398
+	}
399
+
400
+
401
+	/**
402
+	 * detect_activations_or_upgrades
403
+	 * Checks for activation or upgrade of core first;
404
+	 * then also checks if any registered addons have been activated or upgraded
405
+	 * This is hooked into 'AHEE__EE_Bootstrap__detect_activations_or_upgrades'
406
+	 * which runs during the WP 'plugins_loaded' action at priority 3
407
+	 *
408
+	 * @access public
409
+	 * @return void
410
+	 */
411
+	public function detect_activations_or_upgrades()
412
+	{
413
+		// first off: let's make sure to handle core
414
+		$this->detect_if_activation_or_upgrade();
415
+		foreach ($this->registry->addons as $addon) {
416
+			if ($addon instanceof EE_Addon) {
417
+				// detect teh request type for that addon
418
+				$addon->detect_activation_or_upgrade();
419
+			}
420
+		}
421
+	}
422
+
423
+
424
+	/**
425
+	 * detect_if_activation_or_upgrade
426
+	 * Takes care of detecting whether this is a brand new install or code upgrade,
427
+	 * and either setting up the DB or setting up maintenance mode etc.
428
+	 *
429
+	 * @access public
430
+	 * @return void
431
+	 */
432
+	public function detect_if_activation_or_upgrade()
433
+	{
434
+		do_action('AHEE__EE_System___detect_if_activation_or_upgrade__begin');
435
+		// check if db has been updated, or if its a brand-new installation
436
+		$espresso_db_update = $this->fix_espresso_db_upgrade_option();
437
+		$request_type = $this->detect_req_type($espresso_db_update);
438
+		// EEH_Debug_Tools::printr( $request_type, '$request_type', __FILE__, __LINE__ );
439
+		switch ($request_type) {
440
+			case EE_System::req_type_new_activation:
441
+				do_action('AHEE__EE_System__detect_if_activation_or_upgrade__new_activation');
442
+				$this->_handle_core_version_change($espresso_db_update);
443
+				break;
444
+			case EE_System::req_type_reactivation:
445
+				do_action('AHEE__EE_System__detect_if_activation_or_upgrade__reactivation');
446
+				$this->_handle_core_version_change($espresso_db_update);
447
+				break;
448
+			case EE_System::req_type_upgrade:
449
+				do_action('AHEE__EE_System__detect_if_activation_or_upgrade__upgrade');
450
+				// migrations may be required now that we've upgraded
451
+				$this->maintenance_mode->set_maintenance_mode_if_db_old();
452
+				$this->_handle_core_version_change($espresso_db_update);
453
+				break;
454
+			case EE_System::req_type_downgrade:
455
+				do_action('AHEE__EE_System__detect_if_activation_or_upgrade__downgrade');
456
+				// its possible migrations are no longer required
457
+				$this->maintenance_mode->set_maintenance_mode_if_db_old();
458
+				$this->_handle_core_version_change($espresso_db_update);
459
+				break;
460
+			case EE_System::req_type_normal:
461
+			default:
462
+				break;
463
+		}
464
+		do_action('AHEE__EE_System__detect_if_activation_or_upgrade__complete');
465
+	}
466
+
467
+
468
+	/**
469
+	 * Updates the list of installed versions and sets hooks for
470
+	 * initializing the database later during the request
471
+	 *
472
+	 * @param array $espresso_db_update
473
+	 */
474
+	private function _handle_core_version_change($espresso_db_update)
475
+	{
476
+		$this->update_list_of_installed_versions($espresso_db_update);
477
+		// get ready to verify the DB is ok (provided we aren't in maintenance mode, of course)
478
+		add_action(
479
+			'AHEE__EE_System__perform_activations_upgrades_and_migrations',
480
+			array($this, 'initialize_db_if_no_migrations_required')
481
+		);
482
+	}
483
+
484
+
485
+	/**
486
+	 * standardizes the wp option 'espresso_db_upgrade' which actually stores
487
+	 * information about what versions of EE have been installed and activated,
488
+	 * NOT necessarily the state of the database
489
+	 *
490
+	 * @param mixed $espresso_db_update           the value of the WordPress option.
491
+	 *                                            If not supplied, fetches it from the options table
492
+	 * @return array the correct value of 'espresso_db_upgrade', after saving it, if it needed correction
493
+	 */
494
+	private function fix_espresso_db_upgrade_option($espresso_db_update = null)
495
+	{
496
+		do_action('FHEE__EE_System__manage_fix_espresso_db_upgrade_option__begin', $espresso_db_update);
497
+		if (! $espresso_db_update) {
498
+			$espresso_db_update = get_option('espresso_db_update');
499
+		}
500
+		// check that option is an array
501
+		if (! is_array($espresso_db_update)) {
502
+			// if option is FALSE, then it never existed
503
+			if ($espresso_db_update === false) {
504
+				// make $espresso_db_update an array and save option with autoload OFF
505
+				$espresso_db_update = array();
506
+				add_option('espresso_db_update', $espresso_db_update, '', 'no');
507
+			} else {
508
+				// option is NOT FALSE but also is NOT an array, so make it an array and save it
509
+				$espresso_db_update = array($espresso_db_update => array());
510
+				update_option('espresso_db_update', $espresso_db_update);
511
+			}
512
+		} else {
513
+			$corrected_db_update = array();
514
+			// if IS an array, but is it an array where KEYS are version numbers, and values are arrays?
515
+			foreach ($espresso_db_update as $should_be_version_string => $should_be_array) {
516
+				if (is_int($should_be_version_string) && ! is_array($should_be_array)) {
517
+					// the key is an int, and the value IS NOT an array
518
+					// so it must be numerically-indexed, where values are versions installed...
519
+					// fix it!
520
+					$version_string = $should_be_array;
521
+					$corrected_db_update[ $version_string ] = array('unknown-date');
522
+				} else {
523
+					// ok it checks out
524
+					$corrected_db_update[ $should_be_version_string ] = $should_be_array;
525
+				}
526
+			}
527
+			$espresso_db_update = $corrected_db_update;
528
+			update_option('espresso_db_update', $espresso_db_update);
529
+		}
530
+		do_action('FHEE__EE_System__manage_fix_espresso_db_upgrade_option__complete', $espresso_db_update);
531
+		return $espresso_db_update;
532
+	}
533
+
534
+
535
+	/**
536
+	 * Does the traditional work of setting up the plugin's database and adding default data.
537
+	 * If migration script/process did not exist, this is what would happen on every activation/reactivation/upgrade.
538
+	 * NOTE: if we're in maintenance mode (which would be the case if we detect there are data
539
+	 * migration scripts that need to be run and a version change happens), enqueues core for database initialization,
540
+	 * so that it will be done when migrations are finished
541
+	 *
542
+	 * @param boolean $initialize_addons_too if true, we double-check addons' database tables etc too;
543
+	 * @param boolean $verify_schema         if true will re-check the database tables have the correct schema.
544
+	 *                                       This is a resource-intensive job
545
+	 *                                       so we prefer to only do it when necessary
546
+	 * @return void
547
+	 * @throws EE_Error
548
+	 */
549
+	public function initialize_db_if_no_migrations_required($initialize_addons_too = false, $verify_schema = true)
550
+	{
551
+		$request_type = $this->detect_req_type();
552
+		// only initialize system if we're not in maintenance mode.
553
+		if ($this->maintenance_mode->level() !== EE_Maintenance_Mode::level_2_complete_maintenance) {
554
+			/** @var EventEspresso\core\domain\services\custom_post_types\RewriteRules $rewrite_rules */
555
+			$rewrite_rules = $this->loader->getShared(
556
+				'EventEspresso\core\domain\services\custom_post_types\RewriteRules'
557
+			);
558
+			$rewrite_rules->flush();
559
+			if ($verify_schema) {
560
+				EEH_Activation::initialize_db_and_folders();
561
+			}
562
+			EEH_Activation::initialize_db_content();
563
+			EEH_Activation::system_initialization();
564
+			if ($initialize_addons_too) {
565
+				$this->initialize_addons();
566
+			}
567
+		} else {
568
+			EE_Data_Migration_Manager::instance()->enqueue_db_initialization_for('Core');
569
+		}
570
+		if ($request_type === EE_System::req_type_new_activation
571
+			|| $request_type === EE_System::req_type_reactivation
572
+			|| (
573
+				$request_type === EE_System::req_type_upgrade
574
+				&& $this->is_major_version_change()
575
+			)
576
+		) {
577
+			add_action('AHEE__EE_System__initialize_last', array($this, 'redirect_to_about_ee'), 9);
578
+		}
579
+	}
580
+
581
+
582
+	/**
583
+	 * Initializes the db for all registered addons
584
+	 *
585
+	 * @throws EE_Error
586
+	 */
587
+	public function initialize_addons()
588
+	{
589
+		// foreach registered addon, make sure its db is up-to-date too
590
+		foreach ($this->registry->addons as $addon) {
591
+			if ($addon instanceof EE_Addon) {
592
+				$addon->initialize_db_if_no_migrations_required();
593
+			}
594
+		}
595
+	}
596
+
597
+
598
+	/**
599
+	 * Adds the current code version to the saved wp option which stores a list of all ee versions ever installed.
600
+	 *
601
+	 * @param    array  $version_history
602
+	 * @param    string $current_version_to_add version to be added to the version history
603
+	 * @return    boolean success as to whether or not this option was changed
604
+	 */
605
+	public function update_list_of_installed_versions($version_history = null, $current_version_to_add = null)
606
+	{
607
+		if (! $version_history) {
608
+			$version_history = $this->fix_espresso_db_upgrade_option($version_history);
609
+		}
610
+		if ($current_version_to_add === null) {
611
+			$current_version_to_add = espresso_version();
612
+		}
613
+		$version_history[ $current_version_to_add ][] = date('Y-m-d H:i:s', time());
614
+		// re-save
615
+		return update_option('espresso_db_update', $version_history);
616
+	}
617
+
618
+
619
+	/**
620
+	 * Detects if the current version indicated in the has existed in the list of
621
+	 * previously-installed versions of EE (espresso_db_update). Does NOT modify it (ie, no side-effect)
622
+	 *
623
+	 * @param array $espresso_db_update array from the wp option stored under the name 'espresso_db_update'.
624
+	 *                                  If not supplied, fetches it from the options table.
625
+	 *                                  Also, caches its result so later parts of the code can also know whether
626
+	 *                                  there's been an update or not. This way we can add the current version to
627
+	 *                                  espresso_db_update, but still know if this is a new install or not
628
+	 * @return int one of the constants on EE_System::req_type_
629
+	 */
630
+	public function detect_req_type($espresso_db_update = null)
631
+	{
632
+		if ($this->_req_type === null) {
633
+			$espresso_db_update = ! empty($espresso_db_update)
634
+				? $espresso_db_update
635
+				: $this->fix_espresso_db_upgrade_option();
636
+			$this->_req_type = EE_System::detect_req_type_given_activation_history(
637
+				$espresso_db_update,
638
+				'ee_espresso_activation',
639
+				espresso_version()
640
+			);
641
+			$this->_major_version_change = $this->_detect_major_version_change($espresso_db_update);
642
+			$this->request->setIsActivation($this->_req_type !== EE_System::req_type_normal);
643
+		}
644
+		return $this->_req_type;
645
+	}
646
+
647
+
648
+	/**
649
+	 * Returns whether or not there was a non-micro version change (ie, change in either
650
+	 * the first or second number in the version. Eg 4.9.0.rc.001 to 4.10.0.rc.000,
651
+	 * but not 4.9.0.rc.0001 to 4.9.1.rc.0001
652
+	 *
653
+	 * @param $activation_history
654
+	 * @return bool
655
+	 */
656
+	private function _detect_major_version_change($activation_history)
657
+	{
658
+		$previous_version = EE_System::_get_most_recently_active_version_from_activation_history($activation_history);
659
+		$previous_version_parts = explode('.', $previous_version);
660
+		$current_version_parts = explode('.', espresso_version());
661
+		return isset($previous_version_parts[0], $previous_version_parts[1], $current_version_parts[0], $current_version_parts[1])
662
+			   && ($previous_version_parts[0] !== $current_version_parts[0]
663
+				   || $previous_version_parts[1] !== $current_version_parts[1]
664
+			   );
665
+	}
666
+
667
+
668
+	/**
669
+	 * Returns true if either the major or minor version of EE changed during this request.
670
+	 * Eg 4.9.0.rc.001 to 4.10.0.rc.000, but not 4.9.0.rc.0001 to 4.9.1.rc.0001
671
+	 *
672
+	 * @return bool
673
+	 */
674
+	public function is_major_version_change()
675
+	{
676
+		return $this->_major_version_change;
677
+	}
678
+
679
+
680
+	/**
681
+	 * Determines the request type for any ee addon, given three piece of info: the current array of activation
682
+	 * histories (for core that' 'espresso_db_update' wp option); the name of the WordPress option which is temporarily
683
+	 * set upon activation of the plugin (for core it's 'ee_espresso_activation'); and the version that this plugin was
684
+	 * just activated to (for core that will always be espresso_version())
685
+	 *
686
+	 * @param array  $activation_history_for_addon     the option's value which stores the activation history for this
687
+	 *                                                 ee plugin. for core that's 'espresso_db_update'
688
+	 * @param string $activation_indicator_option_name the name of the WordPress option that is temporarily set to
689
+	 *                                                 indicate that this plugin was just activated
690
+	 * @param string $version_to_upgrade_to            the version that was just upgraded to (for core that will be
691
+	 *                                                 espresso_version())
692
+	 * @return int one of the constants on EE_System::req_type_*
693
+	 */
694
+	public static function detect_req_type_given_activation_history(
695
+		$activation_history_for_addon,
696
+		$activation_indicator_option_name,
697
+		$version_to_upgrade_to
698
+	) {
699
+		$version_is_higher = self::_new_version_is_higher($activation_history_for_addon, $version_to_upgrade_to);
700
+		if ($activation_history_for_addon) {
701
+			// it exists, so this isn't a completely new install
702
+			// check if this version already in that list of previously installed versions
703
+			if (! isset($activation_history_for_addon[ $version_to_upgrade_to ])) {
704
+				// it a version we haven't seen before
705
+				if ($version_is_higher === 1) {
706
+					$req_type = EE_System::req_type_upgrade;
707
+				} else {
708
+					$req_type = EE_System::req_type_downgrade;
709
+				}
710
+				delete_option($activation_indicator_option_name);
711
+			} else {
712
+				// its not an update. maybe a reactivation?
713
+				if (get_option($activation_indicator_option_name, false)) {
714
+					if ($version_is_higher === -1) {
715
+						$req_type = EE_System::req_type_downgrade;
716
+					} elseif ($version_is_higher === 0) {
717
+						// we've seen this version before, but it's an activation. must be a reactivation
718
+						$req_type = EE_System::req_type_reactivation;
719
+					} else {// $version_is_higher === 1
720
+						$req_type = EE_System::req_type_upgrade;
721
+					}
722
+					delete_option($activation_indicator_option_name);
723
+				} else {
724
+					// we've seen this version before and the activation indicate doesn't show it was just activated
725
+					if ($version_is_higher === -1) {
726
+						$req_type = EE_System::req_type_downgrade;
727
+					} elseif ($version_is_higher === 0) {
728
+						// we've seen this version before and it's not an activation. its normal request
729
+						$req_type = EE_System::req_type_normal;
730
+					} else {// $version_is_higher === 1
731
+						$req_type = EE_System::req_type_upgrade;
732
+					}
733
+				}
734
+			}
735
+		} else {
736
+			// brand new install
737
+			$req_type = EE_System::req_type_new_activation;
738
+			delete_option($activation_indicator_option_name);
739
+		}
740
+		return $req_type;
741
+	}
742
+
743
+
744
+	/**
745
+	 * Detects if the $version_to_upgrade_to is higher than the most recent version in
746
+	 * the $activation_history_for_addon
747
+	 *
748
+	 * @param array  $activation_history_for_addon (keys are versions, values are arrays of times activated,
749
+	 *                                             sometimes containing 'unknown-date'
750
+	 * @param string $version_to_upgrade_to        (current version)
751
+	 * @return int results of version_compare( $version_to_upgrade_to, $most_recently_active_version ).
752
+	 *                                             ie, -1 if $version_to_upgrade_to is LOWER (downgrade);
753
+	 *                                             0 if $version_to_upgrade_to MATCHES (reactivation or normal request);
754
+	 *                                             1 if $version_to_upgrade_to is HIGHER (upgrade) ;
755
+	 */
756
+	private static function _new_version_is_higher($activation_history_for_addon, $version_to_upgrade_to)
757
+	{
758
+		// find the most recently-activated version
759
+		$most_recently_active_version =
760
+			EE_System::_get_most_recently_active_version_from_activation_history($activation_history_for_addon);
761
+		return version_compare($version_to_upgrade_to, $most_recently_active_version);
762
+	}
763
+
764
+
765
+	/**
766
+	 * Gets the most recently active version listed in the activation history,
767
+	 * and if none are found (ie, it's a brand new install) returns '0.0.0.dev.000'.
768
+	 *
769
+	 * @param array $activation_history  (keys are versions, values are arrays of times activated,
770
+	 *                                   sometimes containing 'unknown-date'
771
+	 * @return string
772
+	 */
773
+	private static function _get_most_recently_active_version_from_activation_history($activation_history)
774
+	{
775
+		$most_recently_active_version_activation = '1970-01-01 00:00:00';
776
+		$most_recently_active_version = '0.0.0.dev.000';
777
+		if (is_array($activation_history)) {
778
+			foreach ($activation_history as $version => $times_activated) {
779
+				// check there is a record of when this version was activated. Otherwise,
780
+				// mark it as unknown
781
+				if (! $times_activated) {
782
+					$times_activated = array('unknown-date');
783
+				}
784
+				if (is_string($times_activated)) {
785
+					$times_activated = array($times_activated);
786
+				}
787
+				foreach ($times_activated as $an_activation) {
788
+					if ($an_activation !== 'unknown-date'
789
+						&& $an_activation
790
+						   > $most_recently_active_version_activation) {
791
+						$most_recently_active_version = $version;
792
+						$most_recently_active_version_activation = $an_activation === 'unknown-date'
793
+							? '1970-01-01 00:00:00'
794
+							: $an_activation;
795
+					}
796
+				}
797
+			}
798
+		}
799
+		return $most_recently_active_version;
800
+	}
801
+
802
+
803
+	/**
804
+	 * This redirects to the about EE page after activation
805
+	 *
806
+	 * @return void
807
+	 */
808
+	public function redirect_to_about_ee()
809
+	{
810
+		$notices = EE_Error::get_notices(false);
811
+		// if current user is an admin and it's not an ajax or rest request
812
+		if (! isset($notices['errors'])
813
+			&& $this->request->isAdmin()
814
+			&& apply_filters(
815
+				'FHEE__EE_System__redirect_to_about_ee__do_redirect',
816
+				$this->capabilities->current_user_can('manage_options', 'espresso_about_default')
817
+			)
818
+		) {
819
+			$query_params = array('page' => 'espresso_about');
820
+			if (EE_System::instance()->detect_req_type() === EE_System::req_type_new_activation) {
821
+				$query_params['new_activation'] = true;
822
+			}
823
+			if (EE_System::instance()->detect_req_type() === EE_System::req_type_reactivation) {
824
+				$query_params['reactivation'] = true;
825
+			}
826
+			$url = add_query_arg($query_params, admin_url('admin.php'));
827
+			wp_safe_redirect($url);
828
+			exit();
829
+		}
830
+	}
831
+
832
+
833
+	/**
834
+	 * load_core_configuration
835
+	 * this is hooked into 'AHEE__EE_Bootstrap__load_core_configuration'
836
+	 * which runs during the WP 'plugins_loaded' action at priority 5
837
+	 *
838
+	 * @return void
839
+	 * @throws ReflectionException
840
+	 */
841
+	public function load_core_configuration()
842
+	{
843
+		do_action('AHEE__EE_System__load_core_configuration__begin', $this);
844
+		$this->loader->getShared('EE_Load_Textdomain');
845
+		// load textdomain
846
+		EE_Load_Textdomain::load_textdomain();
847
+		// load and setup EE_Config and EE_Network_Config
848
+		$config = $this->loader->getShared('EE_Config');
849
+		$this->loader->getShared('EE_Network_Config');
850
+		// setup autoloaders
851
+		// enable logging?
852
+		if ($config->admin->use_full_logging) {
853
+			$this->loader->getShared('EE_Log');
854
+		}
855
+		// check for activation errors
856
+		$activation_errors = get_option('ee_plugin_activation_errors', false);
857
+		if ($activation_errors) {
858
+			EE_Error::add_error($activation_errors, __FILE__, __FUNCTION__, __LINE__);
859
+			update_option('ee_plugin_activation_errors', false);
860
+		}
861
+		// get model names
862
+		$this->_parse_model_names();
863
+		// load caf stuff a chance to play during the activation process too.
864
+		$this->_maybe_brew_regular();
865
+		// configure custom post type definitions
866
+		$this->loader->getShared('EventEspresso\core\domain\entities\custom_post_types\CustomTaxonomyDefinitions');
867
+		$this->loader->getShared('EventEspresso\core\domain\entities\custom_post_types\CustomPostTypeDefinitions');
868
+		do_action('AHEE__EE_System__load_core_configuration__complete', $this);
869
+	}
870
+
871
+
872
+	/**
873
+	 * cycles through all of the models/*.model.php files, and assembles an array of model names
874
+	 *
875
+	 * @return void
876
+	 * @throws ReflectionException
877
+	 */
878
+	private function _parse_model_names()
879
+	{
880
+		// get all the files in the EE_MODELS folder that end in .model.php
881
+		$models = glob(EE_MODELS . '*.model.php');
882
+		$model_names = array();
883
+		$non_abstract_db_models = array();
884
+		foreach ($models as $model) {
885
+			// get model classname
886
+			$classname = EEH_File::get_classname_from_filepath_with_standard_filename($model);
887
+			$short_name = str_replace('EEM_', '', $classname);
888
+			$reflectionClass = new ReflectionClass($classname);
889
+			if ($reflectionClass->isSubclassOf('EEM_Base') && ! $reflectionClass->isAbstract()) {
890
+				$non_abstract_db_models[ $short_name ] = $classname;
891
+			}
892
+			$model_names[ $short_name ] = $classname;
893
+		}
894
+		$this->registry->models = apply_filters('FHEE__EE_System__parse_model_names', $model_names);
895
+		$this->registry->non_abstract_db_models = apply_filters(
896
+			'FHEE__EE_System__parse_implemented_model_names',
897
+			$non_abstract_db_models
898
+		);
899
+	}
900
+
901
+
902
+	/**
903
+	 * The purpose of this method is to simply check for a file named "caffeinated/brewing_regular.php" for any hooks
904
+	 * that need to be setup before our EE_System launches.
905
+	 *
906
+	 * @return void
907
+	 * @throws DomainException
908
+	 * @throws InvalidArgumentException
909
+	 * @throws InvalidDataTypeException
910
+	 * @throws InvalidInterfaceException
911
+	 * @throws InvalidClassException
912
+	 * @throws InvalidFilePathException
913
+	 */
914
+	private function _maybe_brew_regular()
915
+	{
916
+		/** @var Domain $domain */
917
+		$domain = DomainFactory::getShared(
918
+			new FullyQualifiedName(
919
+				'EventEspresso\core\domain\Domain'
920
+			),
921
+			array(
922
+				new FilePath(EVENT_ESPRESSO_MAIN_FILE),
923
+				Version::fromString(espresso_version()),
924
+			)
925
+		);
926
+		if ($domain->isCaffeinated()) {
927
+			require_once EE_CAFF_PATH . 'brewing_regular.php';
928
+		}
929
+	}
930
+
931
+
932
+	/**
933
+	 * register_shortcodes_modules_and_widgets
934
+	 * generate lists of shortcodes and modules, then verify paths and classes
935
+	 * This is hooked into 'AHEE__EE_Bootstrap__register_shortcodes_modules_and_widgets'
936
+	 * which runs during the WP 'plugins_loaded' action at priority 7
937
+	 *
938
+	 * @access public
939
+	 * @return void
940
+	 * @throws Exception
941
+	 */
942
+	public function register_shortcodes_modules_and_widgets()
943
+	{
944
+		try {
945
+			// load, register, and add shortcodes the new way
946
+			if ($this->request->isFrontend() || $this->request->isIframe()) {
947
+				$this->loader->getShared(
948
+					'EventEspresso\core\services\shortcodes\ShortcodesManager',
949
+					array(
950
+						// and the old way, but we'll put it under control of the new system
951
+						EE_Config::getLegacyShortcodesManager(),
952
+					)
953
+				);
954
+			}
955
+		} catch (Exception $exception) {
956
+			new ExceptionStackTraceDisplay($exception);
957
+		}
958
+		do_action('AHEE__EE_System__register_shortcodes_modules_and_widgets');
959
+		// check for addons using old hook point
960
+		if (has_action('AHEE__EE_System__register_shortcodes_modules_and_addons')) {
961
+			$this->_incompatible_addon_error();
962
+		}
963
+	}
964
+
965
+
966
+	/**
967
+	 * _incompatible_addon_error
968
+	 *
969
+	 * @access public
970
+	 * @return void
971
+	 */
972
+	private function _incompatible_addon_error()
973
+	{
974
+		// get array of classes hooking into here
975
+		$class_names = EEH_Class_Tools::get_class_names_for_all_callbacks_on_hook(
976
+			'AHEE__EE_System__register_shortcodes_modules_and_addons'
977
+		);
978
+		if (! empty($class_names)) {
979
+			$msg = __(
980
+				'The following plugins, addons, or modules appear to be incompatible with this version of Event Espresso and were automatically deactivated to avoid fatal errors:',
981
+				'event_espresso'
982
+			);
983
+			$msg .= '<ul>';
984
+			foreach ($class_names as $class_name) {
985
+				$msg .= '<li><b>Event Espresso - '
986
+						. str_replace(
987
+							array('EE_', 'EEM_', 'EED_', 'EES_', 'EEW_'),
988
+							'',
989
+							$class_name
990
+						) . '</b></li>';
991
+			}
992
+			$msg .= '</ul>';
993
+			$msg .= __(
994
+				'Compatibility issues can be avoided and/or resolved by keeping addons and plugins updated to the latest version.',
995
+				'event_espresso'
996
+			);
997
+			// save list of incompatible addons to wp-options for later use
998
+			add_option('ee_incompatible_addons', $class_names, '', 'no');
999
+			if (is_admin()) {
1000
+				EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
1001
+			}
1002
+		}
1003
+	}
1004
+
1005
+
1006
+	/**
1007
+	 * brew_espresso
1008
+	 * begins the process of setting hooks for initializing EE in the correct order
1009
+	 * This is happening on the 'AHEE__EE_Bootstrap__brew_espresso' hook point
1010
+	 * which runs during the WP 'plugins_loaded' action at priority 9
1011
+	 *
1012
+	 * @return void
1013
+	 */
1014
+	public function brew_espresso()
1015
+	{
1016
+		do_action('AHEE__EE_System__brew_espresso__begin', $this);
1017
+		// load some final core systems
1018
+		add_action('init', array($this, 'set_hooks_for_core'), 1);
1019
+		add_action('init', array($this, 'perform_activations_upgrades_and_migrations'), 3);
1020
+		add_action('init', array($this, 'load_CPTs_and_session'), 5);
1021
+		add_action('init', array($this, 'load_controllers'), 7);
1022
+		add_action('init', array($this, 'core_loaded_and_ready'), 9);
1023
+		add_action('init', array($this, 'initialize'), 10);
1024
+		add_action('init', array($this, 'initialize_last'), 100);
1025
+		if (is_admin() && apply_filters('FHEE__EE_System__brew_espresso__load_pue', true)) {
1026
+			// pew pew pew
1027
+			$this->loader->getShared('EventEspresso\core\services\licensing\LicenseService');
1028
+			do_action('AHEE__EE_System__brew_espresso__after_pue_init');
1029
+		}
1030
+		do_action('AHEE__EE_System__brew_espresso__complete', $this);
1031
+	}
1032
+
1033
+
1034
+	/**
1035
+	 *    set_hooks_for_core
1036
+	 *
1037
+	 * @access public
1038
+	 * @return    void
1039
+	 * @throws EE_Error
1040
+	 */
1041
+	public function set_hooks_for_core()
1042
+	{
1043
+		$this->_deactivate_incompatible_addons();
1044
+		do_action('AHEE__EE_System__set_hooks_for_core');
1045
+		$this->loader->getShared('EventEspresso\core\domain\values\session\SessionLifespan');
1046
+		// caps need to be initialized on every request so that capability maps are set.
1047
+		// @see https://events.codebasehq.com/projects/event-espresso/tickets/8674
1048
+		$this->registry->CAP->init_caps();
1049
+	}
1050
+
1051
+
1052
+	/**
1053
+	 * Using the information gathered in EE_System::_incompatible_addon_error,
1054
+	 * deactivates any addons considered incompatible with the current version of EE
1055
+	 */
1056
+	private function _deactivate_incompatible_addons()
1057
+	{
1058
+		$incompatible_addons = get_option('ee_incompatible_addons', array());
1059
+		if (! empty($incompatible_addons)) {
1060
+			$active_plugins = get_option('active_plugins', array());
1061
+			foreach ($active_plugins as $active_plugin) {
1062
+				foreach ($incompatible_addons as $incompatible_addon) {
1063
+					if (strpos($active_plugin, $incompatible_addon) !== false) {
1064
+						unset($_GET['activate']);
1065
+						espresso_deactivate_plugin($active_plugin);
1066
+					}
1067
+				}
1068
+			}
1069
+		}
1070
+	}
1071
+
1072
+
1073
+	/**
1074
+	 *    perform_activations_upgrades_and_migrations
1075
+	 *
1076
+	 * @access public
1077
+	 * @return    void
1078
+	 */
1079
+	public function perform_activations_upgrades_and_migrations()
1080
+	{
1081
+		do_action('AHEE__EE_System__perform_activations_upgrades_and_migrations');
1082
+	}
1083
+
1084
+
1085
+	/**
1086
+	 * @return void
1087
+	 * @throws DomainException
1088
+	 */
1089
+	public function load_CPTs_and_session()
1090
+	{
1091
+		do_action('AHEE__EE_System__load_CPTs_and_session__start');
1092
+		/** @var EventEspresso\core\domain\services\custom_post_types\RegisterCustomTaxonomies $register_custom_taxonomies */
1093
+		$register_custom_taxonomies = $this->loader->getShared(
1094
+			'EventEspresso\core\domain\services\custom_post_types\RegisterCustomTaxonomies'
1095
+		);
1096
+		$register_custom_taxonomies->registerCustomTaxonomies();
1097
+		/** @var EventEspresso\core\domain\services\custom_post_types\RegisterCustomPostTypes $register_custom_post_types */
1098
+		$register_custom_post_types = $this->loader->getShared(
1099
+			'EventEspresso\core\domain\services\custom_post_types\RegisterCustomPostTypes'
1100
+		);
1101
+		$register_custom_post_types->registerCustomPostTypes();
1102
+		/** @var EventEspresso\core\domain\services\custom_post_types\RegisterCustomTaxonomyTerms $register_custom_taxonomy_terms */
1103
+		$register_custom_taxonomy_terms = $this->loader->getShared(
1104
+			'EventEspresso\core\domain\services\custom_post_types\RegisterCustomTaxonomyTerms'
1105
+		);
1106
+		$register_custom_taxonomy_terms->registerCustomTaxonomyTerms();
1107
+		// load legacy Custom Post Types and Taxonomies
1108
+		$this->loader->getShared('EE_Register_CPTs');
1109
+		do_action('AHEE__EE_System__load_CPTs_and_session__complete');
1110
+	}
1111
+
1112
+
1113
+	/**
1114
+	 * load_controllers
1115
+	 * this is the best place to load any additional controllers that needs access to EE core.
1116
+	 * it is expected that all basic core EE systems, that are not dependant on the current request are loaded at this
1117
+	 * time
1118
+	 *
1119
+	 * @access public
1120
+	 * @return void
1121
+	 */
1122
+	public function load_controllers()
1123
+	{
1124
+		do_action('AHEE__EE_System__load_controllers__start');
1125
+		// let's get it started
1126
+		if (! $this->maintenance_mode->level()
1127
+			&& ($this->request->isFrontend() || $this->request->isFrontAjax())
1128
+		) {
1129
+			do_action('AHEE__EE_System__load_controllers__load_front_controllers');
1130
+			$this->loader->getShared('EE_Front_Controller');
1131
+		} elseif ($this->request->isAdmin() || $this->request->isAdminAjax()) {
1132
+			do_action('AHEE__EE_System__load_controllers__load_admin_controllers');
1133
+			$this->loader->getShared('EE_Admin');
1134
+		}
1135
+		do_action('AHEE__EE_System__load_controllers__complete');
1136
+	}
1137
+
1138
+
1139
+	/**
1140
+	 * core_loaded_and_ready
1141
+	 * all of the basic EE core should be loaded at this point and available regardless of M-Mode
1142
+	 *
1143
+	 * @access public
1144
+	 * @return void
1145
+	 */
1146
+	public function core_loaded_and_ready()
1147
+	{
1148
+		if ($this->request->isAdmin()
1149
+			|| $this->request->isEeAjax()
1150
+			|| $this->request->isFrontend()
1151
+		) {
1152
+			$this->loader->getShared('EE_Session');
1153
+		}
1154
+		do_action('AHEE__EE_System__core_loaded_and_ready');
1155
+		// load_espresso_template_tags
1156
+		if (is_readable(EE_PUBLIC . 'template_tags.php')
1157
+			&& (
1158
+				$this->request->isFrontend()
1159
+				|| $this->request->isAdmin()
1160
+				|| $this->request->isIframe()
1161
+				|| $this->request->isFeed()
1162
+			)
1163
+		) {
1164
+			require_once EE_PUBLIC . 'template_tags.php';
1165
+		}
1166
+		do_action('AHEE__EE_System__set_hooks_for_shortcodes_modules_and_addons');
1167
+		if ($this->request->isAdmin() || $this->request->isFrontend() || $this->request->isIframe()) {
1168
+			$this->loader->getShared('EventEspresso\core\services\assets\Registry');
1169
+		}
1170
+	}
1171
+
1172
+
1173
+	/**
1174
+	 * initialize
1175
+	 * this is the best place to begin initializing client code
1176
+	 *
1177
+	 * @access public
1178
+	 * @return void
1179
+	 */
1180
+	public function initialize()
1181
+	{
1182
+		do_action('AHEE__EE_System__initialize');
1183
+	}
1184
+
1185
+
1186
+	/**
1187
+	 * initialize_last
1188
+	 * this is run really late during the WP init hook point, and ensures that mostly everything else that needs to
1189
+	 * initialize has done so
1190
+	 *
1191
+	 * @access public
1192
+	 * @return void
1193
+	 */
1194
+	public function initialize_last()
1195
+	{
1196
+		do_action('AHEE__EE_System__initialize_last');
1197
+		/** @var EventEspresso\core\domain\services\custom_post_types\RewriteRules $rewrite_rules */
1198
+		$rewrite_rules = $this->loader->getShared(
1199
+			'EventEspresso\core\domain\services\custom_post_types\RewriteRules'
1200
+		);
1201
+		$rewrite_rules->flushRewriteRules();
1202
+		add_action('admin_bar_init', array($this, 'addEspressoToolbar'));
1203
+	}
1204
+
1205
+
1206
+	/**
1207
+	 * @return void
1208
+	 * @throws EE_Error
1209
+	 */
1210
+	public function addEspressoToolbar()
1211
+	{
1212
+		$this->loader->getShared(
1213
+			'EventEspresso\core\domain\services\admin\AdminToolBar',
1214
+			array($this->registry->CAP)
1215
+		);
1216
+	}
1217
+
1218
+
1219
+	/**
1220
+	 * do_not_cache
1221
+	 * sets no cache headers and defines no cache constants for WP plugins
1222
+	 *
1223
+	 * @access public
1224
+	 * @return void
1225
+	 */
1226
+	public static function do_not_cache()
1227
+	{
1228
+		// set no cache constants
1229
+		if (! defined('DONOTCACHEPAGE')) {
1230
+			define('DONOTCACHEPAGE', true);
1231
+		}
1232
+		if (! defined('DONOTCACHCEOBJECT')) {
1233
+			define('DONOTCACHCEOBJECT', true);
1234
+		}
1235
+		if (! defined('DONOTCACHEDB')) {
1236
+			define('DONOTCACHEDB', true);
1237
+		}
1238
+		// add no cache headers
1239
+		add_action('send_headers', array('EE_System', 'nocache_headers'), 10);
1240
+		// plus a little extra for nginx and Google Chrome
1241
+		add_filter('nocache_headers', array('EE_System', 'extra_nocache_headers'), 10, 1);
1242
+		// prevent browsers from prefetching of the rel='next' link, because it may contain content that interferes with the registration process
1243
+		remove_action('wp_head', 'adjacent_posts_rel_link_wp_head');
1244
+	}
1245
+
1246
+
1247
+	/**
1248
+	 *    extra_nocache_headers
1249
+	 *
1250
+	 * @access    public
1251
+	 * @param $headers
1252
+	 * @return    array
1253
+	 */
1254
+	public static function extra_nocache_headers($headers)
1255
+	{
1256
+		// for NGINX
1257
+		$headers['X-Accel-Expires'] = 0;
1258
+		// plus extra for Google Chrome since it doesn't seem to respect "no-cache", but WILL respect "no-store"
1259
+		$headers['Cache-Control'] = 'no-store, no-cache, must-revalidate, max-age=0';
1260
+		return $headers;
1261
+	}
1262
+
1263
+
1264
+	/**
1265
+	 *    nocache_headers
1266
+	 *
1267
+	 * @access    public
1268
+	 * @return    void
1269
+	 */
1270
+	public static function nocache_headers()
1271
+	{
1272
+		nocache_headers();
1273
+	}
1274
+
1275
+
1276
+	/**
1277
+	 * simply hooks into "wp_list_pages_exclude" filter (for wp_list_pages method) and makes sure EE critical pages are
1278
+	 * never returned with the function.
1279
+	 *
1280
+	 * @param  array $exclude_array any existing pages being excluded are in this array.
1281
+	 * @return array
1282
+	 */
1283
+	public function remove_pages_from_wp_list_pages($exclude_array)
1284
+	{
1285
+		return array_merge($exclude_array, $this->registry->CFG->core->get_critical_pages_array());
1286
+	}
1287 1287
 }
Please login to merge, or discard this patch.