Completed
Branch master (3457d3)
by
unknown
25:39 queued 23:21
created
admin_pages/venues/Venue_Categories_Admin_List_Table.class.php 2 patches
Spacing   +10 added lines, -10 removed lines patch added patch discarded remove patch
@@ -72,9 +72,9 @@  discard block
 block discarded – undo
72 72
     public function column_id($item)
73 73
     {
74 74
         $content = $item->get('term_id');
75
-        $content .= '  <span class="show-on-mobile-view-only">' . $item->get_first_related('Term')->get(
75
+        $content .= '  <span class="show-on-mobile-view-only">'.$item->get_first_related('Term')->get(
76 76
             'name'
77
-        ) . '</span>';
77
+        ).'</span>';
78 78
         return $content;
79 79
     }
80 80
 
@@ -97,29 +97,29 @@  discard block
 block discarded – undo
97 97
         $term_name = $item->get_first_related('Term')->get('name');
98 98
 
99 99
         $actions = array(
100
-            'edit' => '<a href="' . $edit_link . '" aria-label="' . sprintf(
100
+            'edit' => '<a href="'.$edit_link.'" aria-label="'.sprintf(
101 101
                 /* translators: The name of the venue category */
102 102
                 esc_attr__(
103 103
                     'Edit Category (%s)',
104 104
                     'event_espresso'
105 105
                 ),
106 106
                 $term_name
107
-            ) . '">' . esc_html__('Edit', 'event_espresso') . '</a>',
107
+            ).'">'.esc_html__('Edit', 'event_espresso').'</a>',
108 108
         );
109 109
 
110 110
 
111
-        $actions['delete'] = '<a href="' . $delete_link . '" aria-label="' . sprintf(
111
+        $actions['delete'] = '<a href="'.$delete_link.'" aria-label="'.sprintf(
112 112
             /* translators: The name of the venue category */
113 113
             esc_attr__(
114 114
                 'Delete Category (%s)',
115 115
                 'event_espresso'
116 116
             ),
117 117
             $term_name
118
-        ) . '">' . esc_html__('Delete', 'event_espresso') . '</a>';
118
+        ).'">'.esc_html__('Delete', 'event_espresso').'</a>';
119 119
 
120
-        $content = '<strong><a class="row-title" href="' . $edit_link . '">' . $item->get_first_related('Term')->get(
120
+        $content = '<strong><a class="row-title" href="'.$edit_link.'">'.$item->get_first_related('Term')->get(
121 121
             'name'
122
-        ) . '</a></strong>';
122
+        ).'</a></strong>';
123 123
         $content .= $this->row_actions($actions);
124 124
         return $content;
125 125
     }
@@ -127,7 +127,7 @@  discard block
 block discarded – undo
127 127
 
128 128
     public function column_shortcode($item)
129 129
     {
130
-        $content = '[EVENT_ESPRESSO_CATEGORY category_id="' . $item->get_first_related('Term')->get('slug') . '"]';
130
+        $content = '[EVENT_ESPRESSO_CATEGORY category_id="'.$item->get_first_related('Term')->get('slug').'"]';
131 131
         return $content;
132 132
     }
133 133
 
@@ -139,7 +139,7 @@  discard block
 block discarded – undo
139 139
             'category' => $item->get_first_related('Term')->ID(),
140 140
         );
141 141
         $e_link = EE_Admin_Page::add_query_args_and_nonce($e_args, EE_VENUES_ADMIN_URL);
142
-        $content = '<a href="' . $e_link . '">' . $item->get('term_count') . '</a>';
142
+        $content = '<a href="'.$e_link.'">'.$item->get('term_count').'</a>';
143 143
         return $content;
144 144
     }
145 145
 }
Please login to merge, or discard this patch.
Indentation   +125 added lines, -125 removed lines patch added patch discarded remove patch
@@ -15,129 +15,129 @@
 block discarded – undo
15 15
  */
16 16
 class Venue_Categories_Admin_List_Table extends EE_Admin_List_Table
17 17
 {
18
-    protected function _setup_data()
19
-    {
20
-        $this->_data = $this->_admin_page->get_categories($this->_per_page, $this->_current_page);
21
-        $this->_all_data_count = $this->_admin_page->get_categories($this->_per_page, $this->_current_page, true);
22
-    }
23
-
24
-
25
-    protected function _set_properties()
26
-    {
27
-        $this->_wp_list_args = array(
28
-            'singular' => esc_html__('venue category', 'event_espresso'),
29
-            'plural'   => esc_html__('venue categories', 'event_espresso'),
30
-            'ajax'     => true, // for now,
31
-            'screen'   => $this->_admin_page->get_current_screen()->id,
32
-        );
33
-
34
-        $this->_columns = array(
35
-            'cb'    => '<input type="checkbox" />',
36
-            'id'    => esc_html__('ID', 'event_espresso'),
37
-            'name'  => esc_html__('Name', 'event_espresso'),
38
-            'count' => esc_html__('Venues', 'event_espresso'),
39
-        );
40
-
41
-        $this->_sortable_columns = array(
42
-            'id'    => array('Term.term_id' => true),
43
-            'name'  => array('Term.slug' => false),
44
-            'count' => array('term_count' => false),
45
-        );
46
-
47
-        $this->_hidden_columns = array();
48
-    }
49
-
50
-
51
-    // not needed
52
-    protected function _get_table_filters()
53
-    {
54
-        return array();
55
-    }
56
-
57
-
58
-    protected function _add_view_counts()
59
-    {
60
-        $this->_views['all']['count'] = $this->_all_data_count;
61
-    }
62
-
63
-
64
-    public function column_cb($item)
65
-    {
66
-        return sprintf('<input type="checkbox" name="VEN_CAT_ID[]" value="%s" />', $item->get('term_id'));
67
-    }
68
-
69
-
70
-    public function column_id($item)
71
-    {
72
-        $content = $item->get('term_id');
73
-        $content .= '  <span class="show-on-mobile-view-only">' . $item->get_first_related('Term')->get(
74
-            'name'
75
-        ) . '</span>';
76
-        return $content;
77
-    }
78
-
79
-
80
-    public function column_name($item)
81
-    {
82
-        $edit_query_args = array(
83
-            'action'     => 'edit_category',
84
-            'VEN_CAT_ID' => $item->get('term_id'),
85
-        );
86
-
87
-        $delete_query_args = array(
88
-            'action'     => 'delete_category',
89
-            'VEN_CAT_ID' => $item->get('term_id'),
90
-        );
91
-
92
-        $edit_link = EE_Admin_Page::add_query_args_and_nonce($edit_query_args, EE_VENUES_ADMIN_URL);
93
-        $delete_link = EE_Admin_Page::add_query_args_and_nonce($delete_query_args, EE_VENUES_ADMIN_URL);
94
-
95
-        $term_name = $item->get_first_related('Term')->get('name');
96
-
97
-        $actions = array(
98
-            'edit' => '<a href="' . $edit_link . '" aria-label="' . sprintf(
99
-                /* translators: The name of the venue category */
100
-                esc_attr__(
101
-                    'Edit Category (%s)',
102
-                    'event_espresso'
103
-                ),
104
-                $term_name
105
-            ) . '">' . esc_html__('Edit', 'event_espresso') . '</a>',
106
-        );
107
-
108
-
109
-        $actions['delete'] = '<a href="' . $delete_link . '" aria-label="' . sprintf(
110
-            /* translators: The name of the venue category */
111
-            esc_attr__(
112
-                'Delete Category (%s)',
113
-                'event_espresso'
114
-            ),
115
-            $term_name
116
-        ) . '">' . esc_html__('Delete', 'event_espresso') . '</a>';
117
-
118
-        $content = '<strong><a class="row-title" href="' . $edit_link . '">' . $item->get_first_related('Term')->get(
119
-            'name'
120
-        ) . '</a></strong>';
121
-        $content .= $this->row_actions($actions);
122
-        return $content;
123
-    }
124
-
125
-
126
-    public function column_shortcode($item)
127
-    {
128
-        $content = '[EVENT_ESPRESSO_CATEGORY category_id="' . $item->get_first_related('Term')->get('slug') . '"]';
129
-        return $content;
130
-    }
131
-
132
-
133
-    public function column_count($item)
134
-    {
135
-        $e_args = array(
136
-            'action'   => 'default',
137
-            'category' => $item->get_first_related('Term')->ID(),
138
-        );
139
-        $e_link = EE_Admin_Page::add_query_args_and_nonce($e_args, EE_VENUES_ADMIN_URL);
140
-        $content = '<a href="' . $e_link . '">' . $item->get('term_count') . '</a>';
141
-        return $content;
142
-    }
18
+	protected function _setup_data()
19
+	{
20
+		$this->_data = $this->_admin_page->get_categories($this->_per_page, $this->_current_page);
21
+		$this->_all_data_count = $this->_admin_page->get_categories($this->_per_page, $this->_current_page, true);
22
+	}
23
+
24
+
25
+	protected function _set_properties()
26
+	{
27
+		$this->_wp_list_args = array(
28
+			'singular' => esc_html__('venue category', 'event_espresso'),
29
+			'plural'   => esc_html__('venue categories', 'event_espresso'),
30
+			'ajax'     => true, // for now,
31
+			'screen'   => $this->_admin_page->get_current_screen()->id,
32
+		);
33
+
34
+		$this->_columns = array(
35
+			'cb'    => '<input type="checkbox" />',
36
+			'id'    => esc_html__('ID', 'event_espresso'),
37
+			'name'  => esc_html__('Name', 'event_espresso'),
38
+			'count' => esc_html__('Venues', 'event_espresso'),
39
+		);
40
+
41
+		$this->_sortable_columns = array(
42
+			'id'    => array('Term.term_id' => true),
43
+			'name'  => array('Term.slug' => false),
44
+			'count' => array('term_count' => false),
45
+		);
46
+
47
+		$this->_hidden_columns = array();
48
+	}
49
+
50
+
51
+	// not needed
52
+	protected function _get_table_filters()
53
+	{
54
+		return array();
55
+	}
56
+
57
+
58
+	protected function _add_view_counts()
59
+	{
60
+		$this->_views['all']['count'] = $this->_all_data_count;
61
+	}
62
+
63
+
64
+	public function column_cb($item)
65
+	{
66
+		return sprintf('<input type="checkbox" name="VEN_CAT_ID[]" value="%s" />', $item->get('term_id'));
67
+	}
68
+
69
+
70
+	public function column_id($item)
71
+	{
72
+		$content = $item->get('term_id');
73
+		$content .= '  <span class="show-on-mobile-view-only">' . $item->get_first_related('Term')->get(
74
+			'name'
75
+		) . '</span>';
76
+		return $content;
77
+	}
78
+
79
+
80
+	public function column_name($item)
81
+	{
82
+		$edit_query_args = array(
83
+			'action'     => 'edit_category',
84
+			'VEN_CAT_ID' => $item->get('term_id'),
85
+		);
86
+
87
+		$delete_query_args = array(
88
+			'action'     => 'delete_category',
89
+			'VEN_CAT_ID' => $item->get('term_id'),
90
+		);
91
+
92
+		$edit_link = EE_Admin_Page::add_query_args_and_nonce($edit_query_args, EE_VENUES_ADMIN_URL);
93
+		$delete_link = EE_Admin_Page::add_query_args_and_nonce($delete_query_args, EE_VENUES_ADMIN_URL);
94
+
95
+		$term_name = $item->get_first_related('Term')->get('name');
96
+
97
+		$actions = array(
98
+			'edit' => '<a href="' . $edit_link . '" aria-label="' . sprintf(
99
+				/* translators: The name of the venue category */
100
+				esc_attr__(
101
+					'Edit Category (%s)',
102
+					'event_espresso'
103
+				),
104
+				$term_name
105
+			) . '">' . esc_html__('Edit', 'event_espresso') . '</a>',
106
+		);
107
+
108
+
109
+		$actions['delete'] = '<a href="' . $delete_link . '" aria-label="' . sprintf(
110
+			/* translators: The name of the venue category */
111
+			esc_attr__(
112
+				'Delete Category (%s)',
113
+				'event_espresso'
114
+			),
115
+			$term_name
116
+		) . '">' . esc_html__('Delete', 'event_espresso') . '</a>';
117
+
118
+		$content = '<strong><a class="row-title" href="' . $edit_link . '">' . $item->get_first_related('Term')->get(
119
+			'name'
120
+		) . '</a></strong>';
121
+		$content .= $this->row_actions($actions);
122
+		return $content;
123
+	}
124
+
125
+
126
+	public function column_shortcode($item)
127
+	{
128
+		$content = '[EVENT_ESPRESSO_CATEGORY category_id="' . $item->get_first_related('Term')->get('slug') . '"]';
129
+		return $content;
130
+	}
131
+
132
+
133
+	public function column_count($item)
134
+	{
135
+		$e_args = array(
136
+			'action'   => 'default',
137
+			'category' => $item->get_first_related('Term')->ID(),
138
+		);
139
+		$e_link = EE_Admin_Page::add_query_args_and_nonce($e_args, EE_VENUES_ADMIN_URL);
140
+		$content = '<a href="' . $e_link . '">' . $item->get('term_count') . '</a>';
141
+		return $content;
142
+	}
143 143
 }
Please login to merge, or discard this patch.
core/db_classes/EE_Registration.class.php 2 patches
Spacing   +15 added lines, -15 removed lines patch added patch discarded remove patch
@@ -148,7 +148,7 @@  discard block
 block discarded – undo
148 148
     {
149 149
         switch ($field_name) {
150 150
             case 'REG_code':
151
-                if (! empty($field_value) && $this->reg_code() === null) {
151
+                if ( ! empty($field_value) && $this->reg_code() === null) {
152 152
                     $this->set_reg_code($field_value, $use_default);
153 153
                 }
154 154
                 break;
@@ -449,7 +449,7 @@  discard block
 block discarded – undo
449 449
     public function event()
450 450
     {
451 451
         $event = $this->get_first_related('Event');
452
-        if (! $event instanceof \EE_Event) {
452
+        if ( ! $event instanceof \EE_Event) {
453 453
             throw new EntityNotFoundException('Event ID', $this->event_ID());
454 454
         }
455 455
         return $event;
@@ -496,7 +496,7 @@  discard block
 block discarded – undo
496 496
     {
497 497
         // reserved ticket and datetime counts will be decremented as sold counts are incremented
498 498
         // so stop tracking that this reg has a ticket reserved
499
-        $this->release_reserved_ticket(false, "REG: {$this->ID()} (ln:" . __LINE__ . ')');
499
+        $this->release_reserved_ticket(false, "REG: {$this->ID()} (ln:".__LINE__.')');
500 500
         $ticket = $this->ticket();
501 501
         $ticket->increaseSold();
502 502
         // possibly set event status to sold out
@@ -546,7 +546,7 @@  discard block
 block discarded – undo
546 546
             $reserved = $this->update_extra_meta(EE_Registration::HAS_RESERVED_TICKET_KEY, true);
547 547
             if ($reserved && $update_ticket) {
548 548
                 $ticket = $this->ticket();
549
-                $ticket->increaseReserved(1, "REG: {$this->ID()} (ln:" . __LINE__ . ')');
549
+                $ticket->increaseReserved(1, "REG: {$this->ID()} (ln:".__LINE__.')');
550 550
                 $this->update_extra_meta('reserve_ticket', "{$this->ticket_ID()} from {$source}");
551 551
                 $ticket->save();
552 552
             }
@@ -574,7 +574,7 @@  discard block
 block discarded – undo
574 574
             $reserved = $this->update_extra_meta(EE_Registration::HAS_RESERVED_TICKET_KEY, false);
575 575
             if ($reserved && $update_ticket) {
576 576
                 $ticket = $this->ticket();
577
-                $ticket->decreaseReserved(1, true, "REG: {$this->ID()} (ln:" . __LINE__ . ')');
577
+                $ticket->decreaseReserved(1, true, "REG: {$this->ID()} (ln:".__LINE__.')');
578 578
                 $this->update_extra_meta('release_reserved_ticket', "{$this->ticket_ID()} from {$source}");
579 579
             }
580 580
         }
@@ -1460,7 +1460,7 @@  discard block
 block discarded – undo
1460 1460
                     : '';
1461 1461
                 break;
1462 1462
         }
1463
-        return $icon . $status[ $this->status_ID() ];
1463
+        return $icon.$status[$this->status_ID()];
1464 1464
     }
1465 1465
 
1466 1466
 
@@ -1716,7 +1716,7 @@  discard block
 block discarded – undo
1716 1716
     {
1717 1717
         $DTT_ID = EEM_Datetime::instance()->ensure_is_ID($DTT_OR_ID);
1718 1718
         // first check registration status
1719
-        if (! $DTT_ID || ($check_approved && ! $this->is_approved())) {
1719
+        if ( ! $DTT_ID || ($check_approved && ! $this->is_approved())) {
1720 1720
             return false;
1721 1721
         }
1722 1722
         // is there a datetime ticket that matches this dtt_ID?
@@ -1756,7 +1756,7 @@  discard block
 block discarded – undo
1756 1756
     {
1757 1757
         $DTT_ID = EEM_Datetime::instance()->ensure_is_ID($DTT_OR_ID);
1758 1758
 
1759
-        if (! $DTT_ID) {
1759
+        if ( ! $DTT_ID) {
1760 1760
             return false;
1761 1761
         }
1762 1762
 
@@ -1764,7 +1764,7 @@  discard block
 block discarded – undo
1764 1764
 
1765 1765
         // if max uses is not set or equals infinity then return true cause its not a factor for whether user can
1766 1766
         // check-in or not.
1767
-        if (! $max_uses || $max_uses === EE_INF) {
1767
+        if ( ! $max_uses || $max_uses === EE_INF) {
1768 1768
             return true;
1769 1769
         }
1770 1770
 
@@ -1828,7 +1828,7 @@  discard block
 block discarded – undo
1828 1828
             $datetime = $this->get_latest_related_datetime();
1829 1829
             $DTT_ID = $datetime instanceof EE_Datetime ? $datetime->ID() : 0;
1830 1830
             // verify the registration can checkin for the given DTT_ID
1831
-        } elseif (! $this->can_checkin($DTT_ID, $verify)) {
1831
+        } elseif ( ! $this->can_checkin($DTT_ID, $verify)) {
1832 1832
             EE_Error::add_error(
1833 1833
                 sprintf(
1834 1834
                     esc_html__(
@@ -1851,7 +1851,7 @@  discard block
 block discarded – undo
1851 1851
         );
1852 1852
         // start by getting the current status so we know what status we'll be changing to.
1853 1853
         $cur_status = $this->check_in_status_for_datetime($DTT_ID);
1854
-        $status_to = $status_paths[ $cur_status ];
1854
+        $status_to = $status_paths[$cur_status];
1855 1855
         // database only records true for checked IN or false for checked OUT
1856 1856
         // no record ( null ) means checked in NEVER, but we obviously don't save that
1857 1857
         $new_status = $status_to === EE_Checkin::status_checked_in;
@@ -2042,7 +2042,7 @@  discard block
 block discarded – undo
2042 2042
     public function transaction()
2043 2043
     {
2044 2044
         $transaction = $this->get_first_related('Transaction');
2045
-        if (! $transaction instanceof \EE_Transaction) {
2045
+        if ( ! $transaction instanceof \EE_Transaction) {
2046 2046
             throw new EntityNotFoundException('Transaction ID', $this->transaction_ID());
2047 2047
         }
2048 2048
         return $transaction;
@@ -2115,11 +2115,11 @@  discard block
 block discarded – undo
2115 2115
             );
2116 2116
             return;
2117 2117
         }
2118
-        if (! $this->reg_code()) {
2118
+        if ( ! $this->reg_code()) {
2119 2119
             parent::set('REG_code', $REG_code, $use_default);
2120 2120
         } else {
2121 2121
             EE_Error::doing_it_wrong(
2122
-                __CLASS__ . '::' . __FUNCTION__,
2122
+                __CLASS__.'::'.__FUNCTION__,
2123 2123
                 esc_html__('Can not change a registration REG_code once it has been set.', 'event_espresso'),
2124 2124
                 '4.6.0'
2125 2125
             );
@@ -2310,7 +2310,7 @@  discard block
 block discarded – undo
2310 2310
                 break;
2311 2311
             }
2312 2312
         }
2313
-        if (! ($line_item instanceof \EE_Line_Item && $line_item->OBJ_type() === 'Ticket')) {
2313
+        if ( ! ($line_item instanceof \EE_Line_Item && $line_item->OBJ_type() === 'Ticket')) {
2314 2314
             throw new EntityNotFoundException('Line Item Ticket ID', $ticket->ID());
2315 2315
         }
2316 2316
         return $line_item;
Please login to merge, or discard this patch.
Indentation   +2499 added lines, -2499 removed lines patch added patch discarded remove patch
@@ -16,2503 +16,2503 @@
 block discarded – undo
16 16
  */
17 17
 class EE_Registration extends EE_Soft_Delete_Base_Class implements EEI_Registration, EEI_Admin_Links
18 18
 {
19
-    /**
20
-     * Used to reference when a registration has never been checked in.
21
-     *
22
-     * @deprecated use \EE_Checkin::status_checked_never instead
23
-     * @type int
24
-     */
25
-    const checkin_status_never = 2;
26
-
27
-    /**
28
-     * Used to reference when a registration has been checked in.
29
-     *
30
-     * @deprecated use \EE_Checkin::status_checked_in instead
31
-     * @type int
32
-     */
33
-    const checkin_status_in = 1;
34
-
35
-    /**
36
-     * Used to reference when a registration has been checked out.
37
-     *
38
-     * @deprecated use \EE_Checkin::status_checked_out instead
39
-     * @type int
40
-     */
41
-    const checkin_status_out = 0;
42
-
43
-    /**
44
-     * extra meta key for tracking reg status os trashed registrations
45
-     *
46
-     * @type string
47
-     */
48
-    const PRE_TRASH_REG_STATUS_KEY = 'pre_trash_registration_status';
49
-
50
-    /**
51
-     * extra meta key for tracking if registration has reserved ticket
52
-     *
53
-     * @type string
54
-     */
55
-    const HAS_RESERVED_TICKET_KEY = 'has_reserved_ticket';
56
-
57
-
58
-    /**
59
-     * @param array  $props_n_values          incoming values
60
-     * @param string $timezone                incoming timezone (if not set the timezone set for the website will be
61
-     *                                        used.)
62
-     * @param array  $date_formats            incoming date_formats in an array where the first value is the
63
-     *                                        date_format and the second value is the time format
64
-     * @return EE_Registration
65
-     * @throws EE_Error
66
-     * @throws InvalidArgumentException
67
-     * @throws InvalidDataTypeException
68
-     * @throws InvalidInterfaceException
69
-     * @throws ReflectionException
70
-     */
71
-    public static function new_instance($props_n_values = array(), $timezone = null, $date_formats = array())
72
-    {
73
-        $has_object = parent::_check_for_object($props_n_values, __CLASS__, $timezone, $date_formats);
74
-        return $has_object ? $has_object : new self($props_n_values, false, $timezone, $date_formats);
75
-    }
76
-
77
-
78
-    /**
79
-     * @param array  $props_n_values  incoming values from the database
80
-     * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
81
-     *                                the website will be used.
82
-     * @return EE_Registration
83
-     * @throws EE_Error
84
-     * @throws InvalidArgumentException
85
-     * @throws InvalidDataTypeException
86
-     * @throws InvalidInterfaceException
87
-     * @throws ReflectionException
88
-     */
89
-    public static function new_instance_from_db($props_n_values = array(), $timezone = null)
90
-    {
91
-        return new self($props_n_values, true, $timezone);
92
-    }
93
-
94
-
95
-    /**
96
-     *        Set Event ID
97
-     *
98
-     * @param        int $EVT_ID Event ID
99
-     * @throws DomainException
100
-     * @throws EE_Error
101
-     * @throws EntityNotFoundException
102
-     * @throws InvalidArgumentException
103
-     * @throws InvalidDataTypeException
104
-     * @throws InvalidInterfaceException
105
-     * @throws ReflectionException
106
-     * @throws RuntimeException
107
-     * @throws UnexpectedEntityException
108
-     */
109
-    public function set_event($EVT_ID = 0)
110
-    {
111
-        $this->set('EVT_ID', $EVT_ID);
112
-    }
113
-
114
-
115
-    /**
116
-     * Overrides parent set() method so that all calls to set( 'REG_code', $REG_code ) OR set( 'STS_ID', $STS_ID ) can
117
-     * be routed to internal methods
118
-     *
119
-     * @param string $field_name
120
-     * @param mixed  $field_value
121
-     * @param bool   $use_default
122
-     * @throws DomainException
123
-     * @throws EE_Error
124
-     * @throws EntityNotFoundException
125
-     * @throws InvalidArgumentException
126
-     * @throws InvalidDataTypeException
127
-     * @throws InvalidInterfaceException
128
-     * @throws ReflectionException
129
-     * @throws RuntimeException
130
-     * @throws UnexpectedEntityException
131
-     */
132
-    public function set($field_name, $field_value, $use_default = false)
133
-    {
134
-        switch ($field_name) {
135
-            case 'REG_code':
136
-                if (! empty($field_value) && $this->reg_code() === null) {
137
-                    $this->set_reg_code($field_value, $use_default);
138
-                }
139
-                break;
140
-            case 'STS_ID':
141
-                $this->set_status($field_value, $use_default);
142
-                break;
143
-            default:
144
-                parent::set($field_name, $field_value, $use_default);
145
-        }
146
-    }
147
-
148
-
149
-    /**
150
-     * Set Status ID
151
-     * updates the registration status and ALSO...
152
-     * calls reserve_registration_space() if the reg status changes TO approved from any other reg status
153
-     * calls release_registration_space() if the reg status changes FROM approved to any other reg status
154
-     *
155
-     * @param string                $new_STS_ID
156
-     * @param boolean               $use_default
157
-     * @param ContextInterface|null $context
158
-     * @return bool
159
-     * @throws DomainException
160
-     * @throws EE_Error
161
-     * @throws EntityNotFoundException
162
-     * @throws InvalidArgumentException
163
-     * @throws InvalidDataTypeException
164
-     * @throws InvalidInterfaceException
165
-     * @throws ReflectionException
166
-     * @throws RuntimeException
167
-     * @throws UnexpectedEntityException
168
-     */
169
-    public function set_status($new_STS_ID = null, $use_default = false, ContextInterface $context = null)
170
-    {
171
-        // get current REG_Status
172
-        $old_STS_ID = $this->status_ID();
173
-        // if status has changed
174
-        if (
175
-            $old_STS_ID !== $new_STS_ID // and that status has actually changed
176
-            && ! empty($old_STS_ID) // and that old status is actually set
177
-            && ! empty($new_STS_ID) // as well as the new status
178
-            && $this->ID() // ensure registration is in the db
179
-        ) {
180
-            // update internal status first
181
-            parent::set('STS_ID', $new_STS_ID, $use_default);
182
-            // THEN handle other changes that occur when reg status changes
183
-            // TO approved
184
-            if ($new_STS_ID === EEM_Registration::status_id_approved) {
185
-                // reserve a space by incrementing ticket and datetime sold values
186
-                $this->reserveRegistrationSpace();
187
-                do_action('AHEE__EE_Registration__set_status__to_approved', $this, $old_STS_ID, $new_STS_ID, $context);
188
-                // OR FROM  approved
189
-            } elseif ($old_STS_ID === EEM_Registration::status_id_approved) {
190
-                // release a space by decrementing ticket and datetime sold values
191
-                $this->releaseRegistrationSpace();
192
-                do_action(
193
-                    'AHEE__EE_Registration__set_status__from_approved',
194
-                    $this,
195
-                    $old_STS_ID,
196
-                    $new_STS_ID,
197
-                    $context
198
-                );
199
-            }
200
-            // update status
201
-            parent::set('STS_ID', $new_STS_ID, $use_default);
202
-            $this->updateIfCanceledOrReinstated($new_STS_ID, $old_STS_ID, $context);
203
-            if ($this->statusChangeUpdatesTransaction($context)) {
204
-                $this->updateTransactionAfterStatusChange();
205
-            }
206
-            do_action('AHEE__EE_Registration__set_status__after_update', $this, $old_STS_ID, $new_STS_ID, $context);
207
-            return true;
208
-        }
209
-        // even though the old value matches the new value, it's still good to
210
-        // allow the parent set method to have a say
211
-        parent::set('STS_ID', $new_STS_ID, $use_default);
212
-        return true;
213
-    }
214
-
215
-
216
-    /**
217
-     * update REGs and TXN when cancelled or declined registrations involved
218
-     *
219
-     * @param string                $new_STS_ID
220
-     * @param string                $old_STS_ID
221
-     * @param ContextInterface|null $context
222
-     * @throws EE_Error
223
-     * @throws InvalidArgumentException
224
-     * @throws InvalidDataTypeException
225
-     * @throws InvalidInterfaceException
226
-     * @throws ReflectionException
227
-     * @throws RuntimeException
228
-     */
229
-    private function updateIfCanceledOrReinstated($new_STS_ID, $old_STS_ID, ContextInterface $context = null)
230
-    {
231
-        // these reg statuses should not be considered in any calculations involving monies owing
232
-        $closed_reg_statuses = EEM_Registration::closed_reg_statuses();
233
-        // true if registration has been cancelled or declined
234
-        $this->updateIfCanceled(
235
-            $closed_reg_statuses,
236
-            $new_STS_ID,
237
-            $old_STS_ID,
238
-            $context
239
-        );
240
-        $this->updateIfReinstated(
241
-            $closed_reg_statuses,
242
-            $new_STS_ID,
243
-            $old_STS_ID,
244
-            $context
245
-        );
246
-    }
247
-
248
-
249
-    /**
250
-     * update REGs and TXN when cancelled or declined registrations involved
251
-     *
252
-     * @param array                 $closed_reg_statuses
253
-     * @param string                $new_STS_ID
254
-     * @param string                $old_STS_ID
255
-     * @param ContextInterface|null $context
256
-     * @throws EE_Error
257
-     * @throws InvalidArgumentException
258
-     * @throws InvalidDataTypeException
259
-     * @throws InvalidInterfaceException
260
-     * @throws ReflectionException
261
-     * @throws RuntimeException
262
-     */
263
-    private function updateIfCanceled(
264
-        array $closed_reg_statuses,
265
-        $new_STS_ID,
266
-        $old_STS_ID,
267
-        ContextInterface $context = null
268
-    ) {
269
-        // true if registration has been cancelled or declined
270
-        if (
271
-            in_array($new_STS_ID, $closed_reg_statuses, true)
272
-            && ! in_array($old_STS_ID, $closed_reg_statuses, true)
273
-        ) {
274
-            /** @type EE_Registration_Processor $registration_processor */
275
-            $registration_processor = EE_Registry::instance()->load_class('Registration_Processor');
276
-            /** @type EE_Transaction_Processor $transaction_processor */
277
-            $transaction_processor = EE_Registry::instance()->load_class('Transaction_Processor');
278
-            // cancelled or declined registration
279
-            $registration_processor->update_registration_after_being_canceled_or_declined(
280
-                $this,
281
-                $closed_reg_statuses
282
-            );
283
-            $transaction_processor->update_transaction_after_canceled_or_declined_registration(
284
-                $this,
285
-                $closed_reg_statuses,
286
-                false
287
-            );
288
-            do_action(
289
-                'AHEE__EE_Registration__set_status__canceled_or_declined',
290
-                $this,
291
-                $old_STS_ID,
292
-                $new_STS_ID,
293
-                $context
294
-            );
295
-            return;
296
-        }
297
-    }
298
-
299
-
300
-    /**
301
-     * update REGs and TXN when cancelled or declined registrations involved
302
-     *
303
-     * @param array                 $closed_reg_statuses
304
-     * @param string                $new_STS_ID
305
-     * @param string                $old_STS_ID
306
-     * @param ContextInterface|null $context
307
-     * @throws EE_Error
308
-     * @throws InvalidArgumentException
309
-     * @throws InvalidDataTypeException
310
-     * @throws InvalidInterfaceException
311
-     * @throws ReflectionException
312
-     * @throws RuntimeException
313
-     */
314
-    private function updateIfReinstated(
315
-        array $closed_reg_statuses,
316
-        $new_STS_ID,
317
-        $old_STS_ID,
318
-        ContextInterface $context = null
319
-    ) {
320
-        // true if reinstating cancelled or declined registration
321
-        if (
322
-            in_array($old_STS_ID, $closed_reg_statuses, true)
323
-            && ! in_array($new_STS_ID, $closed_reg_statuses, true)
324
-        ) {
325
-            /** @type EE_Registration_Processor $registration_processor */
326
-            $registration_processor = EE_Registry::instance()->load_class('Registration_Processor');
327
-            /** @type EE_Transaction_Processor $transaction_processor */
328
-            $transaction_processor = EE_Registry::instance()->load_class('Transaction_Processor');
329
-            // reinstating cancelled or declined registration
330
-            $registration_processor->update_canceled_or_declined_registration_after_being_reinstated(
331
-                $this,
332
-                $closed_reg_statuses
333
-            );
334
-            $transaction_processor->update_transaction_after_reinstating_canceled_registration(
335
-                $this,
336
-                $closed_reg_statuses,
337
-                false
338
-            );
339
-            do_action(
340
-                'AHEE__EE_Registration__set_status__after_reinstated',
341
-                $this,
342
-                $old_STS_ID,
343
-                $new_STS_ID,
344
-                $context
345
-            );
346
-        }
347
-    }
348
-
349
-
350
-    /**
351
-     * @param ContextInterface|null $context
352
-     * @return bool
353
-     */
354
-    private function statusChangeUpdatesTransaction(ContextInterface $context = null)
355
-    {
356
-        $contexts_that_do_not_update_transaction = (array) apply_filters(
357
-            'AHEE__EE_Registration__statusChangeUpdatesTransaction__contexts_that_do_not_update_transaction',
358
-            array('spco_reg_step_attendee_information_process_registrations'),
359
-            $context,
360
-            $this
361
-        );
362
-        return ! (
363
-            $context instanceof ContextInterface
364
-            && in_array($context->slug(), $contexts_that_do_not_update_transaction, true)
365
-        );
366
-    }
367
-
368
-
369
-    /**
370
-     * @throws EE_Error
371
-     * @throws EntityNotFoundException
372
-     * @throws InvalidArgumentException
373
-     * @throws InvalidDataTypeException
374
-     * @throws InvalidInterfaceException
375
-     * @throws ReflectionException
376
-     * @throws RuntimeException
377
-     */
378
-    private function updateTransactionAfterStatusChange()
379
-    {
380
-        /** @type EE_Transaction_Payments $transaction_payments */
381
-        $transaction_payments = EE_Registry::instance()->load_class('Transaction_Payments');
382
-        $transaction_payments->recalculate_transaction_total($this->transaction(), false);
383
-        $this->transaction()->update_status_based_on_total_paid();
384
-    }
385
-
386
-
387
-    /**
388
-     * get Status ID
389
-     *
390
-     * @throws EE_Error
391
-     * @throws InvalidArgumentException
392
-     * @throws InvalidDataTypeException
393
-     * @throws InvalidInterfaceException
394
-     * @throws ReflectionException
395
-     */
396
-    public function status_ID()
397
-    {
398
-        return $this->get('STS_ID');
399
-    }
400
-
401
-
402
-    /**
403
-     * Gets the ticket this registration is for
404
-     *
405
-     * @param boolean $include_archived whether to include archived tickets or not.
406
-     * @return EE_Ticket|EE_Base_Class
407
-     * @throws EE_Error
408
-     * @throws InvalidArgumentException
409
-     * @throws InvalidDataTypeException
410
-     * @throws InvalidInterfaceException
411
-     * @throws ReflectionException
412
-     */
413
-    public function ticket($include_archived = true)
414
-    {
415
-        $query_params = array();
416
-        if ($include_archived) {
417
-            $query_params['default_where_conditions'] = 'none';
418
-        }
419
-        return $this->get_first_related('Ticket', $query_params);
420
-    }
421
-
422
-
423
-    /**
424
-     * Gets the event this registration is for
425
-     *
426
-     * @return EE_Event
427
-     * @throws EE_Error
428
-     * @throws EntityNotFoundException
429
-     * @throws InvalidArgumentException
430
-     * @throws InvalidDataTypeException
431
-     * @throws InvalidInterfaceException
432
-     * @throws ReflectionException
433
-     */
434
-    public function event()
435
-    {
436
-        $event = $this->get_first_related('Event');
437
-        if (! $event instanceof \EE_Event) {
438
-            throw new EntityNotFoundException('Event ID', $this->event_ID());
439
-        }
440
-        return $event;
441
-    }
442
-
443
-
444
-    /**
445
-     * Gets the "author" of the registration.  Note that for the purposes of registrations, the author will correspond
446
-     * with the author of the event this registration is for.
447
-     *
448
-     * @since 4.5.0
449
-     * @return int
450
-     * @throws EE_Error
451
-     * @throws EntityNotFoundException
452
-     * @throws InvalidArgumentException
453
-     * @throws InvalidDataTypeException
454
-     * @throws InvalidInterfaceException
455
-     * @throws ReflectionException
456
-     */
457
-    public function wp_user()
458
-    {
459
-        $event = $this->event();
460
-        if ($event instanceof EE_Event) {
461
-            return $event->wp_user();
462
-        }
463
-        return 0;
464
-    }
465
-
466
-
467
-    /**
468
-     * increments this registration's related ticket sold and corresponding datetime sold values
469
-     *
470
-     * @return void
471
-     * @throws DomainException
472
-     * @throws EE_Error
473
-     * @throws EntityNotFoundException
474
-     * @throws InvalidArgumentException
475
-     * @throws InvalidDataTypeException
476
-     * @throws InvalidInterfaceException
477
-     * @throws ReflectionException
478
-     * @throws UnexpectedEntityException
479
-     */
480
-    private function reserveRegistrationSpace()
481
-    {
482
-        // reserved ticket and datetime counts will be decremented as sold counts are incremented
483
-        // so stop tracking that this reg has a ticket reserved
484
-        $this->release_reserved_ticket(false, "REG: {$this->ID()} (ln:" . __LINE__ . ')');
485
-        $ticket = $this->ticket();
486
-        $ticket->increaseSold();
487
-        // possibly set event status to sold out
488
-        $this->event()->perform_sold_out_status_check();
489
-    }
490
-
491
-
492
-    /**
493
-     * decrements (subtracts) this registration's related ticket sold and corresponding datetime sold values
494
-     *
495
-     * @return void
496
-     * @throws DomainException
497
-     * @throws EE_Error
498
-     * @throws EntityNotFoundException
499
-     * @throws InvalidArgumentException
500
-     * @throws InvalidDataTypeException
501
-     * @throws InvalidInterfaceException
502
-     * @throws ReflectionException
503
-     * @throws UnexpectedEntityException
504
-     */
505
-    private function releaseRegistrationSpace()
506
-    {
507
-        $ticket = $this->ticket();
508
-        $ticket->decreaseSold();
509
-        // possibly change event status from sold out back to previous status
510
-        $this->event()->perform_sold_out_status_check();
511
-    }
512
-
513
-
514
-    /**
515
-     * tracks this registration's ticket reservation in extra meta
516
-     * and can increment related ticket reserved and corresponding datetime reserved values
517
-     *
518
-     * @param bool   $update_ticket if true, will increment ticket and datetime reserved count
519
-     * @param string $source
520
-     * @return void
521
-     * @throws EE_Error
522
-     * @throws InvalidArgumentException
523
-     * @throws InvalidDataTypeException
524
-     * @throws InvalidInterfaceException
525
-     * @throws ReflectionException
526
-     */
527
-    public function reserve_ticket($update_ticket = false, $source = 'unknown')
528
-    {
529
-        // only reserve ticket if space is not currently reserved
530
-        if ((bool) $this->get_extra_meta(EE_Registration::HAS_RESERVED_TICKET_KEY, true) !== true) {
531
-            $reserved = $this->update_extra_meta(EE_Registration::HAS_RESERVED_TICKET_KEY, true);
532
-            if ($reserved && $update_ticket) {
533
-                $ticket = $this->ticket();
534
-                $ticket->increaseReserved(1, "REG: {$this->ID()} (ln:" . __LINE__ . ')');
535
-                $this->update_extra_meta('reserve_ticket', "{$this->ticket_ID()} from {$source}");
536
-                $ticket->save();
537
-            }
538
-        }
539
-    }
540
-
541
-
542
-    /**
543
-     * stops tracking this registration's ticket reservation in extra meta
544
-     * decrements (subtracts) related ticket reserved and corresponding datetime reserved values
545
-     *
546
-     * @param bool   $update_ticket if true, will decrement ticket and datetime reserved count
547
-     * @param string $source
548
-     * @return void
549
-     * @throws EE_Error
550
-     * @throws InvalidArgumentException
551
-     * @throws InvalidDataTypeException
552
-     * @throws InvalidInterfaceException
553
-     * @throws ReflectionException
554
-     */
555
-    public function release_reserved_ticket($update_ticket = false, $source = 'unknown')
556
-    {
557
-        // only release ticket if space is currently reserved
558
-        if ((bool) $this->get_extra_meta(EE_Registration::HAS_RESERVED_TICKET_KEY, true) === true) {
559
-            $reserved = $this->update_extra_meta(EE_Registration::HAS_RESERVED_TICKET_KEY, false);
560
-            if ($reserved && $update_ticket) {
561
-                $ticket = $this->ticket();
562
-                $ticket->decreaseReserved(1, true, "REG: {$this->ID()} (ln:" . __LINE__ . ')');
563
-                $this->update_extra_meta('release_reserved_ticket', "{$this->ticket_ID()} from {$source}");
564
-            }
565
-        }
566
-    }
567
-
568
-
569
-    /**
570
-     * Set Attendee ID
571
-     *
572
-     * @param        int $ATT_ID Attendee ID
573
-     * @throws DomainException
574
-     * @throws EE_Error
575
-     * @throws EntityNotFoundException
576
-     * @throws InvalidArgumentException
577
-     * @throws InvalidDataTypeException
578
-     * @throws InvalidInterfaceException
579
-     * @throws ReflectionException
580
-     * @throws RuntimeException
581
-     * @throws UnexpectedEntityException
582
-     */
583
-    public function set_attendee_id($ATT_ID = 0)
584
-    {
585
-        $this->set('ATT_ID', $ATT_ID);
586
-    }
587
-
588
-
589
-    /**
590
-     *        Set Transaction ID
591
-     *
592
-     * @param        int $TXN_ID Transaction ID
593
-     * @throws DomainException
594
-     * @throws EE_Error
595
-     * @throws EntityNotFoundException
596
-     * @throws InvalidArgumentException
597
-     * @throws InvalidDataTypeException
598
-     * @throws InvalidInterfaceException
599
-     * @throws ReflectionException
600
-     * @throws RuntimeException
601
-     * @throws UnexpectedEntityException
602
-     */
603
-    public function set_transaction_id($TXN_ID = 0)
604
-    {
605
-        $this->set('TXN_ID', $TXN_ID);
606
-    }
607
-
608
-
609
-    /**
610
-     *        Set Session
611
-     *
612
-     * @param    string $REG_session PHP Session ID
613
-     * @throws DomainException
614
-     * @throws EE_Error
615
-     * @throws EntityNotFoundException
616
-     * @throws InvalidArgumentException
617
-     * @throws InvalidDataTypeException
618
-     * @throws InvalidInterfaceException
619
-     * @throws ReflectionException
620
-     * @throws RuntimeException
621
-     * @throws UnexpectedEntityException
622
-     */
623
-    public function set_session($REG_session = '')
624
-    {
625
-        $this->set('REG_session', $REG_session);
626
-    }
627
-
628
-
629
-    /**
630
-     *        Set Registration URL Link
631
-     *
632
-     * @param    string $REG_url_link Registration URL Link
633
-     * @throws DomainException
634
-     * @throws EE_Error
635
-     * @throws EntityNotFoundException
636
-     * @throws InvalidArgumentException
637
-     * @throws InvalidDataTypeException
638
-     * @throws InvalidInterfaceException
639
-     * @throws ReflectionException
640
-     * @throws RuntimeException
641
-     * @throws UnexpectedEntityException
642
-     */
643
-    public function set_reg_url_link($REG_url_link = '')
644
-    {
645
-        $this->set('REG_url_link', $REG_url_link);
646
-    }
647
-
648
-
649
-    /**
650
-     *        Set Attendee Counter
651
-     *
652
-     * @param        int $REG_count Primary Attendee
653
-     * @throws DomainException
654
-     * @throws EE_Error
655
-     * @throws EntityNotFoundException
656
-     * @throws InvalidArgumentException
657
-     * @throws InvalidDataTypeException
658
-     * @throws InvalidInterfaceException
659
-     * @throws ReflectionException
660
-     * @throws RuntimeException
661
-     * @throws UnexpectedEntityException
662
-     */
663
-    public function set_count($REG_count = 1)
664
-    {
665
-        $this->set('REG_count', $REG_count);
666
-    }
667
-
668
-
669
-    /**
670
-     *        Set Group Size
671
-     *
672
-     * @param        boolean $REG_group_size Group Registration
673
-     * @throws DomainException
674
-     * @throws EE_Error
675
-     * @throws EntityNotFoundException
676
-     * @throws InvalidArgumentException
677
-     * @throws InvalidDataTypeException
678
-     * @throws InvalidInterfaceException
679
-     * @throws ReflectionException
680
-     * @throws RuntimeException
681
-     * @throws UnexpectedEntityException
682
-     */
683
-    public function set_group_size($REG_group_size = false)
684
-    {
685
-        $this->set('REG_group_size', $REG_group_size);
686
-    }
687
-
688
-
689
-    /**
690
-     *    is_not_approved -  convenience method that returns TRUE if REG status ID ==
691
-     *    EEM_Registration::status_id_not_approved
692
-     *
693
-     * @return        boolean
694
-     * @throws EE_Error
695
-     * @throws InvalidArgumentException
696
-     * @throws InvalidDataTypeException
697
-     * @throws InvalidInterfaceException
698
-     * @throws ReflectionException
699
-     */
700
-    public function is_not_approved()
701
-    {
702
-        return $this->status_ID() === EEM_Registration::status_id_not_approved;
703
-    }
704
-
705
-
706
-    /**
707
-     *    is_pending_payment -  convenience method that returns TRUE if REG status ID ==
708
-     *    EEM_Registration::status_id_pending_payment
709
-     *
710
-     * @return        boolean
711
-     * @throws EE_Error
712
-     * @throws InvalidArgumentException
713
-     * @throws InvalidDataTypeException
714
-     * @throws InvalidInterfaceException
715
-     * @throws ReflectionException
716
-     */
717
-    public function is_pending_payment()
718
-    {
719
-        return $this->status_ID() === EEM_Registration::status_id_pending_payment;
720
-    }
721
-
722
-
723
-    /**
724
-     *    is_approved -  convenience method that returns TRUE if REG status ID == EEM_Registration::status_id_approved
725
-     *
726
-     * @return        boolean
727
-     * @throws EE_Error
728
-     * @throws InvalidArgumentException
729
-     * @throws InvalidDataTypeException
730
-     * @throws InvalidInterfaceException
731
-     * @throws ReflectionException
732
-     */
733
-    public function is_approved()
734
-    {
735
-        return $this->status_ID() === EEM_Registration::status_id_approved;
736
-    }
737
-
738
-
739
-    /**
740
-     *    is_cancelled -  convenience method that returns TRUE if REG status ID == EEM_Registration::status_id_cancelled
741
-     *
742
-     * @return        boolean
743
-     * @throws EE_Error
744
-     * @throws InvalidArgumentException
745
-     * @throws InvalidDataTypeException
746
-     * @throws InvalidInterfaceException
747
-     * @throws ReflectionException
748
-     */
749
-    public function is_cancelled()
750
-    {
751
-        return $this->status_ID() === EEM_Registration::status_id_cancelled;
752
-    }
753
-
754
-
755
-    /**
756
-     *    is_declined -  convenience method that returns TRUE if REG status ID == EEM_Registration::status_id_declined
757
-     *
758
-     * @return        boolean
759
-     * @throws EE_Error
760
-     * @throws InvalidArgumentException
761
-     * @throws InvalidDataTypeException
762
-     * @throws InvalidInterfaceException
763
-     * @throws ReflectionException
764
-     */
765
-    public function is_declined()
766
-    {
767
-        return $this->status_ID() === EEM_Registration::status_id_declined;
768
-    }
769
-
770
-
771
-    /**
772
-     *    is_incomplete -  convenience method that returns TRUE if REG status ID ==
773
-     *    EEM_Registration::status_id_incomplete
774
-     *
775
-     * @return        boolean
776
-     * @throws EE_Error
777
-     * @throws InvalidArgumentException
778
-     * @throws InvalidDataTypeException
779
-     * @throws InvalidInterfaceException
780
-     * @throws ReflectionException
781
-     */
782
-    public function is_incomplete()
783
-    {
784
-        return $this->status_ID() === EEM_Registration::status_id_incomplete;
785
-    }
786
-
787
-
788
-    /**
789
-     *        Set Registration Date
790
-     *
791
-     * @param        mixed ( int or string ) $REG_date Registration Date - Unix timestamp or string representation of
792
-     *                                                 Date
793
-     * @throws DomainException
794
-     * @throws EE_Error
795
-     * @throws EntityNotFoundException
796
-     * @throws InvalidArgumentException
797
-     * @throws InvalidDataTypeException
798
-     * @throws InvalidInterfaceException
799
-     * @throws ReflectionException
800
-     * @throws RuntimeException
801
-     * @throws UnexpectedEntityException
802
-     */
803
-    public function set_reg_date($REG_date = false)
804
-    {
805
-        $this->set('REG_date', $REG_date);
806
-    }
807
-
808
-
809
-    /**
810
-     *    Set final price owing for this registration after all ticket/price modifications
811
-     *
812
-     * @param    float $REG_final_price
813
-     * @throws DomainException
814
-     * @throws EE_Error
815
-     * @throws EntityNotFoundException
816
-     * @throws InvalidArgumentException
817
-     * @throws InvalidDataTypeException
818
-     * @throws InvalidInterfaceException
819
-     * @throws ReflectionException
820
-     * @throws RuntimeException
821
-     * @throws UnexpectedEntityException
822
-     */
823
-    public function set_final_price($REG_final_price = 0.00)
824
-    {
825
-        $this->set('REG_final_price', $REG_final_price);
826
-    }
827
-
828
-
829
-    /**
830
-     *    Set amount paid towards this registration's final price
831
-     *
832
-     * @param    float $REG_paid
833
-     * @throws DomainException
834
-     * @throws EE_Error
835
-     * @throws EntityNotFoundException
836
-     * @throws InvalidArgumentException
837
-     * @throws InvalidDataTypeException
838
-     * @throws InvalidInterfaceException
839
-     * @throws ReflectionException
840
-     * @throws RuntimeException
841
-     * @throws UnexpectedEntityException
842
-     */
843
-    public function set_paid($REG_paid = 0.00)
844
-    {
845
-        $this->set('REG_paid', $REG_paid);
846
-    }
847
-
848
-
849
-    /**
850
-     *        Attendee Is Going
851
-     *
852
-     * @param        boolean $REG_att_is_going Attendee Is Going
853
-     * @throws DomainException
854
-     * @throws EE_Error
855
-     * @throws EntityNotFoundException
856
-     * @throws InvalidArgumentException
857
-     * @throws InvalidDataTypeException
858
-     * @throws InvalidInterfaceException
859
-     * @throws ReflectionException
860
-     * @throws RuntimeException
861
-     * @throws UnexpectedEntityException
862
-     */
863
-    public function set_att_is_going($REG_att_is_going = false)
864
-    {
865
-        $this->set('REG_att_is_going', $REG_att_is_going);
866
-    }
867
-
868
-
869
-    /**
870
-     * Gets the related attendee
871
-     *
872
-     * @return EE_Attendee|EE_Base_Class
873
-     * @throws EE_Error
874
-     * @throws InvalidArgumentException
875
-     * @throws InvalidDataTypeException
876
-     * @throws InvalidInterfaceException
877
-     * @throws ReflectionException
878
-     */
879
-    public function attendee()
880
-    {
881
-        return $this->get_first_related('Attendee');
882
-    }
883
-
884
-    /**
885
-     * Gets the name of the attendee.
886
-     * @since 4.10.12.p
887
-     * @param bool $apply_html_entities set to true if you want to use HTML entities.
888
-     * @return string
889
-     * @throws EE_Error
890
-     * @throws InvalidArgumentException
891
-     * @throws InvalidDataTypeException
892
-     * @throws InvalidInterfaceException
893
-     * @throws ReflectionException
894
-     */
895
-    public function attendeeName($apply_html_entities = false)
896
-    {
897
-        $attendee = $this->get_first_related('Attendee');
898
-        if ($attendee instanceof EE_Attendee) {
899
-            $attendee_name = $attendee->full_name($apply_html_entities);
900
-        } else {
901
-            $attendee_name = esc_html__('Unknown', 'event_espresso');
902
-        }
903
-        return $attendee_name;
904
-    }
905
-
906
-
907
-    /**
908
-     *        get Event ID
909
-     */
910
-    public function event_ID()
911
-    {
912
-        return $this->get('EVT_ID');
913
-    }
914
-
915
-
916
-    /**
917
-     *        get Event ID
918
-     */
919
-    public function event_name()
920
-    {
921
-        $event = $this->event_obj();
922
-        if ($event) {
923
-            return $event->name();
924
-        } else {
925
-            return null;
926
-        }
927
-    }
928
-
929
-
930
-    /**
931
-     * Fetches the event this registration is for
932
-     *
933
-     * @return EE_Base_Class|EE_Event
934
-     * @throws EE_Error
935
-     * @throws InvalidArgumentException
936
-     * @throws InvalidDataTypeException
937
-     * @throws InvalidInterfaceException
938
-     * @throws ReflectionException
939
-     */
940
-    public function event_obj()
941
-    {
942
-        return $this->get_first_related('Event');
943
-    }
944
-
945
-
946
-    /**
947
-     *        get Attendee ID
948
-     */
949
-    public function attendee_ID()
950
-    {
951
-        return $this->get('ATT_ID');
952
-    }
953
-
954
-
955
-    /**
956
-     *        get PHP Session ID
957
-     */
958
-    public function session_ID()
959
-    {
960
-        return $this->get('REG_session');
961
-    }
962
-
963
-
964
-    /**
965
-     * Gets the string which represents the URL trigger for the receipt template in the message template system.
966
-     *
967
-     * @param string $messenger 'pdf' or 'html'.  Default 'html'.
968
-     * @return string
969
-     * @throws DomainException
970
-     * @throws EE_Error
971
-     * @throws InvalidArgumentException
972
-     * @throws InvalidDataTypeException
973
-     * @throws InvalidInterfaceException
974
-     * @throws ReflectionException
975
-     */
976
-    public function receipt_url($messenger = 'html')
977
-    {
978
-
979
-        /**
980
-         * The below will be deprecated one version after this.  We check first if there is a custom receipt template
981
-         * already in use on old system.  If there is then we just return the standard url for it.
982
-         *
983
-         * @since 4.5.0
984
-         */
985
-        $template_relative_path = 'modules/gateways/Invoice/lib/templates/receipt_body.template.php';
986
-        $has_custom = EEH_Template::locate_template(
987
-            $template_relative_path,
988
-            array(),
989
-            true,
990
-            true,
991
-            true
992
-        );
993
-
994
-        if ($has_custom) {
995
-            return add_query_arg(array('receipt' => 'true'), $this->invoice_url('launch'));
996
-        }
997
-        return apply_filters('FHEE__EE_Registration__receipt_url__receipt_url', '', $this, $messenger, 'receipt');
998
-    }
999
-
1000
-
1001
-    /**
1002
-     * Gets the string which represents the URL trigger for the invoice template in the message template system.
1003
-     *
1004
-     * @param string $messenger 'pdf' or 'html'.  Default 'html'.
1005
-     * @return string
1006
-     * @throws DomainException
1007
-     * @throws EE_Error
1008
-     * @throws InvalidArgumentException
1009
-     * @throws InvalidDataTypeException
1010
-     * @throws InvalidInterfaceException
1011
-     * @throws ReflectionException
1012
-     */
1013
-    public function invoice_url($messenger = 'html')
1014
-    {
1015
-        /**
1016
-         * The below will be deprecated one version after this.  We check first if there is a custom invoice template
1017
-         * already in use on old system.  If there is then we just return the standard url for it.
1018
-         *
1019
-         * @since 4.5.0
1020
-         */
1021
-        $template_relative_path = 'modules/gateways/Invoice/lib/templates/invoice_body.template.php';
1022
-        $has_custom = EEH_Template::locate_template(
1023
-            $template_relative_path,
1024
-            array(),
1025
-            true,
1026
-            true,
1027
-            true
1028
-        );
1029
-
1030
-        if ($has_custom) {
1031
-            if ($messenger == 'html') {
1032
-                return $this->invoice_url('launch');
1033
-            }
1034
-            $route = $messenger == 'download' || $messenger == 'pdf' ? 'download_invoice' : 'launch_invoice';
1035
-
1036
-            $query_args = array('ee' => $route, 'id' => $this->reg_url_link());
1037
-            if ($messenger == 'html') {
1038
-                $query_args['html'] = true;
1039
-            }
1040
-            return add_query_arg($query_args, get_permalink(EE_Registry::instance()->CFG->core->thank_you_page_id));
1041
-        }
1042
-        return apply_filters('FHEE__EE_Registration__invoice_url__invoice_url', '', $this, $messenger, 'invoice');
1043
-    }
1044
-
1045
-
1046
-    /**
1047
-     * get Registration URL Link
1048
-     *
1049
-     * @return string
1050
-     * @throws EE_Error
1051
-     * @throws InvalidArgumentException
1052
-     * @throws InvalidDataTypeException
1053
-     * @throws InvalidInterfaceException
1054
-     * @throws ReflectionException
1055
-     */
1056
-    public function reg_url_link()
1057
-    {
1058
-        return (string) $this->get('REG_url_link');
1059
-    }
1060
-
1061
-
1062
-    /**
1063
-     * Echoes out invoice_url()
1064
-     *
1065
-     * @param string $type 'download','launch', or 'html' (default is 'launch')
1066
-     * @return void
1067
-     * @throws DomainException
1068
-     * @throws EE_Error
1069
-     * @throws InvalidArgumentException
1070
-     * @throws InvalidDataTypeException
1071
-     * @throws InvalidInterfaceException
1072
-     * @throws ReflectionException
1073
-     */
1074
-    public function e_invoice_url($type = 'launch')
1075
-    {
1076
-        echo esc_url_raw($this->invoice_url($type));
1077
-    }
1078
-
1079
-
1080
-    /**
1081
-     * Echoes out payment_overview_url
1082
-     */
1083
-    public function e_payment_overview_url()
1084
-    {
1085
-        echo esc_url_raw($this->payment_overview_url());
1086
-    }
1087
-
1088
-
1089
-    /**
1090
-     * Gets the URL for the checkout payment options reg step
1091
-     * with this registration's REG_url_link added as a query parameter
1092
-     *
1093
-     * @param bool $clear_session Set to true when you want to clear the session on revisiting the
1094
-     *                            payment overview url.
1095
-     * @return string
1096
-     * @throws EE_Error
1097
-     * @throws InvalidArgumentException
1098
-     * @throws InvalidDataTypeException
1099
-     * @throws InvalidInterfaceException
1100
-     * @throws ReflectionException
1101
-     */
1102
-    public function payment_overview_url($clear_session = false)
1103
-    {
1104
-        return add_query_arg(
1105
-            (array) apply_filters(
1106
-                'FHEE__EE_Registration__payment_overview_url__query_args',
1107
-                array(
1108
-                    'e_reg_url_link' => $this->reg_url_link(),
1109
-                    'step'           => 'payment_options',
1110
-                    'revisit'        => true,
1111
-                    'clear_session'  => (bool) $clear_session,
1112
-                ),
1113
-                $this
1114
-            ),
1115
-            EE_Registry::instance()->CFG->core->reg_page_url()
1116
-        );
1117
-    }
1118
-
1119
-
1120
-    /**
1121
-     * Gets the URL for the checkout attendee information reg step
1122
-     * with this registration's REG_url_link added as a query parameter
1123
-     *
1124
-     * @return string
1125
-     * @throws EE_Error
1126
-     * @throws InvalidArgumentException
1127
-     * @throws InvalidDataTypeException
1128
-     * @throws InvalidInterfaceException
1129
-     * @throws ReflectionException
1130
-     */
1131
-    public function edit_attendee_information_url()
1132
-    {
1133
-        return add_query_arg(
1134
-            (array) apply_filters(
1135
-                'FHEE__EE_Registration__edit_attendee_information_url__query_args',
1136
-                array(
1137
-                    'e_reg_url_link' => $this->reg_url_link(),
1138
-                    'step'           => 'attendee_information',
1139
-                    'revisit'        => true,
1140
-                ),
1141
-                $this
1142
-            ),
1143
-            EE_Registry::instance()->CFG->core->reg_page_url()
1144
-        );
1145
-    }
1146
-
1147
-
1148
-    /**
1149
-     * Simply generates and returns the appropriate admin_url link to edit this registration
1150
-     *
1151
-     * @return string
1152
-     * @throws EE_Error
1153
-     * @throws InvalidArgumentException
1154
-     * @throws InvalidDataTypeException
1155
-     * @throws InvalidInterfaceException
1156
-     * @throws ReflectionException
1157
-     */
1158
-    public function get_admin_edit_url()
1159
-    {
1160
-        return EEH_URL::add_query_args_and_nonce(
1161
-            array(
1162
-                'page'    => 'espresso_registrations',
1163
-                'action'  => 'view_registration',
1164
-                '_REG_ID' => $this->ID(),
1165
-            ),
1166
-            admin_url('admin.php')
1167
-        );
1168
-    }
1169
-
1170
-
1171
-    /**
1172
-     * is_primary_registrant?
1173
-     *
1174
-     * @throws EE_Error
1175
-     * @throws InvalidArgumentException
1176
-     * @throws InvalidDataTypeException
1177
-     * @throws InvalidInterfaceException
1178
-     * @throws ReflectionException
1179
-     */
1180
-    public function is_primary_registrant()
1181
-    {
1182
-        return (int) $this->get('REG_count') === 1;
1183
-    }
1184
-
1185
-
1186
-    /**
1187
-     * This returns the primary registration object for this registration group (which may be this object).
1188
-     *
1189
-     * @return EE_Registration
1190
-     * @throws EE_Error
1191
-     * @throws InvalidArgumentException
1192
-     * @throws InvalidDataTypeException
1193
-     * @throws InvalidInterfaceException
1194
-     * @throws ReflectionException
1195
-     */
1196
-    public function get_primary_registration()
1197
-    {
1198
-        if ($this->is_primary_registrant()) {
1199
-            return $this;
1200
-        }
1201
-
1202
-        // k reg_count !== 1 so let's get the EE_Registration object matching this txn_id and reg_count == 1
1203
-        /** @var EE_Registration $primary_registrant */
1204
-        $primary_registrant = EEM_Registration::instance()->get_one(
1205
-            array(
1206
-                array(
1207
-                    'TXN_ID'    => $this->transaction_ID(),
1208
-                    'REG_count' => 1,
1209
-                ),
1210
-            )
1211
-        );
1212
-        return $primary_registrant;
1213
-    }
1214
-
1215
-
1216
-    /**
1217
-     * get  Attendee Number
1218
-     *
1219
-     * @throws EE_Error
1220
-     * @throws InvalidArgumentException
1221
-     * @throws InvalidDataTypeException
1222
-     * @throws InvalidInterfaceException
1223
-     * @throws ReflectionException
1224
-     */
1225
-    public function count()
1226
-    {
1227
-        return $this->get('REG_count');
1228
-    }
1229
-
1230
-
1231
-    /**
1232
-     * get Group Size
1233
-     *
1234
-     * @throws EE_Error
1235
-     * @throws InvalidArgumentException
1236
-     * @throws InvalidDataTypeException
1237
-     * @throws InvalidInterfaceException
1238
-     * @throws ReflectionException
1239
-     */
1240
-    public function group_size()
1241
-    {
1242
-        return $this->get('REG_group_size');
1243
-    }
1244
-
1245
-
1246
-    /**
1247
-     * get Registration Date
1248
-     *
1249
-     * @throws EE_Error
1250
-     * @throws InvalidArgumentException
1251
-     * @throws InvalidDataTypeException
1252
-     * @throws InvalidInterfaceException
1253
-     * @throws ReflectionException
1254
-     */
1255
-    public function date()
1256
-    {
1257
-        return $this->get('REG_date');
1258
-    }
1259
-
1260
-
1261
-    /**
1262
-     * gets a pretty date
1263
-     *
1264
-     * @param string $date_format
1265
-     * @param string $time_format
1266
-     * @return string
1267
-     * @throws EE_Error
1268
-     * @throws InvalidArgumentException
1269
-     * @throws InvalidDataTypeException
1270
-     * @throws InvalidInterfaceException
1271
-     * @throws ReflectionException
1272
-     */
1273
-    public function pretty_date($date_format = null, $time_format = null)
1274
-    {
1275
-        return $this->get_datetime('REG_date', $date_format, $time_format);
1276
-    }
1277
-
1278
-
1279
-    /**
1280
-     * final_price
1281
-     * the registration's share of the transaction total, so that the
1282
-     * sum of all the transaction's REG_final_prices equal the transaction's total
1283
-     *
1284
-     * @return float
1285
-     * @throws EE_Error
1286
-     * @throws InvalidArgumentException
1287
-     * @throws InvalidDataTypeException
1288
-     * @throws InvalidInterfaceException
1289
-     * @throws ReflectionException
1290
-     */
1291
-    public function final_price()
1292
-    {
1293
-        return $this->get('REG_final_price');
1294
-    }
1295
-
1296
-
1297
-    /**
1298
-     * pretty_final_price
1299
-     *  final price as formatted string, with correct decimal places and currency symbol
1300
-     *
1301
-     * @return string
1302
-     * @throws EE_Error
1303
-     * @throws InvalidArgumentException
1304
-     * @throws InvalidDataTypeException
1305
-     * @throws InvalidInterfaceException
1306
-     * @throws ReflectionException
1307
-     */
1308
-    public function pretty_final_price()
1309
-    {
1310
-        return $this->get_pretty('REG_final_price');
1311
-    }
1312
-
1313
-
1314
-    /**
1315
-     * get paid (yeah)
1316
-     *
1317
-     * @return float
1318
-     * @throws EE_Error
1319
-     * @throws InvalidArgumentException
1320
-     * @throws InvalidDataTypeException
1321
-     * @throws InvalidInterfaceException
1322
-     * @throws ReflectionException
1323
-     */
1324
-    public function paid()
1325
-    {
1326
-        return $this->get('REG_paid');
1327
-    }
1328
-
1329
-
1330
-    /**
1331
-     * pretty_paid
1332
-     *
1333
-     * @return float
1334
-     * @throws EE_Error
1335
-     * @throws InvalidArgumentException
1336
-     * @throws InvalidDataTypeException
1337
-     * @throws InvalidInterfaceException
1338
-     * @throws ReflectionException
1339
-     */
1340
-    public function pretty_paid()
1341
-    {
1342
-        return $this->get_pretty('REG_paid');
1343
-    }
1344
-
1345
-
1346
-    /**
1347
-     * owes_monies_and_can_pay
1348
-     * whether or not this registration has monies owing and it's' status allows payment
1349
-     *
1350
-     * @param array $requires_payment
1351
-     * @return bool
1352
-     * @throws EE_Error
1353
-     * @throws InvalidArgumentException
1354
-     * @throws InvalidDataTypeException
1355
-     * @throws InvalidInterfaceException
1356
-     * @throws ReflectionException
1357
-     */
1358
-    public function owes_monies_and_can_pay($requires_payment = array())
1359
-    {
1360
-        // these reg statuses require payment (if event is not free)
1361
-        $requires_payment = ! empty($requires_payment)
1362
-            ? $requires_payment
1363
-            : EEM_Registration::reg_statuses_that_allow_payment();
1364
-        if (
1365
-            $this->final_price() !== 0 &&
1366
-            $this->final_price() !== $this->paid() &&
1367
-            in_array($this->status_ID(), $requires_payment)
1368
-        ) {
1369
-            return true;
1370
-        }
1371
-        return false;
1372
-    }
1373
-
1374
-
1375
-    /**
1376
-     * Prints out the return value of $this->pretty_status()
1377
-     *
1378
-     * @param bool $show_icons
1379
-     * @return void
1380
-     * @throws EE_Error
1381
-     * @throws InvalidArgumentException
1382
-     * @throws InvalidDataTypeException
1383
-     * @throws InvalidInterfaceException
1384
-     * @throws ReflectionException
1385
-     */
1386
-    public function e_pretty_status($show_icons = false)
1387
-    {
1388
-        echo wp_kses($this->pretty_status($show_icons), AllowedTags::getAllowedTags());
1389
-    }
1390
-
1391
-
1392
-    /**
1393
-     * Returns a nice version of the status for displaying to customers
1394
-     *
1395
-     * @param bool $show_icons
1396
-     * @return string
1397
-     * @throws EE_Error
1398
-     * @throws InvalidArgumentException
1399
-     * @throws InvalidDataTypeException
1400
-     * @throws InvalidInterfaceException
1401
-     * @throws ReflectionException
1402
-     */
1403
-    public function pretty_status($show_icons = false)
1404
-    {
1405
-        $status = EEM_Status::instance()->localized_status(
1406
-            array($this->status_ID() => esc_html__('unknown', 'event_espresso')),
1407
-            false,
1408
-            'sentence'
1409
-        );
1410
-        $icon = '';
1411
-        switch ($this->status_ID()) {
1412
-            case EEM_Registration::status_id_approved:
1413
-                $icon = $show_icons
1414
-                    ? '<span class="dashicons dashicons-star-filled ee-icon-size-16 green-text"></span>'
1415
-                    : '';
1416
-                break;
1417
-            case EEM_Registration::status_id_pending_payment:
1418
-                $icon = $show_icons
1419
-                    ? '<span class="dashicons dashicons-star-half ee-icon-size-16 orange-text"></span>'
1420
-                    : '';
1421
-                break;
1422
-            case EEM_Registration::status_id_not_approved:
1423
-                $icon = $show_icons
1424
-                    ? '<span class="dashicons dashicons-marker ee-icon-size-16 orange-text"></span>'
1425
-                    : '';
1426
-                break;
1427
-            case EEM_Registration::status_id_cancelled:
1428
-                $icon = $show_icons
1429
-                    ? '<span class="dashicons dashicons-no ee-icon-size-16 lt-grey-text"></span>'
1430
-                    : '';
1431
-                break;
1432
-            case EEM_Registration::status_id_incomplete:
1433
-                $icon = $show_icons
1434
-                    ? '<span class="dashicons dashicons-no ee-icon-size-16 lt-orange-text"></span>'
1435
-                    : '';
1436
-                break;
1437
-            case EEM_Registration::status_id_declined:
1438
-                $icon = $show_icons
1439
-                    ? '<span class="dashicons dashicons-no ee-icon-size-16 red-text"></span>'
1440
-                    : '';
1441
-                break;
1442
-            case EEM_Registration::status_id_wait_list:
1443
-                $icon = $show_icons
1444
-                    ? '<span class="dashicons dashicons-clipboard ee-icon-size-16 purple-text"></span>'
1445
-                    : '';
1446
-                break;
1447
-        }
1448
-        return $icon . $status[ $this->status_ID() ];
1449
-    }
1450
-
1451
-
1452
-    /**
1453
-     *        get Attendee Is Going
1454
-     */
1455
-    public function att_is_going()
1456
-    {
1457
-        return $this->get('REG_att_is_going');
1458
-    }
1459
-
1460
-
1461
-    /**
1462
-     * Gets related answers
1463
-     *
1464
-     * @param array $query_params @see
1465
-     *                            https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1466
-     * @return EE_Answer[]|EE_Base_Class[]
1467
-     * @throws EE_Error
1468
-     * @throws InvalidArgumentException
1469
-     * @throws InvalidDataTypeException
1470
-     * @throws InvalidInterfaceException
1471
-     * @throws ReflectionException
1472
-     */
1473
-    public function answers($query_params = null)
1474
-    {
1475
-        return $this->get_many_related('Answer', $query_params);
1476
-    }
1477
-
1478
-
1479
-    /**
1480
-     * Gets the registration's answer value to the specified question
1481
-     * (either the question's ID or a question object)
1482
-     *
1483
-     * @param EE_Question|int $question
1484
-     * @param bool            $pretty_value
1485
-     * @return array|string if pretty_value= true, the result will always be a string
1486
-     * (because the answer might be an array of answer values, so passing pretty_value=true
1487
-     * will convert it into some kind of string)
1488
-     * @throws EE_Error
1489
-     * @throws InvalidArgumentException
1490
-     * @throws InvalidDataTypeException
1491
-     * @throws InvalidInterfaceException
1492
-     */
1493
-    public function answer_value_to_question($question, $pretty_value = true)
1494
-    {
1495
-        $question_id = EEM_Question::instance()->ensure_is_ID($question);
1496
-        return EEM_Answer::instance()->get_answer_value_to_question($this, $question_id, $pretty_value);
1497
-    }
1498
-
1499
-
1500
-    /**
1501
-     * question_groups
1502
-     * returns an array of EE_Question_Group objects for this registration
1503
-     *
1504
-     * @return EE_Question_Group[]
1505
-     * @throws EE_Error
1506
-     * @throws InvalidArgumentException
1507
-     * @throws InvalidDataTypeException
1508
-     * @throws InvalidInterfaceException
1509
-     * @throws ReflectionException
1510
-     */
1511
-    public function question_groups()
1512
-    {
1513
-        return EEM_Event::instance()->get_question_groups_for_event($this->event_ID(), $this);
1514
-    }
1515
-
1516
-
1517
-    /**
1518
-     * count_question_groups
1519
-     * returns a count of the number of EE_Question_Group objects for this registration
1520
-     *
1521
-     * @return int
1522
-     * @throws EE_Error
1523
-     * @throws EntityNotFoundException
1524
-     * @throws InvalidArgumentException
1525
-     * @throws InvalidDataTypeException
1526
-     * @throws InvalidInterfaceException
1527
-     * @throws ReflectionException
1528
-     */
1529
-    public function count_question_groups()
1530
-    {
1531
-        return EEM_Event::instance()->count_related(
1532
-            $this->event_ID(),
1533
-            'Question_Group',
1534
-            [
1535
-                [
1536
-                    'Event_Question_Group.'
1537
-                    . EEM_Event_Question_Group::instance()->fieldNameForContext($this->is_primary_registrant()) => true,
1538
-                ]
1539
-            ]
1540
-        );
1541
-    }
1542
-
1543
-
1544
-    /**
1545
-     * Returns the registration date in the 'standard' string format
1546
-     * (function may be improved in the future to allow for different formats and timezones)
1547
-     *
1548
-     * @return string
1549
-     * @throws EE_Error
1550
-     * @throws InvalidArgumentException
1551
-     * @throws InvalidDataTypeException
1552
-     * @throws InvalidInterfaceException
1553
-     * @throws ReflectionException
1554
-     */
1555
-    public function reg_date()
1556
-    {
1557
-        return $this->get_datetime('REG_date');
1558
-    }
1559
-
1560
-
1561
-    /**
1562
-     * Gets the datetime-ticket for this registration (ie, it can be used to isolate
1563
-     * the ticket this registration purchased, or the datetime they have registered
1564
-     * to attend)
1565
-     *
1566
-     * @return EE_Base_Class|EE_Datetime_Ticket
1567
-     * @throws EE_Error
1568
-     * @throws InvalidArgumentException
1569
-     * @throws InvalidDataTypeException
1570
-     * @throws InvalidInterfaceException
1571
-     * @throws ReflectionException
1572
-     */
1573
-    public function datetime_ticket()
1574
-    {
1575
-        return $this->get_first_related('Datetime_Ticket');
1576
-    }
1577
-
1578
-
1579
-    /**
1580
-     * Sets the registration's datetime_ticket.
1581
-     *
1582
-     * @param EE_Datetime_Ticket $datetime_ticket
1583
-     * @return EE_Base_Class|EE_Datetime_Ticket
1584
-     * @throws EE_Error
1585
-     * @throws InvalidArgumentException
1586
-     * @throws InvalidDataTypeException
1587
-     * @throws InvalidInterfaceException
1588
-     * @throws ReflectionException
1589
-     */
1590
-    public function set_datetime_ticket($datetime_ticket)
1591
-    {
1592
-        return $this->_add_relation_to($datetime_ticket, 'Datetime_Ticket');
1593
-    }
1594
-
1595
-
1596
-    /**
1597
-     * Gets deleted
1598
-     *
1599
-     * @return bool
1600
-     * @throws EE_Error
1601
-     * @throws InvalidArgumentException
1602
-     * @throws InvalidDataTypeException
1603
-     * @throws InvalidInterfaceException
1604
-     * @throws ReflectionException
1605
-     */
1606
-    public function deleted()
1607
-    {
1608
-        return $this->get('REG_deleted');
1609
-    }
1610
-
1611
-
1612
-    /**
1613
-     * Sets deleted
1614
-     *
1615
-     * @param boolean $deleted
1616
-     * @return void
1617
-     * @throws DomainException
1618
-     * @throws EE_Error
1619
-     * @throws EntityNotFoundException
1620
-     * @throws InvalidArgumentException
1621
-     * @throws InvalidDataTypeException
1622
-     * @throws InvalidInterfaceException
1623
-     * @throws ReflectionException
1624
-     * @throws RuntimeException
1625
-     * @throws UnexpectedEntityException
1626
-     */
1627
-    public function set_deleted($deleted)
1628
-    {
1629
-        if ($deleted) {
1630
-            $this->delete();
1631
-        } else {
1632
-            $this->restore();
1633
-        }
1634
-    }
1635
-
1636
-
1637
-    /**
1638
-     * Get the status object of this object
1639
-     *
1640
-     * @return EE_Base_Class|EE_Status
1641
-     * @throws EE_Error
1642
-     * @throws InvalidArgumentException
1643
-     * @throws InvalidDataTypeException
1644
-     * @throws InvalidInterfaceException
1645
-     * @throws ReflectionException
1646
-     */
1647
-    public function status_obj()
1648
-    {
1649
-        return $this->get_first_related('Status');
1650
-    }
1651
-
1652
-
1653
-    /**
1654
-     * Returns the number of times this registration has checked into any of the datetimes
1655
-     * its available for
1656
-     *
1657
-     * @return int
1658
-     * @throws EE_Error
1659
-     * @throws InvalidArgumentException
1660
-     * @throws InvalidDataTypeException
1661
-     * @throws InvalidInterfaceException
1662
-     * @throws ReflectionException
1663
-     */
1664
-    public function count_checkins()
1665
-    {
1666
-        return $this->get_model()->count_related($this, 'Checkin');
1667
-    }
1668
-
1669
-
1670
-    /**
1671
-     * Returns the number of current Check-ins this registration is checked into for any of the datetimes the
1672
-     * registration is for.  Note, this is ONLY checked in (does not include checkedout)
1673
-     *
1674
-     * @return int
1675
-     * @throws EE_Error
1676
-     * @throws InvalidArgumentException
1677
-     * @throws InvalidDataTypeException
1678
-     * @throws InvalidInterfaceException
1679
-     * @throws ReflectionException
1680
-     */
1681
-    public function count_checkins_not_checkedout()
1682
-    {
1683
-        return $this->get_model()->count_related($this, 'Checkin', array(array('CHK_in' => 1)));
1684
-    }
1685
-
1686
-
1687
-    /**
1688
-     * The purpose of this method is simply to check whether this registration can checkin to the given datetime.
1689
-     *
1690
-     * @param int | EE_Datetime $DTT_OR_ID      The datetime the registration is being checked against
1691
-     * @param bool              $check_approved This is used to indicate whether the caller wants can_checkin to also
1692
-     *                                          consider registration status as well as datetime access.
1693
-     * @return bool
1694
-     * @throws EE_Error
1695
-     * @throws InvalidArgumentException
1696
-     * @throws InvalidDataTypeException
1697
-     * @throws InvalidInterfaceException
1698
-     * @throws ReflectionException
1699
-     */
1700
-    public function can_checkin($DTT_OR_ID, $check_approved = true)
1701
-    {
1702
-        $DTT_ID = EEM_Datetime::instance()->ensure_is_ID($DTT_OR_ID);
1703
-        // first check registration status
1704
-        if (! $DTT_ID || ($check_approved && ! $this->is_approved())) {
1705
-            return false;
1706
-        }
1707
-        // is there a datetime ticket that matches this dtt_ID?
1708
-        if (
1709
-            ! (EEM_Datetime_Ticket::instance()->exists(
1710
-                array(
1711
-                    array(
1712
-                        'TKT_ID' => $this->get('TKT_ID'),
1713
-                        'DTT_ID' => $DTT_ID,
1714
-                    ),
1715
-                )
1716
-            ))
1717
-        ) {
1718
-            return false;
1719
-        }
1720
-
1721
-        // final check is against TKT_uses
1722
-        return $this->verify_can_checkin_against_TKT_uses($DTT_ID);
1723
-    }
1724
-
1725
-
1726
-    /**
1727
-     * This method verifies whether the user can checkin for the given datetime considering the max uses value set on
1728
-     * the ticket. To do this,  a query is done to get the count of the datetime records already checked into.  If the
1729
-     * datetime given does not have a check-in record and checking in for that datetime will exceed the allowed uses,
1730
-     * then return false.  Otherwise return true.
1731
-     *
1732
-     * @param int | EE_Datetime $DTT_OR_ID The datetime the registration is being checked against
1733
-     * @return bool true means can checkin.  false means cannot checkin.
1734
-     * @throws EE_Error
1735
-     * @throws InvalidArgumentException
1736
-     * @throws InvalidDataTypeException
1737
-     * @throws InvalidInterfaceException
1738
-     * @throws ReflectionException
1739
-     */
1740
-    public function verify_can_checkin_against_TKT_uses($DTT_OR_ID)
1741
-    {
1742
-        $DTT_ID = EEM_Datetime::instance()->ensure_is_ID($DTT_OR_ID);
1743
-
1744
-        if (! $DTT_ID) {
1745
-            return false;
1746
-        }
1747
-
1748
-        $max_uses = $this->ticket() instanceof EE_Ticket ? $this->ticket()->uses() : EE_INF;
1749
-
1750
-        // if max uses is not set or equals infinity then return true cause its not a factor for whether user can
1751
-        // check-in or not.
1752
-        if (! $max_uses || $max_uses === EE_INF) {
1753
-            return true;
1754
-        }
1755
-
1756
-        // does this datetime have a checkin record?  If so, then the dtt count has already been verified so we can just
1757
-        // go ahead and toggle.
1758
-        if (EEM_Checkin::instance()->exists(array(array('REG_ID' => $this->ID(), 'DTT_ID' => $DTT_ID)))) {
1759
-            return true;
1760
-        }
1761
-
1762
-        // made it here so the last check is whether the number of checkins per unique datetime on this registration
1763
-        // disallows further check-ins.
1764
-        $count_unique_dtt_checkins = EEM_Checkin::instance()->count(
1765
-            array(
1766
-                array(
1767
-                    'REG_ID' => $this->ID(),
1768
-                    'CHK_in' => true,
1769
-                ),
1770
-            ),
1771
-            'DTT_ID',
1772
-            true
1773
-        );
1774
-        // checkins have already reached their max number of uses
1775
-        // so registrant can NOT checkin
1776
-        if ($count_unique_dtt_checkins >= $max_uses) {
1777
-            EE_Error::add_error(
1778
-                esc_html__(
1779
-                    'Check-in denied because number of datetime uses for the ticket has been reached or exceeded.',
1780
-                    'event_espresso'
1781
-                ),
1782
-                __FILE__,
1783
-                __FUNCTION__,
1784
-                __LINE__
1785
-            );
1786
-            return false;
1787
-        }
1788
-        return true;
1789
-    }
1790
-
1791
-
1792
-    /**
1793
-     * toggle Check-in status for this registration
1794
-     * Check-ins are toggled in the following order:
1795
-     * never checked in -> checked in
1796
-     * checked in -> checked out
1797
-     * checked out -> checked in
1798
-     *
1799
-     * @param  int $DTT_ID  include specific datetime to toggle Check-in for.
1800
-     *                      If not included or null, then it is assumed latest datetime is being toggled.
1801
-     * @param bool $verify  If true then can_checkin() is used to verify whether the person
1802
-     *                      can be checked in or not.  Otherwise this forces change in checkin status.
1803
-     * @return bool|int     the chk_in status toggled to OR false if nothing got changed.
1804
-     * @throws EE_Error
1805
-     * @throws InvalidArgumentException
1806
-     * @throws InvalidDataTypeException
1807
-     * @throws InvalidInterfaceException
1808
-     * @throws ReflectionException
1809
-     */
1810
-    public function toggle_checkin_status($DTT_ID = null, $verify = false)
1811
-    {
1812
-        if (empty($DTT_ID)) {
1813
-            $datetime = $this->get_latest_related_datetime();
1814
-            $DTT_ID = $datetime instanceof EE_Datetime ? $datetime->ID() : 0;
1815
-            // verify the registration can checkin for the given DTT_ID
1816
-        } elseif (! $this->can_checkin($DTT_ID, $verify)) {
1817
-            EE_Error::add_error(
1818
-                sprintf(
1819
-                    esc_html__(
1820
-                        'The given registration (ID:%1$d) can not be checked in to the given DTT_ID (%2$d), because the registration does not have access',
1821
-                        'event_espresso'
1822
-                    ),
1823
-                    $this->ID(),
1824
-                    $DTT_ID
1825
-                ),
1826
-                __FILE__,
1827
-                __FUNCTION__,
1828
-                __LINE__
1829
-            );
1830
-            return false;
1831
-        }
1832
-        $status_paths = array(
1833
-            EE_Checkin::status_checked_never => EE_Checkin::status_checked_in,
1834
-            EE_Checkin::status_checked_in    => EE_Checkin::status_checked_out,
1835
-            EE_Checkin::status_checked_out   => EE_Checkin::status_checked_in,
1836
-        );
1837
-        // start by getting the current status so we know what status we'll be changing to.
1838
-        $cur_status = $this->check_in_status_for_datetime($DTT_ID);
1839
-        $status_to = $status_paths[ $cur_status ];
1840
-        // database only records true for checked IN or false for checked OUT
1841
-        // no record ( null ) means checked in NEVER, but we obviously don't save that
1842
-        $new_status = $status_to === EE_Checkin::status_checked_in;
1843
-        // add relation - note Check-ins are always creating new rows
1844
-        // because we are keeping track of Check-ins over time.
1845
-        // Eventually we'll probably want to show a list table
1846
-        // for the individual Check-ins so that they can be managed.
1847
-        $checkin = EE_Checkin::new_instance(
1848
-            array(
1849
-                'REG_ID' => $this->ID(),
1850
-                'DTT_ID' => $DTT_ID,
1851
-                'CHK_in' => $new_status,
1852
-            )
1853
-        );
1854
-        // if the record could not be saved then return false
1855
-        if ($checkin->save() === 0) {
1856
-            if (WP_DEBUG) {
1857
-                global $wpdb;
1858
-                $error = sprintf(
1859
-                    esc_html__(
1860
-                        'Registration check in update failed because of the following database error: %1$s%2$s',
1861
-                        'event_espresso'
1862
-                    ),
1863
-                    '<br />',
1864
-                    $wpdb->last_error
1865
-                );
1866
-            } else {
1867
-                $error = esc_html__(
1868
-                    'Registration check in update failed because of an unknown database error',
1869
-                    'event_espresso'
1870
-                );
1871
-            }
1872
-            EE_Error::add_error($error, __FILE__, __FUNCTION__, __LINE__);
1873
-            return false;
1874
-        }
1875
-        // Fire a checked_in and checkout_out action.
1876
-        $checked_status = $status_to === EE_Checkin::status_checked_in ? 'checked_in' : 'checked_out';
1877
-        do_action("AHEE__EE_Registration__toggle_checkin_status__{$checked_status}", $this, $DTT_ID);
1878
-        return $status_to;
1879
-    }
1880
-
1881
-
1882
-    /**
1883
-     * Returns the latest datetime related to this registration (via the ticket attached to the registration).
1884
-     * "Latest" is defined by the `DTT_EVT_start` column.
1885
-     *
1886
-     * @return EE_Datetime|null
1887
-     * @throws EE_Error
1888
-     * @throws InvalidArgumentException
1889
-     * @throws InvalidDataTypeException
1890
-     * @throws InvalidInterfaceException
1891
-     * @throws ReflectionException
1892
-     */
1893
-    public function get_latest_related_datetime()
1894
-    {
1895
-        return EEM_Datetime::instance()->get_one(
1896
-            array(
1897
-                array(
1898
-                    'Ticket.Registration.REG_ID' => $this->ID(),
1899
-                ),
1900
-                'order_by' => array('DTT_EVT_start' => 'DESC'),
1901
-            )
1902
-        );
1903
-    }
1904
-
1905
-
1906
-    /**
1907
-     * Returns the earliest datetime related to this registration (via the ticket attached to the registration).
1908
-     * "Earliest" is defined by the `DTT_EVT_start` column.
1909
-     *
1910
-     * @return EE_Base_Class|EE_Soft_Delete_Base_Class|NULL
1911
-     * @throws EE_Error
1912
-     * @throws InvalidArgumentException
1913
-     * @throws InvalidDataTypeException
1914
-     * @throws InvalidInterfaceException
1915
-     * @throws ReflectionException
1916
-     */
1917
-    public function get_earliest_related_datetime()
1918
-    {
1919
-        return EEM_Datetime::instance()->get_one(
1920
-            array(
1921
-                array(
1922
-                    'Ticket.Registration.REG_ID' => $this->ID(),
1923
-                ),
1924
-                'order_by' => array('DTT_EVT_start' => 'ASC'),
1925
-            )
1926
-        );
1927
-    }
1928
-
1929
-
1930
-    /**
1931
-     * This method simply returns the check-in status for this registration and the given datetime.
1932
-     * If neither the datetime nor the checkin values are provided as arguments,
1933
-     * then this will return the LATEST check-in status for the registration across all datetimes it belongs to.
1934
-     *
1935
-     * @param  int       $DTT_ID  The ID of the datetime we're checking against
1936
-     *                            (if empty we'll get the primary datetime for
1937
-     *                            this registration (via event) and use it's ID);
1938
-     * @param EE_Checkin $checkin If present, we use the given checkin object rather than the dtt_id.
1939
-     * @return int                Integer representing Check-in status.
1940
-     * @throws EE_Error
1941
-     * @throws InvalidArgumentException
1942
-     * @throws InvalidDataTypeException
1943
-     * @throws InvalidInterfaceException
1944
-     * @throws ReflectionException
1945
-     */
1946
-    public function check_in_status_for_datetime($DTT_ID = 0, $checkin = null)
1947
-    {
1948
-        $checkin_query_params = array(
1949
-            'order_by' => array('CHK_timestamp' => 'DESC'),
1950
-        );
1951
-
1952
-        if ($DTT_ID > 0) {
1953
-            $checkin_query_params[0] = array('DTT_ID' => $DTT_ID);
1954
-        }
1955
-
1956
-        // get checkin object (if exists)
1957
-        $checkin = $checkin instanceof EE_Checkin
1958
-            ? $checkin
1959
-            : $this->get_first_related('Checkin', $checkin_query_params);
1960
-        if ($checkin instanceof EE_Checkin) {
1961
-            if ($checkin->get('CHK_in')) {
1962
-                return EE_Checkin::status_checked_in; // checked in
1963
-            }
1964
-            return EE_Checkin::status_checked_out; // had checked in but is now checked out.
1965
-        }
1966
-        return EE_Checkin::status_checked_never; // never been checked in
1967
-    }
1968
-
1969
-
1970
-    /**
1971
-     * This method returns a localized message for the toggled Check-in message.
1972
-     *
1973
-     * @param  int $DTT_ID include specific datetime to get the correct Check-in message.  If not included or null,
1974
-     *                     then it is assumed Check-in for primary datetime was toggled.
1975
-     * @param bool $error  This just flags that you want an error message returned. This is put in so that the error
1976
-     *                     message can be customized with the attendee name.
1977
-     * @return string internationalized message
1978
-     * @throws EE_Error
1979
-     * @throws InvalidArgumentException
1980
-     * @throws InvalidDataTypeException
1981
-     * @throws InvalidInterfaceException
1982
-     * @throws ReflectionException
1983
-     */
1984
-    public function get_checkin_msg($DTT_ID, $error = false)
1985
-    {
1986
-        // let's get the attendee first so we can include the name of the attendee
1987
-        $attendee = $this->get_first_related('Attendee');
1988
-        if ($attendee instanceof EE_Attendee) {
1989
-            if ($error) {
1990
-                return sprintf(
1991
-                    esc_html__("%s's check-in status was not changed.", "event_espresso"),
1992
-                    $attendee->full_name()
1993
-                );
1994
-            }
1995
-            $cur_status = $this->check_in_status_for_datetime($DTT_ID);
1996
-            // what is the status message going to be?
1997
-            switch ($cur_status) {
1998
-                case EE_Checkin::status_checked_never:
1999
-                    return sprintf(
2000
-                        esc_html__('%s has been removed from Check-in records', 'event_espresso'),
2001
-                        $attendee->full_name()
2002
-                    );
2003
-                    break;
2004
-                case EE_Checkin::status_checked_in:
2005
-                    return sprintf(esc_html__('%s has been checked in', 'event_espresso'), $attendee->full_name());
2006
-                    break;
2007
-                case EE_Checkin::status_checked_out:
2008
-                    return sprintf(esc_html__('%s has been checked out', 'event_espresso'), $attendee->full_name());
2009
-                    break;
2010
-            }
2011
-        }
2012
-        return esc_html__('The check-in status could not be determined.', 'event_espresso');
2013
-    }
2014
-
2015
-
2016
-    /**
2017
-     * Returns the related EE_Transaction to this registration
2018
-     *
2019
-     * @return EE_Transaction
2020
-     * @throws EE_Error
2021
-     * @throws EntityNotFoundException
2022
-     * @throws InvalidArgumentException
2023
-     * @throws InvalidDataTypeException
2024
-     * @throws InvalidInterfaceException
2025
-     * @throws ReflectionException
2026
-     */
2027
-    public function transaction()
2028
-    {
2029
-        $transaction = $this->get_first_related('Transaction');
2030
-        if (! $transaction instanceof \EE_Transaction) {
2031
-            throw new EntityNotFoundException('Transaction ID', $this->transaction_ID());
2032
-        }
2033
-        return $transaction;
2034
-    }
2035
-
2036
-
2037
-    /**
2038
-     * get Registration Code
2039
-     *
2040
-     * @return mixed
2041
-     * @throws EE_Error
2042
-     * @throws InvalidArgumentException
2043
-     * @throws InvalidDataTypeException
2044
-     * @throws InvalidInterfaceException
2045
-     * @throws ReflectionException
2046
-     */
2047
-    public function reg_code()
2048
-    {
2049
-        return $this->get('REG_code');
2050
-    }
2051
-
2052
-
2053
-    /**
2054
-     * @return mixed
2055
-     * @throws EE_Error
2056
-     * @throws InvalidArgumentException
2057
-     * @throws InvalidDataTypeException
2058
-     * @throws InvalidInterfaceException
2059
-     * @throws ReflectionException
2060
-     */
2061
-    public function transaction_ID()
2062
-    {
2063
-        return $this->get('TXN_ID');
2064
-    }
2065
-
2066
-
2067
-    /**
2068
-     * @return int
2069
-     * @throws EE_Error
2070
-     * @throws InvalidArgumentException
2071
-     * @throws InvalidDataTypeException
2072
-     * @throws InvalidInterfaceException
2073
-     * @throws ReflectionException
2074
-     */
2075
-    public function ticket_ID()
2076
-    {
2077
-        return $this->get('TKT_ID');
2078
-    }
2079
-
2080
-
2081
-    /**
2082
-     * Set Registration Code
2083
-     *
2084
-     * @param    string  $REG_code Registration Code
2085
-     * @param    boolean $use_default
2086
-     * @throws EE_Error
2087
-     * @throws InvalidArgumentException
2088
-     * @throws InvalidDataTypeException
2089
-     * @throws InvalidInterfaceException
2090
-     * @throws ReflectionException
2091
-     */
2092
-    public function set_reg_code($REG_code, $use_default = false)
2093
-    {
2094
-        if (empty($REG_code)) {
2095
-            EE_Error::add_error(
2096
-                esc_html__('REG_code can not be empty.', 'event_espresso'),
2097
-                __FILE__,
2098
-                __FUNCTION__,
2099
-                __LINE__
2100
-            );
2101
-            return;
2102
-        }
2103
-        if (! $this->reg_code()) {
2104
-            parent::set('REG_code', $REG_code, $use_default);
2105
-        } else {
2106
-            EE_Error::doing_it_wrong(
2107
-                __CLASS__ . '::' . __FUNCTION__,
2108
-                esc_html__('Can not change a registration REG_code once it has been set.', 'event_espresso'),
2109
-                '4.6.0'
2110
-            );
2111
-        }
2112
-    }
2113
-
2114
-
2115
-    /**
2116
-     * Returns all other registrations in the same group as this registrant who have the same ticket option.
2117
-     * Note, if you want to just get all registrations in the same transaction (group), use:
2118
-     *    $registration->transaction()->registrations();
2119
-     *
2120
-     * @since 4.5.0
2121
-     * @return EE_Registration[] or empty array if this isn't a group registration.
2122
-     * @throws EE_Error
2123
-     * @throws InvalidArgumentException
2124
-     * @throws InvalidDataTypeException
2125
-     * @throws InvalidInterfaceException
2126
-     * @throws ReflectionException
2127
-     */
2128
-    public function get_all_other_registrations_in_group()
2129
-    {
2130
-        if ($this->group_size() < 2) {
2131
-            return array();
2132
-        }
2133
-
2134
-        $query[0] = array(
2135
-            'TXN_ID' => $this->transaction_ID(),
2136
-            'REG_ID' => array('!=', $this->ID()),
2137
-            'TKT_ID' => $this->ticket_ID(),
2138
-        );
2139
-        /** @var EE_Registration[] $registrations */
2140
-        $registrations = $this->get_model()->get_all($query);
2141
-        return $registrations;
2142
-    }
2143
-
2144
-
2145
-    /**
2146
-     * Return the link to the admin details for the object.
2147
-     *
2148
-     * @return string
2149
-     * @throws EE_Error
2150
-     * @throws InvalidArgumentException
2151
-     * @throws InvalidDataTypeException
2152
-     * @throws InvalidInterfaceException
2153
-     * @throws ReflectionException
2154
-     */
2155
-    public function get_admin_details_link()
2156
-    {
2157
-        EE_Registry::instance()->load_helper('URL');
2158
-        return EEH_URL::add_query_args_and_nonce(
2159
-            array(
2160
-                'page'    => 'espresso_registrations',
2161
-                'action'  => 'view_registration',
2162
-                '_REG_ID' => $this->ID(),
2163
-            ),
2164
-            admin_url('admin.php')
2165
-        );
2166
-    }
2167
-
2168
-
2169
-    /**
2170
-     * Returns the link to the editor for the object.  Sometimes this is the same as the details.
2171
-     *
2172
-     * @return string
2173
-     * @throws EE_Error
2174
-     * @throws InvalidArgumentException
2175
-     * @throws InvalidDataTypeException
2176
-     * @throws InvalidInterfaceException
2177
-     * @throws ReflectionException
2178
-     */
2179
-    public function get_admin_edit_link()
2180
-    {
2181
-        return $this->get_admin_details_link();
2182
-    }
2183
-
2184
-
2185
-    /**
2186
-     * Returns the link to a settings page for the object.
2187
-     *
2188
-     * @return string
2189
-     * @throws EE_Error
2190
-     * @throws InvalidArgumentException
2191
-     * @throws InvalidDataTypeException
2192
-     * @throws InvalidInterfaceException
2193
-     * @throws ReflectionException
2194
-     */
2195
-    public function get_admin_settings_link()
2196
-    {
2197
-        return $this->get_admin_details_link();
2198
-    }
2199
-
2200
-
2201
-    /**
2202
-     * Returns the link to the "overview" for the object (typically the "list table" view).
2203
-     *
2204
-     * @return string
2205
-     * @throws EE_Error
2206
-     * @throws InvalidArgumentException
2207
-     * @throws InvalidDataTypeException
2208
-     * @throws InvalidInterfaceException
2209
-     * @throws ReflectionException
2210
-     */
2211
-    public function get_admin_overview_link()
2212
-    {
2213
-        EE_Registry::instance()->load_helper('URL');
2214
-        return EEH_URL::add_query_args_and_nonce(
2215
-            array(
2216
-                'page' => 'espresso_registrations',
2217
-            ),
2218
-            admin_url('admin.php')
2219
-        );
2220
-    }
2221
-
2222
-
2223
-    /**
2224
-     * @param array $query_params
2225
-     * @return EE_Base_Class[]|EE_Registration[]
2226
-     * @throws EE_Error
2227
-     * @throws InvalidArgumentException
2228
-     * @throws InvalidDataTypeException
2229
-     * @throws InvalidInterfaceException
2230
-     * @throws ReflectionException
2231
-     */
2232
-    public function payments($query_params = array())
2233
-    {
2234
-        return $this->get_many_related('Payment', $query_params);
2235
-    }
2236
-
2237
-
2238
-    /**
2239
-     * @param array $query_params
2240
-     * @return EE_Base_Class[]|EE_Registration_Payment[]
2241
-     * @throws EE_Error
2242
-     * @throws InvalidArgumentException
2243
-     * @throws InvalidDataTypeException
2244
-     * @throws InvalidInterfaceException
2245
-     * @throws ReflectionException
2246
-     */
2247
-    public function registration_payments($query_params = array())
2248
-    {
2249
-        return $this->get_many_related('Registration_Payment', $query_params);
2250
-    }
2251
-
2252
-
2253
-    /**
2254
-     * This grabs the payment method corresponding to the last payment made for the amount owing on the registration.
2255
-     * Note: if there are no payments on the registration there will be no payment method returned.
2256
-     *
2257
-     * @return EE_Payment|EE_Payment_Method|null
2258
-     * @throws EE_Error
2259
-     * @throws InvalidArgumentException
2260
-     * @throws InvalidDataTypeException
2261
-     * @throws InvalidInterfaceException
2262
-     */
2263
-    public function payment_method()
2264
-    {
2265
-        return EEM_Payment_Method::instance()->get_last_used_for_registration($this);
2266
-    }
2267
-
2268
-
2269
-    /**
2270
-     * @return \EE_Line_Item
2271
-     * @throws EE_Error
2272
-     * @throws EntityNotFoundException
2273
-     * @throws InvalidArgumentException
2274
-     * @throws InvalidDataTypeException
2275
-     * @throws InvalidInterfaceException
2276
-     * @throws ReflectionException
2277
-     */
2278
-    public function ticket_line_item()
2279
-    {
2280
-        $ticket = $this->ticket();
2281
-        $transaction = $this->transaction();
2282
-        $line_item = null;
2283
-        $ticket_line_items = \EEH_Line_Item::get_line_items_by_object_type_and_IDs(
2284
-            $transaction->total_line_item(),
2285
-            'Ticket',
2286
-            array($ticket->ID())
2287
-        );
2288
-        foreach ($ticket_line_items as $ticket_line_item) {
2289
-            if (
2290
-                $ticket_line_item instanceof \EE_Line_Item
2291
-                && $ticket_line_item->OBJ_type() === 'Ticket'
2292
-                && $ticket_line_item->OBJ_ID() === $ticket->ID()
2293
-            ) {
2294
-                $line_item = $ticket_line_item;
2295
-                break;
2296
-            }
2297
-        }
2298
-        if (! ($line_item instanceof \EE_Line_Item && $line_item->OBJ_type() === 'Ticket')) {
2299
-            throw new EntityNotFoundException('Line Item Ticket ID', $ticket->ID());
2300
-        }
2301
-        return $line_item;
2302
-    }
2303
-
2304
-
2305
-    /**
2306
-     * Soft Deletes this model object.
2307
-     *
2308
-     * @param string $source function name that called this method
2309
-     * @return boolean | int
2310
-     * @throws DomainException
2311
-     * @throws EE_Error
2312
-     * @throws EntityNotFoundException
2313
-     * @throws InvalidArgumentException
2314
-     * @throws InvalidDataTypeException
2315
-     * @throws InvalidInterfaceException
2316
-     * @throws ReflectionException
2317
-     * @throws RuntimeException
2318
-     * @throws UnexpectedEntityException
2319
-     */
2320
-    public function delete()
2321
-    {
2322
-        if ($this->update_extra_meta(EE_Registration::PRE_TRASH_REG_STATUS_KEY, $this->status_ID()) === true) {
2323
-            $this->set_status(EEM_Registration::status_id_cancelled);
2324
-        }
2325
-        return parent::delete();
2326
-    }
2327
-
2328
-
2329
-    /**
2330
-     * Restores whatever the previous status was on a registration before it was trashed (if possible)
2331
-     *
2332
-     * @param string $source function name that called this method
2333
-     * @return bool|int
2334
-     * @throws DomainException
2335
-     * @throws EE_Error
2336
-     * @throws EntityNotFoundException
2337
-     * @throws InvalidArgumentException
2338
-     * @throws InvalidDataTypeException
2339
-     * @throws InvalidInterfaceException
2340
-     * @throws ReflectionException
2341
-     * @throws RuntimeException
2342
-     * @throws UnexpectedEntityException
2343
-     */
2344
-    public function restore()
2345
-    {
2346
-        $previous_status = $this->get_extra_meta(
2347
-            EE_Registration::PRE_TRASH_REG_STATUS_KEY,
2348
-            true,
2349
-            EEM_Registration::status_id_cancelled
2350
-        );
2351
-        if ($previous_status) {
2352
-            $this->delete_extra_meta(EE_Registration::PRE_TRASH_REG_STATUS_KEY);
2353
-            $this->set_status($previous_status);
2354
-        }
2355
-        return parent::restore();
2356
-    }
2357
-
2358
-
2359
-    /**
2360
-     * possibly toggle Registration status based on comparison of REG_paid vs REG_final_price
2361
-     *
2362
-     * @param  boolean $trigger_set_status_logic EE_Registration::set_status() can trigger additional logic
2363
-     *                                           depending on whether the reg status changes to or from "Approved"
2364
-     * @return boolean whether the Registration status was updated
2365
-     * @throws DomainException
2366
-     * @throws EE_Error
2367
-     * @throws EntityNotFoundException
2368
-     * @throws InvalidArgumentException
2369
-     * @throws InvalidDataTypeException
2370
-     * @throws InvalidInterfaceException
2371
-     * @throws ReflectionException
2372
-     * @throws RuntimeException
2373
-     * @throws UnexpectedEntityException
2374
-     */
2375
-    public function updateStatusBasedOnTotalPaid($trigger_set_status_logic = true)
2376
-    {
2377
-        $paid = $this->paid();
2378
-        $price = $this->final_price();
2379
-        switch (true) {
2380
-            // overpaid or paid
2381
-            case EEH_Money::compare_floats($paid, $price, '>'):
2382
-            case EEH_Money::compare_floats($paid, $price):
2383
-                $new_status = EEM_Registration::status_id_approved;
2384
-                break;
2385
-            //  underpaid
2386
-            case EEH_Money::compare_floats($paid, $price, '<'):
2387
-                $new_status = EEM_Registration::status_id_pending_payment;
2388
-                break;
2389
-            // uhhh Houston...
2390
-            default:
2391
-                throw new RuntimeException(
2392
-                    esc_html__('The total paid calculation for this registration is inaccurate.', 'event_espresso')
2393
-                );
2394
-        }
2395
-        if ($new_status !== $this->status_ID()) {
2396
-            if ($trigger_set_status_logic) {
2397
-                return $this->set_status($new_status);
2398
-            }
2399
-            parent::set('STS_ID', $new_status);
2400
-            return true;
2401
-        }
2402
-        return false;
2403
-    }
2404
-
2405
-
2406
-    /*************************** DEPRECATED ***************************/
2407
-
2408
-
2409
-    /**
2410
-     * @deprecated
2411
-     * @since     4.7.0
2412
-     */
2413
-    public function price_paid()
2414
-    {
2415
-        EE_Error::doing_it_wrong(
2416
-            'EE_Registration::price_paid()',
2417
-            esc_html__(
2418
-                'This method is deprecated, please use EE_Registration::final_price() instead.',
2419
-                'event_espresso'
2420
-            ),
2421
-            '4.7.0'
2422
-        );
2423
-        return $this->final_price();
2424
-    }
2425
-
2426
-
2427
-    /**
2428
-     * @deprecated
2429
-     * @since     4.7.0
2430
-     * @param    float $REG_final_price
2431
-     * @throws EE_Error
2432
-     * @throws EntityNotFoundException
2433
-     * @throws InvalidArgumentException
2434
-     * @throws InvalidDataTypeException
2435
-     * @throws InvalidInterfaceException
2436
-     * @throws ReflectionException
2437
-     * @throws RuntimeException
2438
-     * @throws DomainException
2439
-     */
2440
-    public function set_price_paid($REG_final_price = 0.00)
2441
-    {
2442
-        EE_Error::doing_it_wrong(
2443
-            'EE_Registration::set_price_paid()',
2444
-            esc_html__(
2445
-                'This method is deprecated, please use EE_Registration::set_final_price() instead.',
2446
-                'event_espresso'
2447
-            ),
2448
-            '4.7.0'
2449
-        );
2450
-        $this->set_final_price($REG_final_price);
2451
-    }
2452
-
2453
-
2454
-    /**
2455
-     * @deprecated
2456
-     * @since 4.7.0
2457
-     * @return string
2458
-     * @throws EE_Error
2459
-     * @throws InvalidArgumentException
2460
-     * @throws InvalidDataTypeException
2461
-     * @throws InvalidInterfaceException
2462
-     * @throws ReflectionException
2463
-     */
2464
-    public function pretty_price_paid()
2465
-    {
2466
-        EE_Error::doing_it_wrong(
2467
-            'EE_Registration::pretty_price_paid()',
2468
-            esc_html__(
2469
-                'This method is deprecated, please use EE_Registration::pretty_final_price() instead.',
2470
-                'event_espresso'
2471
-            ),
2472
-            '4.7.0'
2473
-        );
2474
-        return $this->pretty_final_price();
2475
-    }
2476
-
2477
-
2478
-    /**
2479
-     * Gets the primary datetime related to this registration via the related Event to this registration
2480
-     *
2481
-     * @deprecated 4.9.17
2482
-     * @return EE_Datetime
2483
-     * @throws EE_Error
2484
-     * @throws EntityNotFoundException
2485
-     * @throws InvalidArgumentException
2486
-     * @throws InvalidDataTypeException
2487
-     * @throws InvalidInterfaceException
2488
-     * @throws ReflectionException
2489
-     */
2490
-    public function get_related_primary_datetime()
2491
-    {
2492
-        EE_Error::doing_it_wrong(
2493
-            __METHOD__,
2494
-            esc_html__(
2495
-                'Use EE_Registration::get_latest_related_datetime() or EE_Registration::get_earliest_related_datetime()',
2496
-                'event_espresso'
2497
-            ),
2498
-            '4.9.17',
2499
-            '5.0.0'
2500
-        );
2501
-        return $this->event()->primary_datetime();
2502
-    }
2503
-
2504
-    /**
2505
-     * Returns the contact's name (or "Unknown" if there is no contact.)
2506
-     * @since 4.10.12.p
2507
-     * @return string
2508
-     * @throws EE_Error
2509
-     * @throws InvalidArgumentException
2510
-     * @throws InvalidDataTypeException
2511
-     * @throws InvalidInterfaceException
2512
-     * @throws ReflectionException
2513
-     */
2514
-    public function name()
2515
-    {
2516
-        return $this->attendeeName();
2517
-    }
19
+	/**
20
+	 * Used to reference when a registration has never been checked in.
21
+	 *
22
+	 * @deprecated use \EE_Checkin::status_checked_never instead
23
+	 * @type int
24
+	 */
25
+	const checkin_status_never = 2;
26
+
27
+	/**
28
+	 * Used to reference when a registration has been checked in.
29
+	 *
30
+	 * @deprecated use \EE_Checkin::status_checked_in instead
31
+	 * @type int
32
+	 */
33
+	const checkin_status_in = 1;
34
+
35
+	/**
36
+	 * Used to reference when a registration has been checked out.
37
+	 *
38
+	 * @deprecated use \EE_Checkin::status_checked_out instead
39
+	 * @type int
40
+	 */
41
+	const checkin_status_out = 0;
42
+
43
+	/**
44
+	 * extra meta key for tracking reg status os trashed registrations
45
+	 *
46
+	 * @type string
47
+	 */
48
+	const PRE_TRASH_REG_STATUS_KEY = 'pre_trash_registration_status';
49
+
50
+	/**
51
+	 * extra meta key for tracking if registration has reserved ticket
52
+	 *
53
+	 * @type string
54
+	 */
55
+	const HAS_RESERVED_TICKET_KEY = 'has_reserved_ticket';
56
+
57
+
58
+	/**
59
+	 * @param array  $props_n_values          incoming values
60
+	 * @param string $timezone                incoming timezone (if not set the timezone set for the website will be
61
+	 *                                        used.)
62
+	 * @param array  $date_formats            incoming date_formats in an array where the first value is the
63
+	 *                                        date_format and the second value is the time format
64
+	 * @return EE_Registration
65
+	 * @throws EE_Error
66
+	 * @throws InvalidArgumentException
67
+	 * @throws InvalidDataTypeException
68
+	 * @throws InvalidInterfaceException
69
+	 * @throws ReflectionException
70
+	 */
71
+	public static function new_instance($props_n_values = array(), $timezone = null, $date_formats = array())
72
+	{
73
+		$has_object = parent::_check_for_object($props_n_values, __CLASS__, $timezone, $date_formats);
74
+		return $has_object ? $has_object : new self($props_n_values, false, $timezone, $date_formats);
75
+	}
76
+
77
+
78
+	/**
79
+	 * @param array  $props_n_values  incoming values from the database
80
+	 * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
81
+	 *                                the website will be used.
82
+	 * @return EE_Registration
83
+	 * @throws EE_Error
84
+	 * @throws InvalidArgumentException
85
+	 * @throws InvalidDataTypeException
86
+	 * @throws InvalidInterfaceException
87
+	 * @throws ReflectionException
88
+	 */
89
+	public static function new_instance_from_db($props_n_values = array(), $timezone = null)
90
+	{
91
+		return new self($props_n_values, true, $timezone);
92
+	}
93
+
94
+
95
+	/**
96
+	 *        Set Event ID
97
+	 *
98
+	 * @param        int $EVT_ID Event ID
99
+	 * @throws DomainException
100
+	 * @throws EE_Error
101
+	 * @throws EntityNotFoundException
102
+	 * @throws InvalidArgumentException
103
+	 * @throws InvalidDataTypeException
104
+	 * @throws InvalidInterfaceException
105
+	 * @throws ReflectionException
106
+	 * @throws RuntimeException
107
+	 * @throws UnexpectedEntityException
108
+	 */
109
+	public function set_event($EVT_ID = 0)
110
+	{
111
+		$this->set('EVT_ID', $EVT_ID);
112
+	}
113
+
114
+
115
+	/**
116
+	 * Overrides parent set() method so that all calls to set( 'REG_code', $REG_code ) OR set( 'STS_ID', $STS_ID ) can
117
+	 * be routed to internal methods
118
+	 *
119
+	 * @param string $field_name
120
+	 * @param mixed  $field_value
121
+	 * @param bool   $use_default
122
+	 * @throws DomainException
123
+	 * @throws EE_Error
124
+	 * @throws EntityNotFoundException
125
+	 * @throws InvalidArgumentException
126
+	 * @throws InvalidDataTypeException
127
+	 * @throws InvalidInterfaceException
128
+	 * @throws ReflectionException
129
+	 * @throws RuntimeException
130
+	 * @throws UnexpectedEntityException
131
+	 */
132
+	public function set($field_name, $field_value, $use_default = false)
133
+	{
134
+		switch ($field_name) {
135
+			case 'REG_code':
136
+				if (! empty($field_value) && $this->reg_code() === null) {
137
+					$this->set_reg_code($field_value, $use_default);
138
+				}
139
+				break;
140
+			case 'STS_ID':
141
+				$this->set_status($field_value, $use_default);
142
+				break;
143
+			default:
144
+				parent::set($field_name, $field_value, $use_default);
145
+		}
146
+	}
147
+
148
+
149
+	/**
150
+	 * Set Status ID
151
+	 * updates the registration status and ALSO...
152
+	 * calls reserve_registration_space() if the reg status changes TO approved from any other reg status
153
+	 * calls release_registration_space() if the reg status changes FROM approved to any other reg status
154
+	 *
155
+	 * @param string                $new_STS_ID
156
+	 * @param boolean               $use_default
157
+	 * @param ContextInterface|null $context
158
+	 * @return bool
159
+	 * @throws DomainException
160
+	 * @throws EE_Error
161
+	 * @throws EntityNotFoundException
162
+	 * @throws InvalidArgumentException
163
+	 * @throws InvalidDataTypeException
164
+	 * @throws InvalidInterfaceException
165
+	 * @throws ReflectionException
166
+	 * @throws RuntimeException
167
+	 * @throws UnexpectedEntityException
168
+	 */
169
+	public function set_status($new_STS_ID = null, $use_default = false, ContextInterface $context = null)
170
+	{
171
+		// get current REG_Status
172
+		$old_STS_ID = $this->status_ID();
173
+		// if status has changed
174
+		if (
175
+			$old_STS_ID !== $new_STS_ID // and that status has actually changed
176
+			&& ! empty($old_STS_ID) // and that old status is actually set
177
+			&& ! empty($new_STS_ID) // as well as the new status
178
+			&& $this->ID() // ensure registration is in the db
179
+		) {
180
+			// update internal status first
181
+			parent::set('STS_ID', $new_STS_ID, $use_default);
182
+			// THEN handle other changes that occur when reg status changes
183
+			// TO approved
184
+			if ($new_STS_ID === EEM_Registration::status_id_approved) {
185
+				// reserve a space by incrementing ticket and datetime sold values
186
+				$this->reserveRegistrationSpace();
187
+				do_action('AHEE__EE_Registration__set_status__to_approved', $this, $old_STS_ID, $new_STS_ID, $context);
188
+				// OR FROM  approved
189
+			} elseif ($old_STS_ID === EEM_Registration::status_id_approved) {
190
+				// release a space by decrementing ticket and datetime sold values
191
+				$this->releaseRegistrationSpace();
192
+				do_action(
193
+					'AHEE__EE_Registration__set_status__from_approved',
194
+					$this,
195
+					$old_STS_ID,
196
+					$new_STS_ID,
197
+					$context
198
+				);
199
+			}
200
+			// update status
201
+			parent::set('STS_ID', $new_STS_ID, $use_default);
202
+			$this->updateIfCanceledOrReinstated($new_STS_ID, $old_STS_ID, $context);
203
+			if ($this->statusChangeUpdatesTransaction($context)) {
204
+				$this->updateTransactionAfterStatusChange();
205
+			}
206
+			do_action('AHEE__EE_Registration__set_status__after_update', $this, $old_STS_ID, $new_STS_ID, $context);
207
+			return true;
208
+		}
209
+		// even though the old value matches the new value, it's still good to
210
+		// allow the parent set method to have a say
211
+		parent::set('STS_ID', $new_STS_ID, $use_default);
212
+		return true;
213
+	}
214
+
215
+
216
+	/**
217
+	 * update REGs and TXN when cancelled or declined registrations involved
218
+	 *
219
+	 * @param string                $new_STS_ID
220
+	 * @param string                $old_STS_ID
221
+	 * @param ContextInterface|null $context
222
+	 * @throws EE_Error
223
+	 * @throws InvalidArgumentException
224
+	 * @throws InvalidDataTypeException
225
+	 * @throws InvalidInterfaceException
226
+	 * @throws ReflectionException
227
+	 * @throws RuntimeException
228
+	 */
229
+	private function updateIfCanceledOrReinstated($new_STS_ID, $old_STS_ID, ContextInterface $context = null)
230
+	{
231
+		// these reg statuses should not be considered in any calculations involving monies owing
232
+		$closed_reg_statuses = EEM_Registration::closed_reg_statuses();
233
+		// true if registration has been cancelled or declined
234
+		$this->updateIfCanceled(
235
+			$closed_reg_statuses,
236
+			$new_STS_ID,
237
+			$old_STS_ID,
238
+			$context
239
+		);
240
+		$this->updateIfReinstated(
241
+			$closed_reg_statuses,
242
+			$new_STS_ID,
243
+			$old_STS_ID,
244
+			$context
245
+		);
246
+	}
247
+
248
+
249
+	/**
250
+	 * update REGs and TXN when cancelled or declined registrations involved
251
+	 *
252
+	 * @param array                 $closed_reg_statuses
253
+	 * @param string                $new_STS_ID
254
+	 * @param string                $old_STS_ID
255
+	 * @param ContextInterface|null $context
256
+	 * @throws EE_Error
257
+	 * @throws InvalidArgumentException
258
+	 * @throws InvalidDataTypeException
259
+	 * @throws InvalidInterfaceException
260
+	 * @throws ReflectionException
261
+	 * @throws RuntimeException
262
+	 */
263
+	private function updateIfCanceled(
264
+		array $closed_reg_statuses,
265
+		$new_STS_ID,
266
+		$old_STS_ID,
267
+		ContextInterface $context = null
268
+	) {
269
+		// true if registration has been cancelled or declined
270
+		if (
271
+			in_array($new_STS_ID, $closed_reg_statuses, true)
272
+			&& ! in_array($old_STS_ID, $closed_reg_statuses, true)
273
+		) {
274
+			/** @type EE_Registration_Processor $registration_processor */
275
+			$registration_processor = EE_Registry::instance()->load_class('Registration_Processor');
276
+			/** @type EE_Transaction_Processor $transaction_processor */
277
+			$transaction_processor = EE_Registry::instance()->load_class('Transaction_Processor');
278
+			// cancelled or declined registration
279
+			$registration_processor->update_registration_after_being_canceled_or_declined(
280
+				$this,
281
+				$closed_reg_statuses
282
+			);
283
+			$transaction_processor->update_transaction_after_canceled_or_declined_registration(
284
+				$this,
285
+				$closed_reg_statuses,
286
+				false
287
+			);
288
+			do_action(
289
+				'AHEE__EE_Registration__set_status__canceled_or_declined',
290
+				$this,
291
+				$old_STS_ID,
292
+				$new_STS_ID,
293
+				$context
294
+			);
295
+			return;
296
+		}
297
+	}
298
+
299
+
300
+	/**
301
+	 * update REGs and TXN when cancelled or declined registrations involved
302
+	 *
303
+	 * @param array                 $closed_reg_statuses
304
+	 * @param string                $new_STS_ID
305
+	 * @param string                $old_STS_ID
306
+	 * @param ContextInterface|null $context
307
+	 * @throws EE_Error
308
+	 * @throws InvalidArgumentException
309
+	 * @throws InvalidDataTypeException
310
+	 * @throws InvalidInterfaceException
311
+	 * @throws ReflectionException
312
+	 * @throws RuntimeException
313
+	 */
314
+	private function updateIfReinstated(
315
+		array $closed_reg_statuses,
316
+		$new_STS_ID,
317
+		$old_STS_ID,
318
+		ContextInterface $context = null
319
+	) {
320
+		// true if reinstating cancelled or declined registration
321
+		if (
322
+			in_array($old_STS_ID, $closed_reg_statuses, true)
323
+			&& ! in_array($new_STS_ID, $closed_reg_statuses, true)
324
+		) {
325
+			/** @type EE_Registration_Processor $registration_processor */
326
+			$registration_processor = EE_Registry::instance()->load_class('Registration_Processor');
327
+			/** @type EE_Transaction_Processor $transaction_processor */
328
+			$transaction_processor = EE_Registry::instance()->load_class('Transaction_Processor');
329
+			// reinstating cancelled or declined registration
330
+			$registration_processor->update_canceled_or_declined_registration_after_being_reinstated(
331
+				$this,
332
+				$closed_reg_statuses
333
+			);
334
+			$transaction_processor->update_transaction_after_reinstating_canceled_registration(
335
+				$this,
336
+				$closed_reg_statuses,
337
+				false
338
+			);
339
+			do_action(
340
+				'AHEE__EE_Registration__set_status__after_reinstated',
341
+				$this,
342
+				$old_STS_ID,
343
+				$new_STS_ID,
344
+				$context
345
+			);
346
+		}
347
+	}
348
+
349
+
350
+	/**
351
+	 * @param ContextInterface|null $context
352
+	 * @return bool
353
+	 */
354
+	private function statusChangeUpdatesTransaction(ContextInterface $context = null)
355
+	{
356
+		$contexts_that_do_not_update_transaction = (array) apply_filters(
357
+			'AHEE__EE_Registration__statusChangeUpdatesTransaction__contexts_that_do_not_update_transaction',
358
+			array('spco_reg_step_attendee_information_process_registrations'),
359
+			$context,
360
+			$this
361
+		);
362
+		return ! (
363
+			$context instanceof ContextInterface
364
+			&& in_array($context->slug(), $contexts_that_do_not_update_transaction, true)
365
+		);
366
+	}
367
+
368
+
369
+	/**
370
+	 * @throws EE_Error
371
+	 * @throws EntityNotFoundException
372
+	 * @throws InvalidArgumentException
373
+	 * @throws InvalidDataTypeException
374
+	 * @throws InvalidInterfaceException
375
+	 * @throws ReflectionException
376
+	 * @throws RuntimeException
377
+	 */
378
+	private function updateTransactionAfterStatusChange()
379
+	{
380
+		/** @type EE_Transaction_Payments $transaction_payments */
381
+		$transaction_payments = EE_Registry::instance()->load_class('Transaction_Payments');
382
+		$transaction_payments->recalculate_transaction_total($this->transaction(), false);
383
+		$this->transaction()->update_status_based_on_total_paid();
384
+	}
385
+
386
+
387
+	/**
388
+	 * get Status ID
389
+	 *
390
+	 * @throws EE_Error
391
+	 * @throws InvalidArgumentException
392
+	 * @throws InvalidDataTypeException
393
+	 * @throws InvalidInterfaceException
394
+	 * @throws ReflectionException
395
+	 */
396
+	public function status_ID()
397
+	{
398
+		return $this->get('STS_ID');
399
+	}
400
+
401
+
402
+	/**
403
+	 * Gets the ticket this registration is for
404
+	 *
405
+	 * @param boolean $include_archived whether to include archived tickets or not.
406
+	 * @return EE_Ticket|EE_Base_Class
407
+	 * @throws EE_Error
408
+	 * @throws InvalidArgumentException
409
+	 * @throws InvalidDataTypeException
410
+	 * @throws InvalidInterfaceException
411
+	 * @throws ReflectionException
412
+	 */
413
+	public function ticket($include_archived = true)
414
+	{
415
+		$query_params = array();
416
+		if ($include_archived) {
417
+			$query_params['default_where_conditions'] = 'none';
418
+		}
419
+		return $this->get_first_related('Ticket', $query_params);
420
+	}
421
+
422
+
423
+	/**
424
+	 * Gets the event this registration is for
425
+	 *
426
+	 * @return EE_Event
427
+	 * @throws EE_Error
428
+	 * @throws EntityNotFoundException
429
+	 * @throws InvalidArgumentException
430
+	 * @throws InvalidDataTypeException
431
+	 * @throws InvalidInterfaceException
432
+	 * @throws ReflectionException
433
+	 */
434
+	public function event()
435
+	{
436
+		$event = $this->get_first_related('Event');
437
+		if (! $event instanceof \EE_Event) {
438
+			throw new EntityNotFoundException('Event ID', $this->event_ID());
439
+		}
440
+		return $event;
441
+	}
442
+
443
+
444
+	/**
445
+	 * Gets the "author" of the registration.  Note that for the purposes of registrations, the author will correspond
446
+	 * with the author of the event this registration is for.
447
+	 *
448
+	 * @since 4.5.0
449
+	 * @return int
450
+	 * @throws EE_Error
451
+	 * @throws EntityNotFoundException
452
+	 * @throws InvalidArgumentException
453
+	 * @throws InvalidDataTypeException
454
+	 * @throws InvalidInterfaceException
455
+	 * @throws ReflectionException
456
+	 */
457
+	public function wp_user()
458
+	{
459
+		$event = $this->event();
460
+		if ($event instanceof EE_Event) {
461
+			return $event->wp_user();
462
+		}
463
+		return 0;
464
+	}
465
+
466
+
467
+	/**
468
+	 * increments this registration's related ticket sold and corresponding datetime sold values
469
+	 *
470
+	 * @return void
471
+	 * @throws DomainException
472
+	 * @throws EE_Error
473
+	 * @throws EntityNotFoundException
474
+	 * @throws InvalidArgumentException
475
+	 * @throws InvalidDataTypeException
476
+	 * @throws InvalidInterfaceException
477
+	 * @throws ReflectionException
478
+	 * @throws UnexpectedEntityException
479
+	 */
480
+	private function reserveRegistrationSpace()
481
+	{
482
+		// reserved ticket and datetime counts will be decremented as sold counts are incremented
483
+		// so stop tracking that this reg has a ticket reserved
484
+		$this->release_reserved_ticket(false, "REG: {$this->ID()} (ln:" . __LINE__ . ')');
485
+		$ticket = $this->ticket();
486
+		$ticket->increaseSold();
487
+		// possibly set event status to sold out
488
+		$this->event()->perform_sold_out_status_check();
489
+	}
490
+
491
+
492
+	/**
493
+	 * decrements (subtracts) this registration's related ticket sold and corresponding datetime sold values
494
+	 *
495
+	 * @return void
496
+	 * @throws DomainException
497
+	 * @throws EE_Error
498
+	 * @throws EntityNotFoundException
499
+	 * @throws InvalidArgumentException
500
+	 * @throws InvalidDataTypeException
501
+	 * @throws InvalidInterfaceException
502
+	 * @throws ReflectionException
503
+	 * @throws UnexpectedEntityException
504
+	 */
505
+	private function releaseRegistrationSpace()
506
+	{
507
+		$ticket = $this->ticket();
508
+		$ticket->decreaseSold();
509
+		// possibly change event status from sold out back to previous status
510
+		$this->event()->perform_sold_out_status_check();
511
+	}
512
+
513
+
514
+	/**
515
+	 * tracks this registration's ticket reservation in extra meta
516
+	 * and can increment related ticket reserved and corresponding datetime reserved values
517
+	 *
518
+	 * @param bool   $update_ticket if true, will increment ticket and datetime reserved count
519
+	 * @param string $source
520
+	 * @return void
521
+	 * @throws EE_Error
522
+	 * @throws InvalidArgumentException
523
+	 * @throws InvalidDataTypeException
524
+	 * @throws InvalidInterfaceException
525
+	 * @throws ReflectionException
526
+	 */
527
+	public function reserve_ticket($update_ticket = false, $source = 'unknown')
528
+	{
529
+		// only reserve ticket if space is not currently reserved
530
+		if ((bool) $this->get_extra_meta(EE_Registration::HAS_RESERVED_TICKET_KEY, true) !== true) {
531
+			$reserved = $this->update_extra_meta(EE_Registration::HAS_RESERVED_TICKET_KEY, true);
532
+			if ($reserved && $update_ticket) {
533
+				$ticket = $this->ticket();
534
+				$ticket->increaseReserved(1, "REG: {$this->ID()} (ln:" . __LINE__ . ')');
535
+				$this->update_extra_meta('reserve_ticket', "{$this->ticket_ID()} from {$source}");
536
+				$ticket->save();
537
+			}
538
+		}
539
+	}
540
+
541
+
542
+	/**
543
+	 * stops tracking this registration's ticket reservation in extra meta
544
+	 * decrements (subtracts) related ticket reserved and corresponding datetime reserved values
545
+	 *
546
+	 * @param bool   $update_ticket if true, will decrement ticket and datetime reserved count
547
+	 * @param string $source
548
+	 * @return void
549
+	 * @throws EE_Error
550
+	 * @throws InvalidArgumentException
551
+	 * @throws InvalidDataTypeException
552
+	 * @throws InvalidInterfaceException
553
+	 * @throws ReflectionException
554
+	 */
555
+	public function release_reserved_ticket($update_ticket = false, $source = 'unknown')
556
+	{
557
+		// only release ticket if space is currently reserved
558
+		if ((bool) $this->get_extra_meta(EE_Registration::HAS_RESERVED_TICKET_KEY, true) === true) {
559
+			$reserved = $this->update_extra_meta(EE_Registration::HAS_RESERVED_TICKET_KEY, false);
560
+			if ($reserved && $update_ticket) {
561
+				$ticket = $this->ticket();
562
+				$ticket->decreaseReserved(1, true, "REG: {$this->ID()} (ln:" . __LINE__ . ')');
563
+				$this->update_extra_meta('release_reserved_ticket', "{$this->ticket_ID()} from {$source}");
564
+			}
565
+		}
566
+	}
567
+
568
+
569
+	/**
570
+	 * Set Attendee ID
571
+	 *
572
+	 * @param        int $ATT_ID Attendee ID
573
+	 * @throws DomainException
574
+	 * @throws EE_Error
575
+	 * @throws EntityNotFoundException
576
+	 * @throws InvalidArgumentException
577
+	 * @throws InvalidDataTypeException
578
+	 * @throws InvalidInterfaceException
579
+	 * @throws ReflectionException
580
+	 * @throws RuntimeException
581
+	 * @throws UnexpectedEntityException
582
+	 */
583
+	public function set_attendee_id($ATT_ID = 0)
584
+	{
585
+		$this->set('ATT_ID', $ATT_ID);
586
+	}
587
+
588
+
589
+	/**
590
+	 *        Set Transaction ID
591
+	 *
592
+	 * @param        int $TXN_ID Transaction ID
593
+	 * @throws DomainException
594
+	 * @throws EE_Error
595
+	 * @throws EntityNotFoundException
596
+	 * @throws InvalidArgumentException
597
+	 * @throws InvalidDataTypeException
598
+	 * @throws InvalidInterfaceException
599
+	 * @throws ReflectionException
600
+	 * @throws RuntimeException
601
+	 * @throws UnexpectedEntityException
602
+	 */
603
+	public function set_transaction_id($TXN_ID = 0)
604
+	{
605
+		$this->set('TXN_ID', $TXN_ID);
606
+	}
607
+
608
+
609
+	/**
610
+	 *        Set Session
611
+	 *
612
+	 * @param    string $REG_session PHP Session ID
613
+	 * @throws DomainException
614
+	 * @throws EE_Error
615
+	 * @throws EntityNotFoundException
616
+	 * @throws InvalidArgumentException
617
+	 * @throws InvalidDataTypeException
618
+	 * @throws InvalidInterfaceException
619
+	 * @throws ReflectionException
620
+	 * @throws RuntimeException
621
+	 * @throws UnexpectedEntityException
622
+	 */
623
+	public function set_session($REG_session = '')
624
+	{
625
+		$this->set('REG_session', $REG_session);
626
+	}
627
+
628
+
629
+	/**
630
+	 *        Set Registration URL Link
631
+	 *
632
+	 * @param    string $REG_url_link Registration URL Link
633
+	 * @throws DomainException
634
+	 * @throws EE_Error
635
+	 * @throws EntityNotFoundException
636
+	 * @throws InvalidArgumentException
637
+	 * @throws InvalidDataTypeException
638
+	 * @throws InvalidInterfaceException
639
+	 * @throws ReflectionException
640
+	 * @throws RuntimeException
641
+	 * @throws UnexpectedEntityException
642
+	 */
643
+	public function set_reg_url_link($REG_url_link = '')
644
+	{
645
+		$this->set('REG_url_link', $REG_url_link);
646
+	}
647
+
648
+
649
+	/**
650
+	 *        Set Attendee Counter
651
+	 *
652
+	 * @param        int $REG_count Primary Attendee
653
+	 * @throws DomainException
654
+	 * @throws EE_Error
655
+	 * @throws EntityNotFoundException
656
+	 * @throws InvalidArgumentException
657
+	 * @throws InvalidDataTypeException
658
+	 * @throws InvalidInterfaceException
659
+	 * @throws ReflectionException
660
+	 * @throws RuntimeException
661
+	 * @throws UnexpectedEntityException
662
+	 */
663
+	public function set_count($REG_count = 1)
664
+	{
665
+		$this->set('REG_count', $REG_count);
666
+	}
667
+
668
+
669
+	/**
670
+	 *        Set Group Size
671
+	 *
672
+	 * @param        boolean $REG_group_size Group Registration
673
+	 * @throws DomainException
674
+	 * @throws EE_Error
675
+	 * @throws EntityNotFoundException
676
+	 * @throws InvalidArgumentException
677
+	 * @throws InvalidDataTypeException
678
+	 * @throws InvalidInterfaceException
679
+	 * @throws ReflectionException
680
+	 * @throws RuntimeException
681
+	 * @throws UnexpectedEntityException
682
+	 */
683
+	public function set_group_size($REG_group_size = false)
684
+	{
685
+		$this->set('REG_group_size', $REG_group_size);
686
+	}
687
+
688
+
689
+	/**
690
+	 *    is_not_approved -  convenience method that returns TRUE if REG status ID ==
691
+	 *    EEM_Registration::status_id_not_approved
692
+	 *
693
+	 * @return        boolean
694
+	 * @throws EE_Error
695
+	 * @throws InvalidArgumentException
696
+	 * @throws InvalidDataTypeException
697
+	 * @throws InvalidInterfaceException
698
+	 * @throws ReflectionException
699
+	 */
700
+	public function is_not_approved()
701
+	{
702
+		return $this->status_ID() === EEM_Registration::status_id_not_approved;
703
+	}
704
+
705
+
706
+	/**
707
+	 *    is_pending_payment -  convenience method that returns TRUE if REG status ID ==
708
+	 *    EEM_Registration::status_id_pending_payment
709
+	 *
710
+	 * @return        boolean
711
+	 * @throws EE_Error
712
+	 * @throws InvalidArgumentException
713
+	 * @throws InvalidDataTypeException
714
+	 * @throws InvalidInterfaceException
715
+	 * @throws ReflectionException
716
+	 */
717
+	public function is_pending_payment()
718
+	{
719
+		return $this->status_ID() === EEM_Registration::status_id_pending_payment;
720
+	}
721
+
722
+
723
+	/**
724
+	 *    is_approved -  convenience method that returns TRUE if REG status ID == EEM_Registration::status_id_approved
725
+	 *
726
+	 * @return        boolean
727
+	 * @throws EE_Error
728
+	 * @throws InvalidArgumentException
729
+	 * @throws InvalidDataTypeException
730
+	 * @throws InvalidInterfaceException
731
+	 * @throws ReflectionException
732
+	 */
733
+	public function is_approved()
734
+	{
735
+		return $this->status_ID() === EEM_Registration::status_id_approved;
736
+	}
737
+
738
+
739
+	/**
740
+	 *    is_cancelled -  convenience method that returns TRUE if REG status ID == EEM_Registration::status_id_cancelled
741
+	 *
742
+	 * @return        boolean
743
+	 * @throws EE_Error
744
+	 * @throws InvalidArgumentException
745
+	 * @throws InvalidDataTypeException
746
+	 * @throws InvalidInterfaceException
747
+	 * @throws ReflectionException
748
+	 */
749
+	public function is_cancelled()
750
+	{
751
+		return $this->status_ID() === EEM_Registration::status_id_cancelled;
752
+	}
753
+
754
+
755
+	/**
756
+	 *    is_declined -  convenience method that returns TRUE if REG status ID == EEM_Registration::status_id_declined
757
+	 *
758
+	 * @return        boolean
759
+	 * @throws EE_Error
760
+	 * @throws InvalidArgumentException
761
+	 * @throws InvalidDataTypeException
762
+	 * @throws InvalidInterfaceException
763
+	 * @throws ReflectionException
764
+	 */
765
+	public function is_declined()
766
+	{
767
+		return $this->status_ID() === EEM_Registration::status_id_declined;
768
+	}
769
+
770
+
771
+	/**
772
+	 *    is_incomplete -  convenience method that returns TRUE if REG status ID ==
773
+	 *    EEM_Registration::status_id_incomplete
774
+	 *
775
+	 * @return        boolean
776
+	 * @throws EE_Error
777
+	 * @throws InvalidArgumentException
778
+	 * @throws InvalidDataTypeException
779
+	 * @throws InvalidInterfaceException
780
+	 * @throws ReflectionException
781
+	 */
782
+	public function is_incomplete()
783
+	{
784
+		return $this->status_ID() === EEM_Registration::status_id_incomplete;
785
+	}
786
+
787
+
788
+	/**
789
+	 *        Set Registration Date
790
+	 *
791
+	 * @param        mixed ( int or string ) $REG_date Registration Date - Unix timestamp or string representation of
792
+	 *                                                 Date
793
+	 * @throws DomainException
794
+	 * @throws EE_Error
795
+	 * @throws EntityNotFoundException
796
+	 * @throws InvalidArgumentException
797
+	 * @throws InvalidDataTypeException
798
+	 * @throws InvalidInterfaceException
799
+	 * @throws ReflectionException
800
+	 * @throws RuntimeException
801
+	 * @throws UnexpectedEntityException
802
+	 */
803
+	public function set_reg_date($REG_date = false)
804
+	{
805
+		$this->set('REG_date', $REG_date);
806
+	}
807
+
808
+
809
+	/**
810
+	 *    Set final price owing for this registration after all ticket/price modifications
811
+	 *
812
+	 * @param    float $REG_final_price
813
+	 * @throws DomainException
814
+	 * @throws EE_Error
815
+	 * @throws EntityNotFoundException
816
+	 * @throws InvalidArgumentException
817
+	 * @throws InvalidDataTypeException
818
+	 * @throws InvalidInterfaceException
819
+	 * @throws ReflectionException
820
+	 * @throws RuntimeException
821
+	 * @throws UnexpectedEntityException
822
+	 */
823
+	public function set_final_price($REG_final_price = 0.00)
824
+	{
825
+		$this->set('REG_final_price', $REG_final_price);
826
+	}
827
+
828
+
829
+	/**
830
+	 *    Set amount paid towards this registration's final price
831
+	 *
832
+	 * @param    float $REG_paid
833
+	 * @throws DomainException
834
+	 * @throws EE_Error
835
+	 * @throws EntityNotFoundException
836
+	 * @throws InvalidArgumentException
837
+	 * @throws InvalidDataTypeException
838
+	 * @throws InvalidInterfaceException
839
+	 * @throws ReflectionException
840
+	 * @throws RuntimeException
841
+	 * @throws UnexpectedEntityException
842
+	 */
843
+	public function set_paid($REG_paid = 0.00)
844
+	{
845
+		$this->set('REG_paid', $REG_paid);
846
+	}
847
+
848
+
849
+	/**
850
+	 *        Attendee Is Going
851
+	 *
852
+	 * @param        boolean $REG_att_is_going Attendee Is Going
853
+	 * @throws DomainException
854
+	 * @throws EE_Error
855
+	 * @throws EntityNotFoundException
856
+	 * @throws InvalidArgumentException
857
+	 * @throws InvalidDataTypeException
858
+	 * @throws InvalidInterfaceException
859
+	 * @throws ReflectionException
860
+	 * @throws RuntimeException
861
+	 * @throws UnexpectedEntityException
862
+	 */
863
+	public function set_att_is_going($REG_att_is_going = false)
864
+	{
865
+		$this->set('REG_att_is_going', $REG_att_is_going);
866
+	}
867
+
868
+
869
+	/**
870
+	 * Gets the related attendee
871
+	 *
872
+	 * @return EE_Attendee|EE_Base_Class
873
+	 * @throws EE_Error
874
+	 * @throws InvalidArgumentException
875
+	 * @throws InvalidDataTypeException
876
+	 * @throws InvalidInterfaceException
877
+	 * @throws ReflectionException
878
+	 */
879
+	public function attendee()
880
+	{
881
+		return $this->get_first_related('Attendee');
882
+	}
883
+
884
+	/**
885
+	 * Gets the name of the attendee.
886
+	 * @since 4.10.12.p
887
+	 * @param bool $apply_html_entities set to true if you want to use HTML entities.
888
+	 * @return string
889
+	 * @throws EE_Error
890
+	 * @throws InvalidArgumentException
891
+	 * @throws InvalidDataTypeException
892
+	 * @throws InvalidInterfaceException
893
+	 * @throws ReflectionException
894
+	 */
895
+	public function attendeeName($apply_html_entities = false)
896
+	{
897
+		$attendee = $this->get_first_related('Attendee');
898
+		if ($attendee instanceof EE_Attendee) {
899
+			$attendee_name = $attendee->full_name($apply_html_entities);
900
+		} else {
901
+			$attendee_name = esc_html__('Unknown', 'event_espresso');
902
+		}
903
+		return $attendee_name;
904
+	}
905
+
906
+
907
+	/**
908
+	 *        get Event ID
909
+	 */
910
+	public function event_ID()
911
+	{
912
+		return $this->get('EVT_ID');
913
+	}
914
+
915
+
916
+	/**
917
+	 *        get Event ID
918
+	 */
919
+	public function event_name()
920
+	{
921
+		$event = $this->event_obj();
922
+		if ($event) {
923
+			return $event->name();
924
+		} else {
925
+			return null;
926
+		}
927
+	}
928
+
929
+
930
+	/**
931
+	 * Fetches the event this registration is for
932
+	 *
933
+	 * @return EE_Base_Class|EE_Event
934
+	 * @throws EE_Error
935
+	 * @throws InvalidArgumentException
936
+	 * @throws InvalidDataTypeException
937
+	 * @throws InvalidInterfaceException
938
+	 * @throws ReflectionException
939
+	 */
940
+	public function event_obj()
941
+	{
942
+		return $this->get_first_related('Event');
943
+	}
944
+
945
+
946
+	/**
947
+	 *        get Attendee ID
948
+	 */
949
+	public function attendee_ID()
950
+	{
951
+		return $this->get('ATT_ID');
952
+	}
953
+
954
+
955
+	/**
956
+	 *        get PHP Session ID
957
+	 */
958
+	public function session_ID()
959
+	{
960
+		return $this->get('REG_session');
961
+	}
962
+
963
+
964
+	/**
965
+	 * Gets the string which represents the URL trigger for the receipt template in the message template system.
966
+	 *
967
+	 * @param string $messenger 'pdf' or 'html'.  Default 'html'.
968
+	 * @return string
969
+	 * @throws DomainException
970
+	 * @throws EE_Error
971
+	 * @throws InvalidArgumentException
972
+	 * @throws InvalidDataTypeException
973
+	 * @throws InvalidInterfaceException
974
+	 * @throws ReflectionException
975
+	 */
976
+	public function receipt_url($messenger = 'html')
977
+	{
978
+
979
+		/**
980
+		 * The below will be deprecated one version after this.  We check first if there is a custom receipt template
981
+		 * already in use on old system.  If there is then we just return the standard url for it.
982
+		 *
983
+		 * @since 4.5.0
984
+		 */
985
+		$template_relative_path = 'modules/gateways/Invoice/lib/templates/receipt_body.template.php';
986
+		$has_custom = EEH_Template::locate_template(
987
+			$template_relative_path,
988
+			array(),
989
+			true,
990
+			true,
991
+			true
992
+		);
993
+
994
+		if ($has_custom) {
995
+			return add_query_arg(array('receipt' => 'true'), $this->invoice_url('launch'));
996
+		}
997
+		return apply_filters('FHEE__EE_Registration__receipt_url__receipt_url', '', $this, $messenger, 'receipt');
998
+	}
999
+
1000
+
1001
+	/**
1002
+	 * Gets the string which represents the URL trigger for the invoice template in the message template system.
1003
+	 *
1004
+	 * @param string $messenger 'pdf' or 'html'.  Default 'html'.
1005
+	 * @return string
1006
+	 * @throws DomainException
1007
+	 * @throws EE_Error
1008
+	 * @throws InvalidArgumentException
1009
+	 * @throws InvalidDataTypeException
1010
+	 * @throws InvalidInterfaceException
1011
+	 * @throws ReflectionException
1012
+	 */
1013
+	public function invoice_url($messenger = 'html')
1014
+	{
1015
+		/**
1016
+		 * The below will be deprecated one version after this.  We check first if there is a custom invoice template
1017
+		 * already in use on old system.  If there is then we just return the standard url for it.
1018
+		 *
1019
+		 * @since 4.5.0
1020
+		 */
1021
+		$template_relative_path = 'modules/gateways/Invoice/lib/templates/invoice_body.template.php';
1022
+		$has_custom = EEH_Template::locate_template(
1023
+			$template_relative_path,
1024
+			array(),
1025
+			true,
1026
+			true,
1027
+			true
1028
+		);
1029
+
1030
+		if ($has_custom) {
1031
+			if ($messenger == 'html') {
1032
+				return $this->invoice_url('launch');
1033
+			}
1034
+			$route = $messenger == 'download' || $messenger == 'pdf' ? 'download_invoice' : 'launch_invoice';
1035
+
1036
+			$query_args = array('ee' => $route, 'id' => $this->reg_url_link());
1037
+			if ($messenger == 'html') {
1038
+				$query_args['html'] = true;
1039
+			}
1040
+			return add_query_arg($query_args, get_permalink(EE_Registry::instance()->CFG->core->thank_you_page_id));
1041
+		}
1042
+		return apply_filters('FHEE__EE_Registration__invoice_url__invoice_url', '', $this, $messenger, 'invoice');
1043
+	}
1044
+
1045
+
1046
+	/**
1047
+	 * get Registration URL Link
1048
+	 *
1049
+	 * @return string
1050
+	 * @throws EE_Error
1051
+	 * @throws InvalidArgumentException
1052
+	 * @throws InvalidDataTypeException
1053
+	 * @throws InvalidInterfaceException
1054
+	 * @throws ReflectionException
1055
+	 */
1056
+	public function reg_url_link()
1057
+	{
1058
+		return (string) $this->get('REG_url_link');
1059
+	}
1060
+
1061
+
1062
+	/**
1063
+	 * Echoes out invoice_url()
1064
+	 *
1065
+	 * @param string $type 'download','launch', or 'html' (default is 'launch')
1066
+	 * @return void
1067
+	 * @throws DomainException
1068
+	 * @throws EE_Error
1069
+	 * @throws InvalidArgumentException
1070
+	 * @throws InvalidDataTypeException
1071
+	 * @throws InvalidInterfaceException
1072
+	 * @throws ReflectionException
1073
+	 */
1074
+	public function e_invoice_url($type = 'launch')
1075
+	{
1076
+		echo esc_url_raw($this->invoice_url($type));
1077
+	}
1078
+
1079
+
1080
+	/**
1081
+	 * Echoes out payment_overview_url
1082
+	 */
1083
+	public function e_payment_overview_url()
1084
+	{
1085
+		echo esc_url_raw($this->payment_overview_url());
1086
+	}
1087
+
1088
+
1089
+	/**
1090
+	 * Gets the URL for the checkout payment options reg step
1091
+	 * with this registration's REG_url_link added as a query parameter
1092
+	 *
1093
+	 * @param bool $clear_session Set to true when you want to clear the session on revisiting the
1094
+	 *                            payment overview url.
1095
+	 * @return string
1096
+	 * @throws EE_Error
1097
+	 * @throws InvalidArgumentException
1098
+	 * @throws InvalidDataTypeException
1099
+	 * @throws InvalidInterfaceException
1100
+	 * @throws ReflectionException
1101
+	 */
1102
+	public function payment_overview_url($clear_session = false)
1103
+	{
1104
+		return add_query_arg(
1105
+			(array) apply_filters(
1106
+				'FHEE__EE_Registration__payment_overview_url__query_args',
1107
+				array(
1108
+					'e_reg_url_link' => $this->reg_url_link(),
1109
+					'step'           => 'payment_options',
1110
+					'revisit'        => true,
1111
+					'clear_session'  => (bool) $clear_session,
1112
+				),
1113
+				$this
1114
+			),
1115
+			EE_Registry::instance()->CFG->core->reg_page_url()
1116
+		);
1117
+	}
1118
+
1119
+
1120
+	/**
1121
+	 * Gets the URL for the checkout attendee information reg step
1122
+	 * with this registration's REG_url_link added as a query parameter
1123
+	 *
1124
+	 * @return string
1125
+	 * @throws EE_Error
1126
+	 * @throws InvalidArgumentException
1127
+	 * @throws InvalidDataTypeException
1128
+	 * @throws InvalidInterfaceException
1129
+	 * @throws ReflectionException
1130
+	 */
1131
+	public function edit_attendee_information_url()
1132
+	{
1133
+		return add_query_arg(
1134
+			(array) apply_filters(
1135
+				'FHEE__EE_Registration__edit_attendee_information_url__query_args',
1136
+				array(
1137
+					'e_reg_url_link' => $this->reg_url_link(),
1138
+					'step'           => 'attendee_information',
1139
+					'revisit'        => true,
1140
+				),
1141
+				$this
1142
+			),
1143
+			EE_Registry::instance()->CFG->core->reg_page_url()
1144
+		);
1145
+	}
1146
+
1147
+
1148
+	/**
1149
+	 * Simply generates and returns the appropriate admin_url link to edit this registration
1150
+	 *
1151
+	 * @return string
1152
+	 * @throws EE_Error
1153
+	 * @throws InvalidArgumentException
1154
+	 * @throws InvalidDataTypeException
1155
+	 * @throws InvalidInterfaceException
1156
+	 * @throws ReflectionException
1157
+	 */
1158
+	public function get_admin_edit_url()
1159
+	{
1160
+		return EEH_URL::add_query_args_and_nonce(
1161
+			array(
1162
+				'page'    => 'espresso_registrations',
1163
+				'action'  => 'view_registration',
1164
+				'_REG_ID' => $this->ID(),
1165
+			),
1166
+			admin_url('admin.php')
1167
+		);
1168
+	}
1169
+
1170
+
1171
+	/**
1172
+	 * is_primary_registrant?
1173
+	 *
1174
+	 * @throws EE_Error
1175
+	 * @throws InvalidArgumentException
1176
+	 * @throws InvalidDataTypeException
1177
+	 * @throws InvalidInterfaceException
1178
+	 * @throws ReflectionException
1179
+	 */
1180
+	public function is_primary_registrant()
1181
+	{
1182
+		return (int) $this->get('REG_count') === 1;
1183
+	}
1184
+
1185
+
1186
+	/**
1187
+	 * This returns the primary registration object for this registration group (which may be this object).
1188
+	 *
1189
+	 * @return EE_Registration
1190
+	 * @throws EE_Error
1191
+	 * @throws InvalidArgumentException
1192
+	 * @throws InvalidDataTypeException
1193
+	 * @throws InvalidInterfaceException
1194
+	 * @throws ReflectionException
1195
+	 */
1196
+	public function get_primary_registration()
1197
+	{
1198
+		if ($this->is_primary_registrant()) {
1199
+			return $this;
1200
+		}
1201
+
1202
+		// k reg_count !== 1 so let's get the EE_Registration object matching this txn_id and reg_count == 1
1203
+		/** @var EE_Registration $primary_registrant */
1204
+		$primary_registrant = EEM_Registration::instance()->get_one(
1205
+			array(
1206
+				array(
1207
+					'TXN_ID'    => $this->transaction_ID(),
1208
+					'REG_count' => 1,
1209
+				),
1210
+			)
1211
+		);
1212
+		return $primary_registrant;
1213
+	}
1214
+
1215
+
1216
+	/**
1217
+	 * get  Attendee Number
1218
+	 *
1219
+	 * @throws EE_Error
1220
+	 * @throws InvalidArgumentException
1221
+	 * @throws InvalidDataTypeException
1222
+	 * @throws InvalidInterfaceException
1223
+	 * @throws ReflectionException
1224
+	 */
1225
+	public function count()
1226
+	{
1227
+		return $this->get('REG_count');
1228
+	}
1229
+
1230
+
1231
+	/**
1232
+	 * get Group Size
1233
+	 *
1234
+	 * @throws EE_Error
1235
+	 * @throws InvalidArgumentException
1236
+	 * @throws InvalidDataTypeException
1237
+	 * @throws InvalidInterfaceException
1238
+	 * @throws ReflectionException
1239
+	 */
1240
+	public function group_size()
1241
+	{
1242
+		return $this->get('REG_group_size');
1243
+	}
1244
+
1245
+
1246
+	/**
1247
+	 * get Registration Date
1248
+	 *
1249
+	 * @throws EE_Error
1250
+	 * @throws InvalidArgumentException
1251
+	 * @throws InvalidDataTypeException
1252
+	 * @throws InvalidInterfaceException
1253
+	 * @throws ReflectionException
1254
+	 */
1255
+	public function date()
1256
+	{
1257
+		return $this->get('REG_date');
1258
+	}
1259
+
1260
+
1261
+	/**
1262
+	 * gets a pretty date
1263
+	 *
1264
+	 * @param string $date_format
1265
+	 * @param string $time_format
1266
+	 * @return string
1267
+	 * @throws EE_Error
1268
+	 * @throws InvalidArgumentException
1269
+	 * @throws InvalidDataTypeException
1270
+	 * @throws InvalidInterfaceException
1271
+	 * @throws ReflectionException
1272
+	 */
1273
+	public function pretty_date($date_format = null, $time_format = null)
1274
+	{
1275
+		return $this->get_datetime('REG_date', $date_format, $time_format);
1276
+	}
1277
+
1278
+
1279
+	/**
1280
+	 * final_price
1281
+	 * the registration's share of the transaction total, so that the
1282
+	 * sum of all the transaction's REG_final_prices equal the transaction's total
1283
+	 *
1284
+	 * @return float
1285
+	 * @throws EE_Error
1286
+	 * @throws InvalidArgumentException
1287
+	 * @throws InvalidDataTypeException
1288
+	 * @throws InvalidInterfaceException
1289
+	 * @throws ReflectionException
1290
+	 */
1291
+	public function final_price()
1292
+	{
1293
+		return $this->get('REG_final_price');
1294
+	}
1295
+
1296
+
1297
+	/**
1298
+	 * pretty_final_price
1299
+	 *  final price as formatted string, with correct decimal places and currency symbol
1300
+	 *
1301
+	 * @return string
1302
+	 * @throws EE_Error
1303
+	 * @throws InvalidArgumentException
1304
+	 * @throws InvalidDataTypeException
1305
+	 * @throws InvalidInterfaceException
1306
+	 * @throws ReflectionException
1307
+	 */
1308
+	public function pretty_final_price()
1309
+	{
1310
+		return $this->get_pretty('REG_final_price');
1311
+	}
1312
+
1313
+
1314
+	/**
1315
+	 * get paid (yeah)
1316
+	 *
1317
+	 * @return float
1318
+	 * @throws EE_Error
1319
+	 * @throws InvalidArgumentException
1320
+	 * @throws InvalidDataTypeException
1321
+	 * @throws InvalidInterfaceException
1322
+	 * @throws ReflectionException
1323
+	 */
1324
+	public function paid()
1325
+	{
1326
+		return $this->get('REG_paid');
1327
+	}
1328
+
1329
+
1330
+	/**
1331
+	 * pretty_paid
1332
+	 *
1333
+	 * @return float
1334
+	 * @throws EE_Error
1335
+	 * @throws InvalidArgumentException
1336
+	 * @throws InvalidDataTypeException
1337
+	 * @throws InvalidInterfaceException
1338
+	 * @throws ReflectionException
1339
+	 */
1340
+	public function pretty_paid()
1341
+	{
1342
+		return $this->get_pretty('REG_paid');
1343
+	}
1344
+
1345
+
1346
+	/**
1347
+	 * owes_monies_and_can_pay
1348
+	 * whether or not this registration has monies owing and it's' status allows payment
1349
+	 *
1350
+	 * @param array $requires_payment
1351
+	 * @return bool
1352
+	 * @throws EE_Error
1353
+	 * @throws InvalidArgumentException
1354
+	 * @throws InvalidDataTypeException
1355
+	 * @throws InvalidInterfaceException
1356
+	 * @throws ReflectionException
1357
+	 */
1358
+	public function owes_monies_and_can_pay($requires_payment = array())
1359
+	{
1360
+		// these reg statuses require payment (if event is not free)
1361
+		$requires_payment = ! empty($requires_payment)
1362
+			? $requires_payment
1363
+			: EEM_Registration::reg_statuses_that_allow_payment();
1364
+		if (
1365
+			$this->final_price() !== 0 &&
1366
+			$this->final_price() !== $this->paid() &&
1367
+			in_array($this->status_ID(), $requires_payment)
1368
+		) {
1369
+			return true;
1370
+		}
1371
+		return false;
1372
+	}
1373
+
1374
+
1375
+	/**
1376
+	 * Prints out the return value of $this->pretty_status()
1377
+	 *
1378
+	 * @param bool $show_icons
1379
+	 * @return void
1380
+	 * @throws EE_Error
1381
+	 * @throws InvalidArgumentException
1382
+	 * @throws InvalidDataTypeException
1383
+	 * @throws InvalidInterfaceException
1384
+	 * @throws ReflectionException
1385
+	 */
1386
+	public function e_pretty_status($show_icons = false)
1387
+	{
1388
+		echo wp_kses($this->pretty_status($show_icons), AllowedTags::getAllowedTags());
1389
+	}
1390
+
1391
+
1392
+	/**
1393
+	 * Returns a nice version of the status for displaying to customers
1394
+	 *
1395
+	 * @param bool $show_icons
1396
+	 * @return string
1397
+	 * @throws EE_Error
1398
+	 * @throws InvalidArgumentException
1399
+	 * @throws InvalidDataTypeException
1400
+	 * @throws InvalidInterfaceException
1401
+	 * @throws ReflectionException
1402
+	 */
1403
+	public function pretty_status($show_icons = false)
1404
+	{
1405
+		$status = EEM_Status::instance()->localized_status(
1406
+			array($this->status_ID() => esc_html__('unknown', 'event_espresso')),
1407
+			false,
1408
+			'sentence'
1409
+		);
1410
+		$icon = '';
1411
+		switch ($this->status_ID()) {
1412
+			case EEM_Registration::status_id_approved:
1413
+				$icon = $show_icons
1414
+					? '<span class="dashicons dashicons-star-filled ee-icon-size-16 green-text"></span>'
1415
+					: '';
1416
+				break;
1417
+			case EEM_Registration::status_id_pending_payment:
1418
+				$icon = $show_icons
1419
+					? '<span class="dashicons dashicons-star-half ee-icon-size-16 orange-text"></span>'
1420
+					: '';
1421
+				break;
1422
+			case EEM_Registration::status_id_not_approved:
1423
+				$icon = $show_icons
1424
+					? '<span class="dashicons dashicons-marker ee-icon-size-16 orange-text"></span>'
1425
+					: '';
1426
+				break;
1427
+			case EEM_Registration::status_id_cancelled:
1428
+				$icon = $show_icons
1429
+					? '<span class="dashicons dashicons-no ee-icon-size-16 lt-grey-text"></span>'
1430
+					: '';
1431
+				break;
1432
+			case EEM_Registration::status_id_incomplete:
1433
+				$icon = $show_icons
1434
+					? '<span class="dashicons dashicons-no ee-icon-size-16 lt-orange-text"></span>'
1435
+					: '';
1436
+				break;
1437
+			case EEM_Registration::status_id_declined:
1438
+				$icon = $show_icons
1439
+					? '<span class="dashicons dashicons-no ee-icon-size-16 red-text"></span>'
1440
+					: '';
1441
+				break;
1442
+			case EEM_Registration::status_id_wait_list:
1443
+				$icon = $show_icons
1444
+					? '<span class="dashicons dashicons-clipboard ee-icon-size-16 purple-text"></span>'
1445
+					: '';
1446
+				break;
1447
+		}
1448
+		return $icon . $status[ $this->status_ID() ];
1449
+	}
1450
+
1451
+
1452
+	/**
1453
+	 *        get Attendee Is Going
1454
+	 */
1455
+	public function att_is_going()
1456
+	{
1457
+		return $this->get('REG_att_is_going');
1458
+	}
1459
+
1460
+
1461
+	/**
1462
+	 * Gets related answers
1463
+	 *
1464
+	 * @param array $query_params @see
1465
+	 *                            https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1466
+	 * @return EE_Answer[]|EE_Base_Class[]
1467
+	 * @throws EE_Error
1468
+	 * @throws InvalidArgumentException
1469
+	 * @throws InvalidDataTypeException
1470
+	 * @throws InvalidInterfaceException
1471
+	 * @throws ReflectionException
1472
+	 */
1473
+	public function answers($query_params = null)
1474
+	{
1475
+		return $this->get_many_related('Answer', $query_params);
1476
+	}
1477
+
1478
+
1479
+	/**
1480
+	 * Gets the registration's answer value to the specified question
1481
+	 * (either the question's ID or a question object)
1482
+	 *
1483
+	 * @param EE_Question|int $question
1484
+	 * @param bool            $pretty_value
1485
+	 * @return array|string if pretty_value= true, the result will always be a string
1486
+	 * (because the answer might be an array of answer values, so passing pretty_value=true
1487
+	 * will convert it into some kind of string)
1488
+	 * @throws EE_Error
1489
+	 * @throws InvalidArgumentException
1490
+	 * @throws InvalidDataTypeException
1491
+	 * @throws InvalidInterfaceException
1492
+	 */
1493
+	public function answer_value_to_question($question, $pretty_value = true)
1494
+	{
1495
+		$question_id = EEM_Question::instance()->ensure_is_ID($question);
1496
+		return EEM_Answer::instance()->get_answer_value_to_question($this, $question_id, $pretty_value);
1497
+	}
1498
+
1499
+
1500
+	/**
1501
+	 * question_groups
1502
+	 * returns an array of EE_Question_Group objects for this registration
1503
+	 *
1504
+	 * @return EE_Question_Group[]
1505
+	 * @throws EE_Error
1506
+	 * @throws InvalidArgumentException
1507
+	 * @throws InvalidDataTypeException
1508
+	 * @throws InvalidInterfaceException
1509
+	 * @throws ReflectionException
1510
+	 */
1511
+	public function question_groups()
1512
+	{
1513
+		return EEM_Event::instance()->get_question_groups_for_event($this->event_ID(), $this);
1514
+	}
1515
+
1516
+
1517
+	/**
1518
+	 * count_question_groups
1519
+	 * returns a count of the number of EE_Question_Group objects for this registration
1520
+	 *
1521
+	 * @return int
1522
+	 * @throws EE_Error
1523
+	 * @throws EntityNotFoundException
1524
+	 * @throws InvalidArgumentException
1525
+	 * @throws InvalidDataTypeException
1526
+	 * @throws InvalidInterfaceException
1527
+	 * @throws ReflectionException
1528
+	 */
1529
+	public function count_question_groups()
1530
+	{
1531
+		return EEM_Event::instance()->count_related(
1532
+			$this->event_ID(),
1533
+			'Question_Group',
1534
+			[
1535
+				[
1536
+					'Event_Question_Group.'
1537
+					. EEM_Event_Question_Group::instance()->fieldNameForContext($this->is_primary_registrant()) => true,
1538
+				]
1539
+			]
1540
+		);
1541
+	}
1542
+
1543
+
1544
+	/**
1545
+	 * Returns the registration date in the 'standard' string format
1546
+	 * (function may be improved in the future to allow for different formats and timezones)
1547
+	 *
1548
+	 * @return string
1549
+	 * @throws EE_Error
1550
+	 * @throws InvalidArgumentException
1551
+	 * @throws InvalidDataTypeException
1552
+	 * @throws InvalidInterfaceException
1553
+	 * @throws ReflectionException
1554
+	 */
1555
+	public function reg_date()
1556
+	{
1557
+		return $this->get_datetime('REG_date');
1558
+	}
1559
+
1560
+
1561
+	/**
1562
+	 * Gets the datetime-ticket for this registration (ie, it can be used to isolate
1563
+	 * the ticket this registration purchased, or the datetime they have registered
1564
+	 * to attend)
1565
+	 *
1566
+	 * @return EE_Base_Class|EE_Datetime_Ticket
1567
+	 * @throws EE_Error
1568
+	 * @throws InvalidArgumentException
1569
+	 * @throws InvalidDataTypeException
1570
+	 * @throws InvalidInterfaceException
1571
+	 * @throws ReflectionException
1572
+	 */
1573
+	public function datetime_ticket()
1574
+	{
1575
+		return $this->get_first_related('Datetime_Ticket');
1576
+	}
1577
+
1578
+
1579
+	/**
1580
+	 * Sets the registration's datetime_ticket.
1581
+	 *
1582
+	 * @param EE_Datetime_Ticket $datetime_ticket
1583
+	 * @return EE_Base_Class|EE_Datetime_Ticket
1584
+	 * @throws EE_Error
1585
+	 * @throws InvalidArgumentException
1586
+	 * @throws InvalidDataTypeException
1587
+	 * @throws InvalidInterfaceException
1588
+	 * @throws ReflectionException
1589
+	 */
1590
+	public function set_datetime_ticket($datetime_ticket)
1591
+	{
1592
+		return $this->_add_relation_to($datetime_ticket, 'Datetime_Ticket');
1593
+	}
1594
+
1595
+
1596
+	/**
1597
+	 * Gets deleted
1598
+	 *
1599
+	 * @return bool
1600
+	 * @throws EE_Error
1601
+	 * @throws InvalidArgumentException
1602
+	 * @throws InvalidDataTypeException
1603
+	 * @throws InvalidInterfaceException
1604
+	 * @throws ReflectionException
1605
+	 */
1606
+	public function deleted()
1607
+	{
1608
+		return $this->get('REG_deleted');
1609
+	}
1610
+
1611
+
1612
+	/**
1613
+	 * Sets deleted
1614
+	 *
1615
+	 * @param boolean $deleted
1616
+	 * @return void
1617
+	 * @throws DomainException
1618
+	 * @throws EE_Error
1619
+	 * @throws EntityNotFoundException
1620
+	 * @throws InvalidArgumentException
1621
+	 * @throws InvalidDataTypeException
1622
+	 * @throws InvalidInterfaceException
1623
+	 * @throws ReflectionException
1624
+	 * @throws RuntimeException
1625
+	 * @throws UnexpectedEntityException
1626
+	 */
1627
+	public function set_deleted($deleted)
1628
+	{
1629
+		if ($deleted) {
1630
+			$this->delete();
1631
+		} else {
1632
+			$this->restore();
1633
+		}
1634
+	}
1635
+
1636
+
1637
+	/**
1638
+	 * Get the status object of this object
1639
+	 *
1640
+	 * @return EE_Base_Class|EE_Status
1641
+	 * @throws EE_Error
1642
+	 * @throws InvalidArgumentException
1643
+	 * @throws InvalidDataTypeException
1644
+	 * @throws InvalidInterfaceException
1645
+	 * @throws ReflectionException
1646
+	 */
1647
+	public function status_obj()
1648
+	{
1649
+		return $this->get_first_related('Status');
1650
+	}
1651
+
1652
+
1653
+	/**
1654
+	 * Returns the number of times this registration has checked into any of the datetimes
1655
+	 * its available for
1656
+	 *
1657
+	 * @return int
1658
+	 * @throws EE_Error
1659
+	 * @throws InvalidArgumentException
1660
+	 * @throws InvalidDataTypeException
1661
+	 * @throws InvalidInterfaceException
1662
+	 * @throws ReflectionException
1663
+	 */
1664
+	public function count_checkins()
1665
+	{
1666
+		return $this->get_model()->count_related($this, 'Checkin');
1667
+	}
1668
+
1669
+
1670
+	/**
1671
+	 * Returns the number of current Check-ins this registration is checked into for any of the datetimes the
1672
+	 * registration is for.  Note, this is ONLY checked in (does not include checkedout)
1673
+	 *
1674
+	 * @return int
1675
+	 * @throws EE_Error
1676
+	 * @throws InvalidArgumentException
1677
+	 * @throws InvalidDataTypeException
1678
+	 * @throws InvalidInterfaceException
1679
+	 * @throws ReflectionException
1680
+	 */
1681
+	public function count_checkins_not_checkedout()
1682
+	{
1683
+		return $this->get_model()->count_related($this, 'Checkin', array(array('CHK_in' => 1)));
1684
+	}
1685
+
1686
+
1687
+	/**
1688
+	 * The purpose of this method is simply to check whether this registration can checkin to the given datetime.
1689
+	 *
1690
+	 * @param int | EE_Datetime $DTT_OR_ID      The datetime the registration is being checked against
1691
+	 * @param bool              $check_approved This is used to indicate whether the caller wants can_checkin to also
1692
+	 *                                          consider registration status as well as datetime access.
1693
+	 * @return bool
1694
+	 * @throws EE_Error
1695
+	 * @throws InvalidArgumentException
1696
+	 * @throws InvalidDataTypeException
1697
+	 * @throws InvalidInterfaceException
1698
+	 * @throws ReflectionException
1699
+	 */
1700
+	public function can_checkin($DTT_OR_ID, $check_approved = true)
1701
+	{
1702
+		$DTT_ID = EEM_Datetime::instance()->ensure_is_ID($DTT_OR_ID);
1703
+		// first check registration status
1704
+		if (! $DTT_ID || ($check_approved && ! $this->is_approved())) {
1705
+			return false;
1706
+		}
1707
+		// is there a datetime ticket that matches this dtt_ID?
1708
+		if (
1709
+			! (EEM_Datetime_Ticket::instance()->exists(
1710
+				array(
1711
+					array(
1712
+						'TKT_ID' => $this->get('TKT_ID'),
1713
+						'DTT_ID' => $DTT_ID,
1714
+					),
1715
+				)
1716
+			))
1717
+		) {
1718
+			return false;
1719
+		}
1720
+
1721
+		// final check is against TKT_uses
1722
+		return $this->verify_can_checkin_against_TKT_uses($DTT_ID);
1723
+	}
1724
+
1725
+
1726
+	/**
1727
+	 * This method verifies whether the user can checkin for the given datetime considering the max uses value set on
1728
+	 * the ticket. To do this,  a query is done to get the count of the datetime records already checked into.  If the
1729
+	 * datetime given does not have a check-in record and checking in for that datetime will exceed the allowed uses,
1730
+	 * then return false.  Otherwise return true.
1731
+	 *
1732
+	 * @param int | EE_Datetime $DTT_OR_ID The datetime the registration is being checked against
1733
+	 * @return bool true means can checkin.  false means cannot checkin.
1734
+	 * @throws EE_Error
1735
+	 * @throws InvalidArgumentException
1736
+	 * @throws InvalidDataTypeException
1737
+	 * @throws InvalidInterfaceException
1738
+	 * @throws ReflectionException
1739
+	 */
1740
+	public function verify_can_checkin_against_TKT_uses($DTT_OR_ID)
1741
+	{
1742
+		$DTT_ID = EEM_Datetime::instance()->ensure_is_ID($DTT_OR_ID);
1743
+
1744
+		if (! $DTT_ID) {
1745
+			return false;
1746
+		}
1747
+
1748
+		$max_uses = $this->ticket() instanceof EE_Ticket ? $this->ticket()->uses() : EE_INF;
1749
+
1750
+		// if max uses is not set or equals infinity then return true cause its not a factor for whether user can
1751
+		// check-in or not.
1752
+		if (! $max_uses || $max_uses === EE_INF) {
1753
+			return true;
1754
+		}
1755
+
1756
+		// does this datetime have a checkin record?  If so, then the dtt count has already been verified so we can just
1757
+		// go ahead and toggle.
1758
+		if (EEM_Checkin::instance()->exists(array(array('REG_ID' => $this->ID(), 'DTT_ID' => $DTT_ID)))) {
1759
+			return true;
1760
+		}
1761
+
1762
+		// made it here so the last check is whether the number of checkins per unique datetime on this registration
1763
+		// disallows further check-ins.
1764
+		$count_unique_dtt_checkins = EEM_Checkin::instance()->count(
1765
+			array(
1766
+				array(
1767
+					'REG_ID' => $this->ID(),
1768
+					'CHK_in' => true,
1769
+				),
1770
+			),
1771
+			'DTT_ID',
1772
+			true
1773
+		);
1774
+		// checkins have already reached their max number of uses
1775
+		// so registrant can NOT checkin
1776
+		if ($count_unique_dtt_checkins >= $max_uses) {
1777
+			EE_Error::add_error(
1778
+				esc_html__(
1779
+					'Check-in denied because number of datetime uses for the ticket has been reached or exceeded.',
1780
+					'event_espresso'
1781
+				),
1782
+				__FILE__,
1783
+				__FUNCTION__,
1784
+				__LINE__
1785
+			);
1786
+			return false;
1787
+		}
1788
+		return true;
1789
+	}
1790
+
1791
+
1792
+	/**
1793
+	 * toggle Check-in status for this registration
1794
+	 * Check-ins are toggled in the following order:
1795
+	 * never checked in -> checked in
1796
+	 * checked in -> checked out
1797
+	 * checked out -> checked in
1798
+	 *
1799
+	 * @param  int $DTT_ID  include specific datetime to toggle Check-in for.
1800
+	 *                      If not included or null, then it is assumed latest datetime is being toggled.
1801
+	 * @param bool $verify  If true then can_checkin() is used to verify whether the person
1802
+	 *                      can be checked in or not.  Otherwise this forces change in checkin status.
1803
+	 * @return bool|int     the chk_in status toggled to OR false if nothing got changed.
1804
+	 * @throws EE_Error
1805
+	 * @throws InvalidArgumentException
1806
+	 * @throws InvalidDataTypeException
1807
+	 * @throws InvalidInterfaceException
1808
+	 * @throws ReflectionException
1809
+	 */
1810
+	public function toggle_checkin_status($DTT_ID = null, $verify = false)
1811
+	{
1812
+		if (empty($DTT_ID)) {
1813
+			$datetime = $this->get_latest_related_datetime();
1814
+			$DTT_ID = $datetime instanceof EE_Datetime ? $datetime->ID() : 0;
1815
+			// verify the registration can checkin for the given DTT_ID
1816
+		} elseif (! $this->can_checkin($DTT_ID, $verify)) {
1817
+			EE_Error::add_error(
1818
+				sprintf(
1819
+					esc_html__(
1820
+						'The given registration (ID:%1$d) can not be checked in to the given DTT_ID (%2$d), because the registration does not have access',
1821
+						'event_espresso'
1822
+					),
1823
+					$this->ID(),
1824
+					$DTT_ID
1825
+				),
1826
+				__FILE__,
1827
+				__FUNCTION__,
1828
+				__LINE__
1829
+			);
1830
+			return false;
1831
+		}
1832
+		$status_paths = array(
1833
+			EE_Checkin::status_checked_never => EE_Checkin::status_checked_in,
1834
+			EE_Checkin::status_checked_in    => EE_Checkin::status_checked_out,
1835
+			EE_Checkin::status_checked_out   => EE_Checkin::status_checked_in,
1836
+		);
1837
+		// start by getting the current status so we know what status we'll be changing to.
1838
+		$cur_status = $this->check_in_status_for_datetime($DTT_ID);
1839
+		$status_to = $status_paths[ $cur_status ];
1840
+		// database only records true for checked IN or false for checked OUT
1841
+		// no record ( null ) means checked in NEVER, but we obviously don't save that
1842
+		$new_status = $status_to === EE_Checkin::status_checked_in;
1843
+		// add relation - note Check-ins are always creating new rows
1844
+		// because we are keeping track of Check-ins over time.
1845
+		// Eventually we'll probably want to show a list table
1846
+		// for the individual Check-ins so that they can be managed.
1847
+		$checkin = EE_Checkin::new_instance(
1848
+			array(
1849
+				'REG_ID' => $this->ID(),
1850
+				'DTT_ID' => $DTT_ID,
1851
+				'CHK_in' => $new_status,
1852
+			)
1853
+		);
1854
+		// if the record could not be saved then return false
1855
+		if ($checkin->save() === 0) {
1856
+			if (WP_DEBUG) {
1857
+				global $wpdb;
1858
+				$error = sprintf(
1859
+					esc_html__(
1860
+						'Registration check in update failed because of the following database error: %1$s%2$s',
1861
+						'event_espresso'
1862
+					),
1863
+					'<br />',
1864
+					$wpdb->last_error
1865
+				);
1866
+			} else {
1867
+				$error = esc_html__(
1868
+					'Registration check in update failed because of an unknown database error',
1869
+					'event_espresso'
1870
+				);
1871
+			}
1872
+			EE_Error::add_error($error, __FILE__, __FUNCTION__, __LINE__);
1873
+			return false;
1874
+		}
1875
+		// Fire a checked_in and checkout_out action.
1876
+		$checked_status = $status_to === EE_Checkin::status_checked_in ? 'checked_in' : 'checked_out';
1877
+		do_action("AHEE__EE_Registration__toggle_checkin_status__{$checked_status}", $this, $DTT_ID);
1878
+		return $status_to;
1879
+	}
1880
+
1881
+
1882
+	/**
1883
+	 * Returns the latest datetime related to this registration (via the ticket attached to the registration).
1884
+	 * "Latest" is defined by the `DTT_EVT_start` column.
1885
+	 *
1886
+	 * @return EE_Datetime|null
1887
+	 * @throws EE_Error
1888
+	 * @throws InvalidArgumentException
1889
+	 * @throws InvalidDataTypeException
1890
+	 * @throws InvalidInterfaceException
1891
+	 * @throws ReflectionException
1892
+	 */
1893
+	public function get_latest_related_datetime()
1894
+	{
1895
+		return EEM_Datetime::instance()->get_one(
1896
+			array(
1897
+				array(
1898
+					'Ticket.Registration.REG_ID' => $this->ID(),
1899
+				),
1900
+				'order_by' => array('DTT_EVT_start' => 'DESC'),
1901
+			)
1902
+		);
1903
+	}
1904
+
1905
+
1906
+	/**
1907
+	 * Returns the earliest datetime related to this registration (via the ticket attached to the registration).
1908
+	 * "Earliest" is defined by the `DTT_EVT_start` column.
1909
+	 *
1910
+	 * @return EE_Base_Class|EE_Soft_Delete_Base_Class|NULL
1911
+	 * @throws EE_Error
1912
+	 * @throws InvalidArgumentException
1913
+	 * @throws InvalidDataTypeException
1914
+	 * @throws InvalidInterfaceException
1915
+	 * @throws ReflectionException
1916
+	 */
1917
+	public function get_earliest_related_datetime()
1918
+	{
1919
+		return EEM_Datetime::instance()->get_one(
1920
+			array(
1921
+				array(
1922
+					'Ticket.Registration.REG_ID' => $this->ID(),
1923
+				),
1924
+				'order_by' => array('DTT_EVT_start' => 'ASC'),
1925
+			)
1926
+		);
1927
+	}
1928
+
1929
+
1930
+	/**
1931
+	 * This method simply returns the check-in status for this registration and the given datetime.
1932
+	 * If neither the datetime nor the checkin values are provided as arguments,
1933
+	 * then this will return the LATEST check-in status for the registration across all datetimes it belongs to.
1934
+	 *
1935
+	 * @param  int       $DTT_ID  The ID of the datetime we're checking against
1936
+	 *                            (if empty we'll get the primary datetime for
1937
+	 *                            this registration (via event) and use it's ID);
1938
+	 * @param EE_Checkin $checkin If present, we use the given checkin object rather than the dtt_id.
1939
+	 * @return int                Integer representing Check-in status.
1940
+	 * @throws EE_Error
1941
+	 * @throws InvalidArgumentException
1942
+	 * @throws InvalidDataTypeException
1943
+	 * @throws InvalidInterfaceException
1944
+	 * @throws ReflectionException
1945
+	 */
1946
+	public function check_in_status_for_datetime($DTT_ID = 0, $checkin = null)
1947
+	{
1948
+		$checkin_query_params = array(
1949
+			'order_by' => array('CHK_timestamp' => 'DESC'),
1950
+		);
1951
+
1952
+		if ($DTT_ID > 0) {
1953
+			$checkin_query_params[0] = array('DTT_ID' => $DTT_ID);
1954
+		}
1955
+
1956
+		// get checkin object (if exists)
1957
+		$checkin = $checkin instanceof EE_Checkin
1958
+			? $checkin
1959
+			: $this->get_first_related('Checkin', $checkin_query_params);
1960
+		if ($checkin instanceof EE_Checkin) {
1961
+			if ($checkin->get('CHK_in')) {
1962
+				return EE_Checkin::status_checked_in; // checked in
1963
+			}
1964
+			return EE_Checkin::status_checked_out; // had checked in but is now checked out.
1965
+		}
1966
+		return EE_Checkin::status_checked_never; // never been checked in
1967
+	}
1968
+
1969
+
1970
+	/**
1971
+	 * This method returns a localized message for the toggled Check-in message.
1972
+	 *
1973
+	 * @param  int $DTT_ID include specific datetime to get the correct Check-in message.  If not included or null,
1974
+	 *                     then it is assumed Check-in for primary datetime was toggled.
1975
+	 * @param bool $error  This just flags that you want an error message returned. This is put in so that the error
1976
+	 *                     message can be customized with the attendee name.
1977
+	 * @return string internationalized message
1978
+	 * @throws EE_Error
1979
+	 * @throws InvalidArgumentException
1980
+	 * @throws InvalidDataTypeException
1981
+	 * @throws InvalidInterfaceException
1982
+	 * @throws ReflectionException
1983
+	 */
1984
+	public function get_checkin_msg($DTT_ID, $error = false)
1985
+	{
1986
+		// let's get the attendee first so we can include the name of the attendee
1987
+		$attendee = $this->get_first_related('Attendee');
1988
+		if ($attendee instanceof EE_Attendee) {
1989
+			if ($error) {
1990
+				return sprintf(
1991
+					esc_html__("%s's check-in status was not changed.", "event_espresso"),
1992
+					$attendee->full_name()
1993
+				);
1994
+			}
1995
+			$cur_status = $this->check_in_status_for_datetime($DTT_ID);
1996
+			// what is the status message going to be?
1997
+			switch ($cur_status) {
1998
+				case EE_Checkin::status_checked_never:
1999
+					return sprintf(
2000
+						esc_html__('%s has been removed from Check-in records', 'event_espresso'),
2001
+						$attendee->full_name()
2002
+					);
2003
+					break;
2004
+				case EE_Checkin::status_checked_in:
2005
+					return sprintf(esc_html__('%s has been checked in', 'event_espresso'), $attendee->full_name());
2006
+					break;
2007
+				case EE_Checkin::status_checked_out:
2008
+					return sprintf(esc_html__('%s has been checked out', 'event_espresso'), $attendee->full_name());
2009
+					break;
2010
+			}
2011
+		}
2012
+		return esc_html__('The check-in status could not be determined.', 'event_espresso');
2013
+	}
2014
+
2015
+
2016
+	/**
2017
+	 * Returns the related EE_Transaction to this registration
2018
+	 *
2019
+	 * @return EE_Transaction
2020
+	 * @throws EE_Error
2021
+	 * @throws EntityNotFoundException
2022
+	 * @throws InvalidArgumentException
2023
+	 * @throws InvalidDataTypeException
2024
+	 * @throws InvalidInterfaceException
2025
+	 * @throws ReflectionException
2026
+	 */
2027
+	public function transaction()
2028
+	{
2029
+		$transaction = $this->get_first_related('Transaction');
2030
+		if (! $transaction instanceof \EE_Transaction) {
2031
+			throw new EntityNotFoundException('Transaction ID', $this->transaction_ID());
2032
+		}
2033
+		return $transaction;
2034
+	}
2035
+
2036
+
2037
+	/**
2038
+	 * get Registration Code
2039
+	 *
2040
+	 * @return mixed
2041
+	 * @throws EE_Error
2042
+	 * @throws InvalidArgumentException
2043
+	 * @throws InvalidDataTypeException
2044
+	 * @throws InvalidInterfaceException
2045
+	 * @throws ReflectionException
2046
+	 */
2047
+	public function reg_code()
2048
+	{
2049
+		return $this->get('REG_code');
2050
+	}
2051
+
2052
+
2053
+	/**
2054
+	 * @return mixed
2055
+	 * @throws EE_Error
2056
+	 * @throws InvalidArgumentException
2057
+	 * @throws InvalidDataTypeException
2058
+	 * @throws InvalidInterfaceException
2059
+	 * @throws ReflectionException
2060
+	 */
2061
+	public function transaction_ID()
2062
+	{
2063
+		return $this->get('TXN_ID');
2064
+	}
2065
+
2066
+
2067
+	/**
2068
+	 * @return int
2069
+	 * @throws EE_Error
2070
+	 * @throws InvalidArgumentException
2071
+	 * @throws InvalidDataTypeException
2072
+	 * @throws InvalidInterfaceException
2073
+	 * @throws ReflectionException
2074
+	 */
2075
+	public function ticket_ID()
2076
+	{
2077
+		return $this->get('TKT_ID');
2078
+	}
2079
+
2080
+
2081
+	/**
2082
+	 * Set Registration Code
2083
+	 *
2084
+	 * @param    string  $REG_code Registration Code
2085
+	 * @param    boolean $use_default
2086
+	 * @throws EE_Error
2087
+	 * @throws InvalidArgumentException
2088
+	 * @throws InvalidDataTypeException
2089
+	 * @throws InvalidInterfaceException
2090
+	 * @throws ReflectionException
2091
+	 */
2092
+	public function set_reg_code($REG_code, $use_default = false)
2093
+	{
2094
+		if (empty($REG_code)) {
2095
+			EE_Error::add_error(
2096
+				esc_html__('REG_code can not be empty.', 'event_espresso'),
2097
+				__FILE__,
2098
+				__FUNCTION__,
2099
+				__LINE__
2100
+			);
2101
+			return;
2102
+		}
2103
+		if (! $this->reg_code()) {
2104
+			parent::set('REG_code', $REG_code, $use_default);
2105
+		} else {
2106
+			EE_Error::doing_it_wrong(
2107
+				__CLASS__ . '::' . __FUNCTION__,
2108
+				esc_html__('Can not change a registration REG_code once it has been set.', 'event_espresso'),
2109
+				'4.6.0'
2110
+			);
2111
+		}
2112
+	}
2113
+
2114
+
2115
+	/**
2116
+	 * Returns all other registrations in the same group as this registrant who have the same ticket option.
2117
+	 * Note, if you want to just get all registrations in the same transaction (group), use:
2118
+	 *    $registration->transaction()->registrations();
2119
+	 *
2120
+	 * @since 4.5.0
2121
+	 * @return EE_Registration[] or empty array if this isn't a group registration.
2122
+	 * @throws EE_Error
2123
+	 * @throws InvalidArgumentException
2124
+	 * @throws InvalidDataTypeException
2125
+	 * @throws InvalidInterfaceException
2126
+	 * @throws ReflectionException
2127
+	 */
2128
+	public function get_all_other_registrations_in_group()
2129
+	{
2130
+		if ($this->group_size() < 2) {
2131
+			return array();
2132
+		}
2133
+
2134
+		$query[0] = array(
2135
+			'TXN_ID' => $this->transaction_ID(),
2136
+			'REG_ID' => array('!=', $this->ID()),
2137
+			'TKT_ID' => $this->ticket_ID(),
2138
+		);
2139
+		/** @var EE_Registration[] $registrations */
2140
+		$registrations = $this->get_model()->get_all($query);
2141
+		return $registrations;
2142
+	}
2143
+
2144
+
2145
+	/**
2146
+	 * Return the link to the admin details for the object.
2147
+	 *
2148
+	 * @return string
2149
+	 * @throws EE_Error
2150
+	 * @throws InvalidArgumentException
2151
+	 * @throws InvalidDataTypeException
2152
+	 * @throws InvalidInterfaceException
2153
+	 * @throws ReflectionException
2154
+	 */
2155
+	public function get_admin_details_link()
2156
+	{
2157
+		EE_Registry::instance()->load_helper('URL');
2158
+		return EEH_URL::add_query_args_and_nonce(
2159
+			array(
2160
+				'page'    => 'espresso_registrations',
2161
+				'action'  => 'view_registration',
2162
+				'_REG_ID' => $this->ID(),
2163
+			),
2164
+			admin_url('admin.php')
2165
+		);
2166
+	}
2167
+
2168
+
2169
+	/**
2170
+	 * Returns the link to the editor for the object.  Sometimes this is the same as the details.
2171
+	 *
2172
+	 * @return string
2173
+	 * @throws EE_Error
2174
+	 * @throws InvalidArgumentException
2175
+	 * @throws InvalidDataTypeException
2176
+	 * @throws InvalidInterfaceException
2177
+	 * @throws ReflectionException
2178
+	 */
2179
+	public function get_admin_edit_link()
2180
+	{
2181
+		return $this->get_admin_details_link();
2182
+	}
2183
+
2184
+
2185
+	/**
2186
+	 * Returns the link to a settings page for the object.
2187
+	 *
2188
+	 * @return string
2189
+	 * @throws EE_Error
2190
+	 * @throws InvalidArgumentException
2191
+	 * @throws InvalidDataTypeException
2192
+	 * @throws InvalidInterfaceException
2193
+	 * @throws ReflectionException
2194
+	 */
2195
+	public function get_admin_settings_link()
2196
+	{
2197
+		return $this->get_admin_details_link();
2198
+	}
2199
+
2200
+
2201
+	/**
2202
+	 * Returns the link to the "overview" for the object (typically the "list table" view).
2203
+	 *
2204
+	 * @return string
2205
+	 * @throws EE_Error
2206
+	 * @throws InvalidArgumentException
2207
+	 * @throws InvalidDataTypeException
2208
+	 * @throws InvalidInterfaceException
2209
+	 * @throws ReflectionException
2210
+	 */
2211
+	public function get_admin_overview_link()
2212
+	{
2213
+		EE_Registry::instance()->load_helper('URL');
2214
+		return EEH_URL::add_query_args_and_nonce(
2215
+			array(
2216
+				'page' => 'espresso_registrations',
2217
+			),
2218
+			admin_url('admin.php')
2219
+		);
2220
+	}
2221
+
2222
+
2223
+	/**
2224
+	 * @param array $query_params
2225
+	 * @return EE_Base_Class[]|EE_Registration[]
2226
+	 * @throws EE_Error
2227
+	 * @throws InvalidArgumentException
2228
+	 * @throws InvalidDataTypeException
2229
+	 * @throws InvalidInterfaceException
2230
+	 * @throws ReflectionException
2231
+	 */
2232
+	public function payments($query_params = array())
2233
+	{
2234
+		return $this->get_many_related('Payment', $query_params);
2235
+	}
2236
+
2237
+
2238
+	/**
2239
+	 * @param array $query_params
2240
+	 * @return EE_Base_Class[]|EE_Registration_Payment[]
2241
+	 * @throws EE_Error
2242
+	 * @throws InvalidArgumentException
2243
+	 * @throws InvalidDataTypeException
2244
+	 * @throws InvalidInterfaceException
2245
+	 * @throws ReflectionException
2246
+	 */
2247
+	public function registration_payments($query_params = array())
2248
+	{
2249
+		return $this->get_many_related('Registration_Payment', $query_params);
2250
+	}
2251
+
2252
+
2253
+	/**
2254
+	 * This grabs the payment method corresponding to the last payment made for the amount owing on the registration.
2255
+	 * Note: if there are no payments on the registration there will be no payment method returned.
2256
+	 *
2257
+	 * @return EE_Payment|EE_Payment_Method|null
2258
+	 * @throws EE_Error
2259
+	 * @throws InvalidArgumentException
2260
+	 * @throws InvalidDataTypeException
2261
+	 * @throws InvalidInterfaceException
2262
+	 */
2263
+	public function payment_method()
2264
+	{
2265
+		return EEM_Payment_Method::instance()->get_last_used_for_registration($this);
2266
+	}
2267
+
2268
+
2269
+	/**
2270
+	 * @return \EE_Line_Item
2271
+	 * @throws EE_Error
2272
+	 * @throws EntityNotFoundException
2273
+	 * @throws InvalidArgumentException
2274
+	 * @throws InvalidDataTypeException
2275
+	 * @throws InvalidInterfaceException
2276
+	 * @throws ReflectionException
2277
+	 */
2278
+	public function ticket_line_item()
2279
+	{
2280
+		$ticket = $this->ticket();
2281
+		$transaction = $this->transaction();
2282
+		$line_item = null;
2283
+		$ticket_line_items = \EEH_Line_Item::get_line_items_by_object_type_and_IDs(
2284
+			$transaction->total_line_item(),
2285
+			'Ticket',
2286
+			array($ticket->ID())
2287
+		);
2288
+		foreach ($ticket_line_items as $ticket_line_item) {
2289
+			if (
2290
+				$ticket_line_item instanceof \EE_Line_Item
2291
+				&& $ticket_line_item->OBJ_type() === 'Ticket'
2292
+				&& $ticket_line_item->OBJ_ID() === $ticket->ID()
2293
+			) {
2294
+				$line_item = $ticket_line_item;
2295
+				break;
2296
+			}
2297
+		}
2298
+		if (! ($line_item instanceof \EE_Line_Item && $line_item->OBJ_type() === 'Ticket')) {
2299
+			throw new EntityNotFoundException('Line Item Ticket ID', $ticket->ID());
2300
+		}
2301
+		return $line_item;
2302
+	}
2303
+
2304
+
2305
+	/**
2306
+	 * Soft Deletes this model object.
2307
+	 *
2308
+	 * @param string $source function name that called this method
2309
+	 * @return boolean | int
2310
+	 * @throws DomainException
2311
+	 * @throws EE_Error
2312
+	 * @throws EntityNotFoundException
2313
+	 * @throws InvalidArgumentException
2314
+	 * @throws InvalidDataTypeException
2315
+	 * @throws InvalidInterfaceException
2316
+	 * @throws ReflectionException
2317
+	 * @throws RuntimeException
2318
+	 * @throws UnexpectedEntityException
2319
+	 */
2320
+	public function delete()
2321
+	{
2322
+		if ($this->update_extra_meta(EE_Registration::PRE_TRASH_REG_STATUS_KEY, $this->status_ID()) === true) {
2323
+			$this->set_status(EEM_Registration::status_id_cancelled);
2324
+		}
2325
+		return parent::delete();
2326
+	}
2327
+
2328
+
2329
+	/**
2330
+	 * Restores whatever the previous status was on a registration before it was trashed (if possible)
2331
+	 *
2332
+	 * @param string $source function name that called this method
2333
+	 * @return bool|int
2334
+	 * @throws DomainException
2335
+	 * @throws EE_Error
2336
+	 * @throws EntityNotFoundException
2337
+	 * @throws InvalidArgumentException
2338
+	 * @throws InvalidDataTypeException
2339
+	 * @throws InvalidInterfaceException
2340
+	 * @throws ReflectionException
2341
+	 * @throws RuntimeException
2342
+	 * @throws UnexpectedEntityException
2343
+	 */
2344
+	public function restore()
2345
+	{
2346
+		$previous_status = $this->get_extra_meta(
2347
+			EE_Registration::PRE_TRASH_REG_STATUS_KEY,
2348
+			true,
2349
+			EEM_Registration::status_id_cancelled
2350
+		);
2351
+		if ($previous_status) {
2352
+			$this->delete_extra_meta(EE_Registration::PRE_TRASH_REG_STATUS_KEY);
2353
+			$this->set_status($previous_status);
2354
+		}
2355
+		return parent::restore();
2356
+	}
2357
+
2358
+
2359
+	/**
2360
+	 * possibly toggle Registration status based on comparison of REG_paid vs REG_final_price
2361
+	 *
2362
+	 * @param  boolean $trigger_set_status_logic EE_Registration::set_status() can trigger additional logic
2363
+	 *                                           depending on whether the reg status changes to or from "Approved"
2364
+	 * @return boolean whether the Registration status was updated
2365
+	 * @throws DomainException
2366
+	 * @throws EE_Error
2367
+	 * @throws EntityNotFoundException
2368
+	 * @throws InvalidArgumentException
2369
+	 * @throws InvalidDataTypeException
2370
+	 * @throws InvalidInterfaceException
2371
+	 * @throws ReflectionException
2372
+	 * @throws RuntimeException
2373
+	 * @throws UnexpectedEntityException
2374
+	 */
2375
+	public function updateStatusBasedOnTotalPaid($trigger_set_status_logic = true)
2376
+	{
2377
+		$paid = $this->paid();
2378
+		$price = $this->final_price();
2379
+		switch (true) {
2380
+			// overpaid or paid
2381
+			case EEH_Money::compare_floats($paid, $price, '>'):
2382
+			case EEH_Money::compare_floats($paid, $price):
2383
+				$new_status = EEM_Registration::status_id_approved;
2384
+				break;
2385
+			//  underpaid
2386
+			case EEH_Money::compare_floats($paid, $price, '<'):
2387
+				$new_status = EEM_Registration::status_id_pending_payment;
2388
+				break;
2389
+			// uhhh Houston...
2390
+			default:
2391
+				throw new RuntimeException(
2392
+					esc_html__('The total paid calculation for this registration is inaccurate.', 'event_espresso')
2393
+				);
2394
+		}
2395
+		if ($new_status !== $this->status_ID()) {
2396
+			if ($trigger_set_status_logic) {
2397
+				return $this->set_status($new_status);
2398
+			}
2399
+			parent::set('STS_ID', $new_status);
2400
+			return true;
2401
+		}
2402
+		return false;
2403
+	}
2404
+
2405
+
2406
+	/*************************** DEPRECATED ***************************/
2407
+
2408
+
2409
+	/**
2410
+	 * @deprecated
2411
+	 * @since     4.7.0
2412
+	 */
2413
+	public function price_paid()
2414
+	{
2415
+		EE_Error::doing_it_wrong(
2416
+			'EE_Registration::price_paid()',
2417
+			esc_html__(
2418
+				'This method is deprecated, please use EE_Registration::final_price() instead.',
2419
+				'event_espresso'
2420
+			),
2421
+			'4.7.0'
2422
+		);
2423
+		return $this->final_price();
2424
+	}
2425
+
2426
+
2427
+	/**
2428
+	 * @deprecated
2429
+	 * @since     4.7.0
2430
+	 * @param    float $REG_final_price
2431
+	 * @throws EE_Error
2432
+	 * @throws EntityNotFoundException
2433
+	 * @throws InvalidArgumentException
2434
+	 * @throws InvalidDataTypeException
2435
+	 * @throws InvalidInterfaceException
2436
+	 * @throws ReflectionException
2437
+	 * @throws RuntimeException
2438
+	 * @throws DomainException
2439
+	 */
2440
+	public function set_price_paid($REG_final_price = 0.00)
2441
+	{
2442
+		EE_Error::doing_it_wrong(
2443
+			'EE_Registration::set_price_paid()',
2444
+			esc_html__(
2445
+				'This method is deprecated, please use EE_Registration::set_final_price() instead.',
2446
+				'event_espresso'
2447
+			),
2448
+			'4.7.0'
2449
+		);
2450
+		$this->set_final_price($REG_final_price);
2451
+	}
2452
+
2453
+
2454
+	/**
2455
+	 * @deprecated
2456
+	 * @since 4.7.0
2457
+	 * @return string
2458
+	 * @throws EE_Error
2459
+	 * @throws InvalidArgumentException
2460
+	 * @throws InvalidDataTypeException
2461
+	 * @throws InvalidInterfaceException
2462
+	 * @throws ReflectionException
2463
+	 */
2464
+	public function pretty_price_paid()
2465
+	{
2466
+		EE_Error::doing_it_wrong(
2467
+			'EE_Registration::pretty_price_paid()',
2468
+			esc_html__(
2469
+				'This method is deprecated, please use EE_Registration::pretty_final_price() instead.',
2470
+				'event_espresso'
2471
+			),
2472
+			'4.7.0'
2473
+		);
2474
+		return $this->pretty_final_price();
2475
+	}
2476
+
2477
+
2478
+	/**
2479
+	 * Gets the primary datetime related to this registration via the related Event to this registration
2480
+	 *
2481
+	 * @deprecated 4.9.17
2482
+	 * @return EE_Datetime
2483
+	 * @throws EE_Error
2484
+	 * @throws EntityNotFoundException
2485
+	 * @throws InvalidArgumentException
2486
+	 * @throws InvalidDataTypeException
2487
+	 * @throws InvalidInterfaceException
2488
+	 * @throws ReflectionException
2489
+	 */
2490
+	public function get_related_primary_datetime()
2491
+	{
2492
+		EE_Error::doing_it_wrong(
2493
+			__METHOD__,
2494
+			esc_html__(
2495
+				'Use EE_Registration::get_latest_related_datetime() or EE_Registration::get_earliest_related_datetime()',
2496
+				'event_espresso'
2497
+			),
2498
+			'4.9.17',
2499
+			'5.0.0'
2500
+		);
2501
+		return $this->event()->primary_datetime();
2502
+	}
2503
+
2504
+	/**
2505
+	 * Returns the contact's name (or "Unknown" if there is no contact.)
2506
+	 * @since 4.10.12.p
2507
+	 * @return string
2508
+	 * @throws EE_Error
2509
+	 * @throws InvalidArgumentException
2510
+	 * @throws InvalidDataTypeException
2511
+	 * @throws InvalidInterfaceException
2512
+	 * @throws ReflectionException
2513
+	 */
2514
+	public function name()
2515
+	{
2516
+		return $this->attendeeName();
2517
+	}
2518 2518
 }
Please login to merge, or discard this patch.
admin_pages/venues/Venues_Admin_List_Table.class.php 2 patches
Spacing   +6 added lines, -6 removed lines patch added patch discarded remove patch
@@ -116,7 +116,7 @@  discard block
 block discarded – undo
116 116
      */
117 117
     public function column_id(EE_Venue $venue): string
118 118
     {
119
-        return $venue->ID() . '  <span class="show-on-mobile-view-only">' . $venue->name() . '</span>';
119
+        return $venue->ID().'  <span class="show-on-mobile-view-only">'.$venue->name().'</span>';
120 120
     }
121 121
 
122 122
 
@@ -141,10 +141,10 @@  discard block
 block discarded – undo
141 141
         $content = EE_Registry::instance()->CAP->current_user_can('ee_edit_venue', 'espresso_venues_edit', $venue->ID())
142 142
             ? '
143 143
             <strong>
144
-                <a class="row-title" href="' . $edit_link . '">' . stripslashes_deep($venue->name()) . '</a>
144
+                <a class="row-title" href="' . $edit_link.'">'.stripslashes_deep($venue->name()).'</a>
145 145
             </strong>'
146 146
             : $venue->name();
147
-        $content .= $venue->status() == 'draft' ? ' - <span class="post-state">' . $statuses['draft'] . '</span>' : '';
147
+        $content .= $venue->status() == 'draft' ? ' - <span class="post-state">'.$statuses['draft'].'</span>' : '';
148 148
         $content .= $this->row_actions(array_filter($actions));
149 149
         return $content;
150 150
     }
@@ -183,7 +183,7 @@  discard block
 block discarded – undo
183 183
                 );
184 184
             }
185 185
         } else {
186
-            $actions['view']        = $this->getVenueActionLink(
186
+            $actions['view'] = $this->getVenueActionLink(
187 187
                 $venue,
188 188
                 'view',
189 189
                 esc_html__('View', 'event_espresso'),
@@ -193,7 +193,7 @@  discard block
 block discarded – undo
193 193
                 '',
194 194
                 get_permalink($venue->ID())
195 195
             );
196
-            $actions['edit']        = $this->getVenueActionLink(
196
+            $actions['edit'] = $this->getVenueActionLink(
197 197
                 $venue,
198 198
                 'edit',
199 199
                 esc_html__('Edit', 'event_espresso'),
@@ -254,7 +254,7 @@  discard block
 block discarded – undo
254 254
      */
255 255
     public function column_shortcode(EE_Venue $venue): string
256 256
     {
257
-        return '[ESPRESSO_VENUE id=' . $venue->ID() . ']';
257
+        return '[ESPRESSO_VENUE id='.$venue->ID().']';
258 258
     }
259 259
 
260 260
 
Please login to merge, or discard this patch.
Indentation   +281 added lines, -281 removed lines patch added patch discarded remove patch
@@ -13,289 +13,289 @@
 block discarded – undo
13 13
  */
14 14
 class Venues_Admin_List_Table extends EE_Admin_List_Table
15 15
 {
16
-    /**
17
-     * @var   int
18
-     */
19
-    private $related_event_count = 0;
20
-
21
-
22
-    /**
23
-     * @param $admin_page
24
-     */
25
-    public function __construct($admin_page)
26
-    {
27
-        parent::__construct($admin_page);
28
-    }
29
-
30
-
31
-    protected function _setup_data()
32
-    {
33
-        $this->_data = $this->_admin_page->get_venues($this->_per_page);
34
-        $this->_all_data_count = $this->_admin_page->get_venues($this->_per_page, true);
35
-    }
36
-
37
-
38
-    protected function _set_properties()
39
-    {
40
-        $this->_wp_list_args = array(
41
-            'singular' => esc_html__('Event Venue', 'event_espresso'),
42
-            'plural'   => esc_html__('Event Venues', 'event_espresso'),
43
-            'ajax'     => true, // for now,
44
-            'screen'   => $this->_admin_page->get_current_screen()->id,
45
-        );
46
-
47
-        $this->_columns = array(
48
-            'cb'       => '<input type="checkbox" />',
49
-            'id'       => esc_html__('ID', 'event_espresso'),
50
-            'name'     => esc_html__('Name', 'event_espresso'),
51
-            'address'  => esc_html__('Address', 'event_espresso'),
52
-            'city'     => esc_html__('City', 'event_espresso'),
53
-            'capacity' => esc_html__('Capacity', 'event_espresso'),
54
-            // 'shortcode' => esc_html__('Shortcode', 'event_espresso'),
55
-        );
56
-
57
-        $this->_sortable_columns = array(
58
-            'id'       => array('id' => true),
59
-            'name'     => array('name' => false),
60
-            'city'     => array('city' => false),
61
-            'capacity' => array('capacity' => false),
62
-        );
63
-
64
-        $this->_hidden_columns = array();
65
-    }
66
-
67
-
68
-    // todo... add _venue_status in here (which we'll define a EE_Admin_CPT_List_Table for common properties)
69
-
70
-
71
-    /**
72
-     * @return array
73
-     */
74
-    protected function _get_table_filters(): array
75
-    {
76
-        return array();
77
-    }
78
-
79
-
80
-    /**
81
-     * @throws EE_Error
82
-     */
83
-    protected function _add_view_counts()
84
-    {
85
-        $this->_views['all']['count'] = EEM_Venue::instance()->count();
86
-        if (EE_Registry::instance()->CAP->current_user_can('ee_delete_venues', 'espresso_venues_trash_venues')) {
87
-            $this->_views['trash']['count'] = EEM_Venue::instance()->count_deleted();
88
-        }
89
-    }
90
-
91
-
92
-    /**
93
-     * @param EE_Venue|null $venue
94
-     * @return string
95
-     * @throws EE_Error
96
-     * @throws ReflectionException
97
-     */
98
-    public function column_cb($venue): string
99
-    {
100
-        $this->related_event_count = $venue->count_related('Event');
101
-        return $this->related_event_count > 0 && $venue->status() === 'trash'
102
-            ? '<span class="ee-lock-icon"></span>'
103
-            : sprintf(
104
-                '<input type="checkbox" name="venue_id[]" value="%s" />',
105
-                $venue->ID()
106
-            );
107
-    }
108
-
109
-
110
-    /**
111
-     * @param EE_Venue $venue
112
-     * @return string
113
-     * @throws EE_Error
114
-     * @throws ReflectionException
115
-     */
116
-    public function column_id(EE_Venue $venue): string
117
-    {
118
-        return $venue->ID() . '  <span class="show-on-mobile-view-only">' . $venue->name() . '</span>';
119
-    }
120
-
121
-
122
-    /**
123
-     * @param EE_Venue $venue
124
-     * @return string
125
-     * @throws EE_Error
126
-     * @throws ReflectionException
127
-     */
128
-    public function column_name(EE_Venue $venue): string
129
-    {
130
-        $edit_link = EE_Admin_Page::add_query_args_and_nonce(
131
-            [
132
-                'action' => 'edit',
133
-                'post'   => $venue->ID(),
134
-            ],
135
-            EE_VENUES_ADMIN_URL
136
-        );
137
-
138
-        $statuses = EEM_Venue::instance()->get_status_array();
139
-        $actions = $this->_column_name_action_setup($venue);
140
-        $content = EE_Registry::instance()->CAP->current_user_can('ee_edit_venue', 'espresso_venues_edit', $venue->ID())
141
-            ? '
16
+	/**
17
+	 * @var   int
18
+	 */
19
+	private $related_event_count = 0;
20
+
21
+
22
+	/**
23
+	 * @param $admin_page
24
+	 */
25
+	public function __construct($admin_page)
26
+	{
27
+		parent::__construct($admin_page);
28
+	}
29
+
30
+
31
+	protected function _setup_data()
32
+	{
33
+		$this->_data = $this->_admin_page->get_venues($this->_per_page);
34
+		$this->_all_data_count = $this->_admin_page->get_venues($this->_per_page, true);
35
+	}
36
+
37
+
38
+	protected function _set_properties()
39
+	{
40
+		$this->_wp_list_args = array(
41
+			'singular' => esc_html__('Event Venue', 'event_espresso'),
42
+			'plural'   => esc_html__('Event Venues', 'event_espresso'),
43
+			'ajax'     => true, // for now,
44
+			'screen'   => $this->_admin_page->get_current_screen()->id,
45
+		);
46
+
47
+		$this->_columns = array(
48
+			'cb'       => '<input type="checkbox" />',
49
+			'id'       => esc_html__('ID', 'event_espresso'),
50
+			'name'     => esc_html__('Name', 'event_espresso'),
51
+			'address'  => esc_html__('Address', 'event_espresso'),
52
+			'city'     => esc_html__('City', 'event_espresso'),
53
+			'capacity' => esc_html__('Capacity', 'event_espresso'),
54
+			// 'shortcode' => esc_html__('Shortcode', 'event_espresso'),
55
+		);
56
+
57
+		$this->_sortable_columns = array(
58
+			'id'       => array('id' => true),
59
+			'name'     => array('name' => false),
60
+			'city'     => array('city' => false),
61
+			'capacity' => array('capacity' => false),
62
+		);
63
+
64
+		$this->_hidden_columns = array();
65
+	}
66
+
67
+
68
+	// todo... add _venue_status in here (which we'll define a EE_Admin_CPT_List_Table for common properties)
69
+
70
+
71
+	/**
72
+	 * @return array
73
+	 */
74
+	protected function _get_table_filters(): array
75
+	{
76
+		return array();
77
+	}
78
+
79
+
80
+	/**
81
+	 * @throws EE_Error
82
+	 */
83
+	protected function _add_view_counts()
84
+	{
85
+		$this->_views['all']['count'] = EEM_Venue::instance()->count();
86
+		if (EE_Registry::instance()->CAP->current_user_can('ee_delete_venues', 'espresso_venues_trash_venues')) {
87
+			$this->_views['trash']['count'] = EEM_Venue::instance()->count_deleted();
88
+		}
89
+	}
90
+
91
+
92
+	/**
93
+	 * @param EE_Venue|null $venue
94
+	 * @return string
95
+	 * @throws EE_Error
96
+	 * @throws ReflectionException
97
+	 */
98
+	public function column_cb($venue): string
99
+	{
100
+		$this->related_event_count = $venue->count_related('Event');
101
+		return $this->related_event_count > 0 && $venue->status() === 'trash'
102
+			? '<span class="ee-lock-icon"></span>'
103
+			: sprintf(
104
+				'<input type="checkbox" name="venue_id[]" value="%s" />',
105
+				$venue->ID()
106
+			);
107
+	}
108
+
109
+
110
+	/**
111
+	 * @param EE_Venue $venue
112
+	 * @return string
113
+	 * @throws EE_Error
114
+	 * @throws ReflectionException
115
+	 */
116
+	public function column_id(EE_Venue $venue): string
117
+	{
118
+		return $venue->ID() . '  <span class="show-on-mobile-view-only">' . $venue->name() . '</span>';
119
+	}
120
+
121
+
122
+	/**
123
+	 * @param EE_Venue $venue
124
+	 * @return string
125
+	 * @throws EE_Error
126
+	 * @throws ReflectionException
127
+	 */
128
+	public function column_name(EE_Venue $venue): string
129
+	{
130
+		$edit_link = EE_Admin_Page::add_query_args_and_nonce(
131
+			[
132
+				'action' => 'edit',
133
+				'post'   => $venue->ID(),
134
+			],
135
+			EE_VENUES_ADMIN_URL
136
+		);
137
+
138
+		$statuses = EEM_Venue::instance()->get_status_array();
139
+		$actions = $this->_column_name_action_setup($venue);
140
+		$content = EE_Registry::instance()->CAP->current_user_can('ee_edit_venue', 'espresso_venues_edit', $venue->ID())
141
+			? '
142 142
             <strong>
143 143
                 <a class="row-title" href="' . $edit_link . '">' . stripslashes_deep($venue->name()) . '</a>
144 144
             </strong>'
145
-            : $venue->name();
146
-        $content .= $venue->status() == 'draft' ? ' - <span class="post-state">' . $statuses['draft'] . '</span>' : '';
147
-        $content .= $this->row_actions(array_filter($actions));
148
-        return $content;
149
-    }
150
-
151
-
152
-    /**
153
-     * Used to setup the actions for the Venue name column
154
-     *
155
-     * @param EE_Venue $venue
156
-     * @return array()
157
-     * @throws EE_Error
158
-     * @throws ReflectionException
159
-     */
160
-    protected function _column_name_action_setup(EE_Venue $venue): array
161
-    {
162
-        $actions = [];
163
-        if ($venue->status() === 'trash') {
164
-            $actions['restore_venue'] = $this->getVenueActionLink(
165
-                $venue,
166
-                'restore_venue',
167
-                esc_html__('Restore from Trash', 'event_espresso'),
168
-                /* Translators: The name of the venue */
169
-                esc_attr__('Restore Venue (%s) from Trash', 'event_espresso'),
170
-                'ee_delete_venue',
171
-                'espresso_venues_restore_venue'
172
-            );
173
-            if ($this->related_event_count === 0) {
174
-                $actions['delete_venue'] = $this->getVenueActionLink(
175
-                    $venue,
176
-                    'delete_venue',
177
-                    esc_html__('Delete Permanently', 'event_espresso'),
178
-                    /* Translators: The name of the venue */
179
-                    esc_attr__('Permanently Delete Venue (%s)', 'event_espresso'),
180
-                    'ee_delete_venue',
181
-                    'espresso_venues_delete_venue'
182
-                );
183
-            }
184
-        } else {
185
-            $actions['view']        = $this->getVenueActionLink(
186
-                $venue,
187
-                'view',
188
-                esc_html__('View', 'event_espresso'),
189
-                /* Translators: The name of the venue */
190
-                esc_attr__('View Venue (%s)', 'event_espresso'),
191
-                '',
192
-                '',
193
-                get_permalink($venue->ID())
194
-            );
195
-            $actions['edit']        = $this->getVenueActionLink(
196
-                $venue,
197
-                'edit',
198
-                esc_html__('Edit', 'event_espresso'),
199
-                /* Translators: The name of the venue */
200
-                esc_attr__('Edit Venue (%s)', 'event_espresso'),
201
-                'ee_edit_venue',
202
-                'espresso_venues_edit'
203
-            );
204
-            $actions['trash_venue'] = $this->getVenueActionLink(
205
-                $venue,
206
-                'trash_venue',
207
-                esc_html__('Trash', 'event_espresso'),
208
-                /* Translators: The name of the venue */
209
-                esc_attr__('Trash Venue (%s)', 'event_espresso'),
210
-                'ee_delete_venue',
211
-                'espresso_venues_trash_venue'
212
-            );
213
-        }
214
-        return $actions;
215
-    }
216
-
217
-
218
-    /**
219
-     * @param EE_Venue $venue
220
-     * @return string
221
-     */
222
-    public function column_address(EE_Venue $venue): string
223
-    {
224
-        return $venue->address();
225
-    }
226
-
227
-
228
-    /**
229
-     * @param EE_Venue $venue
230
-     * @return string
231
-     */
232
-    public function column_city(EE_Venue $venue): string
233
-    {
234
-        return $venue->city();
235
-    }
236
-
237
-
238
-    /**
239
-     * @param EE_Venue $venue
240
-     * @return int|string
241
-     * @throws EE_Error
242
-     * @throws ReflectionException
243
-     */
244
-    public function column_capacity(EE_Venue $venue)
245
-    {
246
-        return $venue->capacity();
247
-    }
248
-
249
-
250
-    /**
251
-     * @param EE_Venue $venue
252
-     * @return string
253
-     * @throws EE_Error
254
-     * @throws ReflectionException
255
-     */
256
-    public function column_shortcode(EE_Venue $venue): string
257
-    {
258
-        return '[ESPRESSO_VENUE id=' . $venue->ID() . ']';
259
-    }
260
-
261
-
262
-    /**
263
-     * @param EE_Venue    $venue
264
-     * @param string      $action
265
-     * @param string      $link_text
266
-     * @param string      $aria_label
267
-     * @param string      $cap
268
-     * @param string      $context
269
-     * @param string|null $action_url
270
-     * @return string
271
-     * @throws EE_Error
272
-     * @throws ReflectionException
273
-     * @since 4.10.38.p
274
-     */
275
-    public function getVenueActionLink(
276
-        EE_Venue $venue,
277
-        string $action,
278
-        string $link_text,
279
-        string $aria_label,
280
-        string $cap,
281
-        string $context,
282
-        ?string $action_url = null
283
-    ): string {
284
-        if ($cap && ! EE_Registry::instance()->CAP->current_user_can($cap, $context, $venue->ID())) {
285
-            return '';
286
-        }
287
-        $key = $action === 'edit' ? 'post' : 'VNU_ID';
288
-        // if supplied an $action_url, then use that, otherwise build one using the $action param
289
-        $action_url = $action_url
290
-            ?: EE_Admin_Page::add_query_args_and_nonce(
291
-                [
292
-                    'action' => $action,
293
-                    $key     => $venue->ID(),
294
-                ],
295
-                EE_VENUES_ADMIN_URL
296
-            );
297
-        $aria_label = sprintf($aria_label, $venue->name());
298
-        return "
145
+			: $venue->name();
146
+		$content .= $venue->status() == 'draft' ? ' - <span class="post-state">' . $statuses['draft'] . '</span>' : '';
147
+		$content .= $this->row_actions(array_filter($actions));
148
+		return $content;
149
+	}
150
+
151
+
152
+	/**
153
+	 * Used to setup the actions for the Venue name column
154
+	 *
155
+	 * @param EE_Venue $venue
156
+	 * @return array()
157
+	 * @throws EE_Error
158
+	 * @throws ReflectionException
159
+	 */
160
+	protected function _column_name_action_setup(EE_Venue $venue): array
161
+	{
162
+		$actions = [];
163
+		if ($venue->status() === 'trash') {
164
+			$actions['restore_venue'] = $this->getVenueActionLink(
165
+				$venue,
166
+				'restore_venue',
167
+				esc_html__('Restore from Trash', 'event_espresso'),
168
+				/* Translators: The name of the venue */
169
+				esc_attr__('Restore Venue (%s) from Trash', 'event_espresso'),
170
+				'ee_delete_venue',
171
+				'espresso_venues_restore_venue'
172
+			);
173
+			if ($this->related_event_count === 0) {
174
+				$actions['delete_venue'] = $this->getVenueActionLink(
175
+					$venue,
176
+					'delete_venue',
177
+					esc_html__('Delete Permanently', 'event_espresso'),
178
+					/* Translators: The name of the venue */
179
+					esc_attr__('Permanently Delete Venue (%s)', 'event_espresso'),
180
+					'ee_delete_venue',
181
+					'espresso_venues_delete_venue'
182
+				);
183
+			}
184
+		} else {
185
+			$actions['view']        = $this->getVenueActionLink(
186
+				$venue,
187
+				'view',
188
+				esc_html__('View', 'event_espresso'),
189
+				/* Translators: The name of the venue */
190
+				esc_attr__('View Venue (%s)', 'event_espresso'),
191
+				'',
192
+				'',
193
+				get_permalink($venue->ID())
194
+			);
195
+			$actions['edit']        = $this->getVenueActionLink(
196
+				$venue,
197
+				'edit',
198
+				esc_html__('Edit', 'event_espresso'),
199
+				/* Translators: The name of the venue */
200
+				esc_attr__('Edit Venue (%s)', 'event_espresso'),
201
+				'ee_edit_venue',
202
+				'espresso_venues_edit'
203
+			);
204
+			$actions['trash_venue'] = $this->getVenueActionLink(
205
+				$venue,
206
+				'trash_venue',
207
+				esc_html__('Trash', 'event_espresso'),
208
+				/* Translators: The name of the venue */
209
+				esc_attr__('Trash Venue (%s)', 'event_espresso'),
210
+				'ee_delete_venue',
211
+				'espresso_venues_trash_venue'
212
+			);
213
+		}
214
+		return $actions;
215
+	}
216
+
217
+
218
+	/**
219
+	 * @param EE_Venue $venue
220
+	 * @return string
221
+	 */
222
+	public function column_address(EE_Venue $venue): string
223
+	{
224
+		return $venue->address();
225
+	}
226
+
227
+
228
+	/**
229
+	 * @param EE_Venue $venue
230
+	 * @return string
231
+	 */
232
+	public function column_city(EE_Venue $venue): string
233
+	{
234
+		return $venue->city();
235
+	}
236
+
237
+
238
+	/**
239
+	 * @param EE_Venue $venue
240
+	 * @return int|string
241
+	 * @throws EE_Error
242
+	 * @throws ReflectionException
243
+	 */
244
+	public function column_capacity(EE_Venue $venue)
245
+	{
246
+		return $venue->capacity();
247
+	}
248
+
249
+
250
+	/**
251
+	 * @param EE_Venue $venue
252
+	 * @return string
253
+	 * @throws EE_Error
254
+	 * @throws ReflectionException
255
+	 */
256
+	public function column_shortcode(EE_Venue $venue): string
257
+	{
258
+		return '[ESPRESSO_VENUE id=' . $venue->ID() . ']';
259
+	}
260
+
261
+
262
+	/**
263
+	 * @param EE_Venue    $venue
264
+	 * @param string      $action
265
+	 * @param string      $link_text
266
+	 * @param string      $aria_label
267
+	 * @param string      $cap
268
+	 * @param string      $context
269
+	 * @param string|null $action_url
270
+	 * @return string
271
+	 * @throws EE_Error
272
+	 * @throws ReflectionException
273
+	 * @since 4.10.38.p
274
+	 */
275
+	public function getVenueActionLink(
276
+		EE_Venue $venue,
277
+		string $action,
278
+		string $link_text,
279
+		string $aria_label,
280
+		string $cap,
281
+		string $context,
282
+		?string $action_url = null
283
+	): string {
284
+		if ($cap && ! EE_Registry::instance()->CAP->current_user_can($cap, $context, $venue->ID())) {
285
+			return '';
286
+		}
287
+		$key = $action === 'edit' ? 'post' : 'VNU_ID';
288
+		// if supplied an $action_url, then use that, otherwise build one using the $action param
289
+		$action_url = $action_url
290
+			?: EE_Admin_Page::add_query_args_and_nonce(
291
+				[
292
+					'action' => $action,
293
+					$key     => $venue->ID(),
294
+				],
295
+				EE_VENUES_ADMIN_URL
296
+			);
297
+		$aria_label = sprintf($aria_label, $venue->name());
298
+		return "
299 299
         <a href='$action_url' aria-label='$aria_label'>$link_text</a>";
300
-    }
300
+	}
301 301
 }
Please login to merge, or discard this patch.
reg_steps/payment_options/EE_SPCO_Reg_Step_Payment_Options.class.php 2 patches
Indentation   +2828 added lines, -2828 removed lines patch added patch discarded remove patch
@@ -19,2832 +19,2832 @@
 block discarded – undo
19 19
  */
20 20
 class EE_SPCO_Reg_Step_Payment_Options extends EE_SPCO_Reg_Step
21 21
 {
22
-    /**
23
-     * @var EE_Line_Item_Display $Line_Item_Display
24
-     */
25
-    protected $line_item_display;
26
-
27
-    /**
28
-     * @var boolean $handle_IPN_in_this_request
29
-     */
30
-    protected $handle_IPN_in_this_request = false;
31
-
32
-
33
-    /**
34
-     *    set_hooks - for hooking into EE Core, other modules, etc
35
-     *
36
-     * @access    public
37
-     * @return    void
38
-     */
39
-    public static function set_hooks()
40
-    {
41
-        add_filter(
42
-            'FHEE__SPCO__EE_Line_Item_Filter_Collection',
43
-            ['EE_SPCO_Reg_Step_Payment_Options', 'add_spco_line_item_filters']
44
-        );
45
-        add_action(
46
-            'wp_ajax_switch_spco_billing_form',
47
-            ['EE_SPCO_Reg_Step_Payment_Options', 'switch_spco_billing_form']
48
-        );
49
-        add_action(
50
-            'wp_ajax_nopriv_switch_spco_billing_form',
51
-            ['EE_SPCO_Reg_Step_Payment_Options', 'switch_spco_billing_form']
52
-        );
53
-        add_action('wp_ajax_save_payer_details', ['EE_SPCO_Reg_Step_Payment_Options', 'save_payer_details']);
54
-        add_action(
55
-            'wp_ajax_nopriv_save_payer_details',
56
-            ['EE_SPCO_Reg_Step_Payment_Options', 'save_payer_details']
57
-        );
58
-        add_action(
59
-            'wp_ajax_get_transaction_details_for_gateways',
60
-            ['EE_SPCO_Reg_Step_Payment_Options', 'get_transaction_details']
61
-        );
62
-        add_action(
63
-            'wp_ajax_nopriv_get_transaction_details_for_gateways',
64
-            ['EE_SPCO_Reg_Step_Payment_Options', 'get_transaction_details']
65
-        );
66
-        add_filter(
67
-            'FHEE__EED_Recaptcha___bypass_recaptcha__bypass_request_params_array',
68
-            ['EE_SPCO_Reg_Step_Payment_Options', 'bypass_recaptcha_for_load_payment_method'],
69
-            10,
70
-            1
71
-        );
72
-    }
73
-
74
-
75
-    /**
76
-     *    ajax switch_spco_billing_form
77
-     *
78
-     */
79
-    public static function switch_spco_billing_form()
80
-    {
81
-        EED_Single_Page_Checkout::process_ajax_request('switch_payment_method');
82
-    }
83
-
84
-
85
-    /**
86
-     *    ajax save_payer_details
87
-     *
88
-     */
89
-    public static function save_payer_details()
90
-    {
91
-        EED_Single_Page_Checkout::process_ajax_request('save_payer_details_via_ajax');
92
-    }
93
-
94
-
95
-    /**
96
-     *    ajax get_transaction_details
97
-     *
98
-     */
99
-    public static function get_transaction_details()
100
-    {
101
-        EED_Single_Page_Checkout::process_ajax_request('get_transaction_details_for_gateways');
102
-    }
103
-
104
-
105
-    /**
106
-     * bypass_recaptcha_for_load_payment_method
107
-     *
108
-     * @access public
109
-     * @return array
110
-     * @throws InvalidArgumentException
111
-     * @throws InvalidDataTypeException
112
-     * @throws InvalidInterfaceException
113
-     */
114
-    public static function bypass_recaptcha_for_load_payment_method()
115
-    {
116
-        return [
117
-            'EESID'  => EE_Registry::instance()->SSN->id(),
118
-            'step'   => 'payment_options',
119
-            'action' => 'spco_billing_form',
120
-        ];
121
-    }
122
-
123
-
124
-    /**
125
-     *    class constructor
126
-     *
127
-     * @access    public
128
-     * @param EE_Checkout $checkout
129
-     */
130
-    public function __construct(EE_Checkout $checkout)
131
-    {
132
-        $this->request   = EED_Single_Page_Checkout::getRequest();
133
-        $this->_slug     = 'payment_options';
134
-        $this->_name     = esc_html__('Payment Options', 'event_espresso');
135
-        $this->_template = SPCO_REG_STEPS_PATH . $this->_slug . '/payment_options_main.template.php';
136
-        $this->checkout  = $checkout;
137
-        $this->_reset_success_message();
138
-        $this->set_instructions(
139
-            esc_html__(
140
-                'Please select a method of payment and provide any necessary billing information before proceeding.',
141
-                'event_espresso'
142
-            )
143
-        );
144
-    }
145
-
146
-
147
-    /**
148
-     * @return null
149
-     */
150
-    public function line_item_display()
151
-    {
152
-        return $this->line_item_display;
153
-    }
154
-
155
-
156
-    /**
157
-     * @param null $line_item_display
158
-     */
159
-    public function set_line_item_display($line_item_display)
160
-    {
161
-        $this->line_item_display = $line_item_display;
162
-    }
163
-
164
-
165
-    /**
166
-     * @return boolean
167
-     */
168
-    public function handle_IPN_in_this_request()
169
-    {
170
-        return $this->handle_IPN_in_this_request;
171
-    }
172
-
173
-
174
-    /**
175
-     * @param boolean $handle_IPN_in_this_request
176
-     */
177
-    public function set_handle_IPN_in_this_request($handle_IPN_in_this_request)
178
-    {
179
-        $this->handle_IPN_in_this_request = filter_var($handle_IPN_in_this_request, FILTER_VALIDATE_BOOLEAN);
180
-    }
181
-
182
-
183
-    /**
184
-     * translate_js_strings
185
-     *
186
-     * @return void
187
-     */
188
-    public function translate_js_strings()
189
-    {
190
-        EE_Registry::$i18n_js_strings['no_payment_method']      = esc_html__(
191
-            'Please select a method of payment in order to continue.',
192
-            'event_espresso'
193
-        );
194
-        EE_Registry::$i18n_js_strings['invalid_payment_method'] = esc_html__(
195
-            'A valid method of payment could not be determined. Please refresh the page and try again.',
196
-            'event_espresso'
197
-        );
198
-        EE_Registry::$i18n_js_strings['forwarding_to_offsite']  = esc_html__(
199
-            'Forwarding to Secure Payment Provider.',
200
-            'event_espresso'
201
-        );
202
-    }
203
-
204
-
205
-    /**
206
-     * enqueue_styles_and_scripts
207
-     *
208
-     * @return void
209
-     * @throws EE_Error
210
-     * @throws InvalidArgumentException
211
-     * @throws InvalidDataTypeException
212
-     * @throws InvalidInterfaceException
213
-     * @throws ReflectionException
214
-     */
215
-    public function enqueue_styles_and_scripts()
216
-    {
217
-        $transaction = $this->checkout->transaction;
218
-        // if the transaction isn't set or nothing is owed on it, don't enqueue any JS
219
-        if (! $transaction instanceof EE_Transaction || EEH_Money::compare_floats($transaction->remaining(), 0)) {
220
-            return;
221
-        }
222
-        foreach (
223
-            EEM_Payment_Method::instance()->get_all_for_transaction(
224
-                $transaction,
225
-                EEM_Payment_Method::scope_cart
226
-            ) as $payment_method
227
-        ) {
228
-            $type_obj = $payment_method->type_obj();
229
-            if ($type_obj instanceof EE_PMT_Base) {
230
-                $billing_form = $type_obj->generate_new_billing_form($transaction);
231
-                if ($billing_form instanceof EE_Form_Section_Proper) {
232
-                    $billing_form->enqueue_js();
233
-                }
234
-            }
235
-        }
236
-    }
237
-
238
-
239
-    /**
240
-     * initialize_reg_step
241
-     *
242
-     * @return bool
243
-     * @throws EE_Error
244
-     * @throws InvalidArgumentException
245
-     * @throws ReflectionException
246
-     * @throws InvalidDataTypeException
247
-     * @throws InvalidInterfaceException
248
-     */
249
-    public function initialize_reg_step()
250
-    {
251
-        // TODO: if /when we implement donations, then this will need overriding
252
-        if (
253
-            // don't need payment options for:
254
-            // registrations made via the admin
255
-            // completed transactions
256
-            // overpaid transactions
257
-            // $ 0.00 transactions(no payment required)
258
-            ! $this->checkout->payment_required()
259
-            // but do NOT remove if current action being called belongs to this reg step
260
-            && ! is_callable([$this, $this->checkout->action])
261
-            && ! $this->completed()
262
-        ) {
263
-            // and if so, then we no longer need the Payment Options step
264
-            if ($this->is_current_step()) {
265
-                $this->checkout->generate_reg_form = false;
266
-            }
267
-            $this->checkout->remove_reg_step($this->_slug);
268
-            // DEBUG LOG
269
-            // $this->checkout->log( __CLASS__, __FUNCTION__, __LINE__ );
270
-            return false;
271
-        }
272
-        // load EEM_Payment_Method
273
-        EE_Registry::instance()->load_model('Payment_Method');
274
-        // get all active payment methods
275
-        $this->checkout->available_payment_methods = EEM_Payment_Method::instance()->get_all_for_transaction(
276
-            $this->checkout->transaction,
277
-            EEM_Payment_Method::scope_cart
278
-        );
279
-        return true;
280
-    }
281
-
282
-
283
-    /**
284
-     * @return EE_Form_Section_Proper
285
-     * @throws EE_Error
286
-     * @throws InvalidArgumentException
287
-     * @throws ReflectionException
288
-     * @throws EntityNotFoundException
289
-     * @throws InvalidDataTypeException
290
-     * @throws InvalidInterfaceException
291
-     * @throws InvalidStatusException
292
-     */
293
-    public function generate_reg_form()
294
-    {
295
-        // reset in case someone changes their mind
296
-        $this->_reset_selected_method_of_payment();
297
-        // set some defaults
298
-        $this->checkout->selected_method_of_payment = 'payments_closed';
299
-        $registrations_requiring_payment            = [];
300
-        $registrations_for_free_events              = [];
301
-        $registrations_requiring_pre_approval       = [];
302
-        $sold_out_events                            = [];
303
-        $insufficient_spaces_available              = [];
304
-        $no_payment_required                        = true;
305
-        // loop thru registrations to gather info
306
-        $registrations         = $this->checkout->transaction->registrations($this->checkout->reg_cache_where_params);
307
-        $ejected_registrations = EE_SPCO_Reg_Step_Payment_Options::find_registrations_that_lost_their_space(
308
-            $registrations,
309
-            $this->checkout->revisit
310
-        );
311
-        foreach ($registrations as $REG_ID => $registration) {
312
-            /** @var $registration EE_Registration */
313
-            // has this registration lost it's space ?
314
-            if (isset($ejected_registrations[ $REG_ID ])) {
315
-                if ($registration->event()->is_sold_out() || $registration->event()->is_sold_out(true)) {
316
-                    $sold_out_events[ $registration->event()->ID() ] = $registration->event();
317
-                } else {
318
-                    $insufficient_spaces_available[ $registration->event()->ID() ] = $registration->event();
319
-                }
320
-                continue;
321
-            }
322
-            // event requires admin approval
323
-            if ($registration->status_ID() === EEM_Registration::status_id_not_approved) {
324
-                // add event to list of events with pre-approval reg status
325
-                $registrations_requiring_pre_approval[ $REG_ID ] = $registration;
326
-                do_action(
327
-                    'AHEE__EE_SPCO_Reg_Step_Payment_Options__generate_reg_form__event_requires_pre_approval',
328
-                    $registration->event(),
329
-                    $this
330
-                );
331
-                continue;
332
-            }
333
-            if (
334
-                $this->checkout->revisit
335
-                && $registration->status_ID() !== EEM_Registration::status_id_approved
336
-                && (
337
-                    $registration->event()->is_sold_out()
338
-                    || $registration->event()->is_sold_out(true)
339
-                )
340
-            ) {
341
-                // add event to list of events that are sold out
342
-                $sold_out_events[ $registration->event()->ID() ] = $registration->event();
343
-                do_action(
344
-                    'AHEE__EE_SPCO_Reg_Step_Payment_Options__generate_reg_form__sold_out_event',
345
-                    $registration->event(),
346
-                    $this
347
-                );
348
-                continue;
349
-            }
350
-            // are they allowed to pay now and is there monies owing?
351
-            if ($registration->owes_monies_and_can_pay()) {
352
-                $registrations_requiring_payment[ $REG_ID ] = $registration;
353
-                do_action(
354
-                    'AHEE__EE_SPCO_Reg_Step_Payment_Options__generate_reg_form__event_requires_payment',
355
-                    $registration->event(),
356
-                    $this
357
-                );
358
-            } elseif (
359
-                ! $this->checkout->revisit
360
-                      && $registration->status_ID() !== EEM_Registration::status_id_not_approved
361
-                      && $registration->ticket()->is_free()
362
-            ) {
363
-                $registrations_for_free_events[ $registration->ticket()->ID() ] = $registration;
364
-            }
365
-        }
366
-        $subsections = [];
367
-        // now decide which template to load
368
-        if (! empty($sold_out_events)) {
369
-            $subsections['sold_out_events'] = $this->_sold_out_events($sold_out_events);
370
-        }
371
-        if (! empty($insufficient_spaces_available)) {
372
-            $subsections['insufficient_space'] = $this->_insufficient_spaces_available(
373
-                $insufficient_spaces_available
374
-            );
375
-        }
376
-        if (! empty($registrations_requiring_pre_approval)) {
377
-            $subsections['registrations_requiring_pre_approval'] = $this->_registrations_requiring_pre_approval(
378
-                $registrations_requiring_pre_approval
379
-            );
380
-        }
381
-        if (! empty($registrations_for_free_events)) {
382
-            $subsections['no_payment_required'] = $this->_no_payment_required($registrations_for_free_events);
383
-        }
384
-        if (! empty($registrations_requiring_payment)) {
385
-            if ($this->checkout->amount_owing > 0) {
386
-                // autoload Line_Item_Display classes
387
-                EEH_Autoloader::register_line_item_filter_autoloaders();
388
-                $line_item_filter_processor = new EE_Line_Item_Filter_Processor(
389
-                    apply_filters(
390
-                        'FHEE__SPCO__EE_Line_Item_Filter_Collection',
391
-                        new EE_Line_Item_Filter_Collection()
392
-                    ),
393
-                    $this->checkout->cart->get_grand_total()
394
-                );
395
-                /** @var EE_Line_Item $filtered_line_item_tree */
396
-                $filtered_line_item_tree = $line_item_filter_processor->process();
397
-                EEH_Autoloader::register_line_item_display_autoloaders();
398
-                $this->set_line_item_display(new EE_Line_Item_Display('spco'));
399
-                $subsections['payment_options'] = $this->_display_payment_options(
400
-                    $this->line_item_display->display_line_item(
401
-                        $filtered_line_item_tree,
402
-                        ['registrations' => $registrations]
403
-                    )
404
-                );
405
-                $this->checkout->amount_owing   = $filtered_line_item_tree->total();
406
-                $this->_apply_registration_payments_to_amount_owing($registrations);
407
-            }
408
-            $no_payment_required = false;
409
-        } else {
410
-            $this->_hide_reg_step_submit_button_if_revisit();
411
-        }
412
-        $this->_save_selected_method_of_payment();
413
-
414
-        $subsections['default_hidden_inputs'] = $this->reg_step_hidden_inputs();
415
-        $subsections['extra_hidden_inputs']   = $this->_extra_hidden_inputs($no_payment_required);
416
-
417
-        return new EE_Form_Section_Proper(
418
-            [
419
-                'name'            => $this->reg_form_name(),
420
-                'html_id'         => $this->reg_form_name(),
421
-                'subsections'     => $subsections,
422
-                'layout_strategy' => new EE_No_Layout(),
423
-            ]
424
-        );
425
-    }
426
-
427
-
428
-    /**
429
-     * add line item filters required for this reg step
430
-     * these filters are applied via this line in EE_SPCO_Reg_Step_Payment_Options::set_hooks():
431
-     *        add_filter( 'FHEE__SPCO__EE_Line_Item_Filter_Collection', array( 'EE_SPCO_Reg_Step_Payment_Options',
432
-     *        'add_spco_line_item_filters' ) ); so any code that wants to use the same set of filters during the
433
-     *        payment options reg step, can apply these filters via the following: apply_filters(
434
-     *        'FHEE__SPCO__EE_Line_Item_Filter_Collection', new EE_Line_Item_Filter_Collection() ) or to an existing
435
-     *        filter collection by passing that instead of instantiating a new collection
436
-     *
437
-     * @param EE_Line_Item_Filter_Collection $line_item_filter_collection
438
-     * @return EE_Line_Item_Filter_Collection
439
-     * @throws EE_Error
440
-     * @throws InvalidArgumentException
441
-     * @throws ReflectionException
442
-     * @throws EntityNotFoundException
443
-     * @throws InvalidDataTypeException
444
-     * @throws InvalidInterfaceException
445
-     * @throws InvalidStatusException
446
-     */
447
-    public static function add_spco_line_item_filters(EE_Line_Item_Filter_Collection $line_item_filter_collection)
448
-    {
449
-        if (! EE_Registry::instance()->SSN instanceof EE_Session) {
450
-            return $line_item_filter_collection;
451
-        }
452
-        if (! EE_Registry::instance()->SSN->checkout() instanceof EE_Checkout) {
453
-            return $line_item_filter_collection;
454
-        }
455
-        if (! EE_Registry::instance()->SSN->checkout()->transaction instanceof EE_Transaction) {
456
-            return $line_item_filter_collection;
457
-        }
458
-        $line_item_filter_collection->add(
459
-            new EE_Billable_Line_Item_Filter(
460
-                EE_SPCO_Reg_Step_Payment_Options::remove_ejected_registrations(
461
-                    EE_Registry::instance()->SSN->checkout()->transaction->registrations(
462
-                        EE_Registry::instance()->SSN->checkout()->reg_cache_where_params
463
-                    )
464
-                )
465
-            )
466
-        );
467
-        $line_item_filter_collection->add(new EE_Non_Zero_Line_Item_Filter());
468
-        return $line_item_filter_collection;
469
-    }
470
-
471
-
472
-    /**
473
-     * remove_ejected_registrations
474
-     * if a registrant has lost their potential space at an event due to lack of payment,
475
-     * then this method removes them from the list of registrations being paid for during this request
476
-     *
477
-     * @param EE_Registration[] $registrations
478
-     * @return EE_Registration[]
479
-     * @throws EE_Error
480
-     * @throws InvalidArgumentException
481
-     * @throws ReflectionException
482
-     * @throws EntityNotFoundException
483
-     * @throws InvalidDataTypeException
484
-     * @throws InvalidInterfaceException
485
-     * @throws InvalidStatusException
486
-     */
487
-    public static function remove_ejected_registrations(array $registrations)
488
-    {
489
-        $ejected_registrations = EE_SPCO_Reg_Step_Payment_Options::find_registrations_that_lost_their_space(
490
-            $registrations,
491
-            EE_Registry::instance()->SSN->checkout()->revisit
492
-        );
493
-        foreach ($registrations as $REG_ID => $registration) {
494
-            // has this registration lost it's space ?
495
-            if (isset($ejected_registrations[ $REG_ID ])) {
496
-                unset($registrations[ $REG_ID ]);
497
-            }
498
-        }
499
-        return $registrations;
500
-    }
501
-
502
-
503
-    /**
504
-     * find_registrations_that_lost_their_space
505
-     * If a registrant chooses an offline payment method like Invoice,
506
-     * then no space is reserved for them at the event until they fully pay fo that site
507
-     * (unless the event's default reg status is set to APPROVED)
508
-     * if a registrant then later returns to pay, but the number of spaces available has been reduced due to sales,
509
-     * then this method will determine which registrations have lost the ability to complete the reg process.
510
-     *
511
-     * @param EE_Registration[] $registrations
512
-     * @param bool              $revisit
513
-     * @return array
514
-     * @throws EE_Error
515
-     * @throws InvalidArgumentException
516
-     * @throws ReflectionException
517
-     * @throws EntityNotFoundException
518
-     * @throws InvalidDataTypeException
519
-     * @throws InvalidInterfaceException
520
-     * @throws InvalidStatusException
521
-     */
522
-    public static function find_registrations_that_lost_their_space(array $registrations, $revisit = false)
523
-    {
524
-        // registrations per event
525
-        $event_reg_count = [];
526
-        // spaces left per event
527
-        $event_spaces_remaining = [];
528
-        // tickets left sorted by ID
529
-        $tickets_remaining = [];
530
-        // registrations that have lost their space
531
-        $ejected_registrations = [];
532
-        foreach ($registrations as $REG_ID => $registration) {
533
-            if (
534
-                $registration->status_ID() === EEM_Registration::status_id_approved
535
-                || apply_filters(
536
-                    'FHEE__EE_SPCO_Reg_Step_Payment_Options__find_registrations_that_lost_their_space__allow_reg_payment',
537
-                    false,
538
-                    $registration,
539
-                    $revisit
540
-                )
541
-            ) {
542
-                continue;
543
-            }
544
-            $EVT_ID = $registration->event_ID();
545
-            $ticket = $registration->ticket();
546
-            if (! isset($tickets_remaining[ $ticket->ID() ])) {
547
-                $tickets_remaining[ $ticket->ID() ] = $ticket->remaining();
548
-            }
549
-            if ($tickets_remaining[ $ticket->ID() ] > 0) {
550
-                if (! isset($event_reg_count[ $EVT_ID ])) {
551
-                    $event_reg_count[ $EVT_ID ] = 0;
552
-                }
553
-                $event_reg_count[ $EVT_ID ]++;
554
-                if (! isset($event_spaces_remaining[ $EVT_ID ])) {
555
-                    $event_spaces_remaining[ $EVT_ID ] = $registration->event()->spaces_remaining_for_sale();
556
-                }
557
-            }
558
-            if (
559
-                $revisit
560
-                && ($tickets_remaining[ $ticket->ID() ] === 0
561
-                    || $event_reg_count[ $EVT_ID ] > $event_spaces_remaining[ $EVT_ID ]
562
-                )
563
-            ) {
564
-                $ejected_registrations[ $REG_ID ] = $registration->event();
565
-                if ($registration->status_ID() !== EEM_Registration::status_id_wait_list) {
566
-                    /** @type EE_Registration_Processor $registration_processor */
567
-                    $registration_processor = EE_Registry::instance()->load_class('Registration_Processor');
568
-                    // at this point, we should have enough details about the registrant to consider the registration
569
-                    // NOT incomplete
570
-                    $registration_processor->manually_update_registration_status(
571
-                        $registration,
572
-                        EEM_Registration::status_id_wait_list
573
-                    );
574
-                }
575
-            }
576
-        }
577
-        return $ejected_registrations;
578
-    }
579
-
580
-
581
-    /**
582
-     * _hide_reg_step_submit_button
583
-     * removes the html for the reg step submit button
584
-     * by replacing it with an empty string via filter callback
585
-     *
586
-     * @return void
587
-     */
588
-    protected function _adjust_registration_status_if_event_old_sold()
589
-    {
590
-    }
591
-
592
-
593
-    /**
594
-     * _hide_reg_step_submit_button
595
-     * removes the html for the reg step submit button
596
-     * by replacing it with an empty string via filter callback
597
-     *
598
-     * @return void
599
-     */
600
-    protected function _hide_reg_step_submit_button_if_revisit()
601
-    {
602
-        if ($this->checkout->revisit) {
603
-            add_filter('FHEE__EE_SPCO_Reg_Step__reg_step_submit_button__sbmt_btn_html', '__return_empty_string');
604
-        }
605
-    }
606
-
607
-
608
-    /**
609
-     * sold_out_events
610
-     * displays notices regarding events that have sold out since hte registrant first signed up
611
-     *
612
-     * @param EE_Event[] $sold_out_events_array
613
-     * @return EE_Form_Section_Proper
614
-     * @throws EE_Error
615
-     */
616
-    private function _sold_out_events($sold_out_events_array = [])
617
-    {
618
-        // set some defaults
619
-        $this->checkout->selected_method_of_payment = 'events_sold_out';
620
-        $sold_out_events                            = '';
621
-        foreach ($sold_out_events_array as $sold_out_event) {
622
-            $sold_out_events .= EEH_HTML::li(
623
-                EEH_HTML::span(
624
-                    '  ' . $sold_out_event->name(),
625
-                    '',
626
-                    'dashicons dashicons-marker ee-icon-size-16 pink-text'
627
-                )
628
-            );
629
-        }
630
-        return new EE_Form_Section_Proper(
631
-            [
632
-                'layout_strategy' => new EE_Template_Layout(
633
-                    [
634
-                        'layout_template_file' => SPCO_REG_STEPS_PATH
635
-                                                  . $this->_slug
636
-                                                  . '/sold_out_events.template.php',
637
-                        'template_args'        => apply_filters(
638
-                            'FHEE__EE_SPCO_Reg_Step_Payment_Options___sold_out_events__template_args',
639
-                            [
640
-                                'sold_out_events'     => $sold_out_events,
641
-                                'sold_out_events_msg' => apply_filters(
642
-                                    'FHEE__EE_SPCO_Reg_Step_Payment_Options___sold_out_events__sold_out_events_msg',
643
-                                    sprintf(
644
-                                        esc_html__(
645
-                                            'It appears that the event you were about to make a payment for has sold out since you first registered. If you have already made a partial payment towards this event, please contact the event administrator for a refund.%3$s%3$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.%2$s',
646
-                                            'event_espresso'
647
-                                        ),
648
-                                        '<strong>',
649
-                                        '</strong>',
650
-                                        '<br />'
651
-                                    )
652
-                                ),
653
-                            ]
654
-                        ),
655
-                    ]
656
-                ),
657
-            ]
658
-        );
659
-    }
660
-
661
-
662
-    /**
663
-     * _insufficient_spaces_available
664
-     * displays notices regarding events that do not have enough remaining spaces
665
-     * to satisfy the current number of registrations looking to pay
666
-     *
667
-     * @param EE_Event[] $insufficient_spaces_events_array
668
-     * @return EE_Form_Section_Proper
669
-     * @throws EE_Error
670
-     * @throws ReflectionException
671
-     */
672
-    private function _insufficient_spaces_available($insufficient_spaces_events_array = [])
673
-    {
674
-        // set some defaults
675
-        $this->checkout->selected_method_of_payment = 'invoice';
676
-        $insufficient_space_events                  = '';
677
-        foreach ($insufficient_spaces_events_array as $event) {
678
-            if ($event instanceof EE_Event) {
679
-                $insufficient_space_events .= EEH_HTML::li(
680
-                    EEH_HTML::span(' ' . $event->name(), '', 'dashicons dashicons-marker ee-icon-size-16 pink-text')
681
-                );
682
-            }
683
-        }
684
-        return new EE_Form_Section_Proper(
685
-            [
686
-                'subsections'     => [
687
-                    'default_hidden_inputs' => $this->reg_step_hidden_inputs(),
688
-                    'extra_hidden_inputs'   => $this->_extra_hidden_inputs(),
689
-                ],
690
-                'layout_strategy' => new EE_Template_Layout(
691
-                    [
692
-                        'layout_template_file' => SPCO_REG_STEPS_PATH
693
-                                                  . $this->_slug
694
-                                                  . '/sold_out_events.template.php',
695
-                        'template_args'        => apply_filters(
696
-                            'FHEE__EE_SPCO_Reg_Step_Payment_Options___insufficient_spaces_available__template_args',
697
-                            [
698
-                                'sold_out_events'     => $insufficient_space_events,
699
-                                'sold_out_events_msg' => apply_filters(
700
-                                    'FHEE__EE_SPCO_Reg_Step_Payment_Options___insufficient_spaces_available__insufficient_space_msg',
701
-                                    esc_html__(
702
-                                        'It appears that the event you were about to make a payment for has sold additional tickets since you first registered, and there are no longer enough spaces left to accommodate your selections. You may continue to pay and secure the available space(s) remaining, or simply cancel if you no longer wish to purchase. If you have already made a partial payment towards this event, please contact the event administrator for a refund.',
703
-                                        'event_espresso'
704
-                                    )
705
-                                ),
706
-                            ]
707
-                        ),
708
-                    ]
709
-                ),
710
-            ]
711
-        );
712
-    }
713
-
714
-
715
-    /**
716
-     * registrations_requiring_pre_approval
717
-     *
718
-     * @param array $registrations_requiring_pre_approval
719
-     * @return EE_Form_Section_Proper
720
-     * @throws EE_Error
721
-     * @throws EntityNotFoundException
722
-     * @throws ReflectionException
723
-     */
724
-    private function _registrations_requiring_pre_approval($registrations_requiring_pre_approval = [])
725
-    {
726
-        $events_requiring_pre_approval = [];
727
-        foreach ($registrations_requiring_pre_approval as $registration) {
728
-            if ($registration instanceof EE_Registration && $registration->event() instanceof EE_Event) {
729
-                $events_requiring_pre_approval[ $registration->event()->ID() ] = EEH_HTML::li(
730
-                    EEH_HTML::span(
731
-                        '',
732
-                        '',
733
-                        'dashicons dashicons-marker ee-icon-size-16 orange-text'
734
-                    )
735
-                    . EEH_HTML::span($registration->event()->name(), '', 'orange-text')
736
-                );
737
-            }
738
-        }
739
-        return new EE_Form_Section_Proper(
740
-            [
741
-                'layout_strategy' => new EE_Template_Layout(
742
-                    [
743
-                        'layout_template_file' => SPCO_REG_STEPS_PATH
744
-                                                  . $this->_slug
745
-                                                  . '/events_requiring_pre_approval.template.php', // layout_template
746
-                        'template_args'        => apply_filters(
747
-                            'FHEE__EE_SPCO_Reg_Step_Payment_Options___sold_out_events__template_args',
748
-                            [
749
-                                'events_requiring_pre_approval'     => implode('', $events_requiring_pre_approval),
750
-                                'events_requiring_pre_approval_msg' => apply_filters(
751
-                                    'FHEE__EE_SPCO_Reg_Step_Payment_Options___events_requiring_pre_approval__events_requiring_pre_approval_msg',
752
-                                    esc_html__(
753
-                                        'The following events do not require payment at this time and will not be billed during this transaction. Billing will only occur after the attendee has been approved by the event organizer. You will be notified when your registration has been processed. If this is a free event, then no billing will occur.',
754
-                                        'event_espresso'
755
-                                    )
756
-                                ),
757
-                            ]
758
-                        ),
759
-                    ]
760
-                ),
761
-            ]
762
-        );
763
-    }
764
-
765
-
766
-    /**
767
-     * _no_payment_required
768
-     *
769
-     * @param EE_Event[] $registrations_for_free_events
770
-     * @return EE_Form_Section_Proper
771
-     * @throws EE_Error
772
-     */
773
-    private function _no_payment_required($registrations_for_free_events = [])
774
-    {
775
-        // set some defaults
776
-        $this->checkout->selected_method_of_payment = 'no_payment_required';
777
-        // generate no_payment_required form
778
-        return new EE_Form_Section_Proper(
779
-            [
780
-                'layout_strategy' => new EE_Template_Layout(
781
-                    [
782
-                        'layout_template_file' => SPCO_REG_STEPS_PATH
783
-                                                  . $this->_slug
784
-                                                  . '/no_payment_required.template.php', // layout_template
785
-                        'template_args'        => apply_filters(
786
-                            'FHEE__EE_SPCO_Reg_Step_Payment_Options___no_payment_required__template_args',
787
-                            [
788
-                                'revisit'                       => $this->checkout->revisit,
789
-                                'registrations'                 => [],
790
-                                'ticket_count'                  => [],
791
-                                'registrations_for_free_events' => $registrations_for_free_events,
792
-                                'no_payment_required_msg'       => EEH_HTML::p(
793
-                                    esc_html__('This is a free event, so no billing will occur.', 'event_espresso')
794
-                                ),
795
-                            ]
796
-                        ),
797
-                    ]
798
-                ),
799
-            ]
800
-        );
801
-    }
802
-
803
-
804
-    /**
805
-     * _display_payment_options
806
-     *
807
-     * @param string $transaction_details
808
-     * @return EE_Form_Section_Proper
809
-     * @throws EE_Error
810
-     * @throws InvalidArgumentException
811
-     * @throws InvalidDataTypeException
812
-     * @throws InvalidInterfaceException
813
-     */
814
-    private function _display_payment_options($transaction_details = '')
815
-    {
816
-        // has method_of_payment been set by no-js user?
817
-        $this->checkout->selected_method_of_payment = $this->_get_selected_method_of_payment();
818
-        // build payment options form
819
-        return apply_filters(
820
-            'FHEE__EE_SPCO_Reg_Step_Payment_Options___display_payment_options__payment_options_form',
821
-            new EE_Form_Section_Proper(
822
-                [
823
-                    'subsections'     => [
824
-                        'before_payment_options' => apply_filters(
825
-                            'FHEE__EE_SPCO_Reg_Step_Payment_Options___display_payment_options__before_payment_options',
826
-                            new EE_Form_Section_Proper(
827
-                                ['layout_strategy' => new EE_Div_Per_Section_Layout()]
828
-                            )
829
-                        ),
830
-                        'payment_options'        => $this->_setup_payment_options(),
831
-                        'after_payment_options'  => apply_filters(
832
-                            'FHEE__EE_SPCO_Reg_Step_Payment_Options___display_payment_options__after_payment_options',
833
-                            new EE_Form_Section_Proper(
834
-                                ['layout_strategy' => new EE_Div_Per_Section_Layout()]
835
-                            )
836
-                        ),
837
-                    ],
838
-                    'layout_strategy' => new EE_Template_Layout(
839
-                        [
840
-                            'layout_template_file' => $this->_template,
841
-                            'template_args'        => apply_filters(
842
-                                'FHEE__EE_SPCO_Reg_Step_Payment_Options___display_payment_options__template_args',
843
-                                [
844
-                                    'reg_count'                 => $this->line_item_display->total_items(),
845
-                                    'transaction_details'       => $transaction_details,
846
-                                    'available_payment_methods' => [],
847
-                                ]
848
-                            ),
849
-                        ]
850
-                    ),
851
-                ]
852
-            )
853
-        );
854
-    }
855
-
856
-
857
-    /**
858
-     * _extra_hidden_inputs
859
-     *
860
-     * @param bool $no_payment_required
861
-     * @return EE_Form_Section_Proper
862
-     * @throws EE_Error
863
-     * @throws ReflectionException
864
-     */
865
-    private function _extra_hidden_inputs($no_payment_required = true)
866
-    {
867
-        return new EE_Form_Section_Proper(
868
-            [
869
-                'html_id'         => 'ee-' . $this->slug() . '-extra-hidden-inputs',
870
-                'layout_strategy' => new EE_Div_Per_Section_Layout(),
871
-                'subsections'     => [
872
-                    'spco_no_payment_required' => new EE_Hidden_Input(
873
-                        [
874
-                            'normalization_strategy' => new EE_Boolean_Normalization(),
875
-                            'html_name'              => 'spco_no_payment_required',
876
-                            'html_id'                => 'spco-no-payment-required-payment_options',
877
-                            'default'                => $no_payment_required,
878
-                        ]
879
-                    ),
880
-                    'spco_transaction_id'      => new EE_Fixed_Hidden_Input(
881
-                        [
882
-                            'normalization_strategy' => new EE_Int_Normalization(),
883
-                            'html_name'              => 'spco_transaction_id',
884
-                            'html_id'                => 'spco-transaction-id',
885
-                            'default'                => $this->checkout->transaction->ID(),
886
-                        ]
887
-                    ),
888
-                ],
889
-            ]
890
-        );
891
-    }
892
-
893
-
894
-    /**
895
-     *    _apply_registration_payments_to_amount_owing
896
-     *
897
-     * @param array $registrations
898
-     * @throws EE_Error
899
-     */
900
-    protected function _apply_registration_payments_to_amount_owing(array $registrations)
901
-    {
902
-        $payments = [];
903
-        foreach ($registrations as $registration) {
904
-            if ($registration instanceof EE_Registration && $registration->owes_monies_and_can_pay()) {
905
-                $payments += $registration->registration_payments();
906
-            }
907
-        }
908
-        if (! empty($payments)) {
909
-            foreach ($payments as $payment) {
910
-                if ($payment instanceof EE_Registration_Payment) {
911
-                    $this->checkout->amount_owing -= $payment->amount();
912
-                }
913
-            }
914
-        }
915
-    }
916
-
917
-
918
-    /**
919
-     *    _reset_selected_method_of_payment
920
-     *
921
-     * @access    private
922
-     * @param bool $force_reset
923
-     * @return void
924
-     * @throws InvalidArgumentException
925
-     * @throws InvalidDataTypeException
926
-     * @throws InvalidInterfaceException
927
-     */
928
-    private function _reset_selected_method_of_payment($force_reset = false)
929
-    {
930
-        /** @var RequestInterface $request */
931
-        $request              = LoaderFactory::getLoader()->getShared(RequestInterface::class);
932
-        $reset_payment_method = $request->getRequestParam('reset_payment_method', $force_reset, 'bool');
933
-        if ($reset_payment_method) {
934
-            $this->checkout->selected_method_of_payment = null;
935
-            $this->checkout->payment_method             = null;
936
-            $this->checkout->billing_form               = null;
937
-            $this->_save_selected_method_of_payment();
938
-        }
939
-    }
940
-
941
-
942
-    /**
943
-     * _save_selected_method_of_payment
944
-     * stores the selected_method_of_payment in the session
945
-     * so that it's available for all subsequent requests including AJAX
946
-     *
947
-     * @access        private
948
-     * @param string $selected_method_of_payment
949
-     * @return void
950
-     * @throws InvalidArgumentException
951
-     * @throws InvalidDataTypeException
952
-     * @throws InvalidInterfaceException
953
-     */
954
-    private function _save_selected_method_of_payment($selected_method_of_payment = '')
955
-    {
956
-        $selected_method_of_payment = ! empty($selected_method_of_payment)
957
-            ? $selected_method_of_payment
958
-            : $this->checkout->selected_method_of_payment;
959
-        EE_Registry::instance()->SSN->set_session_data(
960
-            ['selected_method_of_payment' => $selected_method_of_payment]
961
-        );
962
-    }
963
-
964
-
965
-    /**
966
-     * _setup_payment_options
967
-     *
968
-     * @return EE_Form_Section_Proper
969
-     * @throws EE_Error
970
-     * @throws InvalidArgumentException
971
-     * @throws InvalidDataTypeException
972
-     * @throws InvalidInterfaceException
973
-     */
974
-    public function _setup_payment_options()
975
-    {
976
-        // load payment method classes
977
-        $this->checkout->available_payment_methods = $this->_get_available_payment_methods();
978
-        if (empty($this->checkout->available_payment_methods)) {
979
-            EE_Error::add_error(
980
-                apply_filters(
981
-                    'FHEE__EE_SPCO_Reg_Step_Payment_Options___setup_payment_options__error_message_no_payment_methods',
982
-                    sprintf(
983
-                        esc_html__(
984
-                            'Sorry, you cannot complete your purchase because a payment method is not active.%1$s Please contact %2$s for assistance and provide a description of the problem.',
985
-                            'event_espresso'
986
-                        ),
987
-                        '<br>',
988
-                        EE_Registry::instance()->CFG->organization->get_pretty('email')
989
-                    )
990
-                ),
991
-                __FILE__,
992
-                __FUNCTION__,
993
-                __LINE__
994
-            );
995
-        }
996
-        // switch up header depending on number of available payment methods
997
-        $payment_method_header     = count($this->checkout->available_payment_methods) > 1
998
-            ? apply_filters(
999
-                'FHEE__registration_page_payment_options__method_of_payment_hdr',
1000
-                esc_html__('Please Select Your Method of Payment', 'event_espresso')
1001
-            )
1002
-            : apply_filters(
1003
-                'FHEE__registration_page_payment_options__method_of_payment_hdr',
1004
-                esc_html__('Method of Payment', 'event_espresso')
1005
-            );
1006
-        $available_payment_methods = [
1007
-            // display the "Payment Method" header
1008
-            'payment_method_header' => new EE_Form_Section_HTML(
1009
-                apply_filters(
1010
-                    'FHEE__EE_SPCO_Reg_Step_Payment_Options___setup_payment_options__payment_method_header',
1011
-                    EEH_HTML::h4($payment_method_header, 'method-of-payment-hdr'),
1012
-                    $payment_method_header
1013
-                )
1014
-            ),
1015
-        ];
1016
-        // the list of actual payment methods ( invoice, paypal, etc ) in a  ( slug => HTML )  format
1017
-        $available_payment_method_options = [];
1018
-        $default_payment_method_option    = [];
1019
-        // additional instructions to be displayed and hidden below payment methods (adding a clearing div to start)
1020
-        $payment_methods_billing_info = [
1021
-            new EE_Form_Section_HTML(
1022
-                EEH_HTML::div('<br />', '', '', 'clear:both;')
1023
-            ),
1024
-        ];
1025
-        // loop through payment methods
1026
-        foreach ($this->checkout->available_payment_methods as $payment_method) {
1027
-            if ($payment_method instanceof EE_Payment_Method) {
1028
-                $payment_method_button = EEH_HTML::img(
1029
-                    $payment_method->button_url(),
1030
-                    $payment_method->name(),
1031
-                    'spco-payment-method-' . $payment_method->slug() . '-btn-img',
1032
-                    'spco-payment-method-btn-img'
1033
-                );
1034
-                // check if any payment methods are set as default
1035
-                // if payment method is already selected OR nothing is selected and this payment method should be
1036
-                // open_by_default
1037
-                if (
1038
-                    ($this->checkout->selected_method_of_payment === $payment_method->slug())
1039
-                    || (! $this->checkout->selected_method_of_payment && $payment_method->open_by_default())
1040
-                ) {
1041
-                    $this->checkout->selected_method_of_payment = $payment_method->slug();
1042
-                    $this->_save_selected_method_of_payment();
1043
-                    $default_payment_method_option[ $payment_method->slug() ] = $payment_method_button;
1044
-                } else {
1045
-                    $available_payment_method_options[ $payment_method->slug() ] = $payment_method_button;
1046
-                }
1047
-                $payment_methods_billing_info[ $payment_method->slug() . '-info' ] =
1048
-                    $this->_payment_method_billing_info(
1049
-                        $payment_method
1050
-                    );
1051
-            }
1052
-        }
1053
-        // prepend available_payment_method_options with default_payment_method_option so that it appears first in list
1054
-        // of PMs
1055
-        $available_payment_method_options = $default_payment_method_option + $available_payment_method_options;
1056
-        // now generate the actual form  inputs
1057
-        $available_payment_methods['available_payment_methods'] = $this->_available_payment_method_inputs(
1058
-            $available_payment_method_options
1059
-        );
1060
-        $available_payment_methods                              += $payment_methods_billing_info;
1061
-        // build the available payment methods form
1062
-        return new EE_Form_Section_Proper(
1063
-            [
1064
-                'html_id'         => 'spco-available-methods-of-payment-dv',
1065
-                'subsections'     => $available_payment_methods,
1066
-                'layout_strategy' => new EE_Div_Per_Section_Layout(),
1067
-            ]
1068
-        );
1069
-    }
1070
-
1071
-
1072
-    /**
1073
-     * _get_available_payment_methods
1074
-     *
1075
-     * @return EE_Payment_Method[]
1076
-     * @throws EE_Error
1077
-     * @throws InvalidArgumentException
1078
-     * @throws InvalidDataTypeException
1079
-     * @throws InvalidInterfaceException
1080
-     */
1081
-    protected function _get_available_payment_methods()
1082
-    {
1083
-        if (! empty($this->checkout->available_payment_methods)) {
1084
-            return $this->checkout->available_payment_methods;
1085
-        }
1086
-        $available_payment_methods = [];
1087
-        $EEM_Payment_Method        = EEM_Payment_Method::instance();
1088
-        // get all active payment methods
1089
-        $payment_methods = $EEM_Payment_Method->get_all_for_transaction(
1090
-            $this->checkout->transaction,
1091
-            EEM_Payment_Method::scope_cart
1092
-        );
1093
-        foreach ($payment_methods as $payment_method) {
1094
-            if ($payment_method instanceof EE_Payment_Method) {
1095
-                $available_payment_methods[ $payment_method->slug() ] = $payment_method;
1096
-            }
1097
-        }
1098
-        return $available_payment_methods;
1099
-    }
1100
-
1101
-
1102
-    /**
1103
-     *    _available_payment_method_inputs
1104
-     *
1105
-     * @access    private
1106
-     * @param array $available_payment_method_options
1107
-     * @return    EE_Form_Section_Proper
1108
-     * @throws EE_Error
1109
-     * @throws EE_Error
1110
-     */
1111
-    private function _available_payment_method_inputs($available_payment_method_options = [])
1112
-    {
1113
-        // generate inputs
1114
-        return new EE_Form_Section_Proper(
1115
-            [
1116
-                'html_id'         => 'ee-available-payment-method-inputs',
1117
-                'layout_strategy' => new EE_Div_Per_Section_Layout(),
1118
-                'subsections'     => [
1119
-                    '' => new EE_Radio_Button_Input(
1120
-                        $available_payment_method_options,
1121
-                        [
1122
-                            'html_name'          => 'selected_method_of_payment',
1123
-                            'html_class'         => 'spco-payment-method',
1124
-                            'default'            => $this->checkout->selected_method_of_payment,
1125
-                            'label_size'         => 11,
1126
-                            'enforce_label_size' => true,
1127
-                        ]
1128
-                    ),
1129
-                ],
1130
-            ]
1131
-        );
1132
-    }
1133
-
1134
-
1135
-    /**
1136
-     *    _payment_method_billing_info
1137
-     *
1138
-     * @access    private
1139
-     * @param EE_Payment_Method $payment_method
1140
-     * @return EE_Form_Section_Proper
1141
-     * @throws EE_Error
1142
-     * @throws InvalidArgumentException
1143
-     * @throws InvalidDataTypeException
1144
-     * @throws InvalidInterfaceException
1145
-     */
1146
-    private function _payment_method_billing_info(EE_Payment_Method $payment_method)
1147
-    {
1148
-        $currently_selected = $this->checkout->selected_method_of_payment === $payment_method->slug();
1149
-        // generate the billing form for payment method
1150
-        $billing_form                 = $currently_selected
1151
-            ? $this->_get_billing_form_for_payment_method($payment_method)
1152
-            : new EE_Form_Section_HTML();
1153
-        $this->checkout->billing_form = $currently_selected
1154
-            ? $billing_form
1155
-            : $this->checkout->billing_form;
1156
-        // it's all in the details
1157
-        $info_html = EEH_HTML::h3(
1158
-            esc_html__('Important information regarding your payment', 'event_espresso'),
1159
-            '',
1160
-            'spco-payment-method-hdr'
1161
-        );
1162
-        // add some info regarding the step, either from what's saved in the admin,
1163
-        // or a default string depending on whether the PM has a billing form or not
1164
-        if ($payment_method->description()) {
1165
-            $payment_method_info = $payment_method->description();
1166
-        } elseif ($billing_form instanceof EE_Billing_Info_Form) {
1167
-            $payment_method_info = sprintf(
1168
-                esc_html__(
1169
-                    'Please provide the following billing information, then click the "%1$s" button below in order to proceed.',
1170
-                    'event_espresso'
1171
-                ),
1172
-                $this->submit_button_text()
1173
-            );
1174
-        } else {
1175
-            $payment_method_info = sprintf(
1176
-                esc_html__('Please click the "%1$s" button below in order to proceed.', 'event_espresso'),
1177
-                $this->submit_button_text()
1178
-            );
1179
-        }
1180
-        $info_html .= EEH_HTML::div(
1181
-            apply_filters(
1182
-                'FHEE__EE_SPCO_Reg_Step_Payment_Options___payment_method_billing_info__payment_method_info',
1183
-                $payment_method_info
1184
-            ),
1185
-            '',
1186
-            'spco-payment-method-desc ee-attention'
1187
-        );
1188
-        return new EE_Form_Section_Proper(
1189
-            [
1190
-                'html_id'         => 'spco-payment-method-info-' . $payment_method->slug(),
1191
-                'html_class'      => 'spco-payment-method-info-dv',
1192
-                // only display the selected or default PM
1193
-                'html_style'      => $currently_selected ? '' : 'display:none;',
1194
-                'layout_strategy' => new EE_Div_Per_Section_Layout(),
1195
-                'subsections'     => [
1196
-                    'info'         => new EE_Form_Section_HTML($info_html),
1197
-                    'billing_form' => $currently_selected ? $billing_form : new EE_Form_Section_HTML(),
1198
-                ],
1199
-            ]
1200
-        );
1201
-    }
1202
-
1203
-
1204
-    /**
1205
-     * get_billing_form_html_for_payment_method
1206
-     *
1207
-     * @return bool
1208
-     * @throws EE_Error
1209
-     * @throws InvalidArgumentException
1210
-     * @throws ReflectionException
1211
-     * @throws InvalidDataTypeException
1212
-     * @throws InvalidInterfaceException
1213
-     */
1214
-    public function get_billing_form_html_for_payment_method()
1215
-    {
1216
-        // how have they chosen to pay?
1217
-        $this->checkout->selected_method_of_payment = $this->_get_selected_method_of_payment(true);
1218
-        $this->checkout->payment_method             = $this->_get_payment_method_for_selected_method_of_payment();
1219
-        if (! $this->checkout->payment_method instanceof EE_Payment_Method) {
1220
-            return false;
1221
-        }
1222
-        if (
1223
-            apply_filters(
1224
-                'FHEE__EE_SPCO_Reg_Step_Payment_Options__registration_checkout__selected_payment_method__display_success',
1225
-                false
1226
-            )
1227
-        ) {
1228
-            EE_Error::add_success(
1229
-                apply_filters(
1230
-                    'FHEE__Single_Page_Checkout__registration_checkout__selected_payment_method',
1231
-                    sprintf(
1232
-                        esc_html__(
1233
-                            'You have selected "%s" as your method of payment. Please note the important payment information below.',
1234
-                            'event_espresso'
1235
-                        ),
1236
-                        $this->checkout->payment_method->name()
1237
-                    )
1238
-                )
1239
-            );
1240
-        }
1241
-        // now generate billing form for selected method of payment
1242
-        $payment_method_billing_form = $this->_get_billing_form_for_payment_method($this->checkout->payment_method);
1243
-        // fill form with attendee info if applicable
1244
-        if (
1245
-            $payment_method_billing_form instanceof EE_Billing_Attendee_Info_Form
1246
-            && $this->checkout->transaction_has_primary_registrant()
1247
-        ) {
1248
-            $payment_method_billing_form->populate_from_attendee(
1249
-                $this->checkout->transaction->primary_registration()->attendee()
1250
-            );
1251
-        }
1252
-        // and debug content
1253
-        if (
1254
-            $payment_method_billing_form instanceof EE_Billing_Info_Form
1255
-            && $this->checkout->payment_method->type_obj() instanceof EE_PMT_Base
1256
-        ) {
1257
-            $payment_method_billing_form =
1258
-                $this->checkout->payment_method->type_obj()->apply_billing_form_debug_settings(
1259
-                    $payment_method_billing_form
1260
-                );
1261
-        }
1262
-        $billing_info = $payment_method_billing_form instanceof EE_Form_Section_Proper
1263
-            ? $payment_method_billing_form->get_html()
1264
-            : '';
1265
-        $this->checkout->json_response->set_return_data(['payment_method_info' => $billing_info]);
1266
-        // localize validation rules for main form
1267
-        $this->checkout->current_step->reg_form->localize_validation_rules();
1268
-        $this->checkout->json_response->add_validation_rules(EE_Form_Section_Proper::js_localization());
1269
-        return true;
1270
-    }
1271
-
1272
-
1273
-    /**
1274
-     * _get_billing_form_for_payment_method
1275
-     *
1276
-     * @param EE_Payment_Method $payment_method
1277
-     * @return EE_Billing_Info_Form|EE_Form_Section_HTML
1278
-     * @throws EE_Error
1279
-     * @throws InvalidArgumentException
1280
-     * @throws InvalidDataTypeException
1281
-     * @throws InvalidInterfaceException
1282
-     */
1283
-    private function _get_billing_form_for_payment_method(EE_Payment_Method $payment_method)
1284
-    {
1285
-        $billing_form = $payment_method->type_obj()->billing_form(
1286
-            $this->checkout->transaction,
1287
-            ['amount_owing' => $this->checkout->amount_owing]
1288
-        );
1289
-        if ($billing_form instanceof EE_Billing_Info_Form) {
1290
-            if (
1291
-                apply_filters(
1292
-                    'FHEE__EE_SPCO_Reg_Step_Payment_Options__registration_checkout__selected_payment_method__display_success',
1293
-                    false
1294
-                )
1295
-                && $this->request->requestParamIsSet('payment_method')
1296
-            ) {
1297
-                EE_Error::add_success(
1298
-                    apply_filters(
1299
-                        'FHEE__Single_Page_Checkout__registration_checkout__selected_payment_method',
1300
-                        sprintf(
1301
-                            esc_html__(
1302
-                                'You have selected "%s" as your method of payment. Please note the important payment information below.',
1303
-                                'event_espresso'
1304
-                            ),
1305
-                            $payment_method->name()
1306
-                        )
1307
-                    )
1308
-                );
1309
-            }
1310
-            return apply_filters(
1311
-                'FHEE__EE_SPCO_Reg_Step_Payment_Options___get_billing_form_for_payment_method__billing_form',
1312
-                $billing_form,
1313
-                $payment_method
1314
-            );
1315
-        }
1316
-        // no actual billing form, so return empty HTML form section
1317
-        return new EE_Form_Section_HTML();
1318
-    }
1319
-
1320
-
1321
-    /**
1322
-     * _get_selected_method_of_payment
1323
-     *
1324
-     * @param boolean $required whether to throw an error if the "selected_method_of_payment"
1325
-     *                          is not found in the incoming request
1326
-     * @param string  $request_param
1327
-     * @return NULL|string
1328
-     * @throws EE_Error
1329
-     * @throws InvalidArgumentException
1330
-     * @throws InvalidDataTypeException
1331
-     * @throws InvalidInterfaceException
1332
-     */
1333
-    private function _get_selected_method_of_payment(
1334
-        $required = false,
1335
-        $request_param = 'selected_method_of_payment'
1336
-    ) {
1337
-        // is selected_method_of_payment set in the request ?
1338
-        $selected_method_of_payment = $this->request->getRequestParam($request_param);
1339
-        if ($selected_method_of_payment) {
1340
-            // sanitize it
1341
-            $selected_method_of_payment = is_array($selected_method_of_payment)
1342
-                ? array_shift($selected_method_of_payment)
1343
-                : $selected_method_of_payment;
1344
-            $selected_method_of_payment = sanitize_text_field($selected_method_of_payment);
1345
-            // store it in the session so that it's available for all subsequent requests including AJAX
1346
-            $this->_save_selected_method_of_payment($selected_method_of_payment);
1347
-        } else {
1348
-            // or is is set in the session ?
1349
-            $selected_method_of_payment = EE_Registry::instance()->SSN->get_session_data(
1350
-                'selected_method_of_payment'
1351
-            );
1352
-        }
1353
-        // do ya really really gotta have it?
1354
-        if (empty($selected_method_of_payment) && $required) {
1355
-            EE_Error::add_error(
1356
-                sprintf(
1357
-                    esc_html__(
1358
-                        'The selected method of payment could not be determined.%sPlease ensure that you have selected one before proceeding.%sIf you continue to experience difficulties, then refresh your browser and try again, or contact %s for assistance.',
1359
-                        'event_espresso'
1360
-                    ),
1361
-                    '<br/>',
1362
-                    '<br/>',
1363
-                    EE_Registry::instance()->CFG->organization->get_pretty('email')
1364
-                ),
1365
-                __FILE__,
1366
-                __FUNCTION__,
1367
-                __LINE__
1368
-            );
1369
-            return null;
1370
-        }
1371
-        return $selected_method_of_payment;
1372
-    }
1373
-
1374
-
1375
-
1376
-
1377
-
1378
-
1379
-    /********************************************************************************************************/
1380
-    /***********************************  SWITCH PAYMENT METHOD  ************************************/
1381
-    /********************************************************************************************************/
1382
-    /**
1383
-     * switch_payment_method
1384
-     *
1385
-     * @return bool
1386
-     * @throws EE_Error
1387
-     * @throws InvalidArgumentException
1388
-     * @throws InvalidDataTypeException
1389
-     * @throws InvalidInterfaceException
1390
-     * @throws ReflectionException
1391
-     */
1392
-    public function switch_payment_method()
1393
-    {
1394
-        if (! $this->_verify_payment_method_is_set()) {
1395
-            return false;
1396
-        }
1397
-        if (
1398
-            apply_filters(
1399
-                'FHEE__EE_SPCO_Reg_Step_Payment_Options__registration_checkout__selected_payment_method__display_success',
1400
-                false
1401
-            )
1402
-        ) {
1403
-            EE_Error::add_success(
1404
-                apply_filters(
1405
-                    'FHEE__Single_Page_Checkout__registration_checkout__selected_payment_method',
1406
-                    sprintf(
1407
-                        esc_html__(
1408
-                            'You have selected "%s" as your method of payment. Please note the important payment information below.',
1409
-                            'event_espresso'
1410
-                        ),
1411
-                        $this->checkout->payment_method->name()
1412
-                    )
1413
-                )
1414
-            );
1415
-        }
1416
-        // generate billing form for selected method of payment if it hasn't been done already
1417
-        if ($this->checkout->payment_method->type_obj()->has_billing_form()) {
1418
-            $this->checkout->billing_form = $this->_get_billing_form_for_payment_method(
1419
-                $this->checkout->payment_method
1420
-            );
1421
-        }
1422
-        // fill form with attendee info if applicable
1423
-        if (
1424
-            apply_filters(
1425
-                'FHEE__populate_billing_form_fields_from_attendee',
1426
-                (
1427
-                $this->checkout->billing_form instanceof EE_Billing_Attendee_Info_Form
1428
-                && $this->checkout->transaction_has_primary_registrant()
1429
-                ),
1430
-                $this->checkout->billing_form,
1431
-                $this->checkout->transaction
1432
-            )
1433
-        ) {
1434
-            $this->checkout->billing_form->populate_from_attendee(
1435
-                $this->checkout->transaction->primary_registration()->attendee()
1436
-            );
1437
-        }
1438
-        // and debug content
1439
-        if (
1440
-            $this->checkout->billing_form instanceof EE_Billing_Info_Form
1441
-            && $this->checkout->payment_method->type_obj() instanceof EE_PMT_Base
1442
-        ) {
1443
-            $this->checkout->billing_form =
1444
-                $this->checkout->payment_method->type_obj()->apply_billing_form_debug_settings(
1445
-                    $this->checkout->billing_form
1446
-                );
1447
-        }
1448
-        // get html and validation rules for form
1449
-        if ($this->checkout->billing_form instanceof EE_Form_Section_Proper) {
1450
-            $this->checkout->json_response->set_return_data(
1451
-                ['payment_method_info' => $this->checkout->billing_form->get_html()]
1452
-            );
1453
-            // localize validation rules for main form
1454
-            $this->checkout->billing_form->localize_validation_rules(true);
1455
-            $this->checkout->json_response->add_validation_rules(EE_Form_Section_Proper::js_localization());
1456
-        } else {
1457
-            $this->checkout->json_response->set_return_data(['payment_method_info' => '']);
1458
-        }
1459
-        // prevents advancement to next step
1460
-        $this->checkout->continue_reg = false;
1461
-        return true;
1462
-    }
1463
-
1464
-
1465
-    /**
1466
-     * _verify_payment_method_is_set
1467
-     *
1468
-     * @return bool
1469
-     * @throws EE_Error
1470
-     * @throws InvalidArgumentException
1471
-     * @throws ReflectionException
1472
-     * @throws InvalidDataTypeException
1473
-     * @throws InvalidInterfaceException
1474
-     */
1475
-    protected function _verify_payment_method_is_set()
1476
-    {
1477
-        // generate billing form for selected method of payment if it hasn't been done already
1478
-        if (empty($this->checkout->selected_method_of_payment)) {
1479
-            // how have they chosen to pay?
1480
-            $this->checkout->selected_method_of_payment = $this->_get_selected_method_of_payment(true);
1481
-        } else {
1482
-            // choose your own adventure based on method_of_payment
1483
-            switch ($this->checkout->selected_method_of_payment) {
1484
-                case 'events_sold_out':
1485
-                    EE_Error::add_attention(
1486
-                        apply_filters(
1487
-                            'FHEE__EE_SPCO_Reg_Step_Payment_Options___verify_payment_method_is_set__sold_out_events_msg',
1488
-                            esc_html__(
1489
-                                'It appears that the event you were about to make a payment for has sold out since this form first loaded. Please contact the event administrator if you believe this is an error.',
1490
-                                'event_espresso'
1491
-                            )
1492
-                        ),
1493
-                        __FILE__,
1494
-                        __FUNCTION__,
1495
-                        __LINE__
1496
-                    );
1497
-                    return false;
1498
-                case 'payments_closed':
1499
-                    EE_Error::add_attention(
1500
-                        apply_filters(
1501
-                            'FHEE__EE_SPCO_Reg_Step_Payment_Options___verify_payment_method_is_set__payments_closed_msg',
1502
-                            esc_html__(
1503
-                                'It appears that the event you were about to make a payment for is not accepting payments at this time. Please contact the event administrator if you believe this is an error.',
1504
-                                'event_espresso'
1505
-                            )
1506
-                        ),
1507
-                        __FILE__,
1508
-                        __FUNCTION__,
1509
-                        __LINE__
1510
-                    );
1511
-                    return false;
1512
-                case 'no_payment_required':
1513
-                    EE_Error::add_attention(
1514
-                        apply_filters(
1515
-                            'FHEE__EE_SPCO_Reg_Step_Payment_Options___verify_payment_method_is_set__no_payment_required_msg',
1516
-                            esc_html__(
1517
-                                'It appears that the event you were about to make a payment for does not require payment. Please contact the event administrator if you believe this is an error.',
1518
-                                'event_espresso'
1519
-                            )
1520
-                        ),
1521
-                        __FILE__,
1522
-                        __FUNCTION__,
1523
-                        __LINE__
1524
-                    );
1525
-                    return false;
1526
-                default:
1527
-            }
1528
-        }
1529
-        // verify payment method
1530
-        if (! $this->checkout->payment_method instanceof EE_Payment_Method) {
1531
-            // get payment method for selected method of payment
1532
-            $this->checkout->payment_method = $this->_get_payment_method_for_selected_method_of_payment();
1533
-        }
1534
-        return $this->checkout->payment_method instanceof EE_Payment_Method;
1535
-    }
1536
-
1537
-
1538
-
1539
-    /********************************************************************************************************/
1540
-    /***************************************  SAVE PAYER DETAILS  ****************************************/
1541
-    /********************************************************************************************************/
1542
-    /**
1543
-     * save_payer_details_via_ajax
1544
-     *
1545
-     * @return void
1546
-     * @throws EE_Error
1547
-     * @throws InvalidArgumentException
1548
-     * @throws ReflectionException
1549
-     * @throws RuntimeException
1550
-     * @throws InvalidDataTypeException
1551
-     * @throws InvalidInterfaceException
1552
-     */
1553
-    public function save_payer_details_via_ajax()
1554
-    {
1555
-        if (! $this->_verify_payment_method_is_set()) {
1556
-            return;
1557
-        }
1558
-        // generate billing form for selected method of payment if it hasn't been done already
1559
-        if ($this->checkout->payment_method->type_obj()->has_billing_form()) {
1560
-            $this->checkout->billing_form = $this->_get_billing_form_for_payment_method(
1561
-                $this->checkout->payment_method
1562
-            );
1563
-        }
1564
-        // generate primary attendee from payer info if applicable
1565
-        if (! $this->checkout->transaction_has_primary_registrant()) {
1566
-            $attendee = $this->_create_attendee_from_request_data();
1567
-            if ($attendee instanceof EE_Attendee) {
1568
-                foreach ($this->checkout->transaction->registrations() as $registration) {
1569
-                    if ($registration->is_primary_registrant()) {
1570
-                        $this->checkout->primary_attendee_obj = $attendee;
1571
-                        $registration->_add_relation_to($attendee, 'Attendee');
1572
-                        $registration->set_attendee_id($attendee->ID());
1573
-                        $registration->update_cache_after_object_save('Attendee', $attendee);
1574
-                    }
1575
-                }
1576
-            }
1577
-        }
1578
-    }
1579
-
1580
-
1581
-    /**
1582
-     * create_attendee_from_request_data
1583
-     * uses info from alternate GET or POST data (such as AJAX) to create a new attendee
1584
-     *
1585
-     * @return EE_Attendee
1586
-     * @throws EE_Error
1587
-     * @throws InvalidArgumentException
1588
-     * @throws ReflectionException
1589
-     * @throws InvalidDataTypeException
1590
-     * @throws InvalidInterfaceException
1591
-     */
1592
-    protected function _create_attendee_from_request_data()
1593
-    {
1594
-        // get State ID
1595
-        $STA_ID = $this->request->getRequestParam('state');
1596
-        if (! empty($STA_ID)) {
1597
-            // can we get state object from name ?
1598
-            EE_Registry::instance()->load_model('State');
1599
-            $state  = EEM_State::instance()->get_col([['STA_name' => $STA_ID], 'limit' => 1], 'STA_ID');
1600
-            $STA_ID = is_array($state) && ! empty($state) ? reset($state) : $STA_ID;
1601
-        }
1602
-        // get Country ISO
1603
-        $CNT_ISO = $this->request->getRequestParam('country');
1604
-        if (! empty($CNT_ISO)) {
1605
-            // can we get country object from name ?
1606
-            EE_Registry::instance()->load_model('Country');
1607
-            $country = EEM_Country::instance()->get_col(
1608
-                [['CNT_name' => $CNT_ISO], 'limit' => 1],
1609
-                'CNT_ISO'
1610
-            );
1611
-            $CNT_ISO = is_array($country) && ! empty($country) ? reset($country) : $CNT_ISO;
1612
-        }
1613
-        // grab attendee data
1614
-        $attendee_data = [
1615
-            'ATT_fname'    => $this->request->getRequestParam('first_name'),
1616
-            'ATT_lname'    => $this->request->getRequestParam('last_name'),
1617
-            'ATT_email'    => $this->request->getRequestParam('email'),
1618
-            'ATT_address'  => $this->request->getRequestParam('address'),
1619
-            'ATT_address2' => $this->request->getRequestParam('address2'),
1620
-            'ATT_city'     => $this->request->getRequestParam('city'),
1621
-            'STA_ID'       => $STA_ID,
1622
-            'CNT_ISO'      => $CNT_ISO,
1623
-            'ATT_zip'      => $this->request->getRequestParam('zip'),
1624
-            'ATT_phone'    => $this->request->getRequestParam('phone'),
1625
-        ];
1626
-        // validate the email address since it is the most important piece of info
1627
-        if (empty($attendee_data['ATT_email'])) {
1628
-            EE_Error::add_error(
1629
-                esc_html__('An invalid email address was submitted.', 'event_espresso'),
1630
-                __FILE__,
1631
-                __FUNCTION__,
1632
-                __LINE__
1633
-            );
1634
-        }
1635
-        // does this attendee already exist in the db ? we're searching using a combination of first name, last name,
1636
-        // AND email address
1637
-        if (
1638
-            ! empty($attendee_data['ATT_fname'])
1639
-            && ! empty($attendee_data['ATT_lname'])
1640
-            && ! empty($attendee_data['ATT_email'])
1641
-        ) {
1642
-            $existing_attendee = EEM_Attendee::instance()->find_existing_attendee(
1643
-                [
1644
-                    'ATT_fname' => $attendee_data['ATT_fname'],
1645
-                    'ATT_lname' => $attendee_data['ATT_lname'],
1646
-                    'ATT_email' => $attendee_data['ATT_email'],
1647
-                ]
1648
-            );
1649
-            if ($existing_attendee instanceof EE_Attendee) {
1650
-                return $existing_attendee;
1651
-            }
1652
-        }
1653
-        // no existing attendee? kk let's create a new one
1654
-        // kinda lame, but we need a first and last name to create an attendee, so use the email address if those
1655
-        // don't exist
1656
-        $attendee_data['ATT_fname'] = ! empty($attendee_data['ATT_fname'])
1657
-            ? $attendee_data['ATT_fname']
1658
-            : $attendee_data['ATT_email'];
1659
-        $attendee_data['ATT_lname'] = ! empty($attendee_data['ATT_lname'])
1660
-            ? $attendee_data['ATT_lname']
1661
-            : $attendee_data['ATT_email'];
1662
-        return EE_Attendee::new_instance($attendee_data);
1663
-    }
1664
-
1665
-
1666
-
1667
-    /********************************************************************************************************/
1668
-    /****************************************  PROCESS REG STEP  *****************************************/
1669
-    /********************************************************************************************************/
1670
-    /**
1671
-     * process_reg_step
1672
-     *
1673
-     * @return bool
1674
-     * @throws EE_Error
1675
-     * @throws InvalidArgumentException
1676
-     * @throws ReflectionException
1677
-     * @throws EntityNotFoundException
1678
-     * @throws InvalidDataTypeException
1679
-     * @throws InvalidInterfaceException
1680
-     * @throws InvalidStatusException
1681
-     */
1682
-    public function process_reg_step()
1683
-    {
1684
-        // how have they chosen to pay?
1685
-        $this->checkout->selected_method_of_payment = $this->checkout->transaction->is_free()
1686
-            ? 'no_payment_required'
1687
-            : $this->_get_selected_method_of_payment(true);
1688
-        // choose your own adventure based on method_of_payment
1689
-        switch ($this->checkout->selected_method_of_payment) {
1690
-            case 'events_sold_out':
1691
-                $this->checkout->redirect     = true;
1692
-                $this->checkout->redirect_url = $this->checkout->cancel_page_url;
1693
-                $this->checkout->json_response->set_redirect_url($this->checkout->redirect_url);
1694
-                // mark this reg step as completed
1695
-                $this->set_completed();
1696
-                return false;
1697
-
1698
-            case 'payments_closed':
1699
-                if (
1700
-                    apply_filters(
1701
-                        'FHEE__EE_SPCO_Reg_Step_Payment_Options__process_reg_step__payments_closed__display_success',
1702
-                        false
1703
-                    )
1704
-                ) {
1705
-                    EE_Error::add_success(
1706
-                        esc_html__('no payment required at this time.', 'event_espresso'),
1707
-                        __FILE__,
1708
-                        __FUNCTION__,
1709
-                        __LINE__
1710
-                    );
1711
-                }
1712
-                // mark this reg step as completed
1713
-                $this->set_completed();
1714
-                return true;
1715
-
1716
-            case 'no_payment_required':
1717
-                if (
1718
-                    apply_filters(
1719
-                        'FHEE__EE_SPCO_Reg_Step_Payment_Options__process_reg_step__no_payment_required__display_success',
1720
-                        false
1721
-                    )
1722
-                ) {
1723
-                    EE_Error::add_success(
1724
-                        esc_html__('no payment required.', 'event_espresso'),
1725
-                        __FILE__,
1726
-                        __FUNCTION__,
1727
-                        __LINE__
1728
-                    );
1729
-                }
1730
-                // mark this reg step as completed
1731
-                $this->set_completed();
1732
-                return true;
1733
-
1734
-            default:
1735
-                $registrations         = EE_Registry::instance()->SSN->checkout()->transaction->registrations(
1736
-                    EE_Registry::instance()->SSN->checkout()->reg_cache_where_params
1737
-                );
1738
-                $ejected_registrations = EE_SPCO_Reg_Step_Payment_Options::find_registrations_that_lost_their_space(
1739
-                    $registrations,
1740
-                    EE_Registry::instance()->SSN->checkout()->revisit
1741
-                );
1742
-                // calculate difference between the two arrays
1743
-                $registrations = array_diff($registrations, $ejected_registrations);
1744
-                if (empty($registrations)) {
1745
-                    $this->_redirect_because_event_sold_out();
1746
-                    return false;
1747
-                }
1748
-                $payment = $this->_process_payment();
1749
-                if ($payment instanceof EE_Payment) {
1750
-                    $this->checkout->continue_reg = true;
1751
-                    $this->_maybe_set_completed($payment);
1752
-                } else {
1753
-                    $this->checkout->continue_reg = false;
1754
-                }
1755
-                return $payment instanceof EE_Payment;
1756
-        }
1757
-    }
1758
-
1759
-
1760
-    /**
1761
-     * _redirect_because_event_sold_out
1762
-     *
1763
-     * @return void
1764
-     */
1765
-    protected function _redirect_because_event_sold_out()
1766
-    {
1767
-        $this->checkout->continue_reg = false;
1768
-        // set redirect URL
1769
-        $this->checkout->redirect_url = add_query_arg(
1770
-            ['e_reg_url_link' => $this->checkout->reg_url_link],
1771
-            $this->checkout->current_step->reg_step_url()
1772
-        );
1773
-        $this->checkout->json_response->set_redirect_url($this->checkout->redirect_url);
1774
-    }
1775
-
1776
-
1777
-    /**
1778
-     * @param EE_Payment $payment
1779
-     * @return void
1780
-     * @throws EE_Error
1781
-     */
1782
-    protected function _maybe_set_completed(EE_Payment $payment)
1783
-    {
1784
-        // Do we need to redirect them? If so, there's more work to be done.
1785
-        if (! $payment->redirect_url()) {
1786
-            $this->set_completed();
1787
-        }
1788
-    }
1789
-
1790
-
1791
-    /**
1792
-     *    update_reg_step
1793
-     *    this is the final step after a user  revisits the site to retry a payment
1794
-     *
1795
-     * @return bool
1796
-     * @throws EE_Error
1797
-     * @throws InvalidArgumentException
1798
-     * @throws ReflectionException
1799
-     * @throws EntityNotFoundException
1800
-     * @throws InvalidDataTypeException
1801
-     * @throws InvalidInterfaceException
1802
-     * @throws InvalidStatusException
1803
-     */
1804
-    public function update_reg_step()
1805
-    {
1806
-        $success = true;
1807
-        // if payment required
1808
-        if ($this->checkout->transaction->total() > 0) {
1809
-            do_action(
1810
-                'AHEE__EE_Single_Page_Checkout__process_finalize_registration__before_gateway',
1811
-                $this->checkout->transaction
1812
-            );
1813
-            // attempt payment via payment method
1814
-            $success = $this->process_reg_step();
1815
-        }
1816
-        if ($success && ! $this->checkout->redirect) {
1817
-            $this->checkout->cart->get_grand_total()->save_this_and_descendants_to_txn(
1818
-                $this->checkout->transaction->ID()
1819
-            );
1820
-            // set return URL
1821
-            $this->checkout->redirect_url = add_query_arg(
1822
-                ['e_reg_url_link' => $this->checkout->reg_url_link],
1823
-                $this->checkout->thank_you_page_url
1824
-            );
1825
-        }
1826
-        return $success;
1827
-    }
1828
-
1829
-
1830
-    /**
1831
-     * @return EE_Payment|null
1832
-     * @throws EE_Error
1833
-     * @throws InvalidArgumentException
1834
-     * @throws ReflectionException
1835
-     * @throws RuntimeException
1836
-     * @throws InvalidDataTypeException
1837
-     * @throws InvalidInterfaceException
1838
-     */
1839
-    private function _process_payment()
1840
-    {
1841
-        // basically confirm that the event hasn't sold out since they hit the page
1842
-        if (! $this->_last_second_ticket_verifications()) {
1843
-            return null;
1844
-        }
1845
-        // ya gotta make a choice man
1846
-        if (empty($this->checkout->selected_method_of_payment)) {
1847
-            $this->checkout->json_response->set_plz_select_method_of_payment(
1848
-                esc_html__('Please select a method of payment before proceeding.', 'event_espresso')
1849
-            );
1850
-            return null;
1851
-        }
1852
-        // get EE_Payment_Method object
1853
-        if (! $this->checkout->payment_method = $this->_get_payment_method_for_selected_method_of_payment()) {
1854
-            return null;
1855
-        }
1856
-        // setup billing form
1857
-        if ($this->checkout->payment_method->type_obj()->has_billing_form()) {
1858
-            $this->checkout->billing_form = $this->_get_billing_form_for_payment_method(
1859
-                $this->checkout->payment_method
1860
-            );
1861
-            // bad billing form ?
1862
-            if (! $this->_billing_form_is_valid()) {
1863
-                return null;
1864
-            }
1865
-        }
1866
-        // ensure primary registrant has been fully processed
1867
-        if (! $this->_setup_primary_registrant_prior_to_payment()) {
1868
-            return null;
1869
-        }
1870
-        // if session is close to expiring (under 10 minutes by default)
1871
-        if ((time() - EE_Registry::instance()->SSN->expiration()) < EE_Registry::instance()->SSN->extension()) {
1872
-            // add some time to session expiration so that payment can be completed
1873
-            EE_Registry::instance()->SSN->extend_expiration();
1874
-        }
1875
-        /** @type EE_Transaction_Processor $transaction_processor */
1876
-        // $transaction_processor = EE_Registry::instance()->load_class( 'Transaction_Processor' );
1877
-        // in case a registrant leaves to an Off-Site Gateway and never returns, we want to approve any registrations
1878
-        // for events with a default reg status of Approved
1879
-        // $transaction_processor->toggle_registration_statuses_for_default_approved_events(
1880
-        //      $this->checkout->transaction, $this->checkout->reg_cache_where_params
1881
-        // );
1882
-        // attempt payment
1883
-        $payment = $this->_attempt_payment($this->checkout->payment_method);
1884
-        // process results
1885
-        $payment = $this->_validate_payment($payment);
1886
-        $payment = $this->_post_payment_processing($payment);
1887
-        // verify payment
1888
-        if ($payment instanceof EE_Payment) {
1889
-            // store that for later
1890
-            $this->checkout->payment = $payment;
1891
-            // we can also consider the TXN to not have been failed, so temporarily upgrade it's status to abandoned
1892
-            $this->checkout->transaction->toggle_failed_transaction_status();
1893
-            $payment_status = $payment->status();
1894
-            if (
1895
-                $payment_status === EEM_Payment::status_id_approved
1896
-                || $payment_status === EEM_Payment::status_id_pending
1897
-            ) {
1898
-                return $payment;
1899
-            }
1900
-            return null;
1901
-        }
1902
-        if ($payment === true) {
1903
-            // please note that offline payment methods will NOT make a payment,
1904
-            // but instead just mark themselves as the PMD_ID on the transaction, and return true
1905
-            $this->checkout->payment = $payment;
1906
-            return $payment;
1907
-        }
1908
-        // where's my money?
1909
-        return null;
1910
-    }
1911
-
1912
-
1913
-    /**
1914
-     * _last_second_ticket_verifications
1915
-     *
1916
-     * @return bool
1917
-     * @throws EE_Error
1918
-     * @throws ReflectionException
1919
-     */
1920
-    protected function _last_second_ticket_verifications()
1921
-    {
1922
-        // don't bother re-validating if not a return visit
1923
-        if (! $this->checkout->revisit) {
1924
-            return true;
1925
-        }
1926
-        $registrations = $this->checkout->transaction->registrations();
1927
-        if (empty($registrations)) {
1928
-            return false;
1929
-        }
1930
-        foreach ($registrations as $registration) {
1931
-            if ($registration instanceof EE_Registration && ! $registration->is_approved()) {
1932
-                $event = $registration->event_obj();
1933
-                if ($event instanceof EE_Event && $event->is_sold_out(true)) {
1934
-                    EE_Error::add_error(
1935
-                        apply_filters(
1936
-                            'FHEE__EE_SPCO_Reg_Step_Payment_Options___last_second_ticket_verifications__sold_out_events_msg',
1937
-                            sprintf(
1938
-                                esc_html__(
1939
-                                    'It appears that the %1$s event that you were about to make a payment for has sold out since you first registered and/or arrived at this page. Please refresh the page and try again. If you have already made a partial payment towards this event, please contact the event administrator for a refund.',
1940
-                                    'event_espresso'
1941
-                                ),
1942
-                                $event->name()
1943
-                            )
1944
-                        ),
1945
-                        __FILE__,
1946
-                        __FUNCTION__,
1947
-                        __LINE__
1948
-                    );
1949
-                    return false;
1950
-                }
1951
-            }
1952
-        }
1953
-        return true;
1954
-    }
1955
-
1956
-
1957
-    /**
1958
-     * redirect_form
1959
-     *
1960
-     * @return bool
1961
-     * @throws EE_Error
1962
-     * @throws InvalidArgumentException
1963
-     * @throws ReflectionException
1964
-     * @throws InvalidDataTypeException
1965
-     * @throws InvalidInterfaceException
1966
-     */
1967
-    public function redirect_form()
1968
-    {
1969
-        $payment_method_billing_info = $this->_payment_method_billing_info(
1970
-            $this->_get_payment_method_for_selected_method_of_payment()
1971
-        );
1972
-        $html                        = $payment_method_billing_info->get_html();
1973
-        $html                        .= $this->checkout->redirect_form;
1974
-        /** @var ResponseInterface $response */
1975
-        $response = LoaderFactory::getLoader()->getShared(ResponseInterface::class);
1976
-        $response->addOutput($html);
1977
-        return true;
1978
-    }
1979
-
1980
-
1981
-    /**
1982
-     * _billing_form_is_valid
1983
-     *
1984
-     * @return bool
1985
-     * @throws EE_Error
1986
-     */
1987
-    private function _billing_form_is_valid()
1988
-    {
1989
-        if (! $this->checkout->payment_method->type_obj()->has_billing_form()) {
1990
-            return true;
1991
-        }
1992
-        if ($this->checkout->billing_form instanceof EE_Billing_Info_Form) {
1993
-            if ($this->checkout->billing_form->was_submitted()) {
1994
-                $this->checkout->billing_form->receive_form_submission();
1995
-                if ($this->checkout->billing_form->is_valid()) {
1996
-                    return true;
1997
-                }
1998
-                $validation_errors = $this->checkout->billing_form->get_validation_errors_accumulated();
1999
-                $error_strings     = [];
2000
-                foreach ($validation_errors as $validation_error) {
2001
-                    if ($validation_error instanceof EE_Validation_Error) {
2002
-                        $form_section = $validation_error->get_form_section();
2003
-                        if ($form_section instanceof EE_Form_Input_Base) {
2004
-                            $label = $form_section->html_label_text();
2005
-                        } elseif ($form_section instanceof EE_Form_Section_Base) {
2006
-                            $label = $form_section->name();
2007
-                        } else {
2008
-                            $label = esc_html__('Validation Error', 'event_espresso');
2009
-                        }
2010
-                        $error_strings[] = sprintf('%1$s: %2$s', $label, $validation_error->getMessage());
2011
-                    }
2012
-                }
2013
-                EE_Error::add_error(
2014
-                    sprintf(
2015
-                        esc_html__(
2016
-                            'One or more billing form inputs are invalid and require correction before proceeding. %1$s %2$s',
2017
-                            'event_espresso'
2018
-                        ),
2019
-                        '<br/>',
2020
-                        implode('<br/>', $error_strings)
2021
-                    ),
2022
-                    __FILE__,
2023
-                    __FUNCTION__,
2024
-                    __LINE__
2025
-                );
2026
-            } else {
2027
-                EE_Error::add_error(
2028
-                    esc_html__(
2029
-                        'The billing form was not submitted or something prevented it\'s submission.',
2030
-                        'event_espresso'
2031
-                    ),
2032
-                    __FILE__,
2033
-                    __FUNCTION__,
2034
-                    __LINE__
2035
-                );
2036
-            }
2037
-        } else {
2038
-            EE_Error::add_error(
2039
-                esc_html__(
2040
-                    'The submitted billing form is invalid possibly due to a technical reason.',
2041
-                    'event_espresso'
2042
-                ),
2043
-                __FILE__,
2044
-                __FUNCTION__,
2045
-                __LINE__
2046
-            );
2047
-        }
2048
-        return false;
2049
-    }
2050
-
2051
-
2052
-    /**
2053
-     * _setup_primary_registrant_prior_to_payment
2054
-     * ensures that the primary registrant has a valid attendee object created with the critical details populated
2055
-     * (first & last name & email) and that both the transaction object and primary registration object have been saved
2056
-     * plz note that any other registrations will NOT be saved at this point (because they may not have any details
2057
-     * yet)
2058
-     *
2059
-     * @return bool
2060
-     * @throws EE_Error
2061
-     * @throws InvalidArgumentException
2062
-     * @throws ReflectionException
2063
-     * @throws RuntimeException
2064
-     * @throws InvalidDataTypeException
2065
-     * @throws InvalidInterfaceException
2066
-     */
2067
-    private function _setup_primary_registrant_prior_to_payment()
2068
-    {
2069
-        // check if transaction has a primary registrant and that it has a related Attendee object
2070
-        // if not, then we need to at least gather some primary registrant data before attempting payment
2071
-        if (
2072
-            $this->checkout->billing_form instanceof EE_Billing_Attendee_Info_Form
2073
-            && ! $this->checkout->transaction_has_primary_registrant()
2074
-            && ! $this->_capture_primary_registration_data_from_billing_form()
2075
-        ) {
2076
-            return false;
2077
-        }
2078
-        // because saving an object clears it's cache, we need to do the chevy shuffle
2079
-        // grab the primary_registration object
2080
-        $primary_registration = $this->checkout->transaction->primary_registration();
2081
-        // at this point we'll consider a TXN to not have been failed
2082
-        $this->checkout->transaction->toggle_failed_transaction_status();
2083
-        // save the TXN ( which clears cached copy of primary_registration)
2084
-        $this->checkout->transaction->save();
2085
-        // grab TXN ID and save it to the primary_registration
2086
-        $primary_registration->set_transaction_id($this->checkout->transaction->ID());
2087
-        // save what we have so far
2088
-        $primary_registration->save();
2089
-        return true;
2090
-    }
2091
-
2092
-
2093
-    /**
2094
-     * _capture_primary_registration_data_from_billing_form
2095
-     *
2096
-     * @return bool
2097
-     * @throws EE_Error
2098
-     * @throws InvalidArgumentException
2099
-     * @throws ReflectionException
2100
-     * @throws InvalidDataTypeException
2101
-     * @throws InvalidInterfaceException
2102
-     */
2103
-    private function _capture_primary_registration_data_from_billing_form()
2104
-    {
2105
-        // convert billing form data into an attendee
2106
-        $this->checkout->primary_attendee_obj = $this->checkout->billing_form->create_attendee_from_billing_form_data();
2107
-        if (! $this->checkout->primary_attendee_obj instanceof EE_Attendee) {
2108
-            EE_Error::add_error(
2109
-                sprintf(
2110
-                    esc_html__(
2111
-                        'The billing form details could not be used for attendee details due to a technical issue.%sPlease try again or contact %s for assistance.',
2112
-                        'event_espresso'
2113
-                    ),
2114
-                    '<br/>',
2115
-                    EE_Registry::instance()->CFG->organization->get_pretty('email')
2116
-                ),
2117
-                __FILE__,
2118
-                __FUNCTION__,
2119
-                __LINE__
2120
-            );
2121
-            return false;
2122
-        }
2123
-        $primary_registration = $this->checkout->transaction->primary_registration();
2124
-        if (! $primary_registration instanceof EE_Registration) {
2125
-            EE_Error::add_error(
2126
-                sprintf(
2127
-                    esc_html__(
2128
-                        'The primary registrant for this transaction could not be determined due to a technical issue.%sPlease try again or contact %s for assistance.',
2129
-                        'event_espresso'
2130
-                    ),
2131
-                    '<br/>',
2132
-                    EE_Registry::instance()->CFG->organization->get_pretty('email')
2133
-                ),
2134
-                __FILE__,
2135
-                __FUNCTION__,
2136
-                __LINE__
2137
-            );
2138
-            return false;
2139
-        }
2140
-        if (
2141
-            ! $primary_registration->_add_relation_to($this->checkout->primary_attendee_obj, 'Attendee')
2142
-              instanceof
2143
-              EE_Attendee
2144
-        ) {
2145
-            EE_Error::add_error(
2146
-                sprintf(
2147
-                    esc_html__(
2148
-                        'The primary registrant could not be associated with this transaction due to a technical issue.%sPlease try again or contact %s for assistance.',
2149
-                        'event_espresso'
2150
-                    ),
2151
-                    '<br/>',
2152
-                    EE_Registry::instance()->CFG->organization->get_pretty('email')
2153
-                ),
2154
-                __FILE__,
2155
-                __FUNCTION__,
2156
-                __LINE__
2157
-            );
2158
-            return false;
2159
-        }
2160
-        /** @type EE_Registration_Processor $registration_processor */
2161
-        $registration_processor = EE_Registry::instance()->load_class('Registration_Processor');
2162
-        // at this point, we should have enough details about the registrant to consider the registration NOT incomplete
2163
-        $registration_processor->toggle_incomplete_registration_status_to_default($primary_registration);
2164
-        return true;
2165
-    }
2166
-
2167
-
2168
-    /**
2169
-     * _get_payment_method_for_selected_method_of_payment
2170
-     * retrieves a valid payment method
2171
-     *
2172
-     * @return EE_Payment_Method
2173
-     * @throws EE_Error
2174
-     * @throws InvalidArgumentException
2175
-     * @throws ReflectionException
2176
-     * @throws InvalidDataTypeException
2177
-     * @throws InvalidInterfaceException
2178
-     */
2179
-    private function _get_payment_method_for_selected_method_of_payment()
2180
-    {
2181
-        if ($this->checkout->selected_method_of_payment === 'events_sold_out') {
2182
-            $this->_redirect_because_event_sold_out();
2183
-            return null;
2184
-        }
2185
-        // get EE_Payment_Method object
2186
-        if (isset($this->checkout->available_payment_methods[ $this->checkout->selected_method_of_payment ])) {
2187
-            $payment_method = $this->checkout->available_payment_methods[ $this->checkout->selected_method_of_payment ];
2188
-        } else {
2189
-            // load EEM_Payment_Method
2190
-            EE_Registry::instance()->load_model('Payment_Method');
2191
-            $EEM_Payment_Method = EEM_Payment_Method::instance();
2192
-            $payment_method     = $EEM_Payment_Method->get_one_by_slug($this->checkout->selected_method_of_payment);
2193
-        }
2194
-        // verify $payment_method
2195
-        if (! $payment_method instanceof EE_Payment_Method) {
2196
-            // not a payment
2197
-            EE_Error::add_error(
2198
-                sprintf(
2199
-                    esc_html__(
2200
-                        'The selected method of payment could not be determined due to a technical issue.%sPlease try again or contact %s for assistance.',
2201
-                        'event_espresso'
2202
-                    ),
2203
-                    '<br/>',
2204
-                    EE_Registry::instance()->CFG->organization->get_pretty('email')
2205
-                ),
2206
-                __FILE__,
2207
-                __FUNCTION__,
2208
-                __LINE__
2209
-            );
2210
-            return null;
2211
-        }
2212
-        // and verify it has a valid Payment_Method Type object
2213
-        if (! $payment_method->type_obj() instanceof EE_PMT_Base) {
2214
-            // not a payment
2215
-            EE_Error::add_error(
2216
-                sprintf(
2217
-                    esc_html__(
2218
-                        'A valid payment method could not be determined due to a technical issue.%sPlease try again or contact %s for assistance.',
2219
-                        'event_espresso'
2220
-                    ),
2221
-                    '<br/>',
2222
-                    EE_Registry::instance()->CFG->organization->get_pretty('email')
2223
-                ),
2224
-                __FILE__,
2225
-                __FUNCTION__,
2226
-                __LINE__
2227
-            );
2228
-            return null;
2229
-        }
2230
-        return $payment_method;
2231
-    }
2232
-
2233
-
2234
-    /**
2235
-     *    _attempt_payment
2236
-     *
2237
-     * @access    private
2238
-     * @type    EE_Payment_Method $payment_method
2239
-     * @return mixed EE_Payment | boolean
2240
-     * @throws EE_Error
2241
-     * @throws InvalidArgumentException
2242
-     * @throws ReflectionException
2243
-     * @throws InvalidDataTypeException
2244
-     * @throws InvalidInterfaceException
2245
-     */
2246
-    private function _attempt_payment(EE_Payment_Method $payment_method)
2247
-    {
2248
-        $payment = null;
2249
-        $this->checkout->transaction->save();
2250
-        $payment_processor = EE_Registry::instance()->load_core('Payment_Processor');
2251
-        if (! $payment_processor instanceof EE_Payment_Processor) {
2252
-            return false;
2253
-        }
2254
-        try {
2255
-            $payment_processor->set_revisit($this->checkout->revisit);
2256
-            // generate payment object
2257
-            $payment = $payment_processor->process_payment(
2258
-                $payment_method,
2259
-                $this->checkout->transaction,
2260
-                $this->checkout->amount_owing,
2261
-                $this->checkout->billing_form,
2262
-                $this->_get_return_url($payment_method),
2263
-                'CART',
2264
-                $this->checkout->admin_request,
2265
-                true,
2266
-                $this->reg_step_url()
2267
-            );
2268
-        } catch (Exception $e) {
2269
-            $this->_handle_payment_processor_exception($e);
2270
-        }
2271
-        return $payment;
2272
-    }
2273
-
2274
-
2275
-    /**
2276
-     * _handle_payment_processor_exception
2277
-     *
2278
-     * @param Exception $e
2279
-     * @return void
2280
-     * @throws EE_Error
2281
-     * @throws InvalidArgumentException
2282
-     * @throws InvalidDataTypeException
2283
-     * @throws InvalidInterfaceException
2284
-     */
2285
-    protected function _handle_payment_processor_exception(Exception $e)
2286
-    {
2287
-        EE_Error::add_error(
2288
-            sprintf(
2289
-                esc_html__(
2290
-                    'The payment could not br processed due to a technical issue.%1$sPlease try again or contact %2$s for assistance.||The following Exception was thrown in %4$s on line %5$s:%1$s%3$s',
2291
-                    'event_espresso'
2292
-                ),
2293
-                '<br/>',
2294
-                EE_Registry::instance()->CFG->organization->get_pretty('email'),
2295
-                $e->getMessage(),
2296
-                $e->getFile(),
2297
-                $e->getLine()
2298
-            ),
2299
-            __FILE__,
2300
-            __FUNCTION__,
2301
-            __LINE__
2302
-        );
2303
-    }
2304
-
2305
-
2306
-    /**
2307
-     * _get_return_url
2308
-     *
2309
-     * @param EE_Payment_Method $payment_method
2310
-     * @return string
2311
-     * @throws EE_Error
2312
-     * @throws ReflectionException
2313
-     */
2314
-    protected function _get_return_url(EE_Payment_Method $payment_method)
2315
-    {
2316
-        $return_url = '';
2317
-        switch ($payment_method->type_obj()->payment_occurs()) {
2318
-            case EE_PMT_Base::offsite:
2319
-                $return_url = add_query_arg(
2320
-                    [
2321
-                        'action'                     => 'process_gateway_response',
2322
-                        'selected_method_of_payment' => $this->checkout->selected_method_of_payment,
2323
-                        'spco_txn'                   => $this->checkout->transaction->ID(),
2324
-                    ],
2325
-                    $this->reg_step_url()
2326
-                );
2327
-                break;
2328
-            case EE_PMT_Base::onsite:
2329
-            case EE_PMT_Base::offline:
2330
-                $return_url = $this->checkout->next_step->reg_step_url();
2331
-                break;
2332
-        }
2333
-        return $return_url;
2334
-    }
2335
-
2336
-
2337
-    /**
2338
-     * _validate_payment
2339
-     *
2340
-     * @param EE_Payment $payment
2341
-     * @return EE_Payment|FALSE
2342
-     * @throws EE_Error
2343
-     * @throws InvalidArgumentException
2344
-     * @throws InvalidDataTypeException
2345
-     * @throws InvalidInterfaceException
2346
-     */
2347
-    private function _validate_payment($payment = null)
2348
-    {
2349
-        if ($this->checkout->payment_method->is_off_line()) {
2350
-            return true;
2351
-        }
2352
-        // verify payment object
2353
-        if (! $payment instanceof EE_Payment) {
2354
-            // not a payment
2355
-            EE_Error::add_error(
2356
-                sprintf(
2357
-                    esc_html__(
2358
-                        'A valid payment was not generated due to a technical issue.%1$sPlease try again or contact %2$s for assistance.',
2359
-                        'event_espresso'
2360
-                    ),
2361
-                    '<br/>',
2362
-                    EE_Registry::instance()->CFG->organization->get_pretty('email')
2363
-                ),
2364
-                __FILE__,
2365
-                __FUNCTION__,
2366
-                __LINE__
2367
-            );
2368
-            return false;
2369
-        }
2370
-        return $payment;
2371
-    }
2372
-
2373
-
2374
-    /**
2375
-     * _post_payment_processing
2376
-     *
2377
-     * @param EE_Payment|bool $payment
2378
-     * @return bool|EE_Payment
2379
-     * @throws EE_Error
2380
-     * @throws InvalidArgumentException
2381
-     * @throws InvalidDataTypeException
2382
-     * @throws InvalidInterfaceException
2383
-     * @throws ReflectionException
2384
-     */
2385
-    private function _post_payment_processing($payment = null)
2386
-    {
2387
-        // Off-Line payment?
2388
-        if ($payment === true) {
2389
-            return true;
2390
-        }
2391
-        if ($payment instanceof EE_Payment) {
2392
-            // Should the user be redirected?
2393
-            if ($payment->redirect_url()) {
2394
-                do_action('AHEE_log', __CLASS__, __FUNCTION__, $payment->redirect_url(), '$payment->redirect_url()');
2395
-                $this->checkout->redirect      = true;
2396
-                $this->checkout->redirect_form = $payment->redirect_form();
2397
-                $this->checkout->redirect_url  = $this->reg_step_url('redirect_form');
2398
-                // set JSON response
2399
-                $this->checkout->json_response->set_redirect_form($this->checkout->redirect_form);
2400
-                // and lastly, let's bump the payment status to pending
2401
-                $payment->set_status(EEM_Payment::status_id_pending);
2402
-                $payment->save();
2403
-            } elseif (! $this->_process_payment_status($payment, EE_PMT_Base::onsite)) {
2404
-                // User shouldn't be redirected. So let's process it here.
2405
-                // $this->_setup_redirect_for_next_step();
2406
-                $this->checkout->continue_reg = false;
2407
-            }
2408
-            return $payment;
2409
-        }
2410
-        // ummm ya... not Off-Line, not On-Site, not off-Site ????
2411
-        $this->checkout->continue_reg = false;
2412
-        return false;
2413
-    }
2414
-
2415
-
2416
-    /**
2417
-     *    _process_payment_status
2418
-     *
2419
-     * @type    EE_Payment $payment
2420
-     * @param string       $payment_occurs
2421
-     * @return bool
2422
-     * @throws EE_Error
2423
-     * @throws InvalidArgumentException
2424
-     * @throws InvalidDataTypeException
2425
-     * @throws InvalidInterfaceException
2426
-     */
2427
-    private function _process_payment_status($payment, $payment_occurs = EE_PMT_Base::offline)
2428
-    {
2429
-        // off-line payment? carry on
2430
-        if ($payment_occurs === EE_PMT_Base::offline) {
2431
-            return true;
2432
-        }
2433
-        // verify payment validity
2434
-        if ($payment instanceof EE_Payment) {
2435
-            do_action('AHEE_log', __CLASS__, __FUNCTION__, $payment->status(), '$payment->status()');
2436
-            $msg = $payment->gateway_response();
2437
-            // check results
2438
-            switch ($payment->status()) {
2439
-                // good payment
2440
-                case EEM_Payment::status_id_approved:
2441
-                    EE_Error::add_success(
2442
-                        esc_html__('Your payment was processed successfully.', 'event_espresso'),
2443
-                        __FILE__,
2444
-                        __FUNCTION__,
2445
-                        __LINE__
2446
-                    );
2447
-                    return true;
2448
-                // slow payment
2449
-                case EEM_Payment::status_id_pending:
2450
-                    if (empty($msg)) {
2451
-                        $msg = esc_html__(
2452
-                            'Your payment appears to have been processed successfully, but the Instant Payment Notification has not yet been received. It should arrive shortly.',
2453
-                            'event_espresso'
2454
-                        );
2455
-                    }
2456
-                    EE_Error::add_success($msg, __FILE__, __FUNCTION__, __LINE__);
2457
-                    return true;
2458
-                // don't wanna payment
2459
-                case EEM_Payment::status_id_cancelled:
2460
-                    if (empty($msg)) {
2461
-                        $msg = _n(
2462
-                            'Payment cancelled. Please try again.',
2463
-                            'Payment cancelled. Please try again or select another method of payment.',
2464
-                            count($this->checkout->available_payment_methods),
2465
-                            'event_espresso'
2466
-                        );
2467
-                    }
2468
-                    EE_Error::add_attention($msg, __FILE__, __FUNCTION__, __LINE__);
2469
-                    return false;
2470
-                // not enough payment
2471
-                case EEM_Payment::status_id_declined:
2472
-                    if (empty($msg)) {
2473
-                        $msg = _n(
2474
-                            'We\'re sorry but your payment was declined. Please try again.',
2475
-                            'We\'re sorry but your payment was declined. Please try again or select another method of payment.',
2476
-                            count($this->checkout->available_payment_methods),
2477
-                            'event_espresso'
2478
-                        );
2479
-                    }
2480
-                    EE_Error::add_attention($msg, __FILE__, __FUNCTION__, __LINE__);
2481
-                    return false;
2482
-                // bad payment
2483
-                case EEM_Payment::status_id_failed:
2484
-                    if (! empty($msg)) {
2485
-                        EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
2486
-                        return false;
2487
-                    }
2488
-                    // default to error below
2489
-                    break;
2490
-            }
2491
-        }
2492
-        // off-site payment gateway responses are too unreliable, so let's just assume that
2493
-        // the payment processing is just running slower than the registrant's request
2494
-        if ($payment_occurs === EE_PMT_Base::offsite) {
2495
-            return true;
2496
-        }
2497
-        EE_Error::add_error(
2498
-            sprintf(
2499
-                esc_html__(
2500
-                    'Your payment could not be processed successfully due to a technical issue.%sPlease try again or contact %s for assistance.',
2501
-                    'event_espresso'
2502
-                ),
2503
-                '<br/>',
2504
-                EE_Registry::instance()->CFG->organization->get_pretty('email')
2505
-            ),
2506
-            __FILE__,
2507
-            __FUNCTION__,
2508
-            __LINE__
2509
-        );
2510
-        return false;
2511
-    }
2512
-
2513
-
2514
-
2515
-
2516
-
2517
-
2518
-    /********************************************************************************************************/
2519
-    /**********************************  PROCESS GATEWAY RESPONSE  **********************************/
2520
-    /********************************************************************************************************/
2521
-    /**
2522
-     * process_gateway_response
2523
-     * this is the return point for Off-Site Payment Methods
2524
-     * It will attempt to "handle the IPN" if it appears that this has not already occurred,
2525
-     * otherwise, it will load up the last payment made for the TXN.
2526
-     * If the payment retrieved looks good, it will then either:
2527
-     *    complete the current step and allow advancement to the next reg step
2528
-     *        or present the payment options again
2529
-     *
2530
-     * @return bool
2531
-     * @throws EE_Error
2532
-     * @throws InvalidArgumentException
2533
-     * @throws ReflectionException
2534
-     * @throws InvalidDataTypeException
2535
-     * @throws InvalidInterfaceException
2536
-     */
2537
-    public function process_gateway_response()
2538
-    {
2539
-        // how have they chosen to pay?
2540
-        $this->checkout->selected_method_of_payment = $this->_get_selected_method_of_payment(true);
2541
-        // get EE_Payment_Method object
2542
-        if (! $this->checkout->payment_method = $this->_get_payment_method_for_selected_method_of_payment()) {
2543
-            $this->checkout->continue_reg = false;
2544
-            return false;
2545
-        }
2546
-        if (! $this->checkout->payment_method->is_off_site()) {
2547
-            return false;
2548
-        }
2549
-        $this->_validate_offsite_return();
2550
-        // verify TXN
2551
-        if ($this->checkout->transaction instanceof EE_Transaction) {
2552
-            $gateway = $this->checkout->payment_method->type_obj()->get_gateway();
2553
-            if (! $gateway instanceof EE_Offsite_Gateway) {
2554
-                $this->checkout->continue_reg = false;
2555
-                return false;
2556
-            }
2557
-            $payment = $this->_process_off_site_payment($gateway);
2558
-            $payment = $this->_process_cancelled_payments($payment);
2559
-            $payment = $this->_validate_payment($payment);
2560
-            // if payment was not declined by the payment gateway or cancelled by the registrant
2561
-            if ($this->_process_payment_status($payment, EE_PMT_Base::offsite)) {
2562
-                // $this->_setup_redirect_for_next_step();
2563
-                // store that for later
2564
-                $this->checkout->payment = $payment;
2565
-                // mark this reg step as completed, as long as gateway doesn't use a separate IPN request,
2566
-                // because we will complete this step during the IPN processing then
2567
-                if (! $this->handle_IPN_in_this_request()) {
2568
-                    $this->set_completed();
2569
-                }
2570
-                return true;
2571
-            }
2572
-        }
2573
-        // DEBUG LOG
2574
-        // $this->checkout->log(
2575
-        //     __CLASS__,
2576
-        //     __FUNCTION__,
2577
-        //     __LINE__,
2578
-        //     array('payment' => $payment)
2579
-        // );
2580
-        $this->checkout->continue_reg = false;
2581
-        return false;
2582
-    }
2583
-
2584
-
2585
-    /**
2586
-     * _validate_return
2587
-     *
2588
-     * @return void
2589
-     * @throws EE_Error
2590
-     * @throws InvalidArgumentException
2591
-     * @throws InvalidDataTypeException
2592
-     * @throws InvalidInterfaceException
2593
-     * @throws ReflectionException
2594
-     */
2595
-    private function _validate_offsite_return()
2596
-    {
2597
-        $TXN_ID = $this->request->getRequestParam('spco_txn', 0, 'int');
2598
-        if ($TXN_ID !== $this->checkout->transaction->ID()) {
2599
-            // Houston... we might have a problem
2600
-            $invalid_TXN = false;
2601
-            // first gather some info
2602
-            $valid_TXN          = EEM_Transaction::instance()->get_one_by_ID($TXN_ID);
2603
-            $primary_registrant = $valid_TXN instanceof EE_Transaction
2604
-                ? $valid_TXN->primary_registration()
2605
-                : null;
2606
-            // let's start by retrieving the cart for this TXN
2607
-            $cart = $this->checkout->get_cart_for_transaction($this->checkout->transaction);
2608
-            if ($cart instanceof EE_Cart) {
2609
-                // verify that the current cart has tickets
2610
-                $tickets = $cart->get_tickets();
2611
-                if (empty($tickets)) {
2612
-                    $invalid_TXN = true;
2613
-                }
2614
-            } else {
2615
-                $invalid_TXN = true;
2616
-            }
2617
-            $valid_TXN_SID = $primary_registrant instanceof EE_Registration
2618
-                ? $primary_registrant->session_ID()
2619
-                : null;
2620
-            // validate current Session ID and compare against valid TXN session ID
2621
-            if (
2622
-                $invalid_TXN // if this is already true, then skip other checks
2623
-                || EE_Session::instance()->id() === null
2624
-                || (
2625
-                    // WARNING !!!
2626
-                    // this could be PayPal sending back duplicate requests (ya they do that)
2627
-                    // or it **could** mean someone is simply registering AGAIN after having just done so
2628
-                    // so now we need to determine if this current TXN looks valid or not
2629
-                    // and whether this reg step has even been started ?
2630
-                    EE_Session::instance()->id() === $valid_TXN_SID
2631
-                    // really? you're half way through this reg step, but you never started it ?
2632
-                    && $this->checkout->transaction->reg_step_completed($this->slug()) === false
2633
-                )
2634
-            ) {
2635
-                $invalid_TXN = true;
2636
-            }
2637
-            if ($invalid_TXN) {
2638
-                // is the valid TXN completed ?
2639
-                if ($valid_TXN instanceof EE_Transaction) {
2640
-                    // has this step even been started ?
2641
-                    $reg_step_completed = $valid_TXN->reg_step_completed($this->slug());
2642
-                    if ($reg_step_completed !== false && $reg_step_completed !== true) {
2643
-                        // so it **looks** like this is a double request from PayPal
2644
-                        // so let's try to pick up where we left off
2645
-                        $this->checkout->transaction = $valid_TXN;
2646
-                        $this->checkout->refresh_all_entities(true);
2647
-                        return;
2648
-                    }
2649
-                }
2650
-                // you appear to be lost?
2651
-                $this->_redirect_wayward_request($primary_registrant);
2652
-            }
2653
-        }
2654
-    }
2655
-
2656
-
2657
-    /**
2658
-     * _redirect_wayward_request
2659
-     *
2660
-     * @param EE_Registration|null $primary_registrant
2661
-     * @return void
2662
-     * @throws EE_Error
2663
-     * @throws InvalidArgumentException
2664
-     * @throws InvalidDataTypeException
2665
-     * @throws InvalidInterfaceException
2666
-     * @throws ReflectionException
2667
-     */
2668
-    private function _redirect_wayward_request(EE_Registration $primary_registrant)
2669
-    {
2670
-        if (! $primary_registrant instanceof EE_Registration) {
2671
-            // try redirecting based on the current TXN
2672
-            $primary_registrant = $this->checkout->transaction instanceof EE_Transaction
2673
-                ? $this->checkout->transaction->primary_registration()
2674
-                : null;
2675
-        }
2676
-        if (! $primary_registrant instanceof EE_Registration) {
2677
-            EE_Error::add_error(
2678
-                sprintf(
2679
-                    esc_html__(
2680
-                        'Invalid information was received from the Off-Site Payment Processor and your Transaction details could not be retrieved from the database.%1$sPlease try again or contact %2$s for assistance.',
2681
-                        'event_espresso'
2682
-                    ),
2683
-                    '<br/>',
2684
-                    EE_Registry::instance()->CFG->organization->get_pretty('email')
2685
-                ),
2686
-                __FILE__,
2687
-                __FUNCTION__,
2688
-                __LINE__
2689
-            );
2690
-            return;
2691
-        }
2692
-        // make sure transaction is not locked
2693
-        $this->checkout->transaction->unlock();
2694
-        wp_safe_redirect(
2695
-            add_query_arg(
2696
-                [
2697
-                    'e_reg_url_link' => $primary_registrant->reg_url_link(),
2698
-                ],
2699
-                $this->checkout->thank_you_page_url
2700
-            )
2701
-        );
2702
-        exit();
2703
-    }
2704
-
2705
-
2706
-    /**
2707
-     * _process_off_site_payment
2708
-     *
2709
-     * @param EE_Offsite_Gateway $gateway
2710
-     * @return EE_Payment
2711
-     * @throws EE_Error
2712
-     * @throws InvalidArgumentException
2713
-     * @throws InvalidDataTypeException
2714
-     * @throws InvalidInterfaceException
2715
-     * @throws ReflectionException
2716
-     */
2717
-    private function _process_off_site_payment(EE_Offsite_Gateway $gateway)
2718
-    {
2719
-        try {
2720
-            $request      = LoaderFactory::getLoader()->getShared(RequestInterface::class);
2721
-            $request_data = $request->requestParams();
2722
-            // if gateway uses_separate_IPN_request, then we don't have to process the IPN manually
2723
-            $this->set_handle_IPN_in_this_request(
2724
-                $gateway->handle_IPN_in_this_request($request_data, false)
2725
-            );
2726
-            if ($this->handle_IPN_in_this_request()) {
2727
-                // get payment details and process results
2728
-                /** @type EE_Payment_Processor $payment_processor */
2729
-                $payment_processor = EE_Registry::instance()->load_core('Payment_Processor');
2730
-                $payment           = $payment_processor->process_ipn(
2731
-                    $request_data,
2732
-                    $this->checkout->transaction,
2733
-                    $this->checkout->payment_method,
2734
-                    true,
2735
-                    false
2736
-                );
2737
-                // $payment_source = 'process_ipn';
2738
-            } else {
2739
-                $payment = $this->checkout->transaction->last_payment();
2740
-                // $payment_source = 'last_payment';
2741
-            }
2742
-        } catch (Exception $e) {
2743
-            // let's just eat the exception and try to move on using any previously set payment info
2744
-            $payment = $this->checkout->transaction->last_payment();
2745
-            // $payment_source = 'last_payment after Exception';
2746
-            // but if we STILL don't have a payment object
2747
-            if (! $payment instanceof EE_Payment) {
2748
-                // then we'll object ! ( not object like a thing... but object like what a lawyer says ! )
2749
-                $this->_handle_payment_processor_exception($e);
2750
-            }
2751
-        }
2752
-        return $payment;
2753
-    }
2754
-
2755
-
2756
-    /**
2757
-     * _process_cancelled_payments
2758
-     * just makes sure that the payment status gets updated correctly
2759
-     * so tha tan error isn't generated during payment validation
2760
-     *
2761
-     * @param EE_Payment $payment
2762
-     * @return EE_Payment|null
2763
-     * @throws EE_Error
2764
-     */
2765
-    private function _process_cancelled_payments($payment = null)
2766
-    {
2767
-        if (
2768
-            $payment instanceof EE_Payment
2769
-            && $this->request->requestParamIsSet('ee_cancel_payment')
2770
-            && $payment->status() === EEM_Payment::status_id_failed
2771
-        ) {
2772
-            $payment->set_status(EEM_Payment::status_id_cancelled);
2773
-        }
2774
-        return $payment;
2775
-    }
2776
-
2777
-
2778
-    /**
2779
-     *    get_transaction_details_for_gateways
2780
-     *
2781
-     * @access    public
2782
-     * @return void
2783
-     * @throws EE_Error
2784
-     * @throws InvalidArgumentException
2785
-     * @throws ReflectionException
2786
-     * @throws InvalidDataTypeException
2787
-     * @throws InvalidInterfaceException
2788
-     */
2789
-    public function get_transaction_details_for_gateways()
2790
-    {
2791
-        $txn_details = [];
2792
-        // ya gotta make a choice man
2793
-        if (empty($this->checkout->selected_method_of_payment)) {
2794
-            $txn_details = [
2795
-                'error' => esc_html__('Please select a method of payment before proceeding.', 'event_espresso'),
2796
-            ];
2797
-        }
2798
-        // get EE_Payment_Method object
2799
-        if (
2800
-            empty($txn_details)
2801
-            && ! $this->checkout->payment_method = $this->_get_payment_method_for_selected_method_of_payment()
2802
-        ) {
2803
-            $txn_details = [
2804
-                'selected_method_of_payment' => $this->checkout->selected_method_of_payment,
2805
-                'error'                      => esc_html__(
2806
-                    'A valid Payment Method could not be determined.',
2807
-                    'event_espresso'
2808
-                ),
2809
-            ];
2810
-        }
2811
-        if (empty($txn_details) && $this->checkout->transaction instanceof EE_Transaction) {
2812
-            $return_url  = $this->_get_return_url($this->checkout->payment_method);
2813
-            $txn_details = [
2814
-                'TXN_ID'         => $this->checkout->transaction->ID(),
2815
-                'TXN_timestamp'  => $this->checkout->transaction->datetime(),
2816
-                'TXN_total'      => $this->checkout->transaction->total(),
2817
-                'TXN_paid'       => $this->checkout->transaction->paid(),
2818
-                'TXN_reg_steps'  => $this->checkout->transaction->reg_steps(),
2819
-                'STS_ID'         => $this->checkout->transaction->status_ID(),
2820
-                'PMD_ID'         => $this->checkout->transaction->payment_method_ID(),
2821
-                'payment_amount' => $this->checkout->amount_owing,
2822
-                'return_url'     => $return_url,
2823
-                'cancel_url'     => add_query_arg(['ee_cancel_payment' => true], $return_url),
2824
-                'notify_url'     => EE_Config::instance()->core->txn_page_url(
2825
-                    [
2826
-                        'e_reg_url_link'    => $this->checkout->transaction->primary_registration()->reg_url_link(),
2827
-                        'ee_payment_method' => $this->checkout->payment_method->slug(),
2828
-                    ]
2829
-                ),
2830
-            ];
2831
-        }
2832
-        echo wp_json_encode($txn_details);
2833
-        exit();
2834
-    }
2835
-
2836
-
2837
-    /**
2838
-     *    __sleep
2839
-     * to conserve db space, let's remove the reg_form and the EE_Checkout object from EE_SPCO_Reg_Step objects upon
2840
-     * serialization EE_Checkout will handle the reimplementation of itself upon waking, but we won't bother with the
2841
-     * reg form, because if needed, it will be regenerated anyways
2842
-     *
2843
-     * @return array
2844
-     */
2845
-    public function __sleep()
2846
-    {
2847
-        // remove the reg form and the checkout
2848
-        return array_diff(array_keys(get_object_vars($this)), ['reg_form', 'checkout', 'line_item_display']);
2849
-    }
22
+	/**
23
+	 * @var EE_Line_Item_Display $Line_Item_Display
24
+	 */
25
+	protected $line_item_display;
26
+
27
+	/**
28
+	 * @var boolean $handle_IPN_in_this_request
29
+	 */
30
+	protected $handle_IPN_in_this_request = false;
31
+
32
+
33
+	/**
34
+	 *    set_hooks - for hooking into EE Core, other modules, etc
35
+	 *
36
+	 * @access    public
37
+	 * @return    void
38
+	 */
39
+	public static function set_hooks()
40
+	{
41
+		add_filter(
42
+			'FHEE__SPCO__EE_Line_Item_Filter_Collection',
43
+			['EE_SPCO_Reg_Step_Payment_Options', 'add_spco_line_item_filters']
44
+		);
45
+		add_action(
46
+			'wp_ajax_switch_spco_billing_form',
47
+			['EE_SPCO_Reg_Step_Payment_Options', 'switch_spco_billing_form']
48
+		);
49
+		add_action(
50
+			'wp_ajax_nopriv_switch_spco_billing_form',
51
+			['EE_SPCO_Reg_Step_Payment_Options', 'switch_spco_billing_form']
52
+		);
53
+		add_action('wp_ajax_save_payer_details', ['EE_SPCO_Reg_Step_Payment_Options', 'save_payer_details']);
54
+		add_action(
55
+			'wp_ajax_nopriv_save_payer_details',
56
+			['EE_SPCO_Reg_Step_Payment_Options', 'save_payer_details']
57
+		);
58
+		add_action(
59
+			'wp_ajax_get_transaction_details_for_gateways',
60
+			['EE_SPCO_Reg_Step_Payment_Options', 'get_transaction_details']
61
+		);
62
+		add_action(
63
+			'wp_ajax_nopriv_get_transaction_details_for_gateways',
64
+			['EE_SPCO_Reg_Step_Payment_Options', 'get_transaction_details']
65
+		);
66
+		add_filter(
67
+			'FHEE__EED_Recaptcha___bypass_recaptcha__bypass_request_params_array',
68
+			['EE_SPCO_Reg_Step_Payment_Options', 'bypass_recaptcha_for_load_payment_method'],
69
+			10,
70
+			1
71
+		);
72
+	}
73
+
74
+
75
+	/**
76
+	 *    ajax switch_spco_billing_form
77
+	 *
78
+	 */
79
+	public static function switch_spco_billing_form()
80
+	{
81
+		EED_Single_Page_Checkout::process_ajax_request('switch_payment_method');
82
+	}
83
+
84
+
85
+	/**
86
+	 *    ajax save_payer_details
87
+	 *
88
+	 */
89
+	public static function save_payer_details()
90
+	{
91
+		EED_Single_Page_Checkout::process_ajax_request('save_payer_details_via_ajax');
92
+	}
93
+
94
+
95
+	/**
96
+	 *    ajax get_transaction_details
97
+	 *
98
+	 */
99
+	public static function get_transaction_details()
100
+	{
101
+		EED_Single_Page_Checkout::process_ajax_request('get_transaction_details_for_gateways');
102
+	}
103
+
104
+
105
+	/**
106
+	 * bypass_recaptcha_for_load_payment_method
107
+	 *
108
+	 * @access public
109
+	 * @return array
110
+	 * @throws InvalidArgumentException
111
+	 * @throws InvalidDataTypeException
112
+	 * @throws InvalidInterfaceException
113
+	 */
114
+	public static function bypass_recaptcha_for_load_payment_method()
115
+	{
116
+		return [
117
+			'EESID'  => EE_Registry::instance()->SSN->id(),
118
+			'step'   => 'payment_options',
119
+			'action' => 'spco_billing_form',
120
+		];
121
+	}
122
+
123
+
124
+	/**
125
+	 *    class constructor
126
+	 *
127
+	 * @access    public
128
+	 * @param EE_Checkout $checkout
129
+	 */
130
+	public function __construct(EE_Checkout $checkout)
131
+	{
132
+		$this->request   = EED_Single_Page_Checkout::getRequest();
133
+		$this->_slug     = 'payment_options';
134
+		$this->_name     = esc_html__('Payment Options', 'event_espresso');
135
+		$this->_template = SPCO_REG_STEPS_PATH . $this->_slug . '/payment_options_main.template.php';
136
+		$this->checkout  = $checkout;
137
+		$this->_reset_success_message();
138
+		$this->set_instructions(
139
+			esc_html__(
140
+				'Please select a method of payment and provide any necessary billing information before proceeding.',
141
+				'event_espresso'
142
+			)
143
+		);
144
+	}
145
+
146
+
147
+	/**
148
+	 * @return null
149
+	 */
150
+	public function line_item_display()
151
+	{
152
+		return $this->line_item_display;
153
+	}
154
+
155
+
156
+	/**
157
+	 * @param null $line_item_display
158
+	 */
159
+	public function set_line_item_display($line_item_display)
160
+	{
161
+		$this->line_item_display = $line_item_display;
162
+	}
163
+
164
+
165
+	/**
166
+	 * @return boolean
167
+	 */
168
+	public function handle_IPN_in_this_request()
169
+	{
170
+		return $this->handle_IPN_in_this_request;
171
+	}
172
+
173
+
174
+	/**
175
+	 * @param boolean $handle_IPN_in_this_request
176
+	 */
177
+	public function set_handle_IPN_in_this_request($handle_IPN_in_this_request)
178
+	{
179
+		$this->handle_IPN_in_this_request = filter_var($handle_IPN_in_this_request, FILTER_VALIDATE_BOOLEAN);
180
+	}
181
+
182
+
183
+	/**
184
+	 * translate_js_strings
185
+	 *
186
+	 * @return void
187
+	 */
188
+	public function translate_js_strings()
189
+	{
190
+		EE_Registry::$i18n_js_strings['no_payment_method']      = esc_html__(
191
+			'Please select a method of payment in order to continue.',
192
+			'event_espresso'
193
+		);
194
+		EE_Registry::$i18n_js_strings['invalid_payment_method'] = esc_html__(
195
+			'A valid method of payment could not be determined. Please refresh the page and try again.',
196
+			'event_espresso'
197
+		);
198
+		EE_Registry::$i18n_js_strings['forwarding_to_offsite']  = esc_html__(
199
+			'Forwarding to Secure Payment Provider.',
200
+			'event_espresso'
201
+		);
202
+	}
203
+
204
+
205
+	/**
206
+	 * enqueue_styles_and_scripts
207
+	 *
208
+	 * @return void
209
+	 * @throws EE_Error
210
+	 * @throws InvalidArgumentException
211
+	 * @throws InvalidDataTypeException
212
+	 * @throws InvalidInterfaceException
213
+	 * @throws ReflectionException
214
+	 */
215
+	public function enqueue_styles_and_scripts()
216
+	{
217
+		$transaction = $this->checkout->transaction;
218
+		// if the transaction isn't set or nothing is owed on it, don't enqueue any JS
219
+		if (! $transaction instanceof EE_Transaction || EEH_Money::compare_floats($transaction->remaining(), 0)) {
220
+			return;
221
+		}
222
+		foreach (
223
+			EEM_Payment_Method::instance()->get_all_for_transaction(
224
+				$transaction,
225
+				EEM_Payment_Method::scope_cart
226
+			) as $payment_method
227
+		) {
228
+			$type_obj = $payment_method->type_obj();
229
+			if ($type_obj instanceof EE_PMT_Base) {
230
+				$billing_form = $type_obj->generate_new_billing_form($transaction);
231
+				if ($billing_form instanceof EE_Form_Section_Proper) {
232
+					$billing_form->enqueue_js();
233
+				}
234
+			}
235
+		}
236
+	}
237
+
238
+
239
+	/**
240
+	 * initialize_reg_step
241
+	 *
242
+	 * @return bool
243
+	 * @throws EE_Error
244
+	 * @throws InvalidArgumentException
245
+	 * @throws ReflectionException
246
+	 * @throws InvalidDataTypeException
247
+	 * @throws InvalidInterfaceException
248
+	 */
249
+	public function initialize_reg_step()
250
+	{
251
+		// TODO: if /when we implement donations, then this will need overriding
252
+		if (
253
+			// don't need payment options for:
254
+			// registrations made via the admin
255
+			// completed transactions
256
+			// overpaid transactions
257
+			// $ 0.00 transactions(no payment required)
258
+			! $this->checkout->payment_required()
259
+			// but do NOT remove if current action being called belongs to this reg step
260
+			&& ! is_callable([$this, $this->checkout->action])
261
+			&& ! $this->completed()
262
+		) {
263
+			// and if so, then we no longer need the Payment Options step
264
+			if ($this->is_current_step()) {
265
+				$this->checkout->generate_reg_form = false;
266
+			}
267
+			$this->checkout->remove_reg_step($this->_slug);
268
+			// DEBUG LOG
269
+			// $this->checkout->log( __CLASS__, __FUNCTION__, __LINE__ );
270
+			return false;
271
+		}
272
+		// load EEM_Payment_Method
273
+		EE_Registry::instance()->load_model('Payment_Method');
274
+		// get all active payment methods
275
+		$this->checkout->available_payment_methods = EEM_Payment_Method::instance()->get_all_for_transaction(
276
+			$this->checkout->transaction,
277
+			EEM_Payment_Method::scope_cart
278
+		);
279
+		return true;
280
+	}
281
+
282
+
283
+	/**
284
+	 * @return EE_Form_Section_Proper
285
+	 * @throws EE_Error
286
+	 * @throws InvalidArgumentException
287
+	 * @throws ReflectionException
288
+	 * @throws EntityNotFoundException
289
+	 * @throws InvalidDataTypeException
290
+	 * @throws InvalidInterfaceException
291
+	 * @throws InvalidStatusException
292
+	 */
293
+	public function generate_reg_form()
294
+	{
295
+		// reset in case someone changes their mind
296
+		$this->_reset_selected_method_of_payment();
297
+		// set some defaults
298
+		$this->checkout->selected_method_of_payment = 'payments_closed';
299
+		$registrations_requiring_payment            = [];
300
+		$registrations_for_free_events              = [];
301
+		$registrations_requiring_pre_approval       = [];
302
+		$sold_out_events                            = [];
303
+		$insufficient_spaces_available              = [];
304
+		$no_payment_required                        = true;
305
+		// loop thru registrations to gather info
306
+		$registrations         = $this->checkout->transaction->registrations($this->checkout->reg_cache_where_params);
307
+		$ejected_registrations = EE_SPCO_Reg_Step_Payment_Options::find_registrations_that_lost_their_space(
308
+			$registrations,
309
+			$this->checkout->revisit
310
+		);
311
+		foreach ($registrations as $REG_ID => $registration) {
312
+			/** @var $registration EE_Registration */
313
+			// has this registration lost it's space ?
314
+			if (isset($ejected_registrations[ $REG_ID ])) {
315
+				if ($registration->event()->is_sold_out() || $registration->event()->is_sold_out(true)) {
316
+					$sold_out_events[ $registration->event()->ID() ] = $registration->event();
317
+				} else {
318
+					$insufficient_spaces_available[ $registration->event()->ID() ] = $registration->event();
319
+				}
320
+				continue;
321
+			}
322
+			// event requires admin approval
323
+			if ($registration->status_ID() === EEM_Registration::status_id_not_approved) {
324
+				// add event to list of events with pre-approval reg status
325
+				$registrations_requiring_pre_approval[ $REG_ID ] = $registration;
326
+				do_action(
327
+					'AHEE__EE_SPCO_Reg_Step_Payment_Options__generate_reg_form__event_requires_pre_approval',
328
+					$registration->event(),
329
+					$this
330
+				);
331
+				continue;
332
+			}
333
+			if (
334
+				$this->checkout->revisit
335
+				&& $registration->status_ID() !== EEM_Registration::status_id_approved
336
+				&& (
337
+					$registration->event()->is_sold_out()
338
+					|| $registration->event()->is_sold_out(true)
339
+				)
340
+			) {
341
+				// add event to list of events that are sold out
342
+				$sold_out_events[ $registration->event()->ID() ] = $registration->event();
343
+				do_action(
344
+					'AHEE__EE_SPCO_Reg_Step_Payment_Options__generate_reg_form__sold_out_event',
345
+					$registration->event(),
346
+					$this
347
+				);
348
+				continue;
349
+			}
350
+			// are they allowed to pay now and is there monies owing?
351
+			if ($registration->owes_monies_and_can_pay()) {
352
+				$registrations_requiring_payment[ $REG_ID ] = $registration;
353
+				do_action(
354
+					'AHEE__EE_SPCO_Reg_Step_Payment_Options__generate_reg_form__event_requires_payment',
355
+					$registration->event(),
356
+					$this
357
+				);
358
+			} elseif (
359
+				! $this->checkout->revisit
360
+					  && $registration->status_ID() !== EEM_Registration::status_id_not_approved
361
+					  && $registration->ticket()->is_free()
362
+			) {
363
+				$registrations_for_free_events[ $registration->ticket()->ID() ] = $registration;
364
+			}
365
+		}
366
+		$subsections = [];
367
+		// now decide which template to load
368
+		if (! empty($sold_out_events)) {
369
+			$subsections['sold_out_events'] = $this->_sold_out_events($sold_out_events);
370
+		}
371
+		if (! empty($insufficient_spaces_available)) {
372
+			$subsections['insufficient_space'] = $this->_insufficient_spaces_available(
373
+				$insufficient_spaces_available
374
+			);
375
+		}
376
+		if (! empty($registrations_requiring_pre_approval)) {
377
+			$subsections['registrations_requiring_pre_approval'] = $this->_registrations_requiring_pre_approval(
378
+				$registrations_requiring_pre_approval
379
+			);
380
+		}
381
+		if (! empty($registrations_for_free_events)) {
382
+			$subsections['no_payment_required'] = $this->_no_payment_required($registrations_for_free_events);
383
+		}
384
+		if (! empty($registrations_requiring_payment)) {
385
+			if ($this->checkout->amount_owing > 0) {
386
+				// autoload Line_Item_Display classes
387
+				EEH_Autoloader::register_line_item_filter_autoloaders();
388
+				$line_item_filter_processor = new EE_Line_Item_Filter_Processor(
389
+					apply_filters(
390
+						'FHEE__SPCO__EE_Line_Item_Filter_Collection',
391
+						new EE_Line_Item_Filter_Collection()
392
+					),
393
+					$this->checkout->cart->get_grand_total()
394
+				);
395
+				/** @var EE_Line_Item $filtered_line_item_tree */
396
+				$filtered_line_item_tree = $line_item_filter_processor->process();
397
+				EEH_Autoloader::register_line_item_display_autoloaders();
398
+				$this->set_line_item_display(new EE_Line_Item_Display('spco'));
399
+				$subsections['payment_options'] = $this->_display_payment_options(
400
+					$this->line_item_display->display_line_item(
401
+						$filtered_line_item_tree,
402
+						['registrations' => $registrations]
403
+					)
404
+				);
405
+				$this->checkout->amount_owing   = $filtered_line_item_tree->total();
406
+				$this->_apply_registration_payments_to_amount_owing($registrations);
407
+			}
408
+			$no_payment_required = false;
409
+		} else {
410
+			$this->_hide_reg_step_submit_button_if_revisit();
411
+		}
412
+		$this->_save_selected_method_of_payment();
413
+
414
+		$subsections['default_hidden_inputs'] = $this->reg_step_hidden_inputs();
415
+		$subsections['extra_hidden_inputs']   = $this->_extra_hidden_inputs($no_payment_required);
416
+
417
+		return new EE_Form_Section_Proper(
418
+			[
419
+				'name'            => $this->reg_form_name(),
420
+				'html_id'         => $this->reg_form_name(),
421
+				'subsections'     => $subsections,
422
+				'layout_strategy' => new EE_No_Layout(),
423
+			]
424
+		);
425
+	}
426
+
427
+
428
+	/**
429
+	 * add line item filters required for this reg step
430
+	 * these filters are applied via this line in EE_SPCO_Reg_Step_Payment_Options::set_hooks():
431
+	 *        add_filter( 'FHEE__SPCO__EE_Line_Item_Filter_Collection', array( 'EE_SPCO_Reg_Step_Payment_Options',
432
+	 *        'add_spco_line_item_filters' ) ); so any code that wants to use the same set of filters during the
433
+	 *        payment options reg step, can apply these filters via the following: apply_filters(
434
+	 *        'FHEE__SPCO__EE_Line_Item_Filter_Collection', new EE_Line_Item_Filter_Collection() ) or to an existing
435
+	 *        filter collection by passing that instead of instantiating a new collection
436
+	 *
437
+	 * @param EE_Line_Item_Filter_Collection $line_item_filter_collection
438
+	 * @return EE_Line_Item_Filter_Collection
439
+	 * @throws EE_Error
440
+	 * @throws InvalidArgumentException
441
+	 * @throws ReflectionException
442
+	 * @throws EntityNotFoundException
443
+	 * @throws InvalidDataTypeException
444
+	 * @throws InvalidInterfaceException
445
+	 * @throws InvalidStatusException
446
+	 */
447
+	public static function add_spco_line_item_filters(EE_Line_Item_Filter_Collection $line_item_filter_collection)
448
+	{
449
+		if (! EE_Registry::instance()->SSN instanceof EE_Session) {
450
+			return $line_item_filter_collection;
451
+		}
452
+		if (! EE_Registry::instance()->SSN->checkout() instanceof EE_Checkout) {
453
+			return $line_item_filter_collection;
454
+		}
455
+		if (! EE_Registry::instance()->SSN->checkout()->transaction instanceof EE_Transaction) {
456
+			return $line_item_filter_collection;
457
+		}
458
+		$line_item_filter_collection->add(
459
+			new EE_Billable_Line_Item_Filter(
460
+				EE_SPCO_Reg_Step_Payment_Options::remove_ejected_registrations(
461
+					EE_Registry::instance()->SSN->checkout()->transaction->registrations(
462
+						EE_Registry::instance()->SSN->checkout()->reg_cache_where_params
463
+					)
464
+				)
465
+			)
466
+		);
467
+		$line_item_filter_collection->add(new EE_Non_Zero_Line_Item_Filter());
468
+		return $line_item_filter_collection;
469
+	}
470
+
471
+
472
+	/**
473
+	 * remove_ejected_registrations
474
+	 * if a registrant has lost their potential space at an event due to lack of payment,
475
+	 * then this method removes them from the list of registrations being paid for during this request
476
+	 *
477
+	 * @param EE_Registration[] $registrations
478
+	 * @return EE_Registration[]
479
+	 * @throws EE_Error
480
+	 * @throws InvalidArgumentException
481
+	 * @throws ReflectionException
482
+	 * @throws EntityNotFoundException
483
+	 * @throws InvalidDataTypeException
484
+	 * @throws InvalidInterfaceException
485
+	 * @throws InvalidStatusException
486
+	 */
487
+	public static function remove_ejected_registrations(array $registrations)
488
+	{
489
+		$ejected_registrations = EE_SPCO_Reg_Step_Payment_Options::find_registrations_that_lost_their_space(
490
+			$registrations,
491
+			EE_Registry::instance()->SSN->checkout()->revisit
492
+		);
493
+		foreach ($registrations as $REG_ID => $registration) {
494
+			// has this registration lost it's space ?
495
+			if (isset($ejected_registrations[ $REG_ID ])) {
496
+				unset($registrations[ $REG_ID ]);
497
+			}
498
+		}
499
+		return $registrations;
500
+	}
501
+
502
+
503
+	/**
504
+	 * find_registrations_that_lost_their_space
505
+	 * If a registrant chooses an offline payment method like Invoice,
506
+	 * then no space is reserved for them at the event until they fully pay fo that site
507
+	 * (unless the event's default reg status is set to APPROVED)
508
+	 * if a registrant then later returns to pay, but the number of spaces available has been reduced due to sales,
509
+	 * then this method will determine which registrations have lost the ability to complete the reg process.
510
+	 *
511
+	 * @param EE_Registration[] $registrations
512
+	 * @param bool              $revisit
513
+	 * @return array
514
+	 * @throws EE_Error
515
+	 * @throws InvalidArgumentException
516
+	 * @throws ReflectionException
517
+	 * @throws EntityNotFoundException
518
+	 * @throws InvalidDataTypeException
519
+	 * @throws InvalidInterfaceException
520
+	 * @throws InvalidStatusException
521
+	 */
522
+	public static function find_registrations_that_lost_their_space(array $registrations, $revisit = false)
523
+	{
524
+		// registrations per event
525
+		$event_reg_count = [];
526
+		// spaces left per event
527
+		$event_spaces_remaining = [];
528
+		// tickets left sorted by ID
529
+		$tickets_remaining = [];
530
+		// registrations that have lost their space
531
+		$ejected_registrations = [];
532
+		foreach ($registrations as $REG_ID => $registration) {
533
+			if (
534
+				$registration->status_ID() === EEM_Registration::status_id_approved
535
+				|| apply_filters(
536
+					'FHEE__EE_SPCO_Reg_Step_Payment_Options__find_registrations_that_lost_their_space__allow_reg_payment',
537
+					false,
538
+					$registration,
539
+					$revisit
540
+				)
541
+			) {
542
+				continue;
543
+			}
544
+			$EVT_ID = $registration->event_ID();
545
+			$ticket = $registration->ticket();
546
+			if (! isset($tickets_remaining[ $ticket->ID() ])) {
547
+				$tickets_remaining[ $ticket->ID() ] = $ticket->remaining();
548
+			}
549
+			if ($tickets_remaining[ $ticket->ID() ] > 0) {
550
+				if (! isset($event_reg_count[ $EVT_ID ])) {
551
+					$event_reg_count[ $EVT_ID ] = 0;
552
+				}
553
+				$event_reg_count[ $EVT_ID ]++;
554
+				if (! isset($event_spaces_remaining[ $EVT_ID ])) {
555
+					$event_spaces_remaining[ $EVT_ID ] = $registration->event()->spaces_remaining_for_sale();
556
+				}
557
+			}
558
+			if (
559
+				$revisit
560
+				&& ($tickets_remaining[ $ticket->ID() ] === 0
561
+					|| $event_reg_count[ $EVT_ID ] > $event_spaces_remaining[ $EVT_ID ]
562
+				)
563
+			) {
564
+				$ejected_registrations[ $REG_ID ] = $registration->event();
565
+				if ($registration->status_ID() !== EEM_Registration::status_id_wait_list) {
566
+					/** @type EE_Registration_Processor $registration_processor */
567
+					$registration_processor = EE_Registry::instance()->load_class('Registration_Processor');
568
+					// at this point, we should have enough details about the registrant to consider the registration
569
+					// NOT incomplete
570
+					$registration_processor->manually_update_registration_status(
571
+						$registration,
572
+						EEM_Registration::status_id_wait_list
573
+					);
574
+				}
575
+			}
576
+		}
577
+		return $ejected_registrations;
578
+	}
579
+
580
+
581
+	/**
582
+	 * _hide_reg_step_submit_button
583
+	 * removes the html for the reg step submit button
584
+	 * by replacing it with an empty string via filter callback
585
+	 *
586
+	 * @return void
587
+	 */
588
+	protected function _adjust_registration_status_if_event_old_sold()
589
+	{
590
+	}
591
+
592
+
593
+	/**
594
+	 * _hide_reg_step_submit_button
595
+	 * removes the html for the reg step submit button
596
+	 * by replacing it with an empty string via filter callback
597
+	 *
598
+	 * @return void
599
+	 */
600
+	protected function _hide_reg_step_submit_button_if_revisit()
601
+	{
602
+		if ($this->checkout->revisit) {
603
+			add_filter('FHEE__EE_SPCO_Reg_Step__reg_step_submit_button__sbmt_btn_html', '__return_empty_string');
604
+		}
605
+	}
606
+
607
+
608
+	/**
609
+	 * sold_out_events
610
+	 * displays notices regarding events that have sold out since hte registrant first signed up
611
+	 *
612
+	 * @param EE_Event[] $sold_out_events_array
613
+	 * @return EE_Form_Section_Proper
614
+	 * @throws EE_Error
615
+	 */
616
+	private function _sold_out_events($sold_out_events_array = [])
617
+	{
618
+		// set some defaults
619
+		$this->checkout->selected_method_of_payment = 'events_sold_out';
620
+		$sold_out_events                            = '';
621
+		foreach ($sold_out_events_array as $sold_out_event) {
622
+			$sold_out_events .= EEH_HTML::li(
623
+				EEH_HTML::span(
624
+					'  ' . $sold_out_event->name(),
625
+					'',
626
+					'dashicons dashicons-marker ee-icon-size-16 pink-text'
627
+				)
628
+			);
629
+		}
630
+		return new EE_Form_Section_Proper(
631
+			[
632
+				'layout_strategy' => new EE_Template_Layout(
633
+					[
634
+						'layout_template_file' => SPCO_REG_STEPS_PATH
635
+												  . $this->_slug
636
+												  . '/sold_out_events.template.php',
637
+						'template_args'        => apply_filters(
638
+							'FHEE__EE_SPCO_Reg_Step_Payment_Options___sold_out_events__template_args',
639
+							[
640
+								'sold_out_events'     => $sold_out_events,
641
+								'sold_out_events_msg' => apply_filters(
642
+									'FHEE__EE_SPCO_Reg_Step_Payment_Options___sold_out_events__sold_out_events_msg',
643
+									sprintf(
644
+										esc_html__(
645
+											'It appears that the event you were about to make a payment for has sold out since you first registered. If you have already made a partial payment towards this event, please contact the event administrator for a refund.%3$s%3$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.%2$s',
646
+											'event_espresso'
647
+										),
648
+										'<strong>',
649
+										'</strong>',
650
+										'<br />'
651
+									)
652
+								),
653
+							]
654
+						),
655
+					]
656
+				),
657
+			]
658
+		);
659
+	}
660
+
661
+
662
+	/**
663
+	 * _insufficient_spaces_available
664
+	 * displays notices regarding events that do not have enough remaining spaces
665
+	 * to satisfy the current number of registrations looking to pay
666
+	 *
667
+	 * @param EE_Event[] $insufficient_spaces_events_array
668
+	 * @return EE_Form_Section_Proper
669
+	 * @throws EE_Error
670
+	 * @throws ReflectionException
671
+	 */
672
+	private function _insufficient_spaces_available($insufficient_spaces_events_array = [])
673
+	{
674
+		// set some defaults
675
+		$this->checkout->selected_method_of_payment = 'invoice';
676
+		$insufficient_space_events                  = '';
677
+		foreach ($insufficient_spaces_events_array as $event) {
678
+			if ($event instanceof EE_Event) {
679
+				$insufficient_space_events .= EEH_HTML::li(
680
+					EEH_HTML::span(' ' . $event->name(), '', 'dashicons dashicons-marker ee-icon-size-16 pink-text')
681
+				);
682
+			}
683
+		}
684
+		return new EE_Form_Section_Proper(
685
+			[
686
+				'subsections'     => [
687
+					'default_hidden_inputs' => $this->reg_step_hidden_inputs(),
688
+					'extra_hidden_inputs'   => $this->_extra_hidden_inputs(),
689
+				],
690
+				'layout_strategy' => new EE_Template_Layout(
691
+					[
692
+						'layout_template_file' => SPCO_REG_STEPS_PATH
693
+												  . $this->_slug
694
+												  . '/sold_out_events.template.php',
695
+						'template_args'        => apply_filters(
696
+							'FHEE__EE_SPCO_Reg_Step_Payment_Options___insufficient_spaces_available__template_args',
697
+							[
698
+								'sold_out_events'     => $insufficient_space_events,
699
+								'sold_out_events_msg' => apply_filters(
700
+									'FHEE__EE_SPCO_Reg_Step_Payment_Options___insufficient_spaces_available__insufficient_space_msg',
701
+									esc_html__(
702
+										'It appears that the event you were about to make a payment for has sold additional tickets since you first registered, and there are no longer enough spaces left to accommodate your selections. You may continue to pay and secure the available space(s) remaining, or simply cancel if you no longer wish to purchase. If you have already made a partial payment towards this event, please contact the event administrator for a refund.',
703
+										'event_espresso'
704
+									)
705
+								),
706
+							]
707
+						),
708
+					]
709
+				),
710
+			]
711
+		);
712
+	}
713
+
714
+
715
+	/**
716
+	 * registrations_requiring_pre_approval
717
+	 *
718
+	 * @param array $registrations_requiring_pre_approval
719
+	 * @return EE_Form_Section_Proper
720
+	 * @throws EE_Error
721
+	 * @throws EntityNotFoundException
722
+	 * @throws ReflectionException
723
+	 */
724
+	private function _registrations_requiring_pre_approval($registrations_requiring_pre_approval = [])
725
+	{
726
+		$events_requiring_pre_approval = [];
727
+		foreach ($registrations_requiring_pre_approval as $registration) {
728
+			if ($registration instanceof EE_Registration && $registration->event() instanceof EE_Event) {
729
+				$events_requiring_pre_approval[ $registration->event()->ID() ] = EEH_HTML::li(
730
+					EEH_HTML::span(
731
+						'',
732
+						'',
733
+						'dashicons dashicons-marker ee-icon-size-16 orange-text'
734
+					)
735
+					. EEH_HTML::span($registration->event()->name(), '', 'orange-text')
736
+				);
737
+			}
738
+		}
739
+		return new EE_Form_Section_Proper(
740
+			[
741
+				'layout_strategy' => new EE_Template_Layout(
742
+					[
743
+						'layout_template_file' => SPCO_REG_STEPS_PATH
744
+												  . $this->_slug
745
+												  . '/events_requiring_pre_approval.template.php', // layout_template
746
+						'template_args'        => apply_filters(
747
+							'FHEE__EE_SPCO_Reg_Step_Payment_Options___sold_out_events__template_args',
748
+							[
749
+								'events_requiring_pre_approval'     => implode('', $events_requiring_pre_approval),
750
+								'events_requiring_pre_approval_msg' => apply_filters(
751
+									'FHEE__EE_SPCO_Reg_Step_Payment_Options___events_requiring_pre_approval__events_requiring_pre_approval_msg',
752
+									esc_html__(
753
+										'The following events do not require payment at this time and will not be billed during this transaction. Billing will only occur after the attendee has been approved by the event organizer. You will be notified when your registration has been processed. If this is a free event, then no billing will occur.',
754
+										'event_espresso'
755
+									)
756
+								),
757
+							]
758
+						),
759
+					]
760
+				),
761
+			]
762
+		);
763
+	}
764
+
765
+
766
+	/**
767
+	 * _no_payment_required
768
+	 *
769
+	 * @param EE_Event[] $registrations_for_free_events
770
+	 * @return EE_Form_Section_Proper
771
+	 * @throws EE_Error
772
+	 */
773
+	private function _no_payment_required($registrations_for_free_events = [])
774
+	{
775
+		// set some defaults
776
+		$this->checkout->selected_method_of_payment = 'no_payment_required';
777
+		// generate no_payment_required form
778
+		return new EE_Form_Section_Proper(
779
+			[
780
+				'layout_strategy' => new EE_Template_Layout(
781
+					[
782
+						'layout_template_file' => SPCO_REG_STEPS_PATH
783
+												  . $this->_slug
784
+												  . '/no_payment_required.template.php', // layout_template
785
+						'template_args'        => apply_filters(
786
+							'FHEE__EE_SPCO_Reg_Step_Payment_Options___no_payment_required__template_args',
787
+							[
788
+								'revisit'                       => $this->checkout->revisit,
789
+								'registrations'                 => [],
790
+								'ticket_count'                  => [],
791
+								'registrations_for_free_events' => $registrations_for_free_events,
792
+								'no_payment_required_msg'       => EEH_HTML::p(
793
+									esc_html__('This is a free event, so no billing will occur.', 'event_espresso')
794
+								),
795
+							]
796
+						),
797
+					]
798
+				),
799
+			]
800
+		);
801
+	}
802
+
803
+
804
+	/**
805
+	 * _display_payment_options
806
+	 *
807
+	 * @param string $transaction_details
808
+	 * @return EE_Form_Section_Proper
809
+	 * @throws EE_Error
810
+	 * @throws InvalidArgumentException
811
+	 * @throws InvalidDataTypeException
812
+	 * @throws InvalidInterfaceException
813
+	 */
814
+	private function _display_payment_options($transaction_details = '')
815
+	{
816
+		// has method_of_payment been set by no-js user?
817
+		$this->checkout->selected_method_of_payment = $this->_get_selected_method_of_payment();
818
+		// build payment options form
819
+		return apply_filters(
820
+			'FHEE__EE_SPCO_Reg_Step_Payment_Options___display_payment_options__payment_options_form',
821
+			new EE_Form_Section_Proper(
822
+				[
823
+					'subsections'     => [
824
+						'before_payment_options' => apply_filters(
825
+							'FHEE__EE_SPCO_Reg_Step_Payment_Options___display_payment_options__before_payment_options',
826
+							new EE_Form_Section_Proper(
827
+								['layout_strategy' => new EE_Div_Per_Section_Layout()]
828
+							)
829
+						),
830
+						'payment_options'        => $this->_setup_payment_options(),
831
+						'after_payment_options'  => apply_filters(
832
+							'FHEE__EE_SPCO_Reg_Step_Payment_Options___display_payment_options__after_payment_options',
833
+							new EE_Form_Section_Proper(
834
+								['layout_strategy' => new EE_Div_Per_Section_Layout()]
835
+							)
836
+						),
837
+					],
838
+					'layout_strategy' => new EE_Template_Layout(
839
+						[
840
+							'layout_template_file' => $this->_template,
841
+							'template_args'        => apply_filters(
842
+								'FHEE__EE_SPCO_Reg_Step_Payment_Options___display_payment_options__template_args',
843
+								[
844
+									'reg_count'                 => $this->line_item_display->total_items(),
845
+									'transaction_details'       => $transaction_details,
846
+									'available_payment_methods' => [],
847
+								]
848
+							),
849
+						]
850
+					),
851
+				]
852
+			)
853
+		);
854
+	}
855
+
856
+
857
+	/**
858
+	 * _extra_hidden_inputs
859
+	 *
860
+	 * @param bool $no_payment_required
861
+	 * @return EE_Form_Section_Proper
862
+	 * @throws EE_Error
863
+	 * @throws ReflectionException
864
+	 */
865
+	private function _extra_hidden_inputs($no_payment_required = true)
866
+	{
867
+		return new EE_Form_Section_Proper(
868
+			[
869
+				'html_id'         => 'ee-' . $this->slug() . '-extra-hidden-inputs',
870
+				'layout_strategy' => new EE_Div_Per_Section_Layout(),
871
+				'subsections'     => [
872
+					'spco_no_payment_required' => new EE_Hidden_Input(
873
+						[
874
+							'normalization_strategy' => new EE_Boolean_Normalization(),
875
+							'html_name'              => 'spco_no_payment_required',
876
+							'html_id'                => 'spco-no-payment-required-payment_options',
877
+							'default'                => $no_payment_required,
878
+						]
879
+					),
880
+					'spco_transaction_id'      => new EE_Fixed_Hidden_Input(
881
+						[
882
+							'normalization_strategy' => new EE_Int_Normalization(),
883
+							'html_name'              => 'spco_transaction_id',
884
+							'html_id'                => 'spco-transaction-id',
885
+							'default'                => $this->checkout->transaction->ID(),
886
+						]
887
+					),
888
+				],
889
+			]
890
+		);
891
+	}
892
+
893
+
894
+	/**
895
+	 *    _apply_registration_payments_to_amount_owing
896
+	 *
897
+	 * @param array $registrations
898
+	 * @throws EE_Error
899
+	 */
900
+	protected function _apply_registration_payments_to_amount_owing(array $registrations)
901
+	{
902
+		$payments = [];
903
+		foreach ($registrations as $registration) {
904
+			if ($registration instanceof EE_Registration && $registration->owes_monies_and_can_pay()) {
905
+				$payments += $registration->registration_payments();
906
+			}
907
+		}
908
+		if (! empty($payments)) {
909
+			foreach ($payments as $payment) {
910
+				if ($payment instanceof EE_Registration_Payment) {
911
+					$this->checkout->amount_owing -= $payment->amount();
912
+				}
913
+			}
914
+		}
915
+	}
916
+
917
+
918
+	/**
919
+	 *    _reset_selected_method_of_payment
920
+	 *
921
+	 * @access    private
922
+	 * @param bool $force_reset
923
+	 * @return void
924
+	 * @throws InvalidArgumentException
925
+	 * @throws InvalidDataTypeException
926
+	 * @throws InvalidInterfaceException
927
+	 */
928
+	private function _reset_selected_method_of_payment($force_reset = false)
929
+	{
930
+		/** @var RequestInterface $request */
931
+		$request              = LoaderFactory::getLoader()->getShared(RequestInterface::class);
932
+		$reset_payment_method = $request->getRequestParam('reset_payment_method', $force_reset, 'bool');
933
+		if ($reset_payment_method) {
934
+			$this->checkout->selected_method_of_payment = null;
935
+			$this->checkout->payment_method             = null;
936
+			$this->checkout->billing_form               = null;
937
+			$this->_save_selected_method_of_payment();
938
+		}
939
+	}
940
+
941
+
942
+	/**
943
+	 * _save_selected_method_of_payment
944
+	 * stores the selected_method_of_payment in the session
945
+	 * so that it's available for all subsequent requests including AJAX
946
+	 *
947
+	 * @access        private
948
+	 * @param string $selected_method_of_payment
949
+	 * @return void
950
+	 * @throws InvalidArgumentException
951
+	 * @throws InvalidDataTypeException
952
+	 * @throws InvalidInterfaceException
953
+	 */
954
+	private function _save_selected_method_of_payment($selected_method_of_payment = '')
955
+	{
956
+		$selected_method_of_payment = ! empty($selected_method_of_payment)
957
+			? $selected_method_of_payment
958
+			: $this->checkout->selected_method_of_payment;
959
+		EE_Registry::instance()->SSN->set_session_data(
960
+			['selected_method_of_payment' => $selected_method_of_payment]
961
+		);
962
+	}
963
+
964
+
965
+	/**
966
+	 * _setup_payment_options
967
+	 *
968
+	 * @return EE_Form_Section_Proper
969
+	 * @throws EE_Error
970
+	 * @throws InvalidArgumentException
971
+	 * @throws InvalidDataTypeException
972
+	 * @throws InvalidInterfaceException
973
+	 */
974
+	public function _setup_payment_options()
975
+	{
976
+		// load payment method classes
977
+		$this->checkout->available_payment_methods = $this->_get_available_payment_methods();
978
+		if (empty($this->checkout->available_payment_methods)) {
979
+			EE_Error::add_error(
980
+				apply_filters(
981
+					'FHEE__EE_SPCO_Reg_Step_Payment_Options___setup_payment_options__error_message_no_payment_methods',
982
+					sprintf(
983
+						esc_html__(
984
+							'Sorry, you cannot complete your purchase because a payment method is not active.%1$s Please contact %2$s for assistance and provide a description of the problem.',
985
+							'event_espresso'
986
+						),
987
+						'<br>',
988
+						EE_Registry::instance()->CFG->organization->get_pretty('email')
989
+					)
990
+				),
991
+				__FILE__,
992
+				__FUNCTION__,
993
+				__LINE__
994
+			);
995
+		}
996
+		// switch up header depending on number of available payment methods
997
+		$payment_method_header     = count($this->checkout->available_payment_methods) > 1
998
+			? apply_filters(
999
+				'FHEE__registration_page_payment_options__method_of_payment_hdr',
1000
+				esc_html__('Please Select Your Method of Payment', 'event_espresso')
1001
+			)
1002
+			: apply_filters(
1003
+				'FHEE__registration_page_payment_options__method_of_payment_hdr',
1004
+				esc_html__('Method of Payment', 'event_espresso')
1005
+			);
1006
+		$available_payment_methods = [
1007
+			// display the "Payment Method" header
1008
+			'payment_method_header' => new EE_Form_Section_HTML(
1009
+				apply_filters(
1010
+					'FHEE__EE_SPCO_Reg_Step_Payment_Options___setup_payment_options__payment_method_header',
1011
+					EEH_HTML::h4($payment_method_header, 'method-of-payment-hdr'),
1012
+					$payment_method_header
1013
+				)
1014
+			),
1015
+		];
1016
+		// the list of actual payment methods ( invoice, paypal, etc ) in a  ( slug => HTML )  format
1017
+		$available_payment_method_options = [];
1018
+		$default_payment_method_option    = [];
1019
+		// additional instructions to be displayed and hidden below payment methods (adding a clearing div to start)
1020
+		$payment_methods_billing_info = [
1021
+			new EE_Form_Section_HTML(
1022
+				EEH_HTML::div('<br />', '', '', 'clear:both;')
1023
+			),
1024
+		];
1025
+		// loop through payment methods
1026
+		foreach ($this->checkout->available_payment_methods as $payment_method) {
1027
+			if ($payment_method instanceof EE_Payment_Method) {
1028
+				$payment_method_button = EEH_HTML::img(
1029
+					$payment_method->button_url(),
1030
+					$payment_method->name(),
1031
+					'spco-payment-method-' . $payment_method->slug() . '-btn-img',
1032
+					'spco-payment-method-btn-img'
1033
+				);
1034
+				// check if any payment methods are set as default
1035
+				// if payment method is already selected OR nothing is selected and this payment method should be
1036
+				// open_by_default
1037
+				if (
1038
+					($this->checkout->selected_method_of_payment === $payment_method->slug())
1039
+					|| (! $this->checkout->selected_method_of_payment && $payment_method->open_by_default())
1040
+				) {
1041
+					$this->checkout->selected_method_of_payment = $payment_method->slug();
1042
+					$this->_save_selected_method_of_payment();
1043
+					$default_payment_method_option[ $payment_method->slug() ] = $payment_method_button;
1044
+				} else {
1045
+					$available_payment_method_options[ $payment_method->slug() ] = $payment_method_button;
1046
+				}
1047
+				$payment_methods_billing_info[ $payment_method->slug() . '-info' ] =
1048
+					$this->_payment_method_billing_info(
1049
+						$payment_method
1050
+					);
1051
+			}
1052
+		}
1053
+		// prepend available_payment_method_options with default_payment_method_option so that it appears first in list
1054
+		// of PMs
1055
+		$available_payment_method_options = $default_payment_method_option + $available_payment_method_options;
1056
+		// now generate the actual form  inputs
1057
+		$available_payment_methods['available_payment_methods'] = $this->_available_payment_method_inputs(
1058
+			$available_payment_method_options
1059
+		);
1060
+		$available_payment_methods                              += $payment_methods_billing_info;
1061
+		// build the available payment methods form
1062
+		return new EE_Form_Section_Proper(
1063
+			[
1064
+				'html_id'         => 'spco-available-methods-of-payment-dv',
1065
+				'subsections'     => $available_payment_methods,
1066
+				'layout_strategy' => new EE_Div_Per_Section_Layout(),
1067
+			]
1068
+		);
1069
+	}
1070
+
1071
+
1072
+	/**
1073
+	 * _get_available_payment_methods
1074
+	 *
1075
+	 * @return EE_Payment_Method[]
1076
+	 * @throws EE_Error
1077
+	 * @throws InvalidArgumentException
1078
+	 * @throws InvalidDataTypeException
1079
+	 * @throws InvalidInterfaceException
1080
+	 */
1081
+	protected function _get_available_payment_methods()
1082
+	{
1083
+		if (! empty($this->checkout->available_payment_methods)) {
1084
+			return $this->checkout->available_payment_methods;
1085
+		}
1086
+		$available_payment_methods = [];
1087
+		$EEM_Payment_Method        = EEM_Payment_Method::instance();
1088
+		// get all active payment methods
1089
+		$payment_methods = $EEM_Payment_Method->get_all_for_transaction(
1090
+			$this->checkout->transaction,
1091
+			EEM_Payment_Method::scope_cart
1092
+		);
1093
+		foreach ($payment_methods as $payment_method) {
1094
+			if ($payment_method instanceof EE_Payment_Method) {
1095
+				$available_payment_methods[ $payment_method->slug() ] = $payment_method;
1096
+			}
1097
+		}
1098
+		return $available_payment_methods;
1099
+	}
1100
+
1101
+
1102
+	/**
1103
+	 *    _available_payment_method_inputs
1104
+	 *
1105
+	 * @access    private
1106
+	 * @param array $available_payment_method_options
1107
+	 * @return    EE_Form_Section_Proper
1108
+	 * @throws EE_Error
1109
+	 * @throws EE_Error
1110
+	 */
1111
+	private function _available_payment_method_inputs($available_payment_method_options = [])
1112
+	{
1113
+		// generate inputs
1114
+		return new EE_Form_Section_Proper(
1115
+			[
1116
+				'html_id'         => 'ee-available-payment-method-inputs',
1117
+				'layout_strategy' => new EE_Div_Per_Section_Layout(),
1118
+				'subsections'     => [
1119
+					'' => new EE_Radio_Button_Input(
1120
+						$available_payment_method_options,
1121
+						[
1122
+							'html_name'          => 'selected_method_of_payment',
1123
+							'html_class'         => 'spco-payment-method',
1124
+							'default'            => $this->checkout->selected_method_of_payment,
1125
+							'label_size'         => 11,
1126
+							'enforce_label_size' => true,
1127
+						]
1128
+					),
1129
+				],
1130
+			]
1131
+		);
1132
+	}
1133
+
1134
+
1135
+	/**
1136
+	 *    _payment_method_billing_info
1137
+	 *
1138
+	 * @access    private
1139
+	 * @param EE_Payment_Method $payment_method
1140
+	 * @return EE_Form_Section_Proper
1141
+	 * @throws EE_Error
1142
+	 * @throws InvalidArgumentException
1143
+	 * @throws InvalidDataTypeException
1144
+	 * @throws InvalidInterfaceException
1145
+	 */
1146
+	private function _payment_method_billing_info(EE_Payment_Method $payment_method)
1147
+	{
1148
+		$currently_selected = $this->checkout->selected_method_of_payment === $payment_method->slug();
1149
+		// generate the billing form for payment method
1150
+		$billing_form                 = $currently_selected
1151
+			? $this->_get_billing_form_for_payment_method($payment_method)
1152
+			: new EE_Form_Section_HTML();
1153
+		$this->checkout->billing_form = $currently_selected
1154
+			? $billing_form
1155
+			: $this->checkout->billing_form;
1156
+		// it's all in the details
1157
+		$info_html = EEH_HTML::h3(
1158
+			esc_html__('Important information regarding your payment', 'event_espresso'),
1159
+			'',
1160
+			'spco-payment-method-hdr'
1161
+		);
1162
+		// add some info regarding the step, either from what's saved in the admin,
1163
+		// or a default string depending on whether the PM has a billing form or not
1164
+		if ($payment_method->description()) {
1165
+			$payment_method_info = $payment_method->description();
1166
+		} elseif ($billing_form instanceof EE_Billing_Info_Form) {
1167
+			$payment_method_info = sprintf(
1168
+				esc_html__(
1169
+					'Please provide the following billing information, then click the "%1$s" button below in order to proceed.',
1170
+					'event_espresso'
1171
+				),
1172
+				$this->submit_button_text()
1173
+			);
1174
+		} else {
1175
+			$payment_method_info = sprintf(
1176
+				esc_html__('Please click the "%1$s" button below in order to proceed.', 'event_espresso'),
1177
+				$this->submit_button_text()
1178
+			);
1179
+		}
1180
+		$info_html .= EEH_HTML::div(
1181
+			apply_filters(
1182
+				'FHEE__EE_SPCO_Reg_Step_Payment_Options___payment_method_billing_info__payment_method_info',
1183
+				$payment_method_info
1184
+			),
1185
+			'',
1186
+			'spco-payment-method-desc ee-attention'
1187
+		);
1188
+		return new EE_Form_Section_Proper(
1189
+			[
1190
+				'html_id'         => 'spco-payment-method-info-' . $payment_method->slug(),
1191
+				'html_class'      => 'spco-payment-method-info-dv',
1192
+				// only display the selected or default PM
1193
+				'html_style'      => $currently_selected ? '' : 'display:none;',
1194
+				'layout_strategy' => new EE_Div_Per_Section_Layout(),
1195
+				'subsections'     => [
1196
+					'info'         => new EE_Form_Section_HTML($info_html),
1197
+					'billing_form' => $currently_selected ? $billing_form : new EE_Form_Section_HTML(),
1198
+				],
1199
+			]
1200
+		);
1201
+	}
1202
+
1203
+
1204
+	/**
1205
+	 * get_billing_form_html_for_payment_method
1206
+	 *
1207
+	 * @return bool
1208
+	 * @throws EE_Error
1209
+	 * @throws InvalidArgumentException
1210
+	 * @throws ReflectionException
1211
+	 * @throws InvalidDataTypeException
1212
+	 * @throws InvalidInterfaceException
1213
+	 */
1214
+	public function get_billing_form_html_for_payment_method()
1215
+	{
1216
+		// how have they chosen to pay?
1217
+		$this->checkout->selected_method_of_payment = $this->_get_selected_method_of_payment(true);
1218
+		$this->checkout->payment_method             = $this->_get_payment_method_for_selected_method_of_payment();
1219
+		if (! $this->checkout->payment_method instanceof EE_Payment_Method) {
1220
+			return false;
1221
+		}
1222
+		if (
1223
+			apply_filters(
1224
+				'FHEE__EE_SPCO_Reg_Step_Payment_Options__registration_checkout__selected_payment_method__display_success',
1225
+				false
1226
+			)
1227
+		) {
1228
+			EE_Error::add_success(
1229
+				apply_filters(
1230
+					'FHEE__Single_Page_Checkout__registration_checkout__selected_payment_method',
1231
+					sprintf(
1232
+						esc_html__(
1233
+							'You have selected "%s" as your method of payment. Please note the important payment information below.',
1234
+							'event_espresso'
1235
+						),
1236
+						$this->checkout->payment_method->name()
1237
+					)
1238
+				)
1239
+			);
1240
+		}
1241
+		// now generate billing form for selected method of payment
1242
+		$payment_method_billing_form = $this->_get_billing_form_for_payment_method($this->checkout->payment_method);
1243
+		// fill form with attendee info if applicable
1244
+		if (
1245
+			$payment_method_billing_form instanceof EE_Billing_Attendee_Info_Form
1246
+			&& $this->checkout->transaction_has_primary_registrant()
1247
+		) {
1248
+			$payment_method_billing_form->populate_from_attendee(
1249
+				$this->checkout->transaction->primary_registration()->attendee()
1250
+			);
1251
+		}
1252
+		// and debug content
1253
+		if (
1254
+			$payment_method_billing_form instanceof EE_Billing_Info_Form
1255
+			&& $this->checkout->payment_method->type_obj() instanceof EE_PMT_Base
1256
+		) {
1257
+			$payment_method_billing_form =
1258
+				$this->checkout->payment_method->type_obj()->apply_billing_form_debug_settings(
1259
+					$payment_method_billing_form
1260
+				);
1261
+		}
1262
+		$billing_info = $payment_method_billing_form instanceof EE_Form_Section_Proper
1263
+			? $payment_method_billing_form->get_html()
1264
+			: '';
1265
+		$this->checkout->json_response->set_return_data(['payment_method_info' => $billing_info]);
1266
+		// localize validation rules for main form
1267
+		$this->checkout->current_step->reg_form->localize_validation_rules();
1268
+		$this->checkout->json_response->add_validation_rules(EE_Form_Section_Proper::js_localization());
1269
+		return true;
1270
+	}
1271
+
1272
+
1273
+	/**
1274
+	 * _get_billing_form_for_payment_method
1275
+	 *
1276
+	 * @param EE_Payment_Method $payment_method
1277
+	 * @return EE_Billing_Info_Form|EE_Form_Section_HTML
1278
+	 * @throws EE_Error
1279
+	 * @throws InvalidArgumentException
1280
+	 * @throws InvalidDataTypeException
1281
+	 * @throws InvalidInterfaceException
1282
+	 */
1283
+	private function _get_billing_form_for_payment_method(EE_Payment_Method $payment_method)
1284
+	{
1285
+		$billing_form = $payment_method->type_obj()->billing_form(
1286
+			$this->checkout->transaction,
1287
+			['amount_owing' => $this->checkout->amount_owing]
1288
+		);
1289
+		if ($billing_form instanceof EE_Billing_Info_Form) {
1290
+			if (
1291
+				apply_filters(
1292
+					'FHEE__EE_SPCO_Reg_Step_Payment_Options__registration_checkout__selected_payment_method__display_success',
1293
+					false
1294
+				)
1295
+				&& $this->request->requestParamIsSet('payment_method')
1296
+			) {
1297
+				EE_Error::add_success(
1298
+					apply_filters(
1299
+						'FHEE__Single_Page_Checkout__registration_checkout__selected_payment_method',
1300
+						sprintf(
1301
+							esc_html__(
1302
+								'You have selected "%s" as your method of payment. Please note the important payment information below.',
1303
+								'event_espresso'
1304
+							),
1305
+							$payment_method->name()
1306
+						)
1307
+					)
1308
+				);
1309
+			}
1310
+			return apply_filters(
1311
+				'FHEE__EE_SPCO_Reg_Step_Payment_Options___get_billing_form_for_payment_method__billing_form',
1312
+				$billing_form,
1313
+				$payment_method
1314
+			);
1315
+		}
1316
+		// no actual billing form, so return empty HTML form section
1317
+		return new EE_Form_Section_HTML();
1318
+	}
1319
+
1320
+
1321
+	/**
1322
+	 * _get_selected_method_of_payment
1323
+	 *
1324
+	 * @param boolean $required whether to throw an error if the "selected_method_of_payment"
1325
+	 *                          is not found in the incoming request
1326
+	 * @param string  $request_param
1327
+	 * @return NULL|string
1328
+	 * @throws EE_Error
1329
+	 * @throws InvalidArgumentException
1330
+	 * @throws InvalidDataTypeException
1331
+	 * @throws InvalidInterfaceException
1332
+	 */
1333
+	private function _get_selected_method_of_payment(
1334
+		$required = false,
1335
+		$request_param = 'selected_method_of_payment'
1336
+	) {
1337
+		// is selected_method_of_payment set in the request ?
1338
+		$selected_method_of_payment = $this->request->getRequestParam($request_param);
1339
+		if ($selected_method_of_payment) {
1340
+			// sanitize it
1341
+			$selected_method_of_payment = is_array($selected_method_of_payment)
1342
+				? array_shift($selected_method_of_payment)
1343
+				: $selected_method_of_payment;
1344
+			$selected_method_of_payment = sanitize_text_field($selected_method_of_payment);
1345
+			// store it in the session so that it's available for all subsequent requests including AJAX
1346
+			$this->_save_selected_method_of_payment($selected_method_of_payment);
1347
+		} else {
1348
+			// or is is set in the session ?
1349
+			$selected_method_of_payment = EE_Registry::instance()->SSN->get_session_data(
1350
+				'selected_method_of_payment'
1351
+			);
1352
+		}
1353
+		// do ya really really gotta have it?
1354
+		if (empty($selected_method_of_payment) && $required) {
1355
+			EE_Error::add_error(
1356
+				sprintf(
1357
+					esc_html__(
1358
+						'The selected method of payment could not be determined.%sPlease ensure that you have selected one before proceeding.%sIf you continue to experience difficulties, then refresh your browser and try again, or contact %s for assistance.',
1359
+						'event_espresso'
1360
+					),
1361
+					'<br/>',
1362
+					'<br/>',
1363
+					EE_Registry::instance()->CFG->organization->get_pretty('email')
1364
+				),
1365
+				__FILE__,
1366
+				__FUNCTION__,
1367
+				__LINE__
1368
+			);
1369
+			return null;
1370
+		}
1371
+		return $selected_method_of_payment;
1372
+	}
1373
+
1374
+
1375
+
1376
+
1377
+
1378
+
1379
+	/********************************************************************************************************/
1380
+	/***********************************  SWITCH PAYMENT METHOD  ************************************/
1381
+	/********************************************************************************************************/
1382
+	/**
1383
+	 * switch_payment_method
1384
+	 *
1385
+	 * @return bool
1386
+	 * @throws EE_Error
1387
+	 * @throws InvalidArgumentException
1388
+	 * @throws InvalidDataTypeException
1389
+	 * @throws InvalidInterfaceException
1390
+	 * @throws ReflectionException
1391
+	 */
1392
+	public function switch_payment_method()
1393
+	{
1394
+		if (! $this->_verify_payment_method_is_set()) {
1395
+			return false;
1396
+		}
1397
+		if (
1398
+			apply_filters(
1399
+				'FHEE__EE_SPCO_Reg_Step_Payment_Options__registration_checkout__selected_payment_method__display_success',
1400
+				false
1401
+			)
1402
+		) {
1403
+			EE_Error::add_success(
1404
+				apply_filters(
1405
+					'FHEE__Single_Page_Checkout__registration_checkout__selected_payment_method',
1406
+					sprintf(
1407
+						esc_html__(
1408
+							'You have selected "%s" as your method of payment. Please note the important payment information below.',
1409
+							'event_espresso'
1410
+						),
1411
+						$this->checkout->payment_method->name()
1412
+					)
1413
+				)
1414
+			);
1415
+		}
1416
+		// generate billing form for selected method of payment if it hasn't been done already
1417
+		if ($this->checkout->payment_method->type_obj()->has_billing_form()) {
1418
+			$this->checkout->billing_form = $this->_get_billing_form_for_payment_method(
1419
+				$this->checkout->payment_method
1420
+			);
1421
+		}
1422
+		// fill form with attendee info if applicable
1423
+		if (
1424
+			apply_filters(
1425
+				'FHEE__populate_billing_form_fields_from_attendee',
1426
+				(
1427
+				$this->checkout->billing_form instanceof EE_Billing_Attendee_Info_Form
1428
+				&& $this->checkout->transaction_has_primary_registrant()
1429
+				),
1430
+				$this->checkout->billing_form,
1431
+				$this->checkout->transaction
1432
+			)
1433
+		) {
1434
+			$this->checkout->billing_form->populate_from_attendee(
1435
+				$this->checkout->transaction->primary_registration()->attendee()
1436
+			);
1437
+		}
1438
+		// and debug content
1439
+		if (
1440
+			$this->checkout->billing_form instanceof EE_Billing_Info_Form
1441
+			&& $this->checkout->payment_method->type_obj() instanceof EE_PMT_Base
1442
+		) {
1443
+			$this->checkout->billing_form =
1444
+				$this->checkout->payment_method->type_obj()->apply_billing_form_debug_settings(
1445
+					$this->checkout->billing_form
1446
+				);
1447
+		}
1448
+		// get html and validation rules for form
1449
+		if ($this->checkout->billing_form instanceof EE_Form_Section_Proper) {
1450
+			$this->checkout->json_response->set_return_data(
1451
+				['payment_method_info' => $this->checkout->billing_form->get_html()]
1452
+			);
1453
+			// localize validation rules for main form
1454
+			$this->checkout->billing_form->localize_validation_rules(true);
1455
+			$this->checkout->json_response->add_validation_rules(EE_Form_Section_Proper::js_localization());
1456
+		} else {
1457
+			$this->checkout->json_response->set_return_data(['payment_method_info' => '']);
1458
+		}
1459
+		// prevents advancement to next step
1460
+		$this->checkout->continue_reg = false;
1461
+		return true;
1462
+	}
1463
+
1464
+
1465
+	/**
1466
+	 * _verify_payment_method_is_set
1467
+	 *
1468
+	 * @return bool
1469
+	 * @throws EE_Error
1470
+	 * @throws InvalidArgumentException
1471
+	 * @throws ReflectionException
1472
+	 * @throws InvalidDataTypeException
1473
+	 * @throws InvalidInterfaceException
1474
+	 */
1475
+	protected function _verify_payment_method_is_set()
1476
+	{
1477
+		// generate billing form for selected method of payment if it hasn't been done already
1478
+		if (empty($this->checkout->selected_method_of_payment)) {
1479
+			// how have they chosen to pay?
1480
+			$this->checkout->selected_method_of_payment = $this->_get_selected_method_of_payment(true);
1481
+		} else {
1482
+			// choose your own adventure based on method_of_payment
1483
+			switch ($this->checkout->selected_method_of_payment) {
1484
+				case 'events_sold_out':
1485
+					EE_Error::add_attention(
1486
+						apply_filters(
1487
+							'FHEE__EE_SPCO_Reg_Step_Payment_Options___verify_payment_method_is_set__sold_out_events_msg',
1488
+							esc_html__(
1489
+								'It appears that the event you were about to make a payment for has sold out since this form first loaded. Please contact the event administrator if you believe this is an error.',
1490
+								'event_espresso'
1491
+							)
1492
+						),
1493
+						__FILE__,
1494
+						__FUNCTION__,
1495
+						__LINE__
1496
+					);
1497
+					return false;
1498
+				case 'payments_closed':
1499
+					EE_Error::add_attention(
1500
+						apply_filters(
1501
+							'FHEE__EE_SPCO_Reg_Step_Payment_Options___verify_payment_method_is_set__payments_closed_msg',
1502
+							esc_html__(
1503
+								'It appears that the event you were about to make a payment for is not accepting payments at this time. Please contact the event administrator if you believe this is an error.',
1504
+								'event_espresso'
1505
+							)
1506
+						),
1507
+						__FILE__,
1508
+						__FUNCTION__,
1509
+						__LINE__
1510
+					);
1511
+					return false;
1512
+				case 'no_payment_required':
1513
+					EE_Error::add_attention(
1514
+						apply_filters(
1515
+							'FHEE__EE_SPCO_Reg_Step_Payment_Options___verify_payment_method_is_set__no_payment_required_msg',
1516
+							esc_html__(
1517
+								'It appears that the event you were about to make a payment for does not require payment. Please contact the event administrator if you believe this is an error.',
1518
+								'event_espresso'
1519
+							)
1520
+						),
1521
+						__FILE__,
1522
+						__FUNCTION__,
1523
+						__LINE__
1524
+					);
1525
+					return false;
1526
+				default:
1527
+			}
1528
+		}
1529
+		// verify payment method
1530
+		if (! $this->checkout->payment_method instanceof EE_Payment_Method) {
1531
+			// get payment method for selected method of payment
1532
+			$this->checkout->payment_method = $this->_get_payment_method_for_selected_method_of_payment();
1533
+		}
1534
+		return $this->checkout->payment_method instanceof EE_Payment_Method;
1535
+	}
1536
+
1537
+
1538
+
1539
+	/********************************************************************************************************/
1540
+	/***************************************  SAVE PAYER DETAILS  ****************************************/
1541
+	/********************************************************************************************************/
1542
+	/**
1543
+	 * save_payer_details_via_ajax
1544
+	 *
1545
+	 * @return void
1546
+	 * @throws EE_Error
1547
+	 * @throws InvalidArgumentException
1548
+	 * @throws ReflectionException
1549
+	 * @throws RuntimeException
1550
+	 * @throws InvalidDataTypeException
1551
+	 * @throws InvalidInterfaceException
1552
+	 */
1553
+	public function save_payer_details_via_ajax()
1554
+	{
1555
+		if (! $this->_verify_payment_method_is_set()) {
1556
+			return;
1557
+		}
1558
+		// generate billing form for selected method of payment if it hasn't been done already
1559
+		if ($this->checkout->payment_method->type_obj()->has_billing_form()) {
1560
+			$this->checkout->billing_form = $this->_get_billing_form_for_payment_method(
1561
+				$this->checkout->payment_method
1562
+			);
1563
+		}
1564
+		// generate primary attendee from payer info if applicable
1565
+		if (! $this->checkout->transaction_has_primary_registrant()) {
1566
+			$attendee = $this->_create_attendee_from_request_data();
1567
+			if ($attendee instanceof EE_Attendee) {
1568
+				foreach ($this->checkout->transaction->registrations() as $registration) {
1569
+					if ($registration->is_primary_registrant()) {
1570
+						$this->checkout->primary_attendee_obj = $attendee;
1571
+						$registration->_add_relation_to($attendee, 'Attendee');
1572
+						$registration->set_attendee_id($attendee->ID());
1573
+						$registration->update_cache_after_object_save('Attendee', $attendee);
1574
+					}
1575
+				}
1576
+			}
1577
+		}
1578
+	}
1579
+
1580
+
1581
+	/**
1582
+	 * create_attendee_from_request_data
1583
+	 * uses info from alternate GET or POST data (such as AJAX) to create a new attendee
1584
+	 *
1585
+	 * @return EE_Attendee
1586
+	 * @throws EE_Error
1587
+	 * @throws InvalidArgumentException
1588
+	 * @throws ReflectionException
1589
+	 * @throws InvalidDataTypeException
1590
+	 * @throws InvalidInterfaceException
1591
+	 */
1592
+	protected function _create_attendee_from_request_data()
1593
+	{
1594
+		// get State ID
1595
+		$STA_ID = $this->request->getRequestParam('state');
1596
+		if (! empty($STA_ID)) {
1597
+			// can we get state object from name ?
1598
+			EE_Registry::instance()->load_model('State');
1599
+			$state  = EEM_State::instance()->get_col([['STA_name' => $STA_ID], 'limit' => 1], 'STA_ID');
1600
+			$STA_ID = is_array($state) && ! empty($state) ? reset($state) : $STA_ID;
1601
+		}
1602
+		// get Country ISO
1603
+		$CNT_ISO = $this->request->getRequestParam('country');
1604
+		if (! empty($CNT_ISO)) {
1605
+			// can we get country object from name ?
1606
+			EE_Registry::instance()->load_model('Country');
1607
+			$country = EEM_Country::instance()->get_col(
1608
+				[['CNT_name' => $CNT_ISO], 'limit' => 1],
1609
+				'CNT_ISO'
1610
+			);
1611
+			$CNT_ISO = is_array($country) && ! empty($country) ? reset($country) : $CNT_ISO;
1612
+		}
1613
+		// grab attendee data
1614
+		$attendee_data = [
1615
+			'ATT_fname'    => $this->request->getRequestParam('first_name'),
1616
+			'ATT_lname'    => $this->request->getRequestParam('last_name'),
1617
+			'ATT_email'    => $this->request->getRequestParam('email'),
1618
+			'ATT_address'  => $this->request->getRequestParam('address'),
1619
+			'ATT_address2' => $this->request->getRequestParam('address2'),
1620
+			'ATT_city'     => $this->request->getRequestParam('city'),
1621
+			'STA_ID'       => $STA_ID,
1622
+			'CNT_ISO'      => $CNT_ISO,
1623
+			'ATT_zip'      => $this->request->getRequestParam('zip'),
1624
+			'ATT_phone'    => $this->request->getRequestParam('phone'),
1625
+		];
1626
+		// validate the email address since it is the most important piece of info
1627
+		if (empty($attendee_data['ATT_email'])) {
1628
+			EE_Error::add_error(
1629
+				esc_html__('An invalid email address was submitted.', 'event_espresso'),
1630
+				__FILE__,
1631
+				__FUNCTION__,
1632
+				__LINE__
1633
+			);
1634
+		}
1635
+		// does this attendee already exist in the db ? we're searching using a combination of first name, last name,
1636
+		// AND email address
1637
+		if (
1638
+			! empty($attendee_data['ATT_fname'])
1639
+			&& ! empty($attendee_data['ATT_lname'])
1640
+			&& ! empty($attendee_data['ATT_email'])
1641
+		) {
1642
+			$existing_attendee = EEM_Attendee::instance()->find_existing_attendee(
1643
+				[
1644
+					'ATT_fname' => $attendee_data['ATT_fname'],
1645
+					'ATT_lname' => $attendee_data['ATT_lname'],
1646
+					'ATT_email' => $attendee_data['ATT_email'],
1647
+				]
1648
+			);
1649
+			if ($existing_attendee instanceof EE_Attendee) {
1650
+				return $existing_attendee;
1651
+			}
1652
+		}
1653
+		// no existing attendee? kk let's create a new one
1654
+		// kinda lame, but we need a first and last name to create an attendee, so use the email address if those
1655
+		// don't exist
1656
+		$attendee_data['ATT_fname'] = ! empty($attendee_data['ATT_fname'])
1657
+			? $attendee_data['ATT_fname']
1658
+			: $attendee_data['ATT_email'];
1659
+		$attendee_data['ATT_lname'] = ! empty($attendee_data['ATT_lname'])
1660
+			? $attendee_data['ATT_lname']
1661
+			: $attendee_data['ATT_email'];
1662
+		return EE_Attendee::new_instance($attendee_data);
1663
+	}
1664
+
1665
+
1666
+
1667
+	/********************************************************************************************************/
1668
+	/****************************************  PROCESS REG STEP  *****************************************/
1669
+	/********************************************************************************************************/
1670
+	/**
1671
+	 * process_reg_step
1672
+	 *
1673
+	 * @return bool
1674
+	 * @throws EE_Error
1675
+	 * @throws InvalidArgumentException
1676
+	 * @throws ReflectionException
1677
+	 * @throws EntityNotFoundException
1678
+	 * @throws InvalidDataTypeException
1679
+	 * @throws InvalidInterfaceException
1680
+	 * @throws InvalidStatusException
1681
+	 */
1682
+	public function process_reg_step()
1683
+	{
1684
+		// how have they chosen to pay?
1685
+		$this->checkout->selected_method_of_payment = $this->checkout->transaction->is_free()
1686
+			? 'no_payment_required'
1687
+			: $this->_get_selected_method_of_payment(true);
1688
+		// choose your own adventure based on method_of_payment
1689
+		switch ($this->checkout->selected_method_of_payment) {
1690
+			case 'events_sold_out':
1691
+				$this->checkout->redirect     = true;
1692
+				$this->checkout->redirect_url = $this->checkout->cancel_page_url;
1693
+				$this->checkout->json_response->set_redirect_url($this->checkout->redirect_url);
1694
+				// mark this reg step as completed
1695
+				$this->set_completed();
1696
+				return false;
1697
+
1698
+			case 'payments_closed':
1699
+				if (
1700
+					apply_filters(
1701
+						'FHEE__EE_SPCO_Reg_Step_Payment_Options__process_reg_step__payments_closed__display_success',
1702
+						false
1703
+					)
1704
+				) {
1705
+					EE_Error::add_success(
1706
+						esc_html__('no payment required at this time.', 'event_espresso'),
1707
+						__FILE__,
1708
+						__FUNCTION__,
1709
+						__LINE__
1710
+					);
1711
+				}
1712
+				// mark this reg step as completed
1713
+				$this->set_completed();
1714
+				return true;
1715
+
1716
+			case 'no_payment_required':
1717
+				if (
1718
+					apply_filters(
1719
+						'FHEE__EE_SPCO_Reg_Step_Payment_Options__process_reg_step__no_payment_required__display_success',
1720
+						false
1721
+					)
1722
+				) {
1723
+					EE_Error::add_success(
1724
+						esc_html__('no payment required.', 'event_espresso'),
1725
+						__FILE__,
1726
+						__FUNCTION__,
1727
+						__LINE__
1728
+					);
1729
+				}
1730
+				// mark this reg step as completed
1731
+				$this->set_completed();
1732
+				return true;
1733
+
1734
+			default:
1735
+				$registrations         = EE_Registry::instance()->SSN->checkout()->transaction->registrations(
1736
+					EE_Registry::instance()->SSN->checkout()->reg_cache_where_params
1737
+				);
1738
+				$ejected_registrations = EE_SPCO_Reg_Step_Payment_Options::find_registrations_that_lost_their_space(
1739
+					$registrations,
1740
+					EE_Registry::instance()->SSN->checkout()->revisit
1741
+				);
1742
+				// calculate difference between the two arrays
1743
+				$registrations = array_diff($registrations, $ejected_registrations);
1744
+				if (empty($registrations)) {
1745
+					$this->_redirect_because_event_sold_out();
1746
+					return false;
1747
+				}
1748
+				$payment = $this->_process_payment();
1749
+				if ($payment instanceof EE_Payment) {
1750
+					$this->checkout->continue_reg = true;
1751
+					$this->_maybe_set_completed($payment);
1752
+				} else {
1753
+					$this->checkout->continue_reg = false;
1754
+				}
1755
+				return $payment instanceof EE_Payment;
1756
+		}
1757
+	}
1758
+
1759
+
1760
+	/**
1761
+	 * _redirect_because_event_sold_out
1762
+	 *
1763
+	 * @return void
1764
+	 */
1765
+	protected function _redirect_because_event_sold_out()
1766
+	{
1767
+		$this->checkout->continue_reg = false;
1768
+		// set redirect URL
1769
+		$this->checkout->redirect_url = add_query_arg(
1770
+			['e_reg_url_link' => $this->checkout->reg_url_link],
1771
+			$this->checkout->current_step->reg_step_url()
1772
+		);
1773
+		$this->checkout->json_response->set_redirect_url($this->checkout->redirect_url);
1774
+	}
1775
+
1776
+
1777
+	/**
1778
+	 * @param EE_Payment $payment
1779
+	 * @return void
1780
+	 * @throws EE_Error
1781
+	 */
1782
+	protected function _maybe_set_completed(EE_Payment $payment)
1783
+	{
1784
+		// Do we need to redirect them? If so, there's more work to be done.
1785
+		if (! $payment->redirect_url()) {
1786
+			$this->set_completed();
1787
+		}
1788
+	}
1789
+
1790
+
1791
+	/**
1792
+	 *    update_reg_step
1793
+	 *    this is the final step after a user  revisits the site to retry a payment
1794
+	 *
1795
+	 * @return bool
1796
+	 * @throws EE_Error
1797
+	 * @throws InvalidArgumentException
1798
+	 * @throws ReflectionException
1799
+	 * @throws EntityNotFoundException
1800
+	 * @throws InvalidDataTypeException
1801
+	 * @throws InvalidInterfaceException
1802
+	 * @throws InvalidStatusException
1803
+	 */
1804
+	public function update_reg_step()
1805
+	{
1806
+		$success = true;
1807
+		// if payment required
1808
+		if ($this->checkout->transaction->total() > 0) {
1809
+			do_action(
1810
+				'AHEE__EE_Single_Page_Checkout__process_finalize_registration__before_gateway',
1811
+				$this->checkout->transaction
1812
+			);
1813
+			// attempt payment via payment method
1814
+			$success = $this->process_reg_step();
1815
+		}
1816
+		if ($success && ! $this->checkout->redirect) {
1817
+			$this->checkout->cart->get_grand_total()->save_this_and_descendants_to_txn(
1818
+				$this->checkout->transaction->ID()
1819
+			);
1820
+			// set return URL
1821
+			$this->checkout->redirect_url = add_query_arg(
1822
+				['e_reg_url_link' => $this->checkout->reg_url_link],
1823
+				$this->checkout->thank_you_page_url
1824
+			);
1825
+		}
1826
+		return $success;
1827
+	}
1828
+
1829
+
1830
+	/**
1831
+	 * @return EE_Payment|null
1832
+	 * @throws EE_Error
1833
+	 * @throws InvalidArgumentException
1834
+	 * @throws ReflectionException
1835
+	 * @throws RuntimeException
1836
+	 * @throws InvalidDataTypeException
1837
+	 * @throws InvalidInterfaceException
1838
+	 */
1839
+	private function _process_payment()
1840
+	{
1841
+		// basically confirm that the event hasn't sold out since they hit the page
1842
+		if (! $this->_last_second_ticket_verifications()) {
1843
+			return null;
1844
+		}
1845
+		// ya gotta make a choice man
1846
+		if (empty($this->checkout->selected_method_of_payment)) {
1847
+			$this->checkout->json_response->set_plz_select_method_of_payment(
1848
+				esc_html__('Please select a method of payment before proceeding.', 'event_espresso')
1849
+			);
1850
+			return null;
1851
+		}
1852
+		// get EE_Payment_Method object
1853
+		if (! $this->checkout->payment_method = $this->_get_payment_method_for_selected_method_of_payment()) {
1854
+			return null;
1855
+		}
1856
+		// setup billing form
1857
+		if ($this->checkout->payment_method->type_obj()->has_billing_form()) {
1858
+			$this->checkout->billing_form = $this->_get_billing_form_for_payment_method(
1859
+				$this->checkout->payment_method
1860
+			);
1861
+			// bad billing form ?
1862
+			if (! $this->_billing_form_is_valid()) {
1863
+				return null;
1864
+			}
1865
+		}
1866
+		// ensure primary registrant has been fully processed
1867
+		if (! $this->_setup_primary_registrant_prior_to_payment()) {
1868
+			return null;
1869
+		}
1870
+		// if session is close to expiring (under 10 minutes by default)
1871
+		if ((time() - EE_Registry::instance()->SSN->expiration()) < EE_Registry::instance()->SSN->extension()) {
1872
+			// add some time to session expiration so that payment can be completed
1873
+			EE_Registry::instance()->SSN->extend_expiration();
1874
+		}
1875
+		/** @type EE_Transaction_Processor $transaction_processor */
1876
+		// $transaction_processor = EE_Registry::instance()->load_class( 'Transaction_Processor' );
1877
+		// in case a registrant leaves to an Off-Site Gateway and never returns, we want to approve any registrations
1878
+		// for events with a default reg status of Approved
1879
+		// $transaction_processor->toggle_registration_statuses_for_default_approved_events(
1880
+		//      $this->checkout->transaction, $this->checkout->reg_cache_where_params
1881
+		// );
1882
+		// attempt payment
1883
+		$payment = $this->_attempt_payment($this->checkout->payment_method);
1884
+		// process results
1885
+		$payment = $this->_validate_payment($payment);
1886
+		$payment = $this->_post_payment_processing($payment);
1887
+		// verify payment
1888
+		if ($payment instanceof EE_Payment) {
1889
+			// store that for later
1890
+			$this->checkout->payment = $payment;
1891
+			// we can also consider the TXN to not have been failed, so temporarily upgrade it's status to abandoned
1892
+			$this->checkout->transaction->toggle_failed_transaction_status();
1893
+			$payment_status = $payment->status();
1894
+			if (
1895
+				$payment_status === EEM_Payment::status_id_approved
1896
+				|| $payment_status === EEM_Payment::status_id_pending
1897
+			) {
1898
+				return $payment;
1899
+			}
1900
+			return null;
1901
+		}
1902
+		if ($payment === true) {
1903
+			// please note that offline payment methods will NOT make a payment,
1904
+			// but instead just mark themselves as the PMD_ID on the transaction, and return true
1905
+			$this->checkout->payment = $payment;
1906
+			return $payment;
1907
+		}
1908
+		// where's my money?
1909
+		return null;
1910
+	}
1911
+
1912
+
1913
+	/**
1914
+	 * _last_second_ticket_verifications
1915
+	 *
1916
+	 * @return bool
1917
+	 * @throws EE_Error
1918
+	 * @throws ReflectionException
1919
+	 */
1920
+	protected function _last_second_ticket_verifications()
1921
+	{
1922
+		// don't bother re-validating if not a return visit
1923
+		if (! $this->checkout->revisit) {
1924
+			return true;
1925
+		}
1926
+		$registrations = $this->checkout->transaction->registrations();
1927
+		if (empty($registrations)) {
1928
+			return false;
1929
+		}
1930
+		foreach ($registrations as $registration) {
1931
+			if ($registration instanceof EE_Registration && ! $registration->is_approved()) {
1932
+				$event = $registration->event_obj();
1933
+				if ($event instanceof EE_Event && $event->is_sold_out(true)) {
1934
+					EE_Error::add_error(
1935
+						apply_filters(
1936
+							'FHEE__EE_SPCO_Reg_Step_Payment_Options___last_second_ticket_verifications__sold_out_events_msg',
1937
+							sprintf(
1938
+								esc_html__(
1939
+									'It appears that the %1$s event that you were about to make a payment for has sold out since you first registered and/or arrived at this page. Please refresh the page and try again. If you have already made a partial payment towards this event, please contact the event administrator for a refund.',
1940
+									'event_espresso'
1941
+								),
1942
+								$event->name()
1943
+							)
1944
+						),
1945
+						__FILE__,
1946
+						__FUNCTION__,
1947
+						__LINE__
1948
+					);
1949
+					return false;
1950
+				}
1951
+			}
1952
+		}
1953
+		return true;
1954
+	}
1955
+
1956
+
1957
+	/**
1958
+	 * redirect_form
1959
+	 *
1960
+	 * @return bool
1961
+	 * @throws EE_Error
1962
+	 * @throws InvalidArgumentException
1963
+	 * @throws ReflectionException
1964
+	 * @throws InvalidDataTypeException
1965
+	 * @throws InvalidInterfaceException
1966
+	 */
1967
+	public function redirect_form()
1968
+	{
1969
+		$payment_method_billing_info = $this->_payment_method_billing_info(
1970
+			$this->_get_payment_method_for_selected_method_of_payment()
1971
+		);
1972
+		$html                        = $payment_method_billing_info->get_html();
1973
+		$html                        .= $this->checkout->redirect_form;
1974
+		/** @var ResponseInterface $response */
1975
+		$response = LoaderFactory::getLoader()->getShared(ResponseInterface::class);
1976
+		$response->addOutput($html);
1977
+		return true;
1978
+	}
1979
+
1980
+
1981
+	/**
1982
+	 * _billing_form_is_valid
1983
+	 *
1984
+	 * @return bool
1985
+	 * @throws EE_Error
1986
+	 */
1987
+	private function _billing_form_is_valid()
1988
+	{
1989
+		if (! $this->checkout->payment_method->type_obj()->has_billing_form()) {
1990
+			return true;
1991
+		}
1992
+		if ($this->checkout->billing_form instanceof EE_Billing_Info_Form) {
1993
+			if ($this->checkout->billing_form->was_submitted()) {
1994
+				$this->checkout->billing_form->receive_form_submission();
1995
+				if ($this->checkout->billing_form->is_valid()) {
1996
+					return true;
1997
+				}
1998
+				$validation_errors = $this->checkout->billing_form->get_validation_errors_accumulated();
1999
+				$error_strings     = [];
2000
+				foreach ($validation_errors as $validation_error) {
2001
+					if ($validation_error instanceof EE_Validation_Error) {
2002
+						$form_section = $validation_error->get_form_section();
2003
+						if ($form_section instanceof EE_Form_Input_Base) {
2004
+							$label = $form_section->html_label_text();
2005
+						} elseif ($form_section instanceof EE_Form_Section_Base) {
2006
+							$label = $form_section->name();
2007
+						} else {
2008
+							$label = esc_html__('Validation Error', 'event_espresso');
2009
+						}
2010
+						$error_strings[] = sprintf('%1$s: %2$s', $label, $validation_error->getMessage());
2011
+					}
2012
+				}
2013
+				EE_Error::add_error(
2014
+					sprintf(
2015
+						esc_html__(
2016
+							'One or more billing form inputs are invalid and require correction before proceeding. %1$s %2$s',
2017
+							'event_espresso'
2018
+						),
2019
+						'<br/>',
2020
+						implode('<br/>', $error_strings)
2021
+					),
2022
+					__FILE__,
2023
+					__FUNCTION__,
2024
+					__LINE__
2025
+				);
2026
+			} else {
2027
+				EE_Error::add_error(
2028
+					esc_html__(
2029
+						'The billing form was not submitted or something prevented it\'s submission.',
2030
+						'event_espresso'
2031
+					),
2032
+					__FILE__,
2033
+					__FUNCTION__,
2034
+					__LINE__
2035
+				);
2036
+			}
2037
+		} else {
2038
+			EE_Error::add_error(
2039
+				esc_html__(
2040
+					'The submitted billing form is invalid possibly due to a technical reason.',
2041
+					'event_espresso'
2042
+				),
2043
+				__FILE__,
2044
+				__FUNCTION__,
2045
+				__LINE__
2046
+			);
2047
+		}
2048
+		return false;
2049
+	}
2050
+
2051
+
2052
+	/**
2053
+	 * _setup_primary_registrant_prior_to_payment
2054
+	 * ensures that the primary registrant has a valid attendee object created with the critical details populated
2055
+	 * (first & last name & email) and that both the transaction object and primary registration object have been saved
2056
+	 * plz note that any other registrations will NOT be saved at this point (because they may not have any details
2057
+	 * yet)
2058
+	 *
2059
+	 * @return bool
2060
+	 * @throws EE_Error
2061
+	 * @throws InvalidArgumentException
2062
+	 * @throws ReflectionException
2063
+	 * @throws RuntimeException
2064
+	 * @throws InvalidDataTypeException
2065
+	 * @throws InvalidInterfaceException
2066
+	 */
2067
+	private function _setup_primary_registrant_prior_to_payment()
2068
+	{
2069
+		// check if transaction has a primary registrant and that it has a related Attendee object
2070
+		// if not, then we need to at least gather some primary registrant data before attempting payment
2071
+		if (
2072
+			$this->checkout->billing_form instanceof EE_Billing_Attendee_Info_Form
2073
+			&& ! $this->checkout->transaction_has_primary_registrant()
2074
+			&& ! $this->_capture_primary_registration_data_from_billing_form()
2075
+		) {
2076
+			return false;
2077
+		}
2078
+		// because saving an object clears it's cache, we need to do the chevy shuffle
2079
+		// grab the primary_registration object
2080
+		$primary_registration = $this->checkout->transaction->primary_registration();
2081
+		// at this point we'll consider a TXN to not have been failed
2082
+		$this->checkout->transaction->toggle_failed_transaction_status();
2083
+		// save the TXN ( which clears cached copy of primary_registration)
2084
+		$this->checkout->transaction->save();
2085
+		// grab TXN ID and save it to the primary_registration
2086
+		$primary_registration->set_transaction_id($this->checkout->transaction->ID());
2087
+		// save what we have so far
2088
+		$primary_registration->save();
2089
+		return true;
2090
+	}
2091
+
2092
+
2093
+	/**
2094
+	 * _capture_primary_registration_data_from_billing_form
2095
+	 *
2096
+	 * @return bool
2097
+	 * @throws EE_Error
2098
+	 * @throws InvalidArgumentException
2099
+	 * @throws ReflectionException
2100
+	 * @throws InvalidDataTypeException
2101
+	 * @throws InvalidInterfaceException
2102
+	 */
2103
+	private function _capture_primary_registration_data_from_billing_form()
2104
+	{
2105
+		// convert billing form data into an attendee
2106
+		$this->checkout->primary_attendee_obj = $this->checkout->billing_form->create_attendee_from_billing_form_data();
2107
+		if (! $this->checkout->primary_attendee_obj instanceof EE_Attendee) {
2108
+			EE_Error::add_error(
2109
+				sprintf(
2110
+					esc_html__(
2111
+						'The billing form details could not be used for attendee details due to a technical issue.%sPlease try again or contact %s for assistance.',
2112
+						'event_espresso'
2113
+					),
2114
+					'<br/>',
2115
+					EE_Registry::instance()->CFG->organization->get_pretty('email')
2116
+				),
2117
+				__FILE__,
2118
+				__FUNCTION__,
2119
+				__LINE__
2120
+			);
2121
+			return false;
2122
+		}
2123
+		$primary_registration = $this->checkout->transaction->primary_registration();
2124
+		if (! $primary_registration instanceof EE_Registration) {
2125
+			EE_Error::add_error(
2126
+				sprintf(
2127
+					esc_html__(
2128
+						'The primary registrant for this transaction could not be determined due to a technical issue.%sPlease try again or contact %s for assistance.',
2129
+						'event_espresso'
2130
+					),
2131
+					'<br/>',
2132
+					EE_Registry::instance()->CFG->organization->get_pretty('email')
2133
+				),
2134
+				__FILE__,
2135
+				__FUNCTION__,
2136
+				__LINE__
2137
+			);
2138
+			return false;
2139
+		}
2140
+		if (
2141
+			! $primary_registration->_add_relation_to($this->checkout->primary_attendee_obj, 'Attendee')
2142
+			  instanceof
2143
+			  EE_Attendee
2144
+		) {
2145
+			EE_Error::add_error(
2146
+				sprintf(
2147
+					esc_html__(
2148
+						'The primary registrant could not be associated with this transaction due to a technical issue.%sPlease try again or contact %s for assistance.',
2149
+						'event_espresso'
2150
+					),
2151
+					'<br/>',
2152
+					EE_Registry::instance()->CFG->organization->get_pretty('email')
2153
+				),
2154
+				__FILE__,
2155
+				__FUNCTION__,
2156
+				__LINE__
2157
+			);
2158
+			return false;
2159
+		}
2160
+		/** @type EE_Registration_Processor $registration_processor */
2161
+		$registration_processor = EE_Registry::instance()->load_class('Registration_Processor');
2162
+		// at this point, we should have enough details about the registrant to consider the registration NOT incomplete
2163
+		$registration_processor->toggle_incomplete_registration_status_to_default($primary_registration);
2164
+		return true;
2165
+	}
2166
+
2167
+
2168
+	/**
2169
+	 * _get_payment_method_for_selected_method_of_payment
2170
+	 * retrieves a valid payment method
2171
+	 *
2172
+	 * @return EE_Payment_Method
2173
+	 * @throws EE_Error
2174
+	 * @throws InvalidArgumentException
2175
+	 * @throws ReflectionException
2176
+	 * @throws InvalidDataTypeException
2177
+	 * @throws InvalidInterfaceException
2178
+	 */
2179
+	private function _get_payment_method_for_selected_method_of_payment()
2180
+	{
2181
+		if ($this->checkout->selected_method_of_payment === 'events_sold_out') {
2182
+			$this->_redirect_because_event_sold_out();
2183
+			return null;
2184
+		}
2185
+		// get EE_Payment_Method object
2186
+		if (isset($this->checkout->available_payment_methods[ $this->checkout->selected_method_of_payment ])) {
2187
+			$payment_method = $this->checkout->available_payment_methods[ $this->checkout->selected_method_of_payment ];
2188
+		} else {
2189
+			// load EEM_Payment_Method
2190
+			EE_Registry::instance()->load_model('Payment_Method');
2191
+			$EEM_Payment_Method = EEM_Payment_Method::instance();
2192
+			$payment_method     = $EEM_Payment_Method->get_one_by_slug($this->checkout->selected_method_of_payment);
2193
+		}
2194
+		// verify $payment_method
2195
+		if (! $payment_method instanceof EE_Payment_Method) {
2196
+			// not a payment
2197
+			EE_Error::add_error(
2198
+				sprintf(
2199
+					esc_html__(
2200
+						'The selected method of payment could not be determined due to a technical issue.%sPlease try again or contact %s for assistance.',
2201
+						'event_espresso'
2202
+					),
2203
+					'<br/>',
2204
+					EE_Registry::instance()->CFG->organization->get_pretty('email')
2205
+				),
2206
+				__FILE__,
2207
+				__FUNCTION__,
2208
+				__LINE__
2209
+			);
2210
+			return null;
2211
+		}
2212
+		// and verify it has a valid Payment_Method Type object
2213
+		if (! $payment_method->type_obj() instanceof EE_PMT_Base) {
2214
+			// not a payment
2215
+			EE_Error::add_error(
2216
+				sprintf(
2217
+					esc_html__(
2218
+						'A valid payment method could not be determined due to a technical issue.%sPlease try again or contact %s for assistance.',
2219
+						'event_espresso'
2220
+					),
2221
+					'<br/>',
2222
+					EE_Registry::instance()->CFG->organization->get_pretty('email')
2223
+				),
2224
+				__FILE__,
2225
+				__FUNCTION__,
2226
+				__LINE__
2227
+			);
2228
+			return null;
2229
+		}
2230
+		return $payment_method;
2231
+	}
2232
+
2233
+
2234
+	/**
2235
+	 *    _attempt_payment
2236
+	 *
2237
+	 * @access    private
2238
+	 * @type    EE_Payment_Method $payment_method
2239
+	 * @return mixed EE_Payment | boolean
2240
+	 * @throws EE_Error
2241
+	 * @throws InvalidArgumentException
2242
+	 * @throws ReflectionException
2243
+	 * @throws InvalidDataTypeException
2244
+	 * @throws InvalidInterfaceException
2245
+	 */
2246
+	private function _attempt_payment(EE_Payment_Method $payment_method)
2247
+	{
2248
+		$payment = null;
2249
+		$this->checkout->transaction->save();
2250
+		$payment_processor = EE_Registry::instance()->load_core('Payment_Processor');
2251
+		if (! $payment_processor instanceof EE_Payment_Processor) {
2252
+			return false;
2253
+		}
2254
+		try {
2255
+			$payment_processor->set_revisit($this->checkout->revisit);
2256
+			// generate payment object
2257
+			$payment = $payment_processor->process_payment(
2258
+				$payment_method,
2259
+				$this->checkout->transaction,
2260
+				$this->checkout->amount_owing,
2261
+				$this->checkout->billing_form,
2262
+				$this->_get_return_url($payment_method),
2263
+				'CART',
2264
+				$this->checkout->admin_request,
2265
+				true,
2266
+				$this->reg_step_url()
2267
+			);
2268
+		} catch (Exception $e) {
2269
+			$this->_handle_payment_processor_exception($e);
2270
+		}
2271
+		return $payment;
2272
+	}
2273
+
2274
+
2275
+	/**
2276
+	 * _handle_payment_processor_exception
2277
+	 *
2278
+	 * @param Exception $e
2279
+	 * @return void
2280
+	 * @throws EE_Error
2281
+	 * @throws InvalidArgumentException
2282
+	 * @throws InvalidDataTypeException
2283
+	 * @throws InvalidInterfaceException
2284
+	 */
2285
+	protected function _handle_payment_processor_exception(Exception $e)
2286
+	{
2287
+		EE_Error::add_error(
2288
+			sprintf(
2289
+				esc_html__(
2290
+					'The payment could not br processed due to a technical issue.%1$sPlease try again or contact %2$s for assistance.||The following Exception was thrown in %4$s on line %5$s:%1$s%3$s',
2291
+					'event_espresso'
2292
+				),
2293
+				'<br/>',
2294
+				EE_Registry::instance()->CFG->organization->get_pretty('email'),
2295
+				$e->getMessage(),
2296
+				$e->getFile(),
2297
+				$e->getLine()
2298
+			),
2299
+			__FILE__,
2300
+			__FUNCTION__,
2301
+			__LINE__
2302
+		);
2303
+	}
2304
+
2305
+
2306
+	/**
2307
+	 * _get_return_url
2308
+	 *
2309
+	 * @param EE_Payment_Method $payment_method
2310
+	 * @return string
2311
+	 * @throws EE_Error
2312
+	 * @throws ReflectionException
2313
+	 */
2314
+	protected function _get_return_url(EE_Payment_Method $payment_method)
2315
+	{
2316
+		$return_url = '';
2317
+		switch ($payment_method->type_obj()->payment_occurs()) {
2318
+			case EE_PMT_Base::offsite:
2319
+				$return_url = add_query_arg(
2320
+					[
2321
+						'action'                     => 'process_gateway_response',
2322
+						'selected_method_of_payment' => $this->checkout->selected_method_of_payment,
2323
+						'spco_txn'                   => $this->checkout->transaction->ID(),
2324
+					],
2325
+					$this->reg_step_url()
2326
+				);
2327
+				break;
2328
+			case EE_PMT_Base::onsite:
2329
+			case EE_PMT_Base::offline:
2330
+				$return_url = $this->checkout->next_step->reg_step_url();
2331
+				break;
2332
+		}
2333
+		return $return_url;
2334
+	}
2335
+
2336
+
2337
+	/**
2338
+	 * _validate_payment
2339
+	 *
2340
+	 * @param EE_Payment $payment
2341
+	 * @return EE_Payment|FALSE
2342
+	 * @throws EE_Error
2343
+	 * @throws InvalidArgumentException
2344
+	 * @throws InvalidDataTypeException
2345
+	 * @throws InvalidInterfaceException
2346
+	 */
2347
+	private function _validate_payment($payment = null)
2348
+	{
2349
+		if ($this->checkout->payment_method->is_off_line()) {
2350
+			return true;
2351
+		}
2352
+		// verify payment object
2353
+		if (! $payment instanceof EE_Payment) {
2354
+			// not a payment
2355
+			EE_Error::add_error(
2356
+				sprintf(
2357
+					esc_html__(
2358
+						'A valid payment was not generated due to a technical issue.%1$sPlease try again or contact %2$s for assistance.',
2359
+						'event_espresso'
2360
+					),
2361
+					'<br/>',
2362
+					EE_Registry::instance()->CFG->organization->get_pretty('email')
2363
+				),
2364
+				__FILE__,
2365
+				__FUNCTION__,
2366
+				__LINE__
2367
+			);
2368
+			return false;
2369
+		}
2370
+		return $payment;
2371
+	}
2372
+
2373
+
2374
+	/**
2375
+	 * _post_payment_processing
2376
+	 *
2377
+	 * @param EE_Payment|bool $payment
2378
+	 * @return bool|EE_Payment
2379
+	 * @throws EE_Error
2380
+	 * @throws InvalidArgumentException
2381
+	 * @throws InvalidDataTypeException
2382
+	 * @throws InvalidInterfaceException
2383
+	 * @throws ReflectionException
2384
+	 */
2385
+	private function _post_payment_processing($payment = null)
2386
+	{
2387
+		// Off-Line payment?
2388
+		if ($payment === true) {
2389
+			return true;
2390
+		}
2391
+		if ($payment instanceof EE_Payment) {
2392
+			// Should the user be redirected?
2393
+			if ($payment->redirect_url()) {
2394
+				do_action('AHEE_log', __CLASS__, __FUNCTION__, $payment->redirect_url(), '$payment->redirect_url()');
2395
+				$this->checkout->redirect      = true;
2396
+				$this->checkout->redirect_form = $payment->redirect_form();
2397
+				$this->checkout->redirect_url  = $this->reg_step_url('redirect_form');
2398
+				// set JSON response
2399
+				$this->checkout->json_response->set_redirect_form($this->checkout->redirect_form);
2400
+				// and lastly, let's bump the payment status to pending
2401
+				$payment->set_status(EEM_Payment::status_id_pending);
2402
+				$payment->save();
2403
+			} elseif (! $this->_process_payment_status($payment, EE_PMT_Base::onsite)) {
2404
+				// User shouldn't be redirected. So let's process it here.
2405
+				// $this->_setup_redirect_for_next_step();
2406
+				$this->checkout->continue_reg = false;
2407
+			}
2408
+			return $payment;
2409
+		}
2410
+		// ummm ya... not Off-Line, not On-Site, not off-Site ????
2411
+		$this->checkout->continue_reg = false;
2412
+		return false;
2413
+	}
2414
+
2415
+
2416
+	/**
2417
+	 *    _process_payment_status
2418
+	 *
2419
+	 * @type    EE_Payment $payment
2420
+	 * @param string       $payment_occurs
2421
+	 * @return bool
2422
+	 * @throws EE_Error
2423
+	 * @throws InvalidArgumentException
2424
+	 * @throws InvalidDataTypeException
2425
+	 * @throws InvalidInterfaceException
2426
+	 */
2427
+	private function _process_payment_status($payment, $payment_occurs = EE_PMT_Base::offline)
2428
+	{
2429
+		// off-line payment? carry on
2430
+		if ($payment_occurs === EE_PMT_Base::offline) {
2431
+			return true;
2432
+		}
2433
+		// verify payment validity
2434
+		if ($payment instanceof EE_Payment) {
2435
+			do_action('AHEE_log', __CLASS__, __FUNCTION__, $payment->status(), '$payment->status()');
2436
+			$msg = $payment->gateway_response();
2437
+			// check results
2438
+			switch ($payment->status()) {
2439
+				// good payment
2440
+				case EEM_Payment::status_id_approved:
2441
+					EE_Error::add_success(
2442
+						esc_html__('Your payment was processed successfully.', 'event_espresso'),
2443
+						__FILE__,
2444
+						__FUNCTION__,
2445
+						__LINE__
2446
+					);
2447
+					return true;
2448
+				// slow payment
2449
+				case EEM_Payment::status_id_pending:
2450
+					if (empty($msg)) {
2451
+						$msg = esc_html__(
2452
+							'Your payment appears to have been processed successfully, but the Instant Payment Notification has not yet been received. It should arrive shortly.',
2453
+							'event_espresso'
2454
+						);
2455
+					}
2456
+					EE_Error::add_success($msg, __FILE__, __FUNCTION__, __LINE__);
2457
+					return true;
2458
+				// don't wanna payment
2459
+				case EEM_Payment::status_id_cancelled:
2460
+					if (empty($msg)) {
2461
+						$msg = _n(
2462
+							'Payment cancelled. Please try again.',
2463
+							'Payment cancelled. Please try again or select another method of payment.',
2464
+							count($this->checkout->available_payment_methods),
2465
+							'event_espresso'
2466
+						);
2467
+					}
2468
+					EE_Error::add_attention($msg, __FILE__, __FUNCTION__, __LINE__);
2469
+					return false;
2470
+				// not enough payment
2471
+				case EEM_Payment::status_id_declined:
2472
+					if (empty($msg)) {
2473
+						$msg = _n(
2474
+							'We\'re sorry but your payment was declined. Please try again.',
2475
+							'We\'re sorry but your payment was declined. Please try again or select another method of payment.',
2476
+							count($this->checkout->available_payment_methods),
2477
+							'event_espresso'
2478
+						);
2479
+					}
2480
+					EE_Error::add_attention($msg, __FILE__, __FUNCTION__, __LINE__);
2481
+					return false;
2482
+				// bad payment
2483
+				case EEM_Payment::status_id_failed:
2484
+					if (! empty($msg)) {
2485
+						EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
2486
+						return false;
2487
+					}
2488
+					// default to error below
2489
+					break;
2490
+			}
2491
+		}
2492
+		// off-site payment gateway responses are too unreliable, so let's just assume that
2493
+		// the payment processing is just running slower than the registrant's request
2494
+		if ($payment_occurs === EE_PMT_Base::offsite) {
2495
+			return true;
2496
+		}
2497
+		EE_Error::add_error(
2498
+			sprintf(
2499
+				esc_html__(
2500
+					'Your payment could not be processed successfully due to a technical issue.%sPlease try again or contact %s for assistance.',
2501
+					'event_espresso'
2502
+				),
2503
+				'<br/>',
2504
+				EE_Registry::instance()->CFG->organization->get_pretty('email')
2505
+			),
2506
+			__FILE__,
2507
+			__FUNCTION__,
2508
+			__LINE__
2509
+		);
2510
+		return false;
2511
+	}
2512
+
2513
+
2514
+
2515
+
2516
+
2517
+
2518
+	/********************************************************************************************************/
2519
+	/**********************************  PROCESS GATEWAY RESPONSE  **********************************/
2520
+	/********************************************************************************************************/
2521
+	/**
2522
+	 * process_gateway_response
2523
+	 * this is the return point for Off-Site Payment Methods
2524
+	 * It will attempt to "handle the IPN" if it appears that this has not already occurred,
2525
+	 * otherwise, it will load up the last payment made for the TXN.
2526
+	 * If the payment retrieved looks good, it will then either:
2527
+	 *    complete the current step and allow advancement to the next reg step
2528
+	 *        or present the payment options again
2529
+	 *
2530
+	 * @return bool
2531
+	 * @throws EE_Error
2532
+	 * @throws InvalidArgumentException
2533
+	 * @throws ReflectionException
2534
+	 * @throws InvalidDataTypeException
2535
+	 * @throws InvalidInterfaceException
2536
+	 */
2537
+	public function process_gateway_response()
2538
+	{
2539
+		// how have they chosen to pay?
2540
+		$this->checkout->selected_method_of_payment = $this->_get_selected_method_of_payment(true);
2541
+		// get EE_Payment_Method object
2542
+		if (! $this->checkout->payment_method = $this->_get_payment_method_for_selected_method_of_payment()) {
2543
+			$this->checkout->continue_reg = false;
2544
+			return false;
2545
+		}
2546
+		if (! $this->checkout->payment_method->is_off_site()) {
2547
+			return false;
2548
+		}
2549
+		$this->_validate_offsite_return();
2550
+		// verify TXN
2551
+		if ($this->checkout->transaction instanceof EE_Transaction) {
2552
+			$gateway = $this->checkout->payment_method->type_obj()->get_gateway();
2553
+			if (! $gateway instanceof EE_Offsite_Gateway) {
2554
+				$this->checkout->continue_reg = false;
2555
+				return false;
2556
+			}
2557
+			$payment = $this->_process_off_site_payment($gateway);
2558
+			$payment = $this->_process_cancelled_payments($payment);
2559
+			$payment = $this->_validate_payment($payment);
2560
+			// if payment was not declined by the payment gateway or cancelled by the registrant
2561
+			if ($this->_process_payment_status($payment, EE_PMT_Base::offsite)) {
2562
+				// $this->_setup_redirect_for_next_step();
2563
+				// store that for later
2564
+				$this->checkout->payment = $payment;
2565
+				// mark this reg step as completed, as long as gateway doesn't use a separate IPN request,
2566
+				// because we will complete this step during the IPN processing then
2567
+				if (! $this->handle_IPN_in_this_request()) {
2568
+					$this->set_completed();
2569
+				}
2570
+				return true;
2571
+			}
2572
+		}
2573
+		// DEBUG LOG
2574
+		// $this->checkout->log(
2575
+		//     __CLASS__,
2576
+		//     __FUNCTION__,
2577
+		//     __LINE__,
2578
+		//     array('payment' => $payment)
2579
+		// );
2580
+		$this->checkout->continue_reg = false;
2581
+		return false;
2582
+	}
2583
+
2584
+
2585
+	/**
2586
+	 * _validate_return
2587
+	 *
2588
+	 * @return void
2589
+	 * @throws EE_Error
2590
+	 * @throws InvalidArgumentException
2591
+	 * @throws InvalidDataTypeException
2592
+	 * @throws InvalidInterfaceException
2593
+	 * @throws ReflectionException
2594
+	 */
2595
+	private function _validate_offsite_return()
2596
+	{
2597
+		$TXN_ID = $this->request->getRequestParam('spco_txn', 0, 'int');
2598
+		if ($TXN_ID !== $this->checkout->transaction->ID()) {
2599
+			// Houston... we might have a problem
2600
+			$invalid_TXN = false;
2601
+			// first gather some info
2602
+			$valid_TXN          = EEM_Transaction::instance()->get_one_by_ID($TXN_ID);
2603
+			$primary_registrant = $valid_TXN instanceof EE_Transaction
2604
+				? $valid_TXN->primary_registration()
2605
+				: null;
2606
+			// let's start by retrieving the cart for this TXN
2607
+			$cart = $this->checkout->get_cart_for_transaction($this->checkout->transaction);
2608
+			if ($cart instanceof EE_Cart) {
2609
+				// verify that the current cart has tickets
2610
+				$tickets = $cart->get_tickets();
2611
+				if (empty($tickets)) {
2612
+					$invalid_TXN = true;
2613
+				}
2614
+			} else {
2615
+				$invalid_TXN = true;
2616
+			}
2617
+			$valid_TXN_SID = $primary_registrant instanceof EE_Registration
2618
+				? $primary_registrant->session_ID()
2619
+				: null;
2620
+			// validate current Session ID and compare against valid TXN session ID
2621
+			if (
2622
+				$invalid_TXN // if this is already true, then skip other checks
2623
+				|| EE_Session::instance()->id() === null
2624
+				|| (
2625
+					// WARNING !!!
2626
+					// this could be PayPal sending back duplicate requests (ya they do that)
2627
+					// or it **could** mean someone is simply registering AGAIN after having just done so
2628
+					// so now we need to determine if this current TXN looks valid or not
2629
+					// and whether this reg step has even been started ?
2630
+					EE_Session::instance()->id() === $valid_TXN_SID
2631
+					// really? you're half way through this reg step, but you never started it ?
2632
+					&& $this->checkout->transaction->reg_step_completed($this->slug()) === false
2633
+				)
2634
+			) {
2635
+				$invalid_TXN = true;
2636
+			}
2637
+			if ($invalid_TXN) {
2638
+				// is the valid TXN completed ?
2639
+				if ($valid_TXN instanceof EE_Transaction) {
2640
+					// has this step even been started ?
2641
+					$reg_step_completed = $valid_TXN->reg_step_completed($this->slug());
2642
+					if ($reg_step_completed !== false && $reg_step_completed !== true) {
2643
+						// so it **looks** like this is a double request from PayPal
2644
+						// so let's try to pick up where we left off
2645
+						$this->checkout->transaction = $valid_TXN;
2646
+						$this->checkout->refresh_all_entities(true);
2647
+						return;
2648
+					}
2649
+				}
2650
+				// you appear to be lost?
2651
+				$this->_redirect_wayward_request($primary_registrant);
2652
+			}
2653
+		}
2654
+	}
2655
+
2656
+
2657
+	/**
2658
+	 * _redirect_wayward_request
2659
+	 *
2660
+	 * @param EE_Registration|null $primary_registrant
2661
+	 * @return void
2662
+	 * @throws EE_Error
2663
+	 * @throws InvalidArgumentException
2664
+	 * @throws InvalidDataTypeException
2665
+	 * @throws InvalidInterfaceException
2666
+	 * @throws ReflectionException
2667
+	 */
2668
+	private function _redirect_wayward_request(EE_Registration $primary_registrant)
2669
+	{
2670
+		if (! $primary_registrant instanceof EE_Registration) {
2671
+			// try redirecting based on the current TXN
2672
+			$primary_registrant = $this->checkout->transaction instanceof EE_Transaction
2673
+				? $this->checkout->transaction->primary_registration()
2674
+				: null;
2675
+		}
2676
+		if (! $primary_registrant instanceof EE_Registration) {
2677
+			EE_Error::add_error(
2678
+				sprintf(
2679
+					esc_html__(
2680
+						'Invalid information was received from the Off-Site Payment Processor and your Transaction details could not be retrieved from the database.%1$sPlease try again or contact %2$s for assistance.',
2681
+						'event_espresso'
2682
+					),
2683
+					'<br/>',
2684
+					EE_Registry::instance()->CFG->organization->get_pretty('email')
2685
+				),
2686
+				__FILE__,
2687
+				__FUNCTION__,
2688
+				__LINE__
2689
+			);
2690
+			return;
2691
+		}
2692
+		// make sure transaction is not locked
2693
+		$this->checkout->transaction->unlock();
2694
+		wp_safe_redirect(
2695
+			add_query_arg(
2696
+				[
2697
+					'e_reg_url_link' => $primary_registrant->reg_url_link(),
2698
+				],
2699
+				$this->checkout->thank_you_page_url
2700
+			)
2701
+		);
2702
+		exit();
2703
+	}
2704
+
2705
+
2706
+	/**
2707
+	 * _process_off_site_payment
2708
+	 *
2709
+	 * @param EE_Offsite_Gateway $gateway
2710
+	 * @return EE_Payment
2711
+	 * @throws EE_Error
2712
+	 * @throws InvalidArgumentException
2713
+	 * @throws InvalidDataTypeException
2714
+	 * @throws InvalidInterfaceException
2715
+	 * @throws ReflectionException
2716
+	 */
2717
+	private function _process_off_site_payment(EE_Offsite_Gateway $gateway)
2718
+	{
2719
+		try {
2720
+			$request      = LoaderFactory::getLoader()->getShared(RequestInterface::class);
2721
+			$request_data = $request->requestParams();
2722
+			// if gateway uses_separate_IPN_request, then we don't have to process the IPN manually
2723
+			$this->set_handle_IPN_in_this_request(
2724
+				$gateway->handle_IPN_in_this_request($request_data, false)
2725
+			);
2726
+			if ($this->handle_IPN_in_this_request()) {
2727
+				// get payment details and process results
2728
+				/** @type EE_Payment_Processor $payment_processor */
2729
+				$payment_processor = EE_Registry::instance()->load_core('Payment_Processor');
2730
+				$payment           = $payment_processor->process_ipn(
2731
+					$request_data,
2732
+					$this->checkout->transaction,
2733
+					$this->checkout->payment_method,
2734
+					true,
2735
+					false
2736
+				);
2737
+				// $payment_source = 'process_ipn';
2738
+			} else {
2739
+				$payment = $this->checkout->transaction->last_payment();
2740
+				// $payment_source = 'last_payment';
2741
+			}
2742
+		} catch (Exception $e) {
2743
+			// let's just eat the exception and try to move on using any previously set payment info
2744
+			$payment = $this->checkout->transaction->last_payment();
2745
+			// $payment_source = 'last_payment after Exception';
2746
+			// but if we STILL don't have a payment object
2747
+			if (! $payment instanceof EE_Payment) {
2748
+				// then we'll object ! ( not object like a thing... but object like what a lawyer says ! )
2749
+				$this->_handle_payment_processor_exception($e);
2750
+			}
2751
+		}
2752
+		return $payment;
2753
+	}
2754
+
2755
+
2756
+	/**
2757
+	 * _process_cancelled_payments
2758
+	 * just makes sure that the payment status gets updated correctly
2759
+	 * so tha tan error isn't generated during payment validation
2760
+	 *
2761
+	 * @param EE_Payment $payment
2762
+	 * @return EE_Payment|null
2763
+	 * @throws EE_Error
2764
+	 */
2765
+	private function _process_cancelled_payments($payment = null)
2766
+	{
2767
+		if (
2768
+			$payment instanceof EE_Payment
2769
+			&& $this->request->requestParamIsSet('ee_cancel_payment')
2770
+			&& $payment->status() === EEM_Payment::status_id_failed
2771
+		) {
2772
+			$payment->set_status(EEM_Payment::status_id_cancelled);
2773
+		}
2774
+		return $payment;
2775
+	}
2776
+
2777
+
2778
+	/**
2779
+	 *    get_transaction_details_for_gateways
2780
+	 *
2781
+	 * @access    public
2782
+	 * @return void
2783
+	 * @throws EE_Error
2784
+	 * @throws InvalidArgumentException
2785
+	 * @throws ReflectionException
2786
+	 * @throws InvalidDataTypeException
2787
+	 * @throws InvalidInterfaceException
2788
+	 */
2789
+	public function get_transaction_details_for_gateways()
2790
+	{
2791
+		$txn_details = [];
2792
+		// ya gotta make a choice man
2793
+		if (empty($this->checkout->selected_method_of_payment)) {
2794
+			$txn_details = [
2795
+				'error' => esc_html__('Please select a method of payment before proceeding.', 'event_espresso'),
2796
+			];
2797
+		}
2798
+		// get EE_Payment_Method object
2799
+		if (
2800
+			empty($txn_details)
2801
+			&& ! $this->checkout->payment_method = $this->_get_payment_method_for_selected_method_of_payment()
2802
+		) {
2803
+			$txn_details = [
2804
+				'selected_method_of_payment' => $this->checkout->selected_method_of_payment,
2805
+				'error'                      => esc_html__(
2806
+					'A valid Payment Method could not be determined.',
2807
+					'event_espresso'
2808
+				),
2809
+			];
2810
+		}
2811
+		if (empty($txn_details) && $this->checkout->transaction instanceof EE_Transaction) {
2812
+			$return_url  = $this->_get_return_url($this->checkout->payment_method);
2813
+			$txn_details = [
2814
+				'TXN_ID'         => $this->checkout->transaction->ID(),
2815
+				'TXN_timestamp'  => $this->checkout->transaction->datetime(),
2816
+				'TXN_total'      => $this->checkout->transaction->total(),
2817
+				'TXN_paid'       => $this->checkout->transaction->paid(),
2818
+				'TXN_reg_steps'  => $this->checkout->transaction->reg_steps(),
2819
+				'STS_ID'         => $this->checkout->transaction->status_ID(),
2820
+				'PMD_ID'         => $this->checkout->transaction->payment_method_ID(),
2821
+				'payment_amount' => $this->checkout->amount_owing,
2822
+				'return_url'     => $return_url,
2823
+				'cancel_url'     => add_query_arg(['ee_cancel_payment' => true], $return_url),
2824
+				'notify_url'     => EE_Config::instance()->core->txn_page_url(
2825
+					[
2826
+						'e_reg_url_link'    => $this->checkout->transaction->primary_registration()->reg_url_link(),
2827
+						'ee_payment_method' => $this->checkout->payment_method->slug(),
2828
+					]
2829
+				),
2830
+			];
2831
+		}
2832
+		echo wp_json_encode($txn_details);
2833
+		exit();
2834
+	}
2835
+
2836
+
2837
+	/**
2838
+	 *    __sleep
2839
+	 * to conserve db space, let's remove the reg_form and the EE_Checkout object from EE_SPCO_Reg_Step objects upon
2840
+	 * serialization EE_Checkout will handle the reimplementation of itself upon waking, but we won't bother with the
2841
+	 * reg form, because if needed, it will be regenerated anyways
2842
+	 *
2843
+	 * @return array
2844
+	 */
2845
+	public function __sleep()
2846
+	{
2847
+		// remove the reg form and the checkout
2848
+		return array_diff(array_keys(get_object_vars($this)), ['reg_form', 'checkout', 'line_item_display']);
2849
+	}
2850 2850
 }
Please login to merge, or discard this patch.
Spacing   +80 added lines, -80 removed lines patch added patch discarded remove patch
@@ -132,7 +132,7 @@  discard block
 block discarded – undo
132 132
         $this->request   = EED_Single_Page_Checkout::getRequest();
133 133
         $this->_slug     = 'payment_options';
134 134
         $this->_name     = esc_html__('Payment Options', 'event_espresso');
135
-        $this->_template = SPCO_REG_STEPS_PATH . $this->_slug . '/payment_options_main.template.php';
135
+        $this->_template = SPCO_REG_STEPS_PATH.$this->_slug.'/payment_options_main.template.php';
136 136
         $this->checkout  = $checkout;
137 137
         $this->_reset_success_message();
138 138
         $this->set_instructions(
@@ -187,7 +187,7 @@  discard block
 block discarded – undo
187 187
      */
188 188
     public function translate_js_strings()
189 189
     {
190
-        EE_Registry::$i18n_js_strings['no_payment_method']      = esc_html__(
190
+        EE_Registry::$i18n_js_strings['no_payment_method'] = esc_html__(
191 191
             'Please select a method of payment in order to continue.',
192 192
             'event_espresso'
193 193
         );
@@ -195,7 +195,7 @@  discard block
 block discarded – undo
195 195
             'A valid method of payment could not be determined. Please refresh the page and try again.',
196 196
             'event_espresso'
197 197
         );
198
-        EE_Registry::$i18n_js_strings['forwarding_to_offsite']  = esc_html__(
198
+        EE_Registry::$i18n_js_strings['forwarding_to_offsite'] = esc_html__(
199 199
             'Forwarding to Secure Payment Provider.',
200 200
             'event_espresso'
201 201
         );
@@ -216,7 +216,7 @@  discard block
 block discarded – undo
216 216
     {
217 217
         $transaction = $this->checkout->transaction;
218 218
         // if the transaction isn't set or nothing is owed on it, don't enqueue any JS
219
-        if (! $transaction instanceof EE_Transaction || EEH_Money::compare_floats($transaction->remaining(), 0)) {
219
+        if ( ! $transaction instanceof EE_Transaction || EEH_Money::compare_floats($transaction->remaining(), 0)) {
220 220
             return;
221 221
         }
222 222
         foreach (
@@ -311,18 +311,18 @@  discard block
 block discarded – undo
311 311
         foreach ($registrations as $REG_ID => $registration) {
312 312
             /** @var $registration EE_Registration */
313 313
             // has this registration lost it's space ?
314
-            if (isset($ejected_registrations[ $REG_ID ])) {
314
+            if (isset($ejected_registrations[$REG_ID])) {
315 315
                 if ($registration->event()->is_sold_out() || $registration->event()->is_sold_out(true)) {
316
-                    $sold_out_events[ $registration->event()->ID() ] = $registration->event();
316
+                    $sold_out_events[$registration->event()->ID()] = $registration->event();
317 317
                 } else {
318
-                    $insufficient_spaces_available[ $registration->event()->ID() ] = $registration->event();
318
+                    $insufficient_spaces_available[$registration->event()->ID()] = $registration->event();
319 319
                 }
320 320
                 continue;
321 321
             }
322 322
             // event requires admin approval
323 323
             if ($registration->status_ID() === EEM_Registration::status_id_not_approved) {
324 324
                 // add event to list of events with pre-approval reg status
325
-                $registrations_requiring_pre_approval[ $REG_ID ] = $registration;
325
+                $registrations_requiring_pre_approval[$REG_ID] = $registration;
326 326
                 do_action(
327 327
                     'AHEE__EE_SPCO_Reg_Step_Payment_Options__generate_reg_form__event_requires_pre_approval',
328 328
                     $registration->event(),
@@ -339,7 +339,7 @@  discard block
 block discarded – undo
339 339
                 )
340 340
             ) {
341 341
                 // add event to list of events that are sold out
342
-                $sold_out_events[ $registration->event()->ID() ] = $registration->event();
342
+                $sold_out_events[$registration->event()->ID()] = $registration->event();
343 343
                 do_action(
344 344
                     'AHEE__EE_SPCO_Reg_Step_Payment_Options__generate_reg_form__sold_out_event',
345 345
                     $registration->event(),
@@ -349,7 +349,7 @@  discard block
 block discarded – undo
349 349
             }
350 350
             // are they allowed to pay now and is there monies owing?
351 351
             if ($registration->owes_monies_and_can_pay()) {
352
-                $registrations_requiring_payment[ $REG_ID ] = $registration;
352
+                $registrations_requiring_payment[$REG_ID] = $registration;
353 353
                 do_action(
354 354
                     'AHEE__EE_SPCO_Reg_Step_Payment_Options__generate_reg_form__event_requires_payment',
355 355
                     $registration->event(),
@@ -360,28 +360,28 @@  discard block
 block discarded – undo
360 360
                       && $registration->status_ID() !== EEM_Registration::status_id_not_approved
361 361
                       && $registration->ticket()->is_free()
362 362
             ) {
363
-                $registrations_for_free_events[ $registration->ticket()->ID() ] = $registration;
363
+                $registrations_for_free_events[$registration->ticket()->ID()] = $registration;
364 364
             }
365 365
         }
366 366
         $subsections = [];
367 367
         // now decide which template to load
368
-        if (! empty($sold_out_events)) {
368
+        if ( ! empty($sold_out_events)) {
369 369
             $subsections['sold_out_events'] = $this->_sold_out_events($sold_out_events);
370 370
         }
371
-        if (! empty($insufficient_spaces_available)) {
371
+        if ( ! empty($insufficient_spaces_available)) {
372 372
             $subsections['insufficient_space'] = $this->_insufficient_spaces_available(
373 373
                 $insufficient_spaces_available
374 374
             );
375 375
         }
376
-        if (! empty($registrations_requiring_pre_approval)) {
376
+        if ( ! empty($registrations_requiring_pre_approval)) {
377 377
             $subsections['registrations_requiring_pre_approval'] = $this->_registrations_requiring_pre_approval(
378 378
                 $registrations_requiring_pre_approval
379 379
             );
380 380
         }
381
-        if (! empty($registrations_for_free_events)) {
381
+        if ( ! empty($registrations_for_free_events)) {
382 382
             $subsections['no_payment_required'] = $this->_no_payment_required($registrations_for_free_events);
383 383
         }
384
-        if (! empty($registrations_requiring_payment)) {
384
+        if ( ! empty($registrations_requiring_payment)) {
385 385
             if ($this->checkout->amount_owing > 0) {
386 386
                 // autoload Line_Item_Display classes
387 387
                 EEH_Autoloader::register_line_item_filter_autoloaders();
@@ -402,7 +402,7 @@  discard block
 block discarded – undo
402 402
                         ['registrations' => $registrations]
403 403
                     )
404 404
                 );
405
-                $this->checkout->amount_owing   = $filtered_line_item_tree->total();
405
+                $this->checkout->amount_owing = $filtered_line_item_tree->total();
406 406
                 $this->_apply_registration_payments_to_amount_owing($registrations);
407 407
             }
408 408
             $no_payment_required = false;
@@ -446,13 +446,13 @@  discard block
 block discarded – undo
446 446
      */
447 447
     public static function add_spco_line_item_filters(EE_Line_Item_Filter_Collection $line_item_filter_collection)
448 448
     {
449
-        if (! EE_Registry::instance()->SSN instanceof EE_Session) {
449
+        if ( ! EE_Registry::instance()->SSN instanceof EE_Session) {
450 450
             return $line_item_filter_collection;
451 451
         }
452
-        if (! EE_Registry::instance()->SSN->checkout() instanceof EE_Checkout) {
452
+        if ( ! EE_Registry::instance()->SSN->checkout() instanceof EE_Checkout) {
453 453
             return $line_item_filter_collection;
454 454
         }
455
-        if (! EE_Registry::instance()->SSN->checkout()->transaction instanceof EE_Transaction) {
455
+        if ( ! EE_Registry::instance()->SSN->checkout()->transaction instanceof EE_Transaction) {
456 456
             return $line_item_filter_collection;
457 457
         }
458 458
         $line_item_filter_collection->add(
@@ -492,8 +492,8 @@  discard block
 block discarded – undo
492 492
         );
493 493
         foreach ($registrations as $REG_ID => $registration) {
494 494
             // has this registration lost it's space ?
495
-            if (isset($ejected_registrations[ $REG_ID ])) {
496
-                unset($registrations[ $REG_ID ]);
495
+            if (isset($ejected_registrations[$REG_ID])) {
496
+                unset($registrations[$REG_ID]);
497 497
             }
498 498
         }
499 499
         return $registrations;
@@ -543,25 +543,25 @@  discard block
 block discarded – undo
543 543
             }
544 544
             $EVT_ID = $registration->event_ID();
545 545
             $ticket = $registration->ticket();
546
-            if (! isset($tickets_remaining[ $ticket->ID() ])) {
547
-                $tickets_remaining[ $ticket->ID() ] = $ticket->remaining();
546
+            if ( ! isset($tickets_remaining[$ticket->ID()])) {
547
+                $tickets_remaining[$ticket->ID()] = $ticket->remaining();
548 548
             }
549
-            if ($tickets_remaining[ $ticket->ID() ] > 0) {
550
-                if (! isset($event_reg_count[ $EVT_ID ])) {
551
-                    $event_reg_count[ $EVT_ID ] = 0;
549
+            if ($tickets_remaining[$ticket->ID()] > 0) {
550
+                if ( ! isset($event_reg_count[$EVT_ID])) {
551
+                    $event_reg_count[$EVT_ID] = 0;
552 552
                 }
553
-                $event_reg_count[ $EVT_ID ]++;
554
-                if (! isset($event_spaces_remaining[ $EVT_ID ])) {
555
-                    $event_spaces_remaining[ $EVT_ID ] = $registration->event()->spaces_remaining_for_sale();
553
+                $event_reg_count[$EVT_ID]++;
554
+                if ( ! isset($event_spaces_remaining[$EVT_ID])) {
555
+                    $event_spaces_remaining[$EVT_ID] = $registration->event()->spaces_remaining_for_sale();
556 556
                 }
557 557
             }
558 558
             if (
559 559
                 $revisit
560
-                && ($tickets_remaining[ $ticket->ID() ] === 0
561
-                    || $event_reg_count[ $EVT_ID ] > $event_spaces_remaining[ $EVT_ID ]
560
+                && ($tickets_remaining[$ticket->ID()] === 0
561
+                    || $event_reg_count[$EVT_ID] > $event_spaces_remaining[$EVT_ID]
562 562
                 )
563 563
             ) {
564
-                $ejected_registrations[ $REG_ID ] = $registration->event();
564
+                $ejected_registrations[$REG_ID] = $registration->event();
565 565
                 if ($registration->status_ID() !== EEM_Registration::status_id_wait_list) {
566 566
                     /** @type EE_Registration_Processor $registration_processor */
567 567
                     $registration_processor = EE_Registry::instance()->load_class('Registration_Processor');
@@ -621,7 +621,7 @@  discard block
 block discarded – undo
621 621
         foreach ($sold_out_events_array as $sold_out_event) {
622 622
             $sold_out_events .= EEH_HTML::li(
623 623
                 EEH_HTML::span(
624
-                    '  ' . $sold_out_event->name(),
624
+                    '  '.$sold_out_event->name(),
625 625
                     '',
626 626
                     'dashicons dashicons-marker ee-icon-size-16 pink-text'
627 627
                 )
@@ -677,7 +677,7 @@  discard block
 block discarded – undo
677 677
         foreach ($insufficient_spaces_events_array as $event) {
678 678
             if ($event instanceof EE_Event) {
679 679
                 $insufficient_space_events .= EEH_HTML::li(
680
-                    EEH_HTML::span(' ' . $event->name(), '', 'dashicons dashicons-marker ee-icon-size-16 pink-text')
680
+                    EEH_HTML::span(' '.$event->name(), '', 'dashicons dashicons-marker ee-icon-size-16 pink-text')
681 681
                 );
682 682
             }
683 683
         }
@@ -726,7 +726,7 @@  discard block
 block discarded – undo
726 726
         $events_requiring_pre_approval = [];
727 727
         foreach ($registrations_requiring_pre_approval as $registration) {
728 728
             if ($registration instanceof EE_Registration && $registration->event() instanceof EE_Event) {
729
-                $events_requiring_pre_approval[ $registration->event()->ID() ] = EEH_HTML::li(
729
+                $events_requiring_pre_approval[$registration->event()->ID()] = EEH_HTML::li(
730 730
                     EEH_HTML::span(
731 731
                         '',
732 732
                         '',
@@ -866,7 +866,7 @@  discard block
 block discarded – undo
866 866
     {
867 867
         return new EE_Form_Section_Proper(
868 868
             [
869
-                'html_id'         => 'ee-' . $this->slug() . '-extra-hidden-inputs',
869
+                'html_id'         => 'ee-'.$this->slug().'-extra-hidden-inputs',
870 870
                 'layout_strategy' => new EE_Div_Per_Section_Layout(),
871 871
                 'subsections'     => [
872 872
                     'spco_no_payment_required' => new EE_Hidden_Input(
@@ -905,7 +905,7 @@  discard block
 block discarded – undo
905 905
                 $payments += $registration->registration_payments();
906 906
             }
907 907
         }
908
-        if (! empty($payments)) {
908
+        if ( ! empty($payments)) {
909 909
             foreach ($payments as $payment) {
910 910
                 if ($payment instanceof EE_Registration_Payment) {
911 911
                     $this->checkout->amount_owing -= $payment->amount();
@@ -994,7 +994,7 @@  discard block
 block discarded – undo
994 994
             );
995 995
         }
996 996
         // switch up header depending on number of available payment methods
997
-        $payment_method_header     = count($this->checkout->available_payment_methods) > 1
997
+        $payment_method_header = count($this->checkout->available_payment_methods) > 1
998 998
             ? apply_filters(
999 999
                 'FHEE__registration_page_payment_options__method_of_payment_hdr',
1000 1000
                 esc_html__('Please Select Your Method of Payment', 'event_espresso')
@@ -1028,7 +1028,7 @@  discard block
 block discarded – undo
1028 1028
                 $payment_method_button = EEH_HTML::img(
1029 1029
                     $payment_method->button_url(),
1030 1030
                     $payment_method->name(),
1031
-                    'spco-payment-method-' . $payment_method->slug() . '-btn-img',
1031
+                    'spco-payment-method-'.$payment_method->slug().'-btn-img',
1032 1032
                     'spco-payment-method-btn-img'
1033 1033
                 );
1034 1034
                 // check if any payment methods are set as default
@@ -1036,15 +1036,15 @@  discard block
 block discarded – undo
1036 1036
                 // open_by_default
1037 1037
                 if (
1038 1038
                     ($this->checkout->selected_method_of_payment === $payment_method->slug())
1039
-                    || (! $this->checkout->selected_method_of_payment && $payment_method->open_by_default())
1039
+                    || ( ! $this->checkout->selected_method_of_payment && $payment_method->open_by_default())
1040 1040
                 ) {
1041 1041
                     $this->checkout->selected_method_of_payment = $payment_method->slug();
1042 1042
                     $this->_save_selected_method_of_payment();
1043
-                    $default_payment_method_option[ $payment_method->slug() ] = $payment_method_button;
1043
+                    $default_payment_method_option[$payment_method->slug()] = $payment_method_button;
1044 1044
                 } else {
1045
-                    $available_payment_method_options[ $payment_method->slug() ] = $payment_method_button;
1045
+                    $available_payment_method_options[$payment_method->slug()] = $payment_method_button;
1046 1046
                 }
1047
-                $payment_methods_billing_info[ $payment_method->slug() . '-info' ] =
1047
+                $payment_methods_billing_info[$payment_method->slug().'-info'] =
1048 1048
                     $this->_payment_method_billing_info(
1049 1049
                         $payment_method
1050 1050
                     );
@@ -1057,7 +1057,7 @@  discard block
 block discarded – undo
1057 1057
         $available_payment_methods['available_payment_methods'] = $this->_available_payment_method_inputs(
1058 1058
             $available_payment_method_options
1059 1059
         );
1060
-        $available_payment_methods                              += $payment_methods_billing_info;
1060
+        $available_payment_methods += $payment_methods_billing_info;
1061 1061
         // build the available payment methods form
1062 1062
         return new EE_Form_Section_Proper(
1063 1063
             [
@@ -1080,7 +1080,7 @@  discard block
 block discarded – undo
1080 1080
      */
1081 1081
     protected function _get_available_payment_methods()
1082 1082
     {
1083
-        if (! empty($this->checkout->available_payment_methods)) {
1083
+        if ( ! empty($this->checkout->available_payment_methods)) {
1084 1084
             return $this->checkout->available_payment_methods;
1085 1085
         }
1086 1086
         $available_payment_methods = [];
@@ -1092,7 +1092,7 @@  discard block
 block discarded – undo
1092 1092
         );
1093 1093
         foreach ($payment_methods as $payment_method) {
1094 1094
             if ($payment_method instanceof EE_Payment_Method) {
1095
-                $available_payment_methods[ $payment_method->slug() ] = $payment_method;
1095
+                $available_payment_methods[$payment_method->slug()] = $payment_method;
1096 1096
             }
1097 1097
         }
1098 1098
         return $available_payment_methods;
@@ -1187,7 +1187,7 @@  discard block
 block discarded – undo
1187 1187
         );
1188 1188
         return new EE_Form_Section_Proper(
1189 1189
             [
1190
-                'html_id'         => 'spco-payment-method-info-' . $payment_method->slug(),
1190
+                'html_id'         => 'spco-payment-method-info-'.$payment_method->slug(),
1191 1191
                 'html_class'      => 'spco-payment-method-info-dv',
1192 1192
                 // only display the selected or default PM
1193 1193
                 'html_style'      => $currently_selected ? '' : 'display:none;',
@@ -1216,7 +1216,7 @@  discard block
 block discarded – undo
1216 1216
         // how have they chosen to pay?
1217 1217
         $this->checkout->selected_method_of_payment = $this->_get_selected_method_of_payment(true);
1218 1218
         $this->checkout->payment_method             = $this->_get_payment_method_for_selected_method_of_payment();
1219
-        if (! $this->checkout->payment_method instanceof EE_Payment_Method) {
1219
+        if ( ! $this->checkout->payment_method instanceof EE_Payment_Method) {
1220 1220
             return false;
1221 1221
         }
1222 1222
         if (
@@ -1391,7 +1391,7 @@  discard block
 block discarded – undo
1391 1391
      */
1392 1392
     public function switch_payment_method()
1393 1393
     {
1394
-        if (! $this->_verify_payment_method_is_set()) {
1394
+        if ( ! $this->_verify_payment_method_is_set()) {
1395 1395
             return false;
1396 1396
         }
1397 1397
         if (
@@ -1527,7 +1527,7 @@  discard block
 block discarded – undo
1527 1527
             }
1528 1528
         }
1529 1529
         // verify payment method
1530
-        if (! $this->checkout->payment_method instanceof EE_Payment_Method) {
1530
+        if ( ! $this->checkout->payment_method instanceof EE_Payment_Method) {
1531 1531
             // get payment method for selected method of payment
1532 1532
             $this->checkout->payment_method = $this->_get_payment_method_for_selected_method_of_payment();
1533 1533
         }
@@ -1552,7 +1552,7 @@  discard block
 block discarded – undo
1552 1552
      */
1553 1553
     public function save_payer_details_via_ajax()
1554 1554
     {
1555
-        if (! $this->_verify_payment_method_is_set()) {
1555
+        if ( ! $this->_verify_payment_method_is_set()) {
1556 1556
             return;
1557 1557
         }
1558 1558
         // generate billing form for selected method of payment if it hasn't been done already
@@ -1562,7 +1562,7 @@  discard block
 block discarded – undo
1562 1562
             );
1563 1563
         }
1564 1564
         // generate primary attendee from payer info if applicable
1565
-        if (! $this->checkout->transaction_has_primary_registrant()) {
1565
+        if ( ! $this->checkout->transaction_has_primary_registrant()) {
1566 1566
             $attendee = $this->_create_attendee_from_request_data();
1567 1567
             if ($attendee instanceof EE_Attendee) {
1568 1568
                 foreach ($this->checkout->transaction->registrations() as $registration) {
@@ -1593,7 +1593,7 @@  discard block
 block discarded – undo
1593 1593
     {
1594 1594
         // get State ID
1595 1595
         $STA_ID = $this->request->getRequestParam('state');
1596
-        if (! empty($STA_ID)) {
1596
+        if ( ! empty($STA_ID)) {
1597 1597
             // can we get state object from name ?
1598 1598
             EE_Registry::instance()->load_model('State');
1599 1599
             $state  = EEM_State::instance()->get_col([['STA_name' => $STA_ID], 'limit' => 1], 'STA_ID');
@@ -1601,7 +1601,7 @@  discard block
 block discarded – undo
1601 1601
         }
1602 1602
         // get Country ISO
1603 1603
         $CNT_ISO = $this->request->getRequestParam('country');
1604
-        if (! empty($CNT_ISO)) {
1604
+        if ( ! empty($CNT_ISO)) {
1605 1605
             // can we get country object from name ?
1606 1606
             EE_Registry::instance()->load_model('Country');
1607 1607
             $country = EEM_Country::instance()->get_col(
@@ -1782,7 +1782,7 @@  discard block
 block discarded – undo
1782 1782
     protected function _maybe_set_completed(EE_Payment $payment)
1783 1783
     {
1784 1784
         // Do we need to redirect them? If so, there's more work to be done.
1785
-        if (! $payment->redirect_url()) {
1785
+        if ( ! $payment->redirect_url()) {
1786 1786
             $this->set_completed();
1787 1787
         }
1788 1788
     }
@@ -1839,7 +1839,7 @@  discard block
 block discarded – undo
1839 1839
     private function _process_payment()
1840 1840
     {
1841 1841
         // basically confirm that the event hasn't sold out since they hit the page
1842
-        if (! $this->_last_second_ticket_verifications()) {
1842
+        if ( ! $this->_last_second_ticket_verifications()) {
1843 1843
             return null;
1844 1844
         }
1845 1845
         // ya gotta make a choice man
@@ -1850,7 +1850,7 @@  discard block
 block discarded – undo
1850 1850
             return null;
1851 1851
         }
1852 1852
         // get EE_Payment_Method object
1853
-        if (! $this->checkout->payment_method = $this->_get_payment_method_for_selected_method_of_payment()) {
1853
+        if ( ! $this->checkout->payment_method = $this->_get_payment_method_for_selected_method_of_payment()) {
1854 1854
             return null;
1855 1855
         }
1856 1856
         // setup billing form
@@ -1859,12 +1859,12 @@  discard block
 block discarded – undo
1859 1859
                 $this->checkout->payment_method
1860 1860
             );
1861 1861
             // bad billing form ?
1862
-            if (! $this->_billing_form_is_valid()) {
1862
+            if ( ! $this->_billing_form_is_valid()) {
1863 1863
                 return null;
1864 1864
             }
1865 1865
         }
1866 1866
         // ensure primary registrant has been fully processed
1867
-        if (! $this->_setup_primary_registrant_prior_to_payment()) {
1867
+        if ( ! $this->_setup_primary_registrant_prior_to_payment()) {
1868 1868
             return null;
1869 1869
         }
1870 1870
         // if session is close to expiring (under 10 minutes by default)
@@ -1920,7 +1920,7 @@  discard block
 block discarded – undo
1920 1920
     protected function _last_second_ticket_verifications()
1921 1921
     {
1922 1922
         // don't bother re-validating if not a return visit
1923
-        if (! $this->checkout->revisit) {
1923
+        if ( ! $this->checkout->revisit) {
1924 1924
             return true;
1925 1925
         }
1926 1926
         $registrations = $this->checkout->transaction->registrations();
@@ -1970,7 +1970,7 @@  discard block
 block discarded – undo
1970 1970
             $this->_get_payment_method_for_selected_method_of_payment()
1971 1971
         );
1972 1972
         $html                        = $payment_method_billing_info->get_html();
1973
-        $html                        .= $this->checkout->redirect_form;
1973
+        $html .= $this->checkout->redirect_form;
1974 1974
         /** @var ResponseInterface $response */
1975 1975
         $response = LoaderFactory::getLoader()->getShared(ResponseInterface::class);
1976 1976
         $response->addOutput($html);
@@ -1986,7 +1986,7 @@  discard block
 block discarded – undo
1986 1986
      */
1987 1987
     private function _billing_form_is_valid()
1988 1988
     {
1989
-        if (! $this->checkout->payment_method->type_obj()->has_billing_form()) {
1989
+        if ( ! $this->checkout->payment_method->type_obj()->has_billing_form()) {
1990 1990
             return true;
1991 1991
         }
1992 1992
         if ($this->checkout->billing_form instanceof EE_Billing_Info_Form) {
@@ -2104,7 +2104,7 @@  discard block
 block discarded – undo
2104 2104
     {
2105 2105
         // convert billing form data into an attendee
2106 2106
         $this->checkout->primary_attendee_obj = $this->checkout->billing_form->create_attendee_from_billing_form_data();
2107
-        if (! $this->checkout->primary_attendee_obj instanceof EE_Attendee) {
2107
+        if ( ! $this->checkout->primary_attendee_obj instanceof EE_Attendee) {
2108 2108
             EE_Error::add_error(
2109 2109
                 sprintf(
2110 2110
                     esc_html__(
@@ -2121,7 +2121,7 @@  discard block
 block discarded – undo
2121 2121
             return false;
2122 2122
         }
2123 2123
         $primary_registration = $this->checkout->transaction->primary_registration();
2124
-        if (! $primary_registration instanceof EE_Registration) {
2124
+        if ( ! $primary_registration instanceof EE_Registration) {
2125 2125
             EE_Error::add_error(
2126 2126
                 sprintf(
2127 2127
                     esc_html__(
@@ -2183,8 +2183,8 @@  discard block
 block discarded – undo
2183 2183
             return null;
2184 2184
         }
2185 2185
         // get EE_Payment_Method object
2186
-        if (isset($this->checkout->available_payment_methods[ $this->checkout->selected_method_of_payment ])) {
2187
-            $payment_method = $this->checkout->available_payment_methods[ $this->checkout->selected_method_of_payment ];
2186
+        if (isset($this->checkout->available_payment_methods[$this->checkout->selected_method_of_payment])) {
2187
+            $payment_method = $this->checkout->available_payment_methods[$this->checkout->selected_method_of_payment];
2188 2188
         } else {
2189 2189
             // load EEM_Payment_Method
2190 2190
             EE_Registry::instance()->load_model('Payment_Method');
@@ -2192,7 +2192,7 @@  discard block
 block discarded – undo
2192 2192
             $payment_method     = $EEM_Payment_Method->get_one_by_slug($this->checkout->selected_method_of_payment);
2193 2193
         }
2194 2194
         // verify $payment_method
2195
-        if (! $payment_method instanceof EE_Payment_Method) {
2195
+        if ( ! $payment_method instanceof EE_Payment_Method) {
2196 2196
             // not a payment
2197 2197
             EE_Error::add_error(
2198 2198
                 sprintf(
@@ -2210,7 +2210,7 @@  discard block
 block discarded – undo
2210 2210
             return null;
2211 2211
         }
2212 2212
         // and verify it has a valid Payment_Method Type object
2213
-        if (! $payment_method->type_obj() instanceof EE_PMT_Base) {
2213
+        if ( ! $payment_method->type_obj() instanceof EE_PMT_Base) {
2214 2214
             // not a payment
2215 2215
             EE_Error::add_error(
2216 2216
                 sprintf(
@@ -2248,7 +2248,7 @@  discard block
 block discarded – undo
2248 2248
         $payment = null;
2249 2249
         $this->checkout->transaction->save();
2250 2250
         $payment_processor = EE_Registry::instance()->load_core('Payment_Processor');
2251
-        if (! $payment_processor instanceof EE_Payment_Processor) {
2251
+        if ( ! $payment_processor instanceof EE_Payment_Processor) {
2252 2252
             return false;
2253 2253
         }
2254 2254
         try {
@@ -2350,7 +2350,7 @@  discard block
 block discarded – undo
2350 2350
             return true;
2351 2351
         }
2352 2352
         // verify payment object
2353
-        if (! $payment instanceof EE_Payment) {
2353
+        if ( ! $payment instanceof EE_Payment) {
2354 2354
             // not a payment
2355 2355
             EE_Error::add_error(
2356 2356
                 sprintf(
@@ -2400,7 +2400,7 @@  discard block
 block discarded – undo
2400 2400
                 // and lastly, let's bump the payment status to pending
2401 2401
                 $payment->set_status(EEM_Payment::status_id_pending);
2402 2402
                 $payment->save();
2403
-            } elseif (! $this->_process_payment_status($payment, EE_PMT_Base::onsite)) {
2403
+            } elseif ( ! $this->_process_payment_status($payment, EE_PMT_Base::onsite)) {
2404 2404
                 // User shouldn't be redirected. So let's process it here.
2405 2405
                 // $this->_setup_redirect_for_next_step();
2406 2406
                 $this->checkout->continue_reg = false;
@@ -2481,7 +2481,7 @@  discard block
 block discarded – undo
2481 2481
                     return false;
2482 2482
                 // bad payment
2483 2483
                 case EEM_Payment::status_id_failed:
2484
-                    if (! empty($msg)) {
2484
+                    if ( ! empty($msg)) {
2485 2485
                         EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
2486 2486
                         return false;
2487 2487
                     }
@@ -2539,18 +2539,18 @@  discard block
 block discarded – undo
2539 2539
         // how have they chosen to pay?
2540 2540
         $this->checkout->selected_method_of_payment = $this->_get_selected_method_of_payment(true);
2541 2541
         // get EE_Payment_Method object
2542
-        if (! $this->checkout->payment_method = $this->_get_payment_method_for_selected_method_of_payment()) {
2542
+        if ( ! $this->checkout->payment_method = $this->_get_payment_method_for_selected_method_of_payment()) {
2543 2543
             $this->checkout->continue_reg = false;
2544 2544
             return false;
2545 2545
         }
2546
-        if (! $this->checkout->payment_method->is_off_site()) {
2546
+        if ( ! $this->checkout->payment_method->is_off_site()) {
2547 2547
             return false;
2548 2548
         }
2549 2549
         $this->_validate_offsite_return();
2550 2550
         // verify TXN
2551 2551
         if ($this->checkout->transaction instanceof EE_Transaction) {
2552 2552
             $gateway = $this->checkout->payment_method->type_obj()->get_gateway();
2553
-            if (! $gateway instanceof EE_Offsite_Gateway) {
2553
+            if ( ! $gateway instanceof EE_Offsite_Gateway) {
2554 2554
                 $this->checkout->continue_reg = false;
2555 2555
                 return false;
2556 2556
             }
@@ -2564,7 +2564,7 @@  discard block
 block discarded – undo
2564 2564
                 $this->checkout->payment = $payment;
2565 2565
                 // mark this reg step as completed, as long as gateway doesn't use a separate IPN request,
2566 2566
                 // because we will complete this step during the IPN processing then
2567
-                if (! $this->handle_IPN_in_this_request()) {
2567
+                if ( ! $this->handle_IPN_in_this_request()) {
2568 2568
                     $this->set_completed();
2569 2569
                 }
2570 2570
                 return true;
@@ -2667,13 +2667,13 @@  discard block
 block discarded – undo
2667 2667
      */
2668 2668
     private function _redirect_wayward_request(EE_Registration $primary_registrant)
2669 2669
     {
2670
-        if (! $primary_registrant instanceof EE_Registration) {
2670
+        if ( ! $primary_registrant instanceof EE_Registration) {
2671 2671
             // try redirecting based on the current TXN
2672 2672
             $primary_registrant = $this->checkout->transaction instanceof EE_Transaction
2673 2673
                 ? $this->checkout->transaction->primary_registration()
2674 2674
                 : null;
2675 2675
         }
2676
-        if (! $primary_registrant instanceof EE_Registration) {
2676
+        if ( ! $primary_registrant instanceof EE_Registration) {
2677 2677
             EE_Error::add_error(
2678 2678
                 sprintf(
2679 2679
                     esc_html__(
@@ -2744,7 +2744,7 @@  discard block
 block discarded – undo
2744 2744
             $payment = $this->checkout->transaction->last_payment();
2745 2745
             // $payment_source = 'last_payment after Exception';
2746 2746
             // but if we STILL don't have a payment object
2747
-            if (! $payment instanceof EE_Payment) {
2747
+            if ( ! $payment instanceof EE_Payment) {
2748 2748
                 // then we'll object ! ( not object like a thing... but object like what a lawyer says ! )
2749 2749
                 $this->_handle_payment_processor_exception($e);
2750 2750
             }
Please login to merge, or discard this patch.
admin_pages/venues/Venues_Admin_Page.core.php 1 patch
Indentation   +1555 added lines, -1555 removed lines patch added patch discarded remove patch
@@ -13,1565 +13,1565 @@
 block discarded – undo
13 13
  */
14 14
 class Venues_Admin_Page extends EE_Admin_Page_CPT
15 15
 {
16
-    /**
17
-     * _venue
18
-     * This will hold the venue object for venue_details screen.
19
-     *
20
-     * @access protected
21
-     * @var object
22
-     */
23
-    protected $_venue;
24
-
25
-
26
-    /**
27
-     * This will hold the category object for category_details screen.
28
-     *
29
-     * @var object
30
-     */
31
-    protected $_category;
32
-
33
-
34
-    /**
35
-     * This property will hold the venue model instance
36
-     *
37
-     * @var object
38
-     */
39
-    protected $_venue_model;
40
-
41
-
42
-    /**
43
-     * @throws EE_Error
44
-     */
45
-    protected function _init_page_props()
46
-    {
47
-        require_once(EE_MODELS . 'EEM_Venue.model.php');
48
-        $this->page_slug        = EE_VENUES_PG_SLUG;
49
-        $this->_admin_base_url  = EE_VENUES_ADMIN_URL;
50
-        $this->_admin_base_path = EE_ADMIN_PAGES . 'venues';
51
-        $this->page_label       = esc_html__('Event Venues', 'event_espresso');
52
-        $this->_cpt_model_names = [
53
-            'create_new' => 'EEM_Venue',
54
-            'edit'       => 'EEM_Venue',
55
-        ];
56
-        $this->_cpt_edit_routes = [
57
-            'espresso_venues' => 'edit',
58
-        ];
59
-        $this->_venue_model     = EEM_Venue::instance();
60
-    }
61
-
62
-
63
-    protected function _ajax_hooks()
64
-    {
65
-        // todo: all hooks for ee_venues ajax goes in here.
66
-    }
67
-
68
-
69
-    protected function _define_page_props()
70
-    {
71
-        $this->_admin_page_title = $this->page_label;
72
-        $this->_labels           = [
73
-            'buttons'      => [
74
-                'add'             => esc_html__('Add New Venue', 'event_espresso'),
75
-                'edit'            => esc_html__('Edit Venue', 'event_espresso'),
76
-                'delete'          => esc_html__('Delete Venue', 'event_espresso'),
77
-                'add_category'    => esc_html__('Add New Category', 'event_espresso'),
78
-                'edit_category'   => esc_html__('Edit Category', 'event_espresso'),
79
-                'delete_category' => esc_html__('Delete Category', 'event_espresso'),
80
-            ],
81
-            'editor_title' => [
82
-                'espresso_venues' => esc_html__('Enter Venue name here', 'event_espresso'),
83
-            ],
84
-            'publishbox'   => [
85
-                'create_new'          => esc_html__('Save New Venue', 'event_espresso'),
86
-                'edit'                => esc_html__('Update Venue', 'event_espresso'),
87
-                'add_category'        => esc_html__('Save New Category', 'event_espresso'),
88
-                'edit_category'       => esc_html__('Update Category', 'event_espresso'),
89
-                'google_map_settings' => esc_html__('Update Settings', 'event_espresso'),
90
-            ],
91
-        ];
92
-    }
93
-
94
-
95
-    protected function _set_page_routes()
96
-    {
97
-
98
-        // load formatter helper
99
-        // load field generator helper
100
-
101
-        // is there a vnu_id in the request?
102
-        $VNU_ID = $this->request->getRequestParam('VNU_ID', 0, 'int');
103
-        $VNU_ID = $this->request->getRequestParam('post', $VNU_ID, 'int');
104
-
105
-        $this->_page_routes = [
106
-            'default'                    => [
107
-                'func'       => [$this, '_overview_list_table'],
108
-                'capability' => 'ee_read_venues',
109
-            ],
110
-            'create_new'                 => [
111
-                'func'       => [$this, '_create_new_cpt_item'],
112
-                'capability' => 'ee_edit_venues',
113
-            ],
114
-            'edit'                       => [
115
-                'func'       => [$this, '_edit_cpt_item'],
116
-                'capability' => 'ee_edit_venue',
117
-                'obj_id'     => $VNU_ID,
118
-            ],
119
-            'trash_venue'                => [
120
-                'func'       => [$this, '_trash_or_restore_venue'],
121
-                'args'       => ['venue_status' => 'trash'],
122
-                'noheader'   => true,
123
-                'capability' => 'ee_delete_venue',
124
-                'obj_id'     => $VNU_ID,
125
-            ],
126
-            'trash_venues'               => [
127
-                'func'       => [$this, '_trash_or_restore_venues'],
128
-                'args'       => ['venue_status' => 'trash'],
129
-                'noheader'   => true,
130
-                'capability' => 'ee_delete_venues',
131
-            ],
132
-            'restore_venue'              => [
133
-                'func'       => [$this, '_trash_or_restore_venue'],
134
-                'args'       => ['venue_status' => 'draft'],
135
-                'noheader'   => true,
136
-                'capability' => 'ee_delete_venue',
137
-                'obj_id'     => $VNU_ID,
138
-            ],
139
-            'restore_venues'             => [
140
-                'func'       => [$this, '_trash_or_restore_venues'],
141
-                'args'       => ['venue_status' => 'draft'],
142
-                'noheader'   => true,
143
-                'capability' => 'ee_delete_venues',
144
-            ],
145
-            'delete_venues'              => [
146
-                'func'       => [$this, '_delete_venues'],
147
-                'noheader'   => true,
148
-                'capability' => 'ee_delete_venues',
149
-            ],
150
-            'delete_venue'               => [
151
-                'func'       => [$this, '_delete_venue'],
152
-                'noheader'   => true,
153
-                'capability' => 'ee_delete_venue',
154
-                'obj_id'     => $VNU_ID,
155
-            ],
156
-            // settings related
157
-            'google_map_settings'        => [
158
-                'func'       => [$this, '_google_map_settings'],
159
-                'capability' => 'manage_options',
160
-            ],
161
-            'update_google_map_settings' => [
162
-                'func'       => [$this, '_update_google_map_settings'],
163
-                'capability' => 'manage_options',
164
-                'noheader'   => true,
165
-            ],
166
-            // venue category tab related
167
-            'add_category'               => [
168
-                'func'       => [$this, '_category_details'],
169
-                'args'       => ['add'],
170
-                'capability' => 'ee_edit_venue_category',
171
-            ],
172
-            'edit_category'              => [
173
-                'func'       => [$this, '_category_details'],
174
-                'args'       => ['edit'],
175
-                'capability' => 'ee_edit_venue_category',
176
-            ],
177
-            'delete_categories'          => [
178
-                'func'       => [$this, '_delete_categories'],
179
-                'noheader'   => true,
180
-                'capability' => 'ee_delete_venue_category',
181
-            ],
182
-
183
-            'delete_category' => [
184
-                'func'       => [$this, '_delete_categories'],
185
-                'noheader'   => true,
186
-                'capability' => 'ee_delete_venue_category',
187
-            ],
188
-
189
-            'insert_category' => [
190
-                'func'       => [$this, '_insert_or_update_category'],
191
-                'args'       => ['new_category' => true],
192
-                'noheader'   => true,
193
-                'capability' => 'ee_edit_venue_category',
194
-            ],
195
-
196
-            'update_category'   => [
197
-                'func'       => [$this, '_insert_or_update_category'],
198
-                'args'       => ['new_category' => false],
199
-                'noheader'   => true,
200
-                'capability' => 'ee_edit_venue_category',
201
-            ],
202
-            'export_categories' => [
203
-                'func'       => [$this, '_categories_export'],
204
-                'noheader'   => true,
205
-                'capability' => 'export',
206
-            ],
207
-            'import_categories' => [
208
-                'func'       => [$this, '_import_categories'],
209
-                'capability' => 'import',
210
-            ],
211
-            'category_list'     => [
212
-                'func'       => [$this, '_category_list_table'],
213
-                'capability' => 'ee_manage_venue_categories',
214
-            ],
215
-        ];
216
-    }
217
-
218
-
219
-    protected function _set_page_config()
220
-    {
221
-        $VNU_ID     = $this->request->getRequestParam('post', 0, 'int');
222
-        $EVT_CAT_ID = $this->request->getRequestParam('EVT_CAT_ID', 0, 'int');
223
-
224
-        $this->_page_config = [
225
-            'default'             => [
226
-                'nav'           => [
227
-                    'label' => esc_html__('Overview', 'event_espresso'),
228
-                    'order' => 10,
229
-                ],
230
-                'list_table'    => 'Venues_Admin_List_Table',
231
-                'help_tabs'     => [
232
-                    'venues_overview_help_tab'                           => [
233
-                        'title'    => esc_html__('Venues Overview', 'event_espresso'),
234
-                        'filename' => 'venues_overview',
235
-                    ],
236
-                    'venues_overview_table_column_headings_help_tab'     => [
237
-                        'title'    => esc_html__('Venues Overview Table Column Headings', 'event_espresso'),
238
-                        'filename' => 'venues_overview_table_column_headings',
239
-                    ],
240
-                    'venues_overview_views_bulk_actions_search_help_tab' => [
241
-                        'title'    => esc_html__('Venues Overview Views & Bulk Actions & Search', 'event_espresso'),
242
-                        'filename' => 'venues_overview_views_bulk_actions_search',
243
-                    ],
244
-                ],
245
-                'metaboxes'     => ['_espresso_news_post_box', '_espresso_links_post_box'],
246
-                'require_nonce' => false,
247
-            ],
248
-            'create_new'          => [
249
-                'nav'           => [
250
-                    'label'      => esc_html__('Add Venue', 'event_espresso'),
251
-                    'order'      => 5,
252
-                    'persistent' => false,
253
-                ],
254
-                'help_tabs'     => [
255
-                    'venues_editor_help_tab'                                               => [
256
-                        'title'    => esc_html__('Venue Editor', 'event_espresso'),
257
-                        'filename' => 'venues_editor',
258
-                    ],
259
-                    'venues_editor_title_richtexteditor_help_tab'                          => [
260
-                        'title'    => esc_html__('Venue Title & Rich Text Editor', 'event_espresso'),
261
-                        'filename' => 'venues_editor_title_richtexteditor',
262
-                    ],
263
-                    'venues_editor_tags_categories_help_tab'                               => [
264
-                        'title'    => esc_html__('Venue Tags & Categories', 'event_espresso'),
265
-                        'filename' => 'venues_editor_tags_categories',
266
-                    ],
267
-                    'venues_editor_physical_location_google_map_virtual_location_help_tab' => [
268
-                        'title'    => esc_html__(
269
-                            'Venue Editor Physical Location & Google Map & Virtual Location',
270
-                            'event_espresso'
271
-                        ),
272
-                        'filename' => 'venues_editor_physical_location_google_map_virtual_location',
273
-                    ],
274
-                    'venues_editor_save_new_venue_help_tab'                                => [
275
-                        'title'    => esc_html__('Save New Venue', 'event_espresso'),
276
-                        'filename' => 'venues_editor_save_new_venue',
277
-                    ],
278
-                    'venues_editor_other_help_tab'                                         => [
279
-                        'title'    => esc_html__('Venue Editor Other', 'event_espresso'),
280
-                        'filename' => 'venues_editor_other',
281
-                    ],
282
-                ],
283
-                'metaboxes'     => ['_venue_editor_metaboxes'],
284
-                'require_nonce' => false,
285
-            ],
286
-            'edit'                => [
287
-                'nav'           => [
288
-                    'label'      => esc_html__('Edit Venue', 'event_espresso'),
289
-                    'order'      => 5,
290
-                    'persistent' => false,
291
-                    'url'        => $VNU_ID
292
-                        ? add_query_arg(['post' => $VNU_ID], $this->_current_page_view_url)
293
-                        : $this->_admin_base_url,
294
-                ],
295
-                'help_tabs'     => [
296
-                    'venues_editor_help_tab'                                               => [
297
-                        'title'    => esc_html__('Venue Editor', 'event_espresso'),
298
-                        'filename' => 'venues_editor',
299
-                    ],
300
-                    'venues_editor_title_richtexteditor_help_tab'                          => [
301
-                        'title'    => esc_html__('Venue Title & Rich Text Editor', 'event_espresso'),
302
-                        'filename' => 'venues_editor_title_richtexteditor',
303
-                    ],
304
-                    'venues_editor_tags_categories_help_tab'                               => [
305
-                        'title'    => esc_html__('Venue Tags & Categories', 'event_espresso'),
306
-                        'filename' => 'venues_editor_tags_categories',
307
-                    ],
308
-                    'venues_editor_physical_location_google_map_virtual_location_help_tab' => [
309
-                        'title'    => esc_html__(
310
-                            'Venue Editor Physical Location & Google Map & Virtual Location',
311
-                            'event_espresso'
312
-                        ),
313
-                        'filename' => 'venues_editor_physical_location_google_map_virtual_location',
314
-                    ],
315
-                    'venues_editor_save_new_venue_help_tab'                                => [
316
-                        'title'    => esc_html__('Save New Venue', 'event_espresso'),
317
-                        'filename' => 'venues_editor_save_new_venue',
318
-                    ],
319
-                    'venues_editor_other_help_tab'                                         => [
320
-                        'title'    => esc_html__('Venue Editor Other', 'event_espresso'),
321
-                        'filename' => 'venues_editor_other',
322
-                    ],
323
-                ],
324
-                'metaboxes'     => ['_venue_editor_metaboxes'],
325
-                'require_nonce' => false,
326
-            ],
327
-            'google_map_settings' => [
328
-                'nav'           => [
329
-                    'label' => esc_html__('Google Maps', 'event_espresso'),
330
-                    'order' => 40,
331
-                ],
332
-                'metaboxes'     => array_merge($this->_default_espresso_metaboxes, ['_publish_post_box']),
333
-                'help_tabs'     => [
334
-                    'general_settings_google_maps_help_tab' => [
335
-                        'title'    => esc_html__('Google Maps', 'event_espresso'),
336
-                        'filename' => 'general_settings_google_maps',
337
-                    ],
338
-                ],
339
-                'require_nonce' => false,
340
-            ],
341
-            // venue category stuff
342
-            'add_category'        => [
343
-                'nav'           => [
344
-                    'label'      => esc_html__('Add Category', 'event_espresso'),
345
-                    'order'      => 15,
346
-                    'persistent' => false,
347
-                ],
348
-                'metaboxes'     => ['_publish_post_box'],
349
-                'help_tabs'     => [
350
-                    'venues_add_category_help_tab' => [
351
-                        'title'    => esc_html__('Add New Venue Category', 'event_espresso'),
352
-                        'filename' => 'venues_add_category',
353
-                    ],
354
-                ],
355
-                'require_nonce' => false,
356
-            ],
357
-            'edit_category'       => [
358
-                'nav'           => [
359
-                    'label'      => esc_html__('Edit Category', 'event_espresso'),
360
-                    'order'      => 15,
361
-                    'persistent' => false,
362
-                    'url'        => $EVT_CAT_ID
363
-                        ? add_query_arg(['EVT_CAT_ID' => $EVT_CAT_ID], $this->_current_page_view_url)
364
-                        : $this->_admin_base_url,
365
-                ],
366
-                'metaboxes'     => ['_publish_post_box'],
367
-                'help_tabs'     => [
368
-                    'venues_edit_category_help_tab' => [
369
-                        'title'    => esc_html__('Edit Venue Category', 'event_espresso'),
370
-                        'filename' => 'venues_edit_category',
371
-                    ],
372
-                ],
373
-                'require_nonce' => false,
374
-            ],
375
-            'category_list'       => [
376
-                'nav'           => [
377
-                    'label' => esc_html__('Categories', 'event_espresso'),
378
-                    'order' => 20,
379
-                ],
380
-                'list_table'    => 'Venue_Categories_Admin_List_Table',
381
-                'help_tabs'     => [
382
-                    'venues_categories_help_tab'                       => [
383
-                        'title'    => esc_html__('Venue Categories', 'event_espresso'),
384
-                        'filename' => 'venues_categories',
385
-                    ],
386
-                    'venues_categories_table_column_headings_help_tab' => [
387
-                        'title'    => esc_html__('Venue Categories Table Column Headings', 'event_espresso'),
388
-                        'filename' => 'venues_categories_table_column_headings',
389
-                    ],
390
-                    'venues_categories_views_help_tab'                 => [
391
-                        'title'    => esc_html__('Venue Categories Views', 'event_espresso'),
392
-                        'filename' => 'venues_categories_views',
393
-                    ],
394
-                    'venues_categories_other_help_tab'                 => [
395
-                        'title'    => esc_html__('Venue Categories Other', 'event_espresso'),
396
-                        'filename' => 'venues_categories_other',
397
-                    ],
398
-                ],
399
-                'metaboxes'     => $this->_default_espresso_metaboxes,
400
-                'require_nonce' => false,
401
-            ],
402
-        ];
403
-    }
404
-
405
-
406
-    protected function _add_screen_options()
407
-    {
408
-        // todo
409
-    }
410
-
411
-
412
-    protected function _add_screen_options_default()
413
-    {
414
-        $this->_per_page_screen_option();
415
-    }
416
-
417
-
418
-    protected function _add_screen_options_category_list()
419
-    {
420
-        $page_title              = $this->_admin_page_title;
421
-        $this->_admin_page_title = esc_html__('Venue Categories', 'event_espresso');
422
-        $this->_per_page_screen_option();
423
-        $this->_admin_page_title = $page_title;
424
-    }
425
-
426
-
427
-    // none of the below group are currently used for Event Venues
428
-    protected function _add_feature_pointers()
429
-    {
430
-    }
431
-
432
-
433
-    public function admin_init()
434
-    {
435
-    }
436
-
437
-
438
-    public function admin_notices()
439
-    {
440
-    }
441
-
442
-
443
-    public function admin_footer_scripts()
444
-    {
445
-    }
446
-
447
-
448
-    public function load_scripts_styles_create_new()
449
-    {
450
-        $this->load_scripts_styles_edit();
451
-    }
452
-
453
-
454
-    public function load_scripts_styles()
455
-    {
456
-        wp_register_style('ee-cat-admin', EVENTS_ASSETS_URL . 'ee-cat-admin.css', [], EVENT_ESPRESSO_VERSION);
457
-        wp_enqueue_style('ee-cat-admin');
458
-    }
459
-
460
-
461
-    public function load_scripts_styles_add_category()
462
-    {
463
-        $this->load_scripts_styles_edit_category();
464
-    }
465
-
466
-
467
-    public function load_scripts_styles_edit_category()
468
-    {
469
-    }
470
-
471
-
472
-    public function load_scripts_styles_edit()
473
-    {
474
-        // styles
475
-        wp_enqueue_style('espresso-ui-theme');
476
-        wp_register_style(
477
-            'espresso_venues',
478
-            EE_VENUES_ASSETS_URL . 'ee-venues-admin.css',
479
-            [],
480
-            EVENT_ESPRESSO_VERSION
481
-        );
482
-        wp_enqueue_style('espresso_venues');
483
-    }
484
-
485
-
486
-    protected function _set_list_table_views_default()
487
-    {
488
-        $this->_views = [
489
-            'all' => [
490
-                'slug'        => 'all',
491
-                'label'       => esc_html__('View All Venues', 'event_espresso'),
492
-                'count'       => 0,
493
-                'bulk_action' => [],
494
-            ],
495
-        ];
496
-
497
-        if (EE_Registry::instance()->CAP->current_user_can('ee_delete_venues', 'espresso_venues_trash_venues')) {
498
-            $this->_views['all']['bulk_action'] = [
499
-                'trash_venues' => esc_html__('Move to Trash', 'event_espresso'),
500
-            ];
501
-            $this->_views['trash']              = [
502
-                'slug'        => 'trash',
503
-                'label'       => esc_html__('Trash', 'event_espresso'),
504
-                'count'       => 0,
505
-                'bulk_action' => [
506
-                    'restore_venues' => esc_html__('Restore from Trash', 'event_espresso'),
507
-                    'delete_venues'  => esc_html__('Delete', 'event_espresso'),
508
-                ],
509
-            ];
510
-        }
511
-    }
512
-
513
-
514
-    protected function _set_list_table_views_category_list()
515
-    {
516
-        $this->_views = [
517
-            'all' => [
518
-                'slug'        => 'all',
519
-                'label'       => esc_html__('All', 'event_espresso'),
520
-                'count'       => 0,
521
-                'bulk_action' => [
522
-                    'delete_categories' => esc_html__('Delete Permanently', 'event_espresso'),
523
-                ],
524
-            ],
525
-        ];
526
-    }
527
-
528
-
529
-    /**
530
-     * @throws EE_Error
531
-     */
532
-    protected function _overview_list_table()
533
-    {
534
-        do_action('AHEE_log', __FILE__, __FUNCTION__, '');
535
-        $this->_template_args['after_list_table'] = EEH_Template::get_button_or_link(
536
-            get_post_type_archive_link('espresso_venues'),
537
-            esc_html__("View Venue Archive Page", "event_espresso"),
538
-            'button'
539
-        );
540
-
541
-        $this->_admin_page_title .= ' ' . $this->get_action_link_or_button(
542
-            'create_new',
543
-            'add',
544
-            [],
545
-            'add-new-h2'
546
-        );
547
-
548
-        $this->_search_btn_label  = esc_html__('Venues', 'event_espresso');
549
-        $this->display_admin_list_table_page_with_sidebar();
550
-    }
551
-
552
-
553
-    /**
554
-     * @throws EE_Error
555
-     * @throws ReflectionException
556
-     */
557
-    public function extra_misc_actions_publish_box()
558
-    {
559
-        $extra_rows = [
560
-            'vnu_capacity' => $this->_cpt_model_obj->get_f('VNU_capacity'),
561
-            'vnu_url'      => $this->_cpt_model_obj->get_f('VNU_url'),
562
-            'vnu_phone'    => $this->_cpt_model_obj->get_f('VNU_phone'),
563
-        ];
564
-        $template   = EE_VENUES_TEMPLATE_PATH . 'venue_publish_box_extras.template.php';
565
-        EEH_Template::display_template($template, $extra_rows);
566
-    }
567
-
568
-
569
-    /*************        Google Maps        *************
16
+	/**
17
+	 * _venue
18
+	 * This will hold the venue object for venue_details screen.
19
+	 *
20
+	 * @access protected
21
+	 * @var object
22
+	 */
23
+	protected $_venue;
24
+
25
+
26
+	/**
27
+	 * This will hold the category object for category_details screen.
28
+	 *
29
+	 * @var object
30
+	 */
31
+	protected $_category;
32
+
33
+
34
+	/**
35
+	 * This property will hold the venue model instance
36
+	 *
37
+	 * @var object
38
+	 */
39
+	protected $_venue_model;
40
+
41
+
42
+	/**
43
+	 * @throws EE_Error
44
+	 */
45
+	protected function _init_page_props()
46
+	{
47
+		require_once(EE_MODELS . 'EEM_Venue.model.php');
48
+		$this->page_slug        = EE_VENUES_PG_SLUG;
49
+		$this->_admin_base_url  = EE_VENUES_ADMIN_URL;
50
+		$this->_admin_base_path = EE_ADMIN_PAGES . 'venues';
51
+		$this->page_label       = esc_html__('Event Venues', 'event_espresso');
52
+		$this->_cpt_model_names = [
53
+			'create_new' => 'EEM_Venue',
54
+			'edit'       => 'EEM_Venue',
55
+		];
56
+		$this->_cpt_edit_routes = [
57
+			'espresso_venues' => 'edit',
58
+		];
59
+		$this->_venue_model     = EEM_Venue::instance();
60
+	}
61
+
62
+
63
+	protected function _ajax_hooks()
64
+	{
65
+		// todo: all hooks for ee_venues ajax goes in here.
66
+	}
67
+
68
+
69
+	protected function _define_page_props()
70
+	{
71
+		$this->_admin_page_title = $this->page_label;
72
+		$this->_labels           = [
73
+			'buttons'      => [
74
+				'add'             => esc_html__('Add New Venue', 'event_espresso'),
75
+				'edit'            => esc_html__('Edit Venue', 'event_espresso'),
76
+				'delete'          => esc_html__('Delete Venue', 'event_espresso'),
77
+				'add_category'    => esc_html__('Add New Category', 'event_espresso'),
78
+				'edit_category'   => esc_html__('Edit Category', 'event_espresso'),
79
+				'delete_category' => esc_html__('Delete Category', 'event_espresso'),
80
+			],
81
+			'editor_title' => [
82
+				'espresso_venues' => esc_html__('Enter Venue name here', 'event_espresso'),
83
+			],
84
+			'publishbox'   => [
85
+				'create_new'          => esc_html__('Save New Venue', 'event_espresso'),
86
+				'edit'                => esc_html__('Update Venue', 'event_espresso'),
87
+				'add_category'        => esc_html__('Save New Category', 'event_espresso'),
88
+				'edit_category'       => esc_html__('Update Category', 'event_espresso'),
89
+				'google_map_settings' => esc_html__('Update Settings', 'event_espresso'),
90
+			],
91
+		];
92
+	}
93
+
94
+
95
+	protected function _set_page_routes()
96
+	{
97
+
98
+		// load formatter helper
99
+		// load field generator helper
100
+
101
+		// is there a vnu_id in the request?
102
+		$VNU_ID = $this->request->getRequestParam('VNU_ID', 0, 'int');
103
+		$VNU_ID = $this->request->getRequestParam('post', $VNU_ID, 'int');
104
+
105
+		$this->_page_routes = [
106
+			'default'                    => [
107
+				'func'       => [$this, '_overview_list_table'],
108
+				'capability' => 'ee_read_venues',
109
+			],
110
+			'create_new'                 => [
111
+				'func'       => [$this, '_create_new_cpt_item'],
112
+				'capability' => 'ee_edit_venues',
113
+			],
114
+			'edit'                       => [
115
+				'func'       => [$this, '_edit_cpt_item'],
116
+				'capability' => 'ee_edit_venue',
117
+				'obj_id'     => $VNU_ID,
118
+			],
119
+			'trash_venue'                => [
120
+				'func'       => [$this, '_trash_or_restore_venue'],
121
+				'args'       => ['venue_status' => 'trash'],
122
+				'noheader'   => true,
123
+				'capability' => 'ee_delete_venue',
124
+				'obj_id'     => $VNU_ID,
125
+			],
126
+			'trash_venues'               => [
127
+				'func'       => [$this, '_trash_or_restore_venues'],
128
+				'args'       => ['venue_status' => 'trash'],
129
+				'noheader'   => true,
130
+				'capability' => 'ee_delete_venues',
131
+			],
132
+			'restore_venue'              => [
133
+				'func'       => [$this, '_trash_or_restore_venue'],
134
+				'args'       => ['venue_status' => 'draft'],
135
+				'noheader'   => true,
136
+				'capability' => 'ee_delete_venue',
137
+				'obj_id'     => $VNU_ID,
138
+			],
139
+			'restore_venues'             => [
140
+				'func'       => [$this, '_trash_or_restore_venues'],
141
+				'args'       => ['venue_status' => 'draft'],
142
+				'noheader'   => true,
143
+				'capability' => 'ee_delete_venues',
144
+			],
145
+			'delete_venues'              => [
146
+				'func'       => [$this, '_delete_venues'],
147
+				'noheader'   => true,
148
+				'capability' => 'ee_delete_venues',
149
+			],
150
+			'delete_venue'               => [
151
+				'func'       => [$this, '_delete_venue'],
152
+				'noheader'   => true,
153
+				'capability' => 'ee_delete_venue',
154
+				'obj_id'     => $VNU_ID,
155
+			],
156
+			// settings related
157
+			'google_map_settings'        => [
158
+				'func'       => [$this, '_google_map_settings'],
159
+				'capability' => 'manage_options',
160
+			],
161
+			'update_google_map_settings' => [
162
+				'func'       => [$this, '_update_google_map_settings'],
163
+				'capability' => 'manage_options',
164
+				'noheader'   => true,
165
+			],
166
+			// venue category tab related
167
+			'add_category'               => [
168
+				'func'       => [$this, '_category_details'],
169
+				'args'       => ['add'],
170
+				'capability' => 'ee_edit_venue_category',
171
+			],
172
+			'edit_category'              => [
173
+				'func'       => [$this, '_category_details'],
174
+				'args'       => ['edit'],
175
+				'capability' => 'ee_edit_venue_category',
176
+			],
177
+			'delete_categories'          => [
178
+				'func'       => [$this, '_delete_categories'],
179
+				'noheader'   => true,
180
+				'capability' => 'ee_delete_venue_category',
181
+			],
182
+
183
+			'delete_category' => [
184
+				'func'       => [$this, '_delete_categories'],
185
+				'noheader'   => true,
186
+				'capability' => 'ee_delete_venue_category',
187
+			],
188
+
189
+			'insert_category' => [
190
+				'func'       => [$this, '_insert_or_update_category'],
191
+				'args'       => ['new_category' => true],
192
+				'noheader'   => true,
193
+				'capability' => 'ee_edit_venue_category',
194
+			],
195
+
196
+			'update_category'   => [
197
+				'func'       => [$this, '_insert_or_update_category'],
198
+				'args'       => ['new_category' => false],
199
+				'noheader'   => true,
200
+				'capability' => 'ee_edit_venue_category',
201
+			],
202
+			'export_categories' => [
203
+				'func'       => [$this, '_categories_export'],
204
+				'noheader'   => true,
205
+				'capability' => 'export',
206
+			],
207
+			'import_categories' => [
208
+				'func'       => [$this, '_import_categories'],
209
+				'capability' => 'import',
210
+			],
211
+			'category_list'     => [
212
+				'func'       => [$this, '_category_list_table'],
213
+				'capability' => 'ee_manage_venue_categories',
214
+			],
215
+		];
216
+	}
217
+
218
+
219
+	protected function _set_page_config()
220
+	{
221
+		$VNU_ID     = $this->request->getRequestParam('post', 0, 'int');
222
+		$EVT_CAT_ID = $this->request->getRequestParam('EVT_CAT_ID', 0, 'int');
223
+
224
+		$this->_page_config = [
225
+			'default'             => [
226
+				'nav'           => [
227
+					'label' => esc_html__('Overview', 'event_espresso'),
228
+					'order' => 10,
229
+				],
230
+				'list_table'    => 'Venues_Admin_List_Table',
231
+				'help_tabs'     => [
232
+					'venues_overview_help_tab'                           => [
233
+						'title'    => esc_html__('Venues Overview', 'event_espresso'),
234
+						'filename' => 'venues_overview',
235
+					],
236
+					'venues_overview_table_column_headings_help_tab'     => [
237
+						'title'    => esc_html__('Venues Overview Table Column Headings', 'event_espresso'),
238
+						'filename' => 'venues_overview_table_column_headings',
239
+					],
240
+					'venues_overview_views_bulk_actions_search_help_tab' => [
241
+						'title'    => esc_html__('Venues Overview Views & Bulk Actions & Search', 'event_espresso'),
242
+						'filename' => 'venues_overview_views_bulk_actions_search',
243
+					],
244
+				],
245
+				'metaboxes'     => ['_espresso_news_post_box', '_espresso_links_post_box'],
246
+				'require_nonce' => false,
247
+			],
248
+			'create_new'          => [
249
+				'nav'           => [
250
+					'label'      => esc_html__('Add Venue', 'event_espresso'),
251
+					'order'      => 5,
252
+					'persistent' => false,
253
+				],
254
+				'help_tabs'     => [
255
+					'venues_editor_help_tab'                                               => [
256
+						'title'    => esc_html__('Venue Editor', 'event_espresso'),
257
+						'filename' => 'venues_editor',
258
+					],
259
+					'venues_editor_title_richtexteditor_help_tab'                          => [
260
+						'title'    => esc_html__('Venue Title & Rich Text Editor', 'event_espresso'),
261
+						'filename' => 'venues_editor_title_richtexteditor',
262
+					],
263
+					'venues_editor_tags_categories_help_tab'                               => [
264
+						'title'    => esc_html__('Venue Tags & Categories', 'event_espresso'),
265
+						'filename' => 'venues_editor_tags_categories',
266
+					],
267
+					'venues_editor_physical_location_google_map_virtual_location_help_tab' => [
268
+						'title'    => esc_html__(
269
+							'Venue Editor Physical Location & Google Map & Virtual Location',
270
+							'event_espresso'
271
+						),
272
+						'filename' => 'venues_editor_physical_location_google_map_virtual_location',
273
+					],
274
+					'venues_editor_save_new_venue_help_tab'                                => [
275
+						'title'    => esc_html__('Save New Venue', 'event_espresso'),
276
+						'filename' => 'venues_editor_save_new_venue',
277
+					],
278
+					'venues_editor_other_help_tab'                                         => [
279
+						'title'    => esc_html__('Venue Editor Other', 'event_espresso'),
280
+						'filename' => 'venues_editor_other',
281
+					],
282
+				],
283
+				'metaboxes'     => ['_venue_editor_metaboxes'],
284
+				'require_nonce' => false,
285
+			],
286
+			'edit'                => [
287
+				'nav'           => [
288
+					'label'      => esc_html__('Edit Venue', 'event_espresso'),
289
+					'order'      => 5,
290
+					'persistent' => false,
291
+					'url'        => $VNU_ID
292
+						? add_query_arg(['post' => $VNU_ID], $this->_current_page_view_url)
293
+						: $this->_admin_base_url,
294
+				],
295
+				'help_tabs'     => [
296
+					'venues_editor_help_tab'                                               => [
297
+						'title'    => esc_html__('Venue Editor', 'event_espresso'),
298
+						'filename' => 'venues_editor',
299
+					],
300
+					'venues_editor_title_richtexteditor_help_tab'                          => [
301
+						'title'    => esc_html__('Venue Title & Rich Text Editor', 'event_espresso'),
302
+						'filename' => 'venues_editor_title_richtexteditor',
303
+					],
304
+					'venues_editor_tags_categories_help_tab'                               => [
305
+						'title'    => esc_html__('Venue Tags & Categories', 'event_espresso'),
306
+						'filename' => 'venues_editor_tags_categories',
307
+					],
308
+					'venues_editor_physical_location_google_map_virtual_location_help_tab' => [
309
+						'title'    => esc_html__(
310
+							'Venue Editor Physical Location & Google Map & Virtual Location',
311
+							'event_espresso'
312
+						),
313
+						'filename' => 'venues_editor_physical_location_google_map_virtual_location',
314
+					],
315
+					'venues_editor_save_new_venue_help_tab'                                => [
316
+						'title'    => esc_html__('Save New Venue', 'event_espresso'),
317
+						'filename' => 'venues_editor_save_new_venue',
318
+					],
319
+					'venues_editor_other_help_tab'                                         => [
320
+						'title'    => esc_html__('Venue Editor Other', 'event_espresso'),
321
+						'filename' => 'venues_editor_other',
322
+					],
323
+				],
324
+				'metaboxes'     => ['_venue_editor_metaboxes'],
325
+				'require_nonce' => false,
326
+			],
327
+			'google_map_settings' => [
328
+				'nav'           => [
329
+					'label' => esc_html__('Google Maps', 'event_espresso'),
330
+					'order' => 40,
331
+				],
332
+				'metaboxes'     => array_merge($this->_default_espresso_metaboxes, ['_publish_post_box']),
333
+				'help_tabs'     => [
334
+					'general_settings_google_maps_help_tab' => [
335
+						'title'    => esc_html__('Google Maps', 'event_espresso'),
336
+						'filename' => 'general_settings_google_maps',
337
+					],
338
+				],
339
+				'require_nonce' => false,
340
+			],
341
+			// venue category stuff
342
+			'add_category'        => [
343
+				'nav'           => [
344
+					'label'      => esc_html__('Add Category', 'event_espresso'),
345
+					'order'      => 15,
346
+					'persistent' => false,
347
+				],
348
+				'metaboxes'     => ['_publish_post_box'],
349
+				'help_tabs'     => [
350
+					'venues_add_category_help_tab' => [
351
+						'title'    => esc_html__('Add New Venue Category', 'event_espresso'),
352
+						'filename' => 'venues_add_category',
353
+					],
354
+				],
355
+				'require_nonce' => false,
356
+			],
357
+			'edit_category'       => [
358
+				'nav'           => [
359
+					'label'      => esc_html__('Edit Category', 'event_espresso'),
360
+					'order'      => 15,
361
+					'persistent' => false,
362
+					'url'        => $EVT_CAT_ID
363
+						? add_query_arg(['EVT_CAT_ID' => $EVT_CAT_ID], $this->_current_page_view_url)
364
+						: $this->_admin_base_url,
365
+				],
366
+				'metaboxes'     => ['_publish_post_box'],
367
+				'help_tabs'     => [
368
+					'venues_edit_category_help_tab' => [
369
+						'title'    => esc_html__('Edit Venue Category', 'event_espresso'),
370
+						'filename' => 'venues_edit_category',
371
+					],
372
+				],
373
+				'require_nonce' => false,
374
+			],
375
+			'category_list'       => [
376
+				'nav'           => [
377
+					'label' => esc_html__('Categories', 'event_espresso'),
378
+					'order' => 20,
379
+				],
380
+				'list_table'    => 'Venue_Categories_Admin_List_Table',
381
+				'help_tabs'     => [
382
+					'venues_categories_help_tab'                       => [
383
+						'title'    => esc_html__('Venue Categories', 'event_espresso'),
384
+						'filename' => 'venues_categories',
385
+					],
386
+					'venues_categories_table_column_headings_help_tab' => [
387
+						'title'    => esc_html__('Venue Categories Table Column Headings', 'event_espresso'),
388
+						'filename' => 'venues_categories_table_column_headings',
389
+					],
390
+					'venues_categories_views_help_tab'                 => [
391
+						'title'    => esc_html__('Venue Categories Views', 'event_espresso'),
392
+						'filename' => 'venues_categories_views',
393
+					],
394
+					'venues_categories_other_help_tab'                 => [
395
+						'title'    => esc_html__('Venue Categories Other', 'event_espresso'),
396
+						'filename' => 'venues_categories_other',
397
+					],
398
+				],
399
+				'metaboxes'     => $this->_default_espresso_metaboxes,
400
+				'require_nonce' => false,
401
+			],
402
+		];
403
+	}
404
+
405
+
406
+	protected function _add_screen_options()
407
+	{
408
+		// todo
409
+	}
410
+
411
+
412
+	protected function _add_screen_options_default()
413
+	{
414
+		$this->_per_page_screen_option();
415
+	}
416
+
417
+
418
+	protected function _add_screen_options_category_list()
419
+	{
420
+		$page_title              = $this->_admin_page_title;
421
+		$this->_admin_page_title = esc_html__('Venue Categories', 'event_espresso');
422
+		$this->_per_page_screen_option();
423
+		$this->_admin_page_title = $page_title;
424
+	}
425
+
426
+
427
+	// none of the below group are currently used for Event Venues
428
+	protected function _add_feature_pointers()
429
+	{
430
+	}
431
+
432
+
433
+	public function admin_init()
434
+	{
435
+	}
436
+
437
+
438
+	public function admin_notices()
439
+	{
440
+	}
441
+
442
+
443
+	public function admin_footer_scripts()
444
+	{
445
+	}
446
+
447
+
448
+	public function load_scripts_styles_create_new()
449
+	{
450
+		$this->load_scripts_styles_edit();
451
+	}
452
+
453
+
454
+	public function load_scripts_styles()
455
+	{
456
+		wp_register_style('ee-cat-admin', EVENTS_ASSETS_URL . 'ee-cat-admin.css', [], EVENT_ESPRESSO_VERSION);
457
+		wp_enqueue_style('ee-cat-admin');
458
+	}
459
+
460
+
461
+	public function load_scripts_styles_add_category()
462
+	{
463
+		$this->load_scripts_styles_edit_category();
464
+	}
465
+
466
+
467
+	public function load_scripts_styles_edit_category()
468
+	{
469
+	}
470
+
471
+
472
+	public function load_scripts_styles_edit()
473
+	{
474
+		// styles
475
+		wp_enqueue_style('espresso-ui-theme');
476
+		wp_register_style(
477
+			'espresso_venues',
478
+			EE_VENUES_ASSETS_URL . 'ee-venues-admin.css',
479
+			[],
480
+			EVENT_ESPRESSO_VERSION
481
+		);
482
+		wp_enqueue_style('espresso_venues');
483
+	}
484
+
485
+
486
+	protected function _set_list_table_views_default()
487
+	{
488
+		$this->_views = [
489
+			'all' => [
490
+				'slug'        => 'all',
491
+				'label'       => esc_html__('View All Venues', 'event_espresso'),
492
+				'count'       => 0,
493
+				'bulk_action' => [],
494
+			],
495
+		];
496
+
497
+		if (EE_Registry::instance()->CAP->current_user_can('ee_delete_venues', 'espresso_venues_trash_venues')) {
498
+			$this->_views['all']['bulk_action'] = [
499
+				'trash_venues' => esc_html__('Move to Trash', 'event_espresso'),
500
+			];
501
+			$this->_views['trash']              = [
502
+				'slug'        => 'trash',
503
+				'label'       => esc_html__('Trash', 'event_espresso'),
504
+				'count'       => 0,
505
+				'bulk_action' => [
506
+					'restore_venues' => esc_html__('Restore from Trash', 'event_espresso'),
507
+					'delete_venues'  => esc_html__('Delete', 'event_espresso'),
508
+				],
509
+			];
510
+		}
511
+	}
512
+
513
+
514
+	protected function _set_list_table_views_category_list()
515
+	{
516
+		$this->_views = [
517
+			'all' => [
518
+				'slug'        => 'all',
519
+				'label'       => esc_html__('All', 'event_espresso'),
520
+				'count'       => 0,
521
+				'bulk_action' => [
522
+					'delete_categories' => esc_html__('Delete Permanently', 'event_espresso'),
523
+				],
524
+			],
525
+		];
526
+	}
527
+
528
+
529
+	/**
530
+	 * @throws EE_Error
531
+	 */
532
+	protected function _overview_list_table()
533
+	{
534
+		do_action('AHEE_log', __FILE__, __FUNCTION__, '');
535
+		$this->_template_args['after_list_table'] = EEH_Template::get_button_or_link(
536
+			get_post_type_archive_link('espresso_venues'),
537
+			esc_html__("View Venue Archive Page", "event_espresso"),
538
+			'button'
539
+		);
540
+
541
+		$this->_admin_page_title .= ' ' . $this->get_action_link_or_button(
542
+			'create_new',
543
+			'add',
544
+			[],
545
+			'add-new-h2'
546
+		);
547
+
548
+		$this->_search_btn_label  = esc_html__('Venues', 'event_espresso');
549
+		$this->display_admin_list_table_page_with_sidebar();
550
+	}
551
+
552
+
553
+	/**
554
+	 * @throws EE_Error
555
+	 * @throws ReflectionException
556
+	 */
557
+	public function extra_misc_actions_publish_box()
558
+	{
559
+		$extra_rows = [
560
+			'vnu_capacity' => $this->_cpt_model_obj->get_f('VNU_capacity'),
561
+			'vnu_url'      => $this->_cpt_model_obj->get_f('VNU_url'),
562
+			'vnu_phone'    => $this->_cpt_model_obj->get_f('VNU_phone'),
563
+		];
564
+		$template   = EE_VENUES_TEMPLATE_PATH . 'venue_publish_box_extras.template.php';
565
+		EEH_Template::display_template($template, $extra_rows);
566
+	}
567
+
568
+
569
+	/*************        Google Maps        *************
570 570
      *
571 571
      * @throws EE_Error
572 572
      * @throws EE_Error
573 573
      */
574 574
 
575 575
 
576
-    protected function _google_map_settings()
577
-    {
578
-
579
-
580
-        $this->_template_args['values']           = $this->_yes_no_values;
581
-        $default_map_settings                     = new stdClass();
582
-        $default_map_settings->use_google_maps    = true;
583
-        $default_map_settings->google_map_api_key = '';
584
-        // for event details pages (reg page)
585
-        $default_map_settings->event_details_map_width    = 585;
586
-        // ee_map_width_single
587
-        $default_map_settings->event_details_map_height   = 362;
588
-        // ee_map_height_single
589
-        $default_map_settings->event_details_map_zoom     = 14;
590
-        // ee_map_zoom_single
591
-        $default_map_settings->event_details_display_nav  = true;
592
-        // ee_map_nav_display_single
593
-        $default_map_settings->event_details_nav_size     = false;
594
-        // ee_map_nav_size_single
595
-        $default_map_settings->event_details_control_type = 'default';
596
-        // ee_map_type_control_single
597
-        $default_map_settings->event_details_map_align    = 'center';
598
-        // ee_map_align_single
599
-
600
-        // for event list pages
601
-        $default_map_settings->event_list_map_width    = 300;
602
-        // ee_map_width
603
-        $default_map_settings->event_list_map_height   = 185;
604
-        // ee_map_height
605
-        $default_map_settings->event_list_map_zoom     = 12;
606
-        // ee_map_zoom
607
-        $default_map_settings->event_list_display_nav  = false;
608
-        // ee_map_nav_display
609
-        $default_map_settings->event_list_nav_size     = true;
610
-        // ee_map_nav_size
611
-        $default_map_settings->event_list_control_type = 'dropdown';
612
-        // ee_map_type_control
613
-        $default_map_settings->event_list_map_align    = 'center';
614
-        // ee_map_align
615
-
616
-        $this->_template_args['map_settings'] =
617
-            isset(EE_Registry::instance()->CFG->map_settings)
618
-            && ! empty(EE_Registry::instance()->CFG->map_settings)
619
-                ? (object) array_merge(
620
-                    (array) $default_map_settings,
621
-                    (array) EE_Registry::instance()->CFG->map_settings
622
-                )
623
-                : $default_map_settings;
624
-
625
-        $this->_set_add_edit_form_tags('update_google_map_settings');
626
-        $this->_set_publish_post_box_vars(null, false, false, null, false);
627
-        $this->_template_args['admin_page_content'] = EEH_Template::display_template(
628
-            EE_VENUES_TEMPLATE_PATH . 'google_map.template.php',
629
-            $this->_template_args,
630
-            true
631
-        );
632
-        $this->display_admin_page_with_sidebar();
633
-    }
634
-
635
-
636
-    /**
637
-     * @throws EE_Error
638
-     */
639
-    protected function _update_google_map_settings()
640
-    {
641
-        $map_settings = EE_Registry::instance()->CFG->map_settings;
642
-
643
-        $settings = [
644
-            'use_google_maps'            => 'int',
645
-            'google_map_api_key'         => 'string',
646
-            'event_details_map_width'    => 'int',
647
-            'event_details_map_zoom'     => 'int',
648
-            'event_details_display_nav'  => 'int',
649
-            'event_details_nav_size'     => 'int',
650
-            'event_details_control_type' => 'string',
651
-            'event_details_map_align'    => 'string',
652
-            'event_list_map_width'       => 'int',
653
-            'event_list_map_height'      => 'int',
654
-            'event_list_map_zoom'        => 'int',
655
-            'event_list_display_nav'     => 'int',
656
-            'event_list_nav_size'        => 'int',
657
-            'event_list_control_type'    => 'string',
658
-            'event_list_map_align'       => 'string',
659
-        ];
660
-
661
-        foreach ($settings as $setting => $type) {
662
-            $map_settings->{$setting} = $this->request->getRequestParam($setting, $map_settings->{$setting}, $type);
663
-        }
664
-
665
-        EE_Registry::instance()->CFG->map_settings = apply_filters(
666
-            'FHEE__Extend_General_Settings_Admin_Page___update_google_map_settings__CFG_map_settings',
667
-            $map_settings
668
-        );
669
-
670
-        $what    = 'Google Map Settings';
671
-        $success = $this->_update_espresso_configuration(
672
-            $what,
673
-            EE_Registry::instance()->CFG->map_settings,
674
-            __FILE__,
675
-            __FUNCTION__,
676
-            __LINE__
677
-        );
678
-        $this->_redirect_after_action($success, $what, 'updated', ['action' => 'google_map_settings']);
679
-    }
680
-
681
-
682
-    /**
683
-     * @throws EE_Error
684
-     * @throws ReflectionException
685
-     */
686
-    protected function _venue_editor_metaboxes()
687
-    {
688
-        $this->verify_cpt_object();
689
-
690
-        add_meta_box(
691
-            'espresso_venue_address_options',
692
-            esc_html__('Physical Location', 'event_espresso'),
693
-            [$this, 'venue_address_metabox'],
694
-            $this->page_slug,
695
-            'side'
696
-        );
697
-        add_meta_box(
698
-            'espresso_venue_gmap_options',
699
-            esc_html__('Google Map', 'event_espresso'),
700
-            [$this, 'venue_gmap_metabox'],
701
-            $this->page_slug,
702
-            'side'
703
-        );
704
-        add_meta_box(
705
-            'espresso_venue_virtual_loc_options',
706
-            esc_html__('Virtual Location', 'event_espresso'),
707
-            [$this, 'venue_virtual_loc_metabox'],
708
-            $this->page_slug,
709
-            'side'
710
-        );
711
-    }
712
-
713
-
714
-    public function venue_gmap_metabox()
715
-    {
716
-        $template_args = [
717
-            'vnu_enable_for_gmap' => EEH_Form_Fields::select_input(
718
-                'vnu_enable_for_gmap',
719
-                $this->get_yes_no_values(),
720
-                $this->_cpt_model_obj instanceof EE_Venue ? $this->_cpt_model_obj->enable_for_gmap() : false
721
-            ),
722
-            'vnu_google_map_link' => $this->_cpt_model_obj->google_map_link(),
723
-        ];
724
-        $template      = EE_VENUES_TEMPLATE_PATH . 'venue_gmap_metabox_content.template.php';
725
-        EEH_Template::display_template($template, $template_args);
726
-    }
727
-
728
-
729
-    /**
730
-     * @throws EE_Error
731
-     * @throws ReflectionException
732
-     */
733
-    public function venue_address_metabox()
734
-    {
735
-        $template_args['_venue'] = $this->_cpt_model_obj;
736
-
737
-        $template_args['states_dropdown']    = EEH_Form_Fields::generate_form_input(
738
-            new EE_Question_Form_Input(
739
-                EE_Question::new_instance(
740
-                    ['QST_display_text' => esc_html__('State', 'event_espresso'), 'QST_system' => 'state']
741
-                ),
742
-                EE_Answer::new_instance(
743
-                    [
744
-                        'ANS_value' => $this->_cpt_model_obj instanceof EE_Venue
745
-                            ? $this->_cpt_model_obj->state_ID()
746
-                            : 0,
747
-                    ]
748
-                ),
749
-                [
750
-                    'input_name'     => 'sta_id',
751
-                    'input_id'       => 'sta_id',
752
-                    'input_class'    => '',
753
-                    'input_prefix'   => '',
754
-                    'append_qstn_id' => false,
755
-                ]
756
-            )
757
-        );
758
-        $template_args['countries_dropdown'] = EEH_Form_Fields::generate_form_input(
759
-            new EE_Question_Form_Input(
760
-                EE_Question::new_instance(
761
-                    ['QST_display_text' => esc_html__('Country', 'event_espresso'), 'QST_system' => 'country']
762
-                ),
763
-                EE_Answer::new_instance(
764
-                    [
765
-                        'ANS_value' => $this->_cpt_model_obj instanceof EE_Venue
766
-                            ? $this->_cpt_model_obj->country_ID()
767
-                            : 0,
768
-                    ]
769
-                ),
770
-                [
771
-                    'input_name'     => 'cnt_iso',
772
-                    'input_id'       => 'cnt_iso',
773
-                    'input_class'    => '',
774
-                    'input_prefix'   => '',
775
-                    'append_qstn_id' => false,
776
-                ]
777
-            )
778
-        );
779
-
780
-        $template = EE_VENUES_TEMPLATE_PATH . 'venue_address_metabox_content.template.php';
781
-        EEH_Template::display_template($template, $template_args);
782
-    }
783
-
784
-
785
-    public function venue_virtual_loc_metabox()
786
-    {
787
-        $template_args = [
788
-            '_venue' => $this->_cpt_model_obj,
789
-        ];
790
-        $template      = EE_VENUES_TEMPLATE_PATH . 'venue_virtual_location_metabox_content.template.php';
791
-        EEH_Template::display_template($template, $template_args);
792
-    }
793
-
794
-
795
-    protected function _restore_cpt_item($post_id, $revision_id)
796
-    {
797
-        $venue_obj = $this->_venue_model->get_one_by_ID($post_id);
798
-
799
-        // meta revision restore
800
-        $venue_obj->restore_revision($revision_id);
801
-    }
802
-
803
-
804
-    /**
805
-     * Handles updates for venue cpts
806
-     *
807
-     * @param int    $post_id ID of Venue CPT
808
-     * @param WP_Post $post    Post object (with "blessed" WP properties)
809
-     * @return void
810
-     */
811
-    protected function _insert_update_cpt_item($post_id, $post)
812
-    {
813
-
814
-        if ($post instanceof WP_Post && $post->post_type !== 'espresso_venues') {
815
-            return;// get out we're not processing the saving of venues.
816
-        }
817
-
818
-        $wheres = [$this->_venue_model->primary_key_name() => $post_id];
819
-
820
-        $venue_values = [
821
-            'VNU_address'         => $this->request->getRequestParam('vnu_address'),
822
-            'VNU_address2'        => $this->request->getRequestParam('vnu_address2'),
823
-            'VNU_city'            => $this->request->getRequestParam('vnu_city'),
824
-            'STA_ID'              => $this->request->getRequestParam('sta_id'),
825
-            'CNT_ISO'             => $this->request->getRequestParam('cnt_iso'),
826
-            'VNU_zip'             => $this->request->getRequestParam('vnu_zip'),
827
-            'VNU_phone'           => $this->request->getRequestParam('vnu_phone'),
828
-            'VNU_capacity'        => $this->request->requestParamIsSet('vnu_capacity')
829
-                ? str_replace(',', '', $this->request->getRequestParam('vnu_capacity'))
830
-                : EE_INF,
831
-            'VNU_url'             => $this->request->getRequestParam('vnu_url'),
832
-            'VNU_virtual_phone'   => $this->request->getRequestParam('vnu_virtual_phone'),
833
-            'VNU_virtual_url'     => $this->request->getRequestParam('vnu_virtual_url'),
834
-            'VNU_enable_for_gmap' => $this->request->getRequestParam('vnu_enable_for_gmap', false, 'bool'),
835
-            'VNU_google_map_link' => $this->request->getRequestParam('vnu_google_map_link'),
836
-        ];
837
-
838
-        // update venue
839
-        $success = $this->_venue_model->update($venue_values, [$wheres]);
840
-
841
-        // get venue_object for other metaboxes that might be added via the filter... though it would seem to make sense to just use $this->_venue_model->get_one_by_ID( $post_id ).. i have to setup where conditions to override the filters in the model that filter out autodraft and inherit statuses so we GET the inherit id!
842
-        $get_one_where = [$this->_venue_model->primary_key_name() => $post_id, 'status' => $post->post_status];
843
-        $venue         = $this->_venue_model->get_one([$get_one_where]);
844
-
845
-        // notice we've applied a filter for venue metabox callbacks but we don't actually have any default venue metaboxes in use.  So this is just here for addons to more easily hook into venue saves.
846
-        $venue_update_callbacks = apply_filters(
847
-            'FHEE__Venues_Admin_Page___insert_update_cpt_item__venue_update_callbacks',
848
-            []
849
-        );
850
-        $att_success            = true;
851
-        foreach ($venue_update_callbacks as $v_callback) {
852
-            // if ANY of these updates fail then we want the appropriate global error message
853
-            $att_success = call_user_func_array($v_callback, [$venue, $this->request->requestParams()])
854
-                ? $att_success
855
-                : false;
856
-        }
857
-
858
-        // any errors?
859
-        if ($success && ! $att_success) {
860
-            EE_Error::add_error(
861
-                esc_html__(
862
-                    'Venue Details saved successfully but something went wrong with saving attachments.',
863
-                    'event_espresso'
864
-                ),
865
-                __FILE__,
866
-                __FUNCTION__,
867
-                __LINE__
868
-            );
869
-        } elseif ($success === false) {
870
-            EE_Error::add_error(
871
-                esc_html__('Venue Details did not save successfully.', 'event_espresso'),
872
-                __FILE__,
873
-                __FUNCTION__,
874
-                __LINE__
875
-            );
876
-        }
877
-    }
878
-
879
-
880
-    /**
881
-     * @param int $post_id
882
-     * @throws EE_Error
883
-     * @throws ReflectionException
884
-     */
885
-    public function trash_cpt_item($post_id)
886
-    {
887
-        $this->request->setRequestParam('VNU_ID', $post_id);
888
-        $this->_trash_or_restore_venue('trash', false);
889
-    }
890
-
891
-
892
-    /**
893
-     * @param int $post_id
894
-     * @throws EE_Error
895
-     * @throws ReflectionException
896
-     */
897
-    public function restore_cpt_item($post_id)
898
-    {
899
-        $this->request->setRequestParam('VNU_ID', $post_id);
900
-        $this->_trash_or_restore_venue('draft', false);
901
-    }
902
-
903
-
904
-    /**
905
-     * @param int $post_id
906
-     * @throws EE_Error
907
-     * @throws ReflectionException
908
-     */
909
-    public function delete_cpt_item($post_id)
910
-    {
911
-        $this->request->setRequestParam('VNU_ID', $post_id);
912
-        $this->_delete_venue(false);
913
-    }
914
-
915
-
916
-    public function get_venue_object()
917
-    {
918
-        return $this->_cpt_model_obj;
919
-    }
920
-
921
-
922
-    /**
923
-     * @throws EE_Error
924
-     * @throws ReflectionException
925
-     */
926
-    protected function _trash_or_restore_venue($venue_status = 'trash', $redirect_after = true)
927
-    {
928
-        $VNU_ID = $this->request->getRequestParam('VNU_ID', 0, 'int');
929
-        // loop thru venues
930
-        if ($VNU_ID) {
931
-            // clean status
932
-            $venue_status = sanitize_key($venue_status);
933
-            // grab status
934
-            if (! empty($venue_status)) {
935
-                $success = $this->_change_venue_status($VNU_ID, $venue_status);
936
-            } else {
937
-                $success = false;
938
-                $msg     = esc_html__(
939
-                    'An error occurred. The venue could not be moved to the trash because a valid venue status was not not supplied.',
940
-                    'event_espresso'
941
-                );
942
-                EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
943
-            }
944
-        } else {
945
-            $success = false;
946
-            $msg     = esc_html__(
947
-                'An error occurred. The venue could not be moved to the trash because a valid venue ID was not not supplied.',
948
-                'event_espresso'
949
-            );
950
-            EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
951
-        }
952
-        $action = $venue_status == 'trash' ? 'moved to the trash' : 'restored from the trash';
953
-
954
-        if ($redirect_after) {
955
-            $this->_redirect_after_action($success, 'Venue', $action, ['action' => 'default']);
956
-        }
957
-    }
958
-
959
-
960
-    /**
961
-     * @throws EE_Error
962
-     * @throws ReflectionException
963
-     */
964
-    protected function _trash_or_restore_venues($venue_status = 'trash')
965
-    {
966
-        // clean status
967
-        $venue_status = sanitize_key($venue_status);
968
-        // grab status
969
-        if (! empty($venue_status)) {
970
-            $success = true;
971
-            // determine the event id and set to array.
972
-            $VNU_IDs = $this->request->getRequestParam('venue_id', [], 'int', true);
973
-            // loop thru events
974
-            foreach ($VNU_IDs as $VNU_ID) {
975
-                if ($VNU_ID = absint($VNU_ID)) {
976
-                    $results = $this->_change_venue_status($VNU_ID, $venue_status);
977
-                    $success = $results !== false ? $success : false;
978
-                } else {
979
-                    $msg = sprintf(
980
-                        esc_html__(
981
-                            'An error occurred. Venue #%d could not be moved to the trash because a valid venue ID was not not supplied.',
982
-                            'event_espresso'
983
-                        ),
984
-                        $VNU_ID
985
-                    );
986
-                    EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
987
-                    $success = false;
988
-                }
989
-            }
990
-        } else {
991
-            $success = false;
992
-            $msg     = esc_html__(
993
-                'An error occurred. The venue could not be moved to the trash because a valid venue status was not not supplied.',
994
-                'event_espresso'
995
-            );
996
-            EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
997
-        }
998
-        // in order to force a pluralized result message we need to send back a success status greater than 1
999
-        $success = $success ? 2 : false;
1000
-        $action  = $venue_status == 'trash' ? 'moved to the trash' : 'restored from the trash';
1001
-        $this->_redirect_after_action($success, 'Venues', $action, ['action' => 'default']);
1002
-    }
1003
-
1004
-
1005
-    /**
1006
-     * _trash_or_restore_venues
1007
-     *
1008
-     * //todo this is pretty much the same as the corresponding change_event_status method in Events_Admin_Page.  We
1009
-     * should probably abstract this up to the EE_Admin_Page_CPT (or even EE_Admin_Page) and make this a common method
1010
-     * accepting a certain number of params.
1011
-     *
1012
-     * @access  private
1013
-     * @param int    $VNU_ID
1014
-     * @param string $venue_status
1015
-     * @return bool
1016
-     * @throws EE_Error
1017
-     * @throws ReflectionException
1018
-     */
1019
-    private function _change_venue_status($VNU_ID = 0, $venue_status = '')
1020
-    {
1021
-        // grab venue id
1022
-        if (! $VNU_ID) {
1023
-            $msg = esc_html__('An error occurred. No Venue ID or an invalid Venue ID was received.', 'event_espresso');
1024
-            EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
1025
-            return false;
1026
-        }
1027
-
1028
-        $this->_cpt_model_obj = EEM_Venue::instance()->get_one_by_ID($VNU_ID);
1029
-
1030
-        // clean status
1031
-        $venue_status = sanitize_key($venue_status);
1032
-        // grab status
1033
-        if (! $venue_status) {
1034
-            $msg = esc_html__(
1035
-                'An error occurred. No Venue Status or an invalid Venue Status was received.',
1036
-                'event_espresso'
1037
-            );
1038
-            EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
1039
-            return false;
1040
-        }
1041
-
1042
-        // was event trashed or restored ?
1043
-        switch ($venue_status) {
1044
-            case 'draft':
1045
-                $action = 'restored from the trash';
1046
-                $hook   = 'AHEE_venue_restored_from_trash';
1047
-                break;
1048
-            case 'trash':
1049
-                $action = 'moved to the trash';
1050
-                $hook   = 'AHEE_venue_moved_to_trash';
1051
-                break;
1052
-            default:
1053
-                $action = 'updated';
1054
-                $hook   = false;
1055
-        }
1056
-        // use class to change status
1057
-        $this->_cpt_model_obj->set_status($venue_status);
1058
-        $success = $this->_cpt_model_obj->save();
1059
-
1060
-        if ($success === false) {
1061
-            $msg = sprintf(esc_html__('An error occurred. The venue could not be %s.', 'event_espresso'), $action);
1062
-            EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
1063
-            return false;
1064
-        }
1065
-        if ($hook) {
1066
-            do_action($hook);
1067
-        }
1068
-        return true;
1069
-    }
1070
-
1071
-
1072
-    /**
1073
-     * @param bool $redirect_after
1074
-     * @return void
1075
-     * @throws EE_Error
1076
-     * @throws ReflectionException
1077
-     */
1078
-    protected function _delete_venue($redirect_after = true)
1079
-    {
1080
-        // determine the venue id and set to array.
1081
-        $VNU_ID = $this->request->getRequestParam('VNU_ID', 0, 'int');
1082
-        $VNU_ID = $this->request->getRequestParam('post', $VNU_ID, 'int');
1083
-
1084
-        // loop thru venues
1085
-        if ($VNU_ID) {
1086
-            $success = $this->_delete_or_trash_venue($VNU_ID);
1087
-        } else {
1088
-            $success = false;
1089
-            $msg     = esc_html__(
1090
-                'An error occurred. An venue could not be deleted because a valid venue ID was not not supplied.',
1091
-                'event_espresso'
1092
-            );
1093
-            EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
1094
-        }
1095
-        if ($redirect_after) {
1096
-            $this->_redirect_after_action($success, 'Venue', 'deleted', ['action' => 'default']);
1097
-        }
1098
-    }
1099
-
1100
-
1101
-    /**
1102
-     * @throws EE_Error
1103
-     * @throws ReflectionException
1104
-     */
1105
-    protected function _delete_venues()
1106
-    {
1107
-        $success = true;
1108
-        // determine the event id and set to array.
1109
-        $VNU_IDs = $this->request->getRequestParam('venue_id', [], 'int', true);
1110
-        // loop thru events
1111
-        foreach ($VNU_IDs as $VNU_ID) {
1112
-            if ($VNU_ID = absint($VNU_ID)) {
1113
-                $results = $this->_delete_or_trash_venue($VNU_ID);
1114
-                $success = $results !== false ? $success : false;
1115
-            } else {
1116
-                $success = false;
1117
-                $msg     = esc_html__(
1118
-                    'An error occurred. An venue could not be deleted because a valid venue ID was not not supplied.',
1119
-                    'event_espresso'
1120
-                );
1121
-                EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
1122
-            }
1123
-        }
1124
-        // in order to force a pluralized result message we need to send back a success status greater than 1
1125
-        $success = $success ? 2 : false;
1126
-        $this->_redirect_after_action(
1127
-            $success,
1128
-            esc_html__('Venues', 'event_espresso'),
1129
-            esc_html__('deleted', 'event_espresso'),
1130
-            ['action' => 'default']
1131
-        );
1132
-    }
1133
-
1134
-
1135
-    // todo: put in parent
1136
-
1137
-
1138
-    /**
1139
-     * @throws EE_Error
1140
-     * @throws ReflectionException
1141
-     */
1142
-    private function _delete_or_trash_venue($VNU_ID = false)
1143
-    {
1144
-        // grab event id
1145
-        if (! $VNU_ID = absint($VNU_ID)) {
1146
-            $msg = esc_html__('An error occurred. No Venue ID or an invalid Venue ID was received.', 'event_espresso');
1147
-            EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
1148
-            return false;
1149
-        }
1150
-
1151
-
1152
-        $venue = EEM_Venue::instance()->get_one_by_ID($VNU_ID);
1153
-        // first need to remove all term relationships
1154
-        $venue->_remove_relations('Term_Taxonomy');
1155
-        $success = $venue->delete_permanently();
1156
-        // did it all go as planned ?
1157
-        if ($success) {
1158
-            $msg = sprintf(esc_html__('Venue ID # %d has been deleted.', 'event_espresso'), $VNU_ID);
1159
-            EE_Error::add_success($msg);
1160
-        } else {
1161
-            $msg =
1162
-                sprintf(
1163
-                    esc_html__('An error occurred. Venue ID # %d could not be deleted.', 'event_espresso'),
1164
-                    $VNU_ID
1165
-                );
1166
-            EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
1167
-            return false;
1168
-        }
1169
-        do_action('AHEE__Venues_Admin_Page___delete_or_trash_venue__after_venue_deleted');
1170
-        return true;
1171
-    }
1172
-
1173
-
1174
-
1175
-
1176
-    /***********/
1177
-    /* QUERIES */
1178
-
1179
-
1180
-    public function get_venues($per_page = 10, $count = false)
1181
-    {
1182
-
1183
-        $orderby = $this->request->getRequestParam('orderby', '');
1184
-
1185
-        switch ($orderby) {
1186
-            case 'id':
1187
-                $orderby = 'VNU_ID';
1188
-                break;
1189
-
1190
-            case 'capacity':
1191
-                $orderby = 'VNU_capacity';
1192
-                break;
1193
-
1194
-            case 'city':
1195
-                $orderby = 'VNU_city';
1196
-                break;
1197
-
1198
-            default:
1199
-                $orderby = 'VNU_name';
1200
-        }
1201
-
1202
-        $sort         = $this->request->getRequestParam('order', 'ASC');
1203
-        $current_page = $this->request->getRequestParam('paged', 1, 'int');
1204
-        $per_page     = ! empty($per_page) ? $per_page : 10;
1205
-        $per_page     = $this->request->getRequestParam('perpage', $per_page, 'int');
1206
-
1207
-        $offset = ($current_page - 1) * $per_page;
1208
-        $limit  = [$offset, $per_page];
1209
-
1210
-        $category = $this->request->getRequestParam('category');
1211
-        $category = $category > 0 ? $category : null;
1212
-
1213
-        $where = [];
1214
-
1215
-        // only set initial status if it is in the incoming request.  Otherwise the "all" view display's all statuses.
1216
-        $status = $this->request->getRequestParam('status');
1217
-        if ($status && $status !== 'all') {
1218
-            $where['status'] = $status;
1219
-        }
1220
-
1221
-        $venue_status = $this->request->getRequestParam('venue_status');
1222
-        if ($venue_status) {
1223
-            $where['status'] = $venue_status;
1224
-        }
1225
-
1226
-
1227
-        if ($category) {
1228
-            $where['Term_Taxonomy.taxonomy'] = 'espresso_venue_categories';
1229
-            $where['Term_Taxonomy.term_id']  = $category;
1230
-        }
1231
-
1232
-
1233
-        if (! EE_Registry::instance()->CAP->current_user_can('ee_read_others_venues', 'get_venues')) {
1234
-            $where['VNU_wp_user'] = get_current_user_id();
1235
-        } else {
1236
-            if (! EE_Registry::instance()->CAP->current_user_can('ee_read_private_venues', 'get_venues')) {
1237
-                $where['OR'] = [
1238
-                    'status*restrict_private' => ['!=', 'private'],
1239
-                    'AND'                     => [
1240
-                        'status*inclusive' => ['=', 'private'],
1241
-                        'VNU_wp_user'      => get_current_user_id(),
1242
-                    ],
1243
-                ];
1244
-            }
1245
-        }
1246
-
1247
-        $search_term = $this->request->getRequestParam('s');
1248
-        if ($search_term) {
1249
-            $search_term = '%' . $search_term . '%';
1250
-            $where['OR'] = [
1251
-                'VNU_name'               => ['LIKE', $search_term],
1252
-                'VNU_desc'               => ['LIKE', $search_term],
1253
-                'VNU_short_desc'         => ['LIKE', $search_term],
1254
-                'VNU_address'            => ['LIKE', $search_term],
1255
-                'VNU_address2'           => ['LIKE', $search_term],
1256
-                'VNU_city'               => ['LIKE', $search_term],
1257
-                'VNU_zip'                => ['LIKE', $search_term],
1258
-                'VNU_phone'              => ['LIKE', $search_term],
1259
-                'VNU_url'                => ['LIKE', $search_term],
1260
-                'VNU_virtual_phone'      => ['LIKE', $search_term],
1261
-                'VNU_virtual_url'        => ['LIKE', $search_term],
1262
-                'VNU_google_map_link'    => ['LIKE', $search_term],
1263
-                'Event.EVT_name'         => ['LIKE', $search_term],
1264
-                'Event.EVT_desc'         => ['LIKE', $search_term],
1265
-                'Event.EVT_phone'        => ['LIKE', $search_term],
1266
-                'Event.EVT_external_URL' => ['LIKE', $search_term],
1267
-            ];
1268
-        }
1269
-
1270
-
1271
-        return $count
1272
-            ? $this->_venue_model->count([$where], 'VNU_ID')
1273
-            : $this->_venue_model->get_all(
1274
-                [$where, 'limit' => $limit, 'order_by' => $orderby, 'order' => $sort]
1275
-            );
1276
-    }
1277
-
1278
-
1279
-
1280
-
1281
-    /** Venue Category Stuff **/
1282
-
1283
-    /**
1284
-     * set the _category property with the category object for the loaded page.
1285
-     *
1286
-     * @access private
1287
-     * @return void
1288
-     */
1289
-    private function _set_category_object()
1290
-    {
1291
-        if (isset($this->_category->id) && ! empty($this->_category->id)) {
1292
-            return;
1293
-        } // already have the category object so get out.
1294
-
1295
-        // set default category object
1296
-        $this->_set_empty_category_object();
1297
-
1298
-        // only set if we've got an id
1299
-        $category_ID = $this->request->getRequestParam('VEN_CAT_ID', 0, 'int');
1300
-        if (! $category_ID) {
1301
-            return;
1302
-        }
1303
-
1304
-        $term = get_term($category_ID, 'espresso_venue_categories');
1305
-
1306
-
1307
-        if (! empty($term)) {
1308
-            $this->_category->category_name       = $term->name;
1309
-            $this->_category->category_identifier = $term->slug;
1310
-            $this->_category->category_desc       = $term->description;
1311
-            $this->_category->id                  = $term->term_id;
1312
-            $this->_category->parent              = $term->parent;
1313
-        }
1314
-    }
1315
-
1316
-
1317
-    private function _set_empty_category_object()
1318
-    {
1319
-        $this->_category                = new stdClass();
1320
-        $this->_category->category_name = $this->_category->category_identifier = $this->_category->category_desc = '';
1321
-        $this->_category->id            = $this->_category->parent = 0;
1322
-    }
1323
-
1324
-
1325
-    /**
1326
-     * @throws EE_Error
1327
-     */
1328
-    protected function _category_list_table()
1329
-    {
1330
-        do_action('AHEE_log', __FILE__, __FUNCTION__, '');
1331
-        $this->_admin_page_title .= ' ' . $this->get_action_link_or_button(
1332
-            'add_category',
1333
-            'add_category',
1334
-            [],
1335
-            'add-new-h2'
1336
-        );
1337
-        $this->_search_btn_label = esc_html__('Venue Categories', 'event_espresso');
1338
-        $this->display_admin_list_table_page_with_sidebar();
1339
-    }
1340
-
1341
-
1342
-    /**
1343
-     * @throws EE_Error
1344
-     */
1345
-    protected function _category_details($view)
1346
-    {
1347
-
1348
-        // load formatter helper
1349
-        // load field generator helper
1350
-
1351
-        $route = $view == 'edit' ? 'update_category' : 'insert_category';
1352
-        $this->_set_add_edit_form_tags($route);
1353
-
1354
-        $this->_set_category_object();
1355
-        $id = ! empty($this->_category->id) ? $this->_category->id : '';
1356
-
1357
-        $delete_action = 'delete_category';
1358
-
1359
-        $redirect = EE_Admin_Page::add_query_args_and_nonce(['action' => 'category_list'], $this->_admin_base_url);
1360
-
1361
-        $this->_set_publish_post_box_vars('VEN_CAT_ID', $id, $delete_action, $redirect);
1362
-
1363
-        // take care of contents
1364
-        $this->_template_args['admin_page_content'] = $this->_category_details_content();
1365
-        $this->display_admin_page_with_sidebar();
1366
-    }
1367
-
1368
-
1369
-    protected function _category_details_content()
1370
-    {
1371
-        $editor_args['category_desc'] = [
1372
-            'type'          => 'wp_editor',
1373
-            'value'         => EEH_Formatter::admin_format_content($this->_category->category_desc),
1374
-            'class'         => 'my_editor_custom',
1375
-            'wpeditor_args' => ['media_buttons' => false],
1376
-        ];
1377
-        $_wp_editor                   = $this->_generate_admin_form_fields($editor_args, 'array');
1378
-
1379
-        $all_terms = get_terms(
1380
-            ['espresso_venue_categories'],
1381
-            ['hide_empty' => 0, 'exclude' => [$this->_category->id]]
1382
-        );
1383
-
1384
-        // setup category select for term parents.
1385
-        $category_select_values[] = [
1386
-            'text' => esc_html__('No Parent', 'event_espresso'),
1387
-            'id'   => 0,
1388
-        ];
1389
-        foreach ($all_terms as $term) {
1390
-            $category_select_values[] = [
1391
-                'text' => $term->name,
1392
-                'id'   => $term->term_id,
1393
-            ];
1394
-        }
1395
-
1396
-        $category_select = EEH_Form_Fields::select_input(
1397
-            'category_parent',
1398
-            $category_select_values,
1399
-            $this->_category->parent
1400
-        );
1401
-        $template_args   = [
1402
-            'category'                 => $this->_category,
1403
-            'category_select'          => $category_select,
1404
-            'unique_id_info_help_link' => $this->_get_help_tab_link('unique_id_info'),
1405
-            'category_desc_editor'     => $_wp_editor['category_desc']['field'],
1406
-            'disable'                  => '',
1407
-            'disabled_message'         => false,
1408
-        ];
1409
-        $template        = EVENTS_TEMPLATE_PATH . 'event_category_details.template.php';
1410
-        return EEH_Template::display_template($template, $template_args, true);
1411
-    }
1412
-
1413
-
1414
-    /**
1415
-     * @throws EE_Error
1416
-     */
1417
-    protected function _delete_categories()
1418
-    {
1419
-        $category_ID  = $this->request->getRequestParam('category_id', 0, 'int');
1420
-        $category_IDs = $this->request->getRequestParam('VEN_CAT_ID', [$category_ID], 'int', true);
1421
-
1422
-        foreach ($category_IDs as $cat_id) {
1423
-            $this->_delete_category($cat_id);
1424
-        }
1425
-
1426
-        // doesn't matter what page we're coming from... we're going to the same place after delete.
1427
-        $query_args = [
1428
-            'action' => 'category_list',
1429
-        ];
1430
-        $this->_redirect_after_action(0, '', '', $query_args);
1431
-    }
1432
-
1433
-
1434
-    protected function _delete_category($cat_id)
1435
-    {
1436
-        $cat_id = absint($cat_id);
1437
-        wp_delete_term($cat_id, 'espresso_venue_categories');
1438
-    }
1439
-
1440
-
1441
-    /**
1442
-     * @throws EE_Error
1443
-     */
1444
-    protected function _insert_or_update_category($new_category)
1445
-    {
1446
-
1447
-        $cat_id  = $new_category ? $this->_insert_category() : $this->_insert_category(true);
1448
-        $success = 0; // we already have a success message so lets not send another.
1449
-        if ($cat_id) {
1450
-            $query_args = [
1451
-                'action'     => 'edit_category',
1452
-                'VEN_CAT_ID' => $cat_id,
1453
-            ];
1454
-        } else {
1455
-            $query_args = ['action' => 'add_category'];
1456
-        }
1457
-        $this->_redirect_after_action($success, '', '', $query_args, true);
1458
-    }
1459
-
1460
-
1461
-    private function _insert_category($update = false)
1462
-    {
1463
-        $category_ID     = $update ? $this->request->getRequestParam('VEN_CAT_ID', '', 'int') : '';
1464
-        $category_name   = $this->request->getRequestParam('category_name', '');
1465
-        $category_desc   = $this->request->getRequestParam('category_desc', '', 'html');
1466
-        $category_parent = $this->request->getRequestParam('category_parent', 0, 'int');
1467
-
1468
-        if (empty($category_name)) {
1469
-            $msg = esc_html__('You must add a name for the category.', 'event_espresso');
1470
-            EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
1471
-            return false;
1472
-        }
1473
-
1474
-
1475
-        $term_args = [
1476
-            'name'        => $category_name,
1477
-            'description' => $category_desc,
1478
-            'parent'      => $category_parent,
1479
-        ];
1480
-
1481
-        $insert_ids = $update
1482
-            ? wp_update_term($category_ID, 'espresso_venue_categories', $term_args)
1483
-            : wp_insert_term(
1484
-                $category_name,
1485
-                'espresso_venue_categories',
1486
-                $term_args
1487
-            );
1488
-
1489
-        if (! is_array($insert_ids)) {
1490
-            EE_Error::add_error(
1491
-                esc_html__('An error occurred and the category has not been saved to the database.', 'event_espresso'),
1492
-                __FILE__,
1493
-                __FUNCTION__,
1494
-                __LINE__
1495
-            );
1496
-        } else {
1497
-            $category_ID = $insert_ids['term_id'];
1498
-            EE_Error::add_success(
1499
-                sprintf(
1500
-                    esc_html__('The category %s was successfully created', 'event_espresso'),
1501
-                    $category_name
1502
-                )
1503
-            );
1504
-        }
1505
-
1506
-        return $category_ID;
1507
-    }
1508
-
1509
-
1510
-    /**
1511
-     * TODO handle category exports()
1512
-     *
1513
-     * @return void
1514
-     */
1515
-    protected function _categories_export()
1516
-    {
1517
-        // todo: I don't like doing this but it'll do until we modify EE_Export Class.
1518
-        $this->request->mergeRequestParams(
1519
-            [
1520
-                'export'       => 'report',
1521
-                'action'       => 'categories',
1522
-                'category_ids' => $this->request->getRequestParam('VEN_CAT_ID', 0, 'int'),
1523
-            ]
1524
-        );
1525
-
1526
-        if (is_readable(EE_CLASSES . 'EE_Export.class.php')) {
1527
-            require_once(EE_CLASSES . 'EE_Export.class.php');
1528
-            $EE_Export = EE_Export::instance($this->request->requestParams());
1529
-            $EE_Export->export();
1530
-        }
1531
-    }
1532
-
1533
-
1534
-    protected function _import_categories()
1535
-    {
1536
-
1537
-        require_once(EE_CLASSES . 'EE_Import.class.php');
1538
-        EE_Import::instance()->import();
1539
-    }
1540
-
1541
-
1542
-    /**
1543
-     * @throws EE_Error
1544
-     */
1545
-    public function get_categories($per_page = 10, $current_page = 1, $count = false)
1546
-    {
1547
-
1548
-        // testing term stuff
1549
-        $orderby     = $this->request->getRequestParam('orderby', 'Term.term_id');
1550
-        $order       = $this->request->getRequestParam('order', 'DESC');
1551
-        $limit       = ($current_page - 1) * $per_page;
1552
-        $where       = ['taxonomy' => 'espresso_venue_categories'];
1553
-        $search_term = $this->request->getRequestParam('s');
1554
-        if ($search_term) {
1555
-            $search_term = '%' . $search_term . '%';
1556
-            $where['OR'] = [
1557
-                'Term.name'   => ['LIKE', $search_term],
1558
-                'description' => ['LIKE', $search_term],
1559
-            ];
1560
-        }
1561
-
1562
-        $query_params = [
1563
-            $where,
1564
-            'order_by'   => [$orderby => $order],
1565
-            'limit'      => $limit . ',' . $per_page,
1566
-            'force_join' => ['Term'],
1567
-        ];
1568
-
1569
-        return $count
1570
-            ? EEM_Term_Taxonomy::instance()->count($query_params, 'term_id')
1571
-            : EEM_Term_Taxonomy::instance()->get_all($query_params);
1572
-    }
1573
-
1574
-
1575
-    /* end category stuff */
1576
-    /**************/
576
+	protected function _google_map_settings()
577
+	{
578
+
579
+
580
+		$this->_template_args['values']           = $this->_yes_no_values;
581
+		$default_map_settings                     = new stdClass();
582
+		$default_map_settings->use_google_maps    = true;
583
+		$default_map_settings->google_map_api_key = '';
584
+		// for event details pages (reg page)
585
+		$default_map_settings->event_details_map_width    = 585;
586
+		// ee_map_width_single
587
+		$default_map_settings->event_details_map_height   = 362;
588
+		// ee_map_height_single
589
+		$default_map_settings->event_details_map_zoom     = 14;
590
+		// ee_map_zoom_single
591
+		$default_map_settings->event_details_display_nav  = true;
592
+		// ee_map_nav_display_single
593
+		$default_map_settings->event_details_nav_size     = false;
594
+		// ee_map_nav_size_single
595
+		$default_map_settings->event_details_control_type = 'default';
596
+		// ee_map_type_control_single
597
+		$default_map_settings->event_details_map_align    = 'center';
598
+		// ee_map_align_single
599
+
600
+		// for event list pages
601
+		$default_map_settings->event_list_map_width    = 300;
602
+		// ee_map_width
603
+		$default_map_settings->event_list_map_height   = 185;
604
+		// ee_map_height
605
+		$default_map_settings->event_list_map_zoom     = 12;
606
+		// ee_map_zoom
607
+		$default_map_settings->event_list_display_nav  = false;
608
+		// ee_map_nav_display
609
+		$default_map_settings->event_list_nav_size     = true;
610
+		// ee_map_nav_size
611
+		$default_map_settings->event_list_control_type = 'dropdown';
612
+		// ee_map_type_control
613
+		$default_map_settings->event_list_map_align    = 'center';
614
+		// ee_map_align
615
+
616
+		$this->_template_args['map_settings'] =
617
+			isset(EE_Registry::instance()->CFG->map_settings)
618
+			&& ! empty(EE_Registry::instance()->CFG->map_settings)
619
+				? (object) array_merge(
620
+					(array) $default_map_settings,
621
+					(array) EE_Registry::instance()->CFG->map_settings
622
+				)
623
+				: $default_map_settings;
624
+
625
+		$this->_set_add_edit_form_tags('update_google_map_settings');
626
+		$this->_set_publish_post_box_vars(null, false, false, null, false);
627
+		$this->_template_args['admin_page_content'] = EEH_Template::display_template(
628
+			EE_VENUES_TEMPLATE_PATH . 'google_map.template.php',
629
+			$this->_template_args,
630
+			true
631
+		);
632
+		$this->display_admin_page_with_sidebar();
633
+	}
634
+
635
+
636
+	/**
637
+	 * @throws EE_Error
638
+	 */
639
+	protected function _update_google_map_settings()
640
+	{
641
+		$map_settings = EE_Registry::instance()->CFG->map_settings;
642
+
643
+		$settings = [
644
+			'use_google_maps'            => 'int',
645
+			'google_map_api_key'         => 'string',
646
+			'event_details_map_width'    => 'int',
647
+			'event_details_map_zoom'     => 'int',
648
+			'event_details_display_nav'  => 'int',
649
+			'event_details_nav_size'     => 'int',
650
+			'event_details_control_type' => 'string',
651
+			'event_details_map_align'    => 'string',
652
+			'event_list_map_width'       => 'int',
653
+			'event_list_map_height'      => 'int',
654
+			'event_list_map_zoom'        => 'int',
655
+			'event_list_display_nav'     => 'int',
656
+			'event_list_nav_size'        => 'int',
657
+			'event_list_control_type'    => 'string',
658
+			'event_list_map_align'       => 'string',
659
+		];
660
+
661
+		foreach ($settings as $setting => $type) {
662
+			$map_settings->{$setting} = $this->request->getRequestParam($setting, $map_settings->{$setting}, $type);
663
+		}
664
+
665
+		EE_Registry::instance()->CFG->map_settings = apply_filters(
666
+			'FHEE__Extend_General_Settings_Admin_Page___update_google_map_settings__CFG_map_settings',
667
+			$map_settings
668
+		);
669
+
670
+		$what    = 'Google Map Settings';
671
+		$success = $this->_update_espresso_configuration(
672
+			$what,
673
+			EE_Registry::instance()->CFG->map_settings,
674
+			__FILE__,
675
+			__FUNCTION__,
676
+			__LINE__
677
+		);
678
+		$this->_redirect_after_action($success, $what, 'updated', ['action' => 'google_map_settings']);
679
+	}
680
+
681
+
682
+	/**
683
+	 * @throws EE_Error
684
+	 * @throws ReflectionException
685
+	 */
686
+	protected function _venue_editor_metaboxes()
687
+	{
688
+		$this->verify_cpt_object();
689
+
690
+		add_meta_box(
691
+			'espresso_venue_address_options',
692
+			esc_html__('Physical Location', 'event_espresso'),
693
+			[$this, 'venue_address_metabox'],
694
+			$this->page_slug,
695
+			'side'
696
+		);
697
+		add_meta_box(
698
+			'espresso_venue_gmap_options',
699
+			esc_html__('Google Map', 'event_espresso'),
700
+			[$this, 'venue_gmap_metabox'],
701
+			$this->page_slug,
702
+			'side'
703
+		);
704
+		add_meta_box(
705
+			'espresso_venue_virtual_loc_options',
706
+			esc_html__('Virtual Location', 'event_espresso'),
707
+			[$this, 'venue_virtual_loc_metabox'],
708
+			$this->page_slug,
709
+			'side'
710
+		);
711
+	}
712
+
713
+
714
+	public function venue_gmap_metabox()
715
+	{
716
+		$template_args = [
717
+			'vnu_enable_for_gmap' => EEH_Form_Fields::select_input(
718
+				'vnu_enable_for_gmap',
719
+				$this->get_yes_no_values(),
720
+				$this->_cpt_model_obj instanceof EE_Venue ? $this->_cpt_model_obj->enable_for_gmap() : false
721
+			),
722
+			'vnu_google_map_link' => $this->_cpt_model_obj->google_map_link(),
723
+		];
724
+		$template      = EE_VENUES_TEMPLATE_PATH . 'venue_gmap_metabox_content.template.php';
725
+		EEH_Template::display_template($template, $template_args);
726
+	}
727
+
728
+
729
+	/**
730
+	 * @throws EE_Error
731
+	 * @throws ReflectionException
732
+	 */
733
+	public function venue_address_metabox()
734
+	{
735
+		$template_args['_venue'] = $this->_cpt_model_obj;
736
+
737
+		$template_args['states_dropdown']    = EEH_Form_Fields::generate_form_input(
738
+			new EE_Question_Form_Input(
739
+				EE_Question::new_instance(
740
+					['QST_display_text' => esc_html__('State', 'event_espresso'), 'QST_system' => 'state']
741
+				),
742
+				EE_Answer::new_instance(
743
+					[
744
+						'ANS_value' => $this->_cpt_model_obj instanceof EE_Venue
745
+							? $this->_cpt_model_obj->state_ID()
746
+							: 0,
747
+					]
748
+				),
749
+				[
750
+					'input_name'     => 'sta_id',
751
+					'input_id'       => 'sta_id',
752
+					'input_class'    => '',
753
+					'input_prefix'   => '',
754
+					'append_qstn_id' => false,
755
+				]
756
+			)
757
+		);
758
+		$template_args['countries_dropdown'] = EEH_Form_Fields::generate_form_input(
759
+			new EE_Question_Form_Input(
760
+				EE_Question::new_instance(
761
+					['QST_display_text' => esc_html__('Country', 'event_espresso'), 'QST_system' => 'country']
762
+				),
763
+				EE_Answer::new_instance(
764
+					[
765
+						'ANS_value' => $this->_cpt_model_obj instanceof EE_Venue
766
+							? $this->_cpt_model_obj->country_ID()
767
+							: 0,
768
+					]
769
+				),
770
+				[
771
+					'input_name'     => 'cnt_iso',
772
+					'input_id'       => 'cnt_iso',
773
+					'input_class'    => '',
774
+					'input_prefix'   => '',
775
+					'append_qstn_id' => false,
776
+				]
777
+			)
778
+		);
779
+
780
+		$template = EE_VENUES_TEMPLATE_PATH . 'venue_address_metabox_content.template.php';
781
+		EEH_Template::display_template($template, $template_args);
782
+	}
783
+
784
+
785
+	public function venue_virtual_loc_metabox()
786
+	{
787
+		$template_args = [
788
+			'_venue' => $this->_cpt_model_obj,
789
+		];
790
+		$template      = EE_VENUES_TEMPLATE_PATH . 'venue_virtual_location_metabox_content.template.php';
791
+		EEH_Template::display_template($template, $template_args);
792
+	}
793
+
794
+
795
+	protected function _restore_cpt_item($post_id, $revision_id)
796
+	{
797
+		$venue_obj = $this->_venue_model->get_one_by_ID($post_id);
798
+
799
+		// meta revision restore
800
+		$venue_obj->restore_revision($revision_id);
801
+	}
802
+
803
+
804
+	/**
805
+	 * Handles updates for venue cpts
806
+	 *
807
+	 * @param int    $post_id ID of Venue CPT
808
+	 * @param WP_Post $post    Post object (with "blessed" WP properties)
809
+	 * @return void
810
+	 */
811
+	protected function _insert_update_cpt_item($post_id, $post)
812
+	{
813
+
814
+		if ($post instanceof WP_Post && $post->post_type !== 'espresso_venues') {
815
+			return;// get out we're not processing the saving of venues.
816
+		}
817
+
818
+		$wheres = [$this->_venue_model->primary_key_name() => $post_id];
819
+
820
+		$venue_values = [
821
+			'VNU_address'         => $this->request->getRequestParam('vnu_address'),
822
+			'VNU_address2'        => $this->request->getRequestParam('vnu_address2'),
823
+			'VNU_city'            => $this->request->getRequestParam('vnu_city'),
824
+			'STA_ID'              => $this->request->getRequestParam('sta_id'),
825
+			'CNT_ISO'             => $this->request->getRequestParam('cnt_iso'),
826
+			'VNU_zip'             => $this->request->getRequestParam('vnu_zip'),
827
+			'VNU_phone'           => $this->request->getRequestParam('vnu_phone'),
828
+			'VNU_capacity'        => $this->request->requestParamIsSet('vnu_capacity')
829
+				? str_replace(',', '', $this->request->getRequestParam('vnu_capacity'))
830
+				: EE_INF,
831
+			'VNU_url'             => $this->request->getRequestParam('vnu_url'),
832
+			'VNU_virtual_phone'   => $this->request->getRequestParam('vnu_virtual_phone'),
833
+			'VNU_virtual_url'     => $this->request->getRequestParam('vnu_virtual_url'),
834
+			'VNU_enable_for_gmap' => $this->request->getRequestParam('vnu_enable_for_gmap', false, 'bool'),
835
+			'VNU_google_map_link' => $this->request->getRequestParam('vnu_google_map_link'),
836
+		];
837
+
838
+		// update venue
839
+		$success = $this->_venue_model->update($venue_values, [$wheres]);
840
+
841
+		// get venue_object for other metaboxes that might be added via the filter... though it would seem to make sense to just use $this->_venue_model->get_one_by_ID( $post_id ).. i have to setup where conditions to override the filters in the model that filter out autodraft and inherit statuses so we GET the inherit id!
842
+		$get_one_where = [$this->_venue_model->primary_key_name() => $post_id, 'status' => $post->post_status];
843
+		$venue         = $this->_venue_model->get_one([$get_one_where]);
844
+
845
+		// notice we've applied a filter for venue metabox callbacks but we don't actually have any default venue metaboxes in use.  So this is just here for addons to more easily hook into venue saves.
846
+		$venue_update_callbacks = apply_filters(
847
+			'FHEE__Venues_Admin_Page___insert_update_cpt_item__venue_update_callbacks',
848
+			[]
849
+		);
850
+		$att_success            = true;
851
+		foreach ($venue_update_callbacks as $v_callback) {
852
+			// if ANY of these updates fail then we want the appropriate global error message
853
+			$att_success = call_user_func_array($v_callback, [$venue, $this->request->requestParams()])
854
+				? $att_success
855
+				: false;
856
+		}
857
+
858
+		// any errors?
859
+		if ($success && ! $att_success) {
860
+			EE_Error::add_error(
861
+				esc_html__(
862
+					'Venue Details saved successfully but something went wrong with saving attachments.',
863
+					'event_espresso'
864
+				),
865
+				__FILE__,
866
+				__FUNCTION__,
867
+				__LINE__
868
+			);
869
+		} elseif ($success === false) {
870
+			EE_Error::add_error(
871
+				esc_html__('Venue Details did not save successfully.', 'event_espresso'),
872
+				__FILE__,
873
+				__FUNCTION__,
874
+				__LINE__
875
+			);
876
+		}
877
+	}
878
+
879
+
880
+	/**
881
+	 * @param int $post_id
882
+	 * @throws EE_Error
883
+	 * @throws ReflectionException
884
+	 */
885
+	public function trash_cpt_item($post_id)
886
+	{
887
+		$this->request->setRequestParam('VNU_ID', $post_id);
888
+		$this->_trash_or_restore_venue('trash', false);
889
+	}
890
+
891
+
892
+	/**
893
+	 * @param int $post_id
894
+	 * @throws EE_Error
895
+	 * @throws ReflectionException
896
+	 */
897
+	public function restore_cpt_item($post_id)
898
+	{
899
+		$this->request->setRequestParam('VNU_ID', $post_id);
900
+		$this->_trash_or_restore_venue('draft', false);
901
+	}
902
+
903
+
904
+	/**
905
+	 * @param int $post_id
906
+	 * @throws EE_Error
907
+	 * @throws ReflectionException
908
+	 */
909
+	public function delete_cpt_item($post_id)
910
+	{
911
+		$this->request->setRequestParam('VNU_ID', $post_id);
912
+		$this->_delete_venue(false);
913
+	}
914
+
915
+
916
+	public function get_venue_object()
917
+	{
918
+		return $this->_cpt_model_obj;
919
+	}
920
+
921
+
922
+	/**
923
+	 * @throws EE_Error
924
+	 * @throws ReflectionException
925
+	 */
926
+	protected function _trash_or_restore_venue($venue_status = 'trash', $redirect_after = true)
927
+	{
928
+		$VNU_ID = $this->request->getRequestParam('VNU_ID', 0, 'int');
929
+		// loop thru venues
930
+		if ($VNU_ID) {
931
+			// clean status
932
+			$venue_status = sanitize_key($venue_status);
933
+			// grab status
934
+			if (! empty($venue_status)) {
935
+				$success = $this->_change_venue_status($VNU_ID, $venue_status);
936
+			} else {
937
+				$success = false;
938
+				$msg     = esc_html__(
939
+					'An error occurred. The venue could not be moved to the trash because a valid venue status was not not supplied.',
940
+					'event_espresso'
941
+				);
942
+				EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
943
+			}
944
+		} else {
945
+			$success = false;
946
+			$msg     = esc_html__(
947
+				'An error occurred. The venue could not be moved to the trash because a valid venue ID was not not supplied.',
948
+				'event_espresso'
949
+			);
950
+			EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
951
+		}
952
+		$action = $venue_status == 'trash' ? 'moved to the trash' : 'restored from the trash';
953
+
954
+		if ($redirect_after) {
955
+			$this->_redirect_after_action($success, 'Venue', $action, ['action' => 'default']);
956
+		}
957
+	}
958
+
959
+
960
+	/**
961
+	 * @throws EE_Error
962
+	 * @throws ReflectionException
963
+	 */
964
+	protected function _trash_or_restore_venues($venue_status = 'trash')
965
+	{
966
+		// clean status
967
+		$venue_status = sanitize_key($venue_status);
968
+		// grab status
969
+		if (! empty($venue_status)) {
970
+			$success = true;
971
+			// determine the event id and set to array.
972
+			$VNU_IDs = $this->request->getRequestParam('venue_id', [], 'int', true);
973
+			// loop thru events
974
+			foreach ($VNU_IDs as $VNU_ID) {
975
+				if ($VNU_ID = absint($VNU_ID)) {
976
+					$results = $this->_change_venue_status($VNU_ID, $venue_status);
977
+					$success = $results !== false ? $success : false;
978
+				} else {
979
+					$msg = sprintf(
980
+						esc_html__(
981
+							'An error occurred. Venue #%d could not be moved to the trash because a valid venue ID was not not supplied.',
982
+							'event_espresso'
983
+						),
984
+						$VNU_ID
985
+					);
986
+					EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
987
+					$success = false;
988
+				}
989
+			}
990
+		} else {
991
+			$success = false;
992
+			$msg     = esc_html__(
993
+				'An error occurred. The venue could not be moved to the trash because a valid venue status was not not supplied.',
994
+				'event_espresso'
995
+			);
996
+			EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
997
+		}
998
+		// in order to force a pluralized result message we need to send back a success status greater than 1
999
+		$success = $success ? 2 : false;
1000
+		$action  = $venue_status == 'trash' ? 'moved to the trash' : 'restored from the trash';
1001
+		$this->_redirect_after_action($success, 'Venues', $action, ['action' => 'default']);
1002
+	}
1003
+
1004
+
1005
+	/**
1006
+	 * _trash_or_restore_venues
1007
+	 *
1008
+	 * //todo this is pretty much the same as the corresponding change_event_status method in Events_Admin_Page.  We
1009
+	 * should probably abstract this up to the EE_Admin_Page_CPT (or even EE_Admin_Page) and make this a common method
1010
+	 * accepting a certain number of params.
1011
+	 *
1012
+	 * @access  private
1013
+	 * @param int    $VNU_ID
1014
+	 * @param string $venue_status
1015
+	 * @return bool
1016
+	 * @throws EE_Error
1017
+	 * @throws ReflectionException
1018
+	 */
1019
+	private function _change_venue_status($VNU_ID = 0, $venue_status = '')
1020
+	{
1021
+		// grab venue id
1022
+		if (! $VNU_ID) {
1023
+			$msg = esc_html__('An error occurred. No Venue ID or an invalid Venue ID was received.', 'event_espresso');
1024
+			EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
1025
+			return false;
1026
+		}
1027
+
1028
+		$this->_cpt_model_obj = EEM_Venue::instance()->get_one_by_ID($VNU_ID);
1029
+
1030
+		// clean status
1031
+		$venue_status = sanitize_key($venue_status);
1032
+		// grab status
1033
+		if (! $venue_status) {
1034
+			$msg = esc_html__(
1035
+				'An error occurred. No Venue Status or an invalid Venue Status was received.',
1036
+				'event_espresso'
1037
+			);
1038
+			EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
1039
+			return false;
1040
+		}
1041
+
1042
+		// was event trashed or restored ?
1043
+		switch ($venue_status) {
1044
+			case 'draft':
1045
+				$action = 'restored from the trash';
1046
+				$hook   = 'AHEE_venue_restored_from_trash';
1047
+				break;
1048
+			case 'trash':
1049
+				$action = 'moved to the trash';
1050
+				$hook   = 'AHEE_venue_moved_to_trash';
1051
+				break;
1052
+			default:
1053
+				$action = 'updated';
1054
+				$hook   = false;
1055
+		}
1056
+		// use class to change status
1057
+		$this->_cpt_model_obj->set_status($venue_status);
1058
+		$success = $this->_cpt_model_obj->save();
1059
+
1060
+		if ($success === false) {
1061
+			$msg = sprintf(esc_html__('An error occurred. The venue could not be %s.', 'event_espresso'), $action);
1062
+			EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
1063
+			return false;
1064
+		}
1065
+		if ($hook) {
1066
+			do_action($hook);
1067
+		}
1068
+		return true;
1069
+	}
1070
+
1071
+
1072
+	/**
1073
+	 * @param bool $redirect_after
1074
+	 * @return void
1075
+	 * @throws EE_Error
1076
+	 * @throws ReflectionException
1077
+	 */
1078
+	protected function _delete_venue($redirect_after = true)
1079
+	{
1080
+		// determine the venue id and set to array.
1081
+		$VNU_ID = $this->request->getRequestParam('VNU_ID', 0, 'int');
1082
+		$VNU_ID = $this->request->getRequestParam('post', $VNU_ID, 'int');
1083
+
1084
+		// loop thru venues
1085
+		if ($VNU_ID) {
1086
+			$success = $this->_delete_or_trash_venue($VNU_ID);
1087
+		} else {
1088
+			$success = false;
1089
+			$msg     = esc_html__(
1090
+				'An error occurred. An venue could not be deleted because a valid venue ID was not not supplied.',
1091
+				'event_espresso'
1092
+			);
1093
+			EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
1094
+		}
1095
+		if ($redirect_after) {
1096
+			$this->_redirect_after_action($success, 'Venue', 'deleted', ['action' => 'default']);
1097
+		}
1098
+	}
1099
+
1100
+
1101
+	/**
1102
+	 * @throws EE_Error
1103
+	 * @throws ReflectionException
1104
+	 */
1105
+	protected function _delete_venues()
1106
+	{
1107
+		$success = true;
1108
+		// determine the event id and set to array.
1109
+		$VNU_IDs = $this->request->getRequestParam('venue_id', [], 'int', true);
1110
+		// loop thru events
1111
+		foreach ($VNU_IDs as $VNU_ID) {
1112
+			if ($VNU_ID = absint($VNU_ID)) {
1113
+				$results = $this->_delete_or_trash_venue($VNU_ID);
1114
+				$success = $results !== false ? $success : false;
1115
+			} else {
1116
+				$success = false;
1117
+				$msg     = esc_html__(
1118
+					'An error occurred. An venue could not be deleted because a valid venue ID was not not supplied.',
1119
+					'event_espresso'
1120
+				);
1121
+				EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
1122
+			}
1123
+		}
1124
+		// in order to force a pluralized result message we need to send back a success status greater than 1
1125
+		$success = $success ? 2 : false;
1126
+		$this->_redirect_after_action(
1127
+			$success,
1128
+			esc_html__('Venues', 'event_espresso'),
1129
+			esc_html__('deleted', 'event_espresso'),
1130
+			['action' => 'default']
1131
+		);
1132
+	}
1133
+
1134
+
1135
+	// todo: put in parent
1136
+
1137
+
1138
+	/**
1139
+	 * @throws EE_Error
1140
+	 * @throws ReflectionException
1141
+	 */
1142
+	private function _delete_or_trash_venue($VNU_ID = false)
1143
+	{
1144
+		// grab event id
1145
+		if (! $VNU_ID = absint($VNU_ID)) {
1146
+			$msg = esc_html__('An error occurred. No Venue ID or an invalid Venue ID was received.', 'event_espresso');
1147
+			EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
1148
+			return false;
1149
+		}
1150
+
1151
+
1152
+		$venue = EEM_Venue::instance()->get_one_by_ID($VNU_ID);
1153
+		// first need to remove all term relationships
1154
+		$venue->_remove_relations('Term_Taxonomy');
1155
+		$success = $venue->delete_permanently();
1156
+		// did it all go as planned ?
1157
+		if ($success) {
1158
+			$msg = sprintf(esc_html__('Venue ID # %d has been deleted.', 'event_espresso'), $VNU_ID);
1159
+			EE_Error::add_success($msg);
1160
+		} else {
1161
+			$msg =
1162
+				sprintf(
1163
+					esc_html__('An error occurred. Venue ID # %d could not be deleted.', 'event_espresso'),
1164
+					$VNU_ID
1165
+				);
1166
+			EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
1167
+			return false;
1168
+		}
1169
+		do_action('AHEE__Venues_Admin_Page___delete_or_trash_venue__after_venue_deleted');
1170
+		return true;
1171
+	}
1172
+
1173
+
1174
+
1175
+
1176
+	/***********/
1177
+	/* QUERIES */
1178
+
1179
+
1180
+	public function get_venues($per_page = 10, $count = false)
1181
+	{
1182
+
1183
+		$orderby = $this->request->getRequestParam('orderby', '');
1184
+
1185
+		switch ($orderby) {
1186
+			case 'id':
1187
+				$orderby = 'VNU_ID';
1188
+				break;
1189
+
1190
+			case 'capacity':
1191
+				$orderby = 'VNU_capacity';
1192
+				break;
1193
+
1194
+			case 'city':
1195
+				$orderby = 'VNU_city';
1196
+				break;
1197
+
1198
+			default:
1199
+				$orderby = 'VNU_name';
1200
+		}
1201
+
1202
+		$sort         = $this->request->getRequestParam('order', 'ASC');
1203
+		$current_page = $this->request->getRequestParam('paged', 1, 'int');
1204
+		$per_page     = ! empty($per_page) ? $per_page : 10;
1205
+		$per_page     = $this->request->getRequestParam('perpage', $per_page, 'int');
1206
+
1207
+		$offset = ($current_page - 1) * $per_page;
1208
+		$limit  = [$offset, $per_page];
1209
+
1210
+		$category = $this->request->getRequestParam('category');
1211
+		$category = $category > 0 ? $category : null;
1212
+
1213
+		$where = [];
1214
+
1215
+		// only set initial status if it is in the incoming request.  Otherwise the "all" view display's all statuses.
1216
+		$status = $this->request->getRequestParam('status');
1217
+		if ($status && $status !== 'all') {
1218
+			$where['status'] = $status;
1219
+		}
1220
+
1221
+		$venue_status = $this->request->getRequestParam('venue_status');
1222
+		if ($venue_status) {
1223
+			$where['status'] = $venue_status;
1224
+		}
1225
+
1226
+
1227
+		if ($category) {
1228
+			$where['Term_Taxonomy.taxonomy'] = 'espresso_venue_categories';
1229
+			$where['Term_Taxonomy.term_id']  = $category;
1230
+		}
1231
+
1232
+
1233
+		if (! EE_Registry::instance()->CAP->current_user_can('ee_read_others_venues', 'get_venues')) {
1234
+			$where['VNU_wp_user'] = get_current_user_id();
1235
+		} else {
1236
+			if (! EE_Registry::instance()->CAP->current_user_can('ee_read_private_venues', 'get_venues')) {
1237
+				$where['OR'] = [
1238
+					'status*restrict_private' => ['!=', 'private'],
1239
+					'AND'                     => [
1240
+						'status*inclusive' => ['=', 'private'],
1241
+						'VNU_wp_user'      => get_current_user_id(),
1242
+					],
1243
+				];
1244
+			}
1245
+		}
1246
+
1247
+		$search_term = $this->request->getRequestParam('s');
1248
+		if ($search_term) {
1249
+			$search_term = '%' . $search_term . '%';
1250
+			$where['OR'] = [
1251
+				'VNU_name'               => ['LIKE', $search_term],
1252
+				'VNU_desc'               => ['LIKE', $search_term],
1253
+				'VNU_short_desc'         => ['LIKE', $search_term],
1254
+				'VNU_address'            => ['LIKE', $search_term],
1255
+				'VNU_address2'           => ['LIKE', $search_term],
1256
+				'VNU_city'               => ['LIKE', $search_term],
1257
+				'VNU_zip'                => ['LIKE', $search_term],
1258
+				'VNU_phone'              => ['LIKE', $search_term],
1259
+				'VNU_url'                => ['LIKE', $search_term],
1260
+				'VNU_virtual_phone'      => ['LIKE', $search_term],
1261
+				'VNU_virtual_url'        => ['LIKE', $search_term],
1262
+				'VNU_google_map_link'    => ['LIKE', $search_term],
1263
+				'Event.EVT_name'         => ['LIKE', $search_term],
1264
+				'Event.EVT_desc'         => ['LIKE', $search_term],
1265
+				'Event.EVT_phone'        => ['LIKE', $search_term],
1266
+				'Event.EVT_external_URL' => ['LIKE', $search_term],
1267
+			];
1268
+		}
1269
+
1270
+
1271
+		return $count
1272
+			? $this->_venue_model->count([$where], 'VNU_ID')
1273
+			: $this->_venue_model->get_all(
1274
+				[$where, 'limit' => $limit, 'order_by' => $orderby, 'order' => $sort]
1275
+			);
1276
+	}
1277
+
1278
+
1279
+
1280
+
1281
+	/** Venue Category Stuff **/
1282
+
1283
+	/**
1284
+	 * set the _category property with the category object for the loaded page.
1285
+	 *
1286
+	 * @access private
1287
+	 * @return void
1288
+	 */
1289
+	private function _set_category_object()
1290
+	{
1291
+		if (isset($this->_category->id) && ! empty($this->_category->id)) {
1292
+			return;
1293
+		} // already have the category object so get out.
1294
+
1295
+		// set default category object
1296
+		$this->_set_empty_category_object();
1297
+
1298
+		// only set if we've got an id
1299
+		$category_ID = $this->request->getRequestParam('VEN_CAT_ID', 0, 'int');
1300
+		if (! $category_ID) {
1301
+			return;
1302
+		}
1303
+
1304
+		$term = get_term($category_ID, 'espresso_venue_categories');
1305
+
1306
+
1307
+		if (! empty($term)) {
1308
+			$this->_category->category_name       = $term->name;
1309
+			$this->_category->category_identifier = $term->slug;
1310
+			$this->_category->category_desc       = $term->description;
1311
+			$this->_category->id                  = $term->term_id;
1312
+			$this->_category->parent              = $term->parent;
1313
+		}
1314
+	}
1315
+
1316
+
1317
+	private function _set_empty_category_object()
1318
+	{
1319
+		$this->_category                = new stdClass();
1320
+		$this->_category->category_name = $this->_category->category_identifier = $this->_category->category_desc = '';
1321
+		$this->_category->id            = $this->_category->parent = 0;
1322
+	}
1323
+
1324
+
1325
+	/**
1326
+	 * @throws EE_Error
1327
+	 */
1328
+	protected function _category_list_table()
1329
+	{
1330
+		do_action('AHEE_log', __FILE__, __FUNCTION__, '');
1331
+		$this->_admin_page_title .= ' ' . $this->get_action_link_or_button(
1332
+			'add_category',
1333
+			'add_category',
1334
+			[],
1335
+			'add-new-h2'
1336
+		);
1337
+		$this->_search_btn_label = esc_html__('Venue Categories', 'event_espresso');
1338
+		$this->display_admin_list_table_page_with_sidebar();
1339
+	}
1340
+
1341
+
1342
+	/**
1343
+	 * @throws EE_Error
1344
+	 */
1345
+	protected function _category_details($view)
1346
+	{
1347
+
1348
+		// load formatter helper
1349
+		// load field generator helper
1350
+
1351
+		$route = $view == 'edit' ? 'update_category' : 'insert_category';
1352
+		$this->_set_add_edit_form_tags($route);
1353
+
1354
+		$this->_set_category_object();
1355
+		$id = ! empty($this->_category->id) ? $this->_category->id : '';
1356
+
1357
+		$delete_action = 'delete_category';
1358
+
1359
+		$redirect = EE_Admin_Page::add_query_args_and_nonce(['action' => 'category_list'], $this->_admin_base_url);
1360
+
1361
+		$this->_set_publish_post_box_vars('VEN_CAT_ID', $id, $delete_action, $redirect);
1362
+
1363
+		// take care of contents
1364
+		$this->_template_args['admin_page_content'] = $this->_category_details_content();
1365
+		$this->display_admin_page_with_sidebar();
1366
+	}
1367
+
1368
+
1369
+	protected function _category_details_content()
1370
+	{
1371
+		$editor_args['category_desc'] = [
1372
+			'type'          => 'wp_editor',
1373
+			'value'         => EEH_Formatter::admin_format_content($this->_category->category_desc),
1374
+			'class'         => 'my_editor_custom',
1375
+			'wpeditor_args' => ['media_buttons' => false],
1376
+		];
1377
+		$_wp_editor                   = $this->_generate_admin_form_fields($editor_args, 'array');
1378
+
1379
+		$all_terms = get_terms(
1380
+			['espresso_venue_categories'],
1381
+			['hide_empty' => 0, 'exclude' => [$this->_category->id]]
1382
+		);
1383
+
1384
+		// setup category select for term parents.
1385
+		$category_select_values[] = [
1386
+			'text' => esc_html__('No Parent', 'event_espresso'),
1387
+			'id'   => 0,
1388
+		];
1389
+		foreach ($all_terms as $term) {
1390
+			$category_select_values[] = [
1391
+				'text' => $term->name,
1392
+				'id'   => $term->term_id,
1393
+			];
1394
+		}
1395
+
1396
+		$category_select = EEH_Form_Fields::select_input(
1397
+			'category_parent',
1398
+			$category_select_values,
1399
+			$this->_category->parent
1400
+		);
1401
+		$template_args   = [
1402
+			'category'                 => $this->_category,
1403
+			'category_select'          => $category_select,
1404
+			'unique_id_info_help_link' => $this->_get_help_tab_link('unique_id_info'),
1405
+			'category_desc_editor'     => $_wp_editor['category_desc']['field'],
1406
+			'disable'                  => '',
1407
+			'disabled_message'         => false,
1408
+		];
1409
+		$template        = EVENTS_TEMPLATE_PATH . 'event_category_details.template.php';
1410
+		return EEH_Template::display_template($template, $template_args, true);
1411
+	}
1412
+
1413
+
1414
+	/**
1415
+	 * @throws EE_Error
1416
+	 */
1417
+	protected function _delete_categories()
1418
+	{
1419
+		$category_ID  = $this->request->getRequestParam('category_id', 0, 'int');
1420
+		$category_IDs = $this->request->getRequestParam('VEN_CAT_ID', [$category_ID], 'int', true);
1421
+
1422
+		foreach ($category_IDs as $cat_id) {
1423
+			$this->_delete_category($cat_id);
1424
+		}
1425
+
1426
+		// doesn't matter what page we're coming from... we're going to the same place after delete.
1427
+		$query_args = [
1428
+			'action' => 'category_list',
1429
+		];
1430
+		$this->_redirect_after_action(0, '', '', $query_args);
1431
+	}
1432
+
1433
+
1434
+	protected function _delete_category($cat_id)
1435
+	{
1436
+		$cat_id = absint($cat_id);
1437
+		wp_delete_term($cat_id, 'espresso_venue_categories');
1438
+	}
1439
+
1440
+
1441
+	/**
1442
+	 * @throws EE_Error
1443
+	 */
1444
+	protected function _insert_or_update_category($new_category)
1445
+	{
1446
+
1447
+		$cat_id  = $new_category ? $this->_insert_category() : $this->_insert_category(true);
1448
+		$success = 0; // we already have a success message so lets not send another.
1449
+		if ($cat_id) {
1450
+			$query_args = [
1451
+				'action'     => 'edit_category',
1452
+				'VEN_CAT_ID' => $cat_id,
1453
+			];
1454
+		} else {
1455
+			$query_args = ['action' => 'add_category'];
1456
+		}
1457
+		$this->_redirect_after_action($success, '', '', $query_args, true);
1458
+	}
1459
+
1460
+
1461
+	private function _insert_category($update = false)
1462
+	{
1463
+		$category_ID     = $update ? $this->request->getRequestParam('VEN_CAT_ID', '', 'int') : '';
1464
+		$category_name   = $this->request->getRequestParam('category_name', '');
1465
+		$category_desc   = $this->request->getRequestParam('category_desc', '', 'html');
1466
+		$category_parent = $this->request->getRequestParam('category_parent', 0, 'int');
1467
+
1468
+		if (empty($category_name)) {
1469
+			$msg = esc_html__('You must add a name for the category.', 'event_espresso');
1470
+			EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
1471
+			return false;
1472
+		}
1473
+
1474
+
1475
+		$term_args = [
1476
+			'name'        => $category_name,
1477
+			'description' => $category_desc,
1478
+			'parent'      => $category_parent,
1479
+		];
1480
+
1481
+		$insert_ids = $update
1482
+			? wp_update_term($category_ID, 'espresso_venue_categories', $term_args)
1483
+			: wp_insert_term(
1484
+				$category_name,
1485
+				'espresso_venue_categories',
1486
+				$term_args
1487
+			);
1488
+
1489
+		if (! is_array($insert_ids)) {
1490
+			EE_Error::add_error(
1491
+				esc_html__('An error occurred and the category has not been saved to the database.', 'event_espresso'),
1492
+				__FILE__,
1493
+				__FUNCTION__,
1494
+				__LINE__
1495
+			);
1496
+		} else {
1497
+			$category_ID = $insert_ids['term_id'];
1498
+			EE_Error::add_success(
1499
+				sprintf(
1500
+					esc_html__('The category %s was successfully created', 'event_espresso'),
1501
+					$category_name
1502
+				)
1503
+			);
1504
+		}
1505
+
1506
+		return $category_ID;
1507
+	}
1508
+
1509
+
1510
+	/**
1511
+	 * TODO handle category exports()
1512
+	 *
1513
+	 * @return void
1514
+	 */
1515
+	protected function _categories_export()
1516
+	{
1517
+		// todo: I don't like doing this but it'll do until we modify EE_Export Class.
1518
+		$this->request->mergeRequestParams(
1519
+			[
1520
+				'export'       => 'report',
1521
+				'action'       => 'categories',
1522
+				'category_ids' => $this->request->getRequestParam('VEN_CAT_ID', 0, 'int'),
1523
+			]
1524
+		);
1525
+
1526
+		if (is_readable(EE_CLASSES . 'EE_Export.class.php')) {
1527
+			require_once(EE_CLASSES . 'EE_Export.class.php');
1528
+			$EE_Export = EE_Export::instance($this->request->requestParams());
1529
+			$EE_Export->export();
1530
+		}
1531
+	}
1532
+
1533
+
1534
+	protected function _import_categories()
1535
+	{
1536
+
1537
+		require_once(EE_CLASSES . 'EE_Import.class.php');
1538
+		EE_Import::instance()->import();
1539
+	}
1540
+
1541
+
1542
+	/**
1543
+	 * @throws EE_Error
1544
+	 */
1545
+	public function get_categories($per_page = 10, $current_page = 1, $count = false)
1546
+	{
1547
+
1548
+		// testing term stuff
1549
+		$orderby     = $this->request->getRequestParam('orderby', 'Term.term_id');
1550
+		$order       = $this->request->getRequestParam('order', 'DESC');
1551
+		$limit       = ($current_page - 1) * $per_page;
1552
+		$where       = ['taxonomy' => 'espresso_venue_categories'];
1553
+		$search_term = $this->request->getRequestParam('s');
1554
+		if ($search_term) {
1555
+			$search_term = '%' . $search_term . '%';
1556
+			$where['OR'] = [
1557
+				'Term.name'   => ['LIKE', $search_term],
1558
+				'description' => ['LIKE', $search_term],
1559
+			];
1560
+		}
1561
+
1562
+		$query_params = [
1563
+			$where,
1564
+			'order_by'   => [$orderby => $order],
1565
+			'limit'      => $limit . ',' . $per_page,
1566
+			'force_join' => ['Term'],
1567
+		];
1568
+
1569
+		return $count
1570
+			? EEM_Term_Taxonomy::instance()->count($query_params, 'term_id')
1571
+			: EEM_Term_Taxonomy::instance()->get_all($query_params);
1572
+	}
1573
+
1574
+
1575
+	/* end category stuff */
1576
+	/**************/
1577 1577
 }
Please login to merge, or discard this patch.
core/EE_System.core.php 1 patch
Indentation   +1358 added lines, -1358 removed lines patch added patch discarded remove patch
@@ -26,1362 +26,1362 @@
 block discarded – undo
26 26
  */
27 27
 final class EE_System implements ResettableInterface
28 28
 {
29
-    /**
30
-     * indicates this is a 'normal' request. Ie, not activation, nor upgrade, nor activation.
31
-     * So examples of this would be a normal GET request on the frontend or backend, or a POST, etc
32
-     */
33
-    const req_type_normal = 0;
34
-
35
-    /**
36
-     * Indicates this is a brand new installation of EE so we should install
37
-     * tables and default data etc
38
-     */
39
-    const req_type_new_activation = 1;
40
-
41
-    /**
42
-     * we've detected that EE has been reactivated (or EE was activated during maintenance mode,
43
-     * and we just exited maintenance mode). We MUST check the database is setup properly
44
-     * and that default data is setup too
45
-     */
46
-    const req_type_reactivation = 2;
47
-
48
-    /**
49
-     * indicates that EE has been upgraded since its previous request.
50
-     * We may have data migration scripts to call and will want to trigger maintenance mode
51
-     */
52
-    const req_type_upgrade = 3;
53
-
54
-    /**
55
-     * TODO  will detect that EE has been DOWNGRADED. We probably don't want to run in this case...
56
-     */
57
-    const req_type_downgrade = 4;
58
-
59
-    /**
60
-     * @deprecated since version 4.6.0.dev.006
61
-     * Now whenever a new_activation is detected the request type is still just
62
-     * new_activation (same for reactivation, upgrade, downgrade etc), but if we'r ein maintenance mode
63
-     * EE_System::initialize_db_if_no_migrations_required and EE_Addon::initialize_db_if_no_migrations_required
64
-     * will instead enqueue that EE plugin's db initialization for when we're taken out of maintenance mode.
65
-     * (Specifically, when the migration manager indicates migrations are finished
66
-     * EE_Data_Migration_Manager::initialize_db_for_enqueued_ee_plugins() will be called)
67
-     */
68
-    const req_type_activation_but_not_installed = 5;
69
-
70
-    /**
71
-     * option prefix for recording the activation history (like core's "espresso_db_update") of addons
72
-     */
73
-    const addon_activation_history_option_prefix = 'ee_addon_activation_history_';
74
-
75
-    /**
76
-     * @var EE_System $_instance
77
-     */
78
-    private static $_instance;
79
-
80
-    /**
81
-     * @var EE_Registry $registry
82
-     */
83
-    private $registry;
84
-
85
-    /**
86
-     * @var LoaderInterface $loader
87
-     */
88
-    private $loader;
89
-
90
-    /**
91
-     * @var EE_Capabilities $capabilities
92
-     */
93
-    private $capabilities;
94
-
95
-    /**
96
-     * @var RequestInterface $request
97
-     */
98
-    private $request;
99
-
100
-    /**
101
-     * @var EE_Maintenance_Mode $maintenance_mode
102
-     */
103
-    private $maintenance_mode;
104
-
105
-    /**
106
-     * Stores which type of request this is, options being one of the constants on EE_System starting with req_type_*.
107
-     * It can be a brand-new activation, a reactivation, an upgrade, a downgrade, or a normal request.
108
-     *
109
-     * @var int $_req_type
110
-     */
111
-    private $_req_type;
112
-
113
-    /**
114
-     * Whether or not there was a non-micro version change in EE core version during this request
115
-     *
116
-     * @var boolean $_major_version_change
117
-     */
118
-    private $_major_version_change = false;
119
-
120
-    /**
121
-     * A Context DTO dedicated solely to identifying the current request type.
122
-     *
123
-     * @var RequestTypeContextCheckerInterface $request_type
124
-     */
125
-    private $request_type;
126
-
127
-    /**
128
-     * @param EventEspresso\core\domain\services\custom_post_types\RegisterCustomPostTypes
129
-     */
130
-    private $register_custom_post_types;
131
-
132
-    /**
133
-     * @param EventEspresso\core\domain\services\custom_post_types\RegisterCustomTaxonomies
134
-     */
135
-    private $register_custom_taxonomies;
136
-
137
-    /**
138
-     * @param EventEspresso\core\domain\services\custom_post_types\RegisterCustomTaxonomyTerms
139
-     */
140
-    private $register_custom_taxonomy_terms;
141
-
142
-    /**
143
-     * @singleton method used to instantiate class object
144
-     * @param EE_Registry|null         $registry
145
-     * @param LoaderInterface|null     $loader
146
-     * @param RequestInterface|null    $request
147
-     * @param EE_Maintenance_Mode|null $maintenance_mode
148
-     * @return EE_System
149
-     */
150
-    public static function instance(
151
-        EE_Registry $registry = null,
152
-        LoaderInterface $loader = null,
153
-        RequestInterface $request = null,
154
-        EE_Maintenance_Mode $maintenance_mode = null
155
-    ) {
156
-        // check if class object is instantiated
157
-        if (! self::$_instance instanceof EE_System) {
158
-            self::$_instance = new self($registry, $loader, $request, $maintenance_mode);
159
-        }
160
-        return self::$_instance;
161
-    }
162
-
163
-
164
-    /**
165
-     * resets the instance and returns it
166
-     *
167
-     * @return EE_System
168
-     */
169
-    public static function reset()
170
-    {
171
-        self::$_instance->_req_type = null;
172
-        // make sure none of the old hooks are left hanging around
173
-        remove_all_actions('AHEE__EE_System__perform_activations_upgrades_and_migrations');
174
-        // we need to reset the migration manager in order for it to detect DMSs properly
175
-        EE_Data_Migration_Manager::reset();
176
-        self::instance()->detect_activations_or_upgrades();
177
-        self::instance()->perform_activations_upgrades_and_migrations();
178
-        return self::instance();
179
-    }
180
-
181
-
182
-    /**
183
-     * sets hooks for running rest of system
184
-     * provides "AHEE__EE_System__construct__complete" hook for EE Addons to use as their starting point
185
-     * starting EE Addons from any other point may lead to problems
186
-     *
187
-     * @param EE_Registry         $registry
188
-     * @param LoaderInterface     $loader
189
-     * @param RequestInterface    $request
190
-     * @param EE_Maintenance_Mode $maintenance_mode
191
-     */
192
-    private function __construct(
193
-        EE_Registry $registry,
194
-        LoaderInterface $loader,
195
-        RequestInterface $request,
196
-        EE_Maintenance_Mode $maintenance_mode
197
-    ) {
198
-        $this->registry = $registry;
199
-        $this->loader = $loader;
200
-        $this->request = $request;
201
-        $this->maintenance_mode = $maintenance_mode;
202
-        do_action('AHEE__EE_System__construct__begin', $this);
203
-        add_action(
204
-            'AHEE__EE_Bootstrap__load_espresso_addons',
205
-            array($this, 'loadCapabilities'),
206
-            5
207
-        );
208
-        add_action(
209
-            'AHEE__EE_Bootstrap__load_espresso_addons',
210
-            array($this, 'loadCommandBus'),
211
-            7
212
-        );
213
-        add_action(
214
-            'AHEE__EE_Bootstrap__load_espresso_addons',
215
-            array($this, 'loadPluginApi'),
216
-            9
217
-        );
218
-        // allow addons to load first so that they can register autoloaders, set hooks for running DMS's, etc
219
-        add_action(
220
-            'AHEE__EE_Bootstrap__load_espresso_addons',
221
-            array($this, 'load_espresso_addons')
222
-        );
223
-        // when an ee addon is activated, we want to call the core hook(s) again
224
-        // because the newly-activated addon didn't get a chance to run at all
225
-        add_action('activate_plugin', array($this, 'load_espresso_addons'), 1);
226
-        // detect whether install or upgrade
227
-        add_action(
228
-            'AHEE__EE_Bootstrap__detect_activations_or_upgrades',
229
-            array($this, 'detect_activations_or_upgrades'),
230
-            3
231
-        );
232
-        // load EE_Config, EE_Textdomain, etc
233
-        add_action(
234
-            'AHEE__EE_Bootstrap__load_core_configuration',
235
-            array($this, 'load_core_configuration'),
236
-            5
237
-        );
238
-        // load specifications for matching routes to current request
239
-        add_action(
240
-            'AHEE__EE_Bootstrap__load_core_configuration',
241
-            array($this, 'loadRouteMatchSpecifications')
242
-        );
243
-        // load specifications for custom post types
244
-        add_action(
245
-            'AHEE__EE_Bootstrap__load_core_configuration',
246
-            array($this, 'loadCustomPostTypes')
247
-        );
248
-        // load EE_Config, EE_Textdomain, etc
249
-        add_action(
250
-            'AHEE__EE_Bootstrap__register_shortcodes_modules_and_widgets',
251
-            array($this, 'register_shortcodes_modules_and_widgets'),
252
-            7
253
-        );
254
-        // you wanna get going? I wanna get going... let's get going!
255
-        add_action(
256
-            'AHEE__EE_Bootstrap__brew_espresso',
257
-            array($this, 'brew_espresso'),
258
-            9
259
-        );
260
-        // other housekeeping
261
-        // exclude EE critical pages from wp_list_pages
262
-        add_filter(
263
-            'wp_list_pages_excludes',
264
-            array($this, 'remove_pages_from_wp_list_pages'),
265
-            10
266
-        );
267
-        // ALL EE Addons should use the following hook point to attach their initial setup too
268
-        // it's extremely important for EE Addons to register any class autoloaders so that they can be available when the EE_Config loads
269
-        do_action('AHEE__EE_System__construct__complete', $this);
270
-    }
271
-
272
-
273
-    /**
274
-     * load and setup EE_Capabilities
275
-     *
276
-     * @return void
277
-     * @throws EE_Error
278
-     */
279
-    public function loadCapabilities()
280
-    {
281
-        $this->capabilities = $this->loader->getShared('EE_Capabilities');
282
-        add_action(
283
-            'AHEE__EE_Capabilities__init_caps__before_initialization',
284
-            function () {
285
-                LoaderFactory::getLoader()->getShared('EE_Payment_Method_Manager');
286
-            }
287
-        );
288
-    }
289
-
290
-
291
-    /**
292
-     * create and cache the CommandBus, and also add middleware
293
-     * The CapChecker middleware requires the use of EE_Capabilities
294
-     * which is why we need to load the CommandBus after Caps are set up
295
-     * CommandBus middleware operate FIFO - First In First Out
296
-     * so LocateMovedCommands will run first in order to return any new commands
297
-     *
298
-     * @return void
299
-     * @throws EE_Error
300
-     */
301
-    public function loadCommandBus()
302
-    {
303
-        $this->loader->getShared(
304
-            'CommandBusInterface',
305
-            array(
306
-                null,
307
-                apply_filters(
308
-                    'FHEE__EE_Load_Espresso_Core__handle_request__CommandBus_middleware',
309
-                    array(
310
-                        $this->loader->getShared('EventEspresso\core\services\commands\middleware\LocateMovedCommands'),
311
-                        $this->loader->getShared('EventEspresso\core\services\commands\middleware\CapChecker'),
312
-                        $this->loader->getShared('EventEspresso\core\services\commands\middleware\AddActionHook'),
313
-                    )
314
-                ),
315
-            )
316
-        );
317
-    }
318
-
319
-
320
-    /**
321
-     * @return void
322
-     * @throws EE_Error
323
-     */
324
-    public function loadPluginApi()
325
-    {
326
-        // set autoloaders for all of the classes implementing EEI_Plugin_API
327
-        // which provide helpers for EE plugin authors to more easily register certain components with EE.
328
-        EEH_Autoloader::instance()->register_autoloaders_for_each_file_in_folder(EE_LIBRARIES . 'plugin_api');
329
-    }
330
-
331
-
332
-    /**
333
-     * @param string $addon_name
334
-     * @param string $version_constant
335
-     * @param string $min_version_required
336
-     * @param string $load_callback
337
-     * @param string $plugin_file_constant
338
-     * @return void
339
-     */
340
-    private function deactivateIncompatibleAddon(
341
-        $addon_name,
342
-        $version_constant,
343
-        $min_version_required,
344
-        $load_callback,
345
-        $plugin_file_constant
346
-    ) {
347
-        if (! defined($version_constant)) {
348
-            return;
349
-        }
350
-        $addon_version = constant($version_constant);
351
-        if ($addon_version && version_compare($addon_version, $min_version_required, '<')) {
352
-            remove_action('AHEE__EE_System__load_espresso_addons', $load_callback);
353
-            if (! function_exists('deactivate_plugins')) {
354
-                require_once ABSPATH . 'wp-admin/includes/plugin.php';
355
-            }
356
-            deactivate_plugins(plugin_basename(constant($plugin_file_constant)));
357
-            $this->request->unSetRequestParams(['activate', 'activate-multi'], true);
358
-            EE_Error::add_error(
359
-                sprintf(
360
-                    esc_html__(
361
-                        '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.',
362
-                        'event_espresso'
363
-                    ),
364
-                    $addon_name,
365
-                    $min_version_required
366
-                ),
367
-                __FILE__,
368
-                __FUNCTION__ . "({$addon_name})",
369
-                __LINE__
370
-            );
371
-            EE_Error::get_notices(false, true);
372
-        }
373
-    }
374
-
375
-
376
-    /**
377
-     * load_espresso_addons
378
-     * allow addons to load first so that they can set hooks for running DMS's, etc
379
-     * this is hooked into both:
380
-     *    'AHEE__EE_Bootstrap__load_core_configuration'
381
-     *        which runs during the WP 'plugins_loaded' action at priority 5
382
-     *    and the WP 'activate_plugin' hook point
383
-     *
384
-     * @access public
385
-     * @return void
386
-     */
387
-    public function load_espresso_addons()
388
-    {
389
-        $this->deactivateIncompatibleAddon(
390
-            'Wait Lists',
391
-            'EE_WAIT_LISTS_VERSION',
392
-            '1.0.0.beta.074',
393
-            'load_espresso_wait_lists',
394
-            'EE_WAIT_LISTS_PLUGIN_FILE'
395
-        );
396
-        $this->deactivateIncompatibleAddon(
397
-            'Automated Upcoming Event Notifications',
398
-            'EE_AUTOMATED_UPCOMING_EVENT_NOTIFICATION_VERSION',
399
-            '1.0.0.beta.091',
400
-            'load_espresso_automated_upcoming_event_notification',
401
-            'EE_AUTOMATED_UPCOMING_EVENT_NOTIFICATION_PLUGIN_FILE'
402
-        );
403
-        do_action('AHEE__EE_System__load_espresso_addons');
404
-        // if the WP API basic auth plugin isn't already loaded, load it now.
405
-        // We want it for mobile apps. Just include the entire plugin
406
-        // also, don't load the basic auth when a plugin is getting activated, because
407
-        // it could be the basic auth plugin, and it doesn't check if its methods are already defined
408
-        // and causes a fatal error
409
-        if (
410
-            ($this->request->isWordPressApi() || $this->request->isApi())
411
-            && $this->request->getRequestParam('activate') !== 'true'
412
-            && ! function_exists('json_basic_auth_handler')
413
-            && ! function_exists('json_basic_auth_error')
414
-            && ! in_array(
415
-                $this->request->getRequestParam('action'),
416
-                array('activate', 'activate-selected'),
417
-                true
418
-            )
419
-        ) {
420
-            include_once EE_THIRD_PARTY . 'wp-api-basic-auth/basic-auth.php';
421
-        }
422
-        do_action('AHEE__EE_System__load_espresso_addons__complete');
423
-    }
424
-
425
-
426
-    /**
427
-     * detect_activations_or_upgrades
428
-     * Checks for activation or upgrade of core first;
429
-     * then also checks if any registered addons have been activated or upgraded
430
-     * This is hooked into 'AHEE__EE_Bootstrap__detect_activations_or_upgrades'
431
-     * which runs during the WP 'plugins_loaded' action at priority 3
432
-     *
433
-     * @access public
434
-     * @return void
435
-     */
436
-    public function detect_activations_or_upgrades()
437
-    {
438
-        // first off: let's make sure to handle core
439
-        $this->detect_if_activation_or_upgrade();
440
-        foreach ($this->registry->addons as $addon) {
441
-            if ($addon instanceof EE_Addon) {
442
-                // detect teh request type for that addon
443
-                $addon->detect_req_type();
444
-            }
445
-        }
446
-    }
447
-
448
-
449
-    /**
450
-     * detect_if_activation_or_upgrade
451
-     * Takes care of detecting whether this is a brand new install or code upgrade,
452
-     * and either setting up the DB or setting up maintenance mode etc.
453
-     *
454
-     * @access public
455
-     * @return void
456
-     */
457
-    public function detect_if_activation_or_upgrade()
458
-    {
459
-        do_action('AHEE__EE_System___detect_if_activation_or_upgrade__begin');
460
-        // check if db has been updated, or if its a brand-new installation
461
-        $espresso_db_update = $this->fix_espresso_db_upgrade_option();
462
-        $request_type = $this->detect_req_type($espresso_db_update);
463
-        // EEH_Debug_Tools::printr( $request_type, '$request_type', __FILE__, __LINE__ );
464
-        switch ($request_type) {
465
-            case EE_System::req_type_new_activation:
466
-                do_action('AHEE__EE_System__detect_if_activation_or_upgrade__new_activation');
467
-                $this->_handle_core_version_change($espresso_db_update);
468
-                break;
469
-            case EE_System::req_type_reactivation:
470
-                do_action('AHEE__EE_System__detect_if_activation_or_upgrade__reactivation');
471
-                $this->_handle_core_version_change($espresso_db_update);
472
-                break;
473
-            case EE_System::req_type_upgrade:
474
-                do_action('AHEE__EE_System__detect_if_activation_or_upgrade__upgrade');
475
-                // migrations may be required now that we've upgraded
476
-                $this->maintenance_mode->set_maintenance_mode_if_db_old();
477
-                $this->_handle_core_version_change($espresso_db_update);
478
-                break;
479
-            case EE_System::req_type_downgrade:
480
-                do_action('AHEE__EE_System__detect_if_activation_or_upgrade__downgrade');
481
-                // its possible migrations are no longer required
482
-                $this->maintenance_mode->set_maintenance_mode_if_db_old();
483
-                $this->_handle_core_version_change($espresso_db_update);
484
-                break;
485
-            case EE_System::req_type_normal:
486
-            default:
487
-                break;
488
-        }
489
-        do_action('AHEE__EE_System__detect_if_activation_or_upgrade__complete');
490
-    }
491
-
492
-
493
-    /**
494
-     * Updates the list of installed versions and sets hooks for
495
-     * initializing the database later during the request
496
-     *
497
-     * @param array $espresso_db_update
498
-     */
499
-    private function _handle_core_version_change($espresso_db_update)
500
-    {
501
-        $this->update_list_of_installed_versions($espresso_db_update);
502
-        // get ready to verify the DB is ok (provided we aren't in maintenance mode, of course)
503
-        add_action(
504
-            'AHEE__EE_System__perform_activations_upgrades_and_migrations',
505
-            array($this, 'initialize_db_if_no_migrations_required')
506
-        );
507
-    }
508
-
509
-
510
-    /**
511
-     * standardizes the wp option 'espresso_db_upgrade' which actually stores
512
-     * information about what versions of EE have been installed and activated,
513
-     * NOT necessarily the state of the database
514
-     *
515
-     * @param mixed $espresso_db_update           the value of the WordPress option.
516
-     *                                            If not supplied, fetches it from the options table
517
-     * @return array the correct value of 'espresso_db_upgrade', after saving it, if it needed correction
518
-     */
519
-    private function fix_espresso_db_upgrade_option($espresso_db_update = null)
520
-    {
521
-        do_action('FHEE__EE_System__manage_fix_espresso_db_upgrade_option__begin', $espresso_db_update);
522
-        if (! $espresso_db_update) {
523
-            $espresso_db_update = get_option('espresso_db_update');
524
-        }
525
-        // check that option is an array
526
-        if (! is_array($espresso_db_update)) {
527
-            // if option is FALSE, then it never existed
528
-            if ($espresso_db_update === false) {
529
-                // make $espresso_db_update an array and save option with autoload OFF
530
-                $espresso_db_update = array();
531
-                add_option('espresso_db_update', $espresso_db_update, '', 'no');
532
-            } else {
533
-                // option is NOT FALSE but also is NOT an array, so make it an array and save it
534
-                $espresso_db_update = array($espresso_db_update => array());
535
-                update_option('espresso_db_update', $espresso_db_update);
536
-            }
537
-        } else {
538
-            $corrected_db_update = array();
539
-            // if IS an array, but is it an array where KEYS are version numbers, and values are arrays?
540
-            foreach ($espresso_db_update as $should_be_version_string => $should_be_array) {
541
-                if (is_int($should_be_version_string) && ! is_array($should_be_array)) {
542
-                    // the key is an int, and the value IS NOT an array
543
-                    // so it must be numerically-indexed, where values are versions installed...
544
-                    // fix it!
545
-                    $version_string = $should_be_array;
546
-                    $corrected_db_update[ $version_string ] = array('unknown-date');
547
-                } else {
548
-                    // ok it checks out
549
-                    $corrected_db_update[ $should_be_version_string ] = $should_be_array;
550
-                }
551
-            }
552
-            $espresso_db_update = $corrected_db_update;
553
-            update_option('espresso_db_update', $espresso_db_update);
554
-        }
555
-        do_action('FHEE__EE_System__manage_fix_espresso_db_upgrade_option__complete', $espresso_db_update);
556
-        return $espresso_db_update;
557
-    }
558
-
559
-
560
-    /**
561
-     * Does the traditional work of setting up the plugin's database and adding default data.
562
-     * If migration script/process did not exist, this is what would happen on every activation/reactivation/upgrade.
563
-     * NOTE: if we're in maintenance mode (which would be the case if we detect there are data
564
-     * migration scripts that need to be run and a version change happens), enqueues core for database initialization,
565
-     * so that it will be done when migrations are finished
566
-     *
567
-     * @param boolean $initialize_addons_too if true, we double-check addons' database tables etc too;
568
-     * @param boolean $verify_schema         if true will re-check the database tables have the correct schema.
569
-     *                                       This is a resource-intensive job
570
-     *                                       so we prefer to only do it when necessary
571
-     * @return void
572
-     * @throws EE_Error
573
-     */
574
-    public function initialize_db_if_no_migrations_required($initialize_addons_too = false, $verify_schema = true)
575
-    {
576
-        $request_type = $this->detect_req_type();
577
-        // only initialize system if we're not in maintenance mode.
578
-        if ($this->maintenance_mode->level() !== EE_Maintenance_Mode::level_2_complete_maintenance) {
579
-            /** @var EventEspresso\core\domain\services\custom_post_types\RewriteRules $rewrite_rules */
580
-            $rewrite_rules = $this->loader->getShared(
581
-                'EventEspresso\core\domain\services\custom_post_types\RewriteRules'
582
-            );
583
-            $rewrite_rules->flush();
584
-            if ($verify_schema) {
585
-                EEH_Activation::initialize_db_and_folders();
586
-            }
587
-            EEH_Activation::initialize_db_content();
588
-            EEH_Activation::system_initialization();
589
-            if ($initialize_addons_too) {
590
-                $this->initialize_addons();
591
-            }
592
-        } else {
593
-            EE_Data_Migration_Manager::instance()->enqueue_db_initialization_for('Core');
594
-        }
595
-        if (
596
-            $request_type === EE_System::req_type_new_activation
597
-            || $request_type === EE_System::req_type_reactivation
598
-            || (
599
-                $request_type === EE_System::req_type_upgrade
600
-                && $this->is_major_version_change()
601
-            )
602
-        ) {
603
-            add_action('AHEE__EE_System__initialize_last', array($this, 'redirect_to_about_ee'), 9);
604
-        }
605
-    }
606
-
607
-
608
-    /**
609
-     * Initializes the db for all registered addons
610
-     *
611
-     * @throws EE_Error
612
-     */
613
-    public function initialize_addons()
614
-    {
615
-        // foreach registered addon, make sure its db is up-to-date too
616
-        foreach ($this->registry->addons as $addon) {
617
-            if ($addon instanceof EE_Addon) {
618
-                $addon->initialize_db_if_no_migrations_required();
619
-            }
620
-        }
621
-    }
622
-
623
-
624
-    /**
625
-     * Adds the current code version to the saved wp option which stores a list of all ee versions ever installed.
626
-     *
627
-     * @param    array  $version_history
628
-     * @param    string $current_version_to_add version to be added to the version history
629
-     * @return    boolean success as to whether or not this option was changed
630
-     */
631
-    public function update_list_of_installed_versions($version_history = null, $current_version_to_add = null)
632
-    {
633
-        if (! $version_history) {
634
-            $version_history = $this->fix_espresso_db_upgrade_option($version_history);
635
-        }
636
-        if ($current_version_to_add === null) {
637
-            $current_version_to_add = espresso_version();
638
-        }
639
-        $version_history[ $current_version_to_add ][] = date('Y-m-d H:i:s', time());
640
-        // re-save
641
-        return update_option('espresso_db_update', $version_history);
642
-    }
643
-
644
-
645
-    /**
646
-     * Detects if the current version indicated in the has existed in the list of
647
-     * previously-installed versions of EE (espresso_db_update). Does NOT modify it (ie, no side-effect)
648
-     *
649
-     * @param array $espresso_db_update array from the wp option stored under the name 'espresso_db_update'.
650
-     *                                  If not supplied, fetches it from the options table.
651
-     *                                  Also, caches its result so later parts of the code can also know whether
652
-     *                                  there's been an update or not. This way we can add the current version to
653
-     *                                  espresso_db_update, but still know if this is a new install or not
654
-     * @return int one of the constants on EE_System::req_type_
655
-     */
656
-    public function detect_req_type($espresso_db_update = null)
657
-    {
658
-        if ($this->_req_type === null) {
659
-            $espresso_db_update = ! empty($espresso_db_update)
660
-                ? $espresso_db_update
661
-                : $this->fix_espresso_db_upgrade_option();
662
-            $this->_req_type = EE_System::detect_req_type_given_activation_history(
663
-                $espresso_db_update,
664
-                'ee_espresso_activation',
665
-                espresso_version()
666
-            );
667
-            $this->_major_version_change = $this->_detect_major_version_change($espresso_db_update);
668
-            $this->request->setIsActivation($this->_req_type !== EE_System::req_type_normal);
669
-        }
670
-        return $this->_req_type;
671
-    }
672
-
673
-
674
-    /**
675
-     * Returns whether or not there was a non-micro version change (ie, change in either
676
-     * the first or second number in the version. Eg 4.9.0.rc.001 to 4.10.0.rc.000,
677
-     * but not 4.9.0.rc.0001 to 4.9.1.rc.0001
678
-     *
679
-     * @param $activation_history
680
-     * @return bool
681
-     */
682
-    private function _detect_major_version_change($activation_history)
683
-    {
684
-        $previous_version = EE_System::_get_most_recently_active_version_from_activation_history($activation_history);
685
-        $previous_version_parts = explode('.', $previous_version);
686
-        $current_version_parts = explode('.', espresso_version());
687
-        return isset($previous_version_parts[0], $previous_version_parts[1], $current_version_parts[0], $current_version_parts[1])
688
-               && ($previous_version_parts[0] !== $current_version_parts[0]
689
-                   || $previous_version_parts[1] !== $current_version_parts[1]
690
-               );
691
-    }
692
-
693
-
694
-    /**
695
-     * Returns true if either the major or minor version of EE changed during this request.
696
-     * 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
697
-     *
698
-     * @return bool
699
-     */
700
-    public function is_major_version_change()
701
-    {
702
-        return $this->_major_version_change;
703
-    }
704
-
705
-
706
-    /**
707
-     * Determines the request type for any ee addon, given three piece of info: the current array of activation
708
-     * histories (for core that' 'espresso_db_update' wp option); the name of the WordPress option which is temporarily
709
-     * set upon activation of the plugin (for core it's 'ee_espresso_activation'); and the version that this plugin was
710
-     * just activated to (for core that will always be espresso_version())
711
-     *
712
-     * @param array  $activation_history_for_addon     the option's value which stores the activation history for this
713
-     *                                                 ee plugin. for core that's 'espresso_db_update'
714
-     * @param string $activation_indicator_option_name the name of the WordPress option that is temporarily set to
715
-     *                                                 indicate that this plugin was just activated
716
-     * @param string $version_to_upgrade_to            the version that was just upgraded to (for core that will be
717
-     *                                                 espresso_version())
718
-     * @return int one of the constants on EE_System::req_type_*
719
-     */
720
-    public static function detect_req_type_given_activation_history(
721
-        $activation_history_for_addon,
722
-        $activation_indicator_option_name,
723
-        $version_to_upgrade_to
724
-    ) {
725
-        $version_is_higher = self::_new_version_is_higher($activation_history_for_addon, $version_to_upgrade_to);
726
-        if ($activation_history_for_addon) {
727
-            // it exists, so this isn't a completely new install
728
-            // check if this version already in that list of previously installed versions
729
-            if (! isset($activation_history_for_addon[ $version_to_upgrade_to ])) {
730
-                // it a version we haven't seen before
731
-                if ($version_is_higher === 1) {
732
-                    $req_type = EE_System::req_type_upgrade;
733
-                } else {
734
-                    $req_type = EE_System::req_type_downgrade;
735
-                }
736
-                delete_option($activation_indicator_option_name);
737
-            } else {
738
-                // its not an update. maybe a reactivation?
739
-                if (get_option($activation_indicator_option_name, false)) {
740
-                    if ($version_is_higher === -1) {
741
-                        $req_type = EE_System::req_type_downgrade;
742
-                    } elseif ($version_is_higher === 0) {
743
-                        // we've seen this version before, but it's an activation. must be a reactivation
744
-                        $req_type = EE_System::req_type_reactivation;
745
-                    } else {// $version_is_higher === 1
746
-                        $req_type = EE_System::req_type_upgrade;
747
-                    }
748
-                    delete_option($activation_indicator_option_name);
749
-                } else {
750
-                    // we've seen this version before and the activation indicate doesn't show it was just activated
751
-                    if ($version_is_higher === -1) {
752
-                        $req_type = EE_System::req_type_downgrade;
753
-                    } elseif ($version_is_higher === 0) {
754
-                        // we've seen this version before and it's not an activation. its normal request
755
-                        $req_type = EE_System::req_type_normal;
756
-                    } else {// $version_is_higher === 1
757
-                        $req_type = EE_System::req_type_upgrade;
758
-                    }
759
-                }
760
-            }
761
-        } else {
762
-            // brand new install
763
-            $req_type = EE_System::req_type_new_activation;
764
-            delete_option($activation_indicator_option_name);
765
-        }
766
-        return $req_type;
767
-    }
768
-
769
-
770
-    /**
771
-     * Detects if the $version_to_upgrade_to is higher than the most recent version in
772
-     * the $activation_history_for_addon
773
-     *
774
-     * @param array  $activation_history_for_addon (keys are versions, values are arrays of times activated,
775
-     *                                             sometimes containing 'unknown-date'
776
-     * @param string $version_to_upgrade_to        (current version)
777
-     * @return int results of version_compare( $version_to_upgrade_to, $most_recently_active_version ).
778
-     *                                             ie, -1 if $version_to_upgrade_to is LOWER (downgrade);
779
-     *                                             0 if $version_to_upgrade_to MATCHES (reactivation or normal request);
780
-     *                                             1 if $version_to_upgrade_to is HIGHER (upgrade) ;
781
-     */
782
-    private static function _new_version_is_higher($activation_history_for_addon, $version_to_upgrade_to)
783
-    {
784
-        // find the most recently-activated version
785
-        $most_recently_active_version =
786
-            EE_System::_get_most_recently_active_version_from_activation_history($activation_history_for_addon);
787
-        return version_compare($version_to_upgrade_to, $most_recently_active_version);
788
-    }
789
-
790
-
791
-    /**
792
-     * Gets the most recently active version listed in the activation history,
793
-     * and if none are found (ie, it's a brand new install) returns '0.0.0.dev.000'.
794
-     *
795
-     * @param array $activation_history  (keys are versions, values are arrays of times activated,
796
-     *                                   sometimes containing 'unknown-date'
797
-     * @return string
798
-     */
799
-    private static function _get_most_recently_active_version_from_activation_history($activation_history)
800
-    {
801
-        $most_recently_active_version_activation = '1970-01-01 00:00:00';
802
-        $most_recently_active_version = '0.0.0.dev.000';
803
-        if (is_array($activation_history)) {
804
-            foreach ($activation_history as $version => $times_activated) {
805
-                // check there is a record of when this version was activated. Otherwise,
806
-                // mark it as unknown
807
-                if (! $times_activated) {
808
-                    $times_activated = array('unknown-date');
809
-                }
810
-                if (is_string($times_activated)) {
811
-                    $times_activated = array($times_activated);
812
-                }
813
-                foreach ($times_activated as $an_activation) {
814
-                    if (
815
-                        $an_activation !== 'unknown-date'
816
-                        && $an_activation
817
-                           > $most_recently_active_version_activation
818
-                    ) {
819
-                        $most_recently_active_version = $version;
820
-                        $most_recently_active_version_activation = $an_activation === 'unknown-date'
821
-                            ? '1970-01-01 00:00:00'
822
-                            : $an_activation;
823
-                    }
824
-                }
825
-            }
826
-        }
827
-        return $most_recently_active_version;
828
-    }
829
-
830
-
831
-    /**
832
-     * This redirects to the about EE page after activation
833
-     *
834
-     * @return void
835
-     */
836
-    public function redirect_to_about_ee()
837
-    {
838
-        $notices = EE_Error::get_notices(false);
839
-        // if current user is an admin and it's not an ajax or rest request
840
-        if (
841
-            ! isset($notices['errors'])
842
-            && $this->request->isAdmin()
843
-            && apply_filters(
844
-                'FHEE__EE_System__redirect_to_about_ee__do_redirect',
845
-                $this->capabilities->current_user_can('manage_options', 'espresso_about_default')
846
-            )
847
-        ) {
848
-            $query_params = array('page' => 'espresso_about');
849
-            if (EE_System::instance()->detect_req_type() === EE_System::req_type_new_activation) {
850
-                $query_params['new_activation'] = true;
851
-            }
852
-            if (EE_System::instance()->detect_req_type() === EE_System::req_type_reactivation) {
853
-                $query_params['reactivation'] = true;
854
-            }
855
-            $url = add_query_arg($query_params, admin_url('admin.php'));
856
-            wp_safe_redirect($url);
857
-            exit();
858
-        }
859
-    }
860
-
861
-
862
-    /**
863
-     * load_core_configuration
864
-     * this is hooked into 'AHEE__EE_Bootstrap__load_core_configuration'
865
-     * which runs during the WP 'plugins_loaded' action at priority 5
866
-     *
867
-     * @return void
868
-     * @throws ReflectionException
869
-     * @throws Exception
870
-     */
871
-    public function load_core_configuration()
872
-    {
873
-        do_action('AHEE__EE_System__load_core_configuration__begin', $this);
874
-        $this->loader->getShared('EE_Load_Textdomain');
875
-        // load textdomain
876
-        EE_Load_Textdomain::load_textdomain();
877
-        // load caf stuff a chance to play during the activation process too.
878
-        $this->_maybe_brew_regular();
879
-        // load and setup EE_Config and EE_Network_Config
880
-        $config = $this->loader->getShared('EE_Config');
881
-        $this->loader->getShared('EE_Network_Config');
882
-        // setup autoloaders
883
-        // enable logging?
884
-        $this->loader->getShared('EventEspresso\core\services\orm\TrashLogger');
885
-        if ($config->admin->use_remote_logging) {
886
-            $this->loader->getShared('EE_Log');
887
-        }
888
-        // check for activation errors
889
-        $activation_errors = get_option('ee_plugin_activation_errors', false);
890
-        if ($activation_errors) {
891
-            EE_Error::add_error($activation_errors, __FILE__, __FUNCTION__, __LINE__);
892
-            update_option('ee_plugin_activation_errors', false);
893
-        }
894
-        // get model names
895
-        $this->_parse_model_names();
896
-        // configure custom post type definitions
897
-        $this->loader->getShared('EventEspresso\core\domain\entities\custom_post_types\CustomTaxonomyDefinitions');
898
-        $this->loader->getShared('EventEspresso\core\domain\entities\custom_post_types\CustomPostTypeDefinitions');
899
-        do_action('AHEE__EE_System__load_core_configuration__complete', $this);
900
-    }
901
-
902
-
903
-    /**
904
-     * cycles through all of the models/*.model.php files, and assembles an array of model names
905
-     *
906
-     * @return void
907
-     * @throws ReflectionException
908
-     */
909
-    private function _parse_model_names()
910
-    {
911
-        // get all the files in the EE_MODELS folder that end in .model.php
912
-        $models = glob(EE_MODELS . '*.model.php');
913
-        $model_names = array();
914
-        $non_abstract_db_models = array();
915
-        foreach ($models as $model) {
916
-            // get model classname
917
-            $classname = EEH_File::get_classname_from_filepath_with_standard_filename($model);
918
-            $short_name = str_replace('EEM_', '', $classname);
919
-            $reflectionClass = new ReflectionClass($classname);
920
-            if ($reflectionClass->isSubclassOf('EEM_Base') && ! $reflectionClass->isAbstract()) {
921
-                $non_abstract_db_models[ $short_name ] = $classname;
922
-            }
923
-            $model_names[ $short_name ] = $classname;
924
-        }
925
-        $this->registry->models = apply_filters('FHEE__EE_System__parse_model_names', $model_names);
926
-        $this->registry->non_abstract_db_models = apply_filters(
927
-            'FHEE__EE_System__parse_implemented_model_names',
928
-            $non_abstract_db_models
929
-        );
930
-    }
931
-
932
-
933
-    /**
934
-     * The purpose of this method is to simply check for a file named "caffeinated/brewing_regular.php" for any hooks
935
-     * that need to be setup before our EE_System launches.
936
-     *
937
-     * @return void
938
-     * @throws DomainException
939
-     * @throws InvalidArgumentException
940
-     * @throws InvalidDataTypeException
941
-     * @throws InvalidInterfaceException
942
-     * @throws InvalidClassException
943
-     * @throws InvalidFilePathException
944
-     */
945
-    private function _maybe_brew_regular()
946
-    {
947
-        /** @var Domain $domain */
948
-        $domain = DomainFactory::getShared(
949
-            new FullyQualifiedName(
950
-                'EventEspresso\core\domain\Domain'
951
-            ),
952
-            array(
953
-                new FilePath(EVENT_ESPRESSO_MAIN_FILE),
954
-                Version::fromString(espresso_version()),
955
-            )
956
-        );
957
-        if ($domain->isCaffeinated()) {
958
-            require_once EE_CAFF_PATH . 'brewing_regular.php';
959
-        }
960
-    }
961
-
962
-
963
-    /**
964
-     * @since 4.9.71.p
965
-     * @throws Exception
966
-     */
967
-    public function loadRouteMatchSpecifications()
968
-    {
969
-        try {
970
-            $this->loader->getShared(
971
-                'EventEspresso\core\services\route_match\RouteMatchSpecificationManager'
972
-            );
973
-        } catch (Exception $exception) {
974
-            new ExceptionStackTraceDisplay($exception);
975
-        }
976
-        do_action('AHEE__EE_System__loadRouteMatchSpecifications');
977
-    }
978
-
979
-
980
-    /**
981
-     * loading CPT related classes earlier so that their definitions are available
982
-     * but not performing any actual registration with WP core until load_CPTs_and_session() is called
983
-     *
984
-     * @since   4.10.21.p
985
-     */
986
-    public function loadCustomPostTypes()
987
-    {
988
-        $this->register_custom_taxonomies = $this->loader->getShared(
989
-            'EventEspresso\core\domain\services\custom_post_types\RegisterCustomTaxonomies'
990
-        );
991
-        $this->register_custom_post_types = $this->loader->getShared(
992
-            'EventEspresso\core\domain\services\custom_post_types\RegisterCustomPostTypes'
993
-        );
994
-        $this->register_custom_taxonomy_terms = $this->loader->getShared(
995
-            'EventEspresso\core\domain\services\custom_post_types\RegisterCustomTaxonomyTerms'
996
-        );
997
-        // integrate WP_Query with the EE models
998
-        $this->loader->getShared('EE_CPT_Strategy');
999
-        // load legacy EE_Request_Handler in case add-ons still need it
1000
-        $this->loader->getShared('EE_Request_Handler');
1001
-    }
1002
-
1003
-
1004
-    /**
1005
-     * register_shortcodes_modules_and_widgets
1006
-     * generate lists of shortcodes and modules, then verify paths and classes
1007
-     * This is hooked into 'AHEE__EE_Bootstrap__register_shortcodes_modules_and_widgets'
1008
-     * which runs during the WP 'plugins_loaded' action at priority 7
1009
-     *
1010
-     * @access public
1011
-     * @return void
1012
-     * @throws Exception
1013
-     */
1014
-    public function register_shortcodes_modules_and_widgets()
1015
-    {
1016
-        if ($this->request->isFrontend() || $this->request->isIframe() || $this->request->isAjax()) {
1017
-            // load, register, and add shortcodes the new way
1018
-            $this->loader->getShared('EventEspresso\core\services\shortcodes\ShortcodesManager');
1019
-        }
1020
-        do_action('AHEE__EE_System__register_shortcodes_modules_and_widgets');
1021
-        // check for addons using old hook point
1022
-        if (has_action('AHEE__EE_System__register_shortcodes_modules_and_addons')) {
1023
-            $this->_incompatible_addon_error();
1024
-        }
1025
-    }
1026
-
1027
-
1028
-    /**
1029
-     * _incompatible_addon_error
1030
-     *
1031
-     * @access public
1032
-     * @return void
1033
-     */
1034
-    private function _incompatible_addon_error()
1035
-    {
1036
-        // get array of classes hooking into here
1037
-        $class_names = EEH_Class_Tools::get_class_names_for_all_callbacks_on_hook(
1038
-            'AHEE__EE_System__register_shortcodes_modules_and_addons'
1039
-        );
1040
-        if (! empty($class_names)) {
1041
-            $msg = esc_html__(
1042
-                'The following plugins, addons, or modules appear to be incompatible with this version of Event Espresso and were automatically deactivated to avoid fatal errors:',
1043
-                'event_espresso'
1044
-            );
1045
-            $msg .= '<ul>';
1046
-            foreach ($class_names as $class_name) {
1047
-                $msg .= '<li><b>Event Espresso - '
1048
-                        . str_replace(
1049
-                            array('EE_', 'EEM_', 'EED_', 'EES_', 'EEW_'),
1050
-                            '',
1051
-                            $class_name
1052
-                        ) . '</b></li>';
1053
-            }
1054
-            $msg .= '</ul>';
1055
-            $msg .= esc_html__(
1056
-                'Compatibility issues can be avoided and/or resolved by keeping addons and plugins updated to the latest version.',
1057
-                'event_espresso'
1058
-            );
1059
-            // save list of incompatible addons to wp-options for later use
1060
-            add_option('ee_incompatible_addons', $class_names, '', 'no');
1061
-            if (is_admin()) {
1062
-                EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
1063
-            }
1064
-        }
1065
-    }
1066
-
1067
-
1068
-    /**
1069
-     * brew_espresso
1070
-     * begins the process of setting hooks for initializing EE in the correct order
1071
-     * This is happening on the 'AHEE__EE_Bootstrap__brew_espresso' hook point
1072
-     * which runs during the WP 'plugins_loaded' action at priority 9
1073
-     *
1074
-     * @return void
1075
-     */
1076
-    public function brew_espresso()
1077
-    {
1078
-        do_action('AHEE__EE_System__brew_espresso__begin', $this);
1079
-        // load some final core systems
1080
-        add_action('init', array($this, 'set_hooks_for_core'), 1);
1081
-        add_action('init', array($this, 'perform_activations_upgrades_and_migrations'), 3);
1082
-        add_action('init', array($this, 'load_CPTs_and_session'), 5);
1083
-        add_action('init', array($this, 'load_controllers'), 7);
1084
-        add_action('init', array($this, 'core_loaded_and_ready'), 9);
1085
-        add_action('init', array($this, 'initialize'), 10);
1086
-        add_action('init', array($this, 'initialize_last'), 100);
1087
-        if (is_admin() && apply_filters('FHEE__EE_System__brew_espresso__load_pue', true)) {
1088
-            // pew pew pew
1089
-            $this->loader->getShared('EventEspresso\core\services\licensing\LicenseService');
1090
-            do_action('AHEE__EE_System__brew_espresso__after_pue_init');
1091
-        }
1092
-        do_action('AHEE__EE_System__brew_espresso__complete', $this);
1093
-    }
1094
-
1095
-
1096
-    /**
1097
-     *    set_hooks_for_core
1098
-     *
1099
-     * @access public
1100
-     * @return    void
1101
-     * @throws EE_Error
1102
-     */
1103
-    public function set_hooks_for_core()
1104
-    {
1105
-        $this->_deactivate_incompatible_addons();
1106
-        do_action('AHEE__EE_System__set_hooks_for_core');
1107
-        $this->loader->getShared('EventEspresso\core\domain\values\session\SessionLifespan');
1108
-        // caps need to be initialized on every request so that capability maps are set.
1109
-        // @see https://events.codebasehq.com/projects/event-espresso/tickets/8674
1110
-        $this->registry->CAP->init_caps();
1111
-    }
1112
-
1113
-
1114
-    /**
1115
-     * Using the information gathered in EE_System::_incompatible_addon_error,
1116
-     * deactivates any addons considered incompatible with the current version of EE
1117
-     */
1118
-    private function _deactivate_incompatible_addons()
1119
-    {
1120
-        $incompatible_addons = get_option('ee_incompatible_addons', array());
1121
-        if (! empty($incompatible_addons)) {
1122
-            $active_plugins = get_option('active_plugins', array());
1123
-            foreach ($active_plugins as $active_plugin) {
1124
-                foreach ($incompatible_addons as $incompatible_addon) {
1125
-                    if (strpos($active_plugin, $incompatible_addon) !== false) {
1126
-                        $this->request->unSetRequestParams(['activate'], true);
1127
-                        espresso_deactivate_plugin($active_plugin);
1128
-                    }
1129
-                }
1130
-            }
1131
-        }
1132
-    }
1133
-
1134
-
1135
-    /**
1136
-     *    perform_activations_upgrades_and_migrations
1137
-     *
1138
-     * @access public
1139
-     * @return    void
1140
-     */
1141
-    public function perform_activations_upgrades_and_migrations()
1142
-    {
1143
-        do_action('AHEE__EE_System__perform_activations_upgrades_and_migrations');
1144
-    }
1145
-
1146
-
1147
-    /**
1148
-     * @return void
1149
-     * @throws DomainException
1150
-     */
1151
-    public function load_CPTs_and_session()
1152
-    {
1153
-        do_action('AHEE__EE_System__load_CPTs_and_session__start');
1154
-        $this->register_custom_taxonomies->registerCustomTaxonomies();
1155
-        $this->register_custom_post_types->registerCustomPostTypes();
1156
-        $this->register_custom_taxonomy_terms->registerCustomTaxonomyTerms();
1157
-        // load legacy Custom Post Types and Taxonomies
1158
-        $this->loader->getShared('EE_Register_CPTs');
1159
-        do_action('AHEE__EE_System__load_CPTs_and_session__complete');
1160
-    }
1161
-
1162
-
1163
-    /**
1164
-     * load_controllers
1165
-     * this is the best place to load any additional controllers that needs access to EE core.
1166
-     * it is expected that all basic core EE systems, that are not dependant on the current request are loaded at this
1167
-     * time
1168
-     *
1169
-     * @access public
1170
-     * @return void
1171
-     */
1172
-    public function load_controllers()
1173
-    {
1174
-        do_action('AHEE__EE_System__load_controllers__start');
1175
-        // let's get it started
1176
-        if (
1177
-            ! $this->maintenance_mode->level()
1178
-            && ($this->request->isFrontend() || $this->request->isFrontAjax())
1179
-        ) {
1180
-            do_action('AHEE__EE_System__load_controllers__load_front_controllers');
1181
-            $this->loader->getShared('EE_Front_Controller');
1182
-        } elseif ($this->request->isAdmin() || $this->request->isAdminAjax()) {
1183
-            do_action('AHEE__EE_System__load_controllers__load_admin_controllers');
1184
-            $this->loader->getShared('EE_Admin');
1185
-        } elseif ($this->request->isWordPressHeartbeat()) {
1186
-            $this->loader->getShared('EventEspresso\core\domain\services\admin\ajax\WordpressHeartbeat');
1187
-        }
1188
-        do_action('AHEE__EE_System__load_controllers__complete');
1189
-    }
1190
-
1191
-
1192
-    /**
1193
-     * core_loaded_and_ready
1194
-     * all of the basic EE core should be loaded at this point and available regardless of M-Mode
1195
-     *
1196
-     * @access public
1197
-     * @return void
1198
-     * @throws Exception
1199
-     */
1200
-    public function core_loaded_and_ready()
1201
-    {
1202
-        if (
1203
-            $this->request->isAdmin()
1204
-            || $this->request->isFrontend()
1205
-            || $this->request->isIframe()
1206
-            || $this->request->isWordPressApi()
1207
-        ) {
1208
-            try {
1209
-                $this->loader->getShared('EventEspresso\core\services\assets\I18nRegistry', [[]]);
1210
-                $this->loader->getShared('EventEspresso\core\services\assets\Registry');
1211
-                $this->loader->getShared('EventEspresso\core\domain\services\assets\CoreAssetManager');
1212
-                if ($this->canLoadBlocks()) {
1213
-                    $this->loader->getShared(
1214
-                        'EventEspresso\core\services\editor\BlockRegistrationManager'
1215
-                    );
1216
-                }
1217
-            } catch (Exception $exception) {
1218
-                new ExceptionStackTraceDisplay($exception);
1219
-            }
1220
-        }
1221
-        if (
1222
-            $this->request->isAdmin()
1223
-            || $this->request->isEeAjax()
1224
-            || $this->request->isFrontend()
1225
-        ) {
1226
-            $this->loader->getShared('EE_Session');
1227
-        }
1228
-        do_action('AHEE__EE_System__core_loaded_and_ready');
1229
-        // always load template tags, because it's faster than checking if it's a front-end request, and many page
1230
-        // builders require these even on the front-end
1231
-        require_once EE_PUBLIC . 'template_tags.php';
1232
-        do_action('AHEE__EE_System__set_hooks_for_shortcodes_modules_and_addons');
1233
-    }
1234
-
1235
-
1236
-    /**
1237
-     * initialize
1238
-     * this is the best place to begin initializing client code
1239
-     *
1240
-     * @access public
1241
-     * @return void
1242
-     */
1243
-    public function initialize()
1244
-    {
1245
-        do_action('AHEE__EE_System__initialize');
1246
-        add_filter(
1247
-            'safe_style_css',
1248
-            function ($styles) {
1249
-                $styles[] = 'display';
1250
-                $styles[] = 'visibility';
1251
-                $styles[] = 'position';
1252
-                $styles[] = 'top';
1253
-                $styles[] = 'right';
1254
-                $styles[] = 'bottom';
1255
-                $styles[] = 'left';
1256
-                $styles[] = 'resize';
1257
-                $styles[] = 'max-width';
1258
-                $styles[] = 'max-height';
1259
-                return $styles;
1260
-            }
1261
-        );
1262
-    }
1263
-
1264
-
1265
-    /**
1266
-     * initialize_last
1267
-     * this is run really late during the WP init hook point, and ensures that mostly everything else that needs to
1268
-     * initialize has done so
1269
-     *
1270
-     * @access public
1271
-     * @return void
1272
-     */
1273
-    public function initialize_last()
1274
-    {
1275
-        do_action('AHEE__EE_System__initialize_last');
1276
-        /** @var EventEspresso\core\domain\services\custom_post_types\RewriteRules $rewrite_rules */
1277
-        $rewrite_rules = $this->loader->getShared(
1278
-            'EventEspresso\core\domain\services\custom_post_types\RewriteRules'
1279
-        );
1280
-        $rewrite_rules->flushRewriteRules();
1281
-        add_action('admin_bar_init', array($this, 'addEspressoToolbar'));
1282
-        if (
1283
-            ($this->request->isAjax() || $this->request->isAdmin())
1284
-            && $this->maintenance_mode->models_can_query()
1285
-        ) {
1286
-            $this->loader->getShared('EventEspresso\core\services\privacy\export\PersonalDataExporterManager');
1287
-            $this->loader->getShared('EventEspresso\core\services\privacy\erasure\PersonalDataEraserManager');
1288
-        }
1289
-    }
1290
-
1291
-
1292
-    /**
1293
-     * @return void
1294
-     * @throws EE_Error
1295
-     */
1296
-    public function addEspressoToolbar()
1297
-    {
1298
-        $this->loader->getShared(
1299
-            'EventEspresso\core\domain\services\admin\AdminToolBar',
1300
-            array($this->registry->CAP)
1301
-        );
1302
-    }
1303
-
1304
-
1305
-    /**
1306
-     * do_not_cache
1307
-     * sets no cache headers and defines no cache constants for WP plugins
1308
-     *
1309
-     * @access public
1310
-     * @return void
1311
-     */
1312
-    public static function do_not_cache()
1313
-    {
1314
-        // set no cache constants
1315
-        if (! defined('DONOTCACHEPAGE')) {
1316
-            define('DONOTCACHEPAGE', true);
1317
-        }
1318
-        if (! defined('DONOTCACHCEOBJECT')) {
1319
-            define('DONOTCACHCEOBJECT', true);
1320
-        }
1321
-        if (! defined('DONOTCACHEDB')) {
1322
-            define('DONOTCACHEDB', true);
1323
-        }
1324
-        // add no cache headers
1325
-        add_action('send_headers', array('EE_System', 'nocache_headers'), 10);
1326
-        // plus a little extra for nginx and Google Chrome
1327
-        add_filter('nocache_headers', array('EE_System', 'extra_nocache_headers'), 10, 1);
1328
-        // prevent browsers from prefetching of the rel='next' link, because it may contain content that interferes with the registration process
1329
-        remove_action('wp_head', 'adjacent_posts_rel_link_wp_head');
1330
-    }
1331
-
1332
-
1333
-    /**
1334
-     *    extra_nocache_headers
1335
-     *
1336
-     * @access    public
1337
-     * @param $headers
1338
-     * @return    array
1339
-     */
1340
-    public static function extra_nocache_headers($headers)
1341
-    {
1342
-        // for NGINX
1343
-        $headers['X-Accel-Expires'] = 0;
1344
-        // plus extra for Google Chrome since it doesn't seem to respect "no-cache", but WILL respect "no-store"
1345
-        $headers['Cache-Control'] = 'no-store, no-cache, must-revalidate, max-age=0';
1346
-        return $headers;
1347
-    }
1348
-
1349
-
1350
-    /**
1351
-     *    nocache_headers
1352
-     *
1353
-     * @access    public
1354
-     * @return    void
1355
-     */
1356
-    public static function nocache_headers()
1357
-    {
1358
-        nocache_headers();
1359
-    }
1360
-
1361
-
1362
-    /**
1363
-     * simply hooks into "wp_list_pages_exclude" filter (for wp_list_pages method) and makes sure EE critical pages are
1364
-     * never returned with the function.
1365
-     *
1366
-     * @param  array $exclude_array any existing pages being excluded are in this array.
1367
-     * @return array
1368
-     */
1369
-    public function remove_pages_from_wp_list_pages($exclude_array)
1370
-    {
1371
-        return array_merge($exclude_array, $this->registry->CFG->core->get_critical_pages_array());
1372
-    }
1373
-
1374
-
1375
-    /**
1376
-     * Return whether blocks can be registered/loaded or not.
1377
-     * @return bool
1378
-     */
1379
-    private function canLoadBlocks()
1380
-    {
1381
-        return apply_filters('FHEE__EE_System__canLoadBlocks', true)
1382
-               && function_exists('register_block_type')
1383
-               // don't load blocks if in the Divi page builder editor context
1384
-               // @see https://github.com/eventespresso/event-espresso-core/issues/814
1385
-               && ! $this->request->getRequestParam('et_fb', false);
1386
-    }
29
+	/**
30
+	 * indicates this is a 'normal' request. Ie, not activation, nor upgrade, nor activation.
31
+	 * So examples of this would be a normal GET request on the frontend or backend, or a POST, etc
32
+	 */
33
+	const req_type_normal = 0;
34
+
35
+	/**
36
+	 * Indicates this is a brand new installation of EE so we should install
37
+	 * tables and default data etc
38
+	 */
39
+	const req_type_new_activation = 1;
40
+
41
+	/**
42
+	 * we've detected that EE has been reactivated (or EE was activated during maintenance mode,
43
+	 * and we just exited maintenance mode). We MUST check the database is setup properly
44
+	 * and that default data is setup too
45
+	 */
46
+	const req_type_reactivation = 2;
47
+
48
+	/**
49
+	 * indicates that EE has been upgraded since its previous request.
50
+	 * We may have data migration scripts to call and will want to trigger maintenance mode
51
+	 */
52
+	const req_type_upgrade = 3;
53
+
54
+	/**
55
+	 * TODO  will detect that EE has been DOWNGRADED. We probably don't want to run in this case...
56
+	 */
57
+	const req_type_downgrade = 4;
58
+
59
+	/**
60
+	 * @deprecated since version 4.6.0.dev.006
61
+	 * Now whenever a new_activation is detected the request type is still just
62
+	 * new_activation (same for reactivation, upgrade, downgrade etc), but if we'r ein maintenance mode
63
+	 * EE_System::initialize_db_if_no_migrations_required and EE_Addon::initialize_db_if_no_migrations_required
64
+	 * will instead enqueue that EE plugin's db initialization for when we're taken out of maintenance mode.
65
+	 * (Specifically, when the migration manager indicates migrations are finished
66
+	 * EE_Data_Migration_Manager::initialize_db_for_enqueued_ee_plugins() will be called)
67
+	 */
68
+	const req_type_activation_but_not_installed = 5;
69
+
70
+	/**
71
+	 * option prefix for recording the activation history (like core's "espresso_db_update") of addons
72
+	 */
73
+	const addon_activation_history_option_prefix = 'ee_addon_activation_history_';
74
+
75
+	/**
76
+	 * @var EE_System $_instance
77
+	 */
78
+	private static $_instance;
79
+
80
+	/**
81
+	 * @var EE_Registry $registry
82
+	 */
83
+	private $registry;
84
+
85
+	/**
86
+	 * @var LoaderInterface $loader
87
+	 */
88
+	private $loader;
89
+
90
+	/**
91
+	 * @var EE_Capabilities $capabilities
92
+	 */
93
+	private $capabilities;
94
+
95
+	/**
96
+	 * @var RequestInterface $request
97
+	 */
98
+	private $request;
99
+
100
+	/**
101
+	 * @var EE_Maintenance_Mode $maintenance_mode
102
+	 */
103
+	private $maintenance_mode;
104
+
105
+	/**
106
+	 * Stores which type of request this is, options being one of the constants on EE_System starting with req_type_*.
107
+	 * It can be a brand-new activation, a reactivation, an upgrade, a downgrade, or a normal request.
108
+	 *
109
+	 * @var int $_req_type
110
+	 */
111
+	private $_req_type;
112
+
113
+	/**
114
+	 * Whether or not there was a non-micro version change in EE core version during this request
115
+	 *
116
+	 * @var boolean $_major_version_change
117
+	 */
118
+	private $_major_version_change = false;
119
+
120
+	/**
121
+	 * A Context DTO dedicated solely to identifying the current request type.
122
+	 *
123
+	 * @var RequestTypeContextCheckerInterface $request_type
124
+	 */
125
+	private $request_type;
126
+
127
+	/**
128
+	 * @param EventEspresso\core\domain\services\custom_post_types\RegisterCustomPostTypes
129
+	 */
130
+	private $register_custom_post_types;
131
+
132
+	/**
133
+	 * @param EventEspresso\core\domain\services\custom_post_types\RegisterCustomTaxonomies
134
+	 */
135
+	private $register_custom_taxonomies;
136
+
137
+	/**
138
+	 * @param EventEspresso\core\domain\services\custom_post_types\RegisterCustomTaxonomyTerms
139
+	 */
140
+	private $register_custom_taxonomy_terms;
141
+
142
+	/**
143
+	 * @singleton method used to instantiate class object
144
+	 * @param EE_Registry|null         $registry
145
+	 * @param LoaderInterface|null     $loader
146
+	 * @param RequestInterface|null    $request
147
+	 * @param EE_Maintenance_Mode|null $maintenance_mode
148
+	 * @return EE_System
149
+	 */
150
+	public static function instance(
151
+		EE_Registry $registry = null,
152
+		LoaderInterface $loader = null,
153
+		RequestInterface $request = null,
154
+		EE_Maintenance_Mode $maintenance_mode = null
155
+	) {
156
+		// check if class object is instantiated
157
+		if (! self::$_instance instanceof EE_System) {
158
+			self::$_instance = new self($registry, $loader, $request, $maintenance_mode);
159
+		}
160
+		return self::$_instance;
161
+	}
162
+
163
+
164
+	/**
165
+	 * resets the instance and returns it
166
+	 *
167
+	 * @return EE_System
168
+	 */
169
+	public static function reset()
170
+	{
171
+		self::$_instance->_req_type = null;
172
+		// make sure none of the old hooks are left hanging around
173
+		remove_all_actions('AHEE__EE_System__perform_activations_upgrades_and_migrations');
174
+		// we need to reset the migration manager in order for it to detect DMSs properly
175
+		EE_Data_Migration_Manager::reset();
176
+		self::instance()->detect_activations_or_upgrades();
177
+		self::instance()->perform_activations_upgrades_and_migrations();
178
+		return self::instance();
179
+	}
180
+
181
+
182
+	/**
183
+	 * sets hooks for running rest of system
184
+	 * provides "AHEE__EE_System__construct__complete" hook for EE Addons to use as their starting point
185
+	 * starting EE Addons from any other point may lead to problems
186
+	 *
187
+	 * @param EE_Registry         $registry
188
+	 * @param LoaderInterface     $loader
189
+	 * @param RequestInterface    $request
190
+	 * @param EE_Maintenance_Mode $maintenance_mode
191
+	 */
192
+	private function __construct(
193
+		EE_Registry $registry,
194
+		LoaderInterface $loader,
195
+		RequestInterface $request,
196
+		EE_Maintenance_Mode $maintenance_mode
197
+	) {
198
+		$this->registry = $registry;
199
+		$this->loader = $loader;
200
+		$this->request = $request;
201
+		$this->maintenance_mode = $maintenance_mode;
202
+		do_action('AHEE__EE_System__construct__begin', $this);
203
+		add_action(
204
+			'AHEE__EE_Bootstrap__load_espresso_addons',
205
+			array($this, 'loadCapabilities'),
206
+			5
207
+		);
208
+		add_action(
209
+			'AHEE__EE_Bootstrap__load_espresso_addons',
210
+			array($this, 'loadCommandBus'),
211
+			7
212
+		);
213
+		add_action(
214
+			'AHEE__EE_Bootstrap__load_espresso_addons',
215
+			array($this, 'loadPluginApi'),
216
+			9
217
+		);
218
+		// allow addons to load first so that they can register autoloaders, set hooks for running DMS's, etc
219
+		add_action(
220
+			'AHEE__EE_Bootstrap__load_espresso_addons',
221
+			array($this, 'load_espresso_addons')
222
+		);
223
+		// when an ee addon is activated, we want to call the core hook(s) again
224
+		// because the newly-activated addon didn't get a chance to run at all
225
+		add_action('activate_plugin', array($this, 'load_espresso_addons'), 1);
226
+		// detect whether install or upgrade
227
+		add_action(
228
+			'AHEE__EE_Bootstrap__detect_activations_or_upgrades',
229
+			array($this, 'detect_activations_or_upgrades'),
230
+			3
231
+		);
232
+		// load EE_Config, EE_Textdomain, etc
233
+		add_action(
234
+			'AHEE__EE_Bootstrap__load_core_configuration',
235
+			array($this, 'load_core_configuration'),
236
+			5
237
+		);
238
+		// load specifications for matching routes to current request
239
+		add_action(
240
+			'AHEE__EE_Bootstrap__load_core_configuration',
241
+			array($this, 'loadRouteMatchSpecifications')
242
+		);
243
+		// load specifications for custom post types
244
+		add_action(
245
+			'AHEE__EE_Bootstrap__load_core_configuration',
246
+			array($this, 'loadCustomPostTypes')
247
+		);
248
+		// load EE_Config, EE_Textdomain, etc
249
+		add_action(
250
+			'AHEE__EE_Bootstrap__register_shortcodes_modules_and_widgets',
251
+			array($this, 'register_shortcodes_modules_and_widgets'),
252
+			7
253
+		);
254
+		// you wanna get going? I wanna get going... let's get going!
255
+		add_action(
256
+			'AHEE__EE_Bootstrap__brew_espresso',
257
+			array($this, 'brew_espresso'),
258
+			9
259
+		);
260
+		// other housekeeping
261
+		// exclude EE critical pages from wp_list_pages
262
+		add_filter(
263
+			'wp_list_pages_excludes',
264
+			array($this, 'remove_pages_from_wp_list_pages'),
265
+			10
266
+		);
267
+		// ALL EE Addons should use the following hook point to attach their initial setup too
268
+		// it's extremely important for EE Addons to register any class autoloaders so that they can be available when the EE_Config loads
269
+		do_action('AHEE__EE_System__construct__complete', $this);
270
+	}
271
+
272
+
273
+	/**
274
+	 * load and setup EE_Capabilities
275
+	 *
276
+	 * @return void
277
+	 * @throws EE_Error
278
+	 */
279
+	public function loadCapabilities()
280
+	{
281
+		$this->capabilities = $this->loader->getShared('EE_Capabilities');
282
+		add_action(
283
+			'AHEE__EE_Capabilities__init_caps__before_initialization',
284
+			function () {
285
+				LoaderFactory::getLoader()->getShared('EE_Payment_Method_Manager');
286
+			}
287
+		);
288
+	}
289
+
290
+
291
+	/**
292
+	 * create and cache the CommandBus, and also add middleware
293
+	 * The CapChecker middleware requires the use of EE_Capabilities
294
+	 * which is why we need to load the CommandBus after Caps are set up
295
+	 * CommandBus middleware operate FIFO - First In First Out
296
+	 * so LocateMovedCommands will run first in order to return any new commands
297
+	 *
298
+	 * @return void
299
+	 * @throws EE_Error
300
+	 */
301
+	public function loadCommandBus()
302
+	{
303
+		$this->loader->getShared(
304
+			'CommandBusInterface',
305
+			array(
306
+				null,
307
+				apply_filters(
308
+					'FHEE__EE_Load_Espresso_Core__handle_request__CommandBus_middleware',
309
+					array(
310
+						$this->loader->getShared('EventEspresso\core\services\commands\middleware\LocateMovedCommands'),
311
+						$this->loader->getShared('EventEspresso\core\services\commands\middleware\CapChecker'),
312
+						$this->loader->getShared('EventEspresso\core\services\commands\middleware\AddActionHook'),
313
+					)
314
+				),
315
+			)
316
+		);
317
+	}
318
+
319
+
320
+	/**
321
+	 * @return void
322
+	 * @throws EE_Error
323
+	 */
324
+	public function loadPluginApi()
325
+	{
326
+		// set autoloaders for all of the classes implementing EEI_Plugin_API
327
+		// which provide helpers for EE plugin authors to more easily register certain components with EE.
328
+		EEH_Autoloader::instance()->register_autoloaders_for_each_file_in_folder(EE_LIBRARIES . 'plugin_api');
329
+	}
330
+
331
+
332
+	/**
333
+	 * @param string $addon_name
334
+	 * @param string $version_constant
335
+	 * @param string $min_version_required
336
+	 * @param string $load_callback
337
+	 * @param string $plugin_file_constant
338
+	 * @return void
339
+	 */
340
+	private function deactivateIncompatibleAddon(
341
+		$addon_name,
342
+		$version_constant,
343
+		$min_version_required,
344
+		$load_callback,
345
+		$plugin_file_constant
346
+	) {
347
+		if (! defined($version_constant)) {
348
+			return;
349
+		}
350
+		$addon_version = constant($version_constant);
351
+		if ($addon_version && version_compare($addon_version, $min_version_required, '<')) {
352
+			remove_action('AHEE__EE_System__load_espresso_addons', $load_callback);
353
+			if (! function_exists('deactivate_plugins')) {
354
+				require_once ABSPATH . 'wp-admin/includes/plugin.php';
355
+			}
356
+			deactivate_plugins(plugin_basename(constant($plugin_file_constant)));
357
+			$this->request->unSetRequestParams(['activate', 'activate-multi'], true);
358
+			EE_Error::add_error(
359
+				sprintf(
360
+					esc_html__(
361
+						'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.',
362
+						'event_espresso'
363
+					),
364
+					$addon_name,
365
+					$min_version_required
366
+				),
367
+				__FILE__,
368
+				__FUNCTION__ . "({$addon_name})",
369
+				__LINE__
370
+			);
371
+			EE_Error::get_notices(false, true);
372
+		}
373
+	}
374
+
375
+
376
+	/**
377
+	 * load_espresso_addons
378
+	 * allow addons to load first so that they can set hooks for running DMS's, etc
379
+	 * this is hooked into both:
380
+	 *    'AHEE__EE_Bootstrap__load_core_configuration'
381
+	 *        which runs during the WP 'plugins_loaded' action at priority 5
382
+	 *    and the WP 'activate_plugin' hook point
383
+	 *
384
+	 * @access public
385
+	 * @return void
386
+	 */
387
+	public function load_espresso_addons()
388
+	{
389
+		$this->deactivateIncompatibleAddon(
390
+			'Wait Lists',
391
+			'EE_WAIT_LISTS_VERSION',
392
+			'1.0.0.beta.074',
393
+			'load_espresso_wait_lists',
394
+			'EE_WAIT_LISTS_PLUGIN_FILE'
395
+		);
396
+		$this->deactivateIncompatibleAddon(
397
+			'Automated Upcoming Event Notifications',
398
+			'EE_AUTOMATED_UPCOMING_EVENT_NOTIFICATION_VERSION',
399
+			'1.0.0.beta.091',
400
+			'load_espresso_automated_upcoming_event_notification',
401
+			'EE_AUTOMATED_UPCOMING_EVENT_NOTIFICATION_PLUGIN_FILE'
402
+		);
403
+		do_action('AHEE__EE_System__load_espresso_addons');
404
+		// if the WP API basic auth plugin isn't already loaded, load it now.
405
+		// We want it for mobile apps. Just include the entire plugin
406
+		// also, don't load the basic auth when a plugin is getting activated, because
407
+		// it could be the basic auth plugin, and it doesn't check if its methods are already defined
408
+		// and causes a fatal error
409
+		if (
410
+			($this->request->isWordPressApi() || $this->request->isApi())
411
+			&& $this->request->getRequestParam('activate') !== 'true'
412
+			&& ! function_exists('json_basic_auth_handler')
413
+			&& ! function_exists('json_basic_auth_error')
414
+			&& ! in_array(
415
+				$this->request->getRequestParam('action'),
416
+				array('activate', 'activate-selected'),
417
+				true
418
+			)
419
+		) {
420
+			include_once EE_THIRD_PARTY . 'wp-api-basic-auth/basic-auth.php';
421
+		}
422
+		do_action('AHEE__EE_System__load_espresso_addons__complete');
423
+	}
424
+
425
+
426
+	/**
427
+	 * detect_activations_or_upgrades
428
+	 * Checks for activation or upgrade of core first;
429
+	 * then also checks if any registered addons have been activated or upgraded
430
+	 * This is hooked into 'AHEE__EE_Bootstrap__detect_activations_or_upgrades'
431
+	 * which runs during the WP 'plugins_loaded' action at priority 3
432
+	 *
433
+	 * @access public
434
+	 * @return void
435
+	 */
436
+	public function detect_activations_or_upgrades()
437
+	{
438
+		// first off: let's make sure to handle core
439
+		$this->detect_if_activation_or_upgrade();
440
+		foreach ($this->registry->addons as $addon) {
441
+			if ($addon instanceof EE_Addon) {
442
+				// detect teh request type for that addon
443
+				$addon->detect_req_type();
444
+			}
445
+		}
446
+	}
447
+
448
+
449
+	/**
450
+	 * detect_if_activation_or_upgrade
451
+	 * Takes care of detecting whether this is a brand new install or code upgrade,
452
+	 * and either setting up the DB or setting up maintenance mode etc.
453
+	 *
454
+	 * @access public
455
+	 * @return void
456
+	 */
457
+	public function detect_if_activation_or_upgrade()
458
+	{
459
+		do_action('AHEE__EE_System___detect_if_activation_or_upgrade__begin');
460
+		// check if db has been updated, or if its a brand-new installation
461
+		$espresso_db_update = $this->fix_espresso_db_upgrade_option();
462
+		$request_type = $this->detect_req_type($espresso_db_update);
463
+		// EEH_Debug_Tools::printr( $request_type, '$request_type', __FILE__, __LINE__ );
464
+		switch ($request_type) {
465
+			case EE_System::req_type_new_activation:
466
+				do_action('AHEE__EE_System__detect_if_activation_or_upgrade__new_activation');
467
+				$this->_handle_core_version_change($espresso_db_update);
468
+				break;
469
+			case EE_System::req_type_reactivation:
470
+				do_action('AHEE__EE_System__detect_if_activation_or_upgrade__reactivation');
471
+				$this->_handle_core_version_change($espresso_db_update);
472
+				break;
473
+			case EE_System::req_type_upgrade:
474
+				do_action('AHEE__EE_System__detect_if_activation_or_upgrade__upgrade');
475
+				// migrations may be required now that we've upgraded
476
+				$this->maintenance_mode->set_maintenance_mode_if_db_old();
477
+				$this->_handle_core_version_change($espresso_db_update);
478
+				break;
479
+			case EE_System::req_type_downgrade:
480
+				do_action('AHEE__EE_System__detect_if_activation_or_upgrade__downgrade');
481
+				// its possible migrations are no longer required
482
+				$this->maintenance_mode->set_maintenance_mode_if_db_old();
483
+				$this->_handle_core_version_change($espresso_db_update);
484
+				break;
485
+			case EE_System::req_type_normal:
486
+			default:
487
+				break;
488
+		}
489
+		do_action('AHEE__EE_System__detect_if_activation_or_upgrade__complete');
490
+	}
491
+
492
+
493
+	/**
494
+	 * Updates the list of installed versions and sets hooks for
495
+	 * initializing the database later during the request
496
+	 *
497
+	 * @param array $espresso_db_update
498
+	 */
499
+	private function _handle_core_version_change($espresso_db_update)
500
+	{
501
+		$this->update_list_of_installed_versions($espresso_db_update);
502
+		// get ready to verify the DB is ok (provided we aren't in maintenance mode, of course)
503
+		add_action(
504
+			'AHEE__EE_System__perform_activations_upgrades_and_migrations',
505
+			array($this, 'initialize_db_if_no_migrations_required')
506
+		);
507
+	}
508
+
509
+
510
+	/**
511
+	 * standardizes the wp option 'espresso_db_upgrade' which actually stores
512
+	 * information about what versions of EE have been installed and activated,
513
+	 * NOT necessarily the state of the database
514
+	 *
515
+	 * @param mixed $espresso_db_update           the value of the WordPress option.
516
+	 *                                            If not supplied, fetches it from the options table
517
+	 * @return array the correct value of 'espresso_db_upgrade', after saving it, if it needed correction
518
+	 */
519
+	private function fix_espresso_db_upgrade_option($espresso_db_update = null)
520
+	{
521
+		do_action('FHEE__EE_System__manage_fix_espresso_db_upgrade_option__begin', $espresso_db_update);
522
+		if (! $espresso_db_update) {
523
+			$espresso_db_update = get_option('espresso_db_update');
524
+		}
525
+		// check that option is an array
526
+		if (! is_array($espresso_db_update)) {
527
+			// if option is FALSE, then it never existed
528
+			if ($espresso_db_update === false) {
529
+				// make $espresso_db_update an array and save option with autoload OFF
530
+				$espresso_db_update = array();
531
+				add_option('espresso_db_update', $espresso_db_update, '', 'no');
532
+			} else {
533
+				// option is NOT FALSE but also is NOT an array, so make it an array and save it
534
+				$espresso_db_update = array($espresso_db_update => array());
535
+				update_option('espresso_db_update', $espresso_db_update);
536
+			}
537
+		} else {
538
+			$corrected_db_update = array();
539
+			// if IS an array, but is it an array where KEYS are version numbers, and values are arrays?
540
+			foreach ($espresso_db_update as $should_be_version_string => $should_be_array) {
541
+				if (is_int($should_be_version_string) && ! is_array($should_be_array)) {
542
+					// the key is an int, and the value IS NOT an array
543
+					// so it must be numerically-indexed, where values are versions installed...
544
+					// fix it!
545
+					$version_string = $should_be_array;
546
+					$corrected_db_update[ $version_string ] = array('unknown-date');
547
+				} else {
548
+					// ok it checks out
549
+					$corrected_db_update[ $should_be_version_string ] = $should_be_array;
550
+				}
551
+			}
552
+			$espresso_db_update = $corrected_db_update;
553
+			update_option('espresso_db_update', $espresso_db_update);
554
+		}
555
+		do_action('FHEE__EE_System__manage_fix_espresso_db_upgrade_option__complete', $espresso_db_update);
556
+		return $espresso_db_update;
557
+	}
558
+
559
+
560
+	/**
561
+	 * Does the traditional work of setting up the plugin's database and adding default data.
562
+	 * If migration script/process did not exist, this is what would happen on every activation/reactivation/upgrade.
563
+	 * NOTE: if we're in maintenance mode (which would be the case if we detect there are data
564
+	 * migration scripts that need to be run and a version change happens), enqueues core for database initialization,
565
+	 * so that it will be done when migrations are finished
566
+	 *
567
+	 * @param boolean $initialize_addons_too if true, we double-check addons' database tables etc too;
568
+	 * @param boolean $verify_schema         if true will re-check the database tables have the correct schema.
569
+	 *                                       This is a resource-intensive job
570
+	 *                                       so we prefer to only do it when necessary
571
+	 * @return void
572
+	 * @throws EE_Error
573
+	 */
574
+	public function initialize_db_if_no_migrations_required($initialize_addons_too = false, $verify_schema = true)
575
+	{
576
+		$request_type = $this->detect_req_type();
577
+		// only initialize system if we're not in maintenance mode.
578
+		if ($this->maintenance_mode->level() !== EE_Maintenance_Mode::level_2_complete_maintenance) {
579
+			/** @var EventEspresso\core\domain\services\custom_post_types\RewriteRules $rewrite_rules */
580
+			$rewrite_rules = $this->loader->getShared(
581
+				'EventEspresso\core\domain\services\custom_post_types\RewriteRules'
582
+			);
583
+			$rewrite_rules->flush();
584
+			if ($verify_schema) {
585
+				EEH_Activation::initialize_db_and_folders();
586
+			}
587
+			EEH_Activation::initialize_db_content();
588
+			EEH_Activation::system_initialization();
589
+			if ($initialize_addons_too) {
590
+				$this->initialize_addons();
591
+			}
592
+		} else {
593
+			EE_Data_Migration_Manager::instance()->enqueue_db_initialization_for('Core');
594
+		}
595
+		if (
596
+			$request_type === EE_System::req_type_new_activation
597
+			|| $request_type === EE_System::req_type_reactivation
598
+			|| (
599
+				$request_type === EE_System::req_type_upgrade
600
+				&& $this->is_major_version_change()
601
+			)
602
+		) {
603
+			add_action('AHEE__EE_System__initialize_last', array($this, 'redirect_to_about_ee'), 9);
604
+		}
605
+	}
606
+
607
+
608
+	/**
609
+	 * Initializes the db for all registered addons
610
+	 *
611
+	 * @throws EE_Error
612
+	 */
613
+	public function initialize_addons()
614
+	{
615
+		// foreach registered addon, make sure its db is up-to-date too
616
+		foreach ($this->registry->addons as $addon) {
617
+			if ($addon instanceof EE_Addon) {
618
+				$addon->initialize_db_if_no_migrations_required();
619
+			}
620
+		}
621
+	}
622
+
623
+
624
+	/**
625
+	 * Adds the current code version to the saved wp option which stores a list of all ee versions ever installed.
626
+	 *
627
+	 * @param    array  $version_history
628
+	 * @param    string $current_version_to_add version to be added to the version history
629
+	 * @return    boolean success as to whether or not this option was changed
630
+	 */
631
+	public function update_list_of_installed_versions($version_history = null, $current_version_to_add = null)
632
+	{
633
+		if (! $version_history) {
634
+			$version_history = $this->fix_espresso_db_upgrade_option($version_history);
635
+		}
636
+		if ($current_version_to_add === null) {
637
+			$current_version_to_add = espresso_version();
638
+		}
639
+		$version_history[ $current_version_to_add ][] = date('Y-m-d H:i:s', time());
640
+		// re-save
641
+		return update_option('espresso_db_update', $version_history);
642
+	}
643
+
644
+
645
+	/**
646
+	 * Detects if the current version indicated in the has existed in the list of
647
+	 * previously-installed versions of EE (espresso_db_update). Does NOT modify it (ie, no side-effect)
648
+	 *
649
+	 * @param array $espresso_db_update array from the wp option stored under the name 'espresso_db_update'.
650
+	 *                                  If not supplied, fetches it from the options table.
651
+	 *                                  Also, caches its result so later parts of the code can also know whether
652
+	 *                                  there's been an update or not. This way we can add the current version to
653
+	 *                                  espresso_db_update, but still know if this is a new install or not
654
+	 * @return int one of the constants on EE_System::req_type_
655
+	 */
656
+	public function detect_req_type($espresso_db_update = null)
657
+	{
658
+		if ($this->_req_type === null) {
659
+			$espresso_db_update = ! empty($espresso_db_update)
660
+				? $espresso_db_update
661
+				: $this->fix_espresso_db_upgrade_option();
662
+			$this->_req_type = EE_System::detect_req_type_given_activation_history(
663
+				$espresso_db_update,
664
+				'ee_espresso_activation',
665
+				espresso_version()
666
+			);
667
+			$this->_major_version_change = $this->_detect_major_version_change($espresso_db_update);
668
+			$this->request->setIsActivation($this->_req_type !== EE_System::req_type_normal);
669
+		}
670
+		return $this->_req_type;
671
+	}
672
+
673
+
674
+	/**
675
+	 * Returns whether or not there was a non-micro version change (ie, change in either
676
+	 * the first or second number in the version. Eg 4.9.0.rc.001 to 4.10.0.rc.000,
677
+	 * but not 4.9.0.rc.0001 to 4.9.1.rc.0001
678
+	 *
679
+	 * @param $activation_history
680
+	 * @return bool
681
+	 */
682
+	private function _detect_major_version_change($activation_history)
683
+	{
684
+		$previous_version = EE_System::_get_most_recently_active_version_from_activation_history($activation_history);
685
+		$previous_version_parts = explode('.', $previous_version);
686
+		$current_version_parts = explode('.', espresso_version());
687
+		return isset($previous_version_parts[0], $previous_version_parts[1], $current_version_parts[0], $current_version_parts[1])
688
+			   && ($previous_version_parts[0] !== $current_version_parts[0]
689
+				   || $previous_version_parts[1] !== $current_version_parts[1]
690
+			   );
691
+	}
692
+
693
+
694
+	/**
695
+	 * Returns true if either the major or minor version of EE changed during this request.
696
+	 * 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
697
+	 *
698
+	 * @return bool
699
+	 */
700
+	public function is_major_version_change()
701
+	{
702
+		return $this->_major_version_change;
703
+	}
704
+
705
+
706
+	/**
707
+	 * Determines the request type for any ee addon, given three piece of info: the current array of activation
708
+	 * histories (for core that' 'espresso_db_update' wp option); the name of the WordPress option which is temporarily
709
+	 * set upon activation of the plugin (for core it's 'ee_espresso_activation'); and the version that this plugin was
710
+	 * just activated to (for core that will always be espresso_version())
711
+	 *
712
+	 * @param array  $activation_history_for_addon     the option's value which stores the activation history for this
713
+	 *                                                 ee plugin. for core that's 'espresso_db_update'
714
+	 * @param string $activation_indicator_option_name the name of the WordPress option that is temporarily set to
715
+	 *                                                 indicate that this plugin was just activated
716
+	 * @param string $version_to_upgrade_to            the version that was just upgraded to (for core that will be
717
+	 *                                                 espresso_version())
718
+	 * @return int one of the constants on EE_System::req_type_*
719
+	 */
720
+	public static function detect_req_type_given_activation_history(
721
+		$activation_history_for_addon,
722
+		$activation_indicator_option_name,
723
+		$version_to_upgrade_to
724
+	) {
725
+		$version_is_higher = self::_new_version_is_higher($activation_history_for_addon, $version_to_upgrade_to);
726
+		if ($activation_history_for_addon) {
727
+			// it exists, so this isn't a completely new install
728
+			// check if this version already in that list of previously installed versions
729
+			if (! isset($activation_history_for_addon[ $version_to_upgrade_to ])) {
730
+				// it a version we haven't seen before
731
+				if ($version_is_higher === 1) {
732
+					$req_type = EE_System::req_type_upgrade;
733
+				} else {
734
+					$req_type = EE_System::req_type_downgrade;
735
+				}
736
+				delete_option($activation_indicator_option_name);
737
+			} else {
738
+				// its not an update. maybe a reactivation?
739
+				if (get_option($activation_indicator_option_name, false)) {
740
+					if ($version_is_higher === -1) {
741
+						$req_type = EE_System::req_type_downgrade;
742
+					} elseif ($version_is_higher === 0) {
743
+						// we've seen this version before, but it's an activation. must be a reactivation
744
+						$req_type = EE_System::req_type_reactivation;
745
+					} else {// $version_is_higher === 1
746
+						$req_type = EE_System::req_type_upgrade;
747
+					}
748
+					delete_option($activation_indicator_option_name);
749
+				} else {
750
+					// we've seen this version before and the activation indicate doesn't show it was just activated
751
+					if ($version_is_higher === -1) {
752
+						$req_type = EE_System::req_type_downgrade;
753
+					} elseif ($version_is_higher === 0) {
754
+						// we've seen this version before and it's not an activation. its normal request
755
+						$req_type = EE_System::req_type_normal;
756
+					} else {// $version_is_higher === 1
757
+						$req_type = EE_System::req_type_upgrade;
758
+					}
759
+				}
760
+			}
761
+		} else {
762
+			// brand new install
763
+			$req_type = EE_System::req_type_new_activation;
764
+			delete_option($activation_indicator_option_name);
765
+		}
766
+		return $req_type;
767
+	}
768
+
769
+
770
+	/**
771
+	 * Detects if the $version_to_upgrade_to is higher than the most recent version in
772
+	 * the $activation_history_for_addon
773
+	 *
774
+	 * @param array  $activation_history_for_addon (keys are versions, values are arrays of times activated,
775
+	 *                                             sometimes containing 'unknown-date'
776
+	 * @param string $version_to_upgrade_to        (current version)
777
+	 * @return int results of version_compare( $version_to_upgrade_to, $most_recently_active_version ).
778
+	 *                                             ie, -1 if $version_to_upgrade_to is LOWER (downgrade);
779
+	 *                                             0 if $version_to_upgrade_to MATCHES (reactivation or normal request);
780
+	 *                                             1 if $version_to_upgrade_to is HIGHER (upgrade) ;
781
+	 */
782
+	private static function _new_version_is_higher($activation_history_for_addon, $version_to_upgrade_to)
783
+	{
784
+		// find the most recently-activated version
785
+		$most_recently_active_version =
786
+			EE_System::_get_most_recently_active_version_from_activation_history($activation_history_for_addon);
787
+		return version_compare($version_to_upgrade_to, $most_recently_active_version);
788
+	}
789
+
790
+
791
+	/**
792
+	 * Gets the most recently active version listed in the activation history,
793
+	 * and if none are found (ie, it's a brand new install) returns '0.0.0.dev.000'.
794
+	 *
795
+	 * @param array $activation_history  (keys are versions, values are arrays of times activated,
796
+	 *                                   sometimes containing 'unknown-date'
797
+	 * @return string
798
+	 */
799
+	private static function _get_most_recently_active_version_from_activation_history($activation_history)
800
+	{
801
+		$most_recently_active_version_activation = '1970-01-01 00:00:00';
802
+		$most_recently_active_version = '0.0.0.dev.000';
803
+		if (is_array($activation_history)) {
804
+			foreach ($activation_history as $version => $times_activated) {
805
+				// check there is a record of when this version was activated. Otherwise,
806
+				// mark it as unknown
807
+				if (! $times_activated) {
808
+					$times_activated = array('unknown-date');
809
+				}
810
+				if (is_string($times_activated)) {
811
+					$times_activated = array($times_activated);
812
+				}
813
+				foreach ($times_activated as $an_activation) {
814
+					if (
815
+						$an_activation !== 'unknown-date'
816
+						&& $an_activation
817
+						   > $most_recently_active_version_activation
818
+					) {
819
+						$most_recently_active_version = $version;
820
+						$most_recently_active_version_activation = $an_activation === 'unknown-date'
821
+							? '1970-01-01 00:00:00'
822
+							: $an_activation;
823
+					}
824
+				}
825
+			}
826
+		}
827
+		return $most_recently_active_version;
828
+	}
829
+
830
+
831
+	/**
832
+	 * This redirects to the about EE page after activation
833
+	 *
834
+	 * @return void
835
+	 */
836
+	public function redirect_to_about_ee()
837
+	{
838
+		$notices = EE_Error::get_notices(false);
839
+		// if current user is an admin and it's not an ajax or rest request
840
+		if (
841
+			! isset($notices['errors'])
842
+			&& $this->request->isAdmin()
843
+			&& apply_filters(
844
+				'FHEE__EE_System__redirect_to_about_ee__do_redirect',
845
+				$this->capabilities->current_user_can('manage_options', 'espresso_about_default')
846
+			)
847
+		) {
848
+			$query_params = array('page' => 'espresso_about');
849
+			if (EE_System::instance()->detect_req_type() === EE_System::req_type_new_activation) {
850
+				$query_params['new_activation'] = true;
851
+			}
852
+			if (EE_System::instance()->detect_req_type() === EE_System::req_type_reactivation) {
853
+				$query_params['reactivation'] = true;
854
+			}
855
+			$url = add_query_arg($query_params, admin_url('admin.php'));
856
+			wp_safe_redirect($url);
857
+			exit();
858
+		}
859
+	}
860
+
861
+
862
+	/**
863
+	 * load_core_configuration
864
+	 * this is hooked into 'AHEE__EE_Bootstrap__load_core_configuration'
865
+	 * which runs during the WP 'plugins_loaded' action at priority 5
866
+	 *
867
+	 * @return void
868
+	 * @throws ReflectionException
869
+	 * @throws Exception
870
+	 */
871
+	public function load_core_configuration()
872
+	{
873
+		do_action('AHEE__EE_System__load_core_configuration__begin', $this);
874
+		$this->loader->getShared('EE_Load_Textdomain');
875
+		// load textdomain
876
+		EE_Load_Textdomain::load_textdomain();
877
+		// load caf stuff a chance to play during the activation process too.
878
+		$this->_maybe_brew_regular();
879
+		// load and setup EE_Config and EE_Network_Config
880
+		$config = $this->loader->getShared('EE_Config');
881
+		$this->loader->getShared('EE_Network_Config');
882
+		// setup autoloaders
883
+		// enable logging?
884
+		$this->loader->getShared('EventEspresso\core\services\orm\TrashLogger');
885
+		if ($config->admin->use_remote_logging) {
886
+			$this->loader->getShared('EE_Log');
887
+		}
888
+		// check for activation errors
889
+		$activation_errors = get_option('ee_plugin_activation_errors', false);
890
+		if ($activation_errors) {
891
+			EE_Error::add_error($activation_errors, __FILE__, __FUNCTION__, __LINE__);
892
+			update_option('ee_plugin_activation_errors', false);
893
+		}
894
+		// get model names
895
+		$this->_parse_model_names();
896
+		// configure custom post type definitions
897
+		$this->loader->getShared('EventEspresso\core\domain\entities\custom_post_types\CustomTaxonomyDefinitions');
898
+		$this->loader->getShared('EventEspresso\core\domain\entities\custom_post_types\CustomPostTypeDefinitions');
899
+		do_action('AHEE__EE_System__load_core_configuration__complete', $this);
900
+	}
901
+
902
+
903
+	/**
904
+	 * cycles through all of the models/*.model.php files, and assembles an array of model names
905
+	 *
906
+	 * @return void
907
+	 * @throws ReflectionException
908
+	 */
909
+	private function _parse_model_names()
910
+	{
911
+		// get all the files in the EE_MODELS folder that end in .model.php
912
+		$models = glob(EE_MODELS . '*.model.php');
913
+		$model_names = array();
914
+		$non_abstract_db_models = array();
915
+		foreach ($models as $model) {
916
+			// get model classname
917
+			$classname = EEH_File::get_classname_from_filepath_with_standard_filename($model);
918
+			$short_name = str_replace('EEM_', '', $classname);
919
+			$reflectionClass = new ReflectionClass($classname);
920
+			if ($reflectionClass->isSubclassOf('EEM_Base') && ! $reflectionClass->isAbstract()) {
921
+				$non_abstract_db_models[ $short_name ] = $classname;
922
+			}
923
+			$model_names[ $short_name ] = $classname;
924
+		}
925
+		$this->registry->models = apply_filters('FHEE__EE_System__parse_model_names', $model_names);
926
+		$this->registry->non_abstract_db_models = apply_filters(
927
+			'FHEE__EE_System__parse_implemented_model_names',
928
+			$non_abstract_db_models
929
+		);
930
+	}
931
+
932
+
933
+	/**
934
+	 * The purpose of this method is to simply check for a file named "caffeinated/brewing_regular.php" for any hooks
935
+	 * that need to be setup before our EE_System launches.
936
+	 *
937
+	 * @return void
938
+	 * @throws DomainException
939
+	 * @throws InvalidArgumentException
940
+	 * @throws InvalidDataTypeException
941
+	 * @throws InvalidInterfaceException
942
+	 * @throws InvalidClassException
943
+	 * @throws InvalidFilePathException
944
+	 */
945
+	private function _maybe_brew_regular()
946
+	{
947
+		/** @var Domain $domain */
948
+		$domain = DomainFactory::getShared(
949
+			new FullyQualifiedName(
950
+				'EventEspresso\core\domain\Domain'
951
+			),
952
+			array(
953
+				new FilePath(EVENT_ESPRESSO_MAIN_FILE),
954
+				Version::fromString(espresso_version()),
955
+			)
956
+		);
957
+		if ($domain->isCaffeinated()) {
958
+			require_once EE_CAFF_PATH . 'brewing_regular.php';
959
+		}
960
+	}
961
+
962
+
963
+	/**
964
+	 * @since 4.9.71.p
965
+	 * @throws Exception
966
+	 */
967
+	public function loadRouteMatchSpecifications()
968
+	{
969
+		try {
970
+			$this->loader->getShared(
971
+				'EventEspresso\core\services\route_match\RouteMatchSpecificationManager'
972
+			);
973
+		} catch (Exception $exception) {
974
+			new ExceptionStackTraceDisplay($exception);
975
+		}
976
+		do_action('AHEE__EE_System__loadRouteMatchSpecifications');
977
+	}
978
+
979
+
980
+	/**
981
+	 * loading CPT related classes earlier so that their definitions are available
982
+	 * but not performing any actual registration with WP core until load_CPTs_and_session() is called
983
+	 *
984
+	 * @since   4.10.21.p
985
+	 */
986
+	public function loadCustomPostTypes()
987
+	{
988
+		$this->register_custom_taxonomies = $this->loader->getShared(
989
+			'EventEspresso\core\domain\services\custom_post_types\RegisterCustomTaxonomies'
990
+		);
991
+		$this->register_custom_post_types = $this->loader->getShared(
992
+			'EventEspresso\core\domain\services\custom_post_types\RegisterCustomPostTypes'
993
+		);
994
+		$this->register_custom_taxonomy_terms = $this->loader->getShared(
995
+			'EventEspresso\core\domain\services\custom_post_types\RegisterCustomTaxonomyTerms'
996
+		);
997
+		// integrate WP_Query with the EE models
998
+		$this->loader->getShared('EE_CPT_Strategy');
999
+		// load legacy EE_Request_Handler in case add-ons still need it
1000
+		$this->loader->getShared('EE_Request_Handler');
1001
+	}
1002
+
1003
+
1004
+	/**
1005
+	 * register_shortcodes_modules_and_widgets
1006
+	 * generate lists of shortcodes and modules, then verify paths and classes
1007
+	 * This is hooked into 'AHEE__EE_Bootstrap__register_shortcodes_modules_and_widgets'
1008
+	 * which runs during the WP 'plugins_loaded' action at priority 7
1009
+	 *
1010
+	 * @access public
1011
+	 * @return void
1012
+	 * @throws Exception
1013
+	 */
1014
+	public function register_shortcodes_modules_and_widgets()
1015
+	{
1016
+		if ($this->request->isFrontend() || $this->request->isIframe() || $this->request->isAjax()) {
1017
+			// load, register, and add shortcodes the new way
1018
+			$this->loader->getShared('EventEspresso\core\services\shortcodes\ShortcodesManager');
1019
+		}
1020
+		do_action('AHEE__EE_System__register_shortcodes_modules_and_widgets');
1021
+		// check for addons using old hook point
1022
+		if (has_action('AHEE__EE_System__register_shortcodes_modules_and_addons')) {
1023
+			$this->_incompatible_addon_error();
1024
+		}
1025
+	}
1026
+
1027
+
1028
+	/**
1029
+	 * _incompatible_addon_error
1030
+	 *
1031
+	 * @access public
1032
+	 * @return void
1033
+	 */
1034
+	private function _incompatible_addon_error()
1035
+	{
1036
+		// get array of classes hooking into here
1037
+		$class_names = EEH_Class_Tools::get_class_names_for_all_callbacks_on_hook(
1038
+			'AHEE__EE_System__register_shortcodes_modules_and_addons'
1039
+		);
1040
+		if (! empty($class_names)) {
1041
+			$msg = esc_html__(
1042
+				'The following plugins, addons, or modules appear to be incompatible with this version of Event Espresso and were automatically deactivated to avoid fatal errors:',
1043
+				'event_espresso'
1044
+			);
1045
+			$msg .= '<ul>';
1046
+			foreach ($class_names as $class_name) {
1047
+				$msg .= '<li><b>Event Espresso - '
1048
+						. str_replace(
1049
+							array('EE_', 'EEM_', 'EED_', 'EES_', 'EEW_'),
1050
+							'',
1051
+							$class_name
1052
+						) . '</b></li>';
1053
+			}
1054
+			$msg .= '</ul>';
1055
+			$msg .= esc_html__(
1056
+				'Compatibility issues can be avoided and/or resolved by keeping addons and plugins updated to the latest version.',
1057
+				'event_espresso'
1058
+			);
1059
+			// save list of incompatible addons to wp-options for later use
1060
+			add_option('ee_incompatible_addons', $class_names, '', 'no');
1061
+			if (is_admin()) {
1062
+				EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
1063
+			}
1064
+		}
1065
+	}
1066
+
1067
+
1068
+	/**
1069
+	 * brew_espresso
1070
+	 * begins the process of setting hooks for initializing EE in the correct order
1071
+	 * This is happening on the 'AHEE__EE_Bootstrap__brew_espresso' hook point
1072
+	 * which runs during the WP 'plugins_loaded' action at priority 9
1073
+	 *
1074
+	 * @return void
1075
+	 */
1076
+	public function brew_espresso()
1077
+	{
1078
+		do_action('AHEE__EE_System__brew_espresso__begin', $this);
1079
+		// load some final core systems
1080
+		add_action('init', array($this, 'set_hooks_for_core'), 1);
1081
+		add_action('init', array($this, 'perform_activations_upgrades_and_migrations'), 3);
1082
+		add_action('init', array($this, 'load_CPTs_and_session'), 5);
1083
+		add_action('init', array($this, 'load_controllers'), 7);
1084
+		add_action('init', array($this, 'core_loaded_and_ready'), 9);
1085
+		add_action('init', array($this, 'initialize'), 10);
1086
+		add_action('init', array($this, 'initialize_last'), 100);
1087
+		if (is_admin() && apply_filters('FHEE__EE_System__brew_espresso__load_pue', true)) {
1088
+			// pew pew pew
1089
+			$this->loader->getShared('EventEspresso\core\services\licensing\LicenseService');
1090
+			do_action('AHEE__EE_System__brew_espresso__after_pue_init');
1091
+		}
1092
+		do_action('AHEE__EE_System__brew_espresso__complete', $this);
1093
+	}
1094
+
1095
+
1096
+	/**
1097
+	 *    set_hooks_for_core
1098
+	 *
1099
+	 * @access public
1100
+	 * @return    void
1101
+	 * @throws EE_Error
1102
+	 */
1103
+	public function set_hooks_for_core()
1104
+	{
1105
+		$this->_deactivate_incompatible_addons();
1106
+		do_action('AHEE__EE_System__set_hooks_for_core');
1107
+		$this->loader->getShared('EventEspresso\core\domain\values\session\SessionLifespan');
1108
+		// caps need to be initialized on every request so that capability maps are set.
1109
+		// @see https://events.codebasehq.com/projects/event-espresso/tickets/8674
1110
+		$this->registry->CAP->init_caps();
1111
+	}
1112
+
1113
+
1114
+	/**
1115
+	 * Using the information gathered in EE_System::_incompatible_addon_error,
1116
+	 * deactivates any addons considered incompatible with the current version of EE
1117
+	 */
1118
+	private function _deactivate_incompatible_addons()
1119
+	{
1120
+		$incompatible_addons = get_option('ee_incompatible_addons', array());
1121
+		if (! empty($incompatible_addons)) {
1122
+			$active_plugins = get_option('active_plugins', array());
1123
+			foreach ($active_plugins as $active_plugin) {
1124
+				foreach ($incompatible_addons as $incompatible_addon) {
1125
+					if (strpos($active_plugin, $incompatible_addon) !== false) {
1126
+						$this->request->unSetRequestParams(['activate'], true);
1127
+						espresso_deactivate_plugin($active_plugin);
1128
+					}
1129
+				}
1130
+			}
1131
+		}
1132
+	}
1133
+
1134
+
1135
+	/**
1136
+	 *    perform_activations_upgrades_and_migrations
1137
+	 *
1138
+	 * @access public
1139
+	 * @return    void
1140
+	 */
1141
+	public function perform_activations_upgrades_and_migrations()
1142
+	{
1143
+		do_action('AHEE__EE_System__perform_activations_upgrades_and_migrations');
1144
+	}
1145
+
1146
+
1147
+	/**
1148
+	 * @return void
1149
+	 * @throws DomainException
1150
+	 */
1151
+	public function load_CPTs_and_session()
1152
+	{
1153
+		do_action('AHEE__EE_System__load_CPTs_and_session__start');
1154
+		$this->register_custom_taxonomies->registerCustomTaxonomies();
1155
+		$this->register_custom_post_types->registerCustomPostTypes();
1156
+		$this->register_custom_taxonomy_terms->registerCustomTaxonomyTerms();
1157
+		// load legacy Custom Post Types and Taxonomies
1158
+		$this->loader->getShared('EE_Register_CPTs');
1159
+		do_action('AHEE__EE_System__load_CPTs_and_session__complete');
1160
+	}
1161
+
1162
+
1163
+	/**
1164
+	 * load_controllers
1165
+	 * this is the best place to load any additional controllers that needs access to EE core.
1166
+	 * it is expected that all basic core EE systems, that are not dependant on the current request are loaded at this
1167
+	 * time
1168
+	 *
1169
+	 * @access public
1170
+	 * @return void
1171
+	 */
1172
+	public function load_controllers()
1173
+	{
1174
+		do_action('AHEE__EE_System__load_controllers__start');
1175
+		// let's get it started
1176
+		if (
1177
+			! $this->maintenance_mode->level()
1178
+			&& ($this->request->isFrontend() || $this->request->isFrontAjax())
1179
+		) {
1180
+			do_action('AHEE__EE_System__load_controllers__load_front_controllers');
1181
+			$this->loader->getShared('EE_Front_Controller');
1182
+		} elseif ($this->request->isAdmin() || $this->request->isAdminAjax()) {
1183
+			do_action('AHEE__EE_System__load_controllers__load_admin_controllers');
1184
+			$this->loader->getShared('EE_Admin');
1185
+		} elseif ($this->request->isWordPressHeartbeat()) {
1186
+			$this->loader->getShared('EventEspresso\core\domain\services\admin\ajax\WordpressHeartbeat');
1187
+		}
1188
+		do_action('AHEE__EE_System__load_controllers__complete');
1189
+	}
1190
+
1191
+
1192
+	/**
1193
+	 * core_loaded_and_ready
1194
+	 * all of the basic EE core should be loaded at this point and available regardless of M-Mode
1195
+	 *
1196
+	 * @access public
1197
+	 * @return void
1198
+	 * @throws Exception
1199
+	 */
1200
+	public function core_loaded_and_ready()
1201
+	{
1202
+		if (
1203
+			$this->request->isAdmin()
1204
+			|| $this->request->isFrontend()
1205
+			|| $this->request->isIframe()
1206
+			|| $this->request->isWordPressApi()
1207
+		) {
1208
+			try {
1209
+				$this->loader->getShared('EventEspresso\core\services\assets\I18nRegistry', [[]]);
1210
+				$this->loader->getShared('EventEspresso\core\services\assets\Registry');
1211
+				$this->loader->getShared('EventEspresso\core\domain\services\assets\CoreAssetManager');
1212
+				if ($this->canLoadBlocks()) {
1213
+					$this->loader->getShared(
1214
+						'EventEspresso\core\services\editor\BlockRegistrationManager'
1215
+					);
1216
+				}
1217
+			} catch (Exception $exception) {
1218
+				new ExceptionStackTraceDisplay($exception);
1219
+			}
1220
+		}
1221
+		if (
1222
+			$this->request->isAdmin()
1223
+			|| $this->request->isEeAjax()
1224
+			|| $this->request->isFrontend()
1225
+		) {
1226
+			$this->loader->getShared('EE_Session');
1227
+		}
1228
+		do_action('AHEE__EE_System__core_loaded_and_ready');
1229
+		// always load template tags, because it's faster than checking if it's a front-end request, and many page
1230
+		// builders require these even on the front-end
1231
+		require_once EE_PUBLIC . 'template_tags.php';
1232
+		do_action('AHEE__EE_System__set_hooks_for_shortcodes_modules_and_addons');
1233
+	}
1234
+
1235
+
1236
+	/**
1237
+	 * initialize
1238
+	 * this is the best place to begin initializing client code
1239
+	 *
1240
+	 * @access public
1241
+	 * @return void
1242
+	 */
1243
+	public function initialize()
1244
+	{
1245
+		do_action('AHEE__EE_System__initialize');
1246
+		add_filter(
1247
+			'safe_style_css',
1248
+			function ($styles) {
1249
+				$styles[] = 'display';
1250
+				$styles[] = 'visibility';
1251
+				$styles[] = 'position';
1252
+				$styles[] = 'top';
1253
+				$styles[] = 'right';
1254
+				$styles[] = 'bottom';
1255
+				$styles[] = 'left';
1256
+				$styles[] = 'resize';
1257
+				$styles[] = 'max-width';
1258
+				$styles[] = 'max-height';
1259
+				return $styles;
1260
+			}
1261
+		);
1262
+	}
1263
+
1264
+
1265
+	/**
1266
+	 * initialize_last
1267
+	 * this is run really late during the WP init hook point, and ensures that mostly everything else that needs to
1268
+	 * initialize has done so
1269
+	 *
1270
+	 * @access public
1271
+	 * @return void
1272
+	 */
1273
+	public function initialize_last()
1274
+	{
1275
+		do_action('AHEE__EE_System__initialize_last');
1276
+		/** @var EventEspresso\core\domain\services\custom_post_types\RewriteRules $rewrite_rules */
1277
+		$rewrite_rules = $this->loader->getShared(
1278
+			'EventEspresso\core\domain\services\custom_post_types\RewriteRules'
1279
+		);
1280
+		$rewrite_rules->flushRewriteRules();
1281
+		add_action('admin_bar_init', array($this, 'addEspressoToolbar'));
1282
+		if (
1283
+			($this->request->isAjax() || $this->request->isAdmin())
1284
+			&& $this->maintenance_mode->models_can_query()
1285
+		) {
1286
+			$this->loader->getShared('EventEspresso\core\services\privacy\export\PersonalDataExporterManager');
1287
+			$this->loader->getShared('EventEspresso\core\services\privacy\erasure\PersonalDataEraserManager');
1288
+		}
1289
+	}
1290
+
1291
+
1292
+	/**
1293
+	 * @return void
1294
+	 * @throws EE_Error
1295
+	 */
1296
+	public function addEspressoToolbar()
1297
+	{
1298
+		$this->loader->getShared(
1299
+			'EventEspresso\core\domain\services\admin\AdminToolBar',
1300
+			array($this->registry->CAP)
1301
+		);
1302
+	}
1303
+
1304
+
1305
+	/**
1306
+	 * do_not_cache
1307
+	 * sets no cache headers and defines no cache constants for WP plugins
1308
+	 *
1309
+	 * @access public
1310
+	 * @return void
1311
+	 */
1312
+	public static function do_not_cache()
1313
+	{
1314
+		// set no cache constants
1315
+		if (! defined('DONOTCACHEPAGE')) {
1316
+			define('DONOTCACHEPAGE', true);
1317
+		}
1318
+		if (! defined('DONOTCACHCEOBJECT')) {
1319
+			define('DONOTCACHCEOBJECT', true);
1320
+		}
1321
+		if (! defined('DONOTCACHEDB')) {
1322
+			define('DONOTCACHEDB', true);
1323
+		}
1324
+		// add no cache headers
1325
+		add_action('send_headers', array('EE_System', 'nocache_headers'), 10);
1326
+		// plus a little extra for nginx and Google Chrome
1327
+		add_filter('nocache_headers', array('EE_System', 'extra_nocache_headers'), 10, 1);
1328
+		// prevent browsers from prefetching of the rel='next' link, because it may contain content that interferes with the registration process
1329
+		remove_action('wp_head', 'adjacent_posts_rel_link_wp_head');
1330
+	}
1331
+
1332
+
1333
+	/**
1334
+	 *    extra_nocache_headers
1335
+	 *
1336
+	 * @access    public
1337
+	 * @param $headers
1338
+	 * @return    array
1339
+	 */
1340
+	public static function extra_nocache_headers($headers)
1341
+	{
1342
+		// for NGINX
1343
+		$headers['X-Accel-Expires'] = 0;
1344
+		// plus extra for Google Chrome since it doesn't seem to respect "no-cache", but WILL respect "no-store"
1345
+		$headers['Cache-Control'] = 'no-store, no-cache, must-revalidate, max-age=0';
1346
+		return $headers;
1347
+	}
1348
+
1349
+
1350
+	/**
1351
+	 *    nocache_headers
1352
+	 *
1353
+	 * @access    public
1354
+	 * @return    void
1355
+	 */
1356
+	public static function nocache_headers()
1357
+	{
1358
+		nocache_headers();
1359
+	}
1360
+
1361
+
1362
+	/**
1363
+	 * simply hooks into "wp_list_pages_exclude" filter (for wp_list_pages method) and makes sure EE critical pages are
1364
+	 * never returned with the function.
1365
+	 *
1366
+	 * @param  array $exclude_array any existing pages being excluded are in this array.
1367
+	 * @return array
1368
+	 */
1369
+	public function remove_pages_from_wp_list_pages($exclude_array)
1370
+	{
1371
+		return array_merge($exclude_array, $this->registry->CFG->core->get_critical_pages_array());
1372
+	}
1373
+
1374
+
1375
+	/**
1376
+	 * Return whether blocks can be registered/loaded or not.
1377
+	 * @return bool
1378
+	 */
1379
+	private function canLoadBlocks()
1380
+	{
1381
+		return apply_filters('FHEE__EE_System__canLoadBlocks', true)
1382
+			   && function_exists('register_block_type')
1383
+			   // don't load blocks if in the Divi page builder editor context
1384
+			   // @see https://github.com/eventespresso/event-espresso-core/issues/814
1385
+			   && ! $this->request->getRequestParam('et_fb', false);
1386
+	}
1387 1387
 }
Please login to merge, or discard this patch.
core/db_classes/EE_Transaction.class.php 2 patches
Indentation   +1706 added lines, -1706 removed lines patch added patch discarded remove patch
@@ -13,1710 +13,1710 @@
 block discarded – undo
13 13
  */
14 14
 class EE_Transaction extends EE_Base_Class implements EEI_Transaction
15 15
 {
16
-    /**
17
-     * The length of time in seconds that a lock is applied before being considered expired.
18
-     * It is not long because a transaction should only be locked for the duration of the request that locked it
19
-     */
20
-    const LOCK_EXPIRATION = 2;
21
-
22
-    /**
23
-     * txn status upon initial construction.
24
-     *
25
-     * @var string
26
-     */
27
-    protected $_old_txn_status;
28
-
29
-
30
-    /**
31
-     * @param array  $props_n_values          incoming values
32
-     * @param string $timezone                incoming timezone
33
-     *                                        (if not set the timezone set for the website will be used.)
34
-     * @param array  $date_formats            incoming date_formats in an array where the first value is the
35
-     *                                        date_format and the second value is the time format
36
-     * @return EE_Transaction
37
-     * @throws EE_Error
38
-     * @throws InvalidArgumentException
39
-     * @throws InvalidDataTypeException
40
-     * @throws InvalidInterfaceException
41
-     * @throws ReflectionException
42
-     */
43
-    public static function new_instance($props_n_values = [], $timezone = null, $date_formats = [])
44
-    {
45
-        $has_object = parent::_check_for_object($props_n_values, __CLASS__, $timezone, $date_formats);
46
-        $txn        = $has_object
47
-            ? $has_object
48
-            : new self($props_n_values, false, $timezone, $date_formats);
49
-        if (! $has_object) {
50
-            $txn->set_old_txn_status($txn->status_ID());
51
-        }
52
-        return $txn;
53
-    }
54
-
55
-
56
-    /**
57
-     * @param array  $props_n_values  incoming values from the database
58
-     * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
59
-     *                                the website will be used.
60
-     * @return EE_Transaction
61
-     * @throws EE_Error
62
-     * @throws InvalidArgumentException
63
-     * @throws InvalidDataTypeException
64
-     * @throws InvalidInterfaceException
65
-     * @throws ReflectionException
66
-     */
67
-    public static function new_instance_from_db($props_n_values = [], $timezone = null)
68
-    {
69
-        $txn = new self($props_n_values, true, $timezone);
70
-        $txn->set_old_txn_status($txn->status_ID());
71
-        return $txn;
72
-    }
73
-
74
-
75
-    /**
76
-     * Sets a meta field indicating that this TXN is locked and should not be updated in the db.
77
-     * If a lock has already been set, then we will attempt to remove it in case it has expired.
78
-     * If that also fails, then an exception is thrown.
79
-     *
80
-     * @throws EE_Error
81
-     * @throws InvalidArgumentException
82
-     * @throws InvalidDataTypeException
83
-     * @throws InvalidInterfaceException
84
-     * @throws ReflectionException
85
-     */
86
-    public function lock()
87
-    {
88
-        // attempt to set lock, but if that fails...
89
-        if (! $this->add_extra_meta('lock', time(), true)) {
90
-            // then attempt to remove the lock in case it is expired
91
-            if ($this->_remove_expired_lock()) {
92
-                // if removal was successful, then try setting lock again
93
-                $this->lock();
94
-            } else {
95
-                // but if the lock can not be removed, then throw an exception
96
-                throw new EE_Error(
97
-                    sprintf(
98
-                        esc_html__(
99
-                            'Could not lock Transaction %1$d because it is already locked, meaning another part of the system is currently editing it. It should already be unlocked by the time you read this, so please refresh the page and try again.',
100
-                            'event_espresso'
101
-                        ),
102
-                        $this->ID()
103
-                    )
104
-                );
105
-            }
106
-        }
107
-    }
108
-
109
-
110
-    /**
111
-     * removes transaction lock applied in EE_Transaction::lock()
112
-     *
113
-     * @return int
114
-     * @throws EE_Error
115
-     * @throws InvalidArgumentException
116
-     * @throws InvalidDataTypeException
117
-     * @throws InvalidInterfaceException
118
-     * @throws ReflectionException
119
-     */
120
-    public function unlock()
121
-    {
122
-        return $this->delete_extra_meta('lock');
123
-    }
124
-
125
-
126
-    /**
127
-     * Decides whether or not now is the right time to update the transaction.
128
-     * This is useful because we don't always know if it is safe to update the transaction
129
-     * and its related data. why?
130
-     * because it's possible that the transaction is being used in another
131
-     * request and could overwrite anything we save.
132
-     * So we want to only update the txn once we know that won't happen.
133
-     * We also check that the lock isn't expired, and remove it if it is
134
-     *
135
-     * @return boolean
136
-     * @throws EE_Error
137
-     * @throws InvalidArgumentException
138
-     * @throws InvalidDataTypeException
139
-     * @throws InvalidInterfaceException
140
-     * @throws ReflectionException
141
-     */
142
-    public function is_locked()
143
-    {
144
-        // if TXN is not locked, then return false immediately
145
-        if (! $this->_get_lock()) {
146
-            return false;
147
-        }
148
-        // if not, then let's try and remove the lock in case it's expired...
149
-        // _remove_expired_lock() returns 0 when lock is valid (ie: removed = false)
150
-        // and a positive number if the lock was removed (ie: number of locks deleted),
151
-        // so we need to return the opposite
152
-        return ! $this->_remove_expired_lock() ? true : false;
153
-    }
154
-
155
-
156
-    /**
157
-     * Gets the meta field indicating that this TXN is locked
158
-     *
159
-     * @return int
160
-     * @throws EE_Error
161
-     * @throws InvalidArgumentException
162
-     * @throws InvalidDataTypeException
163
-     * @throws InvalidInterfaceException
164
-     * @throws ReflectionException
165
-     */
166
-    protected function _get_lock()
167
-    {
168
-        return (int) $this->get_extra_meta('lock', true, 0);
169
-    }
170
-
171
-
172
-    /**
173
-     * If the lock on this transaction is expired, then we want to remove it so that the transaction can be updated
174
-     *
175
-     * @return int
176
-     * @throws EE_Error
177
-     * @throws InvalidArgumentException
178
-     * @throws InvalidDataTypeException
179
-     * @throws InvalidInterfaceException
180
-     * @throws ReflectionException
181
-     */
182
-    protected function _remove_expired_lock()
183
-    {
184
-        $locked = $this->_get_lock();
185
-        if ($locked && time() - EE_Transaction::LOCK_EXPIRATION > $locked) {
186
-            return $this->unlock();
187
-        }
188
-        return 0;
189
-    }
190
-
191
-
192
-    /**
193
-     * Set transaction total
194
-     *
195
-     * @param float $total total value of transaction
196
-     * @throws EE_Error
197
-     * @throws InvalidArgumentException
198
-     * @throws InvalidDataTypeException
199
-     * @throws InvalidInterfaceException
200
-     * @throws ReflectionException
201
-     */
202
-    public function set_total($total = 0.00)
203
-    {
204
-        $this->set('TXN_total', (float) $total);
205
-    }
206
-
207
-
208
-    /**
209
-     * Set Total Amount Paid to Date
210
-     *
211
-     * @param float $total_paid total amount paid to date (sum of all payments)
212
-     * @throws EE_Error
213
-     * @throws InvalidArgumentException
214
-     * @throws InvalidDataTypeException
215
-     * @throws InvalidInterfaceException
216
-     * @throws ReflectionException
217
-     */
218
-    public function set_paid($total_paid = 0.00)
219
-    {
220
-        $this->set('TXN_paid', (float) $total_paid);
221
-    }
222
-
223
-
224
-    /**
225
-     * Set transaction status
226
-     *
227
-     * @param string $status        whether the transaction is open, declined, accepted,
228
-     *                              or any number of custom values that can be set
229
-     * @throws EE_Error
230
-     * @throws InvalidArgumentException
231
-     * @throws InvalidDataTypeException
232
-     * @throws InvalidInterfaceException
233
-     * @throws ReflectionException
234
-     */
235
-    public function set_status($status = '')
236
-    {
237
-        $this->set('STS_ID', $status);
238
-    }
239
-
240
-
241
-    /**
242
-     * Set hash salt
243
-     *
244
-     * @param string $hash_salt required for some payment gateways
245
-     * @throws EE_Error
246
-     * @throws InvalidArgumentException
247
-     * @throws InvalidDataTypeException
248
-     * @throws InvalidInterfaceException
249
-     * @throws ReflectionException
250
-     */
251
-    public function set_hash_salt($hash_salt = '')
252
-    {
253
-        $this->set('TXN_hash_salt', $hash_salt);
254
-    }
255
-
256
-
257
-    /**
258
-     * Sets TXN_reg_steps array
259
-     *
260
-     * @param array $txn_reg_steps
261
-     * @throws EE_Error
262
-     * @throws InvalidArgumentException
263
-     * @throws InvalidDataTypeException
264
-     * @throws InvalidInterfaceException
265
-     * @throws ReflectionException
266
-     */
267
-    public function set_reg_steps(array $txn_reg_steps)
268
-    {
269
-        $this->set('TXN_reg_steps', $txn_reg_steps);
270
-    }
271
-
272
-
273
-    /**
274
-     * Gets TXN_reg_steps
275
-     *
276
-     * @return array
277
-     * @throws EE_Error
278
-     * @throws InvalidArgumentException
279
-     * @throws InvalidDataTypeException
280
-     * @throws InvalidInterfaceException
281
-     * @throws ReflectionException
282
-     */
283
-    public function reg_steps()
284
-    {
285
-        $TXN_reg_steps = $this->get('TXN_reg_steps');
286
-        return is_array($TXN_reg_steps) ? $TXN_reg_steps : [];
287
-    }
288
-
289
-
290
-    /**
291
-     * @return string of transaction's total cost, with currency symbol and decimal
292
-     * @throws EE_Error
293
-     * @throws InvalidArgumentException
294
-     * @throws InvalidDataTypeException
295
-     * @throws InvalidInterfaceException
296
-     * @throws ReflectionException
297
-     */
298
-    public function pretty_total()
299
-    {
300
-        return $this->get_pretty('TXN_total');
301
-    }
302
-
303
-
304
-    /**
305
-     * Gets the amount paid in a pretty string (formatted and with currency symbol)
306
-     *
307
-     * @return string
308
-     * @throws EE_Error
309
-     * @throws InvalidArgumentException
310
-     * @throws InvalidDataTypeException
311
-     * @throws InvalidInterfaceException
312
-     * @throws ReflectionException
313
-     */
314
-    public function pretty_paid()
315
-    {
316
-        return $this->get_pretty('TXN_paid');
317
-    }
318
-
319
-
320
-    /**
321
-     * calculate the amount remaining for this transaction and return;
322
-     *
323
-     * @return float amount remaining
324
-     * @throws EE_Error
325
-     * @throws InvalidArgumentException
326
-     * @throws InvalidDataTypeException
327
-     * @throws InvalidInterfaceException
328
-     * @throws ReflectionException
329
-     */
330
-    public function remaining()
331
-    {
332
-        return $this->total() - $this->paid();
333
-    }
334
-
335
-
336
-    /**
337
-     * get Transaction Total
338
-     *
339
-     * @return float
340
-     * @throws EE_Error
341
-     * @throws InvalidArgumentException
342
-     * @throws InvalidDataTypeException
343
-     * @throws InvalidInterfaceException
344
-     * @throws ReflectionException
345
-     */
346
-    public function total()
347
-    {
348
-        return (float) $this->get('TXN_total');
349
-    }
350
-
351
-
352
-    /**
353
-     * get Total Amount Paid to Date
354
-     *
355
-     * @return float
356
-     * @throws EE_Error
357
-     * @throws InvalidArgumentException
358
-     * @throws InvalidDataTypeException
359
-     * @throws InvalidInterfaceException
360
-     * @throws ReflectionException
361
-     */
362
-    public function paid()
363
-    {
364
-        return (float) $this->get('TXN_paid');
365
-    }
366
-
367
-
368
-    /**
369
-     * @return mixed|null
370
-     * @throws EE_Error
371
-     * @throws InvalidArgumentException
372
-     * @throws InvalidDataTypeException
373
-     * @throws InvalidInterfaceException
374
-     * @throws ReflectionException
375
-     */
376
-    public function get_cart_session()
377
-    {
378
-        $session_data = (array) $this->get('TXN_session_data');
379
-        return isset($session_data['cart']) && $session_data['cart'] instanceof EE_Cart
380
-            ? $session_data['cart']
381
-            : null;
382
-    }
383
-
384
-
385
-    /**
386
-     * get Transaction session data
387
-     *
388
-     * @return array|mixed
389
-     * @throws EE_Error
390
-     * @throws InvalidArgumentException
391
-     * @throws InvalidDataTypeException
392
-     * @throws InvalidInterfaceException
393
-     * @throws ReflectionException
394
-     */
395
-    public function session_data()
396
-    {
397
-        $session_data = $this->get('TXN_session_data');
398
-        if (empty($session_data)) {
399
-            $session_data = [
400
-                'id'            => null,
401
-                'user_id'       => null,
402
-                'ip_address'    => null,
403
-                'user_agent'    => null,
404
-                'init_access'   => null,
405
-                'last_access'   => null,
406
-                'pages_visited' => [],
407
-            ];
408
-        }
409
-        return $session_data;
410
-    }
411
-
412
-
413
-    /**
414
-     * Set session data within the TXN object
415
-     *
416
-     * @param EE_Session|array $session_data
417
-     * @throws EE_Error
418
-     * @throws InvalidArgumentException
419
-     * @throws InvalidDataTypeException
420
-     * @throws InvalidInterfaceException
421
-     * @throws ReflectionException
422
-     */
423
-    public function set_txn_session_data($session_data)
424
-    {
425
-        if ($session_data instanceof EE_Session) {
426
-            $this->set('TXN_session_data', $session_data->get_session_data(null, true));
427
-        } else {
428
-            $this->set('TXN_session_data', $session_data);
429
-        }
430
-    }
431
-
432
-
433
-    /**
434
-     * get Transaction hash salt
435
-     *
436
-     * @return mixed
437
-     * @throws EE_Error
438
-     * @throws InvalidArgumentException
439
-     * @throws InvalidDataTypeException
440
-     * @throws InvalidInterfaceException
441
-     * @throws ReflectionException
442
-     */
443
-    public function hash_salt_()
444
-    {
445
-        return $this->get('TXN_hash_salt');
446
-    }
447
-
448
-
449
-    /**
450
-     * Returns the transaction datetime as either:
451
-     *            - unix timestamp format ($format = false, $gmt = true)
452
-     *            - formatted date string including the UTC (timezone) offset ($format = true ($gmt
453
-     *              has no affect with this option)), this also may include a timezone abbreviation if the
454
-     *              set timezone in this class differs from what the timezone is on the blog.
455
-     *            - formatted date string including the UTC (timezone) offset (default).
456
-     *
457
-     * @param boolean $format   - whether to return a unix timestamp (default) or formatted date string
458
-     * @param boolean $gmt      - whether to return a unix timestamp with UTC offset applied (default)
459
-     *                          or no UTC offset applied
460
-     * @return string | int
461
-     * @throws EE_Error
462
-     * @throws InvalidArgumentException
463
-     * @throws InvalidDataTypeException
464
-     * @throws InvalidInterfaceException
465
-     * @throws ReflectionException
466
-     */
467
-    public function datetime($format = false, $gmt = false)
468
-    {
469
-        if ($format) {
470
-            return $this->get_pretty('TXN_timestamp');
471
-        }
472
-        if ($gmt) {
473
-            return $this->get_raw('TXN_timestamp');
474
-        }
475
-        return $this->get('TXN_timestamp');
476
-    }
477
-
478
-
479
-    /**
480
-     * Gets registrations on this transaction
481
-     *
482
-     * @param array   $query_params array of query parameters
483
-     * @param boolean $get_cached   TRUE to retrieve cached registrations or FALSE to pull from the db
484
-     * @return EE_Base_Class[]|EE_Registration[]
485
-     * @throws EE_Error
486
-     * @throws InvalidArgumentException
487
-     * @throws InvalidDataTypeException
488
-     * @throws InvalidInterfaceException
489
-     * @throws ReflectionException
490
-     */
491
-    public function registrations($query_params = [], $get_cached = false)
492
-    {
493
-        $query_params = (empty($query_params) || ! is_array($query_params))
494
-            ? [
495
-                'order_by' => [
496
-                    'Event.EVT_name'     => 'ASC',
497
-                    'Attendee.ATT_lname' => 'ASC',
498
-                    'Attendee.ATT_fname' => 'ASC',
499
-                ],
500
-            ]
501
-            : $query_params;
502
-        $query_params = $get_cached ? [] : $query_params;
503
-        return $this->get_many_related('Registration', $query_params);
504
-    }
505
-
506
-
507
-    /**
508
-     * Gets all the attendees for this transaction (handy for use with EE_Attendee's get_registrations_for_event
509
-     * function for getting attendees and how many registrations they each have for an event)
510
-     *
511
-     * @return mixed EE_Attendee[] by default, int if $output is set to 'COUNT'
512
-     * @throws EE_Error
513
-     * @throws InvalidArgumentException
514
-     * @throws InvalidDataTypeException
515
-     * @throws InvalidInterfaceException
516
-     * @throws ReflectionException
517
-     */
518
-    public function attendees()
519
-    {
520
-        return $this->get_many_related('Attendee', [['Registration.Transaction.TXN_ID' => $this->ID()]]);
521
-    }
522
-
523
-
524
-    /**
525
-     * Gets payments for this transaction. Unlike other such functions, order by 'DESC' by default
526
-     *
527
-     * @param array $query_params @see
528
-     *                            https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
529
-     * @return EE_Base_Class[]|EE_Payment[]
530
-     * @throws EE_Error
531
-     * @throws InvalidArgumentException
532
-     * @throws InvalidDataTypeException
533
-     * @throws InvalidInterfaceException
534
-     * @throws ReflectionException
535
-     */
536
-    public function payments($query_params = [])
537
-    {
538
-        return $this->get_many_related('Payment', $query_params);
539
-    }
540
-
541
-
542
-    /**
543
-     * gets only approved payments for this transaction
544
-     *
545
-     * @return EE_Base_Class[]|EE_Payment[]
546
-     * @throws EE_Error
547
-     * @throws InvalidArgumentException
548
-     * @throws ReflectionException
549
-     * @throws InvalidDataTypeException
550
-     * @throws InvalidInterfaceException
551
-     */
552
-    public function approved_payments()
553
-    {
554
-        EE_Registry::instance()->load_model('Payment');
555
-        return $this->get_many_related(
556
-            'Payment',
557
-            [
558
-                ['STS_ID' => EEM_Payment::status_id_approved],
559
-                'order_by' => ['PAY_timestamp' => 'DESC'],
560
-            ]
561
-        );
562
-    }
563
-
564
-
565
-    /**
566
-     * Gets all payments which have not been approved
567
-     *
568
-     * @return EE_Base_Class[]|EEI_Payment[]
569
-     * @throws EE_Error if a model is misconfigured somehow
570
-     * @throws InvalidArgumentException
571
-     * @throws InvalidDataTypeException
572
-     * @throws InvalidInterfaceException
573
-     * @throws ReflectionException
574
-     */
575
-    public function pending_payments()
576
-    {
577
-        return $this->get_many_related(
578
-            'Payment',
579
-            [
580
-                [
581
-                    'STS_ID' => EEM_Payment::status_id_pending,
582
-                ],
583
-                'order_by' => [
584
-                    'PAY_timestamp' => 'DESC',
585
-                ],
586
-            ]
587
-        );
588
-    }
589
-
590
-
591
-    /**
592
-     * echoes $this->pretty_status()
593
-     *
594
-     * @param bool $show_icons
595
-     * @throws EE_Error
596
-     * @throws InvalidArgumentException
597
-     * @throws InvalidDataTypeException
598
-     * @throws InvalidInterfaceException
599
-     * @throws ReflectionException
600
-     */
601
-    public function e_pretty_status($show_icons = false)
602
-    {
603
-        echo wp_kses($this->pretty_status($show_icons), AllowedTags::getAllowedTags());
604
-    }
605
-
606
-
607
-    /**
608
-     * returns a pretty version of the status, good for displaying to users
609
-     *
610
-     * @param bool $show_icons
611
-     * @return string
612
-     * @throws EE_Error
613
-     * @throws InvalidArgumentException
614
-     * @throws InvalidDataTypeException
615
-     * @throws InvalidInterfaceException
616
-     * @throws ReflectionException
617
-     */
618
-    public function pretty_status($show_icons = false)
619
-    {
620
-        $status = EEM_Status::instance()->localized_status(
621
-            [$this->status_ID() => esc_html__('unknown', 'event_espresso')],
622
-            false,
623
-            'sentence'
624
-        );
625
-        $icon   = '';
626
-        switch ($this->status_ID()) {
627
-            case EEM_Transaction::complete_status_code:
628
-                $icon = $show_icons ? '<span class="dashicons dashicons-yes ee-icon-size-24 green-text"></span>' : '';
629
-                break;
630
-            case EEM_Transaction::incomplete_status_code:
631
-                $icon = $show_icons ? '<span class="dashicons dashicons-marker ee-icon-size-16 lt-blue-text"></span>'
632
-                    : '';
633
-                break;
634
-            case EEM_Transaction::abandoned_status_code:
635
-                $icon = $show_icons ? '<span class="dashicons dashicons-marker ee-icon-size-16 red-text"></span>' : '';
636
-                break;
637
-            case EEM_Transaction::failed_status_code:
638
-                $icon = $show_icons ? '<span class="dashicons dashicons-no ee-icon-size-16 red-text"></span>' : '';
639
-                break;
640
-            case EEM_Transaction::overpaid_status_code:
641
-                $icon = $show_icons ? '<span class="dashicons dashicons-plus ee-icon-size-16 orange-text"></span>' : '';
642
-                break;
643
-        }
644
-        return $icon . $status[ $this->status_ID() ];
645
-    }
646
-
647
-
648
-    /**
649
-     * get Transaction Status
650
-     *
651
-     * @return mixed
652
-     * @throws EE_Error
653
-     * @throws InvalidArgumentException
654
-     * @throws InvalidDataTypeException
655
-     * @throws InvalidInterfaceException
656
-     * @throws ReflectionException
657
-     */
658
-    public function status_ID()
659
-    {
660
-        return $this->get('STS_ID');
661
-    }
662
-
663
-
664
-    /**
665
-     * Returns TRUE or FALSE for whether or not this transaction cost any money
666
-     *
667
-     * @return boolean
668
-     * @throws EE_Error
669
-     * @throws InvalidArgumentException
670
-     * @throws InvalidDataTypeException
671
-     * @throws InvalidInterfaceException
672
-     * @throws ReflectionException
673
-     */
674
-    public function is_free()
675
-    {
676
-        return EEH_Money::compare_floats($this->get('TXN_total'), 0, '==');
677
-    }
678
-
679
-
680
-    /**
681
-     * Returns whether this transaction is complete
682
-     * Useful in templates and other logic for deciding if we should ask for another payment...
683
-     *
684
-     * @return boolean
685
-     * @throws EE_Error
686
-     * @throws InvalidArgumentException
687
-     * @throws InvalidDataTypeException
688
-     * @throws InvalidInterfaceException
689
-     * @throws ReflectionException
690
-     */
691
-    public function is_completed()
692
-    {
693
-        return $this->status_ID() === EEM_Transaction::complete_status_code;
694
-    }
695
-
696
-
697
-    /**
698
-     * Returns whether this transaction is incomplete
699
-     * Useful in templates and other logic for deciding if we should ask for another payment...
700
-     *
701
-     * @return boolean
702
-     * @throws EE_Error
703
-     * @throws InvalidArgumentException
704
-     * @throws InvalidDataTypeException
705
-     * @throws InvalidInterfaceException
706
-     * @throws ReflectionException
707
-     */
708
-    public function is_incomplete()
709
-    {
710
-        return $this->status_ID() === EEM_Transaction::incomplete_status_code;
711
-    }
712
-
713
-
714
-    /**
715
-     * Returns whether this transaction is overpaid
716
-     * Useful in templates and other logic for deciding if monies need to be refunded
717
-     *
718
-     * @return boolean
719
-     * @throws EE_Error
720
-     * @throws InvalidArgumentException
721
-     * @throws InvalidDataTypeException
722
-     * @throws InvalidInterfaceException
723
-     * @throws ReflectionException
724
-     */
725
-    public function is_overpaid()
726
-    {
727
-        return $this->status_ID() === EEM_Transaction::overpaid_status_code;
728
-    }
729
-
730
-
731
-    /**
732
-     * Returns whether this transaction was abandoned
733
-     * meaning that the transaction/registration process was somehow interrupted and never completed
734
-     * but that contact information exists for at least one registrant
735
-     *
736
-     * @return boolean
737
-     * @throws EE_Error
738
-     * @throws InvalidArgumentException
739
-     * @throws InvalidDataTypeException
740
-     * @throws InvalidInterfaceException
741
-     * @throws ReflectionException
742
-     */
743
-    public function is_abandoned()
744
-    {
745
-        return $this->status_ID() === EEM_Transaction::abandoned_status_code;
746
-    }
747
-
748
-
749
-    /**
750
-     * Returns whether this transaction failed
751
-     * meaning that the transaction/registration process was somehow interrupted and never completed
752
-     * and that NO contact information exists for any registrants
753
-     *
754
-     * @return boolean
755
-     * @throws EE_Error
756
-     * @throws InvalidArgumentException
757
-     * @throws InvalidDataTypeException
758
-     * @throws InvalidInterfaceException
759
-     * @throws ReflectionException
760
-     */
761
-    public function failed()
762
-    {
763
-        return $this->status_ID() === EEM_Transaction::failed_status_code;
764
-    }
765
-
766
-
767
-    /**
768
-     * This returns the url for the invoice of this transaction
769
-     *
770
-     * @param string $type 'html' or 'pdf' (default is pdf)
771
-     * @return string
772
-     * @throws DomainException
773
-     * @throws EE_Error
774
-     * @throws InvalidArgumentException
775
-     * @throws InvalidDataTypeException
776
-     * @throws InvalidInterfaceException
777
-     * @throws ReflectionException
778
-     */
779
-    public function invoice_url($type = 'html')
780
-    {
781
-        $REG = $this->primary_registration();
782
-        if (! $REG instanceof EE_Registration) {
783
-            return '';
784
-        }
785
-        return $REG->invoice_url($type);
786
-    }
787
-
788
-
789
-    /**
790
-     * Gets the primary registration only
791
-     *
792
-     * @return EE_Base_Class|EE_Registration
793
-     * @throws EE_Error
794
-     * @throws InvalidArgumentException
795
-     * @throws InvalidDataTypeException
796
-     * @throws InvalidInterfaceException
797
-     * @throws ReflectionException
798
-     */
799
-    public function primary_registration()
800
-    {
801
-        $registrations = (array) $this->get_many_related(
802
-            'Registration',
803
-            [['REG_count' => EEM_Registration::PRIMARY_REGISTRANT_COUNT]]
804
-        );
805
-        foreach ($registrations as $registration) {
806
-            // valid registration that is NOT cancelled or declined ?
807
-            if (
808
-                $registration instanceof EE_Registration
809
-                && ! in_array($registration->status_ID(), EEM_Registration::closed_reg_statuses(), true)
810
-            ) {
811
-                return $registration;
812
-            }
813
-        }
814
-        // nothing valid found, so just return first thing from array of results
815
-        return reset($registrations);
816
-    }
817
-
818
-
819
-    /**
820
-     * Gets the URL for viewing the receipt
821
-     *
822
-     * @param string $type 'pdf' or 'html' (default is 'html')
823
-     * @return string
824
-     * @throws DomainException
825
-     * @throws EE_Error
826
-     * @throws InvalidArgumentException
827
-     * @throws InvalidDataTypeException
828
-     * @throws InvalidInterfaceException
829
-     * @throws ReflectionException
830
-     */
831
-    public function receipt_url($type = 'html')
832
-    {
833
-        $REG = $this->primary_registration();
834
-        if (! $REG instanceof EE_Registration) {
835
-            return '';
836
-        }
837
-        return $REG->receipt_url($type);
838
-    }
839
-
840
-
841
-    /**
842
-     * Gets the URL of the thank you page with this registration REG_url_link added as
843
-     * a query parameter
844
-     *
845
-     * @return string
846
-     * @throws EE_Error
847
-     * @throws InvalidArgumentException
848
-     * @throws InvalidDataTypeException
849
-     * @throws InvalidInterfaceException
850
-     * @throws ReflectionException
851
-     */
852
-    public function payment_overview_url()
853
-    {
854
-        $primary_registration = $this->primary_registration();
855
-        return $primary_registration instanceof EE_Registration ? $primary_registration->payment_overview_url() : false;
856
-    }
857
-
858
-
859
-    /**
860
-     * @return string
861
-     * @throws EE_Error
862
-     * @throws InvalidArgumentException
863
-     * @throws InvalidDataTypeException
864
-     * @throws InvalidInterfaceException
865
-     * @throws ReflectionException
866
-     */
867
-    public function gateway_response_on_transaction()
868
-    {
869
-        $payment = $this->get_first_related('Payment');
870
-        return $payment instanceof EE_Payment ? $payment->gateway_response() : '';
871
-    }
872
-
873
-
874
-    /**
875
-     * Get the status object of this object
876
-     *
877
-     * @return EE_Base_Class|EE_Status
878
-     * @throws EE_Error
879
-     * @throws InvalidArgumentException
880
-     * @throws InvalidDataTypeException
881
-     * @throws InvalidInterfaceException
882
-     * @throws ReflectionException
883
-     */
884
-    public function status_obj()
885
-    {
886
-        return $this->get_first_related('Status');
887
-    }
888
-
889
-
890
-    /**
891
-     * Gets all the extra meta info on this payment
892
-     *
893
-     * @param array $query_params
894
-     * @return EE_Base_Class[]|EE_Extra_Meta
895
-     * @throws EE_Error
896
-     * @throws InvalidArgumentException
897
-     * @throws InvalidDataTypeException
898
-     * @throws InvalidInterfaceException
899
-     * @throws ReflectionException
900
-     * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
901
-     */
902
-    public function extra_meta($query_params = [])
903
-    {
904
-        return $this->get_many_related('Extra_Meta', $query_params);
905
-    }
906
-
907
-
908
-    /**
909
-     * Wrapper for _add_relation_to
910
-     *
911
-     * @param EE_Registration $registration
912
-     * @return EE_Base_Class the relation was added to
913
-     * @throws EE_Error
914
-     * @throws InvalidArgumentException
915
-     * @throws InvalidDataTypeException
916
-     * @throws InvalidInterfaceException
917
-     * @throws ReflectionException
918
-     */
919
-    public function add_registration(EE_Registration $registration)
920
-    {
921
-        return $this->_add_relation_to($registration, 'Registration');
922
-    }
923
-
924
-
925
-    /**
926
-     * Removes the given registration from being related (even before saving this transaction).
927
-     * If an ID/index is provided and this transaction isn't saved yet, removes it from list of cached relations
928
-     *
929
-     * @param int $registration_or_id
930
-     * @return EE_Base_Class that was removed from being related
931
-     * @throws EE_Error
932
-     * @throws InvalidArgumentException
933
-     * @throws InvalidDataTypeException
934
-     * @throws InvalidInterfaceException
935
-     * @throws ReflectionException
936
-     */
937
-    public function remove_registration_with_id($registration_or_id)
938
-    {
939
-        return $this->_remove_relation_to($registration_or_id, 'Registration');
940
-    }
941
-
942
-
943
-    /**
944
-     * Gets all the line items which are for ACTUAL items
945
-     *
946
-     * @return EE_Line_Item[]
947
-     * @throws EE_Error
948
-     * @throws InvalidArgumentException
949
-     * @throws InvalidDataTypeException
950
-     * @throws InvalidInterfaceException
951
-     * @throws ReflectionException
952
-     */
953
-    public function items_purchased()
954
-    {
955
-        return $this->line_items([['LIN_type' => EEM_Line_Item::type_line_item]]);
956
-    }
957
-
958
-
959
-    /**
960
-     * Wrapper for _add_relation_to
961
-     *
962
-     * @param EE_Line_Item $line_item
963
-     * @return EE_Base_Class the relation was added to
964
-     * @throws EE_Error
965
-     * @throws InvalidArgumentException
966
-     * @throws InvalidDataTypeException
967
-     * @throws InvalidInterfaceException
968
-     * @throws ReflectionException
969
-     */
970
-    public function add_line_item(EE_Line_Item $line_item)
971
-    {
972
-        return $this->_add_relation_to($line_item, 'Line_Item');
973
-    }
974
-
975
-
976
-    /**
977
-     * Gets ALL the line items related to this transaction (unstructured)
978
-     *
979
-     * @param array $query_params
980
-     * @return EE_Base_Class[]|EE_Line_Item[]
981
-     * @throws EE_Error
982
-     * @throws InvalidArgumentException
983
-     * @throws InvalidDataTypeException
984
-     * @throws InvalidInterfaceException
985
-     * @throws ReflectionException
986
-     */
987
-    public function line_items($query_params = [])
988
-    {
989
-        return $this->get_many_related('Line_Item', $query_params);
990
-    }
991
-
992
-
993
-    /**
994
-     * Gets all the line items which are taxes on the total
995
-     *
996
-     * @return EE_Line_Item[]
997
-     * @throws EE_Error
998
-     * @throws InvalidArgumentException
999
-     * @throws InvalidDataTypeException
1000
-     * @throws InvalidInterfaceException
1001
-     * @throws ReflectionException
1002
-     */
1003
-    public function tax_items()
1004
-    {
1005
-        return $this->line_items([['LIN_type' => EEM_Line_Item::type_tax]]);
1006
-    }
1007
-
1008
-
1009
-    /**
1010
-     * Gets the total line item (which is a parent of all other related line items,
1011
-     * meaning it takes them all into account on its total)
1012
-     *
1013
-     * @param bool $create_if_not_found
1014
-     * @return \EE_Line_Item
1015
-     * @throws EE_Error
1016
-     * @throws InvalidArgumentException
1017
-     * @throws InvalidDataTypeException
1018
-     * @throws InvalidInterfaceException
1019
-     * @throws ReflectionException
1020
-     */
1021
-    public function total_line_item($create_if_not_found = true)
1022
-    {
1023
-        $item = $this->get_first_related('Line_Item', [['LIN_type' => EEM_Line_Item::type_total]]);
1024
-        if (! $item && $create_if_not_found) {
1025
-            $item = EEH_Line_Item::create_total_line_item($this);
1026
-        }
1027
-        return $item;
1028
-    }
1029
-
1030
-
1031
-    /**
1032
-     * Returns the total amount of tax on this transaction
1033
-     * (assumes there's only one tax subtotal line item)
1034
-     *
1035
-     * @return float
1036
-     * @throws EE_Error
1037
-     * @throws InvalidArgumentException
1038
-     * @throws InvalidDataTypeException
1039
-     * @throws InvalidInterfaceException
1040
-     * @throws ReflectionException
1041
-     */
1042
-    public function tax_total()
1043
-    {
1044
-        $tax_line_item = $this->tax_total_line_item();
1045
-        if ($tax_line_item) {
1046
-            return (float) $tax_line_item->total();
1047
-        }
1048
-        return (float) 0;
1049
-    }
1050
-
1051
-
1052
-    /**
1053
-     * Gets the tax subtotal line item (assumes there's only one)
1054
-     *
1055
-     * @return EE_Line_Item
1056
-     * @throws EE_Error
1057
-     * @throws InvalidArgumentException
1058
-     * @throws InvalidDataTypeException
1059
-     * @throws InvalidInterfaceException
1060
-     * @throws ReflectionException
1061
-     */
1062
-    public function tax_total_line_item()
1063
-    {
1064
-        return EEH_Line_Item::get_taxes_subtotal($this->total_line_item());
1065
-    }
1066
-
1067
-
1068
-    /**
1069
-     * Gets the array of billing info for the gateway and for this transaction's primary registration's attendee.
1070
-     *
1071
-     * @return EE_Form_Section_Proper
1072
-     * @throws EE_Error
1073
-     * @throws InvalidArgumentException
1074
-     * @throws InvalidDataTypeException
1075
-     * @throws InvalidInterfaceException
1076
-     * @throws ReflectionException
1077
-     */
1078
-    public function billing_info()
1079
-    {
1080
-        $payment_method = $this->payment_method();
1081
-        if (! $payment_method) {
1082
-            EE_Error::add_error(
1083
-                esc_html__(
1084
-                    'Could not find billing info for transaction because no gateway has been used for it yet',
1085
-                    'event_espresso'
1086
-                ),
1087
-                __FILE__,
1088
-                __FUNCTION__,
1089
-                __LINE__
1090
-            );
1091
-            return null;
1092
-        }
1093
-        $primary_reg = $this->primary_registration();
1094
-        if (! $primary_reg) {
1095
-            EE_Error::add_error(
1096
-                esc_html__(
1097
-                    'Cannot get billing info for gateway %s on transaction because no primary registration exists',
1098
-                    'event_espresso'
1099
-                ),
1100
-                __FILE__,
1101
-                __FUNCTION__,
1102
-                __LINE__
1103
-            );
1104
-            return null;
1105
-        }
1106
-        $attendee = $primary_reg->attendee();
1107
-        if (! $attendee) {
1108
-            EE_Error::add_error(
1109
-                esc_html__(
1110
-                    'Cannot get billing info for gateway %s on transaction because the primary registration has no attendee exists',
1111
-                    'event_espresso'
1112
-                ),
1113
-                __FILE__,
1114
-                __FUNCTION__,
1115
-                __LINE__
1116
-            );
1117
-            return null;
1118
-        }
1119
-        return $attendee->billing_info_for_payment_method($payment_method);
1120
-    }
1121
-
1122
-
1123
-    /**
1124
-     * Gets PMD_ID
1125
-     *
1126
-     * @return int
1127
-     * @throws EE_Error
1128
-     * @throws InvalidArgumentException
1129
-     * @throws InvalidDataTypeException
1130
-     * @throws InvalidInterfaceException
1131
-     * @throws ReflectionException
1132
-     */
1133
-    public function payment_method_ID()
1134
-    {
1135
-        return $this->get('PMD_ID');
1136
-    }
1137
-
1138
-
1139
-    /**
1140
-     * Sets PMD_ID
1141
-     *
1142
-     * @param int $PMD_ID
1143
-     * @throws EE_Error
1144
-     * @throws InvalidArgumentException
1145
-     * @throws InvalidDataTypeException
1146
-     * @throws InvalidInterfaceException
1147
-     * @throws ReflectionException
1148
-     */
1149
-    public function set_payment_method_ID($PMD_ID)
1150
-    {
1151
-        $this->set('PMD_ID', $PMD_ID);
1152
-    }
1153
-
1154
-
1155
-    /**
1156
-     * Gets the last-used payment method on this transaction
1157
-     * (we COULD just use the last-made payment, but some payment methods, namely
1158
-     * offline ones, dont' create payments)
1159
-     *
1160
-     * @return EE_Payment_Method
1161
-     * @throws EE_Error
1162
-     * @throws InvalidArgumentException
1163
-     * @throws InvalidDataTypeException
1164
-     * @throws InvalidInterfaceException
1165
-     * @throws ReflectionException
1166
-     */
1167
-    public function payment_method()
1168
-    {
1169
-        $pm = $this->get_first_related('Payment_Method');
1170
-        if ($pm instanceof EE_Payment_Method) {
1171
-            return $pm;
1172
-        }
1173
-        $last_payment = $this->last_payment();
1174
-        if ($last_payment instanceof EE_Payment && $last_payment->payment_method()) {
1175
-            return $last_payment->payment_method();
1176
-        }
1177
-        return null;
1178
-    }
1179
-
1180
-
1181
-    /**
1182
-     * Gets the last payment made
1183
-     *
1184
-     * @return EE_Base_Class|EE_Payment
1185
-     * @throws EE_Error
1186
-     * @throws InvalidArgumentException
1187
-     * @throws InvalidDataTypeException
1188
-     * @throws InvalidInterfaceException
1189
-     * @throws ReflectionException
1190
-     */
1191
-    public function last_payment()
1192
-    {
1193
-        return $this->get_first_related('Payment', ['order_by' => ['PAY_ID' => 'desc']]);
1194
-    }
1195
-
1196
-
1197
-    /**
1198
-     * Gets all the line items which are unrelated to tickets on this transaction
1199
-     *
1200
-     * @return EE_Line_Item[]
1201
-     * @throws EE_Error
1202
-     * @throws InvalidArgumentException
1203
-     * @throws InvalidDataTypeException
1204
-     * @throws InvalidInterfaceException
1205
-     * @throws ReflectionException
1206
-     */
1207
-    public function non_ticket_line_items()
1208
-    {
1209
-        return EEM_Line_Item::instance()->get_all_non_ticket_line_items_for_transaction($this->ID());
1210
-    }
1211
-
1212
-
1213
-    /**
1214
-     * possibly toggles TXN status
1215
-     *
1216
-     * @param boolean $update whether to save the TXN
1217
-     * @return bool whether the TXN was saved
1218
-     * @throws EE_Error
1219
-     * @throws InvalidArgumentException
1220
-     * @throws InvalidDataTypeException
1221
-     * @throws InvalidInterfaceException
1222
-     * @throws ReflectionException
1223
-     * @throws RuntimeException
1224
-     */
1225
-    public function update_status_based_on_total_paid($update = true)
1226
-    {
1227
-        // set transaction status based on comparison of TXN_paid vs TXN_total
1228
-        if (EEH_Money::compare_floats($this->paid(), $this->total(), '>')) {
1229
-            $new_txn_status = EEM_Transaction::overpaid_status_code;
1230
-        } elseif (EEH_Money::compare_floats($this->paid(), $this->total())) {
1231
-            $new_txn_status = EEM_Transaction::complete_status_code;
1232
-        } elseif (EEH_Money::compare_floats($this->paid(), $this->total(), '<')) {
1233
-            $new_txn_status = EEM_Transaction::incomplete_status_code;
1234
-        } else {
1235
-            throw new RuntimeException(
1236
-                esc_html__('The total paid calculation for this transaction is inaccurate.', 'event_espresso')
1237
-            );
1238
-        }
1239
-        if ($new_txn_status !== $this->status_ID()) {
1240
-            $this->set_status($new_txn_status);
1241
-            if ($update) {
1242
-                return (bool) $this->save();
1243
-            }
1244
-        }
1245
-        return false;
1246
-    }
1247
-
1248
-
1249
-    /**
1250
-     * Updates the transaction's status and total_paid based on all the payments
1251
-     * that apply to it
1252
-     *
1253
-     * @return array|bool
1254
-     * @throws EE_Error
1255
-     * @throws InvalidArgumentException
1256
-     * @throws ReflectionException
1257
-     * @throws InvalidDataTypeException
1258
-     * @throws InvalidInterfaceException
1259
-     * @deprecated
1260
-     */
1261
-    public function update_based_on_payments()
1262
-    {
1263
-        EE_Error::doing_it_wrong(
1264
-            __CLASS__ . '::' . __FUNCTION__,
1265
-            sprintf(
1266
-                esc_html__('This method is deprecated. Please use "%s" instead', 'event_espresso'),
1267
-                'EE_Transaction_Processor::update_transaction_and_registrations_after_checkout_or_payment()'
1268
-            ),
1269
-            '4.6.0'
1270
-        );
1271
-        /** @type EE_Transaction_Processor $transaction_processor */
1272
-        $transaction_processor = EE_Registry::instance()->load_class('Transaction_Processor');
1273
-        return $transaction_processor->update_transaction_and_registrations_after_checkout_or_payment($this);
1274
-    }
1275
-
1276
-
1277
-    /**
1278
-     * @return string
1279
-     */
1280
-    public function old_txn_status()
1281
-    {
1282
-        return $this->_old_txn_status;
1283
-    }
1284
-
1285
-
1286
-    /**
1287
-     * @param string $old_txn_status
1288
-     */
1289
-    public function set_old_txn_status($old_txn_status)
1290
-    {
1291
-        // only set the first time
1292
-        if ($this->_old_txn_status === null) {
1293
-            $this->_old_txn_status = $old_txn_status;
1294
-        }
1295
-    }
1296
-
1297
-
1298
-    /**
1299
-     * reg_status_updated
1300
-     *
1301
-     * @return bool
1302
-     * @throws EE_Error
1303
-     * @throws InvalidArgumentException
1304
-     * @throws InvalidDataTypeException
1305
-     * @throws InvalidInterfaceException
1306
-     * @throws ReflectionException
1307
-     */
1308
-    public function txn_status_updated()
1309
-    {
1310
-        return $this->status_ID() !== $this->_old_txn_status && $this->_old_txn_status !== null;
1311
-    }
1312
-
1313
-
1314
-    /**
1315
-     * _reg_steps_completed
1316
-     * if $check_all is TRUE, then returns TRUE if ALL reg steps have been marked as completed,
1317
-     * if a $reg_step_slug is provided, then this step will be skipped when testing for completion
1318
-     * if $check_all is FALSE and a $reg_step_slug is provided, then ONLY that reg step will be tested for completion
1319
-     *
1320
-     * @param string $reg_step_slug
1321
-     * @param bool   $check_all
1322
-     * @return bool|int
1323
-     * @throws EE_Error
1324
-     * @throws InvalidArgumentException
1325
-     * @throws InvalidDataTypeException
1326
-     * @throws InvalidInterfaceException
1327
-     * @throws ReflectionException
1328
-     */
1329
-    private function _reg_steps_completed($reg_step_slug = '', $check_all = true)
1330
-    {
1331
-        $reg_steps = $this->reg_steps();
1332
-        if (! is_array($reg_steps) || empty($reg_steps)) {
1333
-            return false;
1334
-        }
1335
-        // loop thru reg steps array)
1336
-        foreach ($reg_steps as $slug => $reg_step_completed) {
1337
-            // if NOT checking ALL steps (only checking one step)
1338
-            if (! $check_all) {
1339
-                // and this is the one
1340
-                if ($slug === $reg_step_slug) {
1341
-                    return $reg_step_completed;
1342
-                }
1343
-                // skip to next reg step in loop
1344
-                continue;
1345
-            }
1346
-            // $check_all must be true, else we would never have gotten to this point
1347
-            if ($slug === $reg_step_slug) {
1348
-                // if we reach this point, then we are testing either:
1349
-                // all_reg_steps_completed_except() or
1350
-                // all_reg_steps_completed_except_final_step(),
1351
-                // and since this is the reg step EXCEPTION being tested
1352
-                // we want to return true (yes true) if this reg step is NOT completed
1353
-                // ie: "is everything completed except the final step?"
1354
-                // "that is correct... the final step is not completed, but all others are."
1355
-                return $reg_step_completed !== true;
1356
-            }
1357
-            if ($reg_step_completed !== true) {
1358
-                // if any reg step is NOT completed, then ALL steps are not completed
1359
-                return false;
1360
-            }
1361
-        }
1362
-        return true;
1363
-    }
1364
-
1365
-
1366
-    /**
1367
-     * all_reg_steps_completed
1368
-     * returns:
1369
-     *    true if ALL reg steps have been marked as completed
1370
-     *        or false if any step is not completed
1371
-     *
1372
-     * @return bool
1373
-     * @throws EE_Error
1374
-     * @throws InvalidArgumentException
1375
-     * @throws InvalidDataTypeException
1376
-     * @throws InvalidInterfaceException
1377
-     * @throws ReflectionException
1378
-     */
1379
-    public function all_reg_steps_completed()
1380
-    {
1381
-        return $this->_reg_steps_completed();
1382
-    }
1383
-
1384
-
1385
-    /**
1386
-     * all_reg_steps_completed_except
1387
-     * returns:
1388
-     *        true if ALL reg steps, except a particular step that you wish to skip over, have been marked as completed
1389
-     *        or false if any other step is not completed
1390
-     *        or false if ALL steps are completed including the exception you are testing !!!
1391
-     *
1392
-     * @param string $exception
1393
-     * @return bool
1394
-     * @throws EE_Error
1395
-     * @throws InvalidArgumentException
1396
-     * @throws InvalidDataTypeException
1397
-     * @throws InvalidInterfaceException
1398
-     * @throws ReflectionException
1399
-     */
1400
-    public function all_reg_steps_completed_except($exception = '')
1401
-    {
1402
-        return $this->_reg_steps_completed($exception);
1403
-    }
1404
-
1405
-
1406
-    /**
1407
-     * all_reg_steps_completed_except
1408
-     * returns:
1409
-     *        true if ALL reg steps, except the final step, have been marked as completed
1410
-     *        or false if any step is not completed
1411
-     *    or false if ALL steps are completed including the final step !!!
1412
-     *
1413
-     * @return bool
1414
-     * @throws EE_Error
1415
-     * @throws InvalidArgumentException
1416
-     * @throws InvalidDataTypeException
1417
-     * @throws InvalidInterfaceException
1418
-     * @throws ReflectionException
1419
-     */
1420
-    public function all_reg_steps_completed_except_final_step()
1421
-    {
1422
-        return $this->_reg_steps_completed('finalize_registration');
1423
-    }
1424
-
1425
-
1426
-    /**
1427
-     * reg_step_completed
1428
-     * returns:
1429
-     *    true if a specific reg step has been marked as completed
1430
-     *    a Unix timestamp if it has been initialized but not yet completed,
1431
-     *    or false if it has not yet been initialized
1432
-     *
1433
-     * @param string $reg_step_slug
1434
-     * @return bool|int
1435
-     * @throws EE_Error
1436
-     * @throws InvalidArgumentException
1437
-     * @throws InvalidDataTypeException
1438
-     * @throws InvalidInterfaceException
1439
-     * @throws ReflectionException
1440
-     */
1441
-    public function reg_step_completed($reg_step_slug)
1442
-    {
1443
-        return $this->_reg_steps_completed($reg_step_slug, false);
1444
-    }
1445
-
1446
-
1447
-    /**
1448
-     * completed_final_reg_step
1449
-     * returns:
1450
-     *    true if the finalize_registration reg step has been marked as completed
1451
-     *    a Unix timestamp if it has been initialized but not yet completed,
1452
-     *    or false if it has not yet been initialized
1453
-     *
1454
-     * @return bool|int
1455
-     * @throws EE_Error
1456
-     * @throws InvalidArgumentException
1457
-     * @throws InvalidDataTypeException
1458
-     * @throws InvalidInterfaceException
1459
-     * @throws ReflectionException
1460
-     */
1461
-    public function final_reg_step_completed()
1462
-    {
1463
-        return $this->_reg_steps_completed('finalize_registration', false);
1464
-    }
1465
-
1466
-
1467
-    /**
1468
-     * set_reg_step_initiated
1469
-     * given a valid TXN_reg_step, this sets it's value to a unix timestamp
1470
-     *
1471
-     * @param string $reg_step_slug
1472
-     * @return boolean
1473
-     * @throws EE_Error
1474
-     * @throws InvalidArgumentException
1475
-     * @throws InvalidDataTypeException
1476
-     * @throws InvalidInterfaceException
1477
-     * @throws ReflectionException
1478
-     */
1479
-    public function set_reg_step_initiated($reg_step_slug)
1480
-    {
1481
-        return $this->_set_reg_step_completed_status($reg_step_slug, time());
1482
-    }
1483
-
1484
-
1485
-    /**
1486
-     * set_reg_step_completed
1487
-     * given a valid TXN_reg_step, this sets the step as completed
1488
-     *
1489
-     * @param string $reg_step_slug
1490
-     * @return boolean
1491
-     * @throws EE_Error
1492
-     * @throws InvalidArgumentException
1493
-     * @throws InvalidDataTypeException
1494
-     * @throws InvalidInterfaceException
1495
-     * @throws ReflectionException
1496
-     */
1497
-    public function set_reg_step_completed($reg_step_slug)
1498
-    {
1499
-        return $this->_set_reg_step_completed_status($reg_step_slug, true);
1500
-    }
1501
-
1502
-
1503
-    /**
1504
-     * set_reg_step_completed
1505
-     * given a valid TXN_reg_step slug, this sets the step as NOT completed
1506
-     *
1507
-     * @param string $reg_step_slug
1508
-     * @return boolean
1509
-     * @throws EE_Error
1510
-     * @throws InvalidArgumentException
1511
-     * @throws InvalidDataTypeException
1512
-     * @throws InvalidInterfaceException
1513
-     * @throws ReflectionException
1514
-     */
1515
-    public function set_reg_step_not_completed($reg_step_slug)
1516
-    {
1517
-        return $this->_set_reg_step_completed_status($reg_step_slug, false);
1518
-    }
1519
-
1520
-
1521
-    /**
1522
-     * set_reg_step_completed
1523
-     * given a valid reg step slug, this sets the TXN_reg_step completed status which is either:
1524
-     *
1525
-     * @param string      $reg_step_slug
1526
-     * @param boolean|int $status
1527
-     * @return boolean
1528
-     * @throws EE_Error
1529
-     * @throws InvalidArgumentException
1530
-     * @throws InvalidDataTypeException
1531
-     * @throws InvalidInterfaceException
1532
-     * @throws ReflectionException
1533
-     */
1534
-    private function _set_reg_step_completed_status($reg_step_slug, $status)
1535
-    {
1536
-        // validate status
1537
-        $status = is_bool($status) || is_int($status) ? $status : false;
1538
-        // get reg steps array
1539
-        $txn_reg_steps = $this->reg_steps();
1540
-        // if reg step does NOT exist
1541
-        if (! isset($txn_reg_steps[ $reg_step_slug ])) {
1542
-            return false;
1543
-        }
1544
-        // if  we're trying to complete a step that is already completed
1545
-        if ($txn_reg_steps[ $reg_step_slug ] === true) {
1546
-            return true;
1547
-        }
1548
-        // if  we're trying to complete a step that hasn't even started
1549
-        if ($status === true && $txn_reg_steps[ $reg_step_slug ] === false) {
1550
-            return false;
1551
-        }
1552
-        // if current status value matches the incoming value (no change)
1553
-        // type casting as int means values should collapse to either 0, 1, or a timestamp like 1234567890
1554
-        if ((int) $txn_reg_steps[ $reg_step_slug ] === (int) $status) {
1555
-            // this will happen in cases where multiple AJAX requests occur during the same step
1556
-            return true;
1557
-        }
1558
-        // if we're trying to set a start time, but it has already been set...
1559
-        if (is_numeric($status) && is_numeric($txn_reg_steps[ $reg_step_slug ])) {
1560
-            // skip the update below, but don't return FALSE so that errors won't be displayed
1561
-            return true;
1562
-        }
1563
-        // update completed status
1564
-        $txn_reg_steps[ $reg_step_slug ] = $status;
1565
-        $this->set_reg_steps($txn_reg_steps);
1566
-        $this->save();
1567
-        return true;
1568
-    }
1569
-
1570
-
1571
-    /**
1572
-     * remove_reg_step
1573
-     * given a valid TXN_reg_step slug, this will remove (unset)
1574
-     * the reg step from the TXN reg step array
1575
-     *
1576
-     * @param string $reg_step_slug
1577
-     * @return void
1578
-     * @throws EE_Error
1579
-     * @throws InvalidArgumentException
1580
-     * @throws InvalidDataTypeException
1581
-     * @throws InvalidInterfaceException
1582
-     * @throws ReflectionException
1583
-     */
1584
-    public function remove_reg_step($reg_step_slug)
1585
-    {
1586
-        // get reg steps array
1587
-        $txn_reg_steps = $this->reg_steps();
1588
-        unset($txn_reg_steps[ $reg_step_slug ]);
1589
-        $this->set_reg_steps($txn_reg_steps);
1590
-    }
1591
-
1592
-
1593
-    /**
1594
-     * toggle_failed_transaction_status
1595
-     * upgrades a TXNs status from failed to abandoned,
1596
-     * meaning that contact information has been captured for at least one registrant
1597
-     *
1598
-     * @param bool $save
1599
-     * @return bool
1600
-     * @throws EE_Error
1601
-     * @throws InvalidArgumentException
1602
-     * @throws InvalidDataTypeException
1603
-     * @throws InvalidInterfaceException
1604
-     * @throws ReflectionException
1605
-     */
1606
-    public function toggle_failed_transaction_status($save = true)
1607
-    {
1608
-        // if TXN status is still set as "failed"...
1609
-        if ($this->status_ID() === EEM_Transaction::failed_status_code) {
1610
-            $this->set_status(EEM_Transaction::abandoned_status_code);
1611
-            if ($save) {
1612
-                $this->save();
1613
-            }
1614
-            return true;
1615
-        }
1616
-        return false;
1617
-    }
1618
-
1619
-
1620
-    /**
1621
-     * toggle_abandoned_transaction_status
1622
-     * upgrades a TXNs status from failed or abandoned to incomplete
1623
-     *
1624
-     * @return bool
1625
-     * @throws EE_Error
1626
-     * @throws InvalidArgumentException
1627
-     * @throws InvalidDataTypeException
1628
-     * @throws InvalidInterfaceException
1629
-     * @throws ReflectionException
1630
-     */
1631
-    public function toggle_abandoned_transaction_status()
1632
-    {
1633
-        // if TXN status has not been updated already due to a payment, and is still set as "failed" or "abandoned"...
1634
-        $txn_status = $this->status_ID();
1635
-        if (
1636
-            $txn_status === EEM_Transaction::failed_status_code
1637
-            || $txn_status === EEM_Transaction::abandoned_status_code
1638
-        ) {
1639
-            // if a contact record for the primary registrant has been created
1640
-            if (
1641
-                $this->primary_registration() instanceof EE_Registration
1642
-                && $this->primary_registration()->attendee() instanceof EE_Attendee
1643
-            ) {
1644
-                $this->set_status(EEM_Transaction::incomplete_status_code);
1645
-            } else {
1646
-                // no contact record? yer abandoned!
1647
-                $this->set_status(EEM_Transaction::abandoned_status_code);
1648
-            }
1649
-            return true;
1650
-        }
1651
-        return false;
1652
-    }
1653
-
1654
-
1655
-    /**
1656
-     * checks if an Abandoned TXN has any related payments, and if so,
1657
-     * updates the TXN status based on the amount paid
1658
-     *
1659
-     * @throws EE_Error
1660
-     * @throws InvalidArgumentException
1661
-     * @throws InvalidDataTypeException
1662
-     * @throws InvalidInterfaceException
1663
-     * @throws ReflectionException
1664
-     * @throws RuntimeException
1665
-     * @throws ReflectionException
1666
-     */
1667
-    public function verify_abandoned_transaction_status()
1668
-    {
1669
-        if ($this->status_ID() !== EEM_Transaction::abandoned_status_code) {
1670
-            return;
1671
-        }
1672
-        $payments = $this->get_many_related('Payment');
1673
-        if (! empty($payments)) {
1674
-            foreach ($payments as $payment) {
1675
-                if ($payment instanceof EE_Payment) {
1676
-                    // kk this TXN should NOT be abandoned
1677
-                    $this->update_status_based_on_total_paid();
1678
-                    if (! (defined('DOING_AJAX') && DOING_AJAX) && is_admin()) {
1679
-                        EE_Error::add_attention(
1680
-                            sprintf(
1681
-                                esc_html__(
1682
-                                    'The status for Transaction #%1$d has been updated from "Abandoned" to "%2$s", because at least one payment has been made towards it. If the payment appears in the "Payment Details" table below, you may need to edit its status and/or other details as well.',
1683
-                                    'event_espresso'
1684
-                                ),
1685
-                                $this->ID(),
1686
-                                $this->pretty_status()
1687
-                            )
1688
-                        );
1689
-                    }
1690
-                    // get final reg step status
1691
-                    $finalized = $this->final_reg_step_completed();
1692
-                    // if the 'finalize_registration' step has been initiated (has a timestamp)
1693
-                    // but has not yet been fully completed (TRUE)
1694
-                    if (is_int($finalized) && $finalized !== false && $finalized !== true) {
1695
-                        $this->set_reg_step_completed('finalize_registration');
1696
-                        $this->save();
1697
-                    }
1698
-                }
1699
-            }
1700
-        }
1701
-    }
1702
-
1703
-
1704
-    /**
1705
-     * @throws EE_Error
1706
-     * @throws InvalidArgumentException
1707
-     * @throws InvalidDataTypeException
1708
-     * @throws InvalidInterfaceException
1709
-     * @throws ReflectionException
1710
-     * @throws RuntimeException
1711
-     * @since 4.10.4.p
1712
-     */
1713
-    public function recalculateLineItems()
1714
-    {
1715
-        $total_line_item = $this->total_line_item(false);
1716
-        if ($total_line_item instanceof EE_Line_Item) {
1717
-            EEH_Line_Item::resetIsTaxableForTickets($total_line_item);
1718
-            return EEH_Line_Item::apply_taxes($total_line_item, true);
1719
-        }
1720
-        return false;
1721
-    }
16
+	/**
17
+	 * The length of time in seconds that a lock is applied before being considered expired.
18
+	 * It is not long because a transaction should only be locked for the duration of the request that locked it
19
+	 */
20
+	const LOCK_EXPIRATION = 2;
21
+
22
+	/**
23
+	 * txn status upon initial construction.
24
+	 *
25
+	 * @var string
26
+	 */
27
+	protected $_old_txn_status;
28
+
29
+
30
+	/**
31
+	 * @param array  $props_n_values          incoming values
32
+	 * @param string $timezone                incoming timezone
33
+	 *                                        (if not set the timezone set for the website will be used.)
34
+	 * @param array  $date_formats            incoming date_formats in an array where the first value is the
35
+	 *                                        date_format and the second value is the time format
36
+	 * @return EE_Transaction
37
+	 * @throws EE_Error
38
+	 * @throws InvalidArgumentException
39
+	 * @throws InvalidDataTypeException
40
+	 * @throws InvalidInterfaceException
41
+	 * @throws ReflectionException
42
+	 */
43
+	public static function new_instance($props_n_values = [], $timezone = null, $date_formats = [])
44
+	{
45
+		$has_object = parent::_check_for_object($props_n_values, __CLASS__, $timezone, $date_formats);
46
+		$txn        = $has_object
47
+			? $has_object
48
+			: new self($props_n_values, false, $timezone, $date_formats);
49
+		if (! $has_object) {
50
+			$txn->set_old_txn_status($txn->status_ID());
51
+		}
52
+		return $txn;
53
+	}
54
+
55
+
56
+	/**
57
+	 * @param array  $props_n_values  incoming values from the database
58
+	 * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
59
+	 *                                the website will be used.
60
+	 * @return EE_Transaction
61
+	 * @throws EE_Error
62
+	 * @throws InvalidArgumentException
63
+	 * @throws InvalidDataTypeException
64
+	 * @throws InvalidInterfaceException
65
+	 * @throws ReflectionException
66
+	 */
67
+	public static function new_instance_from_db($props_n_values = [], $timezone = null)
68
+	{
69
+		$txn = new self($props_n_values, true, $timezone);
70
+		$txn->set_old_txn_status($txn->status_ID());
71
+		return $txn;
72
+	}
73
+
74
+
75
+	/**
76
+	 * Sets a meta field indicating that this TXN is locked and should not be updated in the db.
77
+	 * If a lock has already been set, then we will attempt to remove it in case it has expired.
78
+	 * If that also fails, then an exception is thrown.
79
+	 *
80
+	 * @throws EE_Error
81
+	 * @throws InvalidArgumentException
82
+	 * @throws InvalidDataTypeException
83
+	 * @throws InvalidInterfaceException
84
+	 * @throws ReflectionException
85
+	 */
86
+	public function lock()
87
+	{
88
+		// attempt to set lock, but if that fails...
89
+		if (! $this->add_extra_meta('lock', time(), true)) {
90
+			// then attempt to remove the lock in case it is expired
91
+			if ($this->_remove_expired_lock()) {
92
+				// if removal was successful, then try setting lock again
93
+				$this->lock();
94
+			} else {
95
+				// but if the lock can not be removed, then throw an exception
96
+				throw new EE_Error(
97
+					sprintf(
98
+						esc_html__(
99
+							'Could not lock Transaction %1$d because it is already locked, meaning another part of the system is currently editing it. It should already be unlocked by the time you read this, so please refresh the page and try again.',
100
+							'event_espresso'
101
+						),
102
+						$this->ID()
103
+					)
104
+				);
105
+			}
106
+		}
107
+	}
108
+
109
+
110
+	/**
111
+	 * removes transaction lock applied in EE_Transaction::lock()
112
+	 *
113
+	 * @return int
114
+	 * @throws EE_Error
115
+	 * @throws InvalidArgumentException
116
+	 * @throws InvalidDataTypeException
117
+	 * @throws InvalidInterfaceException
118
+	 * @throws ReflectionException
119
+	 */
120
+	public function unlock()
121
+	{
122
+		return $this->delete_extra_meta('lock');
123
+	}
124
+
125
+
126
+	/**
127
+	 * Decides whether or not now is the right time to update the transaction.
128
+	 * This is useful because we don't always know if it is safe to update the transaction
129
+	 * and its related data. why?
130
+	 * because it's possible that the transaction is being used in another
131
+	 * request and could overwrite anything we save.
132
+	 * So we want to only update the txn once we know that won't happen.
133
+	 * We also check that the lock isn't expired, and remove it if it is
134
+	 *
135
+	 * @return boolean
136
+	 * @throws EE_Error
137
+	 * @throws InvalidArgumentException
138
+	 * @throws InvalidDataTypeException
139
+	 * @throws InvalidInterfaceException
140
+	 * @throws ReflectionException
141
+	 */
142
+	public function is_locked()
143
+	{
144
+		// if TXN is not locked, then return false immediately
145
+		if (! $this->_get_lock()) {
146
+			return false;
147
+		}
148
+		// if not, then let's try and remove the lock in case it's expired...
149
+		// _remove_expired_lock() returns 0 when lock is valid (ie: removed = false)
150
+		// and a positive number if the lock was removed (ie: number of locks deleted),
151
+		// so we need to return the opposite
152
+		return ! $this->_remove_expired_lock() ? true : false;
153
+	}
154
+
155
+
156
+	/**
157
+	 * Gets the meta field indicating that this TXN is locked
158
+	 *
159
+	 * @return int
160
+	 * @throws EE_Error
161
+	 * @throws InvalidArgumentException
162
+	 * @throws InvalidDataTypeException
163
+	 * @throws InvalidInterfaceException
164
+	 * @throws ReflectionException
165
+	 */
166
+	protected function _get_lock()
167
+	{
168
+		return (int) $this->get_extra_meta('lock', true, 0);
169
+	}
170
+
171
+
172
+	/**
173
+	 * If the lock on this transaction is expired, then we want to remove it so that the transaction can be updated
174
+	 *
175
+	 * @return int
176
+	 * @throws EE_Error
177
+	 * @throws InvalidArgumentException
178
+	 * @throws InvalidDataTypeException
179
+	 * @throws InvalidInterfaceException
180
+	 * @throws ReflectionException
181
+	 */
182
+	protected function _remove_expired_lock()
183
+	{
184
+		$locked = $this->_get_lock();
185
+		if ($locked && time() - EE_Transaction::LOCK_EXPIRATION > $locked) {
186
+			return $this->unlock();
187
+		}
188
+		return 0;
189
+	}
190
+
191
+
192
+	/**
193
+	 * Set transaction total
194
+	 *
195
+	 * @param float $total total value of transaction
196
+	 * @throws EE_Error
197
+	 * @throws InvalidArgumentException
198
+	 * @throws InvalidDataTypeException
199
+	 * @throws InvalidInterfaceException
200
+	 * @throws ReflectionException
201
+	 */
202
+	public function set_total($total = 0.00)
203
+	{
204
+		$this->set('TXN_total', (float) $total);
205
+	}
206
+
207
+
208
+	/**
209
+	 * Set Total Amount Paid to Date
210
+	 *
211
+	 * @param float $total_paid total amount paid to date (sum of all payments)
212
+	 * @throws EE_Error
213
+	 * @throws InvalidArgumentException
214
+	 * @throws InvalidDataTypeException
215
+	 * @throws InvalidInterfaceException
216
+	 * @throws ReflectionException
217
+	 */
218
+	public function set_paid($total_paid = 0.00)
219
+	{
220
+		$this->set('TXN_paid', (float) $total_paid);
221
+	}
222
+
223
+
224
+	/**
225
+	 * Set transaction status
226
+	 *
227
+	 * @param string $status        whether the transaction is open, declined, accepted,
228
+	 *                              or any number of custom values that can be set
229
+	 * @throws EE_Error
230
+	 * @throws InvalidArgumentException
231
+	 * @throws InvalidDataTypeException
232
+	 * @throws InvalidInterfaceException
233
+	 * @throws ReflectionException
234
+	 */
235
+	public function set_status($status = '')
236
+	{
237
+		$this->set('STS_ID', $status);
238
+	}
239
+
240
+
241
+	/**
242
+	 * Set hash salt
243
+	 *
244
+	 * @param string $hash_salt required for some payment gateways
245
+	 * @throws EE_Error
246
+	 * @throws InvalidArgumentException
247
+	 * @throws InvalidDataTypeException
248
+	 * @throws InvalidInterfaceException
249
+	 * @throws ReflectionException
250
+	 */
251
+	public function set_hash_salt($hash_salt = '')
252
+	{
253
+		$this->set('TXN_hash_salt', $hash_salt);
254
+	}
255
+
256
+
257
+	/**
258
+	 * Sets TXN_reg_steps array
259
+	 *
260
+	 * @param array $txn_reg_steps
261
+	 * @throws EE_Error
262
+	 * @throws InvalidArgumentException
263
+	 * @throws InvalidDataTypeException
264
+	 * @throws InvalidInterfaceException
265
+	 * @throws ReflectionException
266
+	 */
267
+	public function set_reg_steps(array $txn_reg_steps)
268
+	{
269
+		$this->set('TXN_reg_steps', $txn_reg_steps);
270
+	}
271
+
272
+
273
+	/**
274
+	 * Gets TXN_reg_steps
275
+	 *
276
+	 * @return array
277
+	 * @throws EE_Error
278
+	 * @throws InvalidArgumentException
279
+	 * @throws InvalidDataTypeException
280
+	 * @throws InvalidInterfaceException
281
+	 * @throws ReflectionException
282
+	 */
283
+	public function reg_steps()
284
+	{
285
+		$TXN_reg_steps = $this->get('TXN_reg_steps');
286
+		return is_array($TXN_reg_steps) ? $TXN_reg_steps : [];
287
+	}
288
+
289
+
290
+	/**
291
+	 * @return string of transaction's total cost, with currency symbol and decimal
292
+	 * @throws EE_Error
293
+	 * @throws InvalidArgumentException
294
+	 * @throws InvalidDataTypeException
295
+	 * @throws InvalidInterfaceException
296
+	 * @throws ReflectionException
297
+	 */
298
+	public function pretty_total()
299
+	{
300
+		return $this->get_pretty('TXN_total');
301
+	}
302
+
303
+
304
+	/**
305
+	 * Gets the amount paid in a pretty string (formatted and with currency symbol)
306
+	 *
307
+	 * @return string
308
+	 * @throws EE_Error
309
+	 * @throws InvalidArgumentException
310
+	 * @throws InvalidDataTypeException
311
+	 * @throws InvalidInterfaceException
312
+	 * @throws ReflectionException
313
+	 */
314
+	public function pretty_paid()
315
+	{
316
+		return $this->get_pretty('TXN_paid');
317
+	}
318
+
319
+
320
+	/**
321
+	 * calculate the amount remaining for this transaction and return;
322
+	 *
323
+	 * @return float amount remaining
324
+	 * @throws EE_Error
325
+	 * @throws InvalidArgumentException
326
+	 * @throws InvalidDataTypeException
327
+	 * @throws InvalidInterfaceException
328
+	 * @throws ReflectionException
329
+	 */
330
+	public function remaining()
331
+	{
332
+		return $this->total() - $this->paid();
333
+	}
334
+
335
+
336
+	/**
337
+	 * get Transaction Total
338
+	 *
339
+	 * @return float
340
+	 * @throws EE_Error
341
+	 * @throws InvalidArgumentException
342
+	 * @throws InvalidDataTypeException
343
+	 * @throws InvalidInterfaceException
344
+	 * @throws ReflectionException
345
+	 */
346
+	public function total()
347
+	{
348
+		return (float) $this->get('TXN_total');
349
+	}
350
+
351
+
352
+	/**
353
+	 * get Total Amount Paid to Date
354
+	 *
355
+	 * @return float
356
+	 * @throws EE_Error
357
+	 * @throws InvalidArgumentException
358
+	 * @throws InvalidDataTypeException
359
+	 * @throws InvalidInterfaceException
360
+	 * @throws ReflectionException
361
+	 */
362
+	public function paid()
363
+	{
364
+		return (float) $this->get('TXN_paid');
365
+	}
366
+
367
+
368
+	/**
369
+	 * @return mixed|null
370
+	 * @throws EE_Error
371
+	 * @throws InvalidArgumentException
372
+	 * @throws InvalidDataTypeException
373
+	 * @throws InvalidInterfaceException
374
+	 * @throws ReflectionException
375
+	 */
376
+	public function get_cart_session()
377
+	{
378
+		$session_data = (array) $this->get('TXN_session_data');
379
+		return isset($session_data['cart']) && $session_data['cart'] instanceof EE_Cart
380
+			? $session_data['cart']
381
+			: null;
382
+	}
383
+
384
+
385
+	/**
386
+	 * get Transaction session data
387
+	 *
388
+	 * @return array|mixed
389
+	 * @throws EE_Error
390
+	 * @throws InvalidArgumentException
391
+	 * @throws InvalidDataTypeException
392
+	 * @throws InvalidInterfaceException
393
+	 * @throws ReflectionException
394
+	 */
395
+	public function session_data()
396
+	{
397
+		$session_data = $this->get('TXN_session_data');
398
+		if (empty($session_data)) {
399
+			$session_data = [
400
+				'id'            => null,
401
+				'user_id'       => null,
402
+				'ip_address'    => null,
403
+				'user_agent'    => null,
404
+				'init_access'   => null,
405
+				'last_access'   => null,
406
+				'pages_visited' => [],
407
+			];
408
+		}
409
+		return $session_data;
410
+	}
411
+
412
+
413
+	/**
414
+	 * Set session data within the TXN object
415
+	 *
416
+	 * @param EE_Session|array $session_data
417
+	 * @throws EE_Error
418
+	 * @throws InvalidArgumentException
419
+	 * @throws InvalidDataTypeException
420
+	 * @throws InvalidInterfaceException
421
+	 * @throws ReflectionException
422
+	 */
423
+	public function set_txn_session_data($session_data)
424
+	{
425
+		if ($session_data instanceof EE_Session) {
426
+			$this->set('TXN_session_data', $session_data->get_session_data(null, true));
427
+		} else {
428
+			$this->set('TXN_session_data', $session_data);
429
+		}
430
+	}
431
+
432
+
433
+	/**
434
+	 * get Transaction hash salt
435
+	 *
436
+	 * @return mixed
437
+	 * @throws EE_Error
438
+	 * @throws InvalidArgumentException
439
+	 * @throws InvalidDataTypeException
440
+	 * @throws InvalidInterfaceException
441
+	 * @throws ReflectionException
442
+	 */
443
+	public function hash_salt_()
444
+	{
445
+		return $this->get('TXN_hash_salt');
446
+	}
447
+
448
+
449
+	/**
450
+	 * Returns the transaction datetime as either:
451
+	 *            - unix timestamp format ($format = false, $gmt = true)
452
+	 *            - formatted date string including the UTC (timezone) offset ($format = true ($gmt
453
+	 *              has no affect with this option)), this also may include a timezone abbreviation if the
454
+	 *              set timezone in this class differs from what the timezone is on the blog.
455
+	 *            - formatted date string including the UTC (timezone) offset (default).
456
+	 *
457
+	 * @param boolean $format   - whether to return a unix timestamp (default) or formatted date string
458
+	 * @param boolean $gmt      - whether to return a unix timestamp with UTC offset applied (default)
459
+	 *                          or no UTC offset applied
460
+	 * @return string | int
461
+	 * @throws EE_Error
462
+	 * @throws InvalidArgumentException
463
+	 * @throws InvalidDataTypeException
464
+	 * @throws InvalidInterfaceException
465
+	 * @throws ReflectionException
466
+	 */
467
+	public function datetime($format = false, $gmt = false)
468
+	{
469
+		if ($format) {
470
+			return $this->get_pretty('TXN_timestamp');
471
+		}
472
+		if ($gmt) {
473
+			return $this->get_raw('TXN_timestamp');
474
+		}
475
+		return $this->get('TXN_timestamp');
476
+	}
477
+
478
+
479
+	/**
480
+	 * Gets registrations on this transaction
481
+	 *
482
+	 * @param array   $query_params array of query parameters
483
+	 * @param boolean $get_cached   TRUE to retrieve cached registrations or FALSE to pull from the db
484
+	 * @return EE_Base_Class[]|EE_Registration[]
485
+	 * @throws EE_Error
486
+	 * @throws InvalidArgumentException
487
+	 * @throws InvalidDataTypeException
488
+	 * @throws InvalidInterfaceException
489
+	 * @throws ReflectionException
490
+	 */
491
+	public function registrations($query_params = [], $get_cached = false)
492
+	{
493
+		$query_params = (empty($query_params) || ! is_array($query_params))
494
+			? [
495
+				'order_by' => [
496
+					'Event.EVT_name'     => 'ASC',
497
+					'Attendee.ATT_lname' => 'ASC',
498
+					'Attendee.ATT_fname' => 'ASC',
499
+				],
500
+			]
501
+			: $query_params;
502
+		$query_params = $get_cached ? [] : $query_params;
503
+		return $this->get_many_related('Registration', $query_params);
504
+	}
505
+
506
+
507
+	/**
508
+	 * Gets all the attendees for this transaction (handy for use with EE_Attendee's get_registrations_for_event
509
+	 * function for getting attendees and how many registrations they each have for an event)
510
+	 *
511
+	 * @return mixed EE_Attendee[] by default, int if $output is set to 'COUNT'
512
+	 * @throws EE_Error
513
+	 * @throws InvalidArgumentException
514
+	 * @throws InvalidDataTypeException
515
+	 * @throws InvalidInterfaceException
516
+	 * @throws ReflectionException
517
+	 */
518
+	public function attendees()
519
+	{
520
+		return $this->get_many_related('Attendee', [['Registration.Transaction.TXN_ID' => $this->ID()]]);
521
+	}
522
+
523
+
524
+	/**
525
+	 * Gets payments for this transaction. Unlike other such functions, order by 'DESC' by default
526
+	 *
527
+	 * @param array $query_params @see
528
+	 *                            https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
529
+	 * @return EE_Base_Class[]|EE_Payment[]
530
+	 * @throws EE_Error
531
+	 * @throws InvalidArgumentException
532
+	 * @throws InvalidDataTypeException
533
+	 * @throws InvalidInterfaceException
534
+	 * @throws ReflectionException
535
+	 */
536
+	public function payments($query_params = [])
537
+	{
538
+		return $this->get_many_related('Payment', $query_params);
539
+	}
540
+
541
+
542
+	/**
543
+	 * gets only approved payments for this transaction
544
+	 *
545
+	 * @return EE_Base_Class[]|EE_Payment[]
546
+	 * @throws EE_Error
547
+	 * @throws InvalidArgumentException
548
+	 * @throws ReflectionException
549
+	 * @throws InvalidDataTypeException
550
+	 * @throws InvalidInterfaceException
551
+	 */
552
+	public function approved_payments()
553
+	{
554
+		EE_Registry::instance()->load_model('Payment');
555
+		return $this->get_many_related(
556
+			'Payment',
557
+			[
558
+				['STS_ID' => EEM_Payment::status_id_approved],
559
+				'order_by' => ['PAY_timestamp' => 'DESC'],
560
+			]
561
+		);
562
+	}
563
+
564
+
565
+	/**
566
+	 * Gets all payments which have not been approved
567
+	 *
568
+	 * @return EE_Base_Class[]|EEI_Payment[]
569
+	 * @throws EE_Error if a model is misconfigured somehow
570
+	 * @throws InvalidArgumentException
571
+	 * @throws InvalidDataTypeException
572
+	 * @throws InvalidInterfaceException
573
+	 * @throws ReflectionException
574
+	 */
575
+	public function pending_payments()
576
+	{
577
+		return $this->get_many_related(
578
+			'Payment',
579
+			[
580
+				[
581
+					'STS_ID' => EEM_Payment::status_id_pending,
582
+				],
583
+				'order_by' => [
584
+					'PAY_timestamp' => 'DESC',
585
+				],
586
+			]
587
+		);
588
+	}
589
+
590
+
591
+	/**
592
+	 * echoes $this->pretty_status()
593
+	 *
594
+	 * @param bool $show_icons
595
+	 * @throws EE_Error
596
+	 * @throws InvalidArgumentException
597
+	 * @throws InvalidDataTypeException
598
+	 * @throws InvalidInterfaceException
599
+	 * @throws ReflectionException
600
+	 */
601
+	public function e_pretty_status($show_icons = false)
602
+	{
603
+		echo wp_kses($this->pretty_status($show_icons), AllowedTags::getAllowedTags());
604
+	}
605
+
606
+
607
+	/**
608
+	 * returns a pretty version of the status, good for displaying to users
609
+	 *
610
+	 * @param bool $show_icons
611
+	 * @return string
612
+	 * @throws EE_Error
613
+	 * @throws InvalidArgumentException
614
+	 * @throws InvalidDataTypeException
615
+	 * @throws InvalidInterfaceException
616
+	 * @throws ReflectionException
617
+	 */
618
+	public function pretty_status($show_icons = false)
619
+	{
620
+		$status = EEM_Status::instance()->localized_status(
621
+			[$this->status_ID() => esc_html__('unknown', 'event_espresso')],
622
+			false,
623
+			'sentence'
624
+		);
625
+		$icon   = '';
626
+		switch ($this->status_ID()) {
627
+			case EEM_Transaction::complete_status_code:
628
+				$icon = $show_icons ? '<span class="dashicons dashicons-yes ee-icon-size-24 green-text"></span>' : '';
629
+				break;
630
+			case EEM_Transaction::incomplete_status_code:
631
+				$icon = $show_icons ? '<span class="dashicons dashicons-marker ee-icon-size-16 lt-blue-text"></span>'
632
+					: '';
633
+				break;
634
+			case EEM_Transaction::abandoned_status_code:
635
+				$icon = $show_icons ? '<span class="dashicons dashicons-marker ee-icon-size-16 red-text"></span>' : '';
636
+				break;
637
+			case EEM_Transaction::failed_status_code:
638
+				$icon = $show_icons ? '<span class="dashicons dashicons-no ee-icon-size-16 red-text"></span>' : '';
639
+				break;
640
+			case EEM_Transaction::overpaid_status_code:
641
+				$icon = $show_icons ? '<span class="dashicons dashicons-plus ee-icon-size-16 orange-text"></span>' : '';
642
+				break;
643
+		}
644
+		return $icon . $status[ $this->status_ID() ];
645
+	}
646
+
647
+
648
+	/**
649
+	 * get Transaction Status
650
+	 *
651
+	 * @return mixed
652
+	 * @throws EE_Error
653
+	 * @throws InvalidArgumentException
654
+	 * @throws InvalidDataTypeException
655
+	 * @throws InvalidInterfaceException
656
+	 * @throws ReflectionException
657
+	 */
658
+	public function status_ID()
659
+	{
660
+		return $this->get('STS_ID');
661
+	}
662
+
663
+
664
+	/**
665
+	 * Returns TRUE or FALSE for whether or not this transaction cost any money
666
+	 *
667
+	 * @return boolean
668
+	 * @throws EE_Error
669
+	 * @throws InvalidArgumentException
670
+	 * @throws InvalidDataTypeException
671
+	 * @throws InvalidInterfaceException
672
+	 * @throws ReflectionException
673
+	 */
674
+	public function is_free()
675
+	{
676
+		return EEH_Money::compare_floats($this->get('TXN_total'), 0, '==');
677
+	}
678
+
679
+
680
+	/**
681
+	 * Returns whether this transaction is complete
682
+	 * Useful in templates and other logic for deciding if we should ask for another payment...
683
+	 *
684
+	 * @return boolean
685
+	 * @throws EE_Error
686
+	 * @throws InvalidArgumentException
687
+	 * @throws InvalidDataTypeException
688
+	 * @throws InvalidInterfaceException
689
+	 * @throws ReflectionException
690
+	 */
691
+	public function is_completed()
692
+	{
693
+		return $this->status_ID() === EEM_Transaction::complete_status_code;
694
+	}
695
+
696
+
697
+	/**
698
+	 * Returns whether this transaction is incomplete
699
+	 * Useful in templates and other logic for deciding if we should ask for another payment...
700
+	 *
701
+	 * @return boolean
702
+	 * @throws EE_Error
703
+	 * @throws InvalidArgumentException
704
+	 * @throws InvalidDataTypeException
705
+	 * @throws InvalidInterfaceException
706
+	 * @throws ReflectionException
707
+	 */
708
+	public function is_incomplete()
709
+	{
710
+		return $this->status_ID() === EEM_Transaction::incomplete_status_code;
711
+	}
712
+
713
+
714
+	/**
715
+	 * Returns whether this transaction is overpaid
716
+	 * Useful in templates and other logic for deciding if monies need to be refunded
717
+	 *
718
+	 * @return boolean
719
+	 * @throws EE_Error
720
+	 * @throws InvalidArgumentException
721
+	 * @throws InvalidDataTypeException
722
+	 * @throws InvalidInterfaceException
723
+	 * @throws ReflectionException
724
+	 */
725
+	public function is_overpaid()
726
+	{
727
+		return $this->status_ID() === EEM_Transaction::overpaid_status_code;
728
+	}
729
+
730
+
731
+	/**
732
+	 * Returns whether this transaction was abandoned
733
+	 * meaning that the transaction/registration process was somehow interrupted and never completed
734
+	 * but that contact information exists for at least one registrant
735
+	 *
736
+	 * @return boolean
737
+	 * @throws EE_Error
738
+	 * @throws InvalidArgumentException
739
+	 * @throws InvalidDataTypeException
740
+	 * @throws InvalidInterfaceException
741
+	 * @throws ReflectionException
742
+	 */
743
+	public function is_abandoned()
744
+	{
745
+		return $this->status_ID() === EEM_Transaction::abandoned_status_code;
746
+	}
747
+
748
+
749
+	/**
750
+	 * Returns whether this transaction failed
751
+	 * meaning that the transaction/registration process was somehow interrupted and never completed
752
+	 * and that NO contact information exists for any registrants
753
+	 *
754
+	 * @return boolean
755
+	 * @throws EE_Error
756
+	 * @throws InvalidArgumentException
757
+	 * @throws InvalidDataTypeException
758
+	 * @throws InvalidInterfaceException
759
+	 * @throws ReflectionException
760
+	 */
761
+	public function failed()
762
+	{
763
+		return $this->status_ID() === EEM_Transaction::failed_status_code;
764
+	}
765
+
766
+
767
+	/**
768
+	 * This returns the url for the invoice of this transaction
769
+	 *
770
+	 * @param string $type 'html' or 'pdf' (default is pdf)
771
+	 * @return string
772
+	 * @throws DomainException
773
+	 * @throws EE_Error
774
+	 * @throws InvalidArgumentException
775
+	 * @throws InvalidDataTypeException
776
+	 * @throws InvalidInterfaceException
777
+	 * @throws ReflectionException
778
+	 */
779
+	public function invoice_url($type = 'html')
780
+	{
781
+		$REG = $this->primary_registration();
782
+		if (! $REG instanceof EE_Registration) {
783
+			return '';
784
+		}
785
+		return $REG->invoice_url($type);
786
+	}
787
+
788
+
789
+	/**
790
+	 * Gets the primary registration only
791
+	 *
792
+	 * @return EE_Base_Class|EE_Registration
793
+	 * @throws EE_Error
794
+	 * @throws InvalidArgumentException
795
+	 * @throws InvalidDataTypeException
796
+	 * @throws InvalidInterfaceException
797
+	 * @throws ReflectionException
798
+	 */
799
+	public function primary_registration()
800
+	{
801
+		$registrations = (array) $this->get_many_related(
802
+			'Registration',
803
+			[['REG_count' => EEM_Registration::PRIMARY_REGISTRANT_COUNT]]
804
+		);
805
+		foreach ($registrations as $registration) {
806
+			// valid registration that is NOT cancelled or declined ?
807
+			if (
808
+				$registration instanceof EE_Registration
809
+				&& ! in_array($registration->status_ID(), EEM_Registration::closed_reg_statuses(), true)
810
+			) {
811
+				return $registration;
812
+			}
813
+		}
814
+		// nothing valid found, so just return first thing from array of results
815
+		return reset($registrations);
816
+	}
817
+
818
+
819
+	/**
820
+	 * Gets the URL for viewing the receipt
821
+	 *
822
+	 * @param string $type 'pdf' or 'html' (default is 'html')
823
+	 * @return string
824
+	 * @throws DomainException
825
+	 * @throws EE_Error
826
+	 * @throws InvalidArgumentException
827
+	 * @throws InvalidDataTypeException
828
+	 * @throws InvalidInterfaceException
829
+	 * @throws ReflectionException
830
+	 */
831
+	public function receipt_url($type = 'html')
832
+	{
833
+		$REG = $this->primary_registration();
834
+		if (! $REG instanceof EE_Registration) {
835
+			return '';
836
+		}
837
+		return $REG->receipt_url($type);
838
+	}
839
+
840
+
841
+	/**
842
+	 * Gets the URL of the thank you page with this registration REG_url_link added as
843
+	 * a query parameter
844
+	 *
845
+	 * @return string
846
+	 * @throws EE_Error
847
+	 * @throws InvalidArgumentException
848
+	 * @throws InvalidDataTypeException
849
+	 * @throws InvalidInterfaceException
850
+	 * @throws ReflectionException
851
+	 */
852
+	public function payment_overview_url()
853
+	{
854
+		$primary_registration = $this->primary_registration();
855
+		return $primary_registration instanceof EE_Registration ? $primary_registration->payment_overview_url() : false;
856
+	}
857
+
858
+
859
+	/**
860
+	 * @return string
861
+	 * @throws EE_Error
862
+	 * @throws InvalidArgumentException
863
+	 * @throws InvalidDataTypeException
864
+	 * @throws InvalidInterfaceException
865
+	 * @throws ReflectionException
866
+	 */
867
+	public function gateway_response_on_transaction()
868
+	{
869
+		$payment = $this->get_first_related('Payment');
870
+		return $payment instanceof EE_Payment ? $payment->gateway_response() : '';
871
+	}
872
+
873
+
874
+	/**
875
+	 * Get the status object of this object
876
+	 *
877
+	 * @return EE_Base_Class|EE_Status
878
+	 * @throws EE_Error
879
+	 * @throws InvalidArgumentException
880
+	 * @throws InvalidDataTypeException
881
+	 * @throws InvalidInterfaceException
882
+	 * @throws ReflectionException
883
+	 */
884
+	public function status_obj()
885
+	{
886
+		return $this->get_first_related('Status');
887
+	}
888
+
889
+
890
+	/**
891
+	 * Gets all the extra meta info on this payment
892
+	 *
893
+	 * @param array $query_params
894
+	 * @return EE_Base_Class[]|EE_Extra_Meta
895
+	 * @throws EE_Error
896
+	 * @throws InvalidArgumentException
897
+	 * @throws InvalidDataTypeException
898
+	 * @throws InvalidInterfaceException
899
+	 * @throws ReflectionException
900
+	 * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
901
+	 */
902
+	public function extra_meta($query_params = [])
903
+	{
904
+		return $this->get_many_related('Extra_Meta', $query_params);
905
+	}
906
+
907
+
908
+	/**
909
+	 * Wrapper for _add_relation_to
910
+	 *
911
+	 * @param EE_Registration $registration
912
+	 * @return EE_Base_Class the relation was added to
913
+	 * @throws EE_Error
914
+	 * @throws InvalidArgumentException
915
+	 * @throws InvalidDataTypeException
916
+	 * @throws InvalidInterfaceException
917
+	 * @throws ReflectionException
918
+	 */
919
+	public function add_registration(EE_Registration $registration)
920
+	{
921
+		return $this->_add_relation_to($registration, 'Registration');
922
+	}
923
+
924
+
925
+	/**
926
+	 * Removes the given registration from being related (even before saving this transaction).
927
+	 * If an ID/index is provided and this transaction isn't saved yet, removes it from list of cached relations
928
+	 *
929
+	 * @param int $registration_or_id
930
+	 * @return EE_Base_Class that was removed from being related
931
+	 * @throws EE_Error
932
+	 * @throws InvalidArgumentException
933
+	 * @throws InvalidDataTypeException
934
+	 * @throws InvalidInterfaceException
935
+	 * @throws ReflectionException
936
+	 */
937
+	public function remove_registration_with_id($registration_or_id)
938
+	{
939
+		return $this->_remove_relation_to($registration_or_id, 'Registration');
940
+	}
941
+
942
+
943
+	/**
944
+	 * Gets all the line items which are for ACTUAL items
945
+	 *
946
+	 * @return EE_Line_Item[]
947
+	 * @throws EE_Error
948
+	 * @throws InvalidArgumentException
949
+	 * @throws InvalidDataTypeException
950
+	 * @throws InvalidInterfaceException
951
+	 * @throws ReflectionException
952
+	 */
953
+	public function items_purchased()
954
+	{
955
+		return $this->line_items([['LIN_type' => EEM_Line_Item::type_line_item]]);
956
+	}
957
+
958
+
959
+	/**
960
+	 * Wrapper for _add_relation_to
961
+	 *
962
+	 * @param EE_Line_Item $line_item
963
+	 * @return EE_Base_Class the relation was added to
964
+	 * @throws EE_Error
965
+	 * @throws InvalidArgumentException
966
+	 * @throws InvalidDataTypeException
967
+	 * @throws InvalidInterfaceException
968
+	 * @throws ReflectionException
969
+	 */
970
+	public function add_line_item(EE_Line_Item $line_item)
971
+	{
972
+		return $this->_add_relation_to($line_item, 'Line_Item');
973
+	}
974
+
975
+
976
+	/**
977
+	 * Gets ALL the line items related to this transaction (unstructured)
978
+	 *
979
+	 * @param array $query_params
980
+	 * @return EE_Base_Class[]|EE_Line_Item[]
981
+	 * @throws EE_Error
982
+	 * @throws InvalidArgumentException
983
+	 * @throws InvalidDataTypeException
984
+	 * @throws InvalidInterfaceException
985
+	 * @throws ReflectionException
986
+	 */
987
+	public function line_items($query_params = [])
988
+	{
989
+		return $this->get_many_related('Line_Item', $query_params);
990
+	}
991
+
992
+
993
+	/**
994
+	 * Gets all the line items which are taxes on the total
995
+	 *
996
+	 * @return EE_Line_Item[]
997
+	 * @throws EE_Error
998
+	 * @throws InvalidArgumentException
999
+	 * @throws InvalidDataTypeException
1000
+	 * @throws InvalidInterfaceException
1001
+	 * @throws ReflectionException
1002
+	 */
1003
+	public function tax_items()
1004
+	{
1005
+		return $this->line_items([['LIN_type' => EEM_Line_Item::type_tax]]);
1006
+	}
1007
+
1008
+
1009
+	/**
1010
+	 * Gets the total line item (which is a parent of all other related line items,
1011
+	 * meaning it takes them all into account on its total)
1012
+	 *
1013
+	 * @param bool $create_if_not_found
1014
+	 * @return \EE_Line_Item
1015
+	 * @throws EE_Error
1016
+	 * @throws InvalidArgumentException
1017
+	 * @throws InvalidDataTypeException
1018
+	 * @throws InvalidInterfaceException
1019
+	 * @throws ReflectionException
1020
+	 */
1021
+	public function total_line_item($create_if_not_found = true)
1022
+	{
1023
+		$item = $this->get_first_related('Line_Item', [['LIN_type' => EEM_Line_Item::type_total]]);
1024
+		if (! $item && $create_if_not_found) {
1025
+			$item = EEH_Line_Item::create_total_line_item($this);
1026
+		}
1027
+		return $item;
1028
+	}
1029
+
1030
+
1031
+	/**
1032
+	 * Returns the total amount of tax on this transaction
1033
+	 * (assumes there's only one tax subtotal line item)
1034
+	 *
1035
+	 * @return float
1036
+	 * @throws EE_Error
1037
+	 * @throws InvalidArgumentException
1038
+	 * @throws InvalidDataTypeException
1039
+	 * @throws InvalidInterfaceException
1040
+	 * @throws ReflectionException
1041
+	 */
1042
+	public function tax_total()
1043
+	{
1044
+		$tax_line_item = $this->tax_total_line_item();
1045
+		if ($tax_line_item) {
1046
+			return (float) $tax_line_item->total();
1047
+		}
1048
+		return (float) 0;
1049
+	}
1050
+
1051
+
1052
+	/**
1053
+	 * Gets the tax subtotal line item (assumes there's only one)
1054
+	 *
1055
+	 * @return EE_Line_Item
1056
+	 * @throws EE_Error
1057
+	 * @throws InvalidArgumentException
1058
+	 * @throws InvalidDataTypeException
1059
+	 * @throws InvalidInterfaceException
1060
+	 * @throws ReflectionException
1061
+	 */
1062
+	public function tax_total_line_item()
1063
+	{
1064
+		return EEH_Line_Item::get_taxes_subtotal($this->total_line_item());
1065
+	}
1066
+
1067
+
1068
+	/**
1069
+	 * Gets the array of billing info for the gateway and for this transaction's primary registration's attendee.
1070
+	 *
1071
+	 * @return EE_Form_Section_Proper
1072
+	 * @throws EE_Error
1073
+	 * @throws InvalidArgumentException
1074
+	 * @throws InvalidDataTypeException
1075
+	 * @throws InvalidInterfaceException
1076
+	 * @throws ReflectionException
1077
+	 */
1078
+	public function billing_info()
1079
+	{
1080
+		$payment_method = $this->payment_method();
1081
+		if (! $payment_method) {
1082
+			EE_Error::add_error(
1083
+				esc_html__(
1084
+					'Could not find billing info for transaction because no gateway has been used for it yet',
1085
+					'event_espresso'
1086
+				),
1087
+				__FILE__,
1088
+				__FUNCTION__,
1089
+				__LINE__
1090
+			);
1091
+			return null;
1092
+		}
1093
+		$primary_reg = $this->primary_registration();
1094
+		if (! $primary_reg) {
1095
+			EE_Error::add_error(
1096
+				esc_html__(
1097
+					'Cannot get billing info for gateway %s on transaction because no primary registration exists',
1098
+					'event_espresso'
1099
+				),
1100
+				__FILE__,
1101
+				__FUNCTION__,
1102
+				__LINE__
1103
+			);
1104
+			return null;
1105
+		}
1106
+		$attendee = $primary_reg->attendee();
1107
+		if (! $attendee) {
1108
+			EE_Error::add_error(
1109
+				esc_html__(
1110
+					'Cannot get billing info for gateway %s on transaction because the primary registration has no attendee exists',
1111
+					'event_espresso'
1112
+				),
1113
+				__FILE__,
1114
+				__FUNCTION__,
1115
+				__LINE__
1116
+			);
1117
+			return null;
1118
+		}
1119
+		return $attendee->billing_info_for_payment_method($payment_method);
1120
+	}
1121
+
1122
+
1123
+	/**
1124
+	 * Gets PMD_ID
1125
+	 *
1126
+	 * @return int
1127
+	 * @throws EE_Error
1128
+	 * @throws InvalidArgumentException
1129
+	 * @throws InvalidDataTypeException
1130
+	 * @throws InvalidInterfaceException
1131
+	 * @throws ReflectionException
1132
+	 */
1133
+	public function payment_method_ID()
1134
+	{
1135
+		return $this->get('PMD_ID');
1136
+	}
1137
+
1138
+
1139
+	/**
1140
+	 * Sets PMD_ID
1141
+	 *
1142
+	 * @param int $PMD_ID
1143
+	 * @throws EE_Error
1144
+	 * @throws InvalidArgumentException
1145
+	 * @throws InvalidDataTypeException
1146
+	 * @throws InvalidInterfaceException
1147
+	 * @throws ReflectionException
1148
+	 */
1149
+	public function set_payment_method_ID($PMD_ID)
1150
+	{
1151
+		$this->set('PMD_ID', $PMD_ID);
1152
+	}
1153
+
1154
+
1155
+	/**
1156
+	 * Gets the last-used payment method on this transaction
1157
+	 * (we COULD just use the last-made payment, but some payment methods, namely
1158
+	 * offline ones, dont' create payments)
1159
+	 *
1160
+	 * @return EE_Payment_Method
1161
+	 * @throws EE_Error
1162
+	 * @throws InvalidArgumentException
1163
+	 * @throws InvalidDataTypeException
1164
+	 * @throws InvalidInterfaceException
1165
+	 * @throws ReflectionException
1166
+	 */
1167
+	public function payment_method()
1168
+	{
1169
+		$pm = $this->get_first_related('Payment_Method');
1170
+		if ($pm instanceof EE_Payment_Method) {
1171
+			return $pm;
1172
+		}
1173
+		$last_payment = $this->last_payment();
1174
+		if ($last_payment instanceof EE_Payment && $last_payment->payment_method()) {
1175
+			return $last_payment->payment_method();
1176
+		}
1177
+		return null;
1178
+	}
1179
+
1180
+
1181
+	/**
1182
+	 * Gets the last payment made
1183
+	 *
1184
+	 * @return EE_Base_Class|EE_Payment
1185
+	 * @throws EE_Error
1186
+	 * @throws InvalidArgumentException
1187
+	 * @throws InvalidDataTypeException
1188
+	 * @throws InvalidInterfaceException
1189
+	 * @throws ReflectionException
1190
+	 */
1191
+	public function last_payment()
1192
+	{
1193
+		return $this->get_first_related('Payment', ['order_by' => ['PAY_ID' => 'desc']]);
1194
+	}
1195
+
1196
+
1197
+	/**
1198
+	 * Gets all the line items which are unrelated to tickets on this transaction
1199
+	 *
1200
+	 * @return EE_Line_Item[]
1201
+	 * @throws EE_Error
1202
+	 * @throws InvalidArgumentException
1203
+	 * @throws InvalidDataTypeException
1204
+	 * @throws InvalidInterfaceException
1205
+	 * @throws ReflectionException
1206
+	 */
1207
+	public function non_ticket_line_items()
1208
+	{
1209
+		return EEM_Line_Item::instance()->get_all_non_ticket_line_items_for_transaction($this->ID());
1210
+	}
1211
+
1212
+
1213
+	/**
1214
+	 * possibly toggles TXN status
1215
+	 *
1216
+	 * @param boolean $update whether to save the TXN
1217
+	 * @return bool whether the TXN was saved
1218
+	 * @throws EE_Error
1219
+	 * @throws InvalidArgumentException
1220
+	 * @throws InvalidDataTypeException
1221
+	 * @throws InvalidInterfaceException
1222
+	 * @throws ReflectionException
1223
+	 * @throws RuntimeException
1224
+	 */
1225
+	public function update_status_based_on_total_paid($update = true)
1226
+	{
1227
+		// set transaction status based on comparison of TXN_paid vs TXN_total
1228
+		if (EEH_Money::compare_floats($this->paid(), $this->total(), '>')) {
1229
+			$new_txn_status = EEM_Transaction::overpaid_status_code;
1230
+		} elseif (EEH_Money::compare_floats($this->paid(), $this->total())) {
1231
+			$new_txn_status = EEM_Transaction::complete_status_code;
1232
+		} elseif (EEH_Money::compare_floats($this->paid(), $this->total(), '<')) {
1233
+			$new_txn_status = EEM_Transaction::incomplete_status_code;
1234
+		} else {
1235
+			throw new RuntimeException(
1236
+				esc_html__('The total paid calculation for this transaction is inaccurate.', 'event_espresso')
1237
+			);
1238
+		}
1239
+		if ($new_txn_status !== $this->status_ID()) {
1240
+			$this->set_status($new_txn_status);
1241
+			if ($update) {
1242
+				return (bool) $this->save();
1243
+			}
1244
+		}
1245
+		return false;
1246
+	}
1247
+
1248
+
1249
+	/**
1250
+	 * Updates the transaction's status and total_paid based on all the payments
1251
+	 * that apply to it
1252
+	 *
1253
+	 * @return array|bool
1254
+	 * @throws EE_Error
1255
+	 * @throws InvalidArgumentException
1256
+	 * @throws ReflectionException
1257
+	 * @throws InvalidDataTypeException
1258
+	 * @throws InvalidInterfaceException
1259
+	 * @deprecated
1260
+	 */
1261
+	public function update_based_on_payments()
1262
+	{
1263
+		EE_Error::doing_it_wrong(
1264
+			__CLASS__ . '::' . __FUNCTION__,
1265
+			sprintf(
1266
+				esc_html__('This method is deprecated. Please use "%s" instead', 'event_espresso'),
1267
+				'EE_Transaction_Processor::update_transaction_and_registrations_after_checkout_or_payment()'
1268
+			),
1269
+			'4.6.0'
1270
+		);
1271
+		/** @type EE_Transaction_Processor $transaction_processor */
1272
+		$transaction_processor = EE_Registry::instance()->load_class('Transaction_Processor');
1273
+		return $transaction_processor->update_transaction_and_registrations_after_checkout_or_payment($this);
1274
+	}
1275
+
1276
+
1277
+	/**
1278
+	 * @return string
1279
+	 */
1280
+	public function old_txn_status()
1281
+	{
1282
+		return $this->_old_txn_status;
1283
+	}
1284
+
1285
+
1286
+	/**
1287
+	 * @param string $old_txn_status
1288
+	 */
1289
+	public function set_old_txn_status($old_txn_status)
1290
+	{
1291
+		// only set the first time
1292
+		if ($this->_old_txn_status === null) {
1293
+			$this->_old_txn_status = $old_txn_status;
1294
+		}
1295
+	}
1296
+
1297
+
1298
+	/**
1299
+	 * reg_status_updated
1300
+	 *
1301
+	 * @return bool
1302
+	 * @throws EE_Error
1303
+	 * @throws InvalidArgumentException
1304
+	 * @throws InvalidDataTypeException
1305
+	 * @throws InvalidInterfaceException
1306
+	 * @throws ReflectionException
1307
+	 */
1308
+	public function txn_status_updated()
1309
+	{
1310
+		return $this->status_ID() !== $this->_old_txn_status && $this->_old_txn_status !== null;
1311
+	}
1312
+
1313
+
1314
+	/**
1315
+	 * _reg_steps_completed
1316
+	 * if $check_all is TRUE, then returns TRUE if ALL reg steps have been marked as completed,
1317
+	 * if a $reg_step_slug is provided, then this step will be skipped when testing for completion
1318
+	 * if $check_all is FALSE and a $reg_step_slug is provided, then ONLY that reg step will be tested for completion
1319
+	 *
1320
+	 * @param string $reg_step_slug
1321
+	 * @param bool   $check_all
1322
+	 * @return bool|int
1323
+	 * @throws EE_Error
1324
+	 * @throws InvalidArgumentException
1325
+	 * @throws InvalidDataTypeException
1326
+	 * @throws InvalidInterfaceException
1327
+	 * @throws ReflectionException
1328
+	 */
1329
+	private function _reg_steps_completed($reg_step_slug = '', $check_all = true)
1330
+	{
1331
+		$reg_steps = $this->reg_steps();
1332
+		if (! is_array($reg_steps) || empty($reg_steps)) {
1333
+			return false;
1334
+		}
1335
+		// loop thru reg steps array)
1336
+		foreach ($reg_steps as $slug => $reg_step_completed) {
1337
+			// if NOT checking ALL steps (only checking one step)
1338
+			if (! $check_all) {
1339
+				// and this is the one
1340
+				if ($slug === $reg_step_slug) {
1341
+					return $reg_step_completed;
1342
+				}
1343
+				// skip to next reg step in loop
1344
+				continue;
1345
+			}
1346
+			// $check_all must be true, else we would never have gotten to this point
1347
+			if ($slug === $reg_step_slug) {
1348
+				// if we reach this point, then we are testing either:
1349
+				// all_reg_steps_completed_except() or
1350
+				// all_reg_steps_completed_except_final_step(),
1351
+				// and since this is the reg step EXCEPTION being tested
1352
+				// we want to return true (yes true) if this reg step is NOT completed
1353
+				// ie: "is everything completed except the final step?"
1354
+				// "that is correct... the final step is not completed, but all others are."
1355
+				return $reg_step_completed !== true;
1356
+			}
1357
+			if ($reg_step_completed !== true) {
1358
+				// if any reg step is NOT completed, then ALL steps are not completed
1359
+				return false;
1360
+			}
1361
+		}
1362
+		return true;
1363
+	}
1364
+
1365
+
1366
+	/**
1367
+	 * all_reg_steps_completed
1368
+	 * returns:
1369
+	 *    true if ALL reg steps have been marked as completed
1370
+	 *        or false if any step is not completed
1371
+	 *
1372
+	 * @return bool
1373
+	 * @throws EE_Error
1374
+	 * @throws InvalidArgumentException
1375
+	 * @throws InvalidDataTypeException
1376
+	 * @throws InvalidInterfaceException
1377
+	 * @throws ReflectionException
1378
+	 */
1379
+	public function all_reg_steps_completed()
1380
+	{
1381
+		return $this->_reg_steps_completed();
1382
+	}
1383
+
1384
+
1385
+	/**
1386
+	 * all_reg_steps_completed_except
1387
+	 * returns:
1388
+	 *        true if ALL reg steps, except a particular step that you wish to skip over, have been marked as completed
1389
+	 *        or false if any other step is not completed
1390
+	 *        or false if ALL steps are completed including the exception you are testing !!!
1391
+	 *
1392
+	 * @param string $exception
1393
+	 * @return bool
1394
+	 * @throws EE_Error
1395
+	 * @throws InvalidArgumentException
1396
+	 * @throws InvalidDataTypeException
1397
+	 * @throws InvalidInterfaceException
1398
+	 * @throws ReflectionException
1399
+	 */
1400
+	public function all_reg_steps_completed_except($exception = '')
1401
+	{
1402
+		return $this->_reg_steps_completed($exception);
1403
+	}
1404
+
1405
+
1406
+	/**
1407
+	 * all_reg_steps_completed_except
1408
+	 * returns:
1409
+	 *        true if ALL reg steps, except the final step, have been marked as completed
1410
+	 *        or false if any step is not completed
1411
+	 *    or false if ALL steps are completed including the final step !!!
1412
+	 *
1413
+	 * @return bool
1414
+	 * @throws EE_Error
1415
+	 * @throws InvalidArgumentException
1416
+	 * @throws InvalidDataTypeException
1417
+	 * @throws InvalidInterfaceException
1418
+	 * @throws ReflectionException
1419
+	 */
1420
+	public function all_reg_steps_completed_except_final_step()
1421
+	{
1422
+		return $this->_reg_steps_completed('finalize_registration');
1423
+	}
1424
+
1425
+
1426
+	/**
1427
+	 * reg_step_completed
1428
+	 * returns:
1429
+	 *    true if a specific reg step has been marked as completed
1430
+	 *    a Unix timestamp if it has been initialized but not yet completed,
1431
+	 *    or false if it has not yet been initialized
1432
+	 *
1433
+	 * @param string $reg_step_slug
1434
+	 * @return bool|int
1435
+	 * @throws EE_Error
1436
+	 * @throws InvalidArgumentException
1437
+	 * @throws InvalidDataTypeException
1438
+	 * @throws InvalidInterfaceException
1439
+	 * @throws ReflectionException
1440
+	 */
1441
+	public function reg_step_completed($reg_step_slug)
1442
+	{
1443
+		return $this->_reg_steps_completed($reg_step_slug, false);
1444
+	}
1445
+
1446
+
1447
+	/**
1448
+	 * completed_final_reg_step
1449
+	 * returns:
1450
+	 *    true if the finalize_registration reg step has been marked as completed
1451
+	 *    a Unix timestamp if it has been initialized but not yet completed,
1452
+	 *    or false if it has not yet been initialized
1453
+	 *
1454
+	 * @return bool|int
1455
+	 * @throws EE_Error
1456
+	 * @throws InvalidArgumentException
1457
+	 * @throws InvalidDataTypeException
1458
+	 * @throws InvalidInterfaceException
1459
+	 * @throws ReflectionException
1460
+	 */
1461
+	public function final_reg_step_completed()
1462
+	{
1463
+		return $this->_reg_steps_completed('finalize_registration', false);
1464
+	}
1465
+
1466
+
1467
+	/**
1468
+	 * set_reg_step_initiated
1469
+	 * given a valid TXN_reg_step, this sets it's value to a unix timestamp
1470
+	 *
1471
+	 * @param string $reg_step_slug
1472
+	 * @return boolean
1473
+	 * @throws EE_Error
1474
+	 * @throws InvalidArgumentException
1475
+	 * @throws InvalidDataTypeException
1476
+	 * @throws InvalidInterfaceException
1477
+	 * @throws ReflectionException
1478
+	 */
1479
+	public function set_reg_step_initiated($reg_step_slug)
1480
+	{
1481
+		return $this->_set_reg_step_completed_status($reg_step_slug, time());
1482
+	}
1483
+
1484
+
1485
+	/**
1486
+	 * set_reg_step_completed
1487
+	 * given a valid TXN_reg_step, this sets the step as completed
1488
+	 *
1489
+	 * @param string $reg_step_slug
1490
+	 * @return boolean
1491
+	 * @throws EE_Error
1492
+	 * @throws InvalidArgumentException
1493
+	 * @throws InvalidDataTypeException
1494
+	 * @throws InvalidInterfaceException
1495
+	 * @throws ReflectionException
1496
+	 */
1497
+	public function set_reg_step_completed($reg_step_slug)
1498
+	{
1499
+		return $this->_set_reg_step_completed_status($reg_step_slug, true);
1500
+	}
1501
+
1502
+
1503
+	/**
1504
+	 * set_reg_step_completed
1505
+	 * given a valid TXN_reg_step slug, this sets the step as NOT completed
1506
+	 *
1507
+	 * @param string $reg_step_slug
1508
+	 * @return boolean
1509
+	 * @throws EE_Error
1510
+	 * @throws InvalidArgumentException
1511
+	 * @throws InvalidDataTypeException
1512
+	 * @throws InvalidInterfaceException
1513
+	 * @throws ReflectionException
1514
+	 */
1515
+	public function set_reg_step_not_completed($reg_step_slug)
1516
+	{
1517
+		return $this->_set_reg_step_completed_status($reg_step_slug, false);
1518
+	}
1519
+
1520
+
1521
+	/**
1522
+	 * set_reg_step_completed
1523
+	 * given a valid reg step slug, this sets the TXN_reg_step completed status which is either:
1524
+	 *
1525
+	 * @param string      $reg_step_slug
1526
+	 * @param boolean|int $status
1527
+	 * @return boolean
1528
+	 * @throws EE_Error
1529
+	 * @throws InvalidArgumentException
1530
+	 * @throws InvalidDataTypeException
1531
+	 * @throws InvalidInterfaceException
1532
+	 * @throws ReflectionException
1533
+	 */
1534
+	private function _set_reg_step_completed_status($reg_step_slug, $status)
1535
+	{
1536
+		// validate status
1537
+		$status = is_bool($status) || is_int($status) ? $status : false;
1538
+		// get reg steps array
1539
+		$txn_reg_steps = $this->reg_steps();
1540
+		// if reg step does NOT exist
1541
+		if (! isset($txn_reg_steps[ $reg_step_slug ])) {
1542
+			return false;
1543
+		}
1544
+		// if  we're trying to complete a step that is already completed
1545
+		if ($txn_reg_steps[ $reg_step_slug ] === true) {
1546
+			return true;
1547
+		}
1548
+		// if  we're trying to complete a step that hasn't even started
1549
+		if ($status === true && $txn_reg_steps[ $reg_step_slug ] === false) {
1550
+			return false;
1551
+		}
1552
+		// if current status value matches the incoming value (no change)
1553
+		// type casting as int means values should collapse to either 0, 1, or a timestamp like 1234567890
1554
+		if ((int) $txn_reg_steps[ $reg_step_slug ] === (int) $status) {
1555
+			// this will happen in cases where multiple AJAX requests occur during the same step
1556
+			return true;
1557
+		}
1558
+		// if we're trying to set a start time, but it has already been set...
1559
+		if (is_numeric($status) && is_numeric($txn_reg_steps[ $reg_step_slug ])) {
1560
+			// skip the update below, but don't return FALSE so that errors won't be displayed
1561
+			return true;
1562
+		}
1563
+		// update completed status
1564
+		$txn_reg_steps[ $reg_step_slug ] = $status;
1565
+		$this->set_reg_steps($txn_reg_steps);
1566
+		$this->save();
1567
+		return true;
1568
+	}
1569
+
1570
+
1571
+	/**
1572
+	 * remove_reg_step
1573
+	 * given a valid TXN_reg_step slug, this will remove (unset)
1574
+	 * the reg step from the TXN reg step array
1575
+	 *
1576
+	 * @param string $reg_step_slug
1577
+	 * @return void
1578
+	 * @throws EE_Error
1579
+	 * @throws InvalidArgumentException
1580
+	 * @throws InvalidDataTypeException
1581
+	 * @throws InvalidInterfaceException
1582
+	 * @throws ReflectionException
1583
+	 */
1584
+	public function remove_reg_step($reg_step_slug)
1585
+	{
1586
+		// get reg steps array
1587
+		$txn_reg_steps = $this->reg_steps();
1588
+		unset($txn_reg_steps[ $reg_step_slug ]);
1589
+		$this->set_reg_steps($txn_reg_steps);
1590
+	}
1591
+
1592
+
1593
+	/**
1594
+	 * toggle_failed_transaction_status
1595
+	 * upgrades a TXNs status from failed to abandoned,
1596
+	 * meaning that contact information has been captured for at least one registrant
1597
+	 *
1598
+	 * @param bool $save
1599
+	 * @return bool
1600
+	 * @throws EE_Error
1601
+	 * @throws InvalidArgumentException
1602
+	 * @throws InvalidDataTypeException
1603
+	 * @throws InvalidInterfaceException
1604
+	 * @throws ReflectionException
1605
+	 */
1606
+	public function toggle_failed_transaction_status($save = true)
1607
+	{
1608
+		// if TXN status is still set as "failed"...
1609
+		if ($this->status_ID() === EEM_Transaction::failed_status_code) {
1610
+			$this->set_status(EEM_Transaction::abandoned_status_code);
1611
+			if ($save) {
1612
+				$this->save();
1613
+			}
1614
+			return true;
1615
+		}
1616
+		return false;
1617
+	}
1618
+
1619
+
1620
+	/**
1621
+	 * toggle_abandoned_transaction_status
1622
+	 * upgrades a TXNs status from failed or abandoned to incomplete
1623
+	 *
1624
+	 * @return bool
1625
+	 * @throws EE_Error
1626
+	 * @throws InvalidArgumentException
1627
+	 * @throws InvalidDataTypeException
1628
+	 * @throws InvalidInterfaceException
1629
+	 * @throws ReflectionException
1630
+	 */
1631
+	public function toggle_abandoned_transaction_status()
1632
+	{
1633
+		// if TXN status has not been updated already due to a payment, and is still set as "failed" or "abandoned"...
1634
+		$txn_status = $this->status_ID();
1635
+		if (
1636
+			$txn_status === EEM_Transaction::failed_status_code
1637
+			|| $txn_status === EEM_Transaction::abandoned_status_code
1638
+		) {
1639
+			// if a contact record for the primary registrant has been created
1640
+			if (
1641
+				$this->primary_registration() instanceof EE_Registration
1642
+				&& $this->primary_registration()->attendee() instanceof EE_Attendee
1643
+			) {
1644
+				$this->set_status(EEM_Transaction::incomplete_status_code);
1645
+			} else {
1646
+				// no contact record? yer abandoned!
1647
+				$this->set_status(EEM_Transaction::abandoned_status_code);
1648
+			}
1649
+			return true;
1650
+		}
1651
+		return false;
1652
+	}
1653
+
1654
+
1655
+	/**
1656
+	 * checks if an Abandoned TXN has any related payments, and if so,
1657
+	 * updates the TXN status based on the amount paid
1658
+	 *
1659
+	 * @throws EE_Error
1660
+	 * @throws InvalidArgumentException
1661
+	 * @throws InvalidDataTypeException
1662
+	 * @throws InvalidInterfaceException
1663
+	 * @throws ReflectionException
1664
+	 * @throws RuntimeException
1665
+	 * @throws ReflectionException
1666
+	 */
1667
+	public function verify_abandoned_transaction_status()
1668
+	{
1669
+		if ($this->status_ID() !== EEM_Transaction::abandoned_status_code) {
1670
+			return;
1671
+		}
1672
+		$payments = $this->get_many_related('Payment');
1673
+		if (! empty($payments)) {
1674
+			foreach ($payments as $payment) {
1675
+				if ($payment instanceof EE_Payment) {
1676
+					// kk this TXN should NOT be abandoned
1677
+					$this->update_status_based_on_total_paid();
1678
+					if (! (defined('DOING_AJAX') && DOING_AJAX) && is_admin()) {
1679
+						EE_Error::add_attention(
1680
+							sprintf(
1681
+								esc_html__(
1682
+									'The status for Transaction #%1$d has been updated from "Abandoned" to "%2$s", because at least one payment has been made towards it. If the payment appears in the "Payment Details" table below, you may need to edit its status and/or other details as well.',
1683
+									'event_espresso'
1684
+								),
1685
+								$this->ID(),
1686
+								$this->pretty_status()
1687
+							)
1688
+						);
1689
+					}
1690
+					// get final reg step status
1691
+					$finalized = $this->final_reg_step_completed();
1692
+					// if the 'finalize_registration' step has been initiated (has a timestamp)
1693
+					// but has not yet been fully completed (TRUE)
1694
+					if (is_int($finalized) && $finalized !== false && $finalized !== true) {
1695
+						$this->set_reg_step_completed('finalize_registration');
1696
+						$this->save();
1697
+					}
1698
+				}
1699
+			}
1700
+		}
1701
+	}
1702
+
1703
+
1704
+	/**
1705
+	 * @throws EE_Error
1706
+	 * @throws InvalidArgumentException
1707
+	 * @throws InvalidDataTypeException
1708
+	 * @throws InvalidInterfaceException
1709
+	 * @throws ReflectionException
1710
+	 * @throws RuntimeException
1711
+	 * @since 4.10.4.p
1712
+	 */
1713
+	public function recalculateLineItems()
1714
+	{
1715
+		$total_line_item = $this->total_line_item(false);
1716
+		if ($total_line_item instanceof EE_Line_Item) {
1717
+			EEH_Line_Item::resetIsTaxableForTickets($total_line_item);
1718
+			return EEH_Line_Item::apply_taxes($total_line_item, true);
1719
+		}
1720
+		return false;
1721
+	}
1722 1722
 }
Please login to merge, or discard this patch.
Spacing   +23 added lines, -23 removed lines patch added patch discarded remove patch
@@ -46,7 +46,7 @@  discard block
 block discarded – undo
46 46
         $txn        = $has_object
47 47
             ? $has_object
48 48
             : new self($props_n_values, false, $timezone, $date_formats);
49
-        if (! $has_object) {
49
+        if ( ! $has_object) {
50 50
             $txn->set_old_txn_status($txn->status_ID());
51 51
         }
52 52
         return $txn;
@@ -86,7 +86,7 @@  discard block
 block discarded – undo
86 86
     public function lock()
87 87
     {
88 88
         // attempt to set lock, but if that fails...
89
-        if (! $this->add_extra_meta('lock', time(), true)) {
89
+        if ( ! $this->add_extra_meta('lock', time(), true)) {
90 90
             // then attempt to remove the lock in case it is expired
91 91
             if ($this->_remove_expired_lock()) {
92 92
                 // if removal was successful, then try setting lock again
@@ -142,7 +142,7 @@  discard block
 block discarded – undo
142 142
     public function is_locked()
143 143
     {
144 144
         // if TXN is not locked, then return false immediately
145
-        if (! $this->_get_lock()) {
145
+        if ( ! $this->_get_lock()) {
146 146
             return false;
147 147
         }
148 148
         // if not, then let's try and remove the lock in case it's expired...
@@ -622,7 +622,7 @@  discard block
 block discarded – undo
622 622
             false,
623 623
             'sentence'
624 624
         );
625
-        $icon   = '';
625
+        $icon = '';
626 626
         switch ($this->status_ID()) {
627 627
             case EEM_Transaction::complete_status_code:
628 628
                 $icon = $show_icons ? '<span class="dashicons dashicons-yes ee-icon-size-24 green-text"></span>' : '';
@@ -641,7 +641,7 @@  discard block
 block discarded – undo
641 641
                 $icon = $show_icons ? '<span class="dashicons dashicons-plus ee-icon-size-16 orange-text"></span>' : '';
642 642
                 break;
643 643
         }
644
-        return $icon . $status[ $this->status_ID() ];
644
+        return $icon.$status[$this->status_ID()];
645 645
     }
646 646
 
647 647
 
@@ -779,7 +779,7 @@  discard block
 block discarded – undo
779 779
     public function invoice_url($type = 'html')
780 780
     {
781 781
         $REG = $this->primary_registration();
782
-        if (! $REG instanceof EE_Registration) {
782
+        if ( ! $REG instanceof EE_Registration) {
783 783
             return '';
784 784
         }
785 785
         return $REG->invoice_url($type);
@@ -831,7 +831,7 @@  discard block
 block discarded – undo
831 831
     public function receipt_url($type = 'html')
832 832
     {
833 833
         $REG = $this->primary_registration();
834
-        if (! $REG instanceof EE_Registration) {
834
+        if ( ! $REG instanceof EE_Registration) {
835 835
             return '';
836 836
         }
837 837
         return $REG->receipt_url($type);
@@ -1021,7 +1021,7 @@  discard block
 block discarded – undo
1021 1021
     public function total_line_item($create_if_not_found = true)
1022 1022
     {
1023 1023
         $item = $this->get_first_related('Line_Item', [['LIN_type' => EEM_Line_Item::type_total]]);
1024
-        if (! $item && $create_if_not_found) {
1024
+        if ( ! $item && $create_if_not_found) {
1025 1025
             $item = EEH_Line_Item::create_total_line_item($this);
1026 1026
         }
1027 1027
         return $item;
@@ -1078,7 +1078,7 @@  discard block
 block discarded – undo
1078 1078
     public function billing_info()
1079 1079
     {
1080 1080
         $payment_method = $this->payment_method();
1081
-        if (! $payment_method) {
1081
+        if ( ! $payment_method) {
1082 1082
             EE_Error::add_error(
1083 1083
                 esc_html__(
1084 1084
                     'Could not find billing info for transaction because no gateway has been used for it yet',
@@ -1091,7 +1091,7 @@  discard block
 block discarded – undo
1091 1091
             return null;
1092 1092
         }
1093 1093
         $primary_reg = $this->primary_registration();
1094
-        if (! $primary_reg) {
1094
+        if ( ! $primary_reg) {
1095 1095
             EE_Error::add_error(
1096 1096
                 esc_html__(
1097 1097
                     'Cannot get billing info for gateway %s on transaction because no primary registration exists',
@@ -1104,7 +1104,7 @@  discard block
 block discarded – undo
1104 1104
             return null;
1105 1105
         }
1106 1106
         $attendee = $primary_reg->attendee();
1107
-        if (! $attendee) {
1107
+        if ( ! $attendee) {
1108 1108
             EE_Error::add_error(
1109 1109
                 esc_html__(
1110 1110
                     'Cannot get billing info for gateway %s on transaction because the primary registration has no attendee exists',
@@ -1261,7 +1261,7 @@  discard block
 block discarded – undo
1261 1261
     public function update_based_on_payments()
1262 1262
     {
1263 1263
         EE_Error::doing_it_wrong(
1264
-            __CLASS__ . '::' . __FUNCTION__,
1264
+            __CLASS__.'::'.__FUNCTION__,
1265 1265
             sprintf(
1266 1266
                 esc_html__('This method is deprecated. Please use "%s" instead', 'event_espresso'),
1267 1267
                 'EE_Transaction_Processor::update_transaction_and_registrations_after_checkout_or_payment()'
@@ -1329,13 +1329,13 @@  discard block
 block discarded – undo
1329 1329
     private function _reg_steps_completed($reg_step_slug = '', $check_all = true)
1330 1330
     {
1331 1331
         $reg_steps = $this->reg_steps();
1332
-        if (! is_array($reg_steps) || empty($reg_steps)) {
1332
+        if ( ! is_array($reg_steps) || empty($reg_steps)) {
1333 1333
             return false;
1334 1334
         }
1335 1335
         // loop thru reg steps array)
1336 1336
         foreach ($reg_steps as $slug => $reg_step_completed) {
1337 1337
             // if NOT checking ALL steps (only checking one step)
1338
-            if (! $check_all) {
1338
+            if ( ! $check_all) {
1339 1339
                 // and this is the one
1340 1340
                 if ($slug === $reg_step_slug) {
1341 1341
                     return $reg_step_completed;
@@ -1538,30 +1538,30 @@  discard block
 block discarded – undo
1538 1538
         // get reg steps array
1539 1539
         $txn_reg_steps = $this->reg_steps();
1540 1540
         // if reg step does NOT exist
1541
-        if (! isset($txn_reg_steps[ $reg_step_slug ])) {
1541
+        if ( ! isset($txn_reg_steps[$reg_step_slug])) {
1542 1542
             return false;
1543 1543
         }
1544 1544
         // if  we're trying to complete a step that is already completed
1545
-        if ($txn_reg_steps[ $reg_step_slug ] === true) {
1545
+        if ($txn_reg_steps[$reg_step_slug] === true) {
1546 1546
             return true;
1547 1547
         }
1548 1548
         // if  we're trying to complete a step that hasn't even started
1549
-        if ($status === true && $txn_reg_steps[ $reg_step_slug ] === false) {
1549
+        if ($status === true && $txn_reg_steps[$reg_step_slug] === false) {
1550 1550
             return false;
1551 1551
         }
1552 1552
         // if current status value matches the incoming value (no change)
1553 1553
         // type casting as int means values should collapse to either 0, 1, or a timestamp like 1234567890
1554
-        if ((int) $txn_reg_steps[ $reg_step_slug ] === (int) $status) {
1554
+        if ((int) $txn_reg_steps[$reg_step_slug] === (int) $status) {
1555 1555
             // this will happen in cases where multiple AJAX requests occur during the same step
1556 1556
             return true;
1557 1557
         }
1558 1558
         // if we're trying to set a start time, but it has already been set...
1559
-        if (is_numeric($status) && is_numeric($txn_reg_steps[ $reg_step_slug ])) {
1559
+        if (is_numeric($status) && is_numeric($txn_reg_steps[$reg_step_slug])) {
1560 1560
             // skip the update below, but don't return FALSE so that errors won't be displayed
1561 1561
             return true;
1562 1562
         }
1563 1563
         // update completed status
1564
-        $txn_reg_steps[ $reg_step_slug ] = $status;
1564
+        $txn_reg_steps[$reg_step_slug] = $status;
1565 1565
         $this->set_reg_steps($txn_reg_steps);
1566 1566
         $this->save();
1567 1567
         return true;
@@ -1585,7 +1585,7 @@  discard block
 block discarded – undo
1585 1585
     {
1586 1586
         // get reg steps array
1587 1587
         $txn_reg_steps = $this->reg_steps();
1588
-        unset($txn_reg_steps[ $reg_step_slug ]);
1588
+        unset($txn_reg_steps[$reg_step_slug]);
1589 1589
         $this->set_reg_steps($txn_reg_steps);
1590 1590
     }
1591 1591
 
@@ -1670,12 +1670,12 @@  discard block
 block discarded – undo
1670 1670
             return;
1671 1671
         }
1672 1672
         $payments = $this->get_many_related('Payment');
1673
-        if (! empty($payments)) {
1673
+        if ( ! empty($payments)) {
1674 1674
             foreach ($payments as $payment) {
1675 1675
                 if ($payment instanceof EE_Payment) {
1676 1676
                     // kk this TXN should NOT be abandoned
1677 1677
                     $this->update_status_based_on_total_paid();
1678
-                    if (! (defined('DOING_AJAX') && DOING_AJAX) && is_admin()) {
1678
+                    if ( ! (defined('DOING_AJAX') && DOING_AJAX) && is_admin()) {
1679 1679
                         EE_Error::add_attention(
1680 1680
                             sprintf(
1681 1681
                                 esc_html__(
Please login to merge, or discard this patch.
core/db_classes/EE_Soft_Delete_Base_Class.class.php 1 patch
Indentation   +51 added lines, -51 removed lines patch added patch discarded remove patch
@@ -11,59 +11,59 @@
 block discarded – undo
11 11
  */
12 12
 abstract class EE_Soft_Delete_Base_Class extends EE_Base_Class
13 13
 {
14
-    /**
15
-     * Overrides parent _delete() so that we do soft deletes.
16
-     *
17
-     * @return bool
18
-     * @throws EE_Error
19
-     * @throws ReflectionException
20
-     */
21
-    protected function _delete()
22
-    {
23
-        return $this->delete_or_restore();
24
-    }
14
+	/**
15
+	 * Overrides parent _delete() so that we do soft deletes.
16
+	 *
17
+	 * @return bool
18
+	 * @throws EE_Error
19
+	 * @throws ReflectionException
20
+	 */
21
+	protected function _delete()
22
+	{
23
+		return $this->delete_or_restore();
24
+	}
25 25
 
26 26
 
27
-    /**
28
-     * Deletes or restores this object.
29
-     *
30
-     * @param bool $delete true=>delete, false=>restore
31
-     * @return bool
32
-     * @throws EE_Error
33
-     * @throws ReflectionException
34
-     */
35
-    public function delete_or_restore($delete = true)
36
-    {
37
-        /**
38
-         * Called just before trashing (soft delete) or restoring a trashed item.
39
-         *
40
-         * @param EE_Base_Class $model_object about to be trashed or restored
41
-         * @param bool          $delete       true the item is being trashed, false the item is being restored.
42
-         */
43
-        do_action('AHEE__EE_Soft_Delete_Base_Class__delete_or_restore__before', $this, $delete);
44
-        $model = $this->get_model();
45
-        $result = $model->delete_or_restore_by_ID($delete, $this->ID());
46
-        /**
47
-         * Called just after trashing (soft delete) or restoring a trashed item.
48
-         *
49
-         * @param EE_Base_Class $model_object that was just trashed or restored.
50
-         * @param bool          $delete       true the item is being trashed, false the item is being restored.
51
-         * @param bool|int      $result
52
-         */
53
-        do_action('AHEE__EE_Soft_Delete_Base_Class__delete_or_restore__after', $this, $delete, $result);
54
-        return $result;
55
-    }
27
+	/**
28
+	 * Deletes or restores this object.
29
+	 *
30
+	 * @param bool $delete true=>delete, false=>restore
31
+	 * @return bool
32
+	 * @throws EE_Error
33
+	 * @throws ReflectionException
34
+	 */
35
+	public function delete_or_restore($delete = true)
36
+	{
37
+		/**
38
+		 * Called just before trashing (soft delete) or restoring a trashed item.
39
+		 *
40
+		 * @param EE_Base_Class $model_object about to be trashed or restored
41
+		 * @param bool          $delete       true the item is being trashed, false the item is being restored.
42
+		 */
43
+		do_action('AHEE__EE_Soft_Delete_Base_Class__delete_or_restore__before', $this, $delete);
44
+		$model = $this->get_model();
45
+		$result = $model->delete_or_restore_by_ID($delete, $this->ID());
46
+		/**
47
+		 * Called just after trashing (soft delete) or restoring a trashed item.
48
+		 *
49
+		 * @param EE_Base_Class $model_object that was just trashed or restored.
50
+		 * @param bool          $delete       true the item is being trashed, false the item is being restored.
51
+		 * @param bool|int      $result
52
+		 */
53
+		do_action('AHEE__EE_Soft_Delete_Base_Class__delete_or_restore__after', $this, $delete, $result);
54
+		return $result;
55
+	}
56 56
 
57 57
 
58
-    /**
59
-     * Performs a restoration (un-deletes) this object
60
-     *
61
-     * @return bool
62
-     * @throws EE_Error
63
-     * @throws ReflectionException
64
-     */
65
-    public function restore()
66
-    {
67
-        return $this->delete_or_restore(false);
68
-    }
58
+	/**
59
+	 * Performs a restoration (un-deletes) this object
60
+	 *
61
+	 * @return bool
62
+	 * @throws EE_Error
63
+	 * @throws ReflectionException
64
+	 */
65
+	public function restore()
66
+	{
67
+		return $this->delete_or_restore(false);
68
+	}
69 69
 }
Please login to merge, or discard this patch.
core/db_models/EEM_Price_Type.model.php 1 patch
Indentation   +124 added lines, -124 removed lines patch added patch discarded remove patch
@@ -11,142 +11,142 @@
 block discarded – undo
11 11
  */
12 12
 class EEM_Price_Type extends EEM_Soft_Delete_Base
13 13
 {
14
-    // private instance of the Price Type object
15
-    protected static $_instance = null;
16
-    // An array of the price type objects
17
-    public $type = null;
14
+	// private instance of the Price Type object
15
+	protected static $_instance = null;
16
+	// An array of the price type objects
17
+	public $type = null;
18 18
 
19
-    /**
20
-    *   Price Base types
21
-    *
22
-    *   @access private
23
-    *   @var int
24
-    */
25
-    public $base_types = null;
19
+	/**
20
+	 *   Price Base types
21
+	 *
22
+	 *   @access private
23
+	 *   @var int
24
+	 */
25
+	public $base_types = null;
26 26
 
27
-    /**
28
-     * return an array of Base types. Keys are INTs which are used in the database,
29
-     * values are text-representations of the base type.
30
-     * @return array
31
-     */
32
-    public function get_base_types()
33
-    {
34
-        return $this->base_types;
35
-    }
27
+	/**
28
+	 * return an array of Base types. Keys are INTs which are used in the database,
29
+	 * values are text-representations of the base type.
30
+	 * @return array
31
+	 */
32
+	public function get_base_types()
33
+	{
34
+		return $this->base_types;
35
+	}
36 36
 
37
-    /**
38
-     * Gets the name of the base
39
-     * @param type $base_type_int
40
-     * @return type
41
-     */
42
-    public function get_base_type_name($base_type_int)
43
-    {
44
-        return $this->base_types[ $base_type_int ];
45
-    }
37
+	/**
38
+	 * Gets the name of the base
39
+	 * @param type $base_type_int
40
+	 * @return type
41
+	 */
42
+	public function get_base_type_name($base_type_int)
43
+	{
44
+		return $this->base_types[ $base_type_int ];
45
+	}
46 46
 
47
-    /**
48
-     * constants for price base types. In the DB, we decided to store the price base type
49
-     * as an integer. So, to avoid just having magic numbers everwhere (eg, querying for
50
-     * all price types with PBT_ID = 2), we define these constants, to make code more understandable.
51
-     * So, as an example, to query for all price types that are a tax, we'd do
52
-     * EEM_PRice_Type::instance()->get_all(array(array('PBT_ID'=>EEM_Price_Type::base_type_tax)))
53
-     * instead of
54
-     * EEM_Price_Type::instance()->get_all(array(array('PBT_ID'=>2)))
55
-     * Although the 2nd is shorter, it's much less obvious what it's doing. Also, should these magic IDs ever
56
-     * change, we can continue to use the constant, by simply change its value.
57
-     */
58
-    const base_type_base_price = 1;
59
-    const base_type_discount = 2;
60
-    const base_type_surcharge = 3;
61
-    const base_type_tax = 4;
62
-    /**
63
-     *      private constructor to prevent direct creation
64
-     *      @Constructor
65
-     *      @access protected
66
-     *      @return void
67
-     */
68
-    protected function __construct($timezone = null)
69
-    {
70
-        $this->base_types = array(
71
-            EEM_Price_Type::base_type_base_price => esc_html__('Price', 'event_espresso'),
72
-            EEM_Price_Type::base_type_discount => esc_html__('Discount', 'event_espresso'),
73
-            EEM_Price_Type::base_type_surcharge => esc_html__('Surcharge', 'event_espresso'),
74
-            EEM_Price_Type::base_type_tax => esc_html__('Tax', 'event_espresso') );
75
-        $this->singular_item = esc_html__('Price Type', 'event_espresso');
76
-        $this->plural_item = esc_html__('Price Types', 'event_espresso');
47
+	/**
48
+	 * constants for price base types. In the DB, we decided to store the price base type
49
+	 * as an integer. So, to avoid just having magic numbers everwhere (eg, querying for
50
+	 * all price types with PBT_ID = 2), we define these constants, to make code more understandable.
51
+	 * So, as an example, to query for all price types that are a tax, we'd do
52
+	 * EEM_PRice_Type::instance()->get_all(array(array('PBT_ID'=>EEM_Price_Type::base_type_tax)))
53
+	 * instead of
54
+	 * EEM_Price_Type::instance()->get_all(array(array('PBT_ID'=>2)))
55
+	 * Although the 2nd is shorter, it's much less obvious what it's doing. Also, should these magic IDs ever
56
+	 * change, we can continue to use the constant, by simply change its value.
57
+	 */
58
+	const base_type_base_price = 1;
59
+	const base_type_discount = 2;
60
+	const base_type_surcharge = 3;
61
+	const base_type_tax = 4;
62
+	/**
63
+	 *      private constructor to prevent direct creation
64
+	 *      @Constructor
65
+	 *      @access protected
66
+	 *      @return void
67
+	 */
68
+	protected function __construct($timezone = null)
69
+	{
70
+		$this->base_types = array(
71
+			EEM_Price_Type::base_type_base_price => esc_html__('Price', 'event_espresso'),
72
+			EEM_Price_Type::base_type_discount => esc_html__('Discount', 'event_espresso'),
73
+			EEM_Price_Type::base_type_surcharge => esc_html__('Surcharge', 'event_espresso'),
74
+			EEM_Price_Type::base_type_tax => esc_html__('Tax', 'event_espresso') );
75
+		$this->singular_item = esc_html__('Price Type', 'event_espresso');
76
+		$this->plural_item = esc_html__('Price Types', 'event_espresso');
77 77
 
78
-        $this->_tables = array(
79
-            'Price_Type' => new EE_Primary_Table('esp_price_type', 'PRT_ID')
80
-        );
81
-        $this->_fields = array(
82
-            'Price_Type' => array(
83
-                'PRT_ID' => new EE_Primary_Key_Int_Field('PRT_ID', esc_html__('Price Type ID', 'event_espresso')),
84
-                'PRT_name' => new EE_Plain_Text_Field('PRT_name', esc_html__('Price Type Name', 'event_espresso'), false, ''),
85
-                'PBT_ID' => new EE_Enum_Integer_Field('PBT_ID', esc_html__('Price Base type ID, 1 = Price , 2 = Discount , 3 = Surcharge , 4 = Tax', 'event_espresso'), false, EEM_Price_Type::base_type_base_price, $this->base_types),
86
-                'PRT_is_percent' => new EE_Boolean_Field('PRT_is_percent', esc_html__('Flag indicating price is a percentage', 'event_espresso'), false, false),
87
-                'PRT_order' => new EE_Integer_Field('PRT_order', esc_html__('Order in which price should be applied. ', 'event_espresso'), false, 0),
88
-                'PRT_deleted' => new EE_Trashed_Flag_Field('PRT_deleted', esc_html__('Flag indicating price type has been trashed', 'event_espresso'), false, false),
89
-                'PRT_wp_user' => new EE_WP_User_Field('PRT_wp_user', esc_html__('Price Type Creator ID', 'event_espresso'), false),
90
-            )
91
-        );
92
-        $this->_model_relations = array(
93
-            'Price' => new EE_Has_Many_Relation(),
94
-            'WP_User' => new EE_Belongs_To_Relation(),
95
-        );
96
-        // this model is generally available for reading
97
-        $this->_cap_restriction_generators[ EEM_Base::caps_read ] = new EE_Restriction_Generator_Public();
98
-        // all price types are "default" in terms of capability names
99
-        $this->_caps_slug = 'default_price_types';
100
-        parent::__construct($timezone);
101
-    }
78
+		$this->_tables = array(
79
+			'Price_Type' => new EE_Primary_Table('esp_price_type', 'PRT_ID')
80
+		);
81
+		$this->_fields = array(
82
+			'Price_Type' => array(
83
+				'PRT_ID' => new EE_Primary_Key_Int_Field('PRT_ID', esc_html__('Price Type ID', 'event_espresso')),
84
+				'PRT_name' => new EE_Plain_Text_Field('PRT_name', esc_html__('Price Type Name', 'event_espresso'), false, ''),
85
+				'PBT_ID' => new EE_Enum_Integer_Field('PBT_ID', esc_html__('Price Base type ID, 1 = Price , 2 = Discount , 3 = Surcharge , 4 = Tax', 'event_espresso'), false, EEM_Price_Type::base_type_base_price, $this->base_types),
86
+				'PRT_is_percent' => new EE_Boolean_Field('PRT_is_percent', esc_html__('Flag indicating price is a percentage', 'event_espresso'), false, false),
87
+				'PRT_order' => new EE_Integer_Field('PRT_order', esc_html__('Order in which price should be applied. ', 'event_espresso'), false, 0),
88
+				'PRT_deleted' => new EE_Trashed_Flag_Field('PRT_deleted', esc_html__('Flag indicating price type has been trashed', 'event_espresso'), false, false),
89
+				'PRT_wp_user' => new EE_WP_User_Field('PRT_wp_user', esc_html__('Price Type Creator ID', 'event_espresso'), false),
90
+			)
91
+		);
92
+		$this->_model_relations = array(
93
+			'Price' => new EE_Has_Many_Relation(),
94
+			'WP_User' => new EE_Belongs_To_Relation(),
95
+		);
96
+		// this model is generally available for reading
97
+		$this->_cap_restriction_generators[ EEM_Base::caps_read ] = new EE_Restriction_Generator_Public();
98
+		// all price types are "default" in terms of capability names
99
+		$this->_caps_slug = 'default_price_types';
100
+		parent::__construct($timezone);
101
+	}
102 102
 
103 103
 
104 104
 
105 105
 
106
-    /**
107
-     *      instantiate a new price type object with blank/empty properties
108
-     *
109
-     *      @access     public
110
-     *      @return     mixed       array on success, FALSE on fail
111
-     */
112
-    public function get_new_price_type()
113
-    {
114
-        return EE_Price_Type::new_instance();
115
-    }
106
+	/**
107
+	 *      instantiate a new price type object with blank/empty properties
108
+	 *
109
+	 *      @access     public
110
+	 *      @return     mixed       array on success, FALSE on fail
111
+	 */
112
+	public function get_new_price_type()
113
+	{
114
+		return EE_Price_Type::new_instance();
115
+	}
116 116
 
117 117
 
118
-    /**
119
-     *
120
-     * @param array   $query_params
121
-     * @param bool    $allow_blocking if TRUE, matched objects will only be deleted if there is no related model info
122
-     *                                that blocks it (ie, there' sno other data that depends on this data);
123
-     *                                if false, deletes regardless of other objects which may depend on it.
124
-     *                                Its generally advisable to always leave this as TRUE,
125
-     *                                otherwise you could easily corrupt your DB
126
-     * @return bool
127
-     * @throws EE_Error
128
-     * @throws ReflectionException
129
-     */
130
-    public function delete_permanently($query_params = array(), $allow_blocking = true)
131
-    {
132
-        $would_be_deleted_price_types = $this->get_all_deleted_and_undeleted($query_params);
133
-        $would_be_deleted_price_type_ids = array_keys($would_be_deleted_price_types);
118
+	/**
119
+	 *
120
+	 * @param array   $query_params
121
+	 * @param bool    $allow_blocking if TRUE, matched objects will only be deleted if there is no related model info
122
+	 *                                that blocks it (ie, there' sno other data that depends on this data);
123
+	 *                                if false, deletes regardless of other objects which may depend on it.
124
+	 *                                Its generally advisable to always leave this as TRUE,
125
+	 *                                otherwise you could easily corrupt your DB
126
+	 * @return bool
127
+	 * @throws EE_Error
128
+	 * @throws ReflectionException
129
+	 */
130
+	public function delete_permanently($query_params = array(), $allow_blocking = true)
131
+	{
132
+		$would_be_deleted_price_types = $this->get_all_deleted_and_undeleted($query_params);
133
+		$would_be_deleted_price_type_ids = array_keys($would_be_deleted_price_types);
134 134
 
135
-        $ID = $query_params[0][ $this->get_primary_key_field()->get_name() ];
135
+		$ID = $query_params[0][ $this->get_primary_key_field()->get_name() ];
136 136
 
137
-        // check if any prices use this price type
138
-        $prc_query_params = array(array('PRT_ID' => array('IN',$would_be_deleted_price_type_ids)));
139
-        if ($prices = $this->get_all_related($ID, 'Price', $prc_query_params)) {
140
-            $prices_names_and_ids = array();
141
-            foreach ($prices as $price) {
142
-                /* @var $price EE_Price */
143
-                $prices_names_and_ids[] = $price->name() . "(" . $price->ID() . ")";
144
-            }
145
-            $msg = sprintf(esc_html__('The Price Type(s) could not be deleted because there are existing Prices that currently use this Price Type.  If you still wish to delete this Price Type, then either delete those Prices or change them to use other Price Types.The prices are: %s', 'event_espresso'), implode(",", $prices_names_and_ids));
146
-            EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
147
-            return false;
148
-        }
137
+		// check if any prices use this price type
138
+		$prc_query_params = array(array('PRT_ID' => array('IN',$would_be_deleted_price_type_ids)));
139
+		if ($prices = $this->get_all_related($ID, 'Price', $prc_query_params)) {
140
+			$prices_names_and_ids = array();
141
+			foreach ($prices as $price) {
142
+				/* @var $price EE_Price */
143
+				$prices_names_and_ids[] = $price->name() . "(" . $price->ID() . ")";
144
+			}
145
+			$msg = sprintf(esc_html__('The Price Type(s) could not be deleted because there are existing Prices that currently use this Price Type.  If you still wish to delete this Price Type, then either delete those Prices or change them to use other Price Types.The prices are: %s', 'event_espresso'), implode(",", $prices_names_and_ids));
146
+			EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
147
+			return false;
148
+		}
149 149
 
150
-        return parent::delete_permanently($query_params);
151
-    }
150
+		return parent::delete_permanently($query_params);
151
+	}
152 152
 }
Please login to merge, or discard this patch.