Completed
Branch master (7421d0)
by
unknown
11:25 queued 06:55
created
PaymentMethods/Manager.php 1 patch
Indentation   +207 added lines, -207 removed lines patch added patch discarded remove patch
@@ -21,211 +21,211 @@
 block discarded – undo
21 21
  */
22 22
 class Manager
23 23
 {
24
-    /**
25
-     * List of PMs that can be replaced with PP Commerce.
26
-     * ['payment method name' => 'settings option']
27
-     *
28
-     * @var array $pms_can_hide
29
-     */
30
-    protected static array $pms_can_hide = [
31
-        'paypal_express' => 'api_username',
32
-        'paypal_pro'     => 'api_username',
33
-        'aim'            => 'login_id',
34
-    ];
35
-
36
-
37
-    /**
38
-     * Manager constructor.
39
-     */
40
-    public function __construct()
41
-    {
42
-        if (DbStatus::isOffline()) {
43
-            return;
44
-        }
45
-        $this->loadPaymentMethods();
46
-        if (is_admin()) {
47
-            // Use only PayPal Commerce if it's a new setup.
48
-            add_filter(
49
-                'FHEE__Payments_Admin_Page___payment_methods_list__payment_methods',
50
-                [__CLASS__, 'hidePaymentMethods']
51
-            );
52
-            // Payment methods related admin notices.
53
-            add_action('admin_init', [__CLASS__, 'adminNotice']);
54
-        }
55
-    }
56
-
57
-
58
-    /**
59
-     * Load all payment methods that are in PaymentMethods folder.
60
-     *
61
-     * @return void
62
-     */
63
-    protected function loadPaymentMethods()
64
-    {
65
-        // Scan the PaymentMethods folder.
66
-        $pms_list = glob(EE_PLUGIN_DIR_PATH . 'PaymentMethods/*', GLOB_ONLYDIR);
67
-        // Filter the discovered PM list.
68
-        $pms_list = apply_filters('FHEE__PaymentMethods__Manager__loadPaymentMethods__pms_list', $pms_list);
69
-        // Clean from duplicates.
70
-        $pms_list = array_unique($pms_list);
71
-        foreach ($pms_list as $pm_path) {
72
-            $this->registerPaymentMethod($pm_path);
73
-        }
74
-    }
75
-
76
-
77
-    /**
78
-     * Looks for the main payment method file and loads it.
79
-     *
80
-     * @param string $pm_path path to the payment method folder
81
-     * @param string $file_ext
82
-     * @param string $pm_namespace
83
-     * @return boolean
84
-     */
85
-    public function registerPaymentMethod(
86
-        string $pm_path,
87
-        string $file_ext = '.php',
88
-        string $pm_namespace = 'EventEspresso\PaymentMethods'
89
-    ): bool {
90
-        do_action('AHEE__PaymentMethods__Manager__registerPaymentMethod__start', $pm_path);
91
-        // Separators should match.
92
-        $pm_path = str_replace('/\\', '/', $pm_path) . DS;
93
-        // Sanitize PM name.
94
-        $module_dir = basename($pm_path);
95
-        // Get class name.
96
-        $pm_class_name = str_replace(' ', '_', $module_dir);
97
-        // Check if file exists.
98
-        if (! is_readable($pm_path . $pm_class_name . $file_ext)) {
99
-            return false;
100
-        }
101
-        // Load the initial PM class.
102
-        require_once($pm_path . DS . $pm_class_name . $file_ext);
103
-        $pm_object = "$pm_namespace\\$pm_class_name";
104
-        if (! class_exists($pm_object)) {
105
-            return false;
106
-        }
107
-        new $pm_object();
108
-        return true;
109
-    }
110
-
111
-
112
-    /**
113
-     * Deactivate a few other PMs if it's a new setup. Use PP Commerce.
114
-     *
115
-     * @param array $pms_to_list
116
-     * @return array
117
-     * @throws EE_Error
118
-     * @throws ReflectionException
119
-     */
120
-    public static function hidePaymentMethods(array $pms_to_list): array
121
-    {
122
-        foreach (self::$pms_can_hide as $pm_name => $pm_option) {
123
-            // Can we deregister this PM ?
124
-            if (isset($pms_to_list[ $pm_name ]) && self::pmCanBeHidden($pm_name, $pm_option)) {
125
-                unset($pms_to_list[ $pm_name ]);
126
-            }
127
-        }
128
-        return $pms_to_list;
129
-    }
130
-
131
-
132
-    /**
133
-     * Deregisters the provided payment method if not used.
134
-     *
135
-     * @param string $pm_name
136
-     * @param string $pm_option
137
-     * @return bool
138
-     * @throws EE_Error
139
-     * @throws ReflectionException
140
-     */
141
-    public static function pmCanBeHidden(string $pm_name, string $pm_option): bool
142
-    {
143
-        $pm_to_hide = EEM_Payment_Method::instance()->get_one_by_slug($pm_name);
144
-        $pm_active  = $pm_active_before = false;
145
-        if ($pm_to_hide instanceof EE_Payment_Method) {
146
-            $pm_active = $pm_to_hide->active();
147
-            // Payment method used before ?
148
-            $option = $pm_to_hide->get_extra_meta($pm_option, true, false);
149
-            $pm_active_before = ! empty($option);
150
-        }
151
-        // If PM not used before and not active, deregister it.
152
-        if (
153
-            apply_filters(
154
-                "FHEE__PaymentMethods__Manager__register_payment_methods__hide_$pm_name",
155
-                ! $pm_active && ! $pm_active_before,
156
-                $pm_name
157
-            )
158
-        ) {
159
-            return true;
160
-        }
161
-        return false;
162
-    }
163
-
164
-
165
-    /**
166
-     * Payment methods related admin notices.
167
-     *
168
-     * @return void
169
-     */
170
-    public static function adminNotice()
171
-    {
172
-        // Is this an EE admin page ?
173
-        $request   = LoaderFactory::getLoader()->getShared(RequestInterface::class);
174
-        $page_name = $request->getRequestParam('page');
175
-        // Only show the notice on core EE pages
176
-        if (! str_contains($page_name, 'espresso')) {
177
-            return;
178
-        }
179
-        // Notice if one of the following payment methods is used: PayPal Express, PayPal Pro, Authorize.net AIM.
180
-        try {
181
-            $pp_commerce = EEM_Payment_Method::instance()->get_one_by_slug('paypalcheckout');
182
-            // Don't show notice if PayPal Commerce is active.
183
-            if ($pp_commerce instanceof EE_Payment_Method && $pp_commerce->active()) {
184
-                return;
185
-            }
186
-            foreach (self::$pms_can_hide as $pm_name => $pm_option) {
187
-                $payment_method = EEM_Payment_Method::instance()->get_one_by_slug($pm_name);
188
-                if ($payment_method instanceof EE_Payment_Method && $payment_method->active()) {
189
-                    add_action('admin_notices', [__CLASS__, 'usePayPalCommerceNotice']);
190
-                    return;
191
-                }
192
-            }
193
-        } catch (EE_Error | ReflectionException $e) {
194
-            // No handling needed right now.
195
-        }
196
-    }
197
-
198
-
199
-    /**
200
-     * Recommend PayPal Commerce notice contents.
201
-     *
202
-     * @return void
203
-     */
204
-    public static function usePayPalCommerceNotice()
205
-    {
206
-        echo '<div class="error"><p>'
207
-         . sprintf(
208
-             esc_html__(
209
-                 'We recommend using our latest PayPal integration - %1$sPayPal Commerce%2$s payment method in place of PayPal Standard, PayPal Express and PayPal Pro.',
210
-                 'event_espresso'
211
-             ),
212
-             '<strong>',
213
-             '</strong>'
214
-         )
215
-         . '</p></div>';
216
-    }
217
-
218
-
219
-    /**
220
-     * @return bool
221
-     * @since  5.0.20.p
222
-     */
223
-    public static function verifySSL(): bool
224
-    {
225
-        static $verify_ssl = null;
226
-        if ($verify_ssl === null) {
227
-            $verify_ssl = (bool) apply_filters('FHEE__EventEspresso_PaymentMethods_Manager__verifySSL', true);
228
-        }
229
-        return (bool) $verify_ssl;
230
-    }
24
+	/**
25
+	 * List of PMs that can be replaced with PP Commerce.
26
+	 * ['payment method name' => 'settings option']
27
+	 *
28
+	 * @var array $pms_can_hide
29
+	 */
30
+	protected static array $pms_can_hide = [
31
+		'paypal_express' => 'api_username',
32
+		'paypal_pro'     => 'api_username',
33
+		'aim'            => 'login_id',
34
+	];
35
+
36
+
37
+	/**
38
+	 * Manager constructor.
39
+	 */
40
+	public function __construct()
41
+	{
42
+		if (DbStatus::isOffline()) {
43
+			return;
44
+		}
45
+		$this->loadPaymentMethods();
46
+		if (is_admin()) {
47
+			// Use only PayPal Commerce if it's a new setup.
48
+			add_filter(
49
+				'FHEE__Payments_Admin_Page___payment_methods_list__payment_methods',
50
+				[__CLASS__, 'hidePaymentMethods']
51
+			);
52
+			// Payment methods related admin notices.
53
+			add_action('admin_init', [__CLASS__, 'adminNotice']);
54
+		}
55
+	}
56
+
57
+
58
+	/**
59
+	 * Load all payment methods that are in PaymentMethods folder.
60
+	 *
61
+	 * @return void
62
+	 */
63
+	protected function loadPaymentMethods()
64
+	{
65
+		// Scan the PaymentMethods folder.
66
+		$pms_list = glob(EE_PLUGIN_DIR_PATH . 'PaymentMethods/*', GLOB_ONLYDIR);
67
+		// Filter the discovered PM list.
68
+		$pms_list = apply_filters('FHEE__PaymentMethods__Manager__loadPaymentMethods__pms_list', $pms_list);
69
+		// Clean from duplicates.
70
+		$pms_list = array_unique($pms_list);
71
+		foreach ($pms_list as $pm_path) {
72
+			$this->registerPaymentMethod($pm_path);
73
+		}
74
+	}
75
+
76
+
77
+	/**
78
+	 * Looks for the main payment method file and loads it.
79
+	 *
80
+	 * @param string $pm_path path to the payment method folder
81
+	 * @param string $file_ext
82
+	 * @param string $pm_namespace
83
+	 * @return boolean
84
+	 */
85
+	public function registerPaymentMethod(
86
+		string $pm_path,
87
+		string $file_ext = '.php',
88
+		string $pm_namespace = 'EventEspresso\PaymentMethods'
89
+	): bool {
90
+		do_action('AHEE__PaymentMethods__Manager__registerPaymentMethod__start', $pm_path);
91
+		// Separators should match.
92
+		$pm_path = str_replace('/\\', '/', $pm_path) . DS;
93
+		// Sanitize PM name.
94
+		$module_dir = basename($pm_path);
95
+		// Get class name.
96
+		$pm_class_name = str_replace(' ', '_', $module_dir);
97
+		// Check if file exists.
98
+		if (! is_readable($pm_path . $pm_class_name . $file_ext)) {
99
+			return false;
100
+		}
101
+		// Load the initial PM class.
102
+		require_once($pm_path . DS . $pm_class_name . $file_ext);
103
+		$pm_object = "$pm_namespace\\$pm_class_name";
104
+		if (! class_exists($pm_object)) {
105
+			return false;
106
+		}
107
+		new $pm_object();
108
+		return true;
109
+	}
110
+
111
+
112
+	/**
113
+	 * Deactivate a few other PMs if it's a new setup. Use PP Commerce.
114
+	 *
115
+	 * @param array $pms_to_list
116
+	 * @return array
117
+	 * @throws EE_Error
118
+	 * @throws ReflectionException
119
+	 */
120
+	public static function hidePaymentMethods(array $pms_to_list): array
121
+	{
122
+		foreach (self::$pms_can_hide as $pm_name => $pm_option) {
123
+			// Can we deregister this PM ?
124
+			if (isset($pms_to_list[ $pm_name ]) && self::pmCanBeHidden($pm_name, $pm_option)) {
125
+				unset($pms_to_list[ $pm_name ]);
126
+			}
127
+		}
128
+		return $pms_to_list;
129
+	}
130
+
131
+
132
+	/**
133
+	 * Deregisters the provided payment method if not used.
134
+	 *
135
+	 * @param string $pm_name
136
+	 * @param string $pm_option
137
+	 * @return bool
138
+	 * @throws EE_Error
139
+	 * @throws ReflectionException
140
+	 */
141
+	public static function pmCanBeHidden(string $pm_name, string $pm_option): bool
142
+	{
143
+		$pm_to_hide = EEM_Payment_Method::instance()->get_one_by_slug($pm_name);
144
+		$pm_active  = $pm_active_before = false;
145
+		if ($pm_to_hide instanceof EE_Payment_Method) {
146
+			$pm_active = $pm_to_hide->active();
147
+			// Payment method used before ?
148
+			$option = $pm_to_hide->get_extra_meta($pm_option, true, false);
149
+			$pm_active_before = ! empty($option);
150
+		}
151
+		// If PM not used before and not active, deregister it.
152
+		if (
153
+			apply_filters(
154
+				"FHEE__PaymentMethods__Manager__register_payment_methods__hide_$pm_name",
155
+				! $pm_active && ! $pm_active_before,
156
+				$pm_name
157
+			)
158
+		) {
159
+			return true;
160
+		}
161
+		return false;
162
+	}
163
+
164
+
165
+	/**
166
+	 * Payment methods related admin notices.
167
+	 *
168
+	 * @return void
169
+	 */
170
+	public static function adminNotice()
171
+	{
172
+		// Is this an EE admin page ?
173
+		$request   = LoaderFactory::getLoader()->getShared(RequestInterface::class);
174
+		$page_name = $request->getRequestParam('page');
175
+		// Only show the notice on core EE pages
176
+		if (! str_contains($page_name, 'espresso')) {
177
+			return;
178
+		}
179
+		// Notice if one of the following payment methods is used: PayPal Express, PayPal Pro, Authorize.net AIM.
180
+		try {
181
+			$pp_commerce = EEM_Payment_Method::instance()->get_one_by_slug('paypalcheckout');
182
+			// Don't show notice if PayPal Commerce is active.
183
+			if ($pp_commerce instanceof EE_Payment_Method && $pp_commerce->active()) {
184
+				return;
185
+			}
186
+			foreach (self::$pms_can_hide as $pm_name => $pm_option) {
187
+				$payment_method = EEM_Payment_Method::instance()->get_one_by_slug($pm_name);
188
+				if ($payment_method instanceof EE_Payment_Method && $payment_method->active()) {
189
+					add_action('admin_notices', [__CLASS__, 'usePayPalCommerceNotice']);
190
+					return;
191
+				}
192
+			}
193
+		} catch (EE_Error | ReflectionException $e) {
194
+			// No handling needed right now.
195
+		}
196
+	}
197
+
198
+
199
+	/**
200
+	 * Recommend PayPal Commerce notice contents.
201
+	 *
202
+	 * @return void
203
+	 */
204
+	public static function usePayPalCommerceNotice()
205
+	{
206
+		echo '<div class="error"><p>'
207
+		 . sprintf(
208
+			 esc_html__(
209
+				 'We recommend using our latest PayPal integration - %1$sPayPal Commerce%2$s payment method in place of PayPal Standard, PayPal Express and PayPal Pro.',
210
+				 'event_espresso'
211
+			 ),
212
+			 '<strong>',
213
+			 '</strong>'
214
+		 )
215
+		 . '</p></div>';
216
+	}
217
+
218
+
219
+	/**
220
+	 * @return bool
221
+	 * @since  5.0.20.p
222
+	 */
223
+	public static function verifySSL(): bool
224
+	{
225
+		static $verify_ssl = null;
226
+		if ($verify_ssl === null) {
227
+			$verify_ssl = (bool) apply_filters('FHEE__EventEspresso_PaymentMethods_Manager__verifySSL', true);
228
+		}
229
+		return (bool) $verify_ssl;
230
+	}
231 231
 }
Please login to merge, or discard this patch.
PaymentMethods/PayPalCommerce/modules/EED_PayPalOnboard.module.php 2 patches
Indentation   +656 added lines, -656 removed lines patch added patch discarded remove patch
@@ -21,660 +21,660 @@
 block discarded – undo
21 21
  */
22 22
 class EED_PayPalOnboard extends EED_Module
23 23
 {
24
-    /**
25
-     * @return EED_Module
26
-     * @throws EE_Error
27
-     * @throws ReflectionException
28
-     */
29
-    public static function instance(): EED_Module
30
-    {
31
-        return parent::get_instance(__CLASS__);
32
-    }
33
-
34
-
35
-    /**
36
-     * Run - initial module setup.
37
-     *
38
-     * @param WP $WP
39
-     * @return void
40
-     */
41
-    public function run($WP)
42
-    {
43
-    }
44
-
45
-
46
-    /**
47
-     * For hooking into EE Admin Core and other modules.
48
-     *
49
-     * @return void
50
-     */
51
-    public static function set_hooks_admin(): void
52
-    {
53
-        if (DbStatus::isOnline()) {
54
-            // Get onboarding URL.
55
-            add_action('wp_ajax_eeaPpGetOnboardingUrl', [__CLASS__, 'getOnboardingUrl']);
56
-            // Catch the return/redirect from PayPal onboarding page.
57
-            add_action('init', [__CLASS__, 'updateOnboardingStatus'], 10);
58
-            // Return the connection/onboard status.
59
-            add_action('wp_ajax_eeaPpGetOnboardStatus', [__CLASS__, 'getOnboardStatus']);
60
-            // Revoke access.
61
-            add_action('wp_ajax_eeaPpOffboard', [__CLASS__, 'offboard']);
62
-            // Admin notice.
63
-            add_action('admin_init', [__CLASS__, 'adminNotice']);
64
-        }
65
-    }
66
-
67
-
68
-    /**
69
-     * Get the onboarding URL.
70
-     * (AJAX)
71
-     *
72
-     * @return void
73
-     */
74
-    public static function getOnboardingUrl(): void
75
-    {
76
-        $signup_link = '';
77
-        try {
78
-            $paypal_pm = EED_PayPalCommerce::getPaymentMethod();
79
-            if (! $paypal_pm instanceof EE_Payment_Method) {
80
-                PayPalLogger::errorLogAndExit(
81
-                    esc_html__('No payment method.', 'event_espresso'),
82
-                    EED_Module::getRequest()->postParams(),
83
-                    $paypal_pm
84
-                );
85
-            }
86
-            PayPalExtraMetaManager::updateDebugMode($paypal_pm, EED_Module::getRequest()->postParams());
87
-            // $signup_link   = PayPalExtraMetaManager::getPmOption($paypal_pm, Domain::META_KEY_ONBOARDING_URL);
88
-            // $token_expired = EED_PayPalOnboard::partnerAccessTokenExpired($paypal_pm);
89
-            // if (! $signup_link || $token_expired) {
90
-            $signup_link = EED_PayPalOnboard::requestOnboardingUrl($paypal_pm);
91
-            // }
92
-            if (! $signup_link) {
93
-                $err_msg = esc_html__('Error! Could not generate a sign-up link.', 'event_espresso');
94
-                PayPalLogger::errorLogAndExit($err_msg, ['signup_link' => $signup_link], $paypal_pm);
95
-            }
96
-            PayPalExtraMetaManager::savePmOption($paypal_pm, Domain::META_KEY_ONBOARDING_URL, $signup_link);
97
-        } catch (Exception $exception) {
98
-            PayPalLogger::errorLogAndExit($exception->getMessage());
99
-        }
100
-        // Is it empty (can happen if we didn't get the URL through the API).
101
-        $signup_link = $signup_link ? $signup_link . '?&displayMode=minibrowser' : '#';
102
-        wp_send_json(
103
-            [
104
-                'signup_link' => $signup_link,
105
-            ]
106
-        );
107
-    }
108
-
109
-
110
-    /**
111
-     * Request the sign-up link from PayPal.
112
-     *
113
-     * @param EE_Payment_Method $paypal_pm
114
-     * @param bool              $one_time_request
115
-     * @return string
116
-     * @throws EE_Error
117
-     * @throws Exception
118
-     */
119
-    public static function requestOnboardingUrl(EE_Payment_Method $paypal_pm, bool $one_time_request = false): string
120
-    {
121
-        $signup_link = '';
122
-        // Get the access token.
123
-        $access_token = EED_PayPalOnboard::getPartnerAccessToken($paypal_pm);
124
-        if (! $access_token) {
125
-            $err_msg = esc_html__('Error! No access token.', 'event_espresso');
126
-            PayPalLogger::errorLog($err_msg, ['access_token' => $access_token], $paypal_pm);
127
-            return '';
128
-        }
129
-        // Request the access token.
130
-        $body_params = EED_PayPalOnboard::signupLinkRequestBody($paypal_pm);
131
-        $bn_code     = PayPalExtraMetaManager::getPmOption($paypal_pm, Domain::META_KEY_BN_CODE);
132
-        $post_params = [
133
-            'method'  => 'POST',
134
-            'headers' => [
135
-                'User-Agent'                    => sanitize_text_field($_SERVER['HTTP_USER_AGENT']),
136
-                'Content-Type'                  => 'application/json',
137
-                'Authorization'                 => 'Bearer ' . $access_token,
138
-                'PayPal-Partner-Attribution-Id' => $bn_code,
139
-            ],
140
-            'body'    => $body_params,
141
-        ];
142
-        $request_url = Domain::getPayPalApiUrl($paypal_pm) . '/v2/customer/partner-referrals';
143
-        $response    = EED_PayPalOnboard::sendRequest($paypal_pm, $request_url, $post_params);
144
-        // Check the data we received.
145
-        if (isset($response['error']) || empty($response['links'])) {
146
-            // Did the original access token get replaced by any chance ?
147
-            if (! $one_time_request
148
-                && ! empty($response['message'])
149
-                && $response['message'] === 'Access Token not found in cache'
150
-            ) {
151
-                // Clear all PM metadata and try getting the access token One more time.
152
-                PayPalExtraMetaManager::deleteAllData($paypal_pm);
153
-                return EED_PayPalOnboard::requestOnboardingUrl($paypal_pm, true);
154
-            }
155
-            $err_msg = esc_html__('Incoming sign-up link parameter validation failed.', 'event_espresso');
156
-            PayPalLogger::errorLog($err_msg, $response, $paypal_pm);
157
-            return '';
158
-        }
159
-        // Now retrieve that sign-up link.
160
-        foreach ($response['links'] as $link) {
161
-            if ($link['rel'] === 'action_url') {
162
-                $signup_link = $link['href'] ?? '';
163
-            }
164
-        }
165
-        return $signup_link;
166
-    }
167
-
168
-
169
-    /**
170
-     * Get the return URL.
171
-     *
172
-     * @param EE_Payment_Method $paypal_pm
173
-     * @return string
174
-     * @throws Exception
175
-     */
176
-    public static function signupLinkRequestBody(EE_Payment_Method $paypal_pm): string
177
-    {
178
-        $identifier_string = new OneTimeString($paypal_pm->debug_mode());
179
-        $tracking_id       = $identifier_string->value();
180
-        $request           = LoaderFactory::getLoader()->getShared(RequestInterface::class);
181
-        $checkout_type     = $request->getRequestParam('checkout_type', 'EXPRESS_CHECKOUT', DataType::STRING);
182
-        // Save the identifier for future use.
183
-        PayPalExtraMetaManager::savePmOption($paypal_pm, Domain::META_KEY_TRACKING_ID, $tracking_id);
184
-        // Assemble the return URL.
185
-        $return_url = EED_PayPalOnboard::getReturnUrl($paypal_pm);
186
-        return json_encode([
187
-            'tracking_id'             => $tracking_id,
188
-            'operations'              => [
189
-                [
190
-                    'operation'                  => 'API_INTEGRATION',
191
-                    'api_integration_preference' => [
192
-                        'rest_api_integration' => [
193
-                            'integration_method'  => 'PAYPAL',
194
-                            'integration_type'    => 'THIRD_PARTY',
195
-                            'third_party_details' => [
196
-                                'features' => ['PAYMENT', 'REFUND'],
197
-                            ],
198
-                        ],
199
-                    ],
200
-                ],
201
-            ],
202
-            'products'                => [$checkout_type],
203
-            'legal_consents'          => [
204
-                [
205
-                    'type'    => 'SHARE_DATA_CONSENT',
206
-                    'granted' => true,
207
-                ],
208
-            ],
209
-            'partner_config_override' => [
210
-                'return_url' => $return_url,
211
-            ],
212
-        ]);
213
-    }
214
-
215
-
216
-    /**
217
-     * Get the return URL.
218
-     *
219
-     * @param EE_Payment_Method $paypal_pm
220
-     * @return string
221
-     * @throws EE_Error
222
-     * @throws ReflectionException
223
-     */
224
-    public static function getReturnUrl(EE_Payment_Method $paypal_pm): string
225
-    {
226
-        $wp_nonce = EED_Module::getRequest()->getRequestParam('wp_nonce');
227
-        $nonce    = wp_create_nonce(Domain::NONCE_NAME_ONBOARDING_RETURN);
228
-        return add_query_arg(
229
-            [
230
-                'page'                        => 'espresso_payment_settings',
231
-                'webhook_action'              => 'eepPpcMerchantOnboard',
232
-                'payment_method'              => $paypal_pm->slug(),
233
-                '_wpnonce'                    => $wp_nonce,
234
-                'nonce'                       => $nonce,
235
-                Domain::META_KEY_SANDBOX_MODE => $paypal_pm->debug_mode() ? '1' : '0',
236
-            ],
237
-            admin_url('admin.php')
238
-        );
239
-    }
240
-
241
-
242
-    /**
243
-     * Redirect to the payment method (PP) settings home page.
244
-     *
245
-     * @return void
246
-     */
247
-    public static function redirectToPmSettingsHome(): void
248
-    {
249
-        $get_params = EED_Module::getRequest()->getParams();
250
-        if (empty($get_params['payment_method'])) {
251
-            // Simply do not redirect.
252
-            return;
253
-        }
254
-        $args_to_add = [
255
-            'page'           => 'espresso_payment_settings',
256
-            'payment_method' => $get_params['payment_method'],
257
-        ];
258
-        if (isset($get_params['sandbox_mode'])) {
259
-            $args_to_add[ Domain::META_KEY_SANDBOX_MODE ] = $get_params['sandbox_mode'];
260
-        }
261
-        $home_url = add_query_arg($args_to_add, admin_url('admin.php'));
262
-        wp_redirect($home_url);
263
-        exit;
264
-    }
265
-
266
-
267
-    /**
268
-     * Check user’s onboarding status.
269
-     * This will handle the user return from the auth page and also check the status via the API.
270
-     *
271
-     * @return void
272
-     * @throws EE_Error
273
-     * @throws ReflectionException
274
-     */
275
-    public static function updateOnboardingStatus(): void
276
-    {
277
-        // Check if this is the webhook from PayPal.
278
-        if (! isset($_GET['webhook_action'], $_GET['nonce'])
279
-            || $_GET['webhook_action'] !== 'eepPpcMerchantOnboard'
280
-        ) {
281
-            return;  // Ignore.
282
-        }
283
-        $get_params = EED_Module::getRequest()->getParams();
284
-        // Get the payment method.
285
-        $paypal_pm = EED_PayPalCommerce::getPaymentMethod();
286
-        // Check the response (GET) parameters.
287
-        if (! EED_PayPalOnboard::onboardingStatusResponseValid($get_params, $paypal_pm)) {
288
-            // Missing parameters. Can't proceed.
289
-            PayPalLogger::errorLog(
290
-                esc_html__('Missing required onboarding parameters.', 'event_espresso'),
291
-                $get_params,
292
-                $paypal_pm
293
-            );
294
-            EED_PayPalOnboard::redirectToPmSettingsHome();
295
-            return;
296
-        }
297
-        // Check on the onboarding status (recommended by PP).
298
-        $onboarding_status = EED_PayPalOnboard::trackSellerOnboarding(
299
-            $paypal_pm,
300
-            $get_params[ Domain::META_KEY_SELLER_MERCHANT_ID ]
301
-        );
302
-        if (! isset($onboarding_status['valid']) || ! $onboarding_status['valid']) {
303
-            PayPalLogger::errorLog(
304
-                $onboarding_status['message'],
305
-                array_merge($get_params, $onboarding_status),
306
-                $paypal_pm
307
-            );
308
-            EED_PayPalOnboard::redirectToPmSettingsHome();
309
-            return;
310
-        }
311
-        // Start saving the setup and info.
312
-        PayPalExtraMetaManager::parseAndSaveOptions($paypal_pm, $onboarding_status);
313
-        // Save the credentials.
314
-        PayPalExtraMetaManager::saveSellerApiCredentials($paypal_pm, $get_params);
315
-        // If onboarded successfully, remove the onboarding URL.
316
-        PayPalExtraMetaManager::deletePmOption($paypal_pm, Domain::META_KEY_ONBOARDING_URL);
317
-        // Also clen GET params by redirecting, because PP auto redirects to the return_url on closing the onboarding window.
318
-        EED_PayPalOnboard::redirectToPmSettingsHome();
319
-    }
320
-
321
-
322
-    /**
323
-     * Check if all required parameters for the onboarding status check are present.
324
-     *
325
-     * @param array $data
326
-     * @param mixed $paypal_pm
327
-     * @return bool
328
-     */
329
-    public static function onboardingStatusResponseValid(array $data, $paypal_pm): bool
330
-    {
331
-        // Check that we have all the required parameters and the nonce is ok.
332
-        if ($paypal_pm instanceof EE_Payment_Method
333
-            && wp_verify_nonce($data['nonce'], Domain::NONCE_NAME_ONBOARDING_RETURN)
334
-            && ! empty($data[ Domain::API_PARAM_PARTNER_ID ])
335
-            && ! empty($data[ Domain::META_KEY_SELLER_MERCHANT_ID ])
336
-            && isset($data[ Domain::API_PARAM_EMAIL_CONFIRMED ])
337
-        ) {
338
-            return true;
339
-        }
340
-        return false;
341
-    }
342
-
343
-
344
-    /**
345
-     * Get partner access token.
346
-     *
347
-     * @param EE_Payment_Method $paypal_pm
348
-     * @return string
349
-     * @throws EE_Error
350
-     * @throws ReflectionException
351
-     */
352
-    public static function getPartnerAccessToken(EE_Payment_Method $paypal_pm): string
353
-    {
354
-        // Do we have it saved ?
355
-        $access_token = PayPalExtraMetaManager::getPmOption($paypal_pm, Domain::META_KEY_ACCESS_TOKEN);
356
-        $expired      = EED_PayPalOnboard::partnerAccessTokenExpired($paypal_pm);
357
-        // If we don't have it, request/update it.
358
-        if (! $access_token || $expired) {
359
-            return EED_PayPalOnboard::requestPartnerAccessToken($paypal_pm);
360
-        }
361
-        // Access token is saved as encrypted, so return decrypted.
362
-        return $access_token;
363
-    }
364
-
365
-
366
-    /**
367
-     * Get partner access token.
368
-     *
369
-     * @param EE_Payment_Method $paypal_pm
370
-     * @return bool
371
-     */
372
-    public static function partnerAccessTokenExpired(EE_Payment_Method $paypal_pm): bool
373
-    {
374
-        $expires_at = (int) PayPalExtraMetaManager::getPmOption($paypal_pm, Domain::META_KEY_TOKEN_EXPIRES_IN);
375
-        if (! $expires_at) {
376
-            return true;
377
-        }
378
-        // Validate the token expiration date.
379
-        $now          = time();
380
-        $minutes_left = round(($expires_at - $now) / 60);
381
-        // Count as expired if less than 60 minutes till expiration left.
382
-        if ($minutes_left <= 60) {
383
-            return true;
384
-        }
385
-        return false;
386
-    }
387
-
388
-
389
-    /**
390
-     * Request the partner access token from PayPal and save/update it.
391
-     *
392
-     * @param EE_Payment_Method $paypal_pm
393
-     * @return string
394
-     * @throws EE_Error
395
-     * @throws ReflectionException
396
-     */
397
-    public static function requestPartnerAccessToken(EE_Payment_Method $paypal_pm): string
398
-    {
399
-        $nonce = wp_create_nonce('eea_pp_commerce_get_access_token');
400
-        // Request the access token.
401
-        $post_args = [
402
-            'method' => 'POST',
403
-            'body'   => [
404
-                'nonce'                       => $nonce,
405
-                'api_version'                 => 'v1',
406
-                Domain::META_KEY_SANDBOX_MODE => $paypal_pm->debug_mode() ? '1' : '0',
407
-            ],
408
-        ];
409
-        if (defined('LOCAL_MIDDLEMAN_SERVER')) {
410
-            $post_args['sslverify'] = Manager::verifySSL();
411
-        }
412
-        $post_url = EED_PayPalOnboard::getMiddlemanBaseUrl($paypal_pm) . 'get_token';
413
-        $response = EED_PayPalOnboard::sendRequest($paypal_pm, $post_url, $post_args);
414
-        if (isset($response['error'])) {
415
-            return '';
416
-        }
417
-        // Check the data we received.
418
-        if (! EED_PayPalOnboard::partnerTokenResponseValid($response, $paypal_pm)) {
419
-            return '';
420
-        }
421
-        // If we are here all seems to be ok. Save the token and it's data.
422
-        $saved = PayPalExtraMetaManager::savePartnerAccessToken($paypal_pm, $response);
423
-        if (! $saved) {
424
-            return '';
425
-        }
426
-        return $response['access_token'];
427
-    }
428
-
429
-
430
-    /**
431
-     * Request seller onboarding status from PayPal.
432
-     *
433
-     * @param EE_Payment_Method $paypal_pm
434
-     * @param string            $merchant_id
435
-     * @return array
436
-     */
437
-    public static function trackSellerOnboarding(EE_Payment_Method $paypal_pm, string $merchant_id): array
438
-    {
439
-        $track_onboarding = EED_PayPalOnboard::getTrackOnboardingApi($paypal_pm, $merchant_id);
440
-        if (! $track_onboarding instanceof TrackSellerOnboarding) {
441
-            $err_msg = esc_html__('Failed to track seller onboarding.', 'event_espresso');
442
-            return ['error' => 'TRACK_ONBOARDING_FAILED', 'message' => $err_msg];
443
-        }
444
-        return $track_onboarding->isValid();
445
-    }
446
-
447
-
448
-    /**
449
-     * Returns the Track Seller Onboarding API.
450
-     *
451
-     * @param EE_Payment_Method $paypal_pm
452
-     * @param string            $merchant_id
453
-     * @return TrackSellerOnboarding|null
454
-     * @throws EE_Error
455
-     * @throws ReflectionException
456
-     */
457
-    public static function getTrackOnboardingApi(
458
-        EE_Payment_Method $paypal_pm,
459
-        string            $merchant_id
460
-    ): ?TrackSellerOnboarding {
461
-        $partner_id = PayPalExtraMetaManager::getPmOption($paypal_pm, Domain::META_KEY_PARTNER_MERCHANT_ID);
462
-        $paypal_api = EED_PayPalCommerce::getPayPalApi($paypal_pm);
463
-        if (! $paypal_api instanceof PayPalApi || ! $partner_id) {
464
-            return null;
465
-        }
466
-        return new TrackSellerOnboarding($paypal_api, $partner_id, $merchant_id, $paypal_pm->debug_mode());
467
-    }
468
-
469
-
470
-    /**
471
-     * Check the onboard status and return the result.
472
-     * (AJAX)
473
-     *
474
-     * @return void
475
-     */
476
-    public static function getOnboardStatus(): void
477
-    {
478
-        $paypal_pm = EED_PayPalCommerce::getPaymentMethod();
479
-        if (! $paypal_pm instanceof EE_Payment_Method) {
480
-            $err_msg = esc_html__('Could not specify the payment method.', 'event_espresso');
481
-            PayPalLogger::errorLog($err_msg, EED_Module::getRequest()->postParams(), $paypal_pm);
482
-            wp_send_json(['on_board' => false]);
483
-        }
484
-        try {
485
-            $seller_id = PayPalExtraMetaManager::getPmOption($paypal_pm, Domain::META_KEY_SELLER_MERCHANT_ID) ?? '--';
486
-        } catch (Exception $e) {
487
-            $seller_id = '--';
488
-        }
489
-        wp_send_json(
490
-            [
491
-                'on_board'  => EED_PayPalOnboard::isOnboard($paypal_pm),
492
-                'seller_id' => $seller_id,
493
-            ]
494
-        );
495
-    }
496
-
497
-
498
-    /**
499
-     * De-authorize the seller. Remove all API credentials.
500
-     * (AJAX)
501
-     *
502
-     * @return void
503
-     */
504
-    public static function offboard(): void
505
-    {
506
-        $paypal_pm = EED_PayPalCommerce::getPaymentMethod();
507
-        if (! $paypal_pm instanceof EE_Payment_Method) {
508
-            wp_send_json([
509
-                'error'   => 'INVALID_PM',
510
-                'message' => esc_html__(
511
-                    'Invalid payment method. Please refresh the page and try again.',
512
-                    'event_espresso'
513
-                ),
514
-            ]);
515
-        }
516
-        PayPalExtraMetaManager::deleteAllData($paypal_pm);
517
-        wp_send_json(['success' => true]);
518
-    }
519
-
520
-
521
-    /**
522
-     * Checks if already onboard.
523
-     *
524
-     * @param EE_Payment_Method $payment_method
525
-     * @return boolean
526
-     */
527
-    public static function isOnboard(EE_Payment_Method $payment_method): bool
528
-    {
529
-        $pp_meta_data = PayPalExtraMetaManager::getAllData($payment_method);
530
-        return
531
-            // onborded with a third party integration ?
532
-            (! empty($pp_meta_data[ Domain::META_KEY_SELLER_MERCHANT_ID ])
533
-                && ! empty($pp_meta_data[ Domain::META_KEY_ACCESS_TOKEN ])
534
-            )
535
-            // or with the first party integration ?
536
-            || (! empty($pp_meta_data[ Domain::META_KEY_CLIENT_ID ])
537
-                && ! empty($pp_meta_data[ Domain::META_KEY_CLIENT_SECRET ])
538
-                && ! empty($pp_meta_data[ Domain::META_KEY_PAYER_ID ])
539
-            );
540
-    }
541
-
542
-
543
-    /**
544
-     * Send a request and return a decoded response body.
545
-     *
546
-     * @param EE_Payment_Method $paypal_pm
547
-     * @param string            $request_url
548
-     * @param array             $request_args
549
-     * @return array
550
-     */
551
-    public static function sendRequest(EE_Payment_Method $paypal_pm, string $request_url, array $request_args): array
552
-    {
553
-        $error_return = ['error' => true];
554
-        $response     = wp_remote_request($request_url, $request_args);
555
-        if (is_wp_error($response)) {
556
-            $message = $response->get_error_message();
557
-            PayPalLogger::errorLog($message, [$request_url, $request_args, $response], $paypal_pm);
558
-            $error_return['message'] = $message;
559
-            return $error_return;
560
-        }
561
-        $response_body = (isset($response['body']) && $response['body']) ? json_decode($response['body'], true) : [];
562
-        if (empty($response_body) || isset($response_body['error'])) {
563
-            $message = $response_body['error_description']
564
-                       ?? sprintf(
565
-                           esc_html__('Unknown response received while sending a request to: %1$s', 'event_espresso'),
566
-                           $request_url
567
-                       );
568
-            PayPalLogger::errorLog($message, [$request_url, $request_args, $response], $paypal_pm);
569
-            $error_return['message'] = $message;
570
-            return $error_return;
571
-        }
572
-        return $response_body;
573
-    }
574
-
575
-
576
-    /**
577
-     * Check the response for a partner token request.
578
-     *
579
-     * @param                   $response
580
-     * @param EE_Payment_Method $paypal_pm
581
-     * @return bool
582
-     */
583
-    public static function partnerTokenResponseValid($response, EE_Payment_Method $paypal_pm): bool
584
-    {
585
-        // Check the data we received.
586
-        if (
587
-            empty($response['nonce'])
588
-            || ! wp_verify_nonce($response['nonce'], 'eea_pp_commerce_get_access_token')
589
-            || empty($response['access_token'])
590
-            || empty($response['app_id'])
591
-            || empty($response['expires_in'])
592
-            || empty($response['partner_client_id'])
593
-            || empty($response['partner_merchant_id'])
594
-        ) {
595
-            // This is an error.
596
-            $err_msg = esc_html__('Incoming parameter validation failed.', 'event_espresso');
597
-            PayPalLogger::errorLog($err_msg, (array) $response, $paypal_pm);
598
-            return false;
599
-        }
600
-        return true;
601
-    }
602
-
603
-
604
-    /**
605
-     * Returns the base URL to the middleman server.
606
-     * If LOCAL_MIDDLEMAN_SERVER is defined, requests will be sent to connect.eventespresso.test
607
-     *
608
-     * @param EE_Payment_Method $payment_method
609
-     * @return string
610
-     * @throws EE_Error
611
-     * @throws ReflectionException
612
-     */
613
-    public static function getMiddlemanBaseUrl(EE_Payment_Method $payment_method): string
614
-    {
615
-        $target = defined('LOCAL_MIDDLEMAN_SERVER') ? 'test' : 'com';
616
-        // If this PM is used under different provider accounts, you might need an account indicator.
617
-        $account = defined('EE_PAYPAL_COMMERCE_ACCOUNT_INDICATOR') ? EE_PAYPAL_COMMERCE_ACCOUNT_INDICATOR : '';
618
-        $postfix = $payment_method->debug_mode() ? '_sandbox' : '';
619
-        $path    = 'paypal_commerce' . $account . $postfix;
620
-        return 'https://connect.eventespresso.' . $target . '/' . $path . '/';
621
-    }
622
-
623
-
624
-    /**
625
-     * This Payment Method admin notices.
626
-     *
627
-     * @return void
628
-     * @throws EE_Error
629
-     * @throws ReflectionException
630
-     */
631
-    public static function adminNotice()
632
-    {
633
-        // Show the notice if PayPal Commerce PM is active but merchant is not onboard.
634
-        $pp_commerce = EEM_Payment_Method::instance()->get_one_by_slug('paypalcheckout');
635
-        if ($pp_commerce instanceof EE_Payment_Method
636
-            && $pp_commerce->active()
637
-            && ! EED_PayPalOnboard::isOnboard($pp_commerce)
638
-        ) {
639
-            add_action('admin_notices', [__CLASS__, 'notOnboardNotice']);
640
-        }
641
-    }
642
-
643
-
644
-    /**
645
-     * Contents of the not onboard admin notice.
646
-     *
647
-     * @return void
648
-     * @throws EE_Error
649
-     * @throws ReflectionException
650
-     */
651
-    public static function notOnboardNotice()
652
-    {
653
-        $open_anchor = $close_anchor = '';
654
-        $pp_commerce = EEM_Payment_Method::instance()->get_one_by_slug('paypalcheckout');
655
-        if ($pp_commerce instanceof EE_Payment_Method) {
656
-            $pm_page = add_query_arg(
657
-                [
658
-                    'page'           => 'espresso_payment_settings',
659
-                    'webhook_action' => 'eepPpcMerchantOnboard',
660
-                    'payment_method' => $pp_commerce->slug(),
661
-                ],
662
-                admin_url('admin.php')
663
-            );
664
-            $open_anchor  = "<a href='$pm_page'>";
665
-            $close_anchor = "</a>";
666
-        }
667
-        echo '<div class="error"><p>'
668
-        . sprintf(
669
-            esc_html__(
670
-                '%1$sPayPal Commerce%2$s payment method was activated but is not connected to PayPal. Please %3$sfinish setting up%4$s this payment method.',
671
-                'event_espresso'
672
-            ),
673
-            '<strong>',
674
-            '</strong>',
675
-            $open_anchor,
676
-            $close_anchor
677
-        )
678
-        . '</p></div>';
679
-    }
24
+	/**
25
+	 * @return EED_Module
26
+	 * @throws EE_Error
27
+	 * @throws ReflectionException
28
+	 */
29
+	public static function instance(): EED_Module
30
+	{
31
+		return parent::get_instance(__CLASS__);
32
+	}
33
+
34
+
35
+	/**
36
+	 * Run - initial module setup.
37
+	 *
38
+	 * @param WP $WP
39
+	 * @return void
40
+	 */
41
+	public function run($WP)
42
+	{
43
+	}
44
+
45
+
46
+	/**
47
+	 * For hooking into EE Admin Core and other modules.
48
+	 *
49
+	 * @return void
50
+	 */
51
+	public static function set_hooks_admin(): void
52
+	{
53
+		if (DbStatus::isOnline()) {
54
+			// Get onboarding URL.
55
+			add_action('wp_ajax_eeaPpGetOnboardingUrl', [__CLASS__, 'getOnboardingUrl']);
56
+			// Catch the return/redirect from PayPal onboarding page.
57
+			add_action('init', [__CLASS__, 'updateOnboardingStatus'], 10);
58
+			// Return the connection/onboard status.
59
+			add_action('wp_ajax_eeaPpGetOnboardStatus', [__CLASS__, 'getOnboardStatus']);
60
+			// Revoke access.
61
+			add_action('wp_ajax_eeaPpOffboard', [__CLASS__, 'offboard']);
62
+			// Admin notice.
63
+			add_action('admin_init', [__CLASS__, 'adminNotice']);
64
+		}
65
+	}
66
+
67
+
68
+	/**
69
+	 * Get the onboarding URL.
70
+	 * (AJAX)
71
+	 *
72
+	 * @return void
73
+	 */
74
+	public static function getOnboardingUrl(): void
75
+	{
76
+		$signup_link = '';
77
+		try {
78
+			$paypal_pm = EED_PayPalCommerce::getPaymentMethod();
79
+			if (! $paypal_pm instanceof EE_Payment_Method) {
80
+				PayPalLogger::errorLogAndExit(
81
+					esc_html__('No payment method.', 'event_espresso'),
82
+					EED_Module::getRequest()->postParams(),
83
+					$paypal_pm
84
+				);
85
+			}
86
+			PayPalExtraMetaManager::updateDebugMode($paypal_pm, EED_Module::getRequest()->postParams());
87
+			// $signup_link   = PayPalExtraMetaManager::getPmOption($paypal_pm, Domain::META_KEY_ONBOARDING_URL);
88
+			// $token_expired = EED_PayPalOnboard::partnerAccessTokenExpired($paypal_pm);
89
+			// if (! $signup_link || $token_expired) {
90
+			$signup_link = EED_PayPalOnboard::requestOnboardingUrl($paypal_pm);
91
+			// }
92
+			if (! $signup_link) {
93
+				$err_msg = esc_html__('Error! Could not generate a sign-up link.', 'event_espresso');
94
+				PayPalLogger::errorLogAndExit($err_msg, ['signup_link' => $signup_link], $paypal_pm);
95
+			}
96
+			PayPalExtraMetaManager::savePmOption($paypal_pm, Domain::META_KEY_ONBOARDING_URL, $signup_link);
97
+		} catch (Exception $exception) {
98
+			PayPalLogger::errorLogAndExit($exception->getMessage());
99
+		}
100
+		// Is it empty (can happen if we didn't get the URL through the API).
101
+		$signup_link = $signup_link ? $signup_link . '?&displayMode=minibrowser' : '#';
102
+		wp_send_json(
103
+			[
104
+				'signup_link' => $signup_link,
105
+			]
106
+		);
107
+	}
108
+
109
+
110
+	/**
111
+	 * Request the sign-up link from PayPal.
112
+	 *
113
+	 * @param EE_Payment_Method $paypal_pm
114
+	 * @param bool              $one_time_request
115
+	 * @return string
116
+	 * @throws EE_Error
117
+	 * @throws Exception
118
+	 */
119
+	public static function requestOnboardingUrl(EE_Payment_Method $paypal_pm, bool $one_time_request = false): string
120
+	{
121
+		$signup_link = '';
122
+		// Get the access token.
123
+		$access_token = EED_PayPalOnboard::getPartnerAccessToken($paypal_pm);
124
+		if (! $access_token) {
125
+			$err_msg = esc_html__('Error! No access token.', 'event_espresso');
126
+			PayPalLogger::errorLog($err_msg, ['access_token' => $access_token], $paypal_pm);
127
+			return '';
128
+		}
129
+		// Request the access token.
130
+		$body_params = EED_PayPalOnboard::signupLinkRequestBody($paypal_pm);
131
+		$bn_code     = PayPalExtraMetaManager::getPmOption($paypal_pm, Domain::META_KEY_BN_CODE);
132
+		$post_params = [
133
+			'method'  => 'POST',
134
+			'headers' => [
135
+				'User-Agent'                    => sanitize_text_field($_SERVER['HTTP_USER_AGENT']),
136
+				'Content-Type'                  => 'application/json',
137
+				'Authorization'                 => 'Bearer ' . $access_token,
138
+				'PayPal-Partner-Attribution-Id' => $bn_code,
139
+			],
140
+			'body'    => $body_params,
141
+		];
142
+		$request_url = Domain::getPayPalApiUrl($paypal_pm) . '/v2/customer/partner-referrals';
143
+		$response    = EED_PayPalOnboard::sendRequest($paypal_pm, $request_url, $post_params);
144
+		// Check the data we received.
145
+		if (isset($response['error']) || empty($response['links'])) {
146
+			// Did the original access token get replaced by any chance ?
147
+			if (! $one_time_request
148
+				&& ! empty($response['message'])
149
+				&& $response['message'] === 'Access Token not found in cache'
150
+			) {
151
+				// Clear all PM metadata and try getting the access token One more time.
152
+				PayPalExtraMetaManager::deleteAllData($paypal_pm);
153
+				return EED_PayPalOnboard::requestOnboardingUrl($paypal_pm, true);
154
+			}
155
+			$err_msg = esc_html__('Incoming sign-up link parameter validation failed.', 'event_espresso');
156
+			PayPalLogger::errorLog($err_msg, $response, $paypal_pm);
157
+			return '';
158
+		}
159
+		// Now retrieve that sign-up link.
160
+		foreach ($response['links'] as $link) {
161
+			if ($link['rel'] === 'action_url') {
162
+				$signup_link = $link['href'] ?? '';
163
+			}
164
+		}
165
+		return $signup_link;
166
+	}
167
+
168
+
169
+	/**
170
+	 * Get the return URL.
171
+	 *
172
+	 * @param EE_Payment_Method $paypal_pm
173
+	 * @return string
174
+	 * @throws Exception
175
+	 */
176
+	public static function signupLinkRequestBody(EE_Payment_Method $paypal_pm): string
177
+	{
178
+		$identifier_string = new OneTimeString($paypal_pm->debug_mode());
179
+		$tracking_id       = $identifier_string->value();
180
+		$request           = LoaderFactory::getLoader()->getShared(RequestInterface::class);
181
+		$checkout_type     = $request->getRequestParam('checkout_type', 'EXPRESS_CHECKOUT', DataType::STRING);
182
+		// Save the identifier for future use.
183
+		PayPalExtraMetaManager::savePmOption($paypal_pm, Domain::META_KEY_TRACKING_ID, $tracking_id);
184
+		// Assemble the return URL.
185
+		$return_url = EED_PayPalOnboard::getReturnUrl($paypal_pm);
186
+		return json_encode([
187
+			'tracking_id'             => $tracking_id,
188
+			'operations'              => [
189
+				[
190
+					'operation'                  => 'API_INTEGRATION',
191
+					'api_integration_preference' => [
192
+						'rest_api_integration' => [
193
+							'integration_method'  => 'PAYPAL',
194
+							'integration_type'    => 'THIRD_PARTY',
195
+							'third_party_details' => [
196
+								'features' => ['PAYMENT', 'REFUND'],
197
+							],
198
+						],
199
+					],
200
+				],
201
+			],
202
+			'products'                => [$checkout_type],
203
+			'legal_consents'          => [
204
+				[
205
+					'type'    => 'SHARE_DATA_CONSENT',
206
+					'granted' => true,
207
+				],
208
+			],
209
+			'partner_config_override' => [
210
+				'return_url' => $return_url,
211
+			],
212
+		]);
213
+	}
214
+
215
+
216
+	/**
217
+	 * Get the return URL.
218
+	 *
219
+	 * @param EE_Payment_Method $paypal_pm
220
+	 * @return string
221
+	 * @throws EE_Error
222
+	 * @throws ReflectionException
223
+	 */
224
+	public static function getReturnUrl(EE_Payment_Method $paypal_pm): string
225
+	{
226
+		$wp_nonce = EED_Module::getRequest()->getRequestParam('wp_nonce');
227
+		$nonce    = wp_create_nonce(Domain::NONCE_NAME_ONBOARDING_RETURN);
228
+		return add_query_arg(
229
+			[
230
+				'page'                        => 'espresso_payment_settings',
231
+				'webhook_action'              => 'eepPpcMerchantOnboard',
232
+				'payment_method'              => $paypal_pm->slug(),
233
+				'_wpnonce'                    => $wp_nonce,
234
+				'nonce'                       => $nonce,
235
+				Domain::META_KEY_SANDBOX_MODE => $paypal_pm->debug_mode() ? '1' : '0',
236
+			],
237
+			admin_url('admin.php')
238
+		);
239
+	}
240
+
241
+
242
+	/**
243
+	 * Redirect to the payment method (PP) settings home page.
244
+	 *
245
+	 * @return void
246
+	 */
247
+	public static function redirectToPmSettingsHome(): void
248
+	{
249
+		$get_params = EED_Module::getRequest()->getParams();
250
+		if (empty($get_params['payment_method'])) {
251
+			// Simply do not redirect.
252
+			return;
253
+		}
254
+		$args_to_add = [
255
+			'page'           => 'espresso_payment_settings',
256
+			'payment_method' => $get_params['payment_method'],
257
+		];
258
+		if (isset($get_params['sandbox_mode'])) {
259
+			$args_to_add[ Domain::META_KEY_SANDBOX_MODE ] = $get_params['sandbox_mode'];
260
+		}
261
+		$home_url = add_query_arg($args_to_add, admin_url('admin.php'));
262
+		wp_redirect($home_url);
263
+		exit;
264
+	}
265
+
266
+
267
+	/**
268
+	 * Check user’s onboarding status.
269
+	 * This will handle the user return from the auth page and also check the status via the API.
270
+	 *
271
+	 * @return void
272
+	 * @throws EE_Error
273
+	 * @throws ReflectionException
274
+	 */
275
+	public static function updateOnboardingStatus(): void
276
+	{
277
+		// Check if this is the webhook from PayPal.
278
+		if (! isset($_GET['webhook_action'], $_GET['nonce'])
279
+			|| $_GET['webhook_action'] !== 'eepPpcMerchantOnboard'
280
+		) {
281
+			return;  // Ignore.
282
+		}
283
+		$get_params = EED_Module::getRequest()->getParams();
284
+		// Get the payment method.
285
+		$paypal_pm = EED_PayPalCommerce::getPaymentMethod();
286
+		// Check the response (GET) parameters.
287
+		if (! EED_PayPalOnboard::onboardingStatusResponseValid($get_params, $paypal_pm)) {
288
+			// Missing parameters. Can't proceed.
289
+			PayPalLogger::errorLog(
290
+				esc_html__('Missing required onboarding parameters.', 'event_espresso'),
291
+				$get_params,
292
+				$paypal_pm
293
+			);
294
+			EED_PayPalOnboard::redirectToPmSettingsHome();
295
+			return;
296
+		}
297
+		// Check on the onboarding status (recommended by PP).
298
+		$onboarding_status = EED_PayPalOnboard::trackSellerOnboarding(
299
+			$paypal_pm,
300
+			$get_params[ Domain::META_KEY_SELLER_MERCHANT_ID ]
301
+		);
302
+		if (! isset($onboarding_status['valid']) || ! $onboarding_status['valid']) {
303
+			PayPalLogger::errorLog(
304
+				$onboarding_status['message'],
305
+				array_merge($get_params, $onboarding_status),
306
+				$paypal_pm
307
+			);
308
+			EED_PayPalOnboard::redirectToPmSettingsHome();
309
+			return;
310
+		}
311
+		// Start saving the setup and info.
312
+		PayPalExtraMetaManager::parseAndSaveOptions($paypal_pm, $onboarding_status);
313
+		// Save the credentials.
314
+		PayPalExtraMetaManager::saveSellerApiCredentials($paypal_pm, $get_params);
315
+		// If onboarded successfully, remove the onboarding URL.
316
+		PayPalExtraMetaManager::deletePmOption($paypal_pm, Domain::META_KEY_ONBOARDING_URL);
317
+		// Also clen GET params by redirecting, because PP auto redirects to the return_url on closing the onboarding window.
318
+		EED_PayPalOnboard::redirectToPmSettingsHome();
319
+	}
320
+
321
+
322
+	/**
323
+	 * Check if all required parameters for the onboarding status check are present.
324
+	 *
325
+	 * @param array $data
326
+	 * @param mixed $paypal_pm
327
+	 * @return bool
328
+	 */
329
+	public static function onboardingStatusResponseValid(array $data, $paypal_pm): bool
330
+	{
331
+		// Check that we have all the required parameters and the nonce is ok.
332
+		if ($paypal_pm instanceof EE_Payment_Method
333
+			&& wp_verify_nonce($data['nonce'], Domain::NONCE_NAME_ONBOARDING_RETURN)
334
+			&& ! empty($data[ Domain::API_PARAM_PARTNER_ID ])
335
+			&& ! empty($data[ Domain::META_KEY_SELLER_MERCHANT_ID ])
336
+			&& isset($data[ Domain::API_PARAM_EMAIL_CONFIRMED ])
337
+		) {
338
+			return true;
339
+		}
340
+		return false;
341
+	}
342
+
343
+
344
+	/**
345
+	 * Get partner access token.
346
+	 *
347
+	 * @param EE_Payment_Method $paypal_pm
348
+	 * @return string
349
+	 * @throws EE_Error
350
+	 * @throws ReflectionException
351
+	 */
352
+	public static function getPartnerAccessToken(EE_Payment_Method $paypal_pm): string
353
+	{
354
+		// Do we have it saved ?
355
+		$access_token = PayPalExtraMetaManager::getPmOption($paypal_pm, Domain::META_KEY_ACCESS_TOKEN);
356
+		$expired      = EED_PayPalOnboard::partnerAccessTokenExpired($paypal_pm);
357
+		// If we don't have it, request/update it.
358
+		if (! $access_token || $expired) {
359
+			return EED_PayPalOnboard::requestPartnerAccessToken($paypal_pm);
360
+		}
361
+		// Access token is saved as encrypted, so return decrypted.
362
+		return $access_token;
363
+	}
364
+
365
+
366
+	/**
367
+	 * Get partner access token.
368
+	 *
369
+	 * @param EE_Payment_Method $paypal_pm
370
+	 * @return bool
371
+	 */
372
+	public static function partnerAccessTokenExpired(EE_Payment_Method $paypal_pm): bool
373
+	{
374
+		$expires_at = (int) PayPalExtraMetaManager::getPmOption($paypal_pm, Domain::META_KEY_TOKEN_EXPIRES_IN);
375
+		if (! $expires_at) {
376
+			return true;
377
+		}
378
+		// Validate the token expiration date.
379
+		$now          = time();
380
+		$minutes_left = round(($expires_at - $now) / 60);
381
+		// Count as expired if less than 60 minutes till expiration left.
382
+		if ($minutes_left <= 60) {
383
+			return true;
384
+		}
385
+		return false;
386
+	}
387
+
388
+
389
+	/**
390
+	 * Request the partner access token from PayPal and save/update it.
391
+	 *
392
+	 * @param EE_Payment_Method $paypal_pm
393
+	 * @return string
394
+	 * @throws EE_Error
395
+	 * @throws ReflectionException
396
+	 */
397
+	public static function requestPartnerAccessToken(EE_Payment_Method $paypal_pm): string
398
+	{
399
+		$nonce = wp_create_nonce('eea_pp_commerce_get_access_token');
400
+		// Request the access token.
401
+		$post_args = [
402
+			'method' => 'POST',
403
+			'body'   => [
404
+				'nonce'                       => $nonce,
405
+				'api_version'                 => 'v1',
406
+				Domain::META_KEY_SANDBOX_MODE => $paypal_pm->debug_mode() ? '1' : '0',
407
+			],
408
+		];
409
+		if (defined('LOCAL_MIDDLEMAN_SERVER')) {
410
+			$post_args['sslverify'] = Manager::verifySSL();
411
+		}
412
+		$post_url = EED_PayPalOnboard::getMiddlemanBaseUrl($paypal_pm) . 'get_token';
413
+		$response = EED_PayPalOnboard::sendRequest($paypal_pm, $post_url, $post_args);
414
+		if (isset($response['error'])) {
415
+			return '';
416
+		}
417
+		// Check the data we received.
418
+		if (! EED_PayPalOnboard::partnerTokenResponseValid($response, $paypal_pm)) {
419
+			return '';
420
+		}
421
+		// If we are here all seems to be ok. Save the token and it's data.
422
+		$saved = PayPalExtraMetaManager::savePartnerAccessToken($paypal_pm, $response);
423
+		if (! $saved) {
424
+			return '';
425
+		}
426
+		return $response['access_token'];
427
+	}
428
+
429
+
430
+	/**
431
+	 * Request seller onboarding status from PayPal.
432
+	 *
433
+	 * @param EE_Payment_Method $paypal_pm
434
+	 * @param string            $merchant_id
435
+	 * @return array
436
+	 */
437
+	public static function trackSellerOnboarding(EE_Payment_Method $paypal_pm, string $merchant_id): array
438
+	{
439
+		$track_onboarding = EED_PayPalOnboard::getTrackOnboardingApi($paypal_pm, $merchant_id);
440
+		if (! $track_onboarding instanceof TrackSellerOnboarding) {
441
+			$err_msg = esc_html__('Failed to track seller onboarding.', 'event_espresso');
442
+			return ['error' => 'TRACK_ONBOARDING_FAILED', 'message' => $err_msg];
443
+		}
444
+		return $track_onboarding->isValid();
445
+	}
446
+
447
+
448
+	/**
449
+	 * Returns the Track Seller Onboarding API.
450
+	 *
451
+	 * @param EE_Payment_Method $paypal_pm
452
+	 * @param string            $merchant_id
453
+	 * @return TrackSellerOnboarding|null
454
+	 * @throws EE_Error
455
+	 * @throws ReflectionException
456
+	 */
457
+	public static function getTrackOnboardingApi(
458
+		EE_Payment_Method $paypal_pm,
459
+		string            $merchant_id
460
+	): ?TrackSellerOnboarding {
461
+		$partner_id = PayPalExtraMetaManager::getPmOption($paypal_pm, Domain::META_KEY_PARTNER_MERCHANT_ID);
462
+		$paypal_api = EED_PayPalCommerce::getPayPalApi($paypal_pm);
463
+		if (! $paypal_api instanceof PayPalApi || ! $partner_id) {
464
+			return null;
465
+		}
466
+		return new TrackSellerOnboarding($paypal_api, $partner_id, $merchant_id, $paypal_pm->debug_mode());
467
+	}
468
+
469
+
470
+	/**
471
+	 * Check the onboard status and return the result.
472
+	 * (AJAX)
473
+	 *
474
+	 * @return void
475
+	 */
476
+	public static function getOnboardStatus(): void
477
+	{
478
+		$paypal_pm = EED_PayPalCommerce::getPaymentMethod();
479
+		if (! $paypal_pm instanceof EE_Payment_Method) {
480
+			$err_msg = esc_html__('Could not specify the payment method.', 'event_espresso');
481
+			PayPalLogger::errorLog($err_msg, EED_Module::getRequest()->postParams(), $paypal_pm);
482
+			wp_send_json(['on_board' => false]);
483
+		}
484
+		try {
485
+			$seller_id = PayPalExtraMetaManager::getPmOption($paypal_pm, Domain::META_KEY_SELLER_MERCHANT_ID) ?? '--';
486
+		} catch (Exception $e) {
487
+			$seller_id = '--';
488
+		}
489
+		wp_send_json(
490
+			[
491
+				'on_board'  => EED_PayPalOnboard::isOnboard($paypal_pm),
492
+				'seller_id' => $seller_id,
493
+			]
494
+		);
495
+	}
496
+
497
+
498
+	/**
499
+	 * De-authorize the seller. Remove all API credentials.
500
+	 * (AJAX)
501
+	 *
502
+	 * @return void
503
+	 */
504
+	public static function offboard(): void
505
+	{
506
+		$paypal_pm = EED_PayPalCommerce::getPaymentMethod();
507
+		if (! $paypal_pm instanceof EE_Payment_Method) {
508
+			wp_send_json([
509
+				'error'   => 'INVALID_PM',
510
+				'message' => esc_html__(
511
+					'Invalid payment method. Please refresh the page and try again.',
512
+					'event_espresso'
513
+				),
514
+			]);
515
+		}
516
+		PayPalExtraMetaManager::deleteAllData($paypal_pm);
517
+		wp_send_json(['success' => true]);
518
+	}
519
+
520
+
521
+	/**
522
+	 * Checks if already onboard.
523
+	 *
524
+	 * @param EE_Payment_Method $payment_method
525
+	 * @return boolean
526
+	 */
527
+	public static function isOnboard(EE_Payment_Method $payment_method): bool
528
+	{
529
+		$pp_meta_data = PayPalExtraMetaManager::getAllData($payment_method);
530
+		return
531
+			// onborded with a third party integration ?
532
+			(! empty($pp_meta_data[ Domain::META_KEY_SELLER_MERCHANT_ID ])
533
+				&& ! empty($pp_meta_data[ Domain::META_KEY_ACCESS_TOKEN ])
534
+			)
535
+			// or with the first party integration ?
536
+			|| (! empty($pp_meta_data[ Domain::META_KEY_CLIENT_ID ])
537
+				&& ! empty($pp_meta_data[ Domain::META_KEY_CLIENT_SECRET ])
538
+				&& ! empty($pp_meta_data[ Domain::META_KEY_PAYER_ID ])
539
+			);
540
+	}
541
+
542
+
543
+	/**
544
+	 * Send a request and return a decoded response body.
545
+	 *
546
+	 * @param EE_Payment_Method $paypal_pm
547
+	 * @param string            $request_url
548
+	 * @param array             $request_args
549
+	 * @return array
550
+	 */
551
+	public static function sendRequest(EE_Payment_Method $paypal_pm, string $request_url, array $request_args): array
552
+	{
553
+		$error_return = ['error' => true];
554
+		$response     = wp_remote_request($request_url, $request_args);
555
+		if (is_wp_error($response)) {
556
+			$message = $response->get_error_message();
557
+			PayPalLogger::errorLog($message, [$request_url, $request_args, $response], $paypal_pm);
558
+			$error_return['message'] = $message;
559
+			return $error_return;
560
+		}
561
+		$response_body = (isset($response['body']) && $response['body']) ? json_decode($response['body'], true) : [];
562
+		if (empty($response_body) || isset($response_body['error'])) {
563
+			$message = $response_body['error_description']
564
+					   ?? sprintf(
565
+						   esc_html__('Unknown response received while sending a request to: %1$s', 'event_espresso'),
566
+						   $request_url
567
+					   );
568
+			PayPalLogger::errorLog($message, [$request_url, $request_args, $response], $paypal_pm);
569
+			$error_return['message'] = $message;
570
+			return $error_return;
571
+		}
572
+		return $response_body;
573
+	}
574
+
575
+
576
+	/**
577
+	 * Check the response for a partner token request.
578
+	 *
579
+	 * @param                   $response
580
+	 * @param EE_Payment_Method $paypal_pm
581
+	 * @return bool
582
+	 */
583
+	public static function partnerTokenResponseValid($response, EE_Payment_Method $paypal_pm): bool
584
+	{
585
+		// Check the data we received.
586
+		if (
587
+			empty($response['nonce'])
588
+			|| ! wp_verify_nonce($response['nonce'], 'eea_pp_commerce_get_access_token')
589
+			|| empty($response['access_token'])
590
+			|| empty($response['app_id'])
591
+			|| empty($response['expires_in'])
592
+			|| empty($response['partner_client_id'])
593
+			|| empty($response['partner_merchant_id'])
594
+		) {
595
+			// This is an error.
596
+			$err_msg = esc_html__('Incoming parameter validation failed.', 'event_espresso');
597
+			PayPalLogger::errorLog($err_msg, (array) $response, $paypal_pm);
598
+			return false;
599
+		}
600
+		return true;
601
+	}
602
+
603
+
604
+	/**
605
+	 * Returns the base URL to the middleman server.
606
+	 * If LOCAL_MIDDLEMAN_SERVER is defined, requests will be sent to connect.eventespresso.test
607
+	 *
608
+	 * @param EE_Payment_Method $payment_method
609
+	 * @return string
610
+	 * @throws EE_Error
611
+	 * @throws ReflectionException
612
+	 */
613
+	public static function getMiddlemanBaseUrl(EE_Payment_Method $payment_method): string
614
+	{
615
+		$target = defined('LOCAL_MIDDLEMAN_SERVER') ? 'test' : 'com';
616
+		// If this PM is used under different provider accounts, you might need an account indicator.
617
+		$account = defined('EE_PAYPAL_COMMERCE_ACCOUNT_INDICATOR') ? EE_PAYPAL_COMMERCE_ACCOUNT_INDICATOR : '';
618
+		$postfix = $payment_method->debug_mode() ? '_sandbox' : '';
619
+		$path    = 'paypal_commerce' . $account . $postfix;
620
+		return 'https://connect.eventespresso.' . $target . '/' . $path . '/';
621
+	}
622
+
623
+
624
+	/**
625
+	 * This Payment Method admin notices.
626
+	 *
627
+	 * @return void
628
+	 * @throws EE_Error
629
+	 * @throws ReflectionException
630
+	 */
631
+	public static function adminNotice()
632
+	{
633
+		// Show the notice if PayPal Commerce PM is active but merchant is not onboard.
634
+		$pp_commerce = EEM_Payment_Method::instance()->get_one_by_slug('paypalcheckout');
635
+		if ($pp_commerce instanceof EE_Payment_Method
636
+			&& $pp_commerce->active()
637
+			&& ! EED_PayPalOnboard::isOnboard($pp_commerce)
638
+		) {
639
+			add_action('admin_notices', [__CLASS__, 'notOnboardNotice']);
640
+		}
641
+	}
642
+
643
+
644
+	/**
645
+	 * Contents of the not onboard admin notice.
646
+	 *
647
+	 * @return void
648
+	 * @throws EE_Error
649
+	 * @throws ReflectionException
650
+	 */
651
+	public static function notOnboardNotice()
652
+	{
653
+		$open_anchor = $close_anchor = '';
654
+		$pp_commerce = EEM_Payment_Method::instance()->get_one_by_slug('paypalcheckout');
655
+		if ($pp_commerce instanceof EE_Payment_Method) {
656
+			$pm_page = add_query_arg(
657
+				[
658
+					'page'           => 'espresso_payment_settings',
659
+					'webhook_action' => 'eepPpcMerchantOnboard',
660
+					'payment_method' => $pp_commerce->slug(),
661
+				],
662
+				admin_url('admin.php')
663
+			);
664
+			$open_anchor  = "<a href='$pm_page'>";
665
+			$close_anchor = "</a>";
666
+		}
667
+		echo '<div class="error"><p>'
668
+		. sprintf(
669
+			esc_html__(
670
+				'%1$sPayPal Commerce%2$s payment method was activated but is not connected to PayPal. Please %3$sfinish setting up%4$s this payment method.',
671
+				'event_espresso'
672
+			),
673
+			'<strong>',
674
+			'</strong>',
675
+			$open_anchor,
676
+			$close_anchor
677
+		)
678
+		. '</p></div>';
679
+	}
680 680
 }
Please login to merge, or discard this patch.
Spacing   +32 added lines, -32 removed lines patch added patch discarded remove patch
@@ -76,7 +76,7 @@  discard block
 block discarded – undo
76 76
         $signup_link = '';
77 77
         try {
78 78
             $paypal_pm = EED_PayPalCommerce::getPaymentMethod();
79
-            if (! $paypal_pm instanceof EE_Payment_Method) {
79
+            if ( ! $paypal_pm instanceof EE_Payment_Method) {
80 80
                 PayPalLogger::errorLogAndExit(
81 81
                     esc_html__('No payment method.', 'event_espresso'),
82 82
                     EED_Module::getRequest()->postParams(),
@@ -89,7 +89,7 @@  discard block
 block discarded – undo
89 89
             // if (! $signup_link || $token_expired) {
90 90
             $signup_link = EED_PayPalOnboard::requestOnboardingUrl($paypal_pm);
91 91
             // }
92
-            if (! $signup_link) {
92
+            if ( ! $signup_link) {
93 93
                 $err_msg = esc_html__('Error! Could not generate a sign-up link.', 'event_espresso');
94 94
                 PayPalLogger::errorLogAndExit($err_msg, ['signup_link' => $signup_link], $paypal_pm);
95 95
             }
@@ -98,7 +98,7 @@  discard block
 block discarded – undo
98 98
             PayPalLogger::errorLogAndExit($exception->getMessage());
99 99
         }
100 100
         // Is it empty (can happen if we didn't get the URL through the API).
101
-        $signup_link = $signup_link ? $signup_link . '?&displayMode=minibrowser' : '#';
101
+        $signup_link = $signup_link ? $signup_link.'?&displayMode=minibrowser' : '#';
102 102
         wp_send_json(
103 103
             [
104 104
                 'signup_link' => $signup_link,
@@ -121,7 +121,7 @@  discard block
 block discarded – undo
121 121
         $signup_link = '';
122 122
         // Get the access token.
123 123
         $access_token = EED_PayPalOnboard::getPartnerAccessToken($paypal_pm);
124
-        if (! $access_token) {
124
+        if ( ! $access_token) {
125 125
             $err_msg = esc_html__('Error! No access token.', 'event_espresso');
126 126
             PayPalLogger::errorLog($err_msg, ['access_token' => $access_token], $paypal_pm);
127 127
             return '';
@@ -134,17 +134,17 @@  discard block
 block discarded – undo
134 134
             'headers' => [
135 135
                 'User-Agent'                    => sanitize_text_field($_SERVER['HTTP_USER_AGENT']),
136 136
                 'Content-Type'                  => 'application/json',
137
-                'Authorization'                 => 'Bearer ' . $access_token,
137
+                'Authorization'                 => 'Bearer '.$access_token,
138 138
                 'PayPal-Partner-Attribution-Id' => $bn_code,
139 139
             ],
140 140
             'body'    => $body_params,
141 141
         ];
142
-        $request_url = Domain::getPayPalApiUrl($paypal_pm) . '/v2/customer/partner-referrals';
142
+        $request_url = Domain::getPayPalApiUrl($paypal_pm).'/v2/customer/partner-referrals';
143 143
         $response    = EED_PayPalOnboard::sendRequest($paypal_pm, $request_url, $post_params);
144 144
         // Check the data we received.
145 145
         if (isset($response['error']) || empty($response['links'])) {
146 146
             // Did the original access token get replaced by any chance ?
147
-            if (! $one_time_request
147
+            if ( ! $one_time_request
148 148
                 && ! empty($response['message'])
149 149
                 && $response['message'] === 'Access Token not found in cache'
150 150
             ) {
@@ -256,7 +256,7 @@  discard block
 block discarded – undo
256 256
             'payment_method' => $get_params['payment_method'],
257 257
         ];
258 258
         if (isset($get_params['sandbox_mode'])) {
259
-            $args_to_add[ Domain::META_KEY_SANDBOX_MODE ] = $get_params['sandbox_mode'];
259
+            $args_to_add[Domain::META_KEY_SANDBOX_MODE] = $get_params['sandbox_mode'];
260 260
         }
261 261
         $home_url = add_query_arg($args_to_add, admin_url('admin.php'));
262 262
         wp_redirect($home_url);
@@ -275,16 +275,16 @@  discard block
 block discarded – undo
275 275
     public static function updateOnboardingStatus(): void
276 276
     {
277 277
         // Check if this is the webhook from PayPal.
278
-        if (! isset($_GET['webhook_action'], $_GET['nonce'])
278
+        if ( ! isset($_GET['webhook_action'], $_GET['nonce'])
279 279
             || $_GET['webhook_action'] !== 'eepPpcMerchantOnboard'
280 280
         ) {
281
-            return;  // Ignore.
281
+            return; // Ignore.
282 282
         }
283 283
         $get_params = EED_Module::getRequest()->getParams();
284 284
         // Get the payment method.
285 285
         $paypal_pm = EED_PayPalCommerce::getPaymentMethod();
286 286
         // Check the response (GET) parameters.
287
-        if (! EED_PayPalOnboard::onboardingStatusResponseValid($get_params, $paypal_pm)) {
287
+        if ( ! EED_PayPalOnboard::onboardingStatusResponseValid($get_params, $paypal_pm)) {
288 288
             // Missing parameters. Can't proceed.
289 289
             PayPalLogger::errorLog(
290 290
                 esc_html__('Missing required onboarding parameters.', 'event_espresso'),
@@ -297,9 +297,9 @@  discard block
 block discarded – undo
297 297
         // Check on the onboarding status (recommended by PP).
298 298
         $onboarding_status = EED_PayPalOnboard::trackSellerOnboarding(
299 299
             $paypal_pm,
300
-            $get_params[ Domain::META_KEY_SELLER_MERCHANT_ID ]
300
+            $get_params[Domain::META_KEY_SELLER_MERCHANT_ID]
301 301
         );
302
-        if (! isset($onboarding_status['valid']) || ! $onboarding_status['valid']) {
302
+        if ( ! isset($onboarding_status['valid']) || ! $onboarding_status['valid']) {
303 303
             PayPalLogger::errorLog(
304 304
                 $onboarding_status['message'],
305 305
                 array_merge($get_params, $onboarding_status),
@@ -331,9 +331,9 @@  discard block
 block discarded – undo
331 331
         // Check that we have all the required parameters and the nonce is ok.
332 332
         if ($paypal_pm instanceof EE_Payment_Method
333 333
             && wp_verify_nonce($data['nonce'], Domain::NONCE_NAME_ONBOARDING_RETURN)
334
-            && ! empty($data[ Domain::API_PARAM_PARTNER_ID ])
335
-            && ! empty($data[ Domain::META_KEY_SELLER_MERCHANT_ID ])
336
-            && isset($data[ Domain::API_PARAM_EMAIL_CONFIRMED ])
334
+            && ! empty($data[Domain::API_PARAM_PARTNER_ID])
335
+            && ! empty($data[Domain::META_KEY_SELLER_MERCHANT_ID])
336
+            && isset($data[Domain::API_PARAM_EMAIL_CONFIRMED])
337 337
         ) {
338 338
             return true;
339 339
         }
@@ -355,7 +355,7 @@  discard block
 block discarded – undo
355 355
         $access_token = PayPalExtraMetaManager::getPmOption($paypal_pm, Domain::META_KEY_ACCESS_TOKEN);
356 356
         $expired      = EED_PayPalOnboard::partnerAccessTokenExpired($paypal_pm);
357 357
         // If we don't have it, request/update it.
358
-        if (! $access_token || $expired) {
358
+        if ( ! $access_token || $expired) {
359 359
             return EED_PayPalOnboard::requestPartnerAccessToken($paypal_pm);
360 360
         }
361 361
         // Access token is saved as encrypted, so return decrypted.
@@ -372,7 +372,7 @@  discard block
 block discarded – undo
372 372
     public static function partnerAccessTokenExpired(EE_Payment_Method $paypal_pm): bool
373 373
     {
374 374
         $expires_at = (int) PayPalExtraMetaManager::getPmOption($paypal_pm, Domain::META_KEY_TOKEN_EXPIRES_IN);
375
-        if (! $expires_at) {
375
+        if ( ! $expires_at) {
376 376
             return true;
377 377
         }
378 378
         // Validate the token expiration date.
@@ -409,18 +409,18 @@  discard block
 block discarded – undo
409 409
         if (defined('LOCAL_MIDDLEMAN_SERVER')) {
410 410
             $post_args['sslverify'] = Manager::verifySSL();
411 411
         }
412
-        $post_url = EED_PayPalOnboard::getMiddlemanBaseUrl($paypal_pm) . 'get_token';
412
+        $post_url = EED_PayPalOnboard::getMiddlemanBaseUrl($paypal_pm).'get_token';
413 413
         $response = EED_PayPalOnboard::sendRequest($paypal_pm, $post_url, $post_args);
414 414
         if (isset($response['error'])) {
415 415
             return '';
416 416
         }
417 417
         // Check the data we received.
418
-        if (! EED_PayPalOnboard::partnerTokenResponseValid($response, $paypal_pm)) {
418
+        if ( ! EED_PayPalOnboard::partnerTokenResponseValid($response, $paypal_pm)) {
419 419
             return '';
420 420
         }
421 421
         // If we are here all seems to be ok. Save the token and it's data.
422 422
         $saved = PayPalExtraMetaManager::savePartnerAccessToken($paypal_pm, $response);
423
-        if (! $saved) {
423
+        if ( ! $saved) {
424 424
             return '';
425 425
         }
426 426
         return $response['access_token'];
@@ -437,7 +437,7 @@  discard block
 block discarded – undo
437 437
     public static function trackSellerOnboarding(EE_Payment_Method $paypal_pm, string $merchant_id): array
438 438
     {
439 439
         $track_onboarding = EED_PayPalOnboard::getTrackOnboardingApi($paypal_pm, $merchant_id);
440
-        if (! $track_onboarding instanceof TrackSellerOnboarding) {
440
+        if ( ! $track_onboarding instanceof TrackSellerOnboarding) {
441 441
             $err_msg = esc_html__('Failed to track seller onboarding.', 'event_espresso');
442 442
             return ['error' => 'TRACK_ONBOARDING_FAILED', 'message' => $err_msg];
443 443
         }
@@ -460,7 +460,7 @@  discard block
 block discarded – undo
460 460
     ): ?TrackSellerOnboarding {
461 461
         $partner_id = PayPalExtraMetaManager::getPmOption($paypal_pm, Domain::META_KEY_PARTNER_MERCHANT_ID);
462 462
         $paypal_api = EED_PayPalCommerce::getPayPalApi($paypal_pm);
463
-        if (! $paypal_api instanceof PayPalApi || ! $partner_id) {
463
+        if ( ! $paypal_api instanceof PayPalApi || ! $partner_id) {
464 464
             return null;
465 465
         }
466 466
         return new TrackSellerOnboarding($paypal_api, $partner_id, $merchant_id, $paypal_pm->debug_mode());
@@ -476,7 +476,7 @@  discard block
 block discarded – undo
476 476
     public static function getOnboardStatus(): void
477 477
     {
478 478
         $paypal_pm = EED_PayPalCommerce::getPaymentMethod();
479
-        if (! $paypal_pm instanceof EE_Payment_Method) {
479
+        if ( ! $paypal_pm instanceof EE_Payment_Method) {
480 480
             $err_msg = esc_html__('Could not specify the payment method.', 'event_espresso');
481 481
             PayPalLogger::errorLog($err_msg, EED_Module::getRequest()->postParams(), $paypal_pm);
482 482
             wp_send_json(['on_board' => false]);
@@ -504,7 +504,7 @@  discard block
 block discarded – undo
504 504
     public static function offboard(): void
505 505
     {
506 506
         $paypal_pm = EED_PayPalCommerce::getPaymentMethod();
507
-        if (! $paypal_pm instanceof EE_Payment_Method) {
507
+        if ( ! $paypal_pm instanceof EE_Payment_Method) {
508 508
             wp_send_json([
509 509
                 'error'   => 'INVALID_PM',
510 510
                 'message' => esc_html__(
@@ -529,13 +529,13 @@  discard block
 block discarded – undo
529 529
         $pp_meta_data = PayPalExtraMetaManager::getAllData($payment_method);
530 530
         return
531 531
             // onborded with a third party integration ?
532
-            (! empty($pp_meta_data[ Domain::META_KEY_SELLER_MERCHANT_ID ])
533
-                && ! empty($pp_meta_data[ Domain::META_KEY_ACCESS_TOKEN ])
532
+            ( ! empty($pp_meta_data[Domain::META_KEY_SELLER_MERCHANT_ID])
533
+                && ! empty($pp_meta_data[Domain::META_KEY_ACCESS_TOKEN])
534 534
             )
535 535
             // or with the first party integration ?
536
-            || (! empty($pp_meta_data[ Domain::META_KEY_CLIENT_ID ])
537
-                && ! empty($pp_meta_data[ Domain::META_KEY_CLIENT_SECRET ])
538
-                && ! empty($pp_meta_data[ Domain::META_KEY_PAYER_ID ])
536
+            || ( ! empty($pp_meta_data[Domain::META_KEY_CLIENT_ID])
537
+                && ! empty($pp_meta_data[Domain::META_KEY_CLIENT_SECRET])
538
+                && ! empty($pp_meta_data[Domain::META_KEY_PAYER_ID])
539 539
             );
540 540
     }
541 541
 
@@ -616,8 +616,8 @@  discard block
 block discarded – undo
616 616
         // If this PM is used under different provider accounts, you might need an account indicator.
617 617
         $account = defined('EE_PAYPAL_COMMERCE_ACCOUNT_INDICATOR') ? EE_PAYPAL_COMMERCE_ACCOUNT_INDICATOR : '';
618 618
         $postfix = $payment_method->debug_mode() ? '_sandbox' : '';
619
-        $path    = 'paypal_commerce' . $account . $postfix;
620
-        return 'https://connect.eventespresso.' . $target . '/' . $path . '/';
619
+        $path    = 'paypal_commerce'.$account.$postfix;
620
+        return 'https://connect.eventespresso.'.$target.'/'.$path.'/';
621 621
     }
622 622
 
623 623
 
Please login to merge, or discard this patch.
modules/events_archive/EED_Events_Archive.module.php 1 patch
Indentation   +1052 added lines, -1052 removed lines patch added patch discarded remove patch
@@ -17,1047 +17,1047 @@  discard block
 block discarded – undo
17 17
  */
18 18
 class EED_Events_Archive extends EED_Module
19 19
 {
20
-    const EVENT_DETAILS_PRIORITY   = 100;
21
-
22
-    const EVENT_DATETIMES_PRIORITY = 110;
23
-
24
-    const EVENT_TICKETS_PRIORITY   = 120;
25
-
26
-    const EVENT_VENUES_PRIORITY    = 130;
27
-
28
-
29
-    public static $espresso_event_list_ID    = 0;
30
-
31
-    public static $espresso_grid_event_lists = [];
32
-
33
-    /**
34
-     * @type bool $using_get_the_excerpt
35
-     */
36
-    protected static $using_get_the_excerpt = false;
37
-
38
-    /**
39
-     * Used to flag when the event list is being called from an external iframe.
40
-     *
41
-     * @var bool $iframe
42
-     */
43
-    protected static $iframe = false;
44
-
45
-    /**
46
-     * @var EventListIframeEmbedButton $_iframe_embed_button
47
-     */
48
-    private static $_iframe_embed_button;
49
-
50
-    /**
51
-     * @type EE_Template_Part_Manager $template_parts
52
-     */
53
-    protected $template_parts;
54
-
55
-
56
-    /**
57
-     * @return EED_Events_Archive
58
-     * @throws EE_Error
59
-     * @throws ReflectionException
60
-     */
61
-    public static function instance()
62
-    {
63
-        return parent::get_instance(__CLASS__);
64
-    }
65
-
66
-
67
-    /**
68
-     * for hooking into EE Core, other modules, etc
69
-     *
70
-     * @return void
71
-     * @throws InvalidArgumentException
72
-     * @throws InvalidDataTypeException
73
-     * @throws InvalidInterfaceException
74
-     */
75
-    public static function set_hooks()
76
-    {
77
-        /** @var CustomPostTypeDefinitions $custom_post_type_definitions */
78
-        $custom_post_type_definitions = LoaderFactory::getLoader()->getShared(CustomPostTypeDefinitions::class);
79
-        $custom_post_types            = $custom_post_type_definitions->getDefinitions();
80
-        EE_Config::register_route(
81
-            $custom_post_types[EspressoPostType::EVENTS]['plural_slug'],
82
-            'Events_Archive',
83
-            'run'
84
-        );
85
-        EE_Config::register_route(
86
-            'event_list',
87
-            'Events_Archive',
88
-            'event_list'
89
-        );
90
-        EE_Config::register_route(
91
-            'iframe',
92
-            'Events_Archive',
93
-            'event_list_iframe',
94
-            'event_list'
95
-        );
96
-        add_action('wp_loaded', ['EED_Events_Archive', 'set_definitions'], 2);
97
-    }
98
-
99
-
100
-    /**
101
-     * for hooking into EE Admin Core, other modules, etc
102
-     *
103
-     * @return void
104
-     */
105
-    public static function set_hooks_admin()
106
-    {
107
-        add_action('wp_loaded', ['EED_Events_Archive', 'set_definitions'], 2);
108
-        // hook into the end of the \EE_Admin_Page::_load_page_dependencies()
109
-        // to load assets for "espresso_events" page on the "default" route (action)
110
-        add_action(
111
-            'FHEE__EE_Admin_Page___load_page_dependencies__after_load__espresso_events__default',
112
-            ['EED_Events_Archive', 'event_list_iframe_embed_button'],
113
-            10
114
-        );
115
-    }
116
-
117
-
118
-    /**
119
-     * @return void
120
-     */
121
-    public static function set_definitions()
122
-    {
123
-        define('EVENTS_ARCHIVE_ASSETS_URL', plugin_dir_url(__FILE__) . 'assets/');
124
-        define('EVENTS_ARCHIVE_TEMPLATES_PATH', str_replace('\\', '/', plugin_dir_path(__FILE__)) . 'templates/');
125
-    }
126
-
127
-
128
-    /**
129
-     * set up EE_Events_Archive_Config
130
-     */
131
-    protected function set_config()
132
-    {
133
-        $this->set_config_section('template_settings');
134
-        $this->set_config_class('EE_Events_Archive_Config');
135
-        $this->set_config_name('EED_Events_Archive');
136
-    }
137
-
138
-
139
-    /**
140
-     * @return EventListIframeEmbedButton
141
-     */
142
-    public static function get_iframe_embed_button()
143
-    {
144
-        if (! self::$_iframe_embed_button instanceof EventListIframeEmbedButton) {
145
-            self::$_iframe_embed_button = new EventListIframeEmbedButton();
146
-        }
147
-        return self::$_iframe_embed_button;
148
-    }
149
-
150
-
151
-    /**
152
-     * @return void
153
-     */
154
-    public static function event_list_iframe_embed_button()
155
-    {
156
-        $iframe_embed_button = EED_Events_Archive::get_iframe_embed_button();
157
-        $iframe_embed_button->addEmbedButton();
158
-    }
159
-
160
-
161
-    /**
162
-     * @param EE_Events_Archive_Config|null $config
163
-     * @return EE_Template_Part_Manager
164
-     * @throws EE_Error
165
-     */
166
-    public function initialize_template_parts(EE_Events_Archive_Config $config = null)
167
-    {
168
-        $config = $config instanceof EE_Events_Archive_Config ? $config : $this->config();
169
-        EEH_Autoloader::register_template_part_autoloaders();
170
-        $template_parts = new EE_Template_Part_Manager();
171
-        $template_parts->add_template_part(
172
-            'tickets',
173
-            esc_html__('Ticket Selector', 'event_espresso'),
174
-            'content-espresso_events-tickets.php',
175
-            $config->display_order_tickets
176
-        );
177
-        $template_parts->add_template_part(
178
-            'datetimes',
179
-            esc_html__('Dates and Times', 'event_espresso'),
180
-            'content-espresso_events-datetimes.php',
181
-            $config->display_order_datetimes
182
-        );
183
-        $template_parts->add_template_part(
184
-            'event',
185
-            esc_html__('Event Description', 'event_espresso'),
186
-            'content-espresso_events-details.php',
187
-            $config->display_order_event
188
-        );
189
-        $template_parts->add_template_part(
190
-            'venue',
191
-            esc_html__('Venue Information', 'event_espresso'),
192
-            'content-espresso_events-venues.php',
193
-            $config->display_order_venue
194
-        );
195
-        do_action('AHEE__EED_Event_Archive__initialize_template_parts', $template_parts);
196
-        return $template_parts;
197
-    }
198
-
199
-
200
-    /**
201
-     * initial module setup
202
-     * this gets called by the EE_Front_Controller if the module route is found in the incoming request
203
-     *
204
-     * @param WP $WP
205
-     * @return void
206
-     */
207
-    public function run($WP)
208
-    {
209
-        do_action('AHEE__EED_Events_Archive__before_run');
210
-        // ensure valid EE_Events_Archive_Config() object exists
211
-        $this->set_config();
212
-        /** @type EE_Events_Archive_Config $config */
213
-        $config = $this->config();
214
-        // load other required components
215
-        $this->load_event_list_assets();
216
-        // filter the WP posts_join, posts_where, and posts_orderby SQL clauses
217
-        // add query filters
218
-        EEH_Event_Query::add_query_filters();
219
-        // set params that will get used by the filters
220
-        EEH_Event_Query::set_query_params(
221
-            '',                             // month
222
-            '',                             // category
223
-            $config->display_expired_events // show_expired
224
-        );
225
-        // check what template is loaded
226
-        add_filter('template_include', [$this, 'template_include'], 999, 1);
227
-    }
228
-
229
-
230
-    /**
231
-     * most likely called by the ESPRESSO_EVENTS shortcode which uses this module to do some of it's lifting
232
-     *
233
-     * @return void
234
-     */
235
-    public function event_list()
236
-    {
237
-        // ensure valid EE_Events_Archive_Config() object exists
238
-        $this->set_config();
239
-        // load other required components
240
-        $this->load_event_list_assets();
241
-    }
242
-
243
-
244
-    /**
245
-     * @return void
246
-     * @throws EE_Error
247
-     * @throws ReflectionException
248
-     */
249
-    public function event_list_iframe()
250
-    {
251
-        EED_Events_Archive::$iframe = true;
252
-        $event_list_iframe          = new EventsArchiveIframe($this);
253
-        $event_list_iframe->display();
254
-    }
255
-
256
-
257
-    /**
258
-     * @return bool
259
-     */
260
-    public static function is_iframe()
261
-    {
262
-        return EED_Events_Archive::$iframe;
263
-    }
264
-
265
-
266
-    /**
267
-     * @return string
268
-     */
269
-    public static function link_target()
270
-    {
271
-        return EED_Events_Archive::$iframe ? ' target="_blank"' : '';
272
-    }
273
-
274
-
275
-    /**
276
-     * @param string $template
277
-     * @return string
278
-     * @throws EE_Error
279
-     * @throws ReflectionException
280
-     */
281
-    public function template_include($template = '')
282
-    {
283
-        // don't add content filter for dedicated EE child themes or private posts
284
-        if (! EEH_Template::is_espresso_theme()) {
285
-            /** @type EE_Events_Archive_Config $config */
286
-            $config = $this->config();
287
-            // add status banner ?
288
-            if ($config->display_status_banner) {
289
-                add_filter('the_title', ['EED_Events_Archive', 'the_title'], 100, 2);
290
-            }
291
-            // if NOT a custom template
292
-            if (
293
-                apply_filters('FHEE__EED_Event_Archive__template_include__allow_custom_selected_template', false)
294
-                || EE_Registry::instance()
295
-                              ->load_core('Front_Controller')
296
-                              ->get_selected_template() !== 'archive-espresso_events.php'
297
-            ) {
298
-                add_filter('FHEE__EED_Events_Archive__template_include__events_list_active', '__return_true');
299
-                // load functions.php file for the theme (loaded by WP if using child theme)
300
-                EEH_Template::load_espresso_theme_functions();
301
-                EED_Events_Archive::addEventDetailsFilters();
302
-                // don't display entry meta because the existing theme will take care of that
303
-                add_filter('FHEE__content_espresso_events_details_template__display_entry_meta', '__return_false');
304
-            }
305
-        }
306
-        return $template;
307
-    }
308
-
309
-
310
-    /**
311
-     * kinda hacky, but if a theme is using get_the_excerpt(),
312
-     * then we need to remove our filters on the_content()
313
-     *
314
-     * @param string $excerpt
315
-     * @return string
316
-     * @throws EE_Error
317
-     * @throws ReflectionException
318
-     */
319
-    public static function get_the_excerpt($excerpt = '')
320
-    {
321
-        if (post_password_required()) {
322
-            return $excerpt;
323
-        }
324
-        if (apply_filters('FHEE__EED_Events_Archive__get_the_excerpt__theme_uses_get_the_excerpt', false)) {
325
-            EED_Events_Archive::removeEventDetailsFilters(false);
326
-            $excerpt = EED_Events_Archive::event_details($excerpt);
327
-        } else {
328
-            EED_Events_Archive::$using_get_the_excerpt = true;
329
-            add_filter('wp_trim_excerpt', ['EED_Events_Archive', 'end_get_the_excerpt'], 999, 1);
330
-        }
331
-        return $excerpt;
332
-    }
333
-
334
-
335
-    /**
336
-     * @param string $text
337
-     * @return string
338
-     */
339
-    public static function end_get_the_excerpt($text = '')
340
-    {
341
-        EED_Events_Archive::$using_get_the_excerpt = false;
342
-        return $text;
343
-    }
344
-
345
-
346
-    /**
347
-     * @param string $title
348
-     * @param string $id
349
-     * @return string
350
-     * @throws EE_Error
351
-     * @throws ReflectionException
352
-     */
353
-    public static function the_title($title = '', $id = '')
354
-    {
355
-        global $post;
356
-        if ($post instanceof WP_Post) {
357
-            return (EED_Events_Archive::isBlockTheme() || in_the_loop()) && $post->ID == $id
358
-                ? espresso_event_status_banner($post->ID) . $title
359
-                : $title;
360
-        }
361
-        return $title;
362
-    }
363
-
364
-
365
-    /**
366
-     * @param string $content
367
-     * @return string
368
-     * @throws EE_Error
369
-     * @throws ReflectionException
370
-     */
371
-    public static function event_details($content)
372
-    {
373
-        global $post;
374
-        static $current_post_ID = 0;
375
-        if (
376
-            $current_post_ID !== $post->ID
377
-            && $post->post_type === EspressoPostType::EVENTS
378
-            && ! EED_Events_Archive::$using_get_the_excerpt
379
-            && ! post_password_required()
380
-            && (
381
-                apply_filters('FHEE__EES_Espresso_Events__process_shortcode__true', false)
382
-                || ! apply_filters('FHEE__content_espresso_events__template_loaded', false)
383
-            )
384
-        ) {
385
-            // Set current post ID to prevent showing content twice, but only if headers have definitely been sent.
386
-            // Reason being is that some plugins, like Yoast, need to run through a copy of the loop early
387
-            // BEFORE headers are sent in order to examine the post content and generate content for the HTML header.
388
-            // We want to allow those plugins to still do their thing and have access to our content, but depending on
389
-            // how your event content is being displayed (shortcode, CPT route, etc), this filter can get applied twice,
390
-            // so the following allows this filter to be applied multiple times, but only once for real
391
-            $current_post_ID = did_action('loop_start') ? $post->ID : 0;
392
-            if (EE_Registry::instance()->CFG->template_settings->EED_Events_Archive->use_sortable_display_order) {
393
-                $content = EED_Events_Archive::use_sortable_display_order();
394
-            } else {
395
-                $content = EED_Events_Archive::use_filterable_display_order();
396
-            }
397
-        }
398
-        return $content;
399
-    }
400
-
401
-
402
-    /**
403
-     * @return string
404
-     * @throws EE_Error
405
-     * @throws ReflectionException
406
-     */
407
-    protected static function use_sortable_display_order()
408
-    {
409
-        // no further password checks required atm
410
-        add_filter('FHEE__EED_Events_Archive__event_details__no_post_password_required', '__return_true');
411
-        // remove this callback from being applied to the_content()
412
-        EED_Events_Archive::removeEventDetailsFilters();
413
-        // now add additional content depending on whether event is using the_excerpt() or the_content()
414
-        EED_Events_Archive::instance()->template_parts = EED_Events_Archive::instance()->initialize_template_parts();
415
-        $content = EEH_Template::locate_template('content-espresso_events-details.php');
416
-        $content = EED_Events_Archive::instance()->template_parts->apply_template_part_filters($content);
417
-        // re-add our main filters (or else the next event won't have them)
418
-        EED_Events_Archive::addEventDetailsFilters();
419
-        remove_filter(
420
-            'FHEE__EED_Events_Archive__event_details__no_post_password_required',
421
-            '__return_true'
422
-        );
423
-        return $content;
424
-    }
425
-
426
-
427
-    /**
428
-     * @return string
429
-     */
430
-    protected static function use_filterable_display_order()
431
-    {
432
-        // remove this callback from being applied to the_content()
433
-        EED_Events_Archive::removeEventDetailsFilters();
434
-        // now add additional content depending on whether event is using the_excerpt() or the_content()
435
-        EED_Events_Archive::_add_additional_excerpt_filters();
436
-        EED_Events_Archive::_add_additional_content_filters();
437
-        do_action('AHEE__EED_Events_Archive__use_filterable_display_order__after_add_filters');
438
-        // now load our template
439
-        $content = EEH_Template::locate_template('content-espresso_events-details.php');
440
-        // re-add our main filters (or else the next event won't have them)
441
-        EED_Events_Archive::addEventDetailsFilters();
442
-        // but remove the other filters so that they don't get applied to the next post
443
-        EED_Events_Archive::_remove_additional_events_archive_filters();
444
-        do_action('AHEE__EED_Events_Archive__use_filterable_display_order__after_remove_filters');
445
-        // we're not returning the $content directly because the template we are loading uses the_content (or the_excerpt)
446
-        // return ! empty( $template ) ? $template : $content;
447
-        return $content;
448
-    }
449
-
450
-
451
-    /**
452
-     * adds datetimes ABOVE content
453
-     *
454
-     * @param string $content
455
-     * @return string
456
-     */
457
-    public static function event_datetimes($content)
458
-    {
459
-        if (post_password_required()) {
460
-            return $content;
461
-        }
462
-        return EEH_Template::locate_template('content-espresso_events-datetimes.php') . $content;
463
-    }
464
-
465
-
466
-    /**
467
-     * adds tickets ABOVE content (which includes datetimes)
468
-     *
469
-     * @param string $content
470
-     * @return string
471
-     */
472
-    public static function event_tickets($content)
473
-    {
474
-        if (post_password_required()) {
475
-            return $content;
476
-        }
477
-        return EEH_Template::locate_template('content-espresso_events-tickets.php') . $content;
478
-    }
479
-
480
-
481
-    /**
482
-     * adds venues BELOW content
483
-     *
484
-     * @param string $content
485
-     * @return string
486
-     */
487
-    public static function event_venue($content)
488
-    {
489
-        return EED_Events_Archive::event_venues($content);
490
-    }
491
-
492
-
493
-    /**
494
-     * adds venues BELOW content
495
-     *
496
-     * @param string $content
497
-     * @return string
498
-     */
499
-    public static function event_venues($content)
500
-    {
501
-        if (post_password_required()) {
502
-            return $content;
503
-        }
504
-        return $content . EEH_Template::locate_template('content-espresso_events-venues.php');
505
-    }
506
-
507
-
508
-    /**
509
-     * @return bool
510
-     * @since 4.10.33.p
511
-     */
512
-    public static function isBlockTheme()
513
-    {
514
-        return function_exists('wp_is_block_theme') && wp_is_block_theme();
515
-    }
516
-
517
-
518
-    /**
519
-     * @return string
520
-     * @since 4.10.33.p
521
-     */
522
-    public static function getExcerptFilter()
523
-    {
524
-        static $excerpt_filter = null;
525
-        if ($excerpt_filter === null) {
526
-            $excerpt_filter = EED_Events_Archive::isBlockTheme() ? 'get_the_excerpt' : 'the_excerpt';
527
-        }
528
-        return $excerpt_filter;
529
-    }
530
-
531
-
532
-    /**
533
-     * @since 4.10.33.p
534
-     */
535
-    protected static function addEventDetailsFilters()
536
-    {
537
-        add_filter(
538
-            'the_content',
539
-            ['EED_Events_Archive', 'event_details'],
540
-            EED_Events_Archive::EVENT_DETAILS_PRIORITY
541
-        );
542
-        // but because we don't know if the theme is using the_excerpt()
543
-        add_filter(
544
-            EED_Events_Archive::getExcerptFilter(),
545
-            ['EED_Events_Archive', 'event_details'],
546
-            EED_Events_Archive::EVENT_DETAILS_PRIORITY
547
-        );
548
-        // and just in case they are running get_the_excerpt() which DESTROYS things
549
-        add_filter('get_the_excerpt', ['EED_Events_Archive', 'get_the_excerpt'], 1, 1);
550
-    }
551
-
552
-
553
-    /**
554
-     * @param bool $and_get_the_excerpt
555
-     * @since 4.10.33.p
556
-     */
557
-    protected static function removeEventDetailsFilters($and_get_the_excerpt = true)
558
-    {
559
-        // we need to first remove all callbacks from being applied to the_content()
560
-        // (otherwise it will recurse and blow up the interweb)
561
-        remove_filter(
562
-            'the_content',
563
-            ['EED_Events_Archive', 'event_details'],
564
-            EED_Events_Archive::EVENT_DETAILS_PRIORITY
565
-        );
566
-        remove_filter(
567
-            EED_Events_Archive::getExcerptFilter(),
568
-            ['EED_Events_Archive', 'event_details'],
569
-            EED_Events_Archive::EVENT_DETAILS_PRIORITY
570
-        );
571
-        if ($and_get_the_excerpt) {
572
-            remove_filter('get_the_excerpt', ['EED_Events_Archive', 'get_the_excerpt'], 1);
573
-        }
574
-    }
575
-
576
-
577
-    /**
578
-     * @return void
579
-     */
580
-    private static function _add_additional_excerpt_filters()
581
-    {
582
-        add_filter(
583
-            EED_Events_Archive::getExcerptFilter(),
584
-            ['EED_Events_Archive', 'event_datetimes'],
585
-            EED_Events_Archive::EVENT_DATETIMES_PRIORITY
586
-        );
587
-        add_filter(
588
-            EED_Events_Archive::getExcerptFilter(),
589
-            ['EED_Events_Archive', 'event_tickets'],
590
-            EED_Events_Archive::EVENT_TICKETS_PRIORITY
591
-        );
592
-        add_filter(
593
-            EED_Events_Archive::getExcerptFilter(),
594
-            ['EED_Events_Archive', 'event_venues'],
595
-            EED_Events_Archive::EVENT_VENUES_PRIORITY
596
-        );
597
-    }
598
-
599
-
600
-    /**
601
-     * @return void
602
-     */
603
-    private static function _add_additional_content_filters()
604
-    {
605
-        add_filter(
606
-            'the_content',
607
-            ['EED_Events_Archive', 'event_datetimes'],
608
-            EED_Events_Archive::EVENT_DATETIMES_PRIORITY
609
-        );
610
-        add_filter(
611
-            'the_content',
612
-            ['EED_Events_Archive', 'event_tickets'],
613
-            EED_Events_Archive::EVENT_TICKETS_PRIORITY
614
-        );
615
-        add_filter(
616
-            'the_content',
617
-            ['EED_Events_Archive', 'event_venues'],
618
-            EED_Events_Archive::EVENT_VENUES_PRIORITY
619
-        );
620
-    }
621
-
622
-
623
-    /**
624
-     * @return void
625
-     */
626
-    private static function _remove_additional_events_archive_filters()
627
-    {
628
-        remove_filter(
629
-            EED_Events_Archive::getExcerptFilter(),
630
-            ['EED_Events_Archive', 'event_datetimes'],
631
-            EED_Events_Archive::EVENT_DATETIMES_PRIORITY
632
-        );
633
-        remove_filter(
634
-            EED_Events_Archive::getExcerptFilter(),
635
-            ['EED_Events_Archive', 'event_tickets'],
636
-            EED_Events_Archive::EVENT_TICKETS_PRIORITY
637
-        );
638
-        remove_filter(
639
-            EED_Events_Archive::getExcerptFilter(),
640
-            ['EED_Events_Archive', 'event_venues'],
641
-            EED_Events_Archive::EVENT_VENUES_PRIORITY
642
-        );
643
-        remove_filter(
644
-            'the_content',
645
-            ['EED_Events_Archive', 'event_datetimes'],
646
-            EED_Events_Archive::EVENT_DATETIMES_PRIORITY
647
-        );
648
-        remove_filter(
649
-            'the_content',
650
-            ['EED_Events_Archive', 'event_tickets'],
651
-            EED_Events_Archive::EVENT_TICKETS_PRIORITY
652
-        );
653
-        remove_filter(
654
-            'the_content',
655
-            ['EED_Events_Archive', 'event_venues'],
656
-            EED_Events_Archive::EVENT_VENUES_PRIORITY
657
-        );
658
-    }
659
-
660
-
661
-    /**
662
-     * @return void
663
-     */
664
-    public static function remove_all_events_archive_filters()
665
-    {
666
-        // remove_filter( 'get_the_excerpt', array( 'EED_Events_Archive', 'get_the_excerpt' ), 1 );
667
-        remove_filter('the_title', ['EED_Events_Archive', 'the_title'], 1);
668
-        remove_filter(
669
-            EED_Events_Archive::getExcerptFilter(),
670
-            ['EED_Events_Archive', 'event_details'],
671
-            EED_Events_Archive::EVENT_DETAILS_PRIORITY
672
-        );
673
-        remove_filter(
674
-            EED_Events_Archive::getExcerptFilter(),
675
-            ['EED_Events_Archive', 'event_datetimes'],
676
-            EED_Events_Archive::EVENT_DATETIMES_PRIORITY
677
-        );
678
-        remove_filter(
679
-            EED_Events_Archive::getExcerptFilter(),
680
-            ['EED_Events_Archive', 'event_tickets'],
681
-            EED_Events_Archive::EVENT_TICKETS_PRIORITY
682
-        );
683
-        remove_filter(
684
-            EED_Events_Archive::getExcerptFilter(),
685
-            ['EED_Events_Archive', 'event_venues'],
686
-            EED_Events_Archive::EVENT_VENUES_PRIORITY
687
-        );
688
-        remove_filter(
689
-            'the_content',
690
-            ['EED_Events_Archive', 'event_details'],
691
-            EED_Events_Archive::EVENT_DETAILS_PRIORITY
692
-        );
693
-        remove_filter(
694
-            'the_content',
695
-            ['EED_Events_Archive', 'event_datetimes'],
696
-            EED_Events_Archive::EVENT_DATETIMES_PRIORITY
697
-        );
698
-        remove_filter(
699
-            'the_content',
700
-            ['EED_Events_Archive', 'event_tickets'],
701
-            EED_Events_Archive::EVENT_TICKETS_PRIORITY
702
-        );
703
-        remove_filter(
704
-            'the_content',
705
-            ['EED_Events_Archive', 'event_venues'],
706
-            EED_Events_Archive::EVENT_VENUES_PRIORITY
707
-        );
708
-        // don't display entry meta because the existing theme will take care of that
709
-        remove_filter(
710
-            'FHEE__content_espresso_events_details_template__display_entry_meta',
711
-            '__return_false'
712
-        );
713
-    }
714
-
715
-
716
-    /**
717
-     * @return void
718
-     */
719
-    public function load_event_list_assets()
720
-    {
721
-        do_action('AHEE__EED_Events_Archive__before_load_assets');
722
-        add_filter('FHEE_load_EE_Session', '__return_true');
723
-        add_filter('FHEE__EED_Ticket_Selector__load_tckt_slctr_assets', '__return_true');
724
-        add_action('wp_enqueue_scripts', [$this, 'wp_enqueue_scripts'], 10);
725
-        if (EE_Registry::instance()->CFG->map_settings->use_google_maps) {
726
-            add_action('wp_enqueue_scripts', ['EEH_Maps', 'espresso_google_map_js'], 11);
727
-        }
728
-    }
729
-
730
-
731
-    /**
732
-     * @return void
733
-     * @throws EE_Error
734
-     */
735
-    public function wp_enqueue_scripts()
736
-    {
737
-        // get some style
738
-        if (apply_filters('FHEE_enable_default_espresso_css', false)) {
739
-            // first check uploads folder
740
-            if (EEH_File::is_readable(get_stylesheet_directory() . $this->theme . '/style.css')) {
741
-                wp_register_style(
742
-                    $this->theme,
743
-                    get_stylesheet_directory_uri() . $this->theme . '/style.css',
744
-                    ['dashicons', 'espresso_default']
745
-                );
746
-            }
747
-            wp_enqueue_style($this->theme);
748
-        }
749
-    }
750
-
751
-
752
-    /**
753
-     * @static
754
-     * @return void
755
-     */
756
-    public static function template_settings_form()
757
-    {
758
-        $template_settings                     = EE_Registry::instance()->CFG->template_settings;
759
-        $template_settings->EED_Events_Archive = isset($template_settings->EED_Events_Archive)
760
-            ? $template_settings->EED_Events_Archive : new EE_Events_Archive_Config();
761
-        $template_settings->EED_Events_Archive = apply_filters(
762
-            'FHEE__EED_Events_Archive__template_settings_form__event_list_config',
763
-            $template_settings->EED_Events_Archive
764
-        );
765
-        $events_archive_settings               = [
766
-            'display_status_banner'   => 0,
767
-            'display_description'     => 1,
768
-            'display_ticket_selector' => 0,
769
-            'display_datetimes'       => 1,
770
-            'display_venue'           => 0,
771
-            'display_expired_events'  => 0,
772
-        ];
773
-        $events_archive_settings               = array_merge(
774
-            $events_archive_settings,
775
-            (array) $template_settings->EED_Events_Archive
776
-        );
777
-        EEH_Template::display_template(
778
-            EVENTS_ARCHIVE_TEMPLATES_PATH . 'admin-event-list-settings.template.php',
779
-            $events_archive_settings
780
-        );
781
-    }
782
-
783
-
784
-    /**
785
-     * @param EE_Template_Config $CFG
786
-     * @param array              $REQ
787
-     * @return EE_Template_Config
788
-     */
789
-    public static function update_template_settings(EE_Template_Config $CFG, array $REQ): EE_Template_Config
790
-    {
791
-        $CFG->EED_Events_Archive = new EE_Events_Archive_Config();
792
-        // unless we are resetting the config...
793
-        if (
794
-            ! isset($REQ['EED_Events_Archive_reset_event_list_settings'])
795
-            || absint($REQ['EED_Events_Archive_reset_event_list_settings']) !== 1
796
-        ) {
797
-            $CFG->EED_Events_Archive->display_status_banner   = isset($REQ['EED_Events_Archive_display_status_banner'])
798
-                ? absint($REQ['EED_Events_Archive_display_status_banner'])
799
-                : 0;
800
-            $CFG->EED_Events_Archive->display_description     = isset($REQ['EED_Events_Archive_display_description'])
801
-                ? absint($REQ['EED_Events_Archive_display_description'])
802
-                : 1;
803
-            $CFG->EED_Events_Archive->display_ticket_selector = isset($REQ['EED_Events_Archive_display_ticket_selector'])
804
-                ? absint($REQ['EED_Events_Archive_display_ticket_selector'])
805
-                : 0;
806
-            $CFG->EED_Events_Archive->display_datetimes       = isset($REQ['EED_Events_Archive_display_datetimes'])
807
-                ? absint($REQ['EED_Events_Archive_display_datetimes'])
808
-                : 1;
809
-            $CFG->EED_Events_Archive->display_venue           = isset($REQ['EED_Events_Archive_display_venue'])
810
-                ? absint($REQ['EED_Events_Archive_display_venue'])
811
-                : 0;
812
-            $CFG->EED_Events_Archive->display_expired_events  = isset($REQ['EED_Events_Archive_display_expired_events'])
813
-                ? absint($REQ['EED_Events_Archive_display_expired_events'])
814
-                : 0;
815
-        }
816
-        return $CFG;
817
-    }
818
-
819
-
820
-    /**
821
-     * @param string $extra_class
822
-     * @return string
823
-     */
824
-    public static function event_list_css($extra_class = '')
825
-    {
826
-        $event_list_css   = ! empty($extra_class) ? [$extra_class] : [];
827
-        $event_list_css[] = 'espresso-event-list-event';
828
-        return implode(' ', $event_list_css);
829
-    }
830
-
831
-
832
-    /**
833
-     * @return array
834
-     * @throws EE_Error
835
-     * @throws ReflectionException
836
-     */
837
-    public static function event_categories()
838
-    {
839
-        return EE_Registry::instance()->load_model('Term')->get_all_ee_categories();
840
-    }
841
-
842
-
843
-    /**
844
-     * @param $value
845
-     * @return bool
846
-     */
847
-    public static function display_description($value)
848
-    {
849
-        $config              = EE_Registry::instance()->CFG->template_settings->EED_Events_Archive;
850
-        $display_description = isset($config->display_description) ? $config->display_description : 1;
851
-        return $display_description === $value;
852
-    }
853
-
854
-
855
-    /**
856
-     * @return bool
857
-     */
858
-    public static function display_ticket_selector()
859
-    {
860
-        $config = EE_Registry::instance()->CFG->template_settings->EED_Events_Archive;
861
-        return isset($config->display_ticket_selector) && $config->display_ticket_selector;
862
-    }
863
-
864
-
865
-    /**
866
-     * @return bool
867
-     * @throws EE_Error
868
-     * @throws ReflectionException
869
-     */
870
-    public static function display_venue()
871
-    {
872
-        $config = EE_Registry::instance()->CFG->template_settings->EED_Events_Archive;
873
-        return isset($config->display_venue) && $config->display_venue && EEH_Venue_View::venue_name();
874
-    }
875
-
876
-
877
-    /**
878
-     * @return bool
879
-     */
880
-    public static function display_datetimes()
881
-    {
882
-        $config = EE_Registry::instance()->CFG->template_settings->EED_Events_Archive;
883
-        return isset($config->display_datetimes) && $config->display_datetimes;
884
-    }
885
-
886
-
887
-    /**
888
-     * @return string
889
-     */
890
-    public static function event_list_title()
891
-    {
892
-        return apply_filters(
893
-            'FHEE__archive_espresso_events_template__upcoming_events_h1',
894
-            esc_html__('Upcoming Events', 'event_espresso')
895
-        );
896
-    }
897
-
898
-
899
-    // GRAVEYARD
900
-
901
-
902
-    /**
903
-     * @since 4.4.0
904
-     */
905
-    public static function _doing_it_wrong_notice($function = '')
906
-    {
907
-        EE_Error::doing_it_wrong(
908
-            __FUNCTION__,
909
-            sprintf(
910
-                esc_html__(
911
-                    'EED_Events_Archive::%1$s was moved to EEH_Event_Query::%1$s:%2$sPlease update your existing code because the method it calls will be removed in version %3$s',
912
-                    'event_espresso'
913
-                ),
914
-                $function,
915
-                '<br />',
916
-                '4.6.0'
917
-            ),
918
-            '4.4.0'
919
-        );
920
-    }
921
-
922
-
923
-    /**
924
-     * @deprecated
925
-     * @since 4.4.0
926
-     */
927
-    public function get_post_data()
928
-    {
929
-        EEH_Event_Query::set_query_params();
930
-    }
931
-
932
-
933
-    /**
934
-     * @throws EE_Error
935
-     * @since 4.4.0
936
-     * @deprecated
937
-     */
938
-    public function posts_fields($SQL, WP_Query $wp_query)
939
-    {
940
-        EED_Events_Archive::_doing_it_wrong_notice(__FUNCTION__);
941
-        return EEH_Event_Query::posts_fields($SQL, $wp_query);
942
-    }
943
-
944
-
945
-    /**
946
-     * @throws EE_Error
947
-     * @since 4.4.0
948
-     * @deprecated
949
-     */
950
-    public static function posts_fields_sql_for_orderby($orderby_params = [])
951
-    {
952
-        EED_Events_Archive::_doing_it_wrong_notice(__FUNCTION__);
953
-        return EEH_Event_Query::posts_fields_sql_for_orderby($orderby_params);
954
-    }
955
-
956
-
957
-    /**
958
-     * @throws EE_Error
959
-     * @since 4.4.0
960
-     * @deprecated
961
-     */
962
-    public function posts_join($SQL, WP_Query $wp_query)
963
-    {
964
-        EED_Events_Archive::_doing_it_wrong_notice(__FUNCTION__);
965
-        return EEH_Event_Query::posts_join($SQL, $wp_query);
966
-    }
967
-
968
-
969
-    /**
970
-     * @deprecated
971
-     * @since 4.4.0
972
-     */
973
-    public static function posts_join_sql_for_terms($join_terms = null)
974
-    {
975
-        EED_Events_Archive::_doing_it_wrong_notice(__FUNCTION__);
976
-        return EEH_Event_Query::posts_join_sql_for_terms($join_terms);
977
-    }
978
-
979
-
980
-    /**
981
-     * @throws EE_Error
982
-     * @since 4.4.0
983
-     * @deprecated
984
-     */
985
-    public static function posts_join_for_orderby($orderby_params = [])
986
-    {
987
-        EED_Events_Archive::_doing_it_wrong_notice(__FUNCTION__);
988
-        return EEH_Event_Query::posts_join_for_orderby($orderby_params);
989
-    }
990
-
991
-
992
-    /**
993
-     * @throws EE_Error
994
-     * @since 4.4.0
995
-     * @deprecated
996
-     */
997
-    public function posts_where($SQL, WP_Query $wp_query)
998
-    {
999
-        EED_Events_Archive::_doing_it_wrong_notice(__FUNCTION__);
1000
-        return EEH_Event_Query::posts_where($SQL, $wp_query);
1001
-    }
1002
-
1003
-
1004
-    /**
1005
-     * @throws EE_Error
1006
-     * @since 4.4.0
1007
-     * @deprecated
1008
-     */
1009
-    public static function posts_where_sql_for_show_expired($show_expired = false)
1010
-    {
1011
-        EED_Events_Archive::_doing_it_wrong_notice(__FUNCTION__);
1012
-        return EEH_Event_Query::posts_where_sql_for_show_expired($show_expired);
1013
-    }
1014
-
1015
-
1016
-    /**
1017
-     * @deprecated
1018
-     * @since 4.4.0
1019
-     */
1020
-    public static function posts_where_sql_for_event_category_slug($event_category_slug = null)
1021
-    {
1022
-        EED_Events_Archive::_doing_it_wrong_notice(__FUNCTION__);
1023
-        return EEH_Event_Query::posts_where_sql_for_event_category_slug($event_category_slug);
1024
-    }
1025
-
1026
-
1027
-    /**
1028
-     * @throws EE_Error
1029
-     * @since 4.4.0
1030
-     * @deprecated
1031
-     */
1032
-    public static function posts_where_sql_for_event_list_month($month = null)
1033
-    {
1034
-        EED_Events_Archive::_doing_it_wrong_notice(__FUNCTION__);
1035
-        return EEH_Event_Query::posts_where_sql_for_event_list_month($month);
1036
-    }
1037
-
1038
-
1039
-    /**
1040
-     * @throws EE_Error
1041
-     * @since 4.4.0
1042
-     * @deprecated
1043
-     */
1044
-    public function posts_orderby($SQL, WP_Query $wp_query)
1045
-    {
1046
-        EED_Events_Archive::_doing_it_wrong_notice(__FUNCTION__);
1047
-        return EEH_Event_Query::posts_orderby($SQL, $wp_query);
1048
-    }
1049
-
1050
-
1051
-    /**
1052
-     * @throws EE_Error
1053
-     * @since 4.4.0
1054
-     * @deprecated
1055
-     */
1056
-    public static function posts_orderby_sql($orderby_params = [], $sort = 'ASC')
1057
-    {
1058
-        EED_Events_Archive::_doing_it_wrong_notice(__FUNCTION__);
1059
-        return EEH_Event_Query::posts_orderby_sql($orderby_params, $sort);
1060
-    }
20
+	const EVENT_DETAILS_PRIORITY   = 100;
21
+
22
+	const EVENT_DATETIMES_PRIORITY = 110;
23
+
24
+	const EVENT_TICKETS_PRIORITY   = 120;
25
+
26
+	const EVENT_VENUES_PRIORITY    = 130;
27
+
28
+
29
+	public static $espresso_event_list_ID    = 0;
30
+
31
+	public static $espresso_grid_event_lists = [];
32
+
33
+	/**
34
+	 * @type bool $using_get_the_excerpt
35
+	 */
36
+	protected static $using_get_the_excerpt = false;
37
+
38
+	/**
39
+	 * Used to flag when the event list is being called from an external iframe.
40
+	 *
41
+	 * @var bool $iframe
42
+	 */
43
+	protected static $iframe = false;
44
+
45
+	/**
46
+	 * @var EventListIframeEmbedButton $_iframe_embed_button
47
+	 */
48
+	private static $_iframe_embed_button;
49
+
50
+	/**
51
+	 * @type EE_Template_Part_Manager $template_parts
52
+	 */
53
+	protected $template_parts;
54
+
55
+
56
+	/**
57
+	 * @return EED_Events_Archive
58
+	 * @throws EE_Error
59
+	 * @throws ReflectionException
60
+	 */
61
+	public static function instance()
62
+	{
63
+		return parent::get_instance(__CLASS__);
64
+	}
65
+
66
+
67
+	/**
68
+	 * for hooking into EE Core, other modules, etc
69
+	 *
70
+	 * @return void
71
+	 * @throws InvalidArgumentException
72
+	 * @throws InvalidDataTypeException
73
+	 * @throws InvalidInterfaceException
74
+	 */
75
+	public static function set_hooks()
76
+	{
77
+		/** @var CustomPostTypeDefinitions $custom_post_type_definitions */
78
+		$custom_post_type_definitions = LoaderFactory::getLoader()->getShared(CustomPostTypeDefinitions::class);
79
+		$custom_post_types            = $custom_post_type_definitions->getDefinitions();
80
+		EE_Config::register_route(
81
+			$custom_post_types[EspressoPostType::EVENTS]['plural_slug'],
82
+			'Events_Archive',
83
+			'run'
84
+		);
85
+		EE_Config::register_route(
86
+			'event_list',
87
+			'Events_Archive',
88
+			'event_list'
89
+		);
90
+		EE_Config::register_route(
91
+			'iframe',
92
+			'Events_Archive',
93
+			'event_list_iframe',
94
+			'event_list'
95
+		);
96
+		add_action('wp_loaded', ['EED_Events_Archive', 'set_definitions'], 2);
97
+	}
98
+
99
+
100
+	/**
101
+	 * for hooking into EE Admin Core, other modules, etc
102
+	 *
103
+	 * @return void
104
+	 */
105
+	public static function set_hooks_admin()
106
+	{
107
+		add_action('wp_loaded', ['EED_Events_Archive', 'set_definitions'], 2);
108
+		// hook into the end of the \EE_Admin_Page::_load_page_dependencies()
109
+		// to load assets for "espresso_events" page on the "default" route (action)
110
+		add_action(
111
+			'FHEE__EE_Admin_Page___load_page_dependencies__after_load__espresso_events__default',
112
+			['EED_Events_Archive', 'event_list_iframe_embed_button'],
113
+			10
114
+		);
115
+	}
116
+
117
+
118
+	/**
119
+	 * @return void
120
+	 */
121
+	public static function set_definitions()
122
+	{
123
+		define('EVENTS_ARCHIVE_ASSETS_URL', plugin_dir_url(__FILE__) . 'assets/');
124
+		define('EVENTS_ARCHIVE_TEMPLATES_PATH', str_replace('\\', '/', plugin_dir_path(__FILE__)) . 'templates/');
125
+	}
126
+
127
+
128
+	/**
129
+	 * set up EE_Events_Archive_Config
130
+	 */
131
+	protected function set_config()
132
+	{
133
+		$this->set_config_section('template_settings');
134
+		$this->set_config_class('EE_Events_Archive_Config');
135
+		$this->set_config_name('EED_Events_Archive');
136
+	}
137
+
138
+
139
+	/**
140
+	 * @return EventListIframeEmbedButton
141
+	 */
142
+	public static function get_iframe_embed_button()
143
+	{
144
+		if (! self::$_iframe_embed_button instanceof EventListIframeEmbedButton) {
145
+			self::$_iframe_embed_button = new EventListIframeEmbedButton();
146
+		}
147
+		return self::$_iframe_embed_button;
148
+	}
149
+
150
+
151
+	/**
152
+	 * @return void
153
+	 */
154
+	public static function event_list_iframe_embed_button()
155
+	{
156
+		$iframe_embed_button = EED_Events_Archive::get_iframe_embed_button();
157
+		$iframe_embed_button->addEmbedButton();
158
+	}
159
+
160
+
161
+	/**
162
+	 * @param EE_Events_Archive_Config|null $config
163
+	 * @return EE_Template_Part_Manager
164
+	 * @throws EE_Error
165
+	 */
166
+	public function initialize_template_parts(EE_Events_Archive_Config $config = null)
167
+	{
168
+		$config = $config instanceof EE_Events_Archive_Config ? $config : $this->config();
169
+		EEH_Autoloader::register_template_part_autoloaders();
170
+		$template_parts = new EE_Template_Part_Manager();
171
+		$template_parts->add_template_part(
172
+			'tickets',
173
+			esc_html__('Ticket Selector', 'event_espresso'),
174
+			'content-espresso_events-tickets.php',
175
+			$config->display_order_tickets
176
+		);
177
+		$template_parts->add_template_part(
178
+			'datetimes',
179
+			esc_html__('Dates and Times', 'event_espresso'),
180
+			'content-espresso_events-datetimes.php',
181
+			$config->display_order_datetimes
182
+		);
183
+		$template_parts->add_template_part(
184
+			'event',
185
+			esc_html__('Event Description', 'event_espresso'),
186
+			'content-espresso_events-details.php',
187
+			$config->display_order_event
188
+		);
189
+		$template_parts->add_template_part(
190
+			'venue',
191
+			esc_html__('Venue Information', 'event_espresso'),
192
+			'content-espresso_events-venues.php',
193
+			$config->display_order_venue
194
+		);
195
+		do_action('AHEE__EED_Event_Archive__initialize_template_parts', $template_parts);
196
+		return $template_parts;
197
+	}
198
+
199
+
200
+	/**
201
+	 * initial module setup
202
+	 * this gets called by the EE_Front_Controller if the module route is found in the incoming request
203
+	 *
204
+	 * @param WP $WP
205
+	 * @return void
206
+	 */
207
+	public function run($WP)
208
+	{
209
+		do_action('AHEE__EED_Events_Archive__before_run');
210
+		// ensure valid EE_Events_Archive_Config() object exists
211
+		$this->set_config();
212
+		/** @type EE_Events_Archive_Config $config */
213
+		$config = $this->config();
214
+		// load other required components
215
+		$this->load_event_list_assets();
216
+		// filter the WP posts_join, posts_where, and posts_orderby SQL clauses
217
+		// add query filters
218
+		EEH_Event_Query::add_query_filters();
219
+		// set params that will get used by the filters
220
+		EEH_Event_Query::set_query_params(
221
+			'',                             // month
222
+			'',                             // category
223
+			$config->display_expired_events // show_expired
224
+		);
225
+		// check what template is loaded
226
+		add_filter('template_include', [$this, 'template_include'], 999, 1);
227
+	}
228
+
229
+
230
+	/**
231
+	 * most likely called by the ESPRESSO_EVENTS shortcode which uses this module to do some of it's lifting
232
+	 *
233
+	 * @return void
234
+	 */
235
+	public function event_list()
236
+	{
237
+		// ensure valid EE_Events_Archive_Config() object exists
238
+		$this->set_config();
239
+		// load other required components
240
+		$this->load_event_list_assets();
241
+	}
242
+
243
+
244
+	/**
245
+	 * @return void
246
+	 * @throws EE_Error
247
+	 * @throws ReflectionException
248
+	 */
249
+	public function event_list_iframe()
250
+	{
251
+		EED_Events_Archive::$iframe = true;
252
+		$event_list_iframe          = new EventsArchiveIframe($this);
253
+		$event_list_iframe->display();
254
+	}
255
+
256
+
257
+	/**
258
+	 * @return bool
259
+	 */
260
+	public static function is_iframe()
261
+	{
262
+		return EED_Events_Archive::$iframe;
263
+	}
264
+
265
+
266
+	/**
267
+	 * @return string
268
+	 */
269
+	public static function link_target()
270
+	{
271
+		return EED_Events_Archive::$iframe ? ' target="_blank"' : '';
272
+	}
273
+
274
+
275
+	/**
276
+	 * @param string $template
277
+	 * @return string
278
+	 * @throws EE_Error
279
+	 * @throws ReflectionException
280
+	 */
281
+	public function template_include($template = '')
282
+	{
283
+		// don't add content filter for dedicated EE child themes or private posts
284
+		if (! EEH_Template::is_espresso_theme()) {
285
+			/** @type EE_Events_Archive_Config $config */
286
+			$config = $this->config();
287
+			// add status banner ?
288
+			if ($config->display_status_banner) {
289
+				add_filter('the_title', ['EED_Events_Archive', 'the_title'], 100, 2);
290
+			}
291
+			// if NOT a custom template
292
+			if (
293
+				apply_filters('FHEE__EED_Event_Archive__template_include__allow_custom_selected_template', false)
294
+				|| EE_Registry::instance()
295
+							  ->load_core('Front_Controller')
296
+							  ->get_selected_template() !== 'archive-espresso_events.php'
297
+			) {
298
+				add_filter('FHEE__EED_Events_Archive__template_include__events_list_active', '__return_true');
299
+				// load functions.php file for the theme (loaded by WP if using child theme)
300
+				EEH_Template::load_espresso_theme_functions();
301
+				EED_Events_Archive::addEventDetailsFilters();
302
+				// don't display entry meta because the existing theme will take care of that
303
+				add_filter('FHEE__content_espresso_events_details_template__display_entry_meta', '__return_false');
304
+			}
305
+		}
306
+		return $template;
307
+	}
308
+
309
+
310
+	/**
311
+	 * kinda hacky, but if a theme is using get_the_excerpt(),
312
+	 * then we need to remove our filters on the_content()
313
+	 *
314
+	 * @param string $excerpt
315
+	 * @return string
316
+	 * @throws EE_Error
317
+	 * @throws ReflectionException
318
+	 */
319
+	public static function get_the_excerpt($excerpt = '')
320
+	{
321
+		if (post_password_required()) {
322
+			return $excerpt;
323
+		}
324
+		if (apply_filters('FHEE__EED_Events_Archive__get_the_excerpt__theme_uses_get_the_excerpt', false)) {
325
+			EED_Events_Archive::removeEventDetailsFilters(false);
326
+			$excerpt = EED_Events_Archive::event_details($excerpt);
327
+		} else {
328
+			EED_Events_Archive::$using_get_the_excerpt = true;
329
+			add_filter('wp_trim_excerpt', ['EED_Events_Archive', 'end_get_the_excerpt'], 999, 1);
330
+		}
331
+		return $excerpt;
332
+	}
333
+
334
+
335
+	/**
336
+	 * @param string $text
337
+	 * @return string
338
+	 */
339
+	public static function end_get_the_excerpt($text = '')
340
+	{
341
+		EED_Events_Archive::$using_get_the_excerpt = false;
342
+		return $text;
343
+	}
344
+
345
+
346
+	/**
347
+	 * @param string $title
348
+	 * @param string $id
349
+	 * @return string
350
+	 * @throws EE_Error
351
+	 * @throws ReflectionException
352
+	 */
353
+	public static function the_title($title = '', $id = '')
354
+	{
355
+		global $post;
356
+		if ($post instanceof WP_Post) {
357
+			return (EED_Events_Archive::isBlockTheme() || in_the_loop()) && $post->ID == $id
358
+				? espresso_event_status_banner($post->ID) . $title
359
+				: $title;
360
+		}
361
+		return $title;
362
+	}
363
+
364
+
365
+	/**
366
+	 * @param string $content
367
+	 * @return string
368
+	 * @throws EE_Error
369
+	 * @throws ReflectionException
370
+	 */
371
+	public static function event_details($content)
372
+	{
373
+		global $post;
374
+		static $current_post_ID = 0;
375
+		if (
376
+			$current_post_ID !== $post->ID
377
+			&& $post->post_type === EspressoPostType::EVENTS
378
+			&& ! EED_Events_Archive::$using_get_the_excerpt
379
+			&& ! post_password_required()
380
+			&& (
381
+				apply_filters('FHEE__EES_Espresso_Events__process_shortcode__true', false)
382
+				|| ! apply_filters('FHEE__content_espresso_events__template_loaded', false)
383
+			)
384
+		) {
385
+			// Set current post ID to prevent showing content twice, but only if headers have definitely been sent.
386
+			// Reason being is that some plugins, like Yoast, need to run through a copy of the loop early
387
+			// BEFORE headers are sent in order to examine the post content and generate content for the HTML header.
388
+			// We want to allow those plugins to still do their thing and have access to our content, but depending on
389
+			// how your event content is being displayed (shortcode, CPT route, etc), this filter can get applied twice,
390
+			// so the following allows this filter to be applied multiple times, but only once for real
391
+			$current_post_ID = did_action('loop_start') ? $post->ID : 0;
392
+			if (EE_Registry::instance()->CFG->template_settings->EED_Events_Archive->use_sortable_display_order) {
393
+				$content = EED_Events_Archive::use_sortable_display_order();
394
+			} else {
395
+				$content = EED_Events_Archive::use_filterable_display_order();
396
+			}
397
+		}
398
+		return $content;
399
+	}
400
+
401
+
402
+	/**
403
+	 * @return string
404
+	 * @throws EE_Error
405
+	 * @throws ReflectionException
406
+	 */
407
+	protected static function use_sortable_display_order()
408
+	{
409
+		// no further password checks required atm
410
+		add_filter('FHEE__EED_Events_Archive__event_details__no_post_password_required', '__return_true');
411
+		// remove this callback from being applied to the_content()
412
+		EED_Events_Archive::removeEventDetailsFilters();
413
+		// now add additional content depending on whether event is using the_excerpt() or the_content()
414
+		EED_Events_Archive::instance()->template_parts = EED_Events_Archive::instance()->initialize_template_parts();
415
+		$content = EEH_Template::locate_template('content-espresso_events-details.php');
416
+		$content = EED_Events_Archive::instance()->template_parts->apply_template_part_filters($content);
417
+		// re-add our main filters (or else the next event won't have them)
418
+		EED_Events_Archive::addEventDetailsFilters();
419
+		remove_filter(
420
+			'FHEE__EED_Events_Archive__event_details__no_post_password_required',
421
+			'__return_true'
422
+		);
423
+		return $content;
424
+	}
425
+
426
+
427
+	/**
428
+	 * @return string
429
+	 */
430
+	protected static function use_filterable_display_order()
431
+	{
432
+		// remove this callback from being applied to the_content()
433
+		EED_Events_Archive::removeEventDetailsFilters();
434
+		// now add additional content depending on whether event is using the_excerpt() or the_content()
435
+		EED_Events_Archive::_add_additional_excerpt_filters();
436
+		EED_Events_Archive::_add_additional_content_filters();
437
+		do_action('AHEE__EED_Events_Archive__use_filterable_display_order__after_add_filters');
438
+		// now load our template
439
+		$content = EEH_Template::locate_template('content-espresso_events-details.php');
440
+		// re-add our main filters (or else the next event won't have them)
441
+		EED_Events_Archive::addEventDetailsFilters();
442
+		// but remove the other filters so that they don't get applied to the next post
443
+		EED_Events_Archive::_remove_additional_events_archive_filters();
444
+		do_action('AHEE__EED_Events_Archive__use_filterable_display_order__after_remove_filters');
445
+		// we're not returning the $content directly because the template we are loading uses the_content (or the_excerpt)
446
+		// return ! empty( $template ) ? $template : $content;
447
+		return $content;
448
+	}
449
+
450
+
451
+	/**
452
+	 * adds datetimes ABOVE content
453
+	 *
454
+	 * @param string $content
455
+	 * @return string
456
+	 */
457
+	public static function event_datetimes($content)
458
+	{
459
+		if (post_password_required()) {
460
+			return $content;
461
+		}
462
+		return EEH_Template::locate_template('content-espresso_events-datetimes.php') . $content;
463
+	}
464
+
465
+
466
+	/**
467
+	 * adds tickets ABOVE content (which includes datetimes)
468
+	 *
469
+	 * @param string $content
470
+	 * @return string
471
+	 */
472
+	public static function event_tickets($content)
473
+	{
474
+		if (post_password_required()) {
475
+			return $content;
476
+		}
477
+		return EEH_Template::locate_template('content-espresso_events-tickets.php') . $content;
478
+	}
479
+
480
+
481
+	/**
482
+	 * adds venues BELOW content
483
+	 *
484
+	 * @param string $content
485
+	 * @return string
486
+	 */
487
+	public static function event_venue($content)
488
+	{
489
+		return EED_Events_Archive::event_venues($content);
490
+	}
491
+
492
+
493
+	/**
494
+	 * adds venues BELOW content
495
+	 *
496
+	 * @param string $content
497
+	 * @return string
498
+	 */
499
+	public static function event_venues($content)
500
+	{
501
+		if (post_password_required()) {
502
+			return $content;
503
+		}
504
+		return $content . EEH_Template::locate_template('content-espresso_events-venues.php');
505
+	}
506
+
507
+
508
+	/**
509
+	 * @return bool
510
+	 * @since 4.10.33.p
511
+	 */
512
+	public static function isBlockTheme()
513
+	{
514
+		return function_exists('wp_is_block_theme') && wp_is_block_theme();
515
+	}
516
+
517
+
518
+	/**
519
+	 * @return string
520
+	 * @since 4.10.33.p
521
+	 */
522
+	public static function getExcerptFilter()
523
+	{
524
+		static $excerpt_filter = null;
525
+		if ($excerpt_filter === null) {
526
+			$excerpt_filter = EED_Events_Archive::isBlockTheme() ? 'get_the_excerpt' : 'the_excerpt';
527
+		}
528
+		return $excerpt_filter;
529
+	}
530
+
531
+
532
+	/**
533
+	 * @since 4.10.33.p
534
+	 */
535
+	protected static function addEventDetailsFilters()
536
+	{
537
+		add_filter(
538
+			'the_content',
539
+			['EED_Events_Archive', 'event_details'],
540
+			EED_Events_Archive::EVENT_DETAILS_PRIORITY
541
+		);
542
+		// but because we don't know if the theme is using the_excerpt()
543
+		add_filter(
544
+			EED_Events_Archive::getExcerptFilter(),
545
+			['EED_Events_Archive', 'event_details'],
546
+			EED_Events_Archive::EVENT_DETAILS_PRIORITY
547
+		);
548
+		// and just in case they are running get_the_excerpt() which DESTROYS things
549
+		add_filter('get_the_excerpt', ['EED_Events_Archive', 'get_the_excerpt'], 1, 1);
550
+	}
551
+
552
+
553
+	/**
554
+	 * @param bool $and_get_the_excerpt
555
+	 * @since 4.10.33.p
556
+	 */
557
+	protected static function removeEventDetailsFilters($and_get_the_excerpt = true)
558
+	{
559
+		// we need to first remove all callbacks from being applied to the_content()
560
+		// (otherwise it will recurse and blow up the interweb)
561
+		remove_filter(
562
+			'the_content',
563
+			['EED_Events_Archive', 'event_details'],
564
+			EED_Events_Archive::EVENT_DETAILS_PRIORITY
565
+		);
566
+		remove_filter(
567
+			EED_Events_Archive::getExcerptFilter(),
568
+			['EED_Events_Archive', 'event_details'],
569
+			EED_Events_Archive::EVENT_DETAILS_PRIORITY
570
+		);
571
+		if ($and_get_the_excerpt) {
572
+			remove_filter('get_the_excerpt', ['EED_Events_Archive', 'get_the_excerpt'], 1);
573
+		}
574
+	}
575
+
576
+
577
+	/**
578
+	 * @return void
579
+	 */
580
+	private static function _add_additional_excerpt_filters()
581
+	{
582
+		add_filter(
583
+			EED_Events_Archive::getExcerptFilter(),
584
+			['EED_Events_Archive', 'event_datetimes'],
585
+			EED_Events_Archive::EVENT_DATETIMES_PRIORITY
586
+		);
587
+		add_filter(
588
+			EED_Events_Archive::getExcerptFilter(),
589
+			['EED_Events_Archive', 'event_tickets'],
590
+			EED_Events_Archive::EVENT_TICKETS_PRIORITY
591
+		);
592
+		add_filter(
593
+			EED_Events_Archive::getExcerptFilter(),
594
+			['EED_Events_Archive', 'event_venues'],
595
+			EED_Events_Archive::EVENT_VENUES_PRIORITY
596
+		);
597
+	}
598
+
599
+
600
+	/**
601
+	 * @return void
602
+	 */
603
+	private static function _add_additional_content_filters()
604
+	{
605
+		add_filter(
606
+			'the_content',
607
+			['EED_Events_Archive', 'event_datetimes'],
608
+			EED_Events_Archive::EVENT_DATETIMES_PRIORITY
609
+		);
610
+		add_filter(
611
+			'the_content',
612
+			['EED_Events_Archive', 'event_tickets'],
613
+			EED_Events_Archive::EVENT_TICKETS_PRIORITY
614
+		);
615
+		add_filter(
616
+			'the_content',
617
+			['EED_Events_Archive', 'event_venues'],
618
+			EED_Events_Archive::EVENT_VENUES_PRIORITY
619
+		);
620
+	}
621
+
622
+
623
+	/**
624
+	 * @return void
625
+	 */
626
+	private static function _remove_additional_events_archive_filters()
627
+	{
628
+		remove_filter(
629
+			EED_Events_Archive::getExcerptFilter(),
630
+			['EED_Events_Archive', 'event_datetimes'],
631
+			EED_Events_Archive::EVENT_DATETIMES_PRIORITY
632
+		);
633
+		remove_filter(
634
+			EED_Events_Archive::getExcerptFilter(),
635
+			['EED_Events_Archive', 'event_tickets'],
636
+			EED_Events_Archive::EVENT_TICKETS_PRIORITY
637
+		);
638
+		remove_filter(
639
+			EED_Events_Archive::getExcerptFilter(),
640
+			['EED_Events_Archive', 'event_venues'],
641
+			EED_Events_Archive::EVENT_VENUES_PRIORITY
642
+		);
643
+		remove_filter(
644
+			'the_content',
645
+			['EED_Events_Archive', 'event_datetimes'],
646
+			EED_Events_Archive::EVENT_DATETIMES_PRIORITY
647
+		);
648
+		remove_filter(
649
+			'the_content',
650
+			['EED_Events_Archive', 'event_tickets'],
651
+			EED_Events_Archive::EVENT_TICKETS_PRIORITY
652
+		);
653
+		remove_filter(
654
+			'the_content',
655
+			['EED_Events_Archive', 'event_venues'],
656
+			EED_Events_Archive::EVENT_VENUES_PRIORITY
657
+		);
658
+	}
659
+
660
+
661
+	/**
662
+	 * @return void
663
+	 */
664
+	public static function remove_all_events_archive_filters()
665
+	{
666
+		// remove_filter( 'get_the_excerpt', array( 'EED_Events_Archive', 'get_the_excerpt' ), 1 );
667
+		remove_filter('the_title', ['EED_Events_Archive', 'the_title'], 1);
668
+		remove_filter(
669
+			EED_Events_Archive::getExcerptFilter(),
670
+			['EED_Events_Archive', 'event_details'],
671
+			EED_Events_Archive::EVENT_DETAILS_PRIORITY
672
+		);
673
+		remove_filter(
674
+			EED_Events_Archive::getExcerptFilter(),
675
+			['EED_Events_Archive', 'event_datetimes'],
676
+			EED_Events_Archive::EVENT_DATETIMES_PRIORITY
677
+		);
678
+		remove_filter(
679
+			EED_Events_Archive::getExcerptFilter(),
680
+			['EED_Events_Archive', 'event_tickets'],
681
+			EED_Events_Archive::EVENT_TICKETS_PRIORITY
682
+		);
683
+		remove_filter(
684
+			EED_Events_Archive::getExcerptFilter(),
685
+			['EED_Events_Archive', 'event_venues'],
686
+			EED_Events_Archive::EVENT_VENUES_PRIORITY
687
+		);
688
+		remove_filter(
689
+			'the_content',
690
+			['EED_Events_Archive', 'event_details'],
691
+			EED_Events_Archive::EVENT_DETAILS_PRIORITY
692
+		);
693
+		remove_filter(
694
+			'the_content',
695
+			['EED_Events_Archive', 'event_datetimes'],
696
+			EED_Events_Archive::EVENT_DATETIMES_PRIORITY
697
+		);
698
+		remove_filter(
699
+			'the_content',
700
+			['EED_Events_Archive', 'event_tickets'],
701
+			EED_Events_Archive::EVENT_TICKETS_PRIORITY
702
+		);
703
+		remove_filter(
704
+			'the_content',
705
+			['EED_Events_Archive', 'event_venues'],
706
+			EED_Events_Archive::EVENT_VENUES_PRIORITY
707
+		);
708
+		// don't display entry meta because the existing theme will take care of that
709
+		remove_filter(
710
+			'FHEE__content_espresso_events_details_template__display_entry_meta',
711
+			'__return_false'
712
+		);
713
+	}
714
+
715
+
716
+	/**
717
+	 * @return void
718
+	 */
719
+	public function load_event_list_assets()
720
+	{
721
+		do_action('AHEE__EED_Events_Archive__before_load_assets');
722
+		add_filter('FHEE_load_EE_Session', '__return_true');
723
+		add_filter('FHEE__EED_Ticket_Selector__load_tckt_slctr_assets', '__return_true');
724
+		add_action('wp_enqueue_scripts', [$this, 'wp_enqueue_scripts'], 10);
725
+		if (EE_Registry::instance()->CFG->map_settings->use_google_maps) {
726
+			add_action('wp_enqueue_scripts', ['EEH_Maps', 'espresso_google_map_js'], 11);
727
+		}
728
+	}
729
+
730
+
731
+	/**
732
+	 * @return void
733
+	 * @throws EE_Error
734
+	 */
735
+	public function wp_enqueue_scripts()
736
+	{
737
+		// get some style
738
+		if (apply_filters('FHEE_enable_default_espresso_css', false)) {
739
+			// first check uploads folder
740
+			if (EEH_File::is_readable(get_stylesheet_directory() . $this->theme . '/style.css')) {
741
+				wp_register_style(
742
+					$this->theme,
743
+					get_stylesheet_directory_uri() . $this->theme . '/style.css',
744
+					['dashicons', 'espresso_default']
745
+				);
746
+			}
747
+			wp_enqueue_style($this->theme);
748
+		}
749
+	}
750
+
751
+
752
+	/**
753
+	 * @static
754
+	 * @return void
755
+	 */
756
+	public static function template_settings_form()
757
+	{
758
+		$template_settings                     = EE_Registry::instance()->CFG->template_settings;
759
+		$template_settings->EED_Events_Archive = isset($template_settings->EED_Events_Archive)
760
+			? $template_settings->EED_Events_Archive : new EE_Events_Archive_Config();
761
+		$template_settings->EED_Events_Archive = apply_filters(
762
+			'FHEE__EED_Events_Archive__template_settings_form__event_list_config',
763
+			$template_settings->EED_Events_Archive
764
+		);
765
+		$events_archive_settings               = [
766
+			'display_status_banner'   => 0,
767
+			'display_description'     => 1,
768
+			'display_ticket_selector' => 0,
769
+			'display_datetimes'       => 1,
770
+			'display_venue'           => 0,
771
+			'display_expired_events'  => 0,
772
+		];
773
+		$events_archive_settings               = array_merge(
774
+			$events_archive_settings,
775
+			(array) $template_settings->EED_Events_Archive
776
+		);
777
+		EEH_Template::display_template(
778
+			EVENTS_ARCHIVE_TEMPLATES_PATH . 'admin-event-list-settings.template.php',
779
+			$events_archive_settings
780
+		);
781
+	}
782
+
783
+
784
+	/**
785
+	 * @param EE_Template_Config $CFG
786
+	 * @param array              $REQ
787
+	 * @return EE_Template_Config
788
+	 */
789
+	public static function update_template_settings(EE_Template_Config $CFG, array $REQ): EE_Template_Config
790
+	{
791
+		$CFG->EED_Events_Archive = new EE_Events_Archive_Config();
792
+		// unless we are resetting the config...
793
+		if (
794
+			! isset($REQ['EED_Events_Archive_reset_event_list_settings'])
795
+			|| absint($REQ['EED_Events_Archive_reset_event_list_settings']) !== 1
796
+		) {
797
+			$CFG->EED_Events_Archive->display_status_banner   = isset($REQ['EED_Events_Archive_display_status_banner'])
798
+				? absint($REQ['EED_Events_Archive_display_status_banner'])
799
+				: 0;
800
+			$CFG->EED_Events_Archive->display_description     = isset($REQ['EED_Events_Archive_display_description'])
801
+				? absint($REQ['EED_Events_Archive_display_description'])
802
+				: 1;
803
+			$CFG->EED_Events_Archive->display_ticket_selector = isset($REQ['EED_Events_Archive_display_ticket_selector'])
804
+				? absint($REQ['EED_Events_Archive_display_ticket_selector'])
805
+				: 0;
806
+			$CFG->EED_Events_Archive->display_datetimes       = isset($REQ['EED_Events_Archive_display_datetimes'])
807
+				? absint($REQ['EED_Events_Archive_display_datetimes'])
808
+				: 1;
809
+			$CFG->EED_Events_Archive->display_venue           = isset($REQ['EED_Events_Archive_display_venue'])
810
+				? absint($REQ['EED_Events_Archive_display_venue'])
811
+				: 0;
812
+			$CFG->EED_Events_Archive->display_expired_events  = isset($REQ['EED_Events_Archive_display_expired_events'])
813
+				? absint($REQ['EED_Events_Archive_display_expired_events'])
814
+				: 0;
815
+		}
816
+		return $CFG;
817
+	}
818
+
819
+
820
+	/**
821
+	 * @param string $extra_class
822
+	 * @return string
823
+	 */
824
+	public static function event_list_css($extra_class = '')
825
+	{
826
+		$event_list_css   = ! empty($extra_class) ? [$extra_class] : [];
827
+		$event_list_css[] = 'espresso-event-list-event';
828
+		return implode(' ', $event_list_css);
829
+	}
830
+
831
+
832
+	/**
833
+	 * @return array
834
+	 * @throws EE_Error
835
+	 * @throws ReflectionException
836
+	 */
837
+	public static function event_categories()
838
+	{
839
+		return EE_Registry::instance()->load_model('Term')->get_all_ee_categories();
840
+	}
841
+
842
+
843
+	/**
844
+	 * @param $value
845
+	 * @return bool
846
+	 */
847
+	public static function display_description($value)
848
+	{
849
+		$config              = EE_Registry::instance()->CFG->template_settings->EED_Events_Archive;
850
+		$display_description = isset($config->display_description) ? $config->display_description : 1;
851
+		return $display_description === $value;
852
+	}
853
+
854
+
855
+	/**
856
+	 * @return bool
857
+	 */
858
+	public static function display_ticket_selector()
859
+	{
860
+		$config = EE_Registry::instance()->CFG->template_settings->EED_Events_Archive;
861
+		return isset($config->display_ticket_selector) && $config->display_ticket_selector;
862
+	}
863
+
864
+
865
+	/**
866
+	 * @return bool
867
+	 * @throws EE_Error
868
+	 * @throws ReflectionException
869
+	 */
870
+	public static function display_venue()
871
+	{
872
+		$config = EE_Registry::instance()->CFG->template_settings->EED_Events_Archive;
873
+		return isset($config->display_venue) && $config->display_venue && EEH_Venue_View::venue_name();
874
+	}
875
+
876
+
877
+	/**
878
+	 * @return bool
879
+	 */
880
+	public static function display_datetimes()
881
+	{
882
+		$config = EE_Registry::instance()->CFG->template_settings->EED_Events_Archive;
883
+		return isset($config->display_datetimes) && $config->display_datetimes;
884
+	}
885
+
886
+
887
+	/**
888
+	 * @return string
889
+	 */
890
+	public static function event_list_title()
891
+	{
892
+		return apply_filters(
893
+			'FHEE__archive_espresso_events_template__upcoming_events_h1',
894
+			esc_html__('Upcoming Events', 'event_espresso')
895
+		);
896
+	}
897
+
898
+
899
+	// GRAVEYARD
900
+
901
+
902
+	/**
903
+	 * @since 4.4.0
904
+	 */
905
+	public static function _doing_it_wrong_notice($function = '')
906
+	{
907
+		EE_Error::doing_it_wrong(
908
+			__FUNCTION__,
909
+			sprintf(
910
+				esc_html__(
911
+					'EED_Events_Archive::%1$s was moved to EEH_Event_Query::%1$s:%2$sPlease update your existing code because the method it calls will be removed in version %3$s',
912
+					'event_espresso'
913
+				),
914
+				$function,
915
+				'<br />',
916
+				'4.6.0'
917
+			),
918
+			'4.4.0'
919
+		);
920
+	}
921
+
922
+
923
+	/**
924
+	 * @deprecated
925
+	 * @since 4.4.0
926
+	 */
927
+	public function get_post_data()
928
+	{
929
+		EEH_Event_Query::set_query_params();
930
+	}
931
+
932
+
933
+	/**
934
+	 * @throws EE_Error
935
+	 * @since 4.4.0
936
+	 * @deprecated
937
+	 */
938
+	public function posts_fields($SQL, WP_Query $wp_query)
939
+	{
940
+		EED_Events_Archive::_doing_it_wrong_notice(__FUNCTION__);
941
+		return EEH_Event_Query::posts_fields($SQL, $wp_query);
942
+	}
943
+
944
+
945
+	/**
946
+	 * @throws EE_Error
947
+	 * @since 4.4.0
948
+	 * @deprecated
949
+	 */
950
+	public static function posts_fields_sql_for_orderby($orderby_params = [])
951
+	{
952
+		EED_Events_Archive::_doing_it_wrong_notice(__FUNCTION__);
953
+		return EEH_Event_Query::posts_fields_sql_for_orderby($orderby_params);
954
+	}
955
+
956
+
957
+	/**
958
+	 * @throws EE_Error
959
+	 * @since 4.4.0
960
+	 * @deprecated
961
+	 */
962
+	public function posts_join($SQL, WP_Query $wp_query)
963
+	{
964
+		EED_Events_Archive::_doing_it_wrong_notice(__FUNCTION__);
965
+		return EEH_Event_Query::posts_join($SQL, $wp_query);
966
+	}
967
+
968
+
969
+	/**
970
+	 * @deprecated
971
+	 * @since 4.4.0
972
+	 */
973
+	public static function posts_join_sql_for_terms($join_terms = null)
974
+	{
975
+		EED_Events_Archive::_doing_it_wrong_notice(__FUNCTION__);
976
+		return EEH_Event_Query::posts_join_sql_for_terms($join_terms);
977
+	}
978
+
979
+
980
+	/**
981
+	 * @throws EE_Error
982
+	 * @since 4.4.0
983
+	 * @deprecated
984
+	 */
985
+	public static function posts_join_for_orderby($orderby_params = [])
986
+	{
987
+		EED_Events_Archive::_doing_it_wrong_notice(__FUNCTION__);
988
+		return EEH_Event_Query::posts_join_for_orderby($orderby_params);
989
+	}
990
+
991
+
992
+	/**
993
+	 * @throws EE_Error
994
+	 * @since 4.4.0
995
+	 * @deprecated
996
+	 */
997
+	public function posts_where($SQL, WP_Query $wp_query)
998
+	{
999
+		EED_Events_Archive::_doing_it_wrong_notice(__FUNCTION__);
1000
+		return EEH_Event_Query::posts_where($SQL, $wp_query);
1001
+	}
1002
+
1003
+
1004
+	/**
1005
+	 * @throws EE_Error
1006
+	 * @since 4.4.0
1007
+	 * @deprecated
1008
+	 */
1009
+	public static function posts_where_sql_for_show_expired($show_expired = false)
1010
+	{
1011
+		EED_Events_Archive::_doing_it_wrong_notice(__FUNCTION__);
1012
+		return EEH_Event_Query::posts_where_sql_for_show_expired($show_expired);
1013
+	}
1014
+
1015
+
1016
+	/**
1017
+	 * @deprecated
1018
+	 * @since 4.4.0
1019
+	 */
1020
+	public static function posts_where_sql_for_event_category_slug($event_category_slug = null)
1021
+	{
1022
+		EED_Events_Archive::_doing_it_wrong_notice(__FUNCTION__);
1023
+		return EEH_Event_Query::posts_where_sql_for_event_category_slug($event_category_slug);
1024
+	}
1025
+
1026
+
1027
+	/**
1028
+	 * @throws EE_Error
1029
+	 * @since 4.4.0
1030
+	 * @deprecated
1031
+	 */
1032
+	public static function posts_where_sql_for_event_list_month($month = null)
1033
+	{
1034
+		EED_Events_Archive::_doing_it_wrong_notice(__FUNCTION__);
1035
+		return EEH_Event_Query::posts_where_sql_for_event_list_month($month);
1036
+	}
1037
+
1038
+
1039
+	/**
1040
+	 * @throws EE_Error
1041
+	 * @since 4.4.0
1042
+	 * @deprecated
1043
+	 */
1044
+	public function posts_orderby($SQL, WP_Query $wp_query)
1045
+	{
1046
+		EED_Events_Archive::_doing_it_wrong_notice(__FUNCTION__);
1047
+		return EEH_Event_Query::posts_orderby($SQL, $wp_query);
1048
+	}
1049
+
1050
+
1051
+	/**
1052
+	 * @throws EE_Error
1053
+	 * @since 4.4.0
1054
+	 * @deprecated
1055
+	 */
1056
+	public static function posts_orderby_sql($orderby_params = [], $sort = 'ASC')
1057
+	{
1058
+		EED_Events_Archive::_doing_it_wrong_notice(__FUNCTION__);
1059
+		return EEH_Event_Query::posts_orderby_sql($orderby_params, $sort);
1060
+	}
1061 1061
 }
1062 1062
 
1063 1063
 
@@ -1066,9 +1066,9 @@  discard block
 block discarded – undo
1066 1066
  */
1067 1067
 function espresso_get_event_list_ID()
1068 1068
 {
1069
-    EED_Events_Archive::$espresso_event_list_ID++;
1070
-    EED_Events_Archive::$espresso_grid_event_lists[] = EED_Events_Archive::$espresso_event_list_ID;
1071
-    return EED_Events_Archive::$espresso_event_list_ID;
1069
+	EED_Events_Archive::$espresso_event_list_ID++;
1070
+	EED_Events_Archive::$espresso_grid_event_lists[] = EED_Events_Archive::$espresso_event_list_ID;
1071
+	return EED_Events_Archive::$espresso_event_list_ID;
1072 1072
 }
1073 1073
 
1074 1074
 
@@ -1077,7 +1077,7 @@  discard block
 block discarded – undo
1077 1077
  */
1078 1078
 function espresso_event_list_title()
1079 1079
 {
1080
-    return EED_Events_Archive::event_list_title();
1080
+	return EED_Events_Archive::event_list_title();
1081 1081
 }
1082 1082
 
1083 1083
 
@@ -1087,7 +1087,7 @@  discard block
 block discarded – undo
1087 1087
  */
1088 1088
 function espresso_event_list_css($extra_class = '')
1089 1089
 {
1090
-    return EED_Events_Archive::event_list_css($extra_class);
1090
+	return EED_Events_Archive::event_list_css($extra_class);
1091 1091
 }
1092 1092
 
1093 1093
 
@@ -1098,7 +1098,7 @@  discard block
 block discarded – undo
1098 1098
  */
1099 1099
 function espresso_get_event_categories()
1100 1100
 {
1101
-    return EED_Events_Archive::event_categories();
1101
+	return EED_Events_Archive::event_categories();
1102 1102
 }
1103 1103
 
1104 1104
 
@@ -1107,7 +1107,7 @@  discard block
 block discarded – undo
1107 1107
  */
1108 1108
 function espresso_display_full_description_in_event_list()
1109 1109
 {
1110
-    return EED_Events_Archive::display_description(2);
1110
+	return EED_Events_Archive::display_description(2);
1111 1111
 }
1112 1112
 
1113 1113
 
@@ -1116,7 +1116,7 @@  discard block
 block discarded – undo
1116 1116
  */
1117 1117
 function espresso_display_excerpt_in_event_list()
1118 1118
 {
1119
-    return EED_Events_Archive::display_description(1);
1119
+	return EED_Events_Archive::display_description(1);
1120 1120
 }
1121 1121
 
1122 1122
 
@@ -1125,7 +1125,7 @@  discard block
 block discarded – undo
1125 1125
  */
1126 1126
 function espresso_display_ticket_selector_in_event_list()
1127 1127
 {
1128
-    return EED_Events_Archive::display_ticket_selector();
1128
+	return EED_Events_Archive::display_ticket_selector();
1129 1129
 }
1130 1130
 
1131 1131
 
@@ -1136,7 +1136,7 @@  discard block
 block discarded – undo
1136 1136
  */
1137 1137
 function espresso_display_venue_in_event_list()
1138 1138
 {
1139
-    return EED_Events_Archive::display_venue();
1139
+	return EED_Events_Archive::display_venue();
1140 1140
 }
1141 1141
 
1142 1142
 
@@ -1145,5 +1145,5 @@  discard block
 block discarded – undo
1145 1145
  */
1146 1146
 function espresso_display_datetimes_in_event_list()
1147 1147
 {
1148
-    return EED_Events_Archive::display_datetimes();
1148
+	return EED_Events_Archive::display_datetimes();
1149 1149
 }
Please login to merge, or discard this patch.
modules/messages/EED_Messages.module.php 1 patch
Indentation   +1352 added lines, -1352 removed lines patch added patch discarded remove patch
@@ -17,1365 +17,1365 @@
 block discarded – undo
17 17
  */
18 18
 class EED_Messages extends EED_Module
19 19
 {
20
-    /**
21
-     * This holds the EE_messages controller
22
-     *
23
-     * @deprecated 4.9.0
24
-     * @var EE_messages $_EEMSG
25
-     */
26
-    protected static $_EEMSG;
27
-
28
-    /**
29
-     * @type EE_Message_Resource_Manager $_message_resource_manager
30
-     */
31
-    protected static $_message_resource_manager;
32
-
33
-    /**
34
-     * This holds the EE_Messages_Processor business class.
35
-     *
36
-     * @type EE_Messages_Processor
37
-     */
38
-    protected static $_MSG_PROCESSOR;
39
-
40
-    /**
41
-     * holds all the paths for various messages components.
42
-     * Utilized by autoloader registry
43
-     *
44
-     * @var array
45
-     */
46
-    protected static $_MSG_PATHS;
47
-
48
-
49
-    /**
50
-     * This will hold an array of messages template packs that are registered in the messages system.
51
-     * Format is:
52
-     * array(
53
-     *    'template_pack_dbref' => EE_Messages_Template_Pack (instance)
54
-     * )
55
-     *
56
-     * @var EE_Messages_Template_Pack[]
57
-     */
58
-    protected static $_TMP_PACKS = [];
59
-
60
-
61
-    /**
62
-     * @return EED_Messages|EED_Module
63
-     * @throws EE_Error
64
-     * @throws ReflectionException
65
-     */
66
-    public static function instance()
67
-    {
68
-        return parent::get_instance(__CLASS__);
69
-    }
70
-
71
-
72
-    /**
73
-     *  set_hooks - for hooking into EE Core, other modules, etc
74
-     *
75
-     * @return    void
76
-     * @since 4.5.0
77
-     */
78
-    public static function set_hooks()
79
-    {
80
-        // actions
81
-        add_action('AHEE__EE_Payment_Processor__update_txn_based_on_payment', ['EED_Messages', 'payment'], 10, 2);
82
-        add_action(
83
-            'AHEE__EE_Registration_Processor__trigger_registration_update_notifications',
84
-            ['EED_Messages', 'maybe_registration'],
85
-            10,
86
-            2
87
-        );
88
-        // filters
89
-        add_filter(
90
-            'FHEE__EE_Registration__receipt_url__receipt_url',
91
-            ['EED_Messages', 'registration_message_trigger_url'],
92
-            10,
93
-            4
94
-        );
95
-        add_filter(
96
-            'FHEE__EE_Registration__invoice_url__invoice_url',
97
-            ['EED_Messages', 'registration_message_trigger_url'],
98
-            10,
99
-            4
100
-        );
101
-        // register routes
102
-        self::_register_routes();
103
-    }
104
-
105
-
106
-    /**
107
-     *    set_hooks_admin - for hooking into EE Admin Core, other modules, etc
108
-     *
109
-     * @access    public
110
-     * @return    void
111
-     */
112
-    public static function set_hooks_admin()
113
-    {
114
-        // actions
115
-        add_action('AHEE__EE_Payment_Processor__update_txn_based_on_payment', ['EED_Messages', 'payment'], 10, 2);
116
-        add_action(
117
-            'AHEE__Transactions_Admin_Page___send_payment_reminder__process_admin_payment_reminder',
118
-            ['EED_Messages', 'payment_reminder'],
119
-            10
120
-        );
121
-        add_action(
122
-            'AHEE__EE_Registration_Processor__trigger_registration_update_notifications',
123
-            ['EED_Messages', 'maybe_registration'],
124
-            10,
125
-            3
126
-        );
127
-        add_action(
128
-            'AHEE__Extend_Registrations_Admin_Page___newsletter_selected_send__with_registrations',
129
-            ['EED_Messages', 'send_newsletter_message'],
130
-            10,
131
-            2
132
-        );
133
-        add_action(
134
-            'AHEE__EES_Espresso_Cancelled__process_shortcode__transaction',
135
-            ['EED_Messages', 'cancelled_registration'],
136
-            10
137
-        );
138
-        add_action(
139
-            'AHEE__EE_Admin_Page___process_admin_payment_notification',
140
-            ['EED_Messages', 'process_admin_payment'],
141
-            10,
142
-            1
143
-        );
144
-        // filters
145
-        add_filter(
146
-            'FHEE__EE_Admin_Page___process_resend_registration__success',
147
-            ['EED_Messages', 'process_resend'],
148
-            10,
149
-            2
150
-        );
151
-        add_filter(
152
-            'FHEE__EE_Registration__receipt_url__receipt_url',
153
-            ['EED_Messages', 'registration_message_trigger_url'],
154
-            10,
155
-            4
156
-        );
157
-        add_filter(
158
-            'FHEE__EE_Registration__invoice_url__invoice_url',
159
-            ['EED_Messages', 'registration_message_trigger_url'],
160
-            10,
161
-            4
162
-        );
163
-    }
164
-
165
-
166
-    /**
167
-     * All the message triggers done by route go in here.
168
-     *
169
-     * @return void
170
-     * @since 4.5.0
171
-     */
172
-    protected static function _register_routes()
173
-    {
174
-        EE_Config::register_route('msg_url_trigger', 'Messages', 'run');
175
-        EE_Config::register_route('msg_cron_trigger', 'Messages', 'execute_batch_request');
176
-        EE_Config::register_route('msg_browser_trigger', 'Messages', 'browser_trigger');
177
-        EE_Config::register_route('msg_browser_error_trigger', 'Messages', 'browser_error_trigger');
178
-        do_action('AHEE__EED_Messages___register_routes');
179
-    }
180
-
181
-
182
-    /**
183
-     * This is called when a browser display trigger is executed.
184
-     * The browser display trigger is typically used when a already generated message is displayed directly in the
185
-     * browser.
186
-     *
187
-     * @param WP $WP
188
-     * @throws EE_Error
189
-     * @throws InvalidArgumentException
190
-     * @throws ReflectionException
191
-     * @throws InvalidDataTypeException
192
-     * @throws InvalidInterfaceException
193
-     * @since 4.9.0
194
-     */
195
-    public function browser_trigger($WP)
196
-    {
197
-        // ensure controller is loaded
198
-        self::_load_controller();
199
-        $token = self::getRequest()->getRequestParam('token');
200
-        try {
201
-            $mtg = new EE_Message_Generated_From_Token($token, 'html', self::$_message_resource_manager);
202
-            self::$_MSG_PROCESSOR->generate_and_send_now($mtg);
203
-        } catch (EE_Error $e) {
204
-            $error_msg = esc_html__(
205
-                'Please note that a system message failed to send due to a technical issue.',
206
-                'event_espresso'
207
-            );
208
-            // add specific message for developers if WP_DEBUG in on
209
-            $error_msg .= '||' . $e->getMessage();
210
-            EE_Error::add_error($error_msg, __FILE__, __FUNCTION__, __LINE__);
211
-        }
212
-    }
213
-
214
-
215
-    /**
216
-     * This is called when a browser error trigger is executed.
217
-     * When triggered this will grab the EE_Message matching the token in the request and use that to get the error
218
-     * message and display it.
219
-     *
220
-     * @param $WP
221
-     * @throws EE_Error
222
-     * @throws InvalidArgumentException
223
-     * @throws InvalidDataTypeException
224
-     * @throws InvalidInterfaceException
225
-     * @throws ReflectionException
226
-     * @since 4.9.0
227
-     */
228
-    public function browser_error_trigger($WP)
229
-    {
230
-        $token = self::getRequest()->getRequestParam('token');
231
-        if ($token) {
232
-            $message = EEM_Message::instance()->get_one_by_token($token);
233
-            if ($message instanceof EE_Message) {
234
-                header('HTTP/1.1 200 OK');
235
-                $error_msg = nl2br($message->error_message());
236
-                ?>
20
+	/**
21
+	 * This holds the EE_messages controller
22
+	 *
23
+	 * @deprecated 4.9.0
24
+	 * @var EE_messages $_EEMSG
25
+	 */
26
+	protected static $_EEMSG;
27
+
28
+	/**
29
+	 * @type EE_Message_Resource_Manager $_message_resource_manager
30
+	 */
31
+	protected static $_message_resource_manager;
32
+
33
+	/**
34
+	 * This holds the EE_Messages_Processor business class.
35
+	 *
36
+	 * @type EE_Messages_Processor
37
+	 */
38
+	protected static $_MSG_PROCESSOR;
39
+
40
+	/**
41
+	 * holds all the paths for various messages components.
42
+	 * Utilized by autoloader registry
43
+	 *
44
+	 * @var array
45
+	 */
46
+	protected static $_MSG_PATHS;
47
+
48
+
49
+	/**
50
+	 * This will hold an array of messages template packs that are registered in the messages system.
51
+	 * Format is:
52
+	 * array(
53
+	 *    'template_pack_dbref' => EE_Messages_Template_Pack (instance)
54
+	 * )
55
+	 *
56
+	 * @var EE_Messages_Template_Pack[]
57
+	 */
58
+	protected static $_TMP_PACKS = [];
59
+
60
+
61
+	/**
62
+	 * @return EED_Messages|EED_Module
63
+	 * @throws EE_Error
64
+	 * @throws ReflectionException
65
+	 */
66
+	public static function instance()
67
+	{
68
+		return parent::get_instance(__CLASS__);
69
+	}
70
+
71
+
72
+	/**
73
+	 *  set_hooks - for hooking into EE Core, other modules, etc
74
+	 *
75
+	 * @return    void
76
+	 * @since 4.5.0
77
+	 */
78
+	public static function set_hooks()
79
+	{
80
+		// actions
81
+		add_action('AHEE__EE_Payment_Processor__update_txn_based_on_payment', ['EED_Messages', 'payment'], 10, 2);
82
+		add_action(
83
+			'AHEE__EE_Registration_Processor__trigger_registration_update_notifications',
84
+			['EED_Messages', 'maybe_registration'],
85
+			10,
86
+			2
87
+		);
88
+		// filters
89
+		add_filter(
90
+			'FHEE__EE_Registration__receipt_url__receipt_url',
91
+			['EED_Messages', 'registration_message_trigger_url'],
92
+			10,
93
+			4
94
+		);
95
+		add_filter(
96
+			'FHEE__EE_Registration__invoice_url__invoice_url',
97
+			['EED_Messages', 'registration_message_trigger_url'],
98
+			10,
99
+			4
100
+		);
101
+		// register routes
102
+		self::_register_routes();
103
+	}
104
+
105
+
106
+	/**
107
+	 *    set_hooks_admin - for hooking into EE Admin Core, other modules, etc
108
+	 *
109
+	 * @access    public
110
+	 * @return    void
111
+	 */
112
+	public static function set_hooks_admin()
113
+	{
114
+		// actions
115
+		add_action('AHEE__EE_Payment_Processor__update_txn_based_on_payment', ['EED_Messages', 'payment'], 10, 2);
116
+		add_action(
117
+			'AHEE__Transactions_Admin_Page___send_payment_reminder__process_admin_payment_reminder',
118
+			['EED_Messages', 'payment_reminder'],
119
+			10
120
+		);
121
+		add_action(
122
+			'AHEE__EE_Registration_Processor__trigger_registration_update_notifications',
123
+			['EED_Messages', 'maybe_registration'],
124
+			10,
125
+			3
126
+		);
127
+		add_action(
128
+			'AHEE__Extend_Registrations_Admin_Page___newsletter_selected_send__with_registrations',
129
+			['EED_Messages', 'send_newsletter_message'],
130
+			10,
131
+			2
132
+		);
133
+		add_action(
134
+			'AHEE__EES_Espresso_Cancelled__process_shortcode__transaction',
135
+			['EED_Messages', 'cancelled_registration'],
136
+			10
137
+		);
138
+		add_action(
139
+			'AHEE__EE_Admin_Page___process_admin_payment_notification',
140
+			['EED_Messages', 'process_admin_payment'],
141
+			10,
142
+			1
143
+		);
144
+		// filters
145
+		add_filter(
146
+			'FHEE__EE_Admin_Page___process_resend_registration__success',
147
+			['EED_Messages', 'process_resend'],
148
+			10,
149
+			2
150
+		);
151
+		add_filter(
152
+			'FHEE__EE_Registration__receipt_url__receipt_url',
153
+			['EED_Messages', 'registration_message_trigger_url'],
154
+			10,
155
+			4
156
+		);
157
+		add_filter(
158
+			'FHEE__EE_Registration__invoice_url__invoice_url',
159
+			['EED_Messages', 'registration_message_trigger_url'],
160
+			10,
161
+			4
162
+		);
163
+	}
164
+
165
+
166
+	/**
167
+	 * All the message triggers done by route go in here.
168
+	 *
169
+	 * @return void
170
+	 * @since 4.5.0
171
+	 */
172
+	protected static function _register_routes()
173
+	{
174
+		EE_Config::register_route('msg_url_trigger', 'Messages', 'run');
175
+		EE_Config::register_route('msg_cron_trigger', 'Messages', 'execute_batch_request');
176
+		EE_Config::register_route('msg_browser_trigger', 'Messages', 'browser_trigger');
177
+		EE_Config::register_route('msg_browser_error_trigger', 'Messages', 'browser_error_trigger');
178
+		do_action('AHEE__EED_Messages___register_routes');
179
+	}
180
+
181
+
182
+	/**
183
+	 * This is called when a browser display trigger is executed.
184
+	 * The browser display trigger is typically used when a already generated message is displayed directly in the
185
+	 * browser.
186
+	 *
187
+	 * @param WP $WP
188
+	 * @throws EE_Error
189
+	 * @throws InvalidArgumentException
190
+	 * @throws ReflectionException
191
+	 * @throws InvalidDataTypeException
192
+	 * @throws InvalidInterfaceException
193
+	 * @since 4.9.0
194
+	 */
195
+	public function browser_trigger($WP)
196
+	{
197
+		// ensure controller is loaded
198
+		self::_load_controller();
199
+		$token = self::getRequest()->getRequestParam('token');
200
+		try {
201
+			$mtg = new EE_Message_Generated_From_Token($token, 'html', self::$_message_resource_manager);
202
+			self::$_MSG_PROCESSOR->generate_and_send_now($mtg);
203
+		} catch (EE_Error $e) {
204
+			$error_msg = esc_html__(
205
+				'Please note that a system message failed to send due to a technical issue.',
206
+				'event_espresso'
207
+			);
208
+			// add specific message for developers if WP_DEBUG in on
209
+			$error_msg .= '||' . $e->getMessage();
210
+			EE_Error::add_error($error_msg, __FILE__, __FUNCTION__, __LINE__);
211
+		}
212
+	}
213
+
214
+
215
+	/**
216
+	 * This is called when a browser error trigger is executed.
217
+	 * When triggered this will grab the EE_Message matching the token in the request and use that to get the error
218
+	 * message and display it.
219
+	 *
220
+	 * @param $WP
221
+	 * @throws EE_Error
222
+	 * @throws InvalidArgumentException
223
+	 * @throws InvalidDataTypeException
224
+	 * @throws InvalidInterfaceException
225
+	 * @throws ReflectionException
226
+	 * @since 4.9.0
227
+	 */
228
+	public function browser_error_trigger($WP)
229
+	{
230
+		$token = self::getRequest()->getRequestParam('token');
231
+		if ($token) {
232
+			$message = EEM_Message::instance()->get_one_by_token($token);
233
+			if ($message instanceof EE_Message) {
234
+				header('HTTP/1.1 200 OK');
235
+				$error_msg = nl2br($message->error_message());
236
+				?>
237 237
                 <!DOCTYPE html>
238 238
                 <html>
239 239
                 <head></head>
240 240
                 <body>
241 241
                 <?php echo empty($error_msg)
242
-                    ? esc_html__(
243
-                        'Unfortunately, we were unable to capture the error message for this message.',
244
-                        'event_espresso'
245
-                    )
246
-                    : wp_kses(
247
-                        $error_msg,
248
-                        [
249
-                            'a'      => [
250
-                                'href'  => [],
251
-                                'title' => [],
252
-                            ],
253
-                            'span'   => [],
254
-                            'div'    => [],
255
-                            'p'      => [],
256
-                            'strong' => [],
257
-                            'em'     => [],
258
-                            'br'     => [],
259
-                        ]
260
-                    ); ?>
242
+					? esc_html__(
243
+						'Unfortunately, we were unable to capture the error message for this message.',
244
+						'event_espresso'
245
+					)
246
+					: wp_kses(
247
+						$error_msg,
248
+						[
249
+							'a'      => [
250
+								'href'  => [],
251
+								'title' => [],
252
+							],
253
+							'span'   => [],
254
+							'div'    => [],
255
+							'p'      => [],
256
+							'strong' => [],
257
+							'em'     => [],
258
+							'br'     => [],
259
+						]
260
+					); ?>
261 261
                 </body>
262 262
                 </html>
263 263
                 <?php
264
-                exit;
265
-            }
266
-        }
267
-    }
268
-
269
-
270
-    /**
271
-     *  This runs when the msg_url_trigger route has initiated.
272
-     *
273
-     * @param WP $WP
274
-     * @throws EE_Error
275
-     * @throws InvalidArgumentException
276
-     * @throws ReflectionException
277
-     * @throws InvalidDataTypeException
278
-     * @throws InvalidInterfaceException
279
-     * @since 4.5.0
280
-     */
281
-    public function run($WP)
282
-    {
283
-        // ensure controller is loaded
284
-        self::_load_controller();
285
-        // attempt to process message
286
-        try {
287
-            /** @type EE_Message_To_Generate_From_Request $message_to_generate */
288
-            $message_to_generate = EE_Registry::instance()->load_lib('Message_To_Generate_From_Request');
289
-            self::$_MSG_PROCESSOR->generate_and_send_now($message_to_generate);
290
-        } catch (EE_Error $e) {
291
-            $error_msg = esc_html__(
292
-                'Please note that a system message failed to send due to a technical issue.',
293
-                'event_espresso'
294
-            );
295
-            // add specific message for developers if WP_DEBUG in on
296
-            $error_msg .= '||' . $e->getMessage();
297
-            EE_Error::add_error($error_msg, __FILE__, __FUNCTION__, __LINE__);
298
-        }
299
-    }
300
-
301
-
302
-    /**
303
-     * This is triggered by the 'msg_cron_trigger' route.
304
-     *
305
-     * @param WP $WP
306
-     * @throws EE_Error
307
-     */
308
-    public function execute_batch_request($WP)
309
-    {
310
-        $this->run_cron();
311
-        header('HTTP/1.1 200 OK');
312
-        exit();
313
-    }
314
-
315
-
316
-    /**
317
-     * This gets executed on wp_cron jobs or when a batch request is initiated on its own separate non regular wp
318
-     * request.
319
-     *
320
-     * @throws EE_Error
321
-     * @throws ReflectionException
322
-     */
323
-    public function run_cron()
324
-    {
325
-        self::_load_controller();
326
-        $request = self::getRequest();
327
-        // get required vars
328
-        $cron_type     = $request->getRequestParam('type');
329
-        $transient_key = $request->getRequestParam('key');
330
-
331
-        // now let's verify transient, if not valid exit immediately
332
-        if (! get_transient($transient_key)) {
333
-            /**
334
-             * trigger error so this gets in the error logs.  This is important because it happens on a non-user
335
-             * request.
336
-             */
337
-            trigger_error(esc_attr__('Invalid Request (Transient does not exist)', 'event_espresso'));
338
-        }
339
-
340
-        // if made it here, lets' delete the transient to keep the db clean
341
-        delete_transient($transient_key);
342
-
343
-        if (apply_filters('FHEE__EED_Messages__run_cron__use_wp_cron', true)) {
344
-            $method = 'batch_' . $cron_type . '_from_queue';
345
-            if (method_exists(self::$_MSG_PROCESSOR, $method)) {
346
-                self::$_MSG_PROCESSOR->$method();
347
-            } else {
348
-                // no matching task
349
-                /**
350
-                 * trigger error so this gets in the error logs.  This is important because it happens on a non user
351
-                 * request.
352
-                 */
353
-                trigger_error(
354
-                    esc_attr(
355
-                        sprintf(
356
-                            esc_html__('There is no task corresponding to this route %s', 'event_espresso'),
357
-                            $cron_type
358
-                        )
359
-                    )
360
-                );
361
-            }
362
-        }
363
-
364
-        do_action('FHEE__EED_Messages__run_cron__end');
365
-    }
366
-
367
-
368
-    /**
369
-     * This is used to retrieve the template pack for the given name.
370
-     * Retrieved packs are cached on the static $_TMP_PACKS array.  If there is no class matching the given name then
371
-     * the default template pack is returned.
372
-     *
373
-     * @param string $template_pack_name This should correspond to the dbref of the template pack (which is also used
374
-     *                                   in generating the Pack class name).
375
-     * @return EE_Messages_Template_Pack
376
-     * @throws EE_Error
377
-     * @throws InvalidArgumentException
378
-     * @throws ReflectionException
379
-     * @throws InvalidDataTypeException
380
-     * @throws InvalidInterfaceException
381
-     * @deprecated 4.9.0  @see EEH_MSG_Template::get_template_pack()
382
-     */
383
-    public static function get_template_pack($template_pack_name)
384
-    {
385
-        EE_Registry::instance()->load_helper('MSG_Template');
386
-        return EEH_MSG_Template::get_template_pack($template_pack_name);
387
-    }
388
-
389
-
390
-    /**
391
-     * Retrieves an array of all template packs.
392
-     * Array is in the format array( 'dbref' => EE_Messages_Template_Pack )
393
-     *
394
-     * @return EE_Messages_Template_Pack[]
395
-     * @throws EE_Error
396
-     * @throws InvalidArgumentException
397
-     * @throws ReflectionException
398
-     * @throws InvalidDataTypeException
399
-     * @throws InvalidInterfaceException
400
-     * @deprecated 4.9.0  @see EEH_MSG_Template_Pack::get_template_pack_collection
401
-     */
402
-    public static function get_template_packs()
403
-    {
404
-        EE_Registry::instance()->load_helper('MSG_Template');
405
-
406
-        // for backward compat, let's make sure this returns in the same format as originally.
407
-        $template_pack_collection = EEH_MSG_Template::get_template_pack_collection();
408
-        $template_pack_collection->rewind();
409
-        $template_packs = [];
410
-        while ($template_pack_collection->valid()) {
411
-            $template_packs[ $template_pack_collection->current()->dbref ] = $template_pack_collection->current();
412
-            $template_pack_collection->next();
413
-        }
414
-        return $template_packs;
415
-    }
416
-
417
-
418
-    /**
419
-     * This simply makes sure the autoloaders are registered for the EE_messages system.
420
-     *
421
-     * @return void
422
-     * @throws EE_Error
423
-     * @since 4.5.0
424
-     */
425
-    public static function set_autoloaders()
426
-    {
427
-        if (empty(self::$_MSG_PATHS)) {
428
-            self::_set_messages_paths();
429
-            foreach (self::$_MSG_PATHS as $path) {
430
-                EEH_Autoloader::register_autoloaders_for_each_file_in_folder($path);
431
-            }
432
-            // add aliases
433
-            EEH_Autoloader::add_alias('EE_messages', 'EE_messages');
434
-            EEH_Autoloader::add_alias('EE_messenger', 'EE_messenger');
435
-        }
436
-    }
437
-
438
-
439
-    /**
440
-     * Take care of adding all the paths for the messages components to the $_MSG_PATHS property
441
-     * for use by the Messages Autoloaders
442
-     *
443
-     * @return void.
444
-     * @since 4.5.0
445
-     */
446
-    protected static function _set_messages_paths()
447
-    {
448
-        self::$_MSG_PATHS = apply_filters(
449
-            'FHEE__EED_Messages___set_messages_paths___MSG_PATHS',
450
-            [
451
-                EE_LIBRARIES . 'messages/message_type',
452
-                EE_LIBRARIES . 'messages/messenger',
453
-                EE_LIBRARIES . 'messages/defaults',
454
-                EE_LIBRARIES . 'messages/defaults/email',
455
-                EE_LIBRARIES . 'messages/data_class',
456
-                EE_LIBRARIES . 'messages/validators',
457
-                EE_LIBRARIES . 'messages/validators/email',
458
-                EE_LIBRARIES . 'messages/validators/html',
459
-                EE_LIBRARIES . 'shortcodes',
460
-            ]
461
-        );
462
-    }
463
-
464
-
465
-    /**
466
-     * Takes care of loading dependencies
467
-     *
468
-     * @return void
469
-     * @throws EE_Error
470
-     * @throws InvalidArgumentException
471
-     * @throws ReflectionException
472
-     * @throws InvalidDataTypeException
473
-     * @throws InvalidInterfaceException
474
-     * @since 4.5.0
475
-     */
476
-    protected static function _load_controller()
477
-    {
478
-        if (! self::$_MSG_PROCESSOR instanceof EE_Messages_Processor) {
479
-            EE_Registry::instance()->load_core('Request_Handler');
480
-            self::set_autoloaders();
481
-            self::$_EEMSG                    = EE_Registry::instance()->load_lib('messages');
482
-            self::$_MSG_PROCESSOR            = EE_Registry::instance()->load_lib('Messages_Processor');
483
-            self::$_message_resource_manager = EE_Registry::instance()->load_lib('Message_Resource_Manager');
484
-        }
485
-    }
486
-
487
-
488
-    /**
489
-     * @param EE_Transaction $transaction
490
-     * @throws EE_Error
491
-     * @throws InvalidArgumentException
492
-     * @throws InvalidDataTypeException
493
-     * @throws InvalidInterfaceException
494
-     * @throws ReflectionException
495
-     */
496
-    public static function payment_reminder(EE_Transaction $transaction)
497
-    {
498
-        self::_load_controller();
499
-        $data = [$transaction, null];
500
-        self::$_MSG_PROCESSOR->generate_for_all_active_messengers('payment_reminder', $data);
501
-    }
502
-
503
-
504
-    /**
505
-     * Any messages triggers for after successful gateway payments should go in here.
506
-     *
507
-     * @param EE_Transaction  $transaction object
508
-     * @param EE_Payment|null $payment     object
509
-     * @return void
510
-     * @throws EE_Error
511
-     * @throws InvalidArgumentException
512
-     * @throws ReflectionException
513
-     * @throws InvalidDataTypeException
514
-     * @throws InvalidInterfaceException
515
-     */
516
-    public static function payment(EE_Transaction $transaction, EE_Payment $payment = null)
517
-    {
518
-        // if there's no payment object, then we cannot do a payment type message!
519
-        if (! $payment instanceof EE_Payment) {
520
-            return;
521
-        }
522
-        self::_load_controller();
523
-        $data = [$transaction, $payment];
524
-        EE_Registry::instance()->load_helper('MSG_Template');
525
-        $message_type = EEH_MSG_Template::convert_payment_status_to_message_type($payment->STS_ID());
526
-        // if payment amount is less than 0 then switch to payment_refund message type.
527
-        $message_type = $payment->amount() < 0 ? 'payment_refund' : $message_type;
528
-        self::$_MSG_PROCESSOR->generate_for_all_active_messengers($message_type, $data);
529
-    }
530
-
531
-
532
-    /**
533
-     * @param EE_Transaction $transaction
534
-     * @throws EE_Error
535
-     * @throws InvalidArgumentException
536
-     * @throws InvalidDataTypeException
537
-     * @throws InvalidInterfaceException
538
-     * @throws ReflectionException
539
-     */
540
-    public static function cancelled_registration(EE_Transaction $transaction)
541
-    {
542
-        self::_load_controller();
543
-        $data = [$transaction, null];
544
-        self::$_MSG_PROCESSOR->generate_for_all_active_messengers('cancelled_registration', $data);
545
-    }
546
-
547
-
548
-    /**
549
-     * Trigger for Registration messages
550
-     * Note that what registration message type is sent depends on what the reg status is for the registrations on the
551
-     * incoming transaction.
552
-     *
553
-     * @param EE_Registration $registration
554
-     * @param array           $extra_details
555
-     * @return void
556
-     * @throws EE_Error
557
-     * @throws InvalidArgumentException
558
-     * @throws InvalidDataTypeException
559
-     * @throws InvalidInterfaceException
560
-     * @throws ReflectionException
561
-     * @throws EntityNotFoundException
562
-     */
563
-    public static function maybe_registration(EE_Registration $registration, $extra_details = [])
564
-    {
565
-
566
-        if (! self::_verify_registration_notification_send($registration, $extra_details)) {
567
-            // no messages please
568
-            return;
569
-        }
570
-
571
-        // get all non-trashed registrations so we make sure we send messages for the right status.
572
-        $all_registrations = $registration->transaction()->registrations(
573
-            [
574
-                ['REG_deleted' => false],
575
-                'order_by' => [
576
-                    'Event.EVT_name'     => 'ASC',
577
-                    'Attendee.ATT_lname' => 'ASC',
578
-                    'Attendee.ATT_fname' => 'ASC',
579
-                ],
580
-            ]
581
-        );
582
-        // cached array of statuses so we only trigger messages once per status.
583
-        $statuses_sent = [];
584
-        self::_load_controller();
585
-        $mtgs = [];
586
-
587
-        // loop through registrations and trigger messages once per status.
588
-        foreach ($all_registrations as $reg) {
589
-            // already triggered?
590
-            if (in_array($reg->status_ID(), $statuses_sent)) {
591
-                continue;
592
-            }
593
-
594
-            $message_type    = EEH_MSG_Template::convert_reg_status_to_message_type($reg->status_ID());
595
-            $mtgs            = array_merge(
596
-                $mtgs,
597
-                self::$_MSG_PROCESSOR->setup_mtgs_for_all_active_messengers(
598
-                    $message_type,
599
-                    [$registration->transaction(), null, $reg->status_ID()]
600
-                )
601
-            );
602
-            $statuses_sent[] = $reg->status_ID();
603
-        }
604
-
605
-        if (count($statuses_sent) > 1) {
606
-            $mtgs = array_merge(
607
-                $mtgs,
608
-                self::$_MSG_PROCESSOR->setup_mtgs_for_all_active_messengers(
609
-                    'registration_summary',
610
-                    [$registration->transaction(), null]
611
-                )
612
-            );
613
-        }
614
-
615
-        // batch queue and initiate request
616
-        self::$_MSG_PROCESSOR->batch_queue_for_generation_and_persist($mtgs);
617
-        self::$_MSG_PROCESSOR->get_queue()->initiate_request_by_priority();
618
-    }
619
-
620
-
621
-    /**
622
-     * This is a helper method used to very whether a registration notification should be sent or
623
-     * not.  Prevents duplicate notifications going out for registration context notifications.
624
-     *
625
-     * @param EE_Registration $registration  [description]
626
-     * @param array           $extra_details [description]
627
-     * @return bool          true = send away, false = nope halt the presses.
628
-     */
629
-    protected static function _verify_registration_notification_send(
630
-        EE_Registration $registration,
631
-        $extra_details = []
632
-    ) {
633
-        $request = self::getRequest();
634
-        if (
635
-            ! $request->getRequestParam('non_primary_reg_notification', 0, 'int')
636
-            && ! $registration->is_primary_registrant()
637
-        ) {
638
-            return false;
639
-        }
640
-        // first we check if we're in admin and not doing front ajax
641
-        if (
642
-            ($request->isAdmin() || $request->isAdminAjax())
643
-            && ! $request->isFrontAjax()
644
-        ) {
645
-            $status_change = $request->getRequestParam('txn_reg_status_change', [], 'int', true);
646
-            // make sure appropriate admin params are set for sending messages
647
-            if (
648
-                ! isset($status_change['send_notifications'])
649
-                || (isset($status_change['send_notifications']) && ! $status_change['send_notifications'])
650
-            ) {
651
-                // no messages sent please.
652
-                return false;
653
-            }
654
-        } else {
655
-            // frontend request (either regular or via AJAX)
656
-            // TXN is NOT finalized ?
657
-            if (! isset($extra_details['finalized']) || $extra_details['finalized'] === false) {
658
-                return false;
659
-            }
660
-            // return visit but nothing changed ???
661
-            if (
662
-                isset($extra_details['revisit'], $extra_details['status_updates'])
663
-                && $extra_details['revisit']
664
-                && ! $extra_details['status_updates']
665
-            ) {
666
-                return false;
667
-            }
668
-            // NOT sending messages && reg status is something other than "Not-Approved"
669
-            if (
670
-                ! apply_filters('FHEE__EED_Messages___maybe_registration__deliver_notifications', false)
671
-                && $registration->status_ID() !== RegStatus::AWAITING_REVIEW
672
-            ) {
673
-                return false;
674
-            }
675
-        }
676
-        // release the kraken
677
-        return true;
678
-    }
679
-
680
-
681
-    /**
682
-     * Simply returns an array indexed by Registration Status ID and the related message_type name associated with that
683
-     * status id.
684
-     *
685
-     * @param string $reg_status
686
-     * @return array
687
-     * @throws EE_Error
688
-     * @throws InvalidArgumentException
689
-     * @throws ReflectionException
690
-     * @throws InvalidDataTypeException
691
-     * @throws InvalidInterfaceException
692
-     * @deprecated        4.9.0  Use EEH_MSG_Template::reg_status_to_message_type_array()
693
-     *                    or EEH_MSG_Template::convert_reg_status_to_message_type
694
-     */
695
-    protected static function _get_reg_status_array($reg_status = '')
696
-    {
697
-        EE_Registry::instance()->load_helper('MSG_Template');
698
-        return EEH_MSG_Template::convert_reg_status_to_message_type($reg_status)
699
-            ? EEH_MSG_Template::convert_reg_status_to_message_type($reg_status)
700
-            : EEH_MSG_Template::reg_status_to_message_type_array();
701
-    }
702
-
703
-
704
-    /**
705
-     * Simply returns the payment message type for the given payment status.
706
-     *
707
-     * @param string $payment_status The payment status being matched.
708
-     * @return bool|string The payment message type slug matching the status or false if no match.
709
-     * @throws EE_Error
710
-     * @throws InvalidArgumentException
711
-     * @throws ReflectionException
712
-     * @throws InvalidDataTypeException
713
-     * @throws InvalidInterfaceException
714
-     * @deprecated       4.9.0 Use EEH_MSG_Template::payment_status_to_message_type_array
715
-     *                   or EEH_MSG_Template::convert_payment_status_to_message_type
716
-     */
717
-    protected static function _get_payment_message_type($payment_status)
718
-    {
719
-        EE_Registry::instance()->load_helper('MSG_Template');
720
-        return EEH_MSG_Template::convert_payment_status_to_message_type($payment_status)
721
-            ? EEH_MSG_Template::convert_payment_status_to_message_type($payment_status)
722
-            : false;
723
-    }
724
-
725
-
726
-    /**
727
-     * Message triggers for a resending already sent message(s) (via EE_Message list table)
728
-     *
729
-     * @access public
730
-     * @param array $req_data This is the $_POST & $_GET data sent from EE_Admin Pages
731
-     * @return bool success/fail
732
-     * @throws EE_Error
733
-     * @throws InvalidArgumentException
734
-     * @throws InvalidDataTypeException
735
-     * @throws InvalidInterfaceException
736
-     * @throws ReflectionException
737
-     */
738
-    public static function process_resend(array $req_data = [])
739
-    {
740
-        self::_load_controller();
741
-        $request = self::getRequest();
742
-        // if $msgID in this request then skip to the new resend_message
743
-        if ($request->getRequestParam('MSG_ID')) {
744
-            return self::resend_message();
745
-        }
746
-
747
-        // make sure any incoming request data is set on the request so that it gets picked up later.
748
-        foreach ((array) $req_data as $request_key => $request_value) {
749
-            if (! $request->requestParamIsSet($request_key)) {
750
-                $request->setRequestParam($request_key, $request_value);
751
-            }
752
-        }
753
-
754
-        if (
755
-            ! $messages_to_send = self::$_MSG_PROCESSOR->setup_messages_to_generate_from_registration_ids_in_request()
756
-        ) {
757
-            return false;
758
-        }
759
-
760
-        try {
761
-            self::$_MSG_PROCESSOR->batch_queue_for_generation_and_persist($messages_to_send);
762
-            self::$_MSG_PROCESSOR->get_queue()->initiate_request_by_priority();
763
-        } catch (EE_Error $e) {
764
-            EE_Error::add_error($e->getMessage(), __FILE__, __FUNCTION__, __LINE__);
765
-            return false;
766
-        }
767
-        EE_Error::add_success(
768
-            esc_html__('Messages have been successfully queued for generation and sending.', 'event_espresso')
769
-        );
770
-        return true; // everything got queued.
771
-    }
772
-
773
-
774
-    /**
775
-     * Message triggers for a resending already sent message(s) (via EE_Message list table)
776
-     *
777
-     * @return bool
778
-     * @throws EE_Error
779
-     * @throws InvalidArgumentException
780
-     * @throws InvalidDataTypeException
781
-     * @throws InvalidInterfaceException
782
-     * @throws ReflectionException
783
-     */
784
-    public static function resend_message()
785
-    {
786
-        self::_load_controller();
787
-
788
-        $msgID = self::getRequest()->getRequestParam('MSG_ID', 0, 'int');
789
-        if (! $msgID) {
790
-            EE_Error::add_error(
791
-                esc_html__(
792
-                    'Something went wrong because there is no "MSG_ID" value in the request',
793
-                    'event_espresso'
794
-                ),
795
-                __FILE__,
796
-                __FUNCTION__,
797
-                __LINE__
798
-            );
799
-            return false;
800
-        }
801
-
802
-        self::$_MSG_PROCESSOR->setup_messages_from_ids_and_send((array) $msgID);
803
-
804
-        // setup success message.
805
-        $count_ready_for_resend = self::$_MSG_PROCESSOR->get_queue()->count_STS_in_queue(EEM_Message::status_resend);
806
-        EE_Error::add_success(
807
-            sprintf(
808
-                _n(
809
-                    'There was %d message queued for resending.',
810
-                    'There were %d messages queued for resending.',
811
-                    $count_ready_for_resend,
812
-                    'event_espresso'
813
-                ),
814
-                $count_ready_for_resend
815
-            )
816
-        );
817
-        return true;
818
-    }
819
-
820
-
821
-    /**
822
-     * Message triggers for manual payment applied by admin
823
-     *
824
-     * @param EE_Payment $payment EE_payment object
825
-     * @return bool success/fail
826
-     * @throws EE_Error
827
-     * @throws InvalidArgumentException
828
-     * @throws ReflectionException
829
-     * @throws InvalidDataTypeException
830
-     * @throws InvalidInterfaceException
831
-     */
832
-    public static function process_admin_payment(EE_Payment $payment)
833
-    {
834
-        EE_Registry::instance()->load_helper('MSG_Template');
835
-        // we need to get the transaction object
836
-        $transaction = $payment->transaction();
837
-        if ($transaction instanceof EE_Transaction) {
838
-            $data         = [$transaction, $payment];
839
-            $message_type = EEH_MSG_Template::convert_payment_status_to_message_type($payment->STS_ID());
840
-
841
-            // if payment amount is less than 0 then switch to payment_refund message type.
842
-            $message_type = $payment->amount() < 0 ? 'payment_refund' : $message_type;
843
-
844
-            // if payment_refund is selected, but the status is NOT accepted.  Then change message type to false so NO message notification goes out.
845
-            $message_type = $message_type == 'payment_refund' && $payment->STS_ID() != EEM_Payment::status_id_approved
846
-                ? false : $message_type;
847
-
848
-            self::_load_controller();
849
-
850
-            self::$_MSG_PROCESSOR->generate_for_all_active_messengers($message_type, $data);
851
-
852
-            // get count of queued for generation
853
-            $count_to_generate = self::$_MSG_PROCESSOR->get_queue()->count_STS_in_queue(
854
-                [
855
-                    EEM_Message::status_incomplete,
856
-                    EEM_Message::status_idle,
857
-                ]
858
-            );
859
-
860
-            if ($count_to_generate > 0 && self::$_MSG_PROCESSOR->get_queue()->get_message_repository()->count() !== 0) {
861
-                add_filter('FHEE__EE_Admin_Page___process_admin_payment_notification__success', '__return_true');
862
-                return true;
863
-            } else {
864
-                $count_failed = self::$_MSG_PROCESSOR->get_queue()->count_STS_in_queue(
865
-                    EEM_Message::instance()->stati_indicating_failed_sending()
866
-                );
867
-                /**
868
-                 * Verify that there are actually errors.  If not then we return a success message because the queue might have been emptied due to successful
869
-                 * IMMEDIATE generation.
870
-                 */
871
-                if ($count_failed > 0) {
872
-                    EE_Error::add_error(
873
-                        sprintf(
874
-                            _n(
875
-                                'The payment notification generation failed.',
876
-                                '%d payment notifications failed being sent.',
877
-                                $count_failed,
878
-                                'event_espresso'
879
-                            ),
880
-                            $count_failed
881
-                        ),
882
-                        __FILE__,
883
-                        __FUNCTION__,
884
-                        __LINE__
885
-                    );
886
-
887
-                    return false;
888
-                } else {
889
-                    add_filter('FHEE__EE_Admin_Page___process_admin_payment_notification__success', '__return_true');
890
-                    return true;
891
-                }
892
-            }
893
-        } else {
894
-            EE_Error::add_error(
895
-                esc_html__(
896
-                    'Unable to generate the payment notification because the given value for the transaction is invalid.',
897
-                    'event_espresso'
898
-                ),
899
-                __FILE__,
900
-                __FUNCTION__,
901
-                __LINE__
902
-            );
903
-            return false;
904
-        }
905
-    }
906
-
907
-
908
-    /**
909
-     * Callback for AHEE__Extend_Registrations_Admin_Page___newsletter_selected_send_with_registrations trigger
910
-     *
911
-     * @param EE_Registration[] $registrations an array of EE_Registration objects
912
-     * @param int               $grp_id        a specific message template group id.
913
-     * @return void
914
-     * @throws EE_Error
915
-     * @throws InvalidArgumentException
916
-     * @throws InvalidDataTypeException
917
-     * @throws InvalidInterfaceException
918
-     * @throws ReflectionException
919
-     * @since   4.3.0
920
-     */
921
-    public static function send_newsletter_message($registrations, $grp_id)
922
-    {
923
-        // make sure mtp is id and set it in the request later messages setup.
924
-        self::getRequest()->setRequestParam('GRP_ID', (int) $grp_id);
925
-        self::_load_controller();
926
-        self::$_MSG_PROCESSOR->generate_for_all_active_messengers('newsletter', $registrations);
927
-    }
928
-
929
-
930
-    /**
931
-     * Callback for FHEE__EE_Registration__invoice_url__invoice_url or FHEE__EE_Registration__receipt_url__receipt_url
932
-     *
933
-     * @param string          $registration_message_trigger_url
934
-     * @param EE_Registration $registration
935
-     * @param string          $messenger
936
-     * @param string          $message_type
937
-     * @return string
938
-     * @throws EE_Error
939
-     * @throws InvalidArgumentException
940
-     * @throws InvalidDataTypeException
941
-     * @throws InvalidInterfaceException
942
-     * @throws ReflectionException
943
-     * @since   4.3.0
944
-     */
945
-    public static function registration_message_trigger_url(
946
-        $registration_message_trigger_url,
947
-        EE_Registration $registration,
948
-        $messenger = 'html',
949
-        $message_type = 'invoice'
950
-    ) {
951
-        // whitelist $messenger
952
-        switch ($messenger) {
953
-            case 'pdf':
954
-                $sending_messenger    = 'pdf';
955
-                $generating_messenger = 'html';
956
-                break;
957
-            case 'html':
958
-            default:
959
-                $sending_messenger    = 'html';
960
-                $generating_messenger = 'html';
961
-                break;
962
-        }
963
-        // whitelist $message_type
964
-        switch ($message_type) {
965
-            case 'receipt':
966
-                $message_type = 'receipt';
967
-                break;
968
-            case 'invoice':
969
-            default:
970
-                $message_type = 'invoice';
971
-                break;
972
-        }
973
-        // verify that both the messenger AND the message type are active
974
-        if (
975
-            EEH_MSG_Template::is_messenger_active($sending_messenger)
976
-            && EEH_MSG_Template::is_mt_active($message_type)
977
-        ) {
978
-            // need to get the correct message template group for this (i.e. is there a custom invoice for the event this registration is registered for?)
979
-            $template_query_params = [
980
-                'MTP_is_active'    => true,
981
-                'MTP_messenger'    => $generating_messenger,
982
-                'MTP_message_type' => $message_type,
983
-                'Event.EVT_ID'     => $registration->event_ID(),
984
-            ];
985
-            // get the message template group.
986
-            $msg_template_group = EEM_Message_Template_Group::instance()->get_one([$template_query_params]);
987
-            // if we don't have an EE_Message_Template_Group then return
988
-            if (! $msg_template_group instanceof EE_Message_Template_Group) {
989
-                // remove EVT_ID from query params so that global templates get picked up
990
-                unset($template_query_params['Event.EVT_ID']);
991
-                // get global template as the fallback
992
-                $msg_template_group = EEM_Message_Template_Group::instance()->get_one([$template_query_params]);
993
-            }
994
-            // if we don't have an EE_Message_Template_Group then return
995
-            if (! $msg_template_group instanceof EE_Message_Template_Group) {
996
-                return '';
997
-            }
998
-            // generate the URL
999
-            $registration_message_trigger_url = EEH_MSG_Template::generate_url_trigger(
1000
-                $sending_messenger,
1001
-                $generating_messenger,
1002
-                'purchaser',
1003
-                $message_type,
1004
-                $registration,
1005
-                $msg_template_group->ID(),
1006
-                $registration->transaction_ID()
1007
-            );
1008
-        }
1009
-        return $registration_message_trigger_url;
1010
-    }
1011
-
1012
-
1013
-    /**
1014
-     * Use to generate and return a message preview!
1015
-     *
1016
-     * @param string $type       This should correspond with a valid message type
1017
-     * @param string $context    This should correspond with a valid context for the message type
1018
-     * @param string $messenger  This should correspond with a valid messenger.
1019
-     * @param bool   $send       true we will do a test send using the messenger delivery, false we just do a regular
1020
-     *                           preview
1021
-     * @return bool|string The body of the message or if send is requested, sends.
1022
-     * @throws EE_Error
1023
-     * @throws InvalidArgumentException
1024
-     * @throws InvalidDataTypeException
1025
-     * @throws InvalidInterfaceException
1026
-     * @throws ReflectionException
1027
-     */
1028
-    public static function preview_message($type, $context, $messenger, $send = false)
1029
-    {
1030
-        self::_load_controller();
1031
-        $message_to_generate     = new EE_Message_To_Generate(
1032
-            $messenger,
1033
-            $type,
1034
-            [],
1035
-            $context,
1036
-            true
1037
-        );
1038
-        $generated_preview_queue = self::$_MSG_PROCESSOR->generate_for_preview($message_to_generate, $send);
1039
-
1040
-        if ($generated_preview_queue instanceof EE_Messages_Queue) {
1041
-            // loop through all content for the preview and remove any persisted records.
1042
-            $content = '';
1043
-            foreach ($generated_preview_queue->get_message_repository() as $message) {
1044
-                $content = $message->content();
1045
-                if ($message->ID() > 0 && $message->STS_ID() !== EEM_Message::status_failed) {
1046
-                    $message->delete();
1047
-                }
1048
-            }
1049
-            return $content;
1050
-        }
1051
-        return $generated_preview_queue;
1052
-    }
1053
-
1054
-
1055
-    /**
1056
-     * This is a method that allows for sending a message using a messenger matching the string given and the provided
1057
-     * EE_Message_Queue object.  The EE_Message_Queue object is used to create a single aggregate EE_Message via the
1058
-     * content found in the EE_Message objects in the queue.
1059
-     *
1060
-     * @param string            $messenger            a string matching a valid active messenger in the system
1061
-     * @param string            $message_type         Although it seems contrary to the name of the method, a message
1062
-     *                                                type name is still required to send along the message type to the
1063
-     *                                                messenger because this is used for determining what specific
1064
-     *                                                variations might be loaded for the generated message.
1065
-     * @param EE_Messages_Queue $queue
1066
-     * @param string            $custom_subject       Can be used to set what the custom subject string will be on the
1067
-     *                                                aggregate EE_Message object.
1068
-     * @return bool success or fail.
1069
-     * @throws EE_Error
1070
-     * @throws InvalidArgumentException
1071
-     * @throws ReflectionException
1072
-     * @throws InvalidDataTypeException
1073
-     * @throws InvalidInterfaceException
1074
-     * @since 4.9.0
1075
-     */
1076
-    public static function send_message_with_messenger_only(
1077
-        $messenger,
1078
-        $message_type,
1079
-        EE_Messages_Queue $queue,
1080
-        $custom_subject = ''
1081
-    ) {
1082
-        self::_load_controller();
1083
-        /** @type EE_Message_To_Generate_From_Queue $message_to_generate */
1084
-        $message_to_generate = EE_Registry::instance()->load_lib(
1085
-            'Message_To_Generate_From_Queue',
1086
-            [
1087
-                $messenger,
1088
-                $message_type,
1089
-                $queue,
1090
-                $custom_subject,
1091
-            ]
1092
-        );
1093
-        return self::$_MSG_PROCESSOR->queue_for_sending($message_to_generate);
1094
-    }
1095
-
1096
-
1097
-    /**
1098
-     * Generates Messages immediately for EE_Message IDs (but only for the correct status for generation)
1099
-     *
1100
-     * @param array $message_ids An array of message ids
1101
-     * @return bool|EE_Messages_Queue false if nothing was generated, EE_Messages_Queue containing generated
1102
-     *                           messages.
1103
-     * @throws EE_Error
1104
-     * @throws InvalidArgumentException
1105
-     * @throws InvalidDataTypeException
1106
-     * @throws InvalidInterfaceException
1107
-     * @throws ReflectionException
1108
-     * @since 4.9.0
1109
-     */
1110
-    public static function generate_now($message_ids)
1111
-    {
1112
-        self::_load_controller();
1113
-        $messages        = EEM_Message::instance()->get_all(
1114
-            [
1115
-                0 => [
1116
-                    'MSG_ID' => ['IN', $message_ids],
1117
-                    'STS_ID' => EEM_Message::status_incomplete,
1118
-                ],
1119
-            ]
1120
-        );
1121
-        $generated_queue = false;
1122
-        if ($messages) {
1123
-            $generated_queue = self::$_MSG_PROCESSOR->batch_generate_from_queue($messages);
1124
-        }
1125
-
1126
-        if (! $generated_queue instanceof EE_Messages_Queue) {
1127
-            EE_Error::add_error(
1128
-                esc_html__(
1129
-                    'The messages were not generated. This could mean there is already a batch being generated on a separate request, or because the selected messages are not ready for generation. Please wait a minute or two and try again.',
1130
-                    'event_espresso'
1131
-                ),
1132
-                __FILE__,
1133
-                __FUNCTION__,
1134
-                __LINE__
1135
-            );
1136
-        }
1137
-        return $generated_queue;
1138
-    }
1139
-
1140
-
1141
-    /**
1142
-     * Sends messages immediately for the incoming message_ids that have the status of EEM_Message::status_resend or,
1143
-     * EEM_Message::status_idle
1144
-     *
1145
-     * @param $message_ids
1146
-     * @return bool|EE_Messages_Queue false if no messages sent.
1147
-     * @throws EE_Error
1148
-     * @throws InvalidArgumentException
1149
-     * @throws InvalidDataTypeException
1150
-     * @throws InvalidInterfaceException
1151
-     * @throws ReflectionException
1152
-     * @since 4.9.0
1153
-     */
1154
-    public static function send_now($message_ids)
1155
-    {
1156
-        self::_load_controller();
1157
-        $messages   = EEM_Message::instance()->get_all(
1158
-            [
1159
-                0 => [
1160
-                    'MSG_ID' => ['IN', $message_ids],
1161
-                    'STS_ID' => [
1162
-                        'IN',
1163
-                        [EEM_Message::status_idle, EEM_Message::status_resend, EEM_Message::status_retry],
1164
-                    ],
1165
-                ],
1166
-            ]
1167
-        );
1168
-        $sent_queue = false;
1169
-        if ($messages) {
1170
-            $sent_queue = self::$_MSG_PROCESSOR->batch_send_from_queue($messages);
1171
-        }
1172
-
1173
-        if (! $sent_queue instanceof EE_Messages_Queue) {
1174
-            EE_Error::add_error(
1175
-                esc_html__(
1176
-                    'The messages were not sent. This could mean there is already a batch being sent on a separate request, or because the selected messages are not sendable. Please wait a minute or two and try again.',
1177
-                    'event_espresso'
1178
-                ),
1179
-                __FILE__,
1180
-                __FUNCTION__,
1181
-                __LINE__
1182
-            );
1183
-        } else {
1184
-            // can count how many sent by using the messages in the queue
1185
-            $sent_count = $sent_queue->count_STS_in_queue(EEM_Message::instance()->stati_indicating_sent());
1186
-            if ($sent_count > 0) {
1187
-                EE_Error::add_success(
1188
-                    sprintf(
1189
-                        _n(
1190
-                            'There was %d message successfully sent.',
1191
-                            'There were %d messages successfully sent.',
1192
-                            $sent_count,
1193
-                            'event_espresso'
1194
-                        ),
1195
-                        $sent_count
1196
-                    )
1197
-                );
1198
-            } else {
1199
-                EE_Error::overwrite_errors();
1200
-                EE_Error::add_error(
1201
-                    esc_html__(
1202
-                        'No message was sent because of problems with sending. Either all the messages you selected were not a sendable message, they were ALREADY sent on a different scheduled task, or there was an error.
264
+				exit;
265
+			}
266
+		}
267
+	}
268
+
269
+
270
+	/**
271
+	 *  This runs when the msg_url_trigger route has initiated.
272
+	 *
273
+	 * @param WP $WP
274
+	 * @throws EE_Error
275
+	 * @throws InvalidArgumentException
276
+	 * @throws ReflectionException
277
+	 * @throws InvalidDataTypeException
278
+	 * @throws InvalidInterfaceException
279
+	 * @since 4.5.0
280
+	 */
281
+	public function run($WP)
282
+	{
283
+		// ensure controller is loaded
284
+		self::_load_controller();
285
+		// attempt to process message
286
+		try {
287
+			/** @type EE_Message_To_Generate_From_Request $message_to_generate */
288
+			$message_to_generate = EE_Registry::instance()->load_lib('Message_To_Generate_From_Request');
289
+			self::$_MSG_PROCESSOR->generate_and_send_now($message_to_generate);
290
+		} catch (EE_Error $e) {
291
+			$error_msg = esc_html__(
292
+				'Please note that a system message failed to send due to a technical issue.',
293
+				'event_espresso'
294
+			);
295
+			// add specific message for developers if WP_DEBUG in on
296
+			$error_msg .= '||' . $e->getMessage();
297
+			EE_Error::add_error($error_msg, __FILE__, __FUNCTION__, __LINE__);
298
+		}
299
+	}
300
+
301
+
302
+	/**
303
+	 * This is triggered by the 'msg_cron_trigger' route.
304
+	 *
305
+	 * @param WP $WP
306
+	 * @throws EE_Error
307
+	 */
308
+	public function execute_batch_request($WP)
309
+	{
310
+		$this->run_cron();
311
+		header('HTTP/1.1 200 OK');
312
+		exit();
313
+	}
314
+
315
+
316
+	/**
317
+	 * This gets executed on wp_cron jobs or when a batch request is initiated on its own separate non regular wp
318
+	 * request.
319
+	 *
320
+	 * @throws EE_Error
321
+	 * @throws ReflectionException
322
+	 */
323
+	public function run_cron()
324
+	{
325
+		self::_load_controller();
326
+		$request = self::getRequest();
327
+		// get required vars
328
+		$cron_type     = $request->getRequestParam('type');
329
+		$transient_key = $request->getRequestParam('key');
330
+
331
+		// now let's verify transient, if not valid exit immediately
332
+		if (! get_transient($transient_key)) {
333
+			/**
334
+			 * trigger error so this gets in the error logs.  This is important because it happens on a non-user
335
+			 * request.
336
+			 */
337
+			trigger_error(esc_attr__('Invalid Request (Transient does not exist)', 'event_espresso'));
338
+		}
339
+
340
+		// if made it here, lets' delete the transient to keep the db clean
341
+		delete_transient($transient_key);
342
+
343
+		if (apply_filters('FHEE__EED_Messages__run_cron__use_wp_cron', true)) {
344
+			$method = 'batch_' . $cron_type . '_from_queue';
345
+			if (method_exists(self::$_MSG_PROCESSOR, $method)) {
346
+				self::$_MSG_PROCESSOR->$method();
347
+			} else {
348
+				// no matching task
349
+				/**
350
+				 * trigger error so this gets in the error logs.  This is important because it happens on a non user
351
+				 * request.
352
+				 */
353
+				trigger_error(
354
+					esc_attr(
355
+						sprintf(
356
+							esc_html__('There is no task corresponding to this route %s', 'event_espresso'),
357
+							$cron_type
358
+						)
359
+					)
360
+				);
361
+			}
362
+		}
363
+
364
+		do_action('FHEE__EED_Messages__run_cron__end');
365
+	}
366
+
367
+
368
+	/**
369
+	 * This is used to retrieve the template pack for the given name.
370
+	 * Retrieved packs are cached on the static $_TMP_PACKS array.  If there is no class matching the given name then
371
+	 * the default template pack is returned.
372
+	 *
373
+	 * @param string $template_pack_name This should correspond to the dbref of the template pack (which is also used
374
+	 *                                   in generating the Pack class name).
375
+	 * @return EE_Messages_Template_Pack
376
+	 * @throws EE_Error
377
+	 * @throws InvalidArgumentException
378
+	 * @throws ReflectionException
379
+	 * @throws InvalidDataTypeException
380
+	 * @throws InvalidInterfaceException
381
+	 * @deprecated 4.9.0  @see EEH_MSG_Template::get_template_pack()
382
+	 */
383
+	public static function get_template_pack($template_pack_name)
384
+	{
385
+		EE_Registry::instance()->load_helper('MSG_Template');
386
+		return EEH_MSG_Template::get_template_pack($template_pack_name);
387
+	}
388
+
389
+
390
+	/**
391
+	 * Retrieves an array of all template packs.
392
+	 * Array is in the format array( 'dbref' => EE_Messages_Template_Pack )
393
+	 *
394
+	 * @return EE_Messages_Template_Pack[]
395
+	 * @throws EE_Error
396
+	 * @throws InvalidArgumentException
397
+	 * @throws ReflectionException
398
+	 * @throws InvalidDataTypeException
399
+	 * @throws InvalidInterfaceException
400
+	 * @deprecated 4.9.0  @see EEH_MSG_Template_Pack::get_template_pack_collection
401
+	 */
402
+	public static function get_template_packs()
403
+	{
404
+		EE_Registry::instance()->load_helper('MSG_Template');
405
+
406
+		// for backward compat, let's make sure this returns in the same format as originally.
407
+		$template_pack_collection = EEH_MSG_Template::get_template_pack_collection();
408
+		$template_pack_collection->rewind();
409
+		$template_packs = [];
410
+		while ($template_pack_collection->valid()) {
411
+			$template_packs[ $template_pack_collection->current()->dbref ] = $template_pack_collection->current();
412
+			$template_pack_collection->next();
413
+		}
414
+		return $template_packs;
415
+	}
416
+
417
+
418
+	/**
419
+	 * This simply makes sure the autoloaders are registered for the EE_messages system.
420
+	 *
421
+	 * @return void
422
+	 * @throws EE_Error
423
+	 * @since 4.5.0
424
+	 */
425
+	public static function set_autoloaders()
426
+	{
427
+		if (empty(self::$_MSG_PATHS)) {
428
+			self::_set_messages_paths();
429
+			foreach (self::$_MSG_PATHS as $path) {
430
+				EEH_Autoloader::register_autoloaders_for_each_file_in_folder($path);
431
+			}
432
+			// add aliases
433
+			EEH_Autoloader::add_alias('EE_messages', 'EE_messages');
434
+			EEH_Autoloader::add_alias('EE_messenger', 'EE_messenger');
435
+		}
436
+	}
437
+
438
+
439
+	/**
440
+	 * Take care of adding all the paths for the messages components to the $_MSG_PATHS property
441
+	 * for use by the Messages Autoloaders
442
+	 *
443
+	 * @return void.
444
+	 * @since 4.5.0
445
+	 */
446
+	protected static function _set_messages_paths()
447
+	{
448
+		self::$_MSG_PATHS = apply_filters(
449
+			'FHEE__EED_Messages___set_messages_paths___MSG_PATHS',
450
+			[
451
+				EE_LIBRARIES . 'messages/message_type',
452
+				EE_LIBRARIES . 'messages/messenger',
453
+				EE_LIBRARIES . 'messages/defaults',
454
+				EE_LIBRARIES . 'messages/defaults/email',
455
+				EE_LIBRARIES . 'messages/data_class',
456
+				EE_LIBRARIES . 'messages/validators',
457
+				EE_LIBRARIES . 'messages/validators/email',
458
+				EE_LIBRARIES . 'messages/validators/html',
459
+				EE_LIBRARIES . 'shortcodes',
460
+			]
461
+		);
462
+	}
463
+
464
+
465
+	/**
466
+	 * Takes care of loading dependencies
467
+	 *
468
+	 * @return void
469
+	 * @throws EE_Error
470
+	 * @throws InvalidArgumentException
471
+	 * @throws ReflectionException
472
+	 * @throws InvalidDataTypeException
473
+	 * @throws InvalidInterfaceException
474
+	 * @since 4.5.0
475
+	 */
476
+	protected static function _load_controller()
477
+	{
478
+		if (! self::$_MSG_PROCESSOR instanceof EE_Messages_Processor) {
479
+			EE_Registry::instance()->load_core('Request_Handler');
480
+			self::set_autoloaders();
481
+			self::$_EEMSG                    = EE_Registry::instance()->load_lib('messages');
482
+			self::$_MSG_PROCESSOR            = EE_Registry::instance()->load_lib('Messages_Processor');
483
+			self::$_message_resource_manager = EE_Registry::instance()->load_lib('Message_Resource_Manager');
484
+		}
485
+	}
486
+
487
+
488
+	/**
489
+	 * @param EE_Transaction $transaction
490
+	 * @throws EE_Error
491
+	 * @throws InvalidArgumentException
492
+	 * @throws InvalidDataTypeException
493
+	 * @throws InvalidInterfaceException
494
+	 * @throws ReflectionException
495
+	 */
496
+	public static function payment_reminder(EE_Transaction $transaction)
497
+	{
498
+		self::_load_controller();
499
+		$data = [$transaction, null];
500
+		self::$_MSG_PROCESSOR->generate_for_all_active_messengers('payment_reminder', $data);
501
+	}
502
+
503
+
504
+	/**
505
+	 * Any messages triggers for after successful gateway payments should go in here.
506
+	 *
507
+	 * @param EE_Transaction  $transaction object
508
+	 * @param EE_Payment|null $payment     object
509
+	 * @return void
510
+	 * @throws EE_Error
511
+	 * @throws InvalidArgumentException
512
+	 * @throws ReflectionException
513
+	 * @throws InvalidDataTypeException
514
+	 * @throws InvalidInterfaceException
515
+	 */
516
+	public static function payment(EE_Transaction $transaction, EE_Payment $payment = null)
517
+	{
518
+		// if there's no payment object, then we cannot do a payment type message!
519
+		if (! $payment instanceof EE_Payment) {
520
+			return;
521
+		}
522
+		self::_load_controller();
523
+		$data = [$transaction, $payment];
524
+		EE_Registry::instance()->load_helper('MSG_Template');
525
+		$message_type = EEH_MSG_Template::convert_payment_status_to_message_type($payment->STS_ID());
526
+		// if payment amount is less than 0 then switch to payment_refund message type.
527
+		$message_type = $payment->amount() < 0 ? 'payment_refund' : $message_type;
528
+		self::$_MSG_PROCESSOR->generate_for_all_active_messengers($message_type, $data);
529
+	}
530
+
531
+
532
+	/**
533
+	 * @param EE_Transaction $transaction
534
+	 * @throws EE_Error
535
+	 * @throws InvalidArgumentException
536
+	 * @throws InvalidDataTypeException
537
+	 * @throws InvalidInterfaceException
538
+	 * @throws ReflectionException
539
+	 */
540
+	public static function cancelled_registration(EE_Transaction $transaction)
541
+	{
542
+		self::_load_controller();
543
+		$data = [$transaction, null];
544
+		self::$_MSG_PROCESSOR->generate_for_all_active_messengers('cancelled_registration', $data);
545
+	}
546
+
547
+
548
+	/**
549
+	 * Trigger for Registration messages
550
+	 * Note that what registration message type is sent depends on what the reg status is for the registrations on the
551
+	 * incoming transaction.
552
+	 *
553
+	 * @param EE_Registration $registration
554
+	 * @param array           $extra_details
555
+	 * @return void
556
+	 * @throws EE_Error
557
+	 * @throws InvalidArgumentException
558
+	 * @throws InvalidDataTypeException
559
+	 * @throws InvalidInterfaceException
560
+	 * @throws ReflectionException
561
+	 * @throws EntityNotFoundException
562
+	 */
563
+	public static function maybe_registration(EE_Registration $registration, $extra_details = [])
564
+	{
565
+
566
+		if (! self::_verify_registration_notification_send($registration, $extra_details)) {
567
+			// no messages please
568
+			return;
569
+		}
570
+
571
+		// get all non-trashed registrations so we make sure we send messages for the right status.
572
+		$all_registrations = $registration->transaction()->registrations(
573
+			[
574
+				['REG_deleted' => false],
575
+				'order_by' => [
576
+					'Event.EVT_name'     => 'ASC',
577
+					'Attendee.ATT_lname' => 'ASC',
578
+					'Attendee.ATT_fname' => 'ASC',
579
+				],
580
+			]
581
+		);
582
+		// cached array of statuses so we only trigger messages once per status.
583
+		$statuses_sent = [];
584
+		self::_load_controller();
585
+		$mtgs = [];
586
+
587
+		// loop through registrations and trigger messages once per status.
588
+		foreach ($all_registrations as $reg) {
589
+			// already triggered?
590
+			if (in_array($reg->status_ID(), $statuses_sent)) {
591
+				continue;
592
+			}
593
+
594
+			$message_type    = EEH_MSG_Template::convert_reg_status_to_message_type($reg->status_ID());
595
+			$mtgs            = array_merge(
596
+				$mtgs,
597
+				self::$_MSG_PROCESSOR->setup_mtgs_for_all_active_messengers(
598
+					$message_type,
599
+					[$registration->transaction(), null, $reg->status_ID()]
600
+				)
601
+			);
602
+			$statuses_sent[] = $reg->status_ID();
603
+		}
604
+
605
+		if (count($statuses_sent) > 1) {
606
+			$mtgs = array_merge(
607
+				$mtgs,
608
+				self::$_MSG_PROCESSOR->setup_mtgs_for_all_active_messengers(
609
+					'registration_summary',
610
+					[$registration->transaction(), null]
611
+				)
612
+			);
613
+		}
614
+
615
+		// batch queue and initiate request
616
+		self::$_MSG_PROCESSOR->batch_queue_for_generation_and_persist($mtgs);
617
+		self::$_MSG_PROCESSOR->get_queue()->initiate_request_by_priority();
618
+	}
619
+
620
+
621
+	/**
622
+	 * This is a helper method used to very whether a registration notification should be sent or
623
+	 * not.  Prevents duplicate notifications going out for registration context notifications.
624
+	 *
625
+	 * @param EE_Registration $registration  [description]
626
+	 * @param array           $extra_details [description]
627
+	 * @return bool          true = send away, false = nope halt the presses.
628
+	 */
629
+	protected static function _verify_registration_notification_send(
630
+		EE_Registration $registration,
631
+		$extra_details = []
632
+	) {
633
+		$request = self::getRequest();
634
+		if (
635
+			! $request->getRequestParam('non_primary_reg_notification', 0, 'int')
636
+			&& ! $registration->is_primary_registrant()
637
+		) {
638
+			return false;
639
+		}
640
+		// first we check if we're in admin and not doing front ajax
641
+		if (
642
+			($request->isAdmin() || $request->isAdminAjax())
643
+			&& ! $request->isFrontAjax()
644
+		) {
645
+			$status_change = $request->getRequestParam('txn_reg_status_change', [], 'int', true);
646
+			// make sure appropriate admin params are set for sending messages
647
+			if (
648
+				! isset($status_change['send_notifications'])
649
+				|| (isset($status_change['send_notifications']) && ! $status_change['send_notifications'])
650
+			) {
651
+				// no messages sent please.
652
+				return false;
653
+			}
654
+		} else {
655
+			// frontend request (either regular or via AJAX)
656
+			// TXN is NOT finalized ?
657
+			if (! isset($extra_details['finalized']) || $extra_details['finalized'] === false) {
658
+				return false;
659
+			}
660
+			// return visit but nothing changed ???
661
+			if (
662
+				isset($extra_details['revisit'], $extra_details['status_updates'])
663
+				&& $extra_details['revisit']
664
+				&& ! $extra_details['status_updates']
665
+			) {
666
+				return false;
667
+			}
668
+			// NOT sending messages && reg status is something other than "Not-Approved"
669
+			if (
670
+				! apply_filters('FHEE__EED_Messages___maybe_registration__deliver_notifications', false)
671
+				&& $registration->status_ID() !== RegStatus::AWAITING_REVIEW
672
+			) {
673
+				return false;
674
+			}
675
+		}
676
+		// release the kraken
677
+		return true;
678
+	}
679
+
680
+
681
+	/**
682
+	 * Simply returns an array indexed by Registration Status ID and the related message_type name associated with that
683
+	 * status id.
684
+	 *
685
+	 * @param string $reg_status
686
+	 * @return array
687
+	 * @throws EE_Error
688
+	 * @throws InvalidArgumentException
689
+	 * @throws ReflectionException
690
+	 * @throws InvalidDataTypeException
691
+	 * @throws InvalidInterfaceException
692
+	 * @deprecated        4.9.0  Use EEH_MSG_Template::reg_status_to_message_type_array()
693
+	 *                    or EEH_MSG_Template::convert_reg_status_to_message_type
694
+	 */
695
+	protected static function _get_reg_status_array($reg_status = '')
696
+	{
697
+		EE_Registry::instance()->load_helper('MSG_Template');
698
+		return EEH_MSG_Template::convert_reg_status_to_message_type($reg_status)
699
+			? EEH_MSG_Template::convert_reg_status_to_message_type($reg_status)
700
+			: EEH_MSG_Template::reg_status_to_message_type_array();
701
+	}
702
+
703
+
704
+	/**
705
+	 * Simply returns the payment message type for the given payment status.
706
+	 *
707
+	 * @param string $payment_status The payment status being matched.
708
+	 * @return bool|string The payment message type slug matching the status or false if no match.
709
+	 * @throws EE_Error
710
+	 * @throws InvalidArgumentException
711
+	 * @throws ReflectionException
712
+	 * @throws InvalidDataTypeException
713
+	 * @throws InvalidInterfaceException
714
+	 * @deprecated       4.9.0 Use EEH_MSG_Template::payment_status_to_message_type_array
715
+	 *                   or EEH_MSG_Template::convert_payment_status_to_message_type
716
+	 */
717
+	protected static function _get_payment_message_type($payment_status)
718
+	{
719
+		EE_Registry::instance()->load_helper('MSG_Template');
720
+		return EEH_MSG_Template::convert_payment_status_to_message_type($payment_status)
721
+			? EEH_MSG_Template::convert_payment_status_to_message_type($payment_status)
722
+			: false;
723
+	}
724
+
725
+
726
+	/**
727
+	 * Message triggers for a resending already sent message(s) (via EE_Message list table)
728
+	 *
729
+	 * @access public
730
+	 * @param array $req_data This is the $_POST & $_GET data sent from EE_Admin Pages
731
+	 * @return bool success/fail
732
+	 * @throws EE_Error
733
+	 * @throws InvalidArgumentException
734
+	 * @throws InvalidDataTypeException
735
+	 * @throws InvalidInterfaceException
736
+	 * @throws ReflectionException
737
+	 */
738
+	public static function process_resend(array $req_data = [])
739
+	{
740
+		self::_load_controller();
741
+		$request = self::getRequest();
742
+		// if $msgID in this request then skip to the new resend_message
743
+		if ($request->getRequestParam('MSG_ID')) {
744
+			return self::resend_message();
745
+		}
746
+
747
+		// make sure any incoming request data is set on the request so that it gets picked up later.
748
+		foreach ((array) $req_data as $request_key => $request_value) {
749
+			if (! $request->requestParamIsSet($request_key)) {
750
+				$request->setRequestParam($request_key, $request_value);
751
+			}
752
+		}
753
+
754
+		if (
755
+			! $messages_to_send = self::$_MSG_PROCESSOR->setup_messages_to_generate_from_registration_ids_in_request()
756
+		) {
757
+			return false;
758
+		}
759
+
760
+		try {
761
+			self::$_MSG_PROCESSOR->batch_queue_for_generation_and_persist($messages_to_send);
762
+			self::$_MSG_PROCESSOR->get_queue()->initiate_request_by_priority();
763
+		} catch (EE_Error $e) {
764
+			EE_Error::add_error($e->getMessage(), __FILE__, __FUNCTION__, __LINE__);
765
+			return false;
766
+		}
767
+		EE_Error::add_success(
768
+			esc_html__('Messages have been successfully queued for generation and sending.', 'event_espresso')
769
+		);
770
+		return true; // everything got queued.
771
+	}
772
+
773
+
774
+	/**
775
+	 * Message triggers for a resending already sent message(s) (via EE_Message list table)
776
+	 *
777
+	 * @return bool
778
+	 * @throws EE_Error
779
+	 * @throws InvalidArgumentException
780
+	 * @throws InvalidDataTypeException
781
+	 * @throws InvalidInterfaceException
782
+	 * @throws ReflectionException
783
+	 */
784
+	public static function resend_message()
785
+	{
786
+		self::_load_controller();
787
+
788
+		$msgID = self::getRequest()->getRequestParam('MSG_ID', 0, 'int');
789
+		if (! $msgID) {
790
+			EE_Error::add_error(
791
+				esc_html__(
792
+					'Something went wrong because there is no "MSG_ID" value in the request',
793
+					'event_espresso'
794
+				),
795
+				__FILE__,
796
+				__FUNCTION__,
797
+				__LINE__
798
+			);
799
+			return false;
800
+		}
801
+
802
+		self::$_MSG_PROCESSOR->setup_messages_from_ids_and_send((array) $msgID);
803
+
804
+		// setup success message.
805
+		$count_ready_for_resend = self::$_MSG_PROCESSOR->get_queue()->count_STS_in_queue(EEM_Message::status_resend);
806
+		EE_Error::add_success(
807
+			sprintf(
808
+				_n(
809
+					'There was %d message queued for resending.',
810
+					'There were %d messages queued for resending.',
811
+					$count_ready_for_resend,
812
+					'event_espresso'
813
+				),
814
+				$count_ready_for_resend
815
+			)
816
+		);
817
+		return true;
818
+	}
819
+
820
+
821
+	/**
822
+	 * Message triggers for manual payment applied by admin
823
+	 *
824
+	 * @param EE_Payment $payment EE_payment object
825
+	 * @return bool success/fail
826
+	 * @throws EE_Error
827
+	 * @throws InvalidArgumentException
828
+	 * @throws ReflectionException
829
+	 * @throws InvalidDataTypeException
830
+	 * @throws InvalidInterfaceException
831
+	 */
832
+	public static function process_admin_payment(EE_Payment $payment)
833
+	{
834
+		EE_Registry::instance()->load_helper('MSG_Template');
835
+		// we need to get the transaction object
836
+		$transaction = $payment->transaction();
837
+		if ($transaction instanceof EE_Transaction) {
838
+			$data         = [$transaction, $payment];
839
+			$message_type = EEH_MSG_Template::convert_payment_status_to_message_type($payment->STS_ID());
840
+
841
+			// if payment amount is less than 0 then switch to payment_refund message type.
842
+			$message_type = $payment->amount() < 0 ? 'payment_refund' : $message_type;
843
+
844
+			// if payment_refund is selected, but the status is NOT accepted.  Then change message type to false so NO message notification goes out.
845
+			$message_type = $message_type == 'payment_refund' && $payment->STS_ID() != EEM_Payment::status_id_approved
846
+				? false : $message_type;
847
+
848
+			self::_load_controller();
849
+
850
+			self::$_MSG_PROCESSOR->generate_for_all_active_messengers($message_type, $data);
851
+
852
+			// get count of queued for generation
853
+			$count_to_generate = self::$_MSG_PROCESSOR->get_queue()->count_STS_in_queue(
854
+				[
855
+					EEM_Message::status_incomplete,
856
+					EEM_Message::status_idle,
857
+				]
858
+			);
859
+
860
+			if ($count_to_generate > 0 && self::$_MSG_PROCESSOR->get_queue()->get_message_repository()->count() !== 0) {
861
+				add_filter('FHEE__EE_Admin_Page___process_admin_payment_notification__success', '__return_true');
862
+				return true;
863
+			} else {
864
+				$count_failed = self::$_MSG_PROCESSOR->get_queue()->count_STS_in_queue(
865
+					EEM_Message::instance()->stati_indicating_failed_sending()
866
+				);
867
+				/**
868
+				 * Verify that there are actually errors.  If not then we return a success message because the queue might have been emptied due to successful
869
+				 * IMMEDIATE generation.
870
+				 */
871
+				if ($count_failed > 0) {
872
+					EE_Error::add_error(
873
+						sprintf(
874
+							_n(
875
+								'The payment notification generation failed.',
876
+								'%d payment notifications failed being sent.',
877
+								$count_failed,
878
+								'event_espresso'
879
+							),
880
+							$count_failed
881
+						),
882
+						__FILE__,
883
+						__FUNCTION__,
884
+						__LINE__
885
+					);
886
+
887
+					return false;
888
+				} else {
889
+					add_filter('FHEE__EE_Admin_Page___process_admin_payment_notification__success', '__return_true');
890
+					return true;
891
+				}
892
+			}
893
+		} else {
894
+			EE_Error::add_error(
895
+				esc_html__(
896
+					'Unable to generate the payment notification because the given value for the transaction is invalid.',
897
+					'event_espresso'
898
+				),
899
+				__FILE__,
900
+				__FUNCTION__,
901
+				__LINE__
902
+			);
903
+			return false;
904
+		}
905
+	}
906
+
907
+
908
+	/**
909
+	 * Callback for AHEE__Extend_Registrations_Admin_Page___newsletter_selected_send_with_registrations trigger
910
+	 *
911
+	 * @param EE_Registration[] $registrations an array of EE_Registration objects
912
+	 * @param int               $grp_id        a specific message template group id.
913
+	 * @return void
914
+	 * @throws EE_Error
915
+	 * @throws InvalidArgumentException
916
+	 * @throws InvalidDataTypeException
917
+	 * @throws InvalidInterfaceException
918
+	 * @throws ReflectionException
919
+	 * @since   4.3.0
920
+	 */
921
+	public static function send_newsletter_message($registrations, $grp_id)
922
+	{
923
+		// make sure mtp is id and set it in the request later messages setup.
924
+		self::getRequest()->setRequestParam('GRP_ID', (int) $grp_id);
925
+		self::_load_controller();
926
+		self::$_MSG_PROCESSOR->generate_for_all_active_messengers('newsletter', $registrations);
927
+	}
928
+
929
+
930
+	/**
931
+	 * Callback for FHEE__EE_Registration__invoice_url__invoice_url or FHEE__EE_Registration__receipt_url__receipt_url
932
+	 *
933
+	 * @param string          $registration_message_trigger_url
934
+	 * @param EE_Registration $registration
935
+	 * @param string          $messenger
936
+	 * @param string          $message_type
937
+	 * @return string
938
+	 * @throws EE_Error
939
+	 * @throws InvalidArgumentException
940
+	 * @throws InvalidDataTypeException
941
+	 * @throws InvalidInterfaceException
942
+	 * @throws ReflectionException
943
+	 * @since   4.3.0
944
+	 */
945
+	public static function registration_message_trigger_url(
946
+		$registration_message_trigger_url,
947
+		EE_Registration $registration,
948
+		$messenger = 'html',
949
+		$message_type = 'invoice'
950
+	) {
951
+		// whitelist $messenger
952
+		switch ($messenger) {
953
+			case 'pdf':
954
+				$sending_messenger    = 'pdf';
955
+				$generating_messenger = 'html';
956
+				break;
957
+			case 'html':
958
+			default:
959
+				$sending_messenger    = 'html';
960
+				$generating_messenger = 'html';
961
+				break;
962
+		}
963
+		// whitelist $message_type
964
+		switch ($message_type) {
965
+			case 'receipt':
966
+				$message_type = 'receipt';
967
+				break;
968
+			case 'invoice':
969
+			default:
970
+				$message_type = 'invoice';
971
+				break;
972
+		}
973
+		// verify that both the messenger AND the message type are active
974
+		if (
975
+			EEH_MSG_Template::is_messenger_active($sending_messenger)
976
+			&& EEH_MSG_Template::is_mt_active($message_type)
977
+		) {
978
+			// need to get the correct message template group for this (i.e. is there a custom invoice for the event this registration is registered for?)
979
+			$template_query_params = [
980
+				'MTP_is_active'    => true,
981
+				'MTP_messenger'    => $generating_messenger,
982
+				'MTP_message_type' => $message_type,
983
+				'Event.EVT_ID'     => $registration->event_ID(),
984
+			];
985
+			// get the message template group.
986
+			$msg_template_group = EEM_Message_Template_Group::instance()->get_one([$template_query_params]);
987
+			// if we don't have an EE_Message_Template_Group then return
988
+			if (! $msg_template_group instanceof EE_Message_Template_Group) {
989
+				// remove EVT_ID from query params so that global templates get picked up
990
+				unset($template_query_params['Event.EVT_ID']);
991
+				// get global template as the fallback
992
+				$msg_template_group = EEM_Message_Template_Group::instance()->get_one([$template_query_params]);
993
+			}
994
+			// if we don't have an EE_Message_Template_Group then return
995
+			if (! $msg_template_group instanceof EE_Message_Template_Group) {
996
+				return '';
997
+			}
998
+			// generate the URL
999
+			$registration_message_trigger_url = EEH_MSG_Template::generate_url_trigger(
1000
+				$sending_messenger,
1001
+				$generating_messenger,
1002
+				'purchaser',
1003
+				$message_type,
1004
+				$registration,
1005
+				$msg_template_group->ID(),
1006
+				$registration->transaction_ID()
1007
+			);
1008
+		}
1009
+		return $registration_message_trigger_url;
1010
+	}
1011
+
1012
+
1013
+	/**
1014
+	 * Use to generate and return a message preview!
1015
+	 *
1016
+	 * @param string $type       This should correspond with a valid message type
1017
+	 * @param string $context    This should correspond with a valid context for the message type
1018
+	 * @param string $messenger  This should correspond with a valid messenger.
1019
+	 * @param bool   $send       true we will do a test send using the messenger delivery, false we just do a regular
1020
+	 *                           preview
1021
+	 * @return bool|string The body of the message or if send is requested, sends.
1022
+	 * @throws EE_Error
1023
+	 * @throws InvalidArgumentException
1024
+	 * @throws InvalidDataTypeException
1025
+	 * @throws InvalidInterfaceException
1026
+	 * @throws ReflectionException
1027
+	 */
1028
+	public static function preview_message($type, $context, $messenger, $send = false)
1029
+	{
1030
+		self::_load_controller();
1031
+		$message_to_generate     = new EE_Message_To_Generate(
1032
+			$messenger,
1033
+			$type,
1034
+			[],
1035
+			$context,
1036
+			true
1037
+		);
1038
+		$generated_preview_queue = self::$_MSG_PROCESSOR->generate_for_preview($message_to_generate, $send);
1039
+
1040
+		if ($generated_preview_queue instanceof EE_Messages_Queue) {
1041
+			// loop through all content for the preview and remove any persisted records.
1042
+			$content = '';
1043
+			foreach ($generated_preview_queue->get_message_repository() as $message) {
1044
+				$content = $message->content();
1045
+				if ($message->ID() > 0 && $message->STS_ID() !== EEM_Message::status_failed) {
1046
+					$message->delete();
1047
+				}
1048
+			}
1049
+			return $content;
1050
+		}
1051
+		return $generated_preview_queue;
1052
+	}
1053
+
1054
+
1055
+	/**
1056
+	 * This is a method that allows for sending a message using a messenger matching the string given and the provided
1057
+	 * EE_Message_Queue object.  The EE_Message_Queue object is used to create a single aggregate EE_Message via the
1058
+	 * content found in the EE_Message objects in the queue.
1059
+	 *
1060
+	 * @param string            $messenger            a string matching a valid active messenger in the system
1061
+	 * @param string            $message_type         Although it seems contrary to the name of the method, a message
1062
+	 *                                                type name is still required to send along the message type to the
1063
+	 *                                                messenger because this is used for determining what specific
1064
+	 *                                                variations might be loaded for the generated message.
1065
+	 * @param EE_Messages_Queue $queue
1066
+	 * @param string            $custom_subject       Can be used to set what the custom subject string will be on the
1067
+	 *                                                aggregate EE_Message object.
1068
+	 * @return bool success or fail.
1069
+	 * @throws EE_Error
1070
+	 * @throws InvalidArgumentException
1071
+	 * @throws ReflectionException
1072
+	 * @throws InvalidDataTypeException
1073
+	 * @throws InvalidInterfaceException
1074
+	 * @since 4.9.0
1075
+	 */
1076
+	public static function send_message_with_messenger_only(
1077
+		$messenger,
1078
+		$message_type,
1079
+		EE_Messages_Queue $queue,
1080
+		$custom_subject = ''
1081
+	) {
1082
+		self::_load_controller();
1083
+		/** @type EE_Message_To_Generate_From_Queue $message_to_generate */
1084
+		$message_to_generate = EE_Registry::instance()->load_lib(
1085
+			'Message_To_Generate_From_Queue',
1086
+			[
1087
+				$messenger,
1088
+				$message_type,
1089
+				$queue,
1090
+				$custom_subject,
1091
+			]
1092
+		);
1093
+		return self::$_MSG_PROCESSOR->queue_for_sending($message_to_generate);
1094
+	}
1095
+
1096
+
1097
+	/**
1098
+	 * Generates Messages immediately for EE_Message IDs (but only for the correct status for generation)
1099
+	 *
1100
+	 * @param array $message_ids An array of message ids
1101
+	 * @return bool|EE_Messages_Queue false if nothing was generated, EE_Messages_Queue containing generated
1102
+	 *                           messages.
1103
+	 * @throws EE_Error
1104
+	 * @throws InvalidArgumentException
1105
+	 * @throws InvalidDataTypeException
1106
+	 * @throws InvalidInterfaceException
1107
+	 * @throws ReflectionException
1108
+	 * @since 4.9.0
1109
+	 */
1110
+	public static function generate_now($message_ids)
1111
+	{
1112
+		self::_load_controller();
1113
+		$messages        = EEM_Message::instance()->get_all(
1114
+			[
1115
+				0 => [
1116
+					'MSG_ID' => ['IN', $message_ids],
1117
+					'STS_ID' => EEM_Message::status_incomplete,
1118
+				],
1119
+			]
1120
+		);
1121
+		$generated_queue = false;
1122
+		if ($messages) {
1123
+			$generated_queue = self::$_MSG_PROCESSOR->batch_generate_from_queue($messages);
1124
+		}
1125
+
1126
+		if (! $generated_queue instanceof EE_Messages_Queue) {
1127
+			EE_Error::add_error(
1128
+				esc_html__(
1129
+					'The messages were not generated. This could mean there is already a batch being generated on a separate request, or because the selected messages are not ready for generation. Please wait a minute or two and try again.',
1130
+					'event_espresso'
1131
+				),
1132
+				__FILE__,
1133
+				__FUNCTION__,
1134
+				__LINE__
1135
+			);
1136
+		}
1137
+		return $generated_queue;
1138
+	}
1139
+
1140
+
1141
+	/**
1142
+	 * Sends messages immediately for the incoming message_ids that have the status of EEM_Message::status_resend or,
1143
+	 * EEM_Message::status_idle
1144
+	 *
1145
+	 * @param $message_ids
1146
+	 * @return bool|EE_Messages_Queue false if no messages sent.
1147
+	 * @throws EE_Error
1148
+	 * @throws InvalidArgumentException
1149
+	 * @throws InvalidDataTypeException
1150
+	 * @throws InvalidInterfaceException
1151
+	 * @throws ReflectionException
1152
+	 * @since 4.9.0
1153
+	 */
1154
+	public static function send_now($message_ids)
1155
+	{
1156
+		self::_load_controller();
1157
+		$messages   = EEM_Message::instance()->get_all(
1158
+			[
1159
+				0 => [
1160
+					'MSG_ID' => ['IN', $message_ids],
1161
+					'STS_ID' => [
1162
+						'IN',
1163
+						[EEM_Message::status_idle, EEM_Message::status_resend, EEM_Message::status_retry],
1164
+					],
1165
+				],
1166
+			]
1167
+		);
1168
+		$sent_queue = false;
1169
+		if ($messages) {
1170
+			$sent_queue = self::$_MSG_PROCESSOR->batch_send_from_queue($messages);
1171
+		}
1172
+
1173
+		if (! $sent_queue instanceof EE_Messages_Queue) {
1174
+			EE_Error::add_error(
1175
+				esc_html__(
1176
+					'The messages were not sent. This could mean there is already a batch being sent on a separate request, or because the selected messages are not sendable. Please wait a minute or two and try again.',
1177
+					'event_espresso'
1178
+				),
1179
+				__FILE__,
1180
+				__FUNCTION__,
1181
+				__LINE__
1182
+			);
1183
+		} else {
1184
+			// can count how many sent by using the messages in the queue
1185
+			$sent_count = $sent_queue->count_STS_in_queue(EEM_Message::instance()->stati_indicating_sent());
1186
+			if ($sent_count > 0) {
1187
+				EE_Error::add_success(
1188
+					sprintf(
1189
+						_n(
1190
+							'There was %d message successfully sent.',
1191
+							'There were %d messages successfully sent.',
1192
+							$sent_count,
1193
+							'event_espresso'
1194
+						),
1195
+						$sent_count
1196
+					)
1197
+				);
1198
+			} else {
1199
+				EE_Error::overwrite_errors();
1200
+				EE_Error::add_error(
1201
+					esc_html__(
1202
+						'No message was sent because of problems with sending. Either all the messages you selected were not a sendable message, they were ALREADY sent on a different scheduled task, or there was an error.
1203 1203
 					If there was an error, you can look at the messages in the message activity list table for any error messages.',
1204
-                        'event_espresso'
1205
-                    ),
1206
-                    __FILE__,
1207
-                    __FUNCTION__,
1208
-                    __LINE__
1209
-                );
1210
-            }
1211
-        }
1212
-        return $sent_queue;
1213
-    }
1214
-
1215
-
1216
-    /**
1217
-     * Generate and send immediately from the given $message_ids
1218
-     *
1219
-     * @param array $message_ids EE_Message entity ids.
1220
-     * @throws EE_Error
1221
-     * @throws InvalidArgumentException
1222
-     * @throws InvalidDataTypeException
1223
-     * @throws InvalidInterfaceException
1224
-     * @throws ReflectionException
1225
-     */
1226
-    public static function generate_and_send_now(array $message_ids)
1227
-    {
1228
-        $generated_queue = self::generate_now($message_ids);
1229
-        // now let's just trigger sending immediately from this queue.
1230
-        $messages_sent = $generated_queue instanceof EE_Messages_Queue
1231
-            ? $generated_queue->execute()
1232
-            : 0;
1233
-        if ($messages_sent) {
1234
-            EE_Error::add_success(
1235
-                esc_html(
1236
-                    sprintf(
1237
-                        _n(
1238
-                            'There was %d message successfully generated and sent.',
1239
-                            'There were %d messages successfully generated and sent.',
1240
-                            $messages_sent,
1241
-                            'event_espresso'
1242
-                        ),
1243
-                        $messages_sent
1244
-                    )
1245
-                )
1246
-            );
1247
-            // errors would be added via the generate_now method.
1248
-        }
1249
-    }
1250
-
1251
-
1252
-    /**
1253
-     * This will queue the incoming message ids for resending.
1254
-     * Note, only message_ids corresponding to messages with the status of EEM_Message::sent will be queued.
1255
-     *
1256
-     * @param array $message_ids An array of EE_Message IDs
1257
-     * @return bool true means messages were successfully queued for resending, false means none were queued for
1258
-     *                           resending.
1259
-     * @throws EE_Error
1260
-     * @throws InvalidArgumentException
1261
-     * @throws InvalidDataTypeException
1262
-     * @throws InvalidInterfaceException
1263
-     * @throws ReflectionException
1264
-     * @since 4.9.0
1265
-     */
1266
-    public static function queue_for_resending($message_ids)
1267
-    {
1268
-        self::_load_controller();
1269
-        self::$_MSG_PROCESSOR->setup_messages_from_ids_and_send($message_ids);
1270
-
1271
-        // get queue and count
1272
-        $queue_count = self::$_MSG_PROCESSOR->get_queue()->count_STS_in_queue(EEM_Message::status_resend);
1273
-
1274
-        if (
1275
-            $queue_count > 0
1276
-        ) {
1277
-            EE_Error::add_success(
1278
-                sprintf(
1279
-                    _n(
1280
-                        '%d message successfully queued for resending.',
1281
-                        '%d messages successfully queued for resending.',
1282
-                        $queue_count,
1283
-                        'event_espresso'
1284
-                    ),
1285
-                    $queue_count
1286
-                )
1287
-            );
1288
-            /**
1289
-             * @see filter usage in EE_Messages_Queue::initiate_request_by_priority
1290
-             */
1291
-        } elseif (
1292
-            apply_filters('FHEE__EE_Messages_Processor__initiate_request_by_priority__do_immediate_processing', true)
1293
-            || EE_Registry::instance()->NET_CFG->core->do_messages_on_same_request
1294
-        ) {
1295
-            $queue_count = self::$_MSG_PROCESSOR->get_queue()->count_STS_in_queue(EEM_Message::status_sent);
1296
-            if ($queue_count > 0) {
1297
-                EE_Error::add_success(
1298
-                    sprintf(
1299
-                        _n(
1300
-                            '%d message successfully sent.',
1301
-                            '%d messages successfully sent.',
1302
-                            $queue_count,
1303
-                            'event_espresso'
1304
-                        ),
1305
-                        $queue_count
1306
-                    )
1307
-                );
1308
-            } else {
1309
-                EE_Error::add_error(
1310
-                    esc_html__(
1311
-                        'No messages were queued for resending. This usually only happens when all the messages flagged for resending are not a status that can be resent.',
1312
-                        'event_espresso'
1313
-                    ),
1314
-                    __FILE__,
1315
-                    __FUNCTION__,
1316
-                    __LINE__
1317
-                );
1318
-            }
1319
-        } else {
1320
-            EE_Error::add_error(
1321
-                esc_html__(
1322
-                    'No messages were queued for resending. This usually only happens when all the messages flagged for resending are not a status that can be resent.',
1323
-                    'event_espresso'
1324
-                ),
1325
-                __FILE__,
1326
-                __FUNCTION__,
1327
-                __LINE__
1328
-            );
1329
-        }
1330
-        return (bool) $queue_count;
1331
-    }
1332
-
1333
-
1334
-    /**
1335
-     * debug
1336
-     *
1337
-     * @param string              $class
1338
-     * @param string              $func
1339
-     * @param string              $line
1340
-     * @param EE_Transaction|null $transaction
1341
-     * @param array               $info
1342
-     * @param bool                $display_request
1343
-     * @throws EE_Error
1344
-     * @throws ReflectionException
1345
-     * @throws InvalidSessionDataException
1346
-     */
1347
-    protected static function log(
1348
-        $class = '',
1349
-        $func = '',
1350
-        $line = '',
1351
-        EE_Transaction $transaction = null,
1352
-        $info = [],
1353
-        $display_request = false
1354
-    ) {
1355
-        if (defined('EE_DEBUG') && EE_DEBUG) {
1356
-            if ($transaction instanceof EE_Transaction) {
1357
-                // don't serialize objects
1358
-                $info                  = EEH_Debug_Tools::strip_objects($info);
1359
-                $info['TXN_status']    = $transaction->status_ID();
1360
-                $info['TXN_reg_steps'] = $transaction->reg_steps();
1361
-                if ($transaction->ID()) {
1362
-                    $index = 'EE_Transaction: ' . $transaction->ID();
1363
-                    EEH_Debug_Tools::log($class, $func, $line, $info, $display_request, $index);
1364
-                }
1365
-            }
1366
-        }
1367
-    }
1368
-
1369
-
1370
-    /**
1371
-     *  Resets all the static properties in this class when called.
1372
-     */
1373
-    public static function reset()
1374
-    {
1375
-        self::$_EEMSG                    = null;
1376
-        self::$_message_resource_manager = null;
1377
-        self::$_MSG_PROCESSOR            = null;
1378
-        self::$_MSG_PATHS                = null;
1379
-        self::$_TMP_PACKS                = [];
1380
-    }
1204
+						'event_espresso'
1205
+					),
1206
+					__FILE__,
1207
+					__FUNCTION__,
1208
+					__LINE__
1209
+				);
1210
+			}
1211
+		}
1212
+		return $sent_queue;
1213
+	}
1214
+
1215
+
1216
+	/**
1217
+	 * Generate and send immediately from the given $message_ids
1218
+	 *
1219
+	 * @param array $message_ids EE_Message entity ids.
1220
+	 * @throws EE_Error
1221
+	 * @throws InvalidArgumentException
1222
+	 * @throws InvalidDataTypeException
1223
+	 * @throws InvalidInterfaceException
1224
+	 * @throws ReflectionException
1225
+	 */
1226
+	public static function generate_and_send_now(array $message_ids)
1227
+	{
1228
+		$generated_queue = self::generate_now($message_ids);
1229
+		// now let's just trigger sending immediately from this queue.
1230
+		$messages_sent = $generated_queue instanceof EE_Messages_Queue
1231
+			? $generated_queue->execute()
1232
+			: 0;
1233
+		if ($messages_sent) {
1234
+			EE_Error::add_success(
1235
+				esc_html(
1236
+					sprintf(
1237
+						_n(
1238
+							'There was %d message successfully generated and sent.',
1239
+							'There were %d messages successfully generated and sent.',
1240
+							$messages_sent,
1241
+							'event_espresso'
1242
+						),
1243
+						$messages_sent
1244
+					)
1245
+				)
1246
+			);
1247
+			// errors would be added via the generate_now method.
1248
+		}
1249
+	}
1250
+
1251
+
1252
+	/**
1253
+	 * This will queue the incoming message ids for resending.
1254
+	 * Note, only message_ids corresponding to messages with the status of EEM_Message::sent will be queued.
1255
+	 *
1256
+	 * @param array $message_ids An array of EE_Message IDs
1257
+	 * @return bool true means messages were successfully queued for resending, false means none were queued for
1258
+	 *                           resending.
1259
+	 * @throws EE_Error
1260
+	 * @throws InvalidArgumentException
1261
+	 * @throws InvalidDataTypeException
1262
+	 * @throws InvalidInterfaceException
1263
+	 * @throws ReflectionException
1264
+	 * @since 4.9.0
1265
+	 */
1266
+	public static function queue_for_resending($message_ids)
1267
+	{
1268
+		self::_load_controller();
1269
+		self::$_MSG_PROCESSOR->setup_messages_from_ids_and_send($message_ids);
1270
+
1271
+		// get queue and count
1272
+		$queue_count = self::$_MSG_PROCESSOR->get_queue()->count_STS_in_queue(EEM_Message::status_resend);
1273
+
1274
+		if (
1275
+			$queue_count > 0
1276
+		) {
1277
+			EE_Error::add_success(
1278
+				sprintf(
1279
+					_n(
1280
+						'%d message successfully queued for resending.',
1281
+						'%d messages successfully queued for resending.',
1282
+						$queue_count,
1283
+						'event_espresso'
1284
+					),
1285
+					$queue_count
1286
+				)
1287
+			);
1288
+			/**
1289
+			 * @see filter usage in EE_Messages_Queue::initiate_request_by_priority
1290
+			 */
1291
+		} elseif (
1292
+			apply_filters('FHEE__EE_Messages_Processor__initiate_request_by_priority__do_immediate_processing', true)
1293
+			|| EE_Registry::instance()->NET_CFG->core->do_messages_on_same_request
1294
+		) {
1295
+			$queue_count = self::$_MSG_PROCESSOR->get_queue()->count_STS_in_queue(EEM_Message::status_sent);
1296
+			if ($queue_count > 0) {
1297
+				EE_Error::add_success(
1298
+					sprintf(
1299
+						_n(
1300
+							'%d message successfully sent.',
1301
+							'%d messages successfully sent.',
1302
+							$queue_count,
1303
+							'event_espresso'
1304
+						),
1305
+						$queue_count
1306
+					)
1307
+				);
1308
+			} else {
1309
+				EE_Error::add_error(
1310
+					esc_html__(
1311
+						'No messages were queued for resending. This usually only happens when all the messages flagged for resending are not a status that can be resent.',
1312
+						'event_espresso'
1313
+					),
1314
+					__FILE__,
1315
+					__FUNCTION__,
1316
+					__LINE__
1317
+				);
1318
+			}
1319
+		} else {
1320
+			EE_Error::add_error(
1321
+				esc_html__(
1322
+					'No messages were queued for resending. This usually only happens when all the messages flagged for resending are not a status that can be resent.',
1323
+					'event_espresso'
1324
+				),
1325
+				__FILE__,
1326
+				__FUNCTION__,
1327
+				__LINE__
1328
+			);
1329
+		}
1330
+		return (bool) $queue_count;
1331
+	}
1332
+
1333
+
1334
+	/**
1335
+	 * debug
1336
+	 *
1337
+	 * @param string              $class
1338
+	 * @param string              $func
1339
+	 * @param string              $line
1340
+	 * @param EE_Transaction|null $transaction
1341
+	 * @param array               $info
1342
+	 * @param bool                $display_request
1343
+	 * @throws EE_Error
1344
+	 * @throws ReflectionException
1345
+	 * @throws InvalidSessionDataException
1346
+	 */
1347
+	protected static function log(
1348
+		$class = '',
1349
+		$func = '',
1350
+		$line = '',
1351
+		EE_Transaction $transaction = null,
1352
+		$info = [],
1353
+		$display_request = false
1354
+	) {
1355
+		if (defined('EE_DEBUG') && EE_DEBUG) {
1356
+			if ($transaction instanceof EE_Transaction) {
1357
+				// don't serialize objects
1358
+				$info                  = EEH_Debug_Tools::strip_objects($info);
1359
+				$info['TXN_status']    = $transaction->status_ID();
1360
+				$info['TXN_reg_steps'] = $transaction->reg_steps();
1361
+				if ($transaction->ID()) {
1362
+					$index = 'EE_Transaction: ' . $transaction->ID();
1363
+					EEH_Debug_Tools::log($class, $func, $line, $info, $display_request, $index);
1364
+				}
1365
+			}
1366
+		}
1367
+	}
1368
+
1369
+
1370
+	/**
1371
+	 *  Resets all the static properties in this class when called.
1372
+	 */
1373
+	public static function reset()
1374
+	{
1375
+		self::$_EEMSG                    = null;
1376
+		self::$_message_resource_manager = null;
1377
+		self::$_MSG_PROCESSOR            = null;
1378
+		self::$_MSG_PATHS                = null;
1379
+		self::$_TMP_PACKS                = [];
1380
+	}
1381 1381
 }
Please login to merge, or discard this patch.
modules/ticket_sales_monitor/EED_Ticket_Sales_Monitor.module.php 1 patch
Indentation   +1068 added lines, -1068 removed lines patch added patch discarded remove patch
@@ -19,1074 +19,1074 @@
 block discarded – undo
19 19
  */
20 20
 class EED_Ticket_Sales_Monitor extends EED_Module
21 21
 {
22
-    const debug = false;    // true false
23
-
24
-    private static $nl = '';
25
-
26
-    /**
27
-     * an array for tracking names of tickets that have sold out
28
-     *
29
-     * @var array $sold_out_tickets
30
-     */
31
-    protected $sold_out_tickets = array();
32
-
33
-    /**
34
-     * an array for tracking names of tickets that have had their quantities reduced
35
-     *
36
-     * @var array $decremented_tickets
37
-     */
38
-    protected $decremented_tickets = array();
39
-
40
-
41
-    /**
42
-     * set_hooks - for hooking into EE Core, other modules, etc
43
-     *
44
-     * @return    void
45
-     */
46
-    public static function set_hooks()
47
-    {
48
-        self::$nl = defined('EE_TESTS_DIR') ? "\n" : '<br />';
49
-        // release tickets for expired carts
50
-        add_action(
51
-            'EED_Ticket_Selector__process_ticket_selections__before',
52
-            array('EED_Ticket_Sales_Monitor', 'release_tickets_for_expired_carts'),
53
-            1
54
-        );
55
-        // check ticket reserves AFTER MER does it's check (hence priority 20)
56
-        add_filter(
57
-            'FHEE__EE_Ticket_Selector___add_ticket_to_cart__ticket_qty',
58
-            array('EED_Ticket_Sales_Monitor', 'validate_ticket_sale'),
59
-            20,
60
-            3
61
-        );
62
-        // add notices for sold out tickets
63
-        add_action(
64
-            'AHEE__EE_Ticket_Selector__process_ticket_selections__after_tickets_added_to_cart',
65
-            array('EED_Ticket_Sales_Monitor', 'post_notices'),
66
-            10
67
-        );
68
-
69
-        // handle tickets deleted from cart
70
-        add_action(
71
-            'FHEE__EED_Multi_Event_Registration__delete_ticket__ticket_removed_from_cart',
72
-            array('EED_Ticket_Sales_Monitor', 'ticket_removed_from_cart'),
73
-            10,
74
-            2
75
-        );
76
-        // handle emptied carts
77
-        add_action(
78
-            'AHEE__EE_Session__reset_cart__before_reset',
79
-            array('EED_Ticket_Sales_Monitor', 'session_cart_reset'),
80
-            10,
81
-            1
82
-        );
83
-        add_action(
84
-            'AHEE__EED_Multi_Event_Registration__empty_event_cart__before_delete_cart',
85
-            array('EED_Ticket_Sales_Monitor', 'session_cart_reset'),
86
-            10,
87
-            1
88
-        );
89
-        // handle cancelled registrations
90
-        add_action(
91
-            'AHEE__EE_Session__reset_checkout__before_reset',
92
-            array('EED_Ticket_Sales_Monitor', 'session_checkout_reset'),
93
-            10,
94
-            1
95
-        );
96
-        // cron tasks
97
-        add_action(
98
-            'AHEE__EE_Cron_Tasks__process_expired_transactions__abandoned_transaction',
99
-            array('EED_Ticket_Sales_Monitor', 'process_abandoned_transactions'),
100
-            10,
101
-            1
102
-        );
103
-        add_action(
104
-            'AHEE__EE_Cron_Tasks__process_expired_transactions__incomplete_transaction',
105
-            array('EED_Ticket_Sales_Monitor', 'process_abandoned_transactions'),
106
-            10,
107
-            1
108
-        );
109
-        add_action(
110
-            'AHEE__EE_Cron_Tasks__process_expired_transactions__failed_transaction',
111
-            array('EED_Ticket_Sales_Monitor', 'process_failed_transactions'),
112
-            10,
113
-            1
114
-        );
115
-    }
116
-
117
-
118
-    /**
119
-     * set_hooks_admin - for hooking into EE Admin Core, other modules, etc
120
-     *
121
-     * @return void
122
-     */
123
-    public static function set_hooks_admin()
124
-    {
125
-        EED_Ticket_Sales_Monitor::set_hooks();
126
-    }
127
-
128
-
129
-    /**
130
-     * @return EED_Ticket_Sales_Monitor|EED_Module
131
-     * @throws EE_Error
132
-     * @throws ReflectionException
133
-     */
134
-    public static function instance()
135
-    {
136
-        return parent::get_instance(__CLASS__);
137
-    }
138
-
139
-
140
-    /**
141
-     * @param WP_Query $WP
142
-     * @return    void
143
-     */
144
-    public function run($WP)
145
-    {
146
-    }
147
-
148
-
149
-
150
-    /********************************** PRE_TICKET_SALES  **********************************/
151
-
152
-
153
-    /**
154
-     * Retrieves grand totals from the line items that have no TXN ID
155
-     * and timestamps less than the current time minus the session lifespan.
156
-     * These are carts that have been abandoned before the "registrant" even attempted to checkout.
157
-     * We're going to release the tickets for these line items before attempting to add more to the cart.
158
-     *
159
-     * @return void
160
-     * @throws DomainException
161
-     * @throws EE_Error
162
-     * @throws InvalidArgumentException
163
-     * @throws InvalidDataTypeException
164
-     * @throws InvalidInterfaceException
165
-     * @throws UnexpectedEntityException
166
-     * @throws ReflectionException
167
-     */
168
-    public static function release_tickets_for_expired_carts()
169
-    {
170
-        // self::debug hardcoded to false
171
-        if (self::debug) {
172
-            echo self::$nl . self::$nl . __LINE__ . ') ' . __METHOD__ . '()';
173
-        }
174
-        do_action('AHEE__EED_Ticket_Sales_Monitor__release_tickets_for_expired_carts__begin');
175
-        $expired_ticket_IDs = array();
176
-        /** @var EventEspresso\core\domain\values\session\SessionLifespan $session_lifespan */
177
-        $session_lifespan = LoaderFactory::getLoader()->getShared(
178
-            'EventEspresso\core\domain\values\session\SessionLifespan'
179
-        );
180
-        $timestamp = $session_lifespan->expiration();
181
-        $expired_ticket_line_items = EEM_Line_Item::instance()->getTicketLineItemsForExpiredCarts($timestamp);
182
-        if (self::debug) {
183
-            echo self::$nl . ' . time(): ' . time();
184
-            echo self::$nl . ' . time() as date: ' . date('Y-m-d H:i a');
185
-            echo self::$nl . ' . session expiration: ' . $session_lifespan->expiration();
186
-            echo self::$nl . ' . session expiration as date: ' . date('Y-m-d H:i a', $session_lifespan->expiration());
187
-            echo self::$nl . ' . timestamp: ' . $timestamp;
188
-            echo self::$nl . ' . $expired_ticket_line_items: ' . count($expired_ticket_line_items);
189
-        }
190
-        if (! empty($expired_ticket_line_items)) {
191
-            foreach ($expired_ticket_line_items as $expired_ticket_line_item) {
192
-                if (! $expired_ticket_line_item instanceof EE_Line_Item) {
193
-                    continue;
194
-                }
195
-                $expired_ticket_IDs[ $expired_ticket_line_item->OBJ_ID() ] = $expired_ticket_line_item->OBJ_ID();
196
-                if (self::debug) {
197
-                    echo self::$nl . ' . $expired_ticket_line_item->OBJ_ID(): ' . $expired_ticket_line_item->OBJ_ID();
198
-                    echo self::$nl . ' . $expired_ticket_line_item->timestamp(): '
199
-                         . date(
200
-                             'Y-m-d h:i a',
201
-                             $expired_ticket_line_item->timestamp(true)
202
-                         );
203
-                }
204
-            }
205
-            if (! empty($expired_ticket_IDs)) {
206
-                EED_Ticket_Sales_Monitor::release_reservations_for_tickets(
207
-                    EEM_Ticket::instance()->get_tickets_with_IDs($expired_ticket_IDs),
208
-                    array(),
209
-                    __FUNCTION__
210
-                );
211
-                // now  let's get rid of expired line items so that they can't interfere with tracking
212
-                EED_Ticket_Sales_Monitor::clear_expired_line_items_with_no_transaction($timestamp);
213
-            }
214
-        }
215
-        do_action(
216
-            'AHEE__EED_Ticket_Sales_Monitor__release_tickets_for_expired_carts__end',
217
-            $expired_ticket_IDs,
218
-            $expired_ticket_line_items
219
-        );
220
-    }
221
-
222
-
223
-
224
-    /********************************** VALIDATE_TICKET_SALE  **********************************/
225
-
226
-
227
-    /**
228
-     * callback for 'FHEE__EED_Ticket_Selector__process_ticket_selections__valid_post_data'
229
-     *
230
-     * @param int       $qty
231
-     * @param EE_Ticket $ticket
232
-     * @return int
233
-     * @throws UnexpectedEntityException
234
-     * @throws EE_Error
235
-     * @throws ReflectionException
236
-     */
237
-    public static function validate_ticket_sale(int $qty, EE_Ticket $ticket): int
238
-    {
239
-        $qty = absint($qty);
240
-        if ($qty > 0) {
241
-            $qty = EED_Ticket_Sales_Monitor::instance()->_validate_ticket_sale($ticket, $qty);
242
-        }
243
-        // self::debug hardcoded to false
244
-        if (self::debug) {
245
-            echo self::$nl . self::$nl . __LINE__ . ') ' . __METHOD__ . '()';
246
-            echo self::$nl . self::$nl . '<b> RETURNED QTY: ' . $qty . '</b>';
247
-        }
248
-        return $qty;
249
-    }
250
-
251
-
252
-    /**
253
-     * checks whether an individual ticket is available for purchase based on datetime, and ticket details
254
-     *
255
-     * @param EE_Ticket|null $ticket
256
-     * @param int       $qty
257
-     * @return int
258
-     * @throws UnexpectedEntityException
259
-     * @throws EE_Error
260
-     * @throws ReflectionException
261
-     */
262
-    protected function _validate_ticket_sale(?EE_Ticket $ticket, int $qty = 1): int
263
-    {
264
-        // self::debug hardcoded to false
265
-        if (self::debug) {
266
-            echo self::$nl . self::$nl . __LINE__ . ') ' . __METHOD__ . '() ';
267
-        }
268
-        if (! $ticket instanceof EE_Ticket) {
269
-            return 0;
270
-        }
271
-        if (self::debug) {
272
-            echo self::$nl . '<b> . ticket->ID: ' . $ticket->ID() . '</b>';
273
-            echo self::$nl . ' . original ticket->reserved: ' . $ticket->reserved();
274
-        }
275
-        $ticket->refresh_from_db();
276
-        // first let's determine the ticket availability based on sales
277
-        $available = $ticket->qty('saleable');
278
-        if (self::debug) {
279
-            echo self::$nl . ' . . . ticket->qty: ' . $ticket->qty();
280
-            echo self::$nl . ' . . . ticket->sold: ' . $ticket->sold();
281
-            echo self::$nl . ' . . . ticket->reserved: ' . $ticket->reserved();
282
-            echo self::$nl . ' . . . ticket->qty(saleable): ' . $ticket->qty('saleable');
283
-            echo self::$nl . ' . . . available: ' . $available;
284
-        }
285
-        if ($available < 1) {
286
-            $this->_ticket_sold_out($ticket);
287
-            return 0;
288
-        }
289
-        if (self::debug) {
290
-            echo self::$nl . ' . . . qty: ' . $qty;
291
-        }
292
-        if ($available < $qty) {
293
-            $qty = $available;
294
-            if (self::debug) {
295
-                echo self::$nl . ' . . . QTY ADJUSTED: ' . $qty;
296
-            }
297
-            $this->_ticket_quantity_decremented($ticket);
298
-        }
299
-        if ($this->_reserve_ticket($ticket, $qty)) {
300
-            return $qty;
301
-        }
302
-        return 0;
303
-    }
304
-
305
-
306
-    /**
307
-     * increments ticket reserved based on quantity passed
308
-     *
309
-     * @param EE_Ticket $ticket
310
-     * @param int       $quantity
311
-     * @return bool indicating success or failure
312
-     * @throws EE_Error
313
-     * @throws ReflectionException
314
-     */
315
-    protected function _reserve_ticket(EE_Ticket $ticket, int $quantity = 1): bool
316
-    {
317
-        // self::debug hardcoded to false
318
-        if (self::debug) {
319
-            echo self::$nl . self::$nl . ' . . . INCREASE RESERVED: ' . $quantity;
320
-        }
321
-        return $ticket->increaseReserved($quantity, 'TicketSalesMonitor:' . __LINE__);
322
-    }
323
-
324
-
325
-    /**
326
-     * @param EE_Ticket $ticket
327
-     * @param int       $quantity
328
-     * @return bool
329
-     * @throws EE_Error
330
-     * @throws ReflectionException
331
-     */
332
-    protected function _release_reserved_ticket(EE_Ticket $ticket, int $quantity = 1): bool
333
-    {
334
-        // self::debug hardcoded to false
335
-        if (self::debug) {
336
-            echo self::$nl . ' . . . ticket->ID: ' . $ticket->ID();
337
-            echo self::$nl . ' . . . ticket->reserved before: ' . $ticket->reserved();
338
-        }
339
-        $ticket->decreaseReserved($quantity, true, 'TicketSalesMonitor:' . __LINE__);
340
-        if (self::debug) {
341
-            echo self::$nl . ' . . . ticket->reserved after: ' . $ticket->reserved();
342
-        }
343
-        return (bool) $ticket->save();
344
-    }
345
-
346
-
347
-    /**
348
-     * removes quantities within the ticket selector based on zero ticket availability
349
-     *
350
-     * @param EE_Ticket $ticket
351
-     * @return    void
352
-     * @throws UnexpectedEntityException
353
-     * @throws EE_Error
354
-     * @throws ReflectionException
355
-     */
356
-    protected function _ticket_sold_out(EE_Ticket $ticket)
357
-    {
358
-        // self::debug hardcoded to false
359
-        if (self::debug) {
360
-            echo self::$nl . self::$nl . __LINE__ . ') ' . __METHOD__ . '() ';
361
-            echo self::$nl . ' . . ticket->name: ' . $this->_get_ticket_and_event_name($ticket);
362
-        }
363
-        $this->sold_out_tickets[] = $this->_get_ticket_and_event_name($ticket);
364
-    }
365
-
366
-
367
-    /**
368
-     * adjusts quantities within the ticket selector based on decreased ticket availability
369
-     *
370
-     * @param EE_Ticket $ticket
371
-     * @return void
372
-     * @throws UnexpectedEntityException
373
-     * @throws EE_Error
374
-     * @throws ReflectionException
375
-     */
376
-    protected function _ticket_quantity_decremented(EE_Ticket $ticket)
377
-    {
378
-        // self::debug hardcoded to false
379
-        if (self::debug) {
380
-            echo self::$nl . self::$nl . __LINE__ . ') ' . __METHOD__ . '() ';
381
-            echo self::$nl . ' . . ticket->name: ' . $this->_get_ticket_and_event_name($ticket);
382
-        }
383
-        $this->decremented_tickets[] = $this->_get_ticket_and_event_name($ticket);
384
-    }
385
-
386
-
387
-    /**
388
-     * builds string out of ticket and event name
389
-     *
390
-     * @param EE_Ticket $ticket
391
-     * @return string
392
-     * @throws UnexpectedEntityException
393
-     * @throws EE_Error
394
-     * @throws ReflectionException
395
-     */
396
-    protected function _get_ticket_and_event_name(EE_Ticket $ticket): string
397
-    {
398
-        $event = $ticket->get_related_event();
399
-        if ($event instanceof EE_Event) {
400
-            $ticket_name = sprintf(
401
-                _x('%1$s for %2$s', 'ticket name for event name', 'event_espresso'),
402
-                $ticket->name(),
403
-                $event->name()
404
-            );
405
-        } else {
406
-            $ticket_name = $ticket->name();
407
-        }
408
-        return $ticket_name;
409
-    }
410
-
411
-
412
-
413
-    /********************************** EVENT CART  **********************************/
414
-
415
-
416
-    /**
417
-     * releases or reserves ticket(s) based on quantity passed
418
-     *
419
-     * @param EE_Line_Item $line_item
420
-     * @param int          $quantity
421
-     * @return void
422
-     * @throws EE_Error
423
-     * @throws InvalidArgumentException
424
-     * @throws InvalidDataTypeException
425
-     * @throws InvalidInterfaceException
426
-     * @throws ReflectionException
427
-     */
428
-    public static function ticket_quantity_updated(EE_Line_Item $line_item, int $quantity = 1)
429
-    {
430
-        $ticket = EEM_Ticket::instance()->get_one_by_ID(absint($line_item->OBJ_ID()));
431
-        if ($ticket instanceof EE_Ticket) {
432
-            $ticket->add_extra_meta(
433
-                EE_Ticket::META_KEY_TICKET_RESERVATIONS,
434
-                __LINE__ . ') ' . __METHOD__ . '()'
435
-            );
436
-            if ($quantity > 0) {
437
-                EED_Ticket_Sales_Monitor::instance()->_reserve_ticket($ticket, $quantity);
438
-            } else {
439
-                EED_Ticket_Sales_Monitor::instance()->_release_reserved_ticket($ticket, $quantity);
440
-            }
441
-        }
442
-    }
443
-
444
-
445
-    /**
446
-     * releases reserved ticket(s) based on quantity passed
447
-     *
448
-     * @param EE_Ticket $ticket
449
-     * @param int       $quantity
450
-     * @return void
451
-     * @throws EE_Error
452
-     * @throws ReflectionException
453
-     */
454
-    public static function ticket_removed_from_cart(EE_Ticket $ticket, int $quantity = 1)
455
-    {
456
-        $ticket->add_extra_meta(
457
-            EE_Ticket::META_KEY_TICKET_RESERVATIONS,
458
-            __LINE__ . ') ' . __METHOD__ . '()'
459
-        );
460
-        EED_Ticket_Sales_Monitor::instance()->_release_reserved_ticket($ticket, $quantity);
461
-    }
462
-
463
-
464
-
465
-    /********************************** POST_NOTICES  **********************************/
466
-
467
-
468
-    /**
469
-     * @return void
470
-     * @throws EE_Error
471
-     * @throws ReflectionException
472
-     */
473
-    public static function post_notices()
474
-    {
475
-        EED_Ticket_Sales_Monitor::instance()->_post_notices();
476
-    }
477
-
478
-
479
-    /**
480
-     * @return void
481
-     * @throws EE_Error
482
-     * @throws InvalidArgumentException
483
-     * @throws ReflectionException
484
-     * @throws InvalidDataTypeException
485
-     * @throws InvalidInterfaceException
486
-     */
487
-    protected function _post_notices()
488
-    {
489
-        // self::debug hardcoded to false
490
-        if (self::debug) {
491
-            echo self::$nl . self::$nl . __LINE__ . ') ' . __METHOD__ . '() ';
492
-        }
493
-        $refresh_msg = '';
494
-        $none_added_msg = '';
495
-        if (defined('DOING_AJAX') && DOING_AJAX) {
496
-            $refresh_msg = esc_html__(
497
-                'Please refresh the page to view updated ticket quantities.',
498
-                'event_espresso'
499
-            );
500
-            $none_added_msg = esc_html__('No tickets were added for the event.', 'event_espresso');
501
-        }
502
-        if (! empty($this->sold_out_tickets)) {
503
-            EE_Error::add_attention(
504
-                sprintf(
505
-                    apply_filters(
506
-                        'FHEE__EED_Ticket_Sales_Monitor___post_notices__sold_out_tickets_notice',
507
-                        esc_html__(
508
-                            'We\'re sorry...%1$sThe following items have sold out since you first viewed this page, and can no longer be registered for:%1$s%1$s%2$s%1$s%1$sPlease note that availability can change at any time due to cancellations, so please check back again later if registration for this event(s) is important to you.%1$s%1$s%3$s%1$s%4$s%1$s',
509
-                            'event_espresso'
510
-                        )
511
-                    ),
512
-                    '<br />',
513
-                    implode('<br />', $this->sold_out_tickets),
514
-                    $none_added_msg,
515
-                    $refresh_msg
516
-                )
517
-            );
518
-            // alter code flow in the Ticket Selector for better UX
519
-            add_filter('FHEE__EED_Ticket_Selector__process_ticket_selections__tckts_slctd', '__return_true');
520
-            add_filter('FHEE__EED_Ticket_Selector__process_ticket_selections__success', '__return_false');
521
-            $this->sold_out_tickets = array();
522
-            // and reset the cart
523
-            EED_Ticket_Sales_Monitor::session_cart_reset(EE_Registry::instance()->SSN);
524
-        }
525
-        if (! empty($this->decremented_tickets)) {
526
-            EE_Error::add_attention(
527
-                sprintf(
528
-                    apply_filters(
529
-                        'FHEE__EED_Ticket_Sales_Monitor___ticket_quantity_decremented__notice',
530
-                        esc_html__(
531
-                            'We\'re sorry...%1$sDue to sales that have occurred since you first viewed the last page, the following items have had their quantities adjusted to match the current available amount:%1$s%1$s%2$s%1$s%1$sPlease note that availability can change at any time due to cancellations, so please check back again later if registration for this event(s) is important to you.%1$s%1$s%3$s%1$s%4$s%1$s',
532
-                            'event_espresso'
533
-                        )
534
-                    ),
535
-                    '<br />',
536
-                    implode('<br />', $this->decremented_tickets),
537
-                    $none_added_msg,
538
-                    $refresh_msg
539
-                )
540
-            );
541
-            $this->decremented_tickets = array();
542
-        }
543
-    }
544
-
545
-
546
-
547
-    /********************************** RELEASE_ALL_RESERVED_TICKETS_FOR_TRANSACTION  **********************************/
548
-
549
-
550
-    /**
551
-     * releases reserved tickets for all registrations of an EE_Transaction
552
-     * by default, will NOT release tickets for finalized transactions
553
-     *
554
-     * @param EE_Transaction $transaction
555
-     * @return int
556
-     * @throws EE_Error
557
-     * @throws ReflectionException
558
-     */
559
-    protected function _release_all_reserved_tickets_for_transaction(EE_Transaction $transaction): int
560
-    {
561
-        // self::debug hardcoded to false
562
-        if (self::debug) {
563
-            echo self::$nl . self::$nl . __LINE__ . ') ' . __METHOD__ . '() ';
564
-            echo self::$nl . ' . transaction->ID: ' . $transaction->ID();
565
-            echo self::$nl . ' . TXN status_ID: ' . $transaction->status_ID();
566
-        }
567
-        // check if 'finalize_registration' step has been completed...
568
-        $finalized = $transaction->reg_step_completed('finalize_registration');
569
-        if (self::debug) {
570
-            // DEBUG LOG
571
-            EEH_Debug_Tools::log(
572
-                __CLASS__,
573
-                __FUNCTION__,
574
-                __LINE__,
575
-                array('finalized' => $finalized),
576
-                false,
577
-                'EE_Transaction: ' . $transaction->ID()
578
-            );
579
-        }
580
-        // how many tickets were released
581
-        $count = 0;
582
-        if (self::debug) {
583
-            echo self::$nl . ' . . . TXN finalized: ' . $finalized;
584
-        }
585
-        $release_tickets_with_TXN_status = array(
586
-            EEM_Transaction::failed_status_code,
587
-            EEM_Transaction::abandoned_status_code,
588
-            EEM_Transaction::incomplete_status_code,
589
-        );
590
-        $events = array();
591
-        // if the session is getting cleared BEFORE the TXN has been finalized or the transaction is not completed
592
-        if (! $finalized || in_array($transaction->status_ID(), $release_tickets_with_TXN_status, true)) {
593
-            // cancel any reserved tickets for registrations that were not approved
594
-            $registrations = $transaction->registrations();
595
-            if (self::debug) {
596
-                echo self::$nl . ' . . . # registrations: ' . count($registrations);
597
-                $reg = reset($registrations);
598
-                $ticket = $reg->ticket();
599
-                if ($ticket instanceof EE_Ticket) {
600
-                    $ticket->add_extra_meta(
601
-                        EE_Ticket::META_KEY_TICKET_RESERVATIONS,
602
-                        __LINE__ . ') Release All Tickets TXN:' . $transaction->ID()
603
-                    );
604
-                }
605
-            }
606
-            if (! empty($registrations)) {
607
-                foreach ($registrations as $registration) {
608
-                    if (
609
-                        $registration instanceof EE_Registration
610
-                        && $this->_release_reserved_ticket_for_registration($registration, $transaction)
611
-                    ) {
612
-                        $count++;
613
-                        $events[ $registration->event_ID() ] = $registration->event();
614
-                    }
615
-                }
616
-            }
617
-        }
618
-        if ($events !== array()) {
619
-            foreach ($events as $event) {
620
-                /** @var EE_Event $event */
621
-                $event->perform_sold_out_status_check();
622
-            }
623
-        }
624
-        return $count;
625
-    }
626
-
627
-
628
-    /**
629
-     * releases reserved tickets for an EE_Registration
630
-     * by default, will NOT release tickets for APPROVED registrations
631
-     *
632
-     * @param EE_Registration $registration
633
-     * @param EE_Transaction  $transaction
634
-     * @return int
635
-     * @throws EE_Error
636
-     * @throws ReflectionException
637
-     */
638
-    protected function _release_reserved_ticket_for_registration(
639
-        EE_Registration $registration,
640
-        EE_Transaction $transaction
641
-    ): int {
642
-        $STS_ID = $transaction->status_ID();
643
-        // self::debug hardcoded to false
644
-        if (self::debug) {
645
-            echo self::$nl . self::$nl . __LINE__ . ') ' . __METHOD__ . '() ';
646
-            echo self::$nl . ' . . registration->ID: ' . $registration->ID();
647
-            echo self::$nl . ' . . registration->status_ID: ' . $registration->status_ID();
648
-            echo self::$nl . ' . . transaction->status_ID(): ' . $STS_ID;
649
-        }
650
-        if (
22
+	const debug = false;    // true false
23
+
24
+	private static $nl = '';
25
+
26
+	/**
27
+	 * an array for tracking names of tickets that have sold out
28
+	 *
29
+	 * @var array $sold_out_tickets
30
+	 */
31
+	protected $sold_out_tickets = array();
32
+
33
+	/**
34
+	 * an array for tracking names of tickets that have had their quantities reduced
35
+	 *
36
+	 * @var array $decremented_tickets
37
+	 */
38
+	protected $decremented_tickets = array();
39
+
40
+
41
+	/**
42
+	 * set_hooks - for hooking into EE Core, other modules, etc
43
+	 *
44
+	 * @return    void
45
+	 */
46
+	public static function set_hooks()
47
+	{
48
+		self::$nl = defined('EE_TESTS_DIR') ? "\n" : '<br />';
49
+		// release tickets for expired carts
50
+		add_action(
51
+			'EED_Ticket_Selector__process_ticket_selections__before',
52
+			array('EED_Ticket_Sales_Monitor', 'release_tickets_for_expired_carts'),
53
+			1
54
+		);
55
+		// check ticket reserves AFTER MER does it's check (hence priority 20)
56
+		add_filter(
57
+			'FHEE__EE_Ticket_Selector___add_ticket_to_cart__ticket_qty',
58
+			array('EED_Ticket_Sales_Monitor', 'validate_ticket_sale'),
59
+			20,
60
+			3
61
+		);
62
+		// add notices for sold out tickets
63
+		add_action(
64
+			'AHEE__EE_Ticket_Selector__process_ticket_selections__after_tickets_added_to_cart',
65
+			array('EED_Ticket_Sales_Monitor', 'post_notices'),
66
+			10
67
+		);
68
+
69
+		// handle tickets deleted from cart
70
+		add_action(
71
+			'FHEE__EED_Multi_Event_Registration__delete_ticket__ticket_removed_from_cart',
72
+			array('EED_Ticket_Sales_Monitor', 'ticket_removed_from_cart'),
73
+			10,
74
+			2
75
+		);
76
+		// handle emptied carts
77
+		add_action(
78
+			'AHEE__EE_Session__reset_cart__before_reset',
79
+			array('EED_Ticket_Sales_Monitor', 'session_cart_reset'),
80
+			10,
81
+			1
82
+		);
83
+		add_action(
84
+			'AHEE__EED_Multi_Event_Registration__empty_event_cart__before_delete_cart',
85
+			array('EED_Ticket_Sales_Monitor', 'session_cart_reset'),
86
+			10,
87
+			1
88
+		);
89
+		// handle cancelled registrations
90
+		add_action(
91
+			'AHEE__EE_Session__reset_checkout__before_reset',
92
+			array('EED_Ticket_Sales_Monitor', 'session_checkout_reset'),
93
+			10,
94
+			1
95
+		);
96
+		// cron tasks
97
+		add_action(
98
+			'AHEE__EE_Cron_Tasks__process_expired_transactions__abandoned_transaction',
99
+			array('EED_Ticket_Sales_Monitor', 'process_abandoned_transactions'),
100
+			10,
101
+			1
102
+		);
103
+		add_action(
104
+			'AHEE__EE_Cron_Tasks__process_expired_transactions__incomplete_transaction',
105
+			array('EED_Ticket_Sales_Monitor', 'process_abandoned_transactions'),
106
+			10,
107
+			1
108
+		);
109
+		add_action(
110
+			'AHEE__EE_Cron_Tasks__process_expired_transactions__failed_transaction',
111
+			array('EED_Ticket_Sales_Monitor', 'process_failed_transactions'),
112
+			10,
113
+			1
114
+		);
115
+	}
116
+
117
+
118
+	/**
119
+	 * set_hooks_admin - for hooking into EE Admin Core, other modules, etc
120
+	 *
121
+	 * @return void
122
+	 */
123
+	public static function set_hooks_admin()
124
+	{
125
+		EED_Ticket_Sales_Monitor::set_hooks();
126
+	}
127
+
128
+
129
+	/**
130
+	 * @return EED_Ticket_Sales_Monitor|EED_Module
131
+	 * @throws EE_Error
132
+	 * @throws ReflectionException
133
+	 */
134
+	public static function instance()
135
+	{
136
+		return parent::get_instance(__CLASS__);
137
+	}
138
+
139
+
140
+	/**
141
+	 * @param WP_Query $WP
142
+	 * @return    void
143
+	 */
144
+	public function run($WP)
145
+	{
146
+	}
147
+
148
+
149
+
150
+	/********************************** PRE_TICKET_SALES  **********************************/
151
+
152
+
153
+	/**
154
+	 * Retrieves grand totals from the line items that have no TXN ID
155
+	 * and timestamps less than the current time minus the session lifespan.
156
+	 * These are carts that have been abandoned before the "registrant" even attempted to checkout.
157
+	 * We're going to release the tickets for these line items before attempting to add more to the cart.
158
+	 *
159
+	 * @return void
160
+	 * @throws DomainException
161
+	 * @throws EE_Error
162
+	 * @throws InvalidArgumentException
163
+	 * @throws InvalidDataTypeException
164
+	 * @throws InvalidInterfaceException
165
+	 * @throws UnexpectedEntityException
166
+	 * @throws ReflectionException
167
+	 */
168
+	public static function release_tickets_for_expired_carts()
169
+	{
170
+		// self::debug hardcoded to false
171
+		if (self::debug) {
172
+			echo self::$nl . self::$nl . __LINE__ . ') ' . __METHOD__ . '()';
173
+		}
174
+		do_action('AHEE__EED_Ticket_Sales_Monitor__release_tickets_for_expired_carts__begin');
175
+		$expired_ticket_IDs = array();
176
+		/** @var EventEspresso\core\domain\values\session\SessionLifespan $session_lifespan */
177
+		$session_lifespan = LoaderFactory::getLoader()->getShared(
178
+			'EventEspresso\core\domain\values\session\SessionLifespan'
179
+		);
180
+		$timestamp = $session_lifespan->expiration();
181
+		$expired_ticket_line_items = EEM_Line_Item::instance()->getTicketLineItemsForExpiredCarts($timestamp);
182
+		if (self::debug) {
183
+			echo self::$nl . ' . time(): ' . time();
184
+			echo self::$nl . ' . time() as date: ' . date('Y-m-d H:i a');
185
+			echo self::$nl . ' . session expiration: ' . $session_lifespan->expiration();
186
+			echo self::$nl . ' . session expiration as date: ' . date('Y-m-d H:i a', $session_lifespan->expiration());
187
+			echo self::$nl . ' . timestamp: ' . $timestamp;
188
+			echo self::$nl . ' . $expired_ticket_line_items: ' . count($expired_ticket_line_items);
189
+		}
190
+		if (! empty($expired_ticket_line_items)) {
191
+			foreach ($expired_ticket_line_items as $expired_ticket_line_item) {
192
+				if (! $expired_ticket_line_item instanceof EE_Line_Item) {
193
+					continue;
194
+				}
195
+				$expired_ticket_IDs[ $expired_ticket_line_item->OBJ_ID() ] = $expired_ticket_line_item->OBJ_ID();
196
+				if (self::debug) {
197
+					echo self::$nl . ' . $expired_ticket_line_item->OBJ_ID(): ' . $expired_ticket_line_item->OBJ_ID();
198
+					echo self::$nl . ' . $expired_ticket_line_item->timestamp(): '
199
+						 . date(
200
+							 'Y-m-d h:i a',
201
+							 $expired_ticket_line_item->timestamp(true)
202
+						 );
203
+				}
204
+			}
205
+			if (! empty($expired_ticket_IDs)) {
206
+				EED_Ticket_Sales_Monitor::release_reservations_for_tickets(
207
+					EEM_Ticket::instance()->get_tickets_with_IDs($expired_ticket_IDs),
208
+					array(),
209
+					__FUNCTION__
210
+				);
211
+				// now  let's get rid of expired line items so that they can't interfere with tracking
212
+				EED_Ticket_Sales_Monitor::clear_expired_line_items_with_no_transaction($timestamp);
213
+			}
214
+		}
215
+		do_action(
216
+			'AHEE__EED_Ticket_Sales_Monitor__release_tickets_for_expired_carts__end',
217
+			$expired_ticket_IDs,
218
+			$expired_ticket_line_items
219
+		);
220
+	}
221
+
222
+
223
+
224
+	/********************************** VALIDATE_TICKET_SALE  **********************************/
225
+
226
+
227
+	/**
228
+	 * callback for 'FHEE__EED_Ticket_Selector__process_ticket_selections__valid_post_data'
229
+	 *
230
+	 * @param int       $qty
231
+	 * @param EE_Ticket $ticket
232
+	 * @return int
233
+	 * @throws UnexpectedEntityException
234
+	 * @throws EE_Error
235
+	 * @throws ReflectionException
236
+	 */
237
+	public static function validate_ticket_sale(int $qty, EE_Ticket $ticket): int
238
+	{
239
+		$qty = absint($qty);
240
+		if ($qty > 0) {
241
+			$qty = EED_Ticket_Sales_Monitor::instance()->_validate_ticket_sale($ticket, $qty);
242
+		}
243
+		// self::debug hardcoded to false
244
+		if (self::debug) {
245
+			echo self::$nl . self::$nl . __LINE__ . ') ' . __METHOD__ . '()';
246
+			echo self::$nl . self::$nl . '<b> RETURNED QTY: ' . $qty . '</b>';
247
+		}
248
+		return $qty;
249
+	}
250
+
251
+
252
+	/**
253
+	 * checks whether an individual ticket is available for purchase based on datetime, and ticket details
254
+	 *
255
+	 * @param EE_Ticket|null $ticket
256
+	 * @param int       $qty
257
+	 * @return int
258
+	 * @throws UnexpectedEntityException
259
+	 * @throws EE_Error
260
+	 * @throws ReflectionException
261
+	 */
262
+	protected function _validate_ticket_sale(?EE_Ticket $ticket, int $qty = 1): int
263
+	{
264
+		// self::debug hardcoded to false
265
+		if (self::debug) {
266
+			echo self::$nl . self::$nl . __LINE__ . ') ' . __METHOD__ . '() ';
267
+		}
268
+		if (! $ticket instanceof EE_Ticket) {
269
+			return 0;
270
+		}
271
+		if (self::debug) {
272
+			echo self::$nl . '<b> . ticket->ID: ' . $ticket->ID() . '</b>';
273
+			echo self::$nl . ' . original ticket->reserved: ' . $ticket->reserved();
274
+		}
275
+		$ticket->refresh_from_db();
276
+		// first let's determine the ticket availability based on sales
277
+		$available = $ticket->qty('saleable');
278
+		if (self::debug) {
279
+			echo self::$nl . ' . . . ticket->qty: ' . $ticket->qty();
280
+			echo self::$nl . ' . . . ticket->sold: ' . $ticket->sold();
281
+			echo self::$nl . ' . . . ticket->reserved: ' . $ticket->reserved();
282
+			echo self::$nl . ' . . . ticket->qty(saleable): ' . $ticket->qty('saleable');
283
+			echo self::$nl . ' . . . available: ' . $available;
284
+		}
285
+		if ($available < 1) {
286
+			$this->_ticket_sold_out($ticket);
287
+			return 0;
288
+		}
289
+		if (self::debug) {
290
+			echo self::$nl . ' . . . qty: ' . $qty;
291
+		}
292
+		if ($available < $qty) {
293
+			$qty = $available;
294
+			if (self::debug) {
295
+				echo self::$nl . ' . . . QTY ADJUSTED: ' . $qty;
296
+			}
297
+			$this->_ticket_quantity_decremented($ticket);
298
+		}
299
+		if ($this->_reserve_ticket($ticket, $qty)) {
300
+			return $qty;
301
+		}
302
+		return 0;
303
+	}
304
+
305
+
306
+	/**
307
+	 * increments ticket reserved based on quantity passed
308
+	 *
309
+	 * @param EE_Ticket $ticket
310
+	 * @param int       $quantity
311
+	 * @return bool indicating success or failure
312
+	 * @throws EE_Error
313
+	 * @throws ReflectionException
314
+	 */
315
+	protected function _reserve_ticket(EE_Ticket $ticket, int $quantity = 1): bool
316
+	{
317
+		// self::debug hardcoded to false
318
+		if (self::debug) {
319
+			echo self::$nl . self::$nl . ' . . . INCREASE RESERVED: ' . $quantity;
320
+		}
321
+		return $ticket->increaseReserved($quantity, 'TicketSalesMonitor:' . __LINE__);
322
+	}
323
+
324
+
325
+	/**
326
+	 * @param EE_Ticket $ticket
327
+	 * @param int       $quantity
328
+	 * @return bool
329
+	 * @throws EE_Error
330
+	 * @throws ReflectionException
331
+	 */
332
+	protected function _release_reserved_ticket(EE_Ticket $ticket, int $quantity = 1): bool
333
+	{
334
+		// self::debug hardcoded to false
335
+		if (self::debug) {
336
+			echo self::$nl . ' . . . ticket->ID: ' . $ticket->ID();
337
+			echo self::$nl . ' . . . ticket->reserved before: ' . $ticket->reserved();
338
+		}
339
+		$ticket->decreaseReserved($quantity, true, 'TicketSalesMonitor:' . __LINE__);
340
+		if (self::debug) {
341
+			echo self::$nl . ' . . . ticket->reserved after: ' . $ticket->reserved();
342
+		}
343
+		return (bool) $ticket->save();
344
+	}
345
+
346
+
347
+	/**
348
+	 * removes quantities within the ticket selector based on zero ticket availability
349
+	 *
350
+	 * @param EE_Ticket $ticket
351
+	 * @return    void
352
+	 * @throws UnexpectedEntityException
353
+	 * @throws EE_Error
354
+	 * @throws ReflectionException
355
+	 */
356
+	protected function _ticket_sold_out(EE_Ticket $ticket)
357
+	{
358
+		// self::debug hardcoded to false
359
+		if (self::debug) {
360
+			echo self::$nl . self::$nl . __LINE__ . ') ' . __METHOD__ . '() ';
361
+			echo self::$nl . ' . . ticket->name: ' . $this->_get_ticket_and_event_name($ticket);
362
+		}
363
+		$this->sold_out_tickets[] = $this->_get_ticket_and_event_name($ticket);
364
+	}
365
+
366
+
367
+	/**
368
+	 * adjusts quantities within the ticket selector based on decreased ticket availability
369
+	 *
370
+	 * @param EE_Ticket $ticket
371
+	 * @return void
372
+	 * @throws UnexpectedEntityException
373
+	 * @throws EE_Error
374
+	 * @throws ReflectionException
375
+	 */
376
+	protected function _ticket_quantity_decremented(EE_Ticket $ticket)
377
+	{
378
+		// self::debug hardcoded to false
379
+		if (self::debug) {
380
+			echo self::$nl . self::$nl . __LINE__ . ') ' . __METHOD__ . '() ';
381
+			echo self::$nl . ' . . ticket->name: ' . $this->_get_ticket_and_event_name($ticket);
382
+		}
383
+		$this->decremented_tickets[] = $this->_get_ticket_and_event_name($ticket);
384
+	}
385
+
386
+
387
+	/**
388
+	 * builds string out of ticket and event name
389
+	 *
390
+	 * @param EE_Ticket $ticket
391
+	 * @return string
392
+	 * @throws UnexpectedEntityException
393
+	 * @throws EE_Error
394
+	 * @throws ReflectionException
395
+	 */
396
+	protected function _get_ticket_and_event_name(EE_Ticket $ticket): string
397
+	{
398
+		$event = $ticket->get_related_event();
399
+		if ($event instanceof EE_Event) {
400
+			$ticket_name = sprintf(
401
+				_x('%1$s for %2$s', 'ticket name for event name', 'event_espresso'),
402
+				$ticket->name(),
403
+				$event->name()
404
+			);
405
+		} else {
406
+			$ticket_name = $ticket->name();
407
+		}
408
+		return $ticket_name;
409
+	}
410
+
411
+
412
+
413
+	/********************************** EVENT CART  **********************************/
414
+
415
+
416
+	/**
417
+	 * releases or reserves ticket(s) based on quantity passed
418
+	 *
419
+	 * @param EE_Line_Item $line_item
420
+	 * @param int          $quantity
421
+	 * @return void
422
+	 * @throws EE_Error
423
+	 * @throws InvalidArgumentException
424
+	 * @throws InvalidDataTypeException
425
+	 * @throws InvalidInterfaceException
426
+	 * @throws ReflectionException
427
+	 */
428
+	public static function ticket_quantity_updated(EE_Line_Item $line_item, int $quantity = 1)
429
+	{
430
+		$ticket = EEM_Ticket::instance()->get_one_by_ID(absint($line_item->OBJ_ID()));
431
+		if ($ticket instanceof EE_Ticket) {
432
+			$ticket->add_extra_meta(
433
+				EE_Ticket::META_KEY_TICKET_RESERVATIONS,
434
+				__LINE__ . ') ' . __METHOD__ . '()'
435
+			);
436
+			if ($quantity > 0) {
437
+				EED_Ticket_Sales_Monitor::instance()->_reserve_ticket($ticket, $quantity);
438
+			} else {
439
+				EED_Ticket_Sales_Monitor::instance()->_release_reserved_ticket($ticket, $quantity);
440
+			}
441
+		}
442
+	}
443
+
444
+
445
+	/**
446
+	 * releases reserved ticket(s) based on quantity passed
447
+	 *
448
+	 * @param EE_Ticket $ticket
449
+	 * @param int       $quantity
450
+	 * @return void
451
+	 * @throws EE_Error
452
+	 * @throws ReflectionException
453
+	 */
454
+	public static function ticket_removed_from_cart(EE_Ticket $ticket, int $quantity = 1)
455
+	{
456
+		$ticket->add_extra_meta(
457
+			EE_Ticket::META_KEY_TICKET_RESERVATIONS,
458
+			__LINE__ . ') ' . __METHOD__ . '()'
459
+		);
460
+		EED_Ticket_Sales_Monitor::instance()->_release_reserved_ticket($ticket, $quantity);
461
+	}
462
+
463
+
464
+
465
+	/********************************** POST_NOTICES  **********************************/
466
+
467
+
468
+	/**
469
+	 * @return void
470
+	 * @throws EE_Error
471
+	 * @throws ReflectionException
472
+	 */
473
+	public static function post_notices()
474
+	{
475
+		EED_Ticket_Sales_Monitor::instance()->_post_notices();
476
+	}
477
+
478
+
479
+	/**
480
+	 * @return void
481
+	 * @throws EE_Error
482
+	 * @throws InvalidArgumentException
483
+	 * @throws ReflectionException
484
+	 * @throws InvalidDataTypeException
485
+	 * @throws InvalidInterfaceException
486
+	 */
487
+	protected function _post_notices()
488
+	{
489
+		// self::debug hardcoded to false
490
+		if (self::debug) {
491
+			echo self::$nl . self::$nl . __LINE__ . ') ' . __METHOD__ . '() ';
492
+		}
493
+		$refresh_msg = '';
494
+		$none_added_msg = '';
495
+		if (defined('DOING_AJAX') && DOING_AJAX) {
496
+			$refresh_msg = esc_html__(
497
+				'Please refresh the page to view updated ticket quantities.',
498
+				'event_espresso'
499
+			);
500
+			$none_added_msg = esc_html__('No tickets were added for the event.', 'event_espresso');
501
+		}
502
+		if (! empty($this->sold_out_tickets)) {
503
+			EE_Error::add_attention(
504
+				sprintf(
505
+					apply_filters(
506
+						'FHEE__EED_Ticket_Sales_Monitor___post_notices__sold_out_tickets_notice',
507
+						esc_html__(
508
+							'We\'re sorry...%1$sThe following items have sold out since you first viewed this page, and can no longer be registered for:%1$s%1$s%2$s%1$s%1$sPlease note that availability can change at any time due to cancellations, so please check back again later if registration for this event(s) is important to you.%1$s%1$s%3$s%1$s%4$s%1$s',
509
+							'event_espresso'
510
+						)
511
+					),
512
+					'<br />',
513
+					implode('<br />', $this->sold_out_tickets),
514
+					$none_added_msg,
515
+					$refresh_msg
516
+				)
517
+			);
518
+			// alter code flow in the Ticket Selector for better UX
519
+			add_filter('FHEE__EED_Ticket_Selector__process_ticket_selections__tckts_slctd', '__return_true');
520
+			add_filter('FHEE__EED_Ticket_Selector__process_ticket_selections__success', '__return_false');
521
+			$this->sold_out_tickets = array();
522
+			// and reset the cart
523
+			EED_Ticket_Sales_Monitor::session_cart_reset(EE_Registry::instance()->SSN);
524
+		}
525
+		if (! empty($this->decremented_tickets)) {
526
+			EE_Error::add_attention(
527
+				sprintf(
528
+					apply_filters(
529
+						'FHEE__EED_Ticket_Sales_Monitor___ticket_quantity_decremented__notice',
530
+						esc_html__(
531
+							'We\'re sorry...%1$sDue to sales that have occurred since you first viewed the last page, the following items have had their quantities adjusted to match the current available amount:%1$s%1$s%2$s%1$s%1$sPlease note that availability can change at any time due to cancellations, so please check back again later if registration for this event(s) is important to you.%1$s%1$s%3$s%1$s%4$s%1$s',
532
+							'event_espresso'
533
+						)
534
+					),
535
+					'<br />',
536
+					implode('<br />', $this->decremented_tickets),
537
+					$none_added_msg,
538
+					$refresh_msg
539
+				)
540
+			);
541
+			$this->decremented_tickets = array();
542
+		}
543
+	}
544
+
545
+
546
+
547
+	/********************************** RELEASE_ALL_RESERVED_TICKETS_FOR_TRANSACTION  **********************************/
548
+
549
+
550
+	/**
551
+	 * releases reserved tickets for all registrations of an EE_Transaction
552
+	 * by default, will NOT release tickets for finalized transactions
553
+	 *
554
+	 * @param EE_Transaction $transaction
555
+	 * @return int
556
+	 * @throws EE_Error
557
+	 * @throws ReflectionException
558
+	 */
559
+	protected function _release_all_reserved_tickets_for_transaction(EE_Transaction $transaction): int
560
+	{
561
+		// self::debug hardcoded to false
562
+		if (self::debug) {
563
+			echo self::$nl . self::$nl . __LINE__ . ') ' . __METHOD__ . '() ';
564
+			echo self::$nl . ' . transaction->ID: ' . $transaction->ID();
565
+			echo self::$nl . ' . TXN status_ID: ' . $transaction->status_ID();
566
+		}
567
+		// check if 'finalize_registration' step has been completed...
568
+		$finalized = $transaction->reg_step_completed('finalize_registration');
569
+		if (self::debug) {
570
+			// DEBUG LOG
571
+			EEH_Debug_Tools::log(
572
+				__CLASS__,
573
+				__FUNCTION__,
574
+				__LINE__,
575
+				array('finalized' => $finalized),
576
+				false,
577
+				'EE_Transaction: ' . $transaction->ID()
578
+			);
579
+		}
580
+		// how many tickets were released
581
+		$count = 0;
582
+		if (self::debug) {
583
+			echo self::$nl . ' . . . TXN finalized: ' . $finalized;
584
+		}
585
+		$release_tickets_with_TXN_status = array(
586
+			EEM_Transaction::failed_status_code,
587
+			EEM_Transaction::abandoned_status_code,
588
+			EEM_Transaction::incomplete_status_code,
589
+		);
590
+		$events = array();
591
+		// if the session is getting cleared BEFORE the TXN has been finalized or the transaction is not completed
592
+		if (! $finalized || in_array($transaction->status_ID(), $release_tickets_with_TXN_status, true)) {
593
+			// cancel any reserved tickets for registrations that were not approved
594
+			$registrations = $transaction->registrations();
595
+			if (self::debug) {
596
+				echo self::$nl . ' . . . # registrations: ' . count($registrations);
597
+				$reg = reset($registrations);
598
+				$ticket = $reg->ticket();
599
+				if ($ticket instanceof EE_Ticket) {
600
+					$ticket->add_extra_meta(
601
+						EE_Ticket::META_KEY_TICKET_RESERVATIONS,
602
+						__LINE__ . ') Release All Tickets TXN:' . $transaction->ID()
603
+					);
604
+				}
605
+			}
606
+			if (! empty($registrations)) {
607
+				foreach ($registrations as $registration) {
608
+					if (
609
+						$registration instanceof EE_Registration
610
+						&& $this->_release_reserved_ticket_for_registration($registration, $transaction)
611
+					) {
612
+						$count++;
613
+						$events[ $registration->event_ID() ] = $registration->event();
614
+					}
615
+				}
616
+			}
617
+		}
618
+		if ($events !== array()) {
619
+			foreach ($events as $event) {
620
+				/** @var EE_Event $event */
621
+				$event->perform_sold_out_status_check();
622
+			}
623
+		}
624
+		return $count;
625
+	}
626
+
627
+
628
+	/**
629
+	 * releases reserved tickets for an EE_Registration
630
+	 * by default, will NOT release tickets for APPROVED registrations
631
+	 *
632
+	 * @param EE_Registration $registration
633
+	 * @param EE_Transaction  $transaction
634
+	 * @return int
635
+	 * @throws EE_Error
636
+	 * @throws ReflectionException
637
+	 */
638
+	protected function _release_reserved_ticket_for_registration(
639
+		EE_Registration $registration,
640
+		EE_Transaction $transaction
641
+	): int {
642
+		$STS_ID = $transaction->status_ID();
643
+		// self::debug hardcoded to false
644
+		if (self::debug) {
645
+			echo self::$nl . self::$nl . __LINE__ . ') ' . __METHOD__ . '() ';
646
+			echo self::$nl . ' . . registration->ID: ' . $registration->ID();
647
+			echo self::$nl . ' . . registration->status_ID: ' . $registration->status_ID();
648
+			echo self::$nl . ' . . transaction->status_ID(): ' . $STS_ID;
649
+		}
650
+		if (
651 651
 // release Tickets for Failed Transactions and Abandoned Transactions
652
-            $STS_ID === EEM_Transaction::failed_status_code
653
-            || $STS_ID === EEM_Transaction::abandoned_status_code
654
-            || (
655
-                // also release Tickets for Incomplete Transactions, but ONLY if the Registrations are NOT Approved
656
-                $STS_ID === EEM_Transaction::incomplete_status_code
657
-                && $registration->status_ID() !== RegStatus::APPROVED
658
-            )
659
-        ) {
660
-            if (self::debug) {
661
-                echo self::$nl . self::$nl . ' . . RELEASE RESERVED TICKET';
662
-                $reserved = $registration->get_extra_meta(EE_Registration::HAS_RESERVED_TICKET_KEY, true);
663
-                echo self::$nl . ' . . . registration HAS_RESERVED_TICKET_KEY: ';
664
-                var_dump($reserved);
665
-            }
666
-            $registration->release_reserved_ticket(true, 'TicketSalesMonitor:' . __LINE__);
667
-            return 1;
668
-        }
669
-        return 0;
670
-    }
671
-
672
-
673
-
674
-    /********************************** SESSION_CART_RESET  **********************************/
675
-
676
-
677
-    /**
678
-     * callback hooked into 'AHEE__EE_Session__reset_cart__before_reset'
679
-     *
680
-     * @param EE_Session $session
681
-     * @return void
682
-     * @throws EE_Error
683
-     * @throws InvalidArgumentException
684
-     * @throws ReflectionException
685
-     * @throws InvalidDataTypeException
686
-     * @throws InvalidInterfaceException
687
-     */
688
-    public static function session_cart_reset(EE_Session $session)
689
-    {
690
-        // don't release tickets if checkout was already reset
691
-        if (did_action('AHEE__EE_Session__reset_checkout__before_reset')) {
692
-            return;
693
-        }
694
-        // self::debug hardcoded to false
695
-        if (self::debug) {
696
-            echo self::$nl . self::$nl . __LINE__ . ') ' . __METHOD__ . '() ';
697
-        }
698
-        // first check of the session has a valid Checkout object
699
-        $checkout = $session->checkout();
700
-        if ($checkout instanceof EE_Checkout) {
701
-            // and use that to clear ticket reservations because it will update the associated registration meta data
702
-            EED_Ticket_Sales_Monitor::instance()->_session_checkout_reset($checkout);
703
-            return;
704
-        }
705
-        $cart = $session->cart();
706
-        if ($cart instanceof EE_Cart) {
707
-            if (self::debug) {
708
-                echo self::$nl . self::$nl . ' cart instance of EE_Cart: ';
709
-            }
710
-            EED_Ticket_Sales_Monitor::instance()->_session_cart_reset($cart, $session);
711
-        } else {
712
-            if (self::debug) {
713
-                echo self::$nl . self::$nl . ' invalid EE_Cart: ';
714
-                var_export($cart, true);
715
-            }
716
-        }
717
-    }
718
-
719
-
720
-    /**
721
-     * releases reserved tickets in the EE_Cart
722
-     *
723
-     * @param EE_Cart $cart
724
-     * @param EE_Session $session
725
-     * @return void
726
-     * @throws EE_Error
727
-     * @throws InvalidArgumentException
728
-     * @throws ReflectionException
729
-     * @throws InvalidDataTypeException
730
-     * @throws InvalidInterfaceException
731
-     */
732
-    protected function _session_cart_reset(EE_Cart $cart, EE_Session $session)
733
-    {
734
-        // self::debug hardcoded to false
735
-        if (self::debug) {
736
-            echo self::$nl . self::$nl . __LINE__ . ') ' . __METHOD__ . '() ';
737
-        }
738
-        $ticket_line_items = $cart->get_tickets();
739
-        if (empty($ticket_line_items)) {
740
-            return;
741
-        }
742
-        if (self::debug) {
743
-            echo '<br /> . ticket_line_item count: ' . count($ticket_line_items);
744
-        }
745
-        foreach ($ticket_line_items as $ticket_line_item) {
746
-            if (self::debug) {
747
-                echo self::$nl . ' . ticket_line_item->ID(): ' . $ticket_line_item->ID();
748
-            }
749
-            if ($ticket_line_item instanceof EE_Line_Item && $ticket_line_item->OBJ_type() === 'Ticket') {
750
-                if (self::debug) {
751
-                    echo self::$nl . ' . . ticket_line_item->OBJ_ID(): ' . $ticket_line_item->OBJ_ID();
752
-                }
753
-                $ticket = EEM_Ticket::instance()->get_one_by_ID($ticket_line_item->OBJ_ID());
754
-                if ($ticket instanceof EE_Ticket) {
755
-                    if (self::debug) {
756
-                        echo self::$nl . ' . . ticket->ID(): ' . $ticket->ID();
757
-                        echo self::$nl . ' . . ticket_line_item->quantity(): ' . $ticket_line_item->quantity();
758
-                    }
759
-                    $ticket->add_extra_meta(
760
-                        EE_Ticket::META_KEY_TICKET_RESERVATIONS,
761
-                        __LINE__ . ') ' . __METHOD__ . '() SID = ' . $session->id()
762
-                    );
763
-                    $this->_release_reserved_ticket($ticket, $ticket_line_item->quantity());
764
-                }
765
-            }
766
-        }
767
-        if (self::debug) {
768
-            echo self::$nl . self::$nl . ' RESET COMPLETED ';
769
-        }
770
-    }
771
-
772
-
773
-
774
-    /********************************** SESSION_CHECKOUT_RESET  **********************************/
775
-
776
-
777
-    /**
778
-     * callback hooked into 'AHEE__EE_Session__reset_checkout__before_reset'
779
-     *
780
-     * @param EE_Session $session
781
-     * @return void
782
-     * @throws EE_Error
783
-     * @throws ReflectionException
784
-     */
785
-    public static function session_checkout_reset(EE_Session $session)
786
-    {
787
-        // don't release tickets if cart was already reset
788
-        if (did_action('AHEE__EE_Session__reset_cart__before_reset')) {
789
-            return;
790
-        }
791
-        $checkout = $session->checkout();
792
-        if ($checkout instanceof EE_Checkout) {
793
-            EED_Ticket_Sales_Monitor::instance()->_session_checkout_reset($checkout);
794
-        }
795
-    }
796
-
797
-
798
-    /**
799
-     * releases reserved tickets for the EE_Checkout->transaction
800
-     *
801
-     * @param EE_Checkout $checkout
802
-     * @return void
803
-     * @throws EE_Error
804
-     * @throws ReflectionException
805
-     */
806
-    protected function _session_checkout_reset(EE_Checkout $checkout)
807
-    {
808
-        // self::debug hardcoded to false
809
-        if (self::debug) {
810
-            echo self::$nl . self::$nl . __LINE__ . ') ' . __METHOD__ . '() ';
811
-        }
812
-        // we want to release the each registration's reserved tickets if the session was cleared, but not if this is a revisit
813
-        if ($checkout->revisit || ! $checkout->transaction instanceof EE_Transaction) {
814
-            return;
815
-        }
816
-        $this->_release_all_reserved_tickets_for_transaction($checkout->transaction);
817
-    }
818
-
819
-
820
-
821
-    /********************************** SESSION_EXPIRED_RESET  **********************************/
822
-
823
-
824
-    /**
825
-     * @param    EE_Session $session
826
-     * @return    void
827
-     */
828
-    public static function session_expired_reset(EE_Session $session)
829
-    {
830
-    }
831
-
832
-
833
-
834
-    /********************************** PROCESS_ABANDONED_TRANSACTIONS  **********************************/
835
-
836
-
837
-    /**
838
-     * releases reserved tickets for all registrations of an ABANDONED EE_Transaction
839
-     * by default, will NOT release tickets for free transactions, or any that have received a payment
840
-     *
841
-     * @param EE_Transaction $transaction
842
-     * @return void
843
-     * @throws EE_Error
844
-     * @throws ReflectionException
845
-     */
846
-    public static function process_abandoned_transactions(EE_Transaction $transaction)
847
-    {
848
-        // is this TXN free or has any money been paid towards this TXN? If so, then leave it alone
849
-        if ($transaction->is_free() || $transaction->paid() > 0) {
850
-            // self::debug hardcoded to false
851
-            if (self::debug) {
852
-                // DEBUG LOG
853
-                EEH_Debug_Tools::log(
854
-                    __CLASS__,
855
-                    __FUNCTION__,
856
-                    __LINE__,
857
-                    array($transaction),
858
-                    false,
859
-                    'EE_Transaction: ' . $transaction->ID()
860
-                );
861
-            }
862
-            return;
863
-        }
864
-        // have their been any successful payments made ?
865
-        $payments = $transaction->payments();
866
-        foreach ($payments as $payment) {
867
-            if ($payment instanceof EE_Payment && $payment->status() === EEM_Payment::status_id_approved) {
868
-                if (self::debug) {
869
-                    // DEBUG LOG
870
-                    EEH_Debug_Tools::log(
871
-                        __CLASS__,
872
-                        __FUNCTION__,
873
-                        __LINE__,
874
-                        array($payment),
875
-                        false,
876
-                        'EE_Transaction: ' . $transaction->ID()
877
-                    );
878
-                }
879
-                return;
880
-            }
881
-        }
882
-        // since you haven't even attempted to pay for your ticket...
883
-        EED_Ticket_Sales_Monitor::instance()->_release_all_reserved_tickets_for_transaction($transaction);
884
-    }
885
-
886
-
887
-
888
-    /********************************** PROCESS_FAILED_TRANSACTIONS  **********************************/
889
-
890
-
891
-    /**
892
-     * releases reserved tickets for absolutely ALL registrations of a FAILED EE_Transaction
893
-     *
894
-     * @param EE_Transaction $transaction
895
-     * @return void
896
-     * @throws EE_Error
897
-     * @throws ReflectionException
898
-     */
899
-    public static function process_failed_transactions(EE_Transaction $transaction)
900
-    {
901
-        // since you haven't even attempted to pay for your ticket...
902
-        EED_Ticket_Sales_Monitor::instance()->_release_all_reserved_tickets_for_transaction($transaction);
903
-    }
904
-
905
-
906
-
907
-    /********************************** RESET RESERVATION COUNTS  *********************************/
908
-
909
-
910
-    /**
911
-     * Resets the ticket and datetime reserved counts.
912
-     *
913
-     * For all the tickets with reservations, recalculates what their actual reserved counts should be based
914
-     * on the valid transactions.
915
-     *
916
-     * @return int number of tickets whose reservations were released.
917
-     * @throws EE_Error
918
-     * @throws DomainException
919
-     * @throws InvalidDataTypeException
920
-     * @throws InvalidInterfaceException
921
-     * @throws InvalidArgumentException
922
-     * @throws UnexpectedEntityException
923
-     * @throws ReflectionException
924
-     */
925
-    public static function reset_reservation_counts(): int
926
-    {
927
-        /** @var EE_Line_Item[] $valid_reserved_tickets */
928
-        $valid_reserved_tickets = array();
929
-        /** @var EE_Transaction[] $transactions_in_progress */
930
-        $transactions_in_progress = EEM_Transaction::instance()->get_transactions_in_progress();
931
-        foreach ($transactions_in_progress as $transaction) {
932
-            // if this TXN has been fully completed, then skip it
933
-            if ($transaction->reg_step_completed('finalize_registration')) {
934
-                continue;
935
-            }
936
-            $total_line_item = $transaction->total_line_item();
937
-            // $transaction_in_progress->line
938
-            if (! $total_line_item instanceof EE_Line_Item) {
939
-                throw new DomainException(
940
-                    esc_html__(
941
-                        'Transaction does not have a valid Total Line Item associated with it.',
942
-                        'event_espresso'
943
-                    )
944
-                );
945
-            }
946
-            $valid_reserved_tickets += EED_Ticket_Sales_Monitor::get_ticket_line_items_for_grand_total(
947
-                $total_line_item
948
-            );
949
-        }
950
-        $total_line_items = EEM_Line_Item::instance()->get_total_line_items_for_active_carts();
951
-        foreach ($total_line_items as $total_line_item) {
952
-            $valid_reserved_tickets += EED_Ticket_Sales_Monitor::get_ticket_line_items_for_grand_total(
953
-                $total_line_item
954
-            );
955
-        }
956
-        $tickets_with_reservations = EEM_Ticket::instance()->get_tickets_with_reservations();
957
-        return EED_Ticket_Sales_Monitor::release_reservations_for_tickets(
958
-            $tickets_with_reservations,
959
-            $valid_reserved_tickets,
960
-            __FUNCTION__
961
-        );
962
-    }
963
-
964
-
965
-    /**
966
-     * @param EE_Line_Item $total_line_item
967
-     * @return EE_Line_Item[]
968
-     * @throws EE_Error
969
-     * @throws ReflectionException
970
-     */
971
-    private static function get_ticket_line_items_for_grand_total(EE_Line_Item $total_line_item): array
972
-    {
973
-        /** @var EE_Line_Item[] $valid_reserved_tickets */
974
-        $valid_reserved_tickets = array();
975
-        $ticket_line_items = EEH_Line_Item::get_ticket_line_items($total_line_item);
976
-        foreach ($ticket_line_items as $ticket_line_item) {
977
-            if ($ticket_line_item instanceof EE_Line_Item) {
978
-                $valid_reserved_tickets[ $ticket_line_item->ID() ] = $ticket_line_item;
979
-            }
980
-        }
981
-        return $valid_reserved_tickets;
982
-    }
983
-
984
-
985
-    /**
986
-     * Releases ticket and datetime reservations (ie, reduces the number of reserved spots on them).
987
-     *
988
-     * Given the list of tickets which have reserved spots on them, uses the complete list of line items for tickets
989
-     * whose transactions aren't complete and also aren't yet expired (ie, they're incomplete and younger than the
990
-     * session's expiry time) to update the ticket (and their datetimes') reserved counts.
991
-     *
992
-     * @param EE_Ticket[]    $tickets_with_reservations        all tickets with TKT_reserved > 0
993
-     * @param EE_Line_Item[] $valid_reserved_ticket_line_items all line items for tickets and incomplete transactions
994
-     *                                                         whose session has NOT expired. We will use these to
995
-     *                                                         determine the number of ticket reservations are now
996
-     *                                                         invalid. We don't use the list of invalid ticket line
997
-     *                                                         items because we don't know which of those have already
998
-     *                                                         been taken into account when reducing ticket reservation
999
-     *                                                         counts, and which haven't.
1000
-     * @param string $source
1001
-     * @return int
1002
-     * @throws UnexpectedEntityException
1003
-     * @throws DomainException
1004
-     * @throws EE_Error
1005
-     * @throws ReflectionException
1006
-     */
1007
-    protected static function release_reservations_for_tickets(
1008
-        array $tickets_with_reservations,
1009
-        array $valid_reserved_ticket_line_items = [],
1010
-        string $source = ''
1011
-    ): int {
1012
-        $total_tickets_released = 0;
1013
-        $sold_out_events = array();
1014
-        foreach ($tickets_with_reservations as $ticket_with_reservations) {
1015
-            if (! $ticket_with_reservations instanceof EE_Ticket) {
1016
-                continue;
1017
-            }
1018
-            // The $valid_reserved_ticket_line_items tells us what the reserved count on their tickets (and datetimes)
1019
-            // SHOULD be. Instead of just directly updating the list, we're going to use EE_Ticket::decreaseReserved()
1020
-            // to try to avoid race conditions, so instead of just finding the number to update TO, we're going to find
1021
-            // the number to RELEASE. It's the same end result, just different path.
1022
-            // Begin by assuming we're going to release all the reservations on this ticket.
1023
-            $expired_reservations_count = $ticket_with_reservations->reserved();
1024
-            // Now reduce that number using the list of current valid reservations.
1025
-            foreach ($valid_reserved_ticket_line_items as $valid_reserved_ticket_line_item) {
1026
-                if (
1027
-                    $valid_reserved_ticket_line_item instanceof EE_Line_Item
1028
-                    && $valid_reserved_ticket_line_item->OBJ_ID() === $ticket_with_reservations->ID()
1029
-                ) {
1030
-                    $expired_reservations_count -= $valid_reserved_ticket_line_item->quantity();
1031
-                }
1032
-            }
1033
-            // Only bother saving the tickets and datetimes if we're actually going to release some spots.
1034
-            if ($expired_reservations_count > 0) {
1035
-                $ticket_with_reservations->add_extra_meta(
1036
-                    EE_Ticket::META_KEY_TICKET_RESERVATIONS,
1037
-                    __LINE__ . ') ' . $source . '()'
1038
-                );
1039
-                $ticket_with_reservations->decreaseReserved($expired_reservations_count, true, 'TicketSalesMonitor:' . __LINE__);
1040
-                $total_tickets_released += $expired_reservations_count;
1041
-                $event = $ticket_with_reservations->get_related_event();
1042
-                // track sold out events
1043
-                if ($event instanceof EE_Event && $event->is_sold_out()) {
1044
-                    $sold_out_events[] = $event;
1045
-                }
1046
-            }
1047
-        }
1048
-        // Double check whether sold out events should remain sold out after releasing tickets
1049
-        if ($sold_out_events !== array()) {
1050
-            foreach ($sold_out_events as $sold_out_event) {
1051
-                /** @var EE_Event $sold_out_event */
1052
-                $sold_out_event->perform_sold_out_status_check();
1053
-            }
1054
-        }
1055
-        return $total_tickets_released;
1056
-    }
1057
-
1058
-
1059
-
1060
-    /********************************** SHUTDOWN  **********************************/
1061
-
1062
-
1063
-    /**
1064
-     * @param int $timestamp
1065
-     * @return false|int
1066
-     * @throws EE_Error
1067
-     * @throws InvalidArgumentException
1068
-     * @throws InvalidDataTypeException
1069
-     * @throws InvalidInterfaceException
1070
-     * @throws ReflectionException
1071
-     */
1072
-    public static function clear_expired_line_items_with_no_transaction(int $timestamp = 0)
1073
-    {
1074
-        /** @type WPDB $wpdb */
1075
-        global $wpdb;
1076
-        if (! absint($timestamp)) {
1077
-            /** @var EventEspresso\core\domain\values\session\SessionLifespan $session_lifespan */
1078
-            $session_lifespan = LoaderFactory::getLoader()->getShared(
1079
-                'EventEspresso\core\domain\values\session\SessionLifespan'
1080
-            );
1081
-            $timestamp = $session_lifespan->expiration();
1082
-        }
1083
-        return $wpdb->query(
1084
-            $wpdb->prepare(
1085
-                'DELETE FROM ' . EEM_Line_Item::instance()->table() . '
652
+			$STS_ID === EEM_Transaction::failed_status_code
653
+			|| $STS_ID === EEM_Transaction::abandoned_status_code
654
+			|| (
655
+				// also release Tickets for Incomplete Transactions, but ONLY if the Registrations are NOT Approved
656
+				$STS_ID === EEM_Transaction::incomplete_status_code
657
+				&& $registration->status_ID() !== RegStatus::APPROVED
658
+			)
659
+		) {
660
+			if (self::debug) {
661
+				echo self::$nl . self::$nl . ' . . RELEASE RESERVED TICKET';
662
+				$reserved = $registration->get_extra_meta(EE_Registration::HAS_RESERVED_TICKET_KEY, true);
663
+				echo self::$nl . ' . . . registration HAS_RESERVED_TICKET_KEY: ';
664
+				var_dump($reserved);
665
+			}
666
+			$registration->release_reserved_ticket(true, 'TicketSalesMonitor:' . __LINE__);
667
+			return 1;
668
+		}
669
+		return 0;
670
+	}
671
+
672
+
673
+
674
+	/********************************** SESSION_CART_RESET  **********************************/
675
+
676
+
677
+	/**
678
+	 * callback hooked into 'AHEE__EE_Session__reset_cart__before_reset'
679
+	 *
680
+	 * @param EE_Session $session
681
+	 * @return void
682
+	 * @throws EE_Error
683
+	 * @throws InvalidArgumentException
684
+	 * @throws ReflectionException
685
+	 * @throws InvalidDataTypeException
686
+	 * @throws InvalidInterfaceException
687
+	 */
688
+	public static function session_cart_reset(EE_Session $session)
689
+	{
690
+		// don't release tickets if checkout was already reset
691
+		if (did_action('AHEE__EE_Session__reset_checkout__before_reset')) {
692
+			return;
693
+		}
694
+		// self::debug hardcoded to false
695
+		if (self::debug) {
696
+			echo self::$nl . self::$nl . __LINE__ . ') ' . __METHOD__ . '() ';
697
+		}
698
+		// first check of the session has a valid Checkout object
699
+		$checkout = $session->checkout();
700
+		if ($checkout instanceof EE_Checkout) {
701
+			// and use that to clear ticket reservations because it will update the associated registration meta data
702
+			EED_Ticket_Sales_Monitor::instance()->_session_checkout_reset($checkout);
703
+			return;
704
+		}
705
+		$cart = $session->cart();
706
+		if ($cart instanceof EE_Cart) {
707
+			if (self::debug) {
708
+				echo self::$nl . self::$nl . ' cart instance of EE_Cart: ';
709
+			}
710
+			EED_Ticket_Sales_Monitor::instance()->_session_cart_reset($cart, $session);
711
+		} else {
712
+			if (self::debug) {
713
+				echo self::$nl . self::$nl . ' invalid EE_Cart: ';
714
+				var_export($cart, true);
715
+			}
716
+		}
717
+	}
718
+
719
+
720
+	/**
721
+	 * releases reserved tickets in the EE_Cart
722
+	 *
723
+	 * @param EE_Cart $cart
724
+	 * @param EE_Session $session
725
+	 * @return void
726
+	 * @throws EE_Error
727
+	 * @throws InvalidArgumentException
728
+	 * @throws ReflectionException
729
+	 * @throws InvalidDataTypeException
730
+	 * @throws InvalidInterfaceException
731
+	 */
732
+	protected function _session_cart_reset(EE_Cart $cart, EE_Session $session)
733
+	{
734
+		// self::debug hardcoded to false
735
+		if (self::debug) {
736
+			echo self::$nl . self::$nl . __LINE__ . ') ' . __METHOD__ . '() ';
737
+		}
738
+		$ticket_line_items = $cart->get_tickets();
739
+		if (empty($ticket_line_items)) {
740
+			return;
741
+		}
742
+		if (self::debug) {
743
+			echo '<br /> . ticket_line_item count: ' . count($ticket_line_items);
744
+		}
745
+		foreach ($ticket_line_items as $ticket_line_item) {
746
+			if (self::debug) {
747
+				echo self::$nl . ' . ticket_line_item->ID(): ' . $ticket_line_item->ID();
748
+			}
749
+			if ($ticket_line_item instanceof EE_Line_Item && $ticket_line_item->OBJ_type() === 'Ticket') {
750
+				if (self::debug) {
751
+					echo self::$nl . ' . . ticket_line_item->OBJ_ID(): ' . $ticket_line_item->OBJ_ID();
752
+				}
753
+				$ticket = EEM_Ticket::instance()->get_one_by_ID($ticket_line_item->OBJ_ID());
754
+				if ($ticket instanceof EE_Ticket) {
755
+					if (self::debug) {
756
+						echo self::$nl . ' . . ticket->ID(): ' . $ticket->ID();
757
+						echo self::$nl . ' . . ticket_line_item->quantity(): ' . $ticket_line_item->quantity();
758
+					}
759
+					$ticket->add_extra_meta(
760
+						EE_Ticket::META_KEY_TICKET_RESERVATIONS,
761
+						__LINE__ . ') ' . __METHOD__ . '() SID = ' . $session->id()
762
+					);
763
+					$this->_release_reserved_ticket($ticket, $ticket_line_item->quantity());
764
+				}
765
+			}
766
+		}
767
+		if (self::debug) {
768
+			echo self::$nl . self::$nl . ' RESET COMPLETED ';
769
+		}
770
+	}
771
+
772
+
773
+
774
+	/********************************** SESSION_CHECKOUT_RESET  **********************************/
775
+
776
+
777
+	/**
778
+	 * callback hooked into 'AHEE__EE_Session__reset_checkout__before_reset'
779
+	 *
780
+	 * @param EE_Session $session
781
+	 * @return void
782
+	 * @throws EE_Error
783
+	 * @throws ReflectionException
784
+	 */
785
+	public static function session_checkout_reset(EE_Session $session)
786
+	{
787
+		// don't release tickets if cart was already reset
788
+		if (did_action('AHEE__EE_Session__reset_cart__before_reset')) {
789
+			return;
790
+		}
791
+		$checkout = $session->checkout();
792
+		if ($checkout instanceof EE_Checkout) {
793
+			EED_Ticket_Sales_Monitor::instance()->_session_checkout_reset($checkout);
794
+		}
795
+	}
796
+
797
+
798
+	/**
799
+	 * releases reserved tickets for the EE_Checkout->transaction
800
+	 *
801
+	 * @param EE_Checkout $checkout
802
+	 * @return void
803
+	 * @throws EE_Error
804
+	 * @throws ReflectionException
805
+	 */
806
+	protected function _session_checkout_reset(EE_Checkout $checkout)
807
+	{
808
+		// self::debug hardcoded to false
809
+		if (self::debug) {
810
+			echo self::$nl . self::$nl . __LINE__ . ') ' . __METHOD__ . '() ';
811
+		}
812
+		// we want to release the each registration's reserved tickets if the session was cleared, but not if this is a revisit
813
+		if ($checkout->revisit || ! $checkout->transaction instanceof EE_Transaction) {
814
+			return;
815
+		}
816
+		$this->_release_all_reserved_tickets_for_transaction($checkout->transaction);
817
+	}
818
+
819
+
820
+
821
+	/********************************** SESSION_EXPIRED_RESET  **********************************/
822
+
823
+
824
+	/**
825
+	 * @param    EE_Session $session
826
+	 * @return    void
827
+	 */
828
+	public static function session_expired_reset(EE_Session $session)
829
+	{
830
+	}
831
+
832
+
833
+
834
+	/********************************** PROCESS_ABANDONED_TRANSACTIONS  **********************************/
835
+
836
+
837
+	/**
838
+	 * releases reserved tickets for all registrations of an ABANDONED EE_Transaction
839
+	 * by default, will NOT release tickets for free transactions, or any that have received a payment
840
+	 *
841
+	 * @param EE_Transaction $transaction
842
+	 * @return void
843
+	 * @throws EE_Error
844
+	 * @throws ReflectionException
845
+	 */
846
+	public static function process_abandoned_transactions(EE_Transaction $transaction)
847
+	{
848
+		// is this TXN free or has any money been paid towards this TXN? If so, then leave it alone
849
+		if ($transaction->is_free() || $transaction->paid() > 0) {
850
+			// self::debug hardcoded to false
851
+			if (self::debug) {
852
+				// DEBUG LOG
853
+				EEH_Debug_Tools::log(
854
+					__CLASS__,
855
+					__FUNCTION__,
856
+					__LINE__,
857
+					array($transaction),
858
+					false,
859
+					'EE_Transaction: ' . $transaction->ID()
860
+				);
861
+			}
862
+			return;
863
+		}
864
+		// have their been any successful payments made ?
865
+		$payments = $transaction->payments();
866
+		foreach ($payments as $payment) {
867
+			if ($payment instanceof EE_Payment && $payment->status() === EEM_Payment::status_id_approved) {
868
+				if (self::debug) {
869
+					// DEBUG LOG
870
+					EEH_Debug_Tools::log(
871
+						__CLASS__,
872
+						__FUNCTION__,
873
+						__LINE__,
874
+						array($payment),
875
+						false,
876
+						'EE_Transaction: ' . $transaction->ID()
877
+					);
878
+				}
879
+				return;
880
+			}
881
+		}
882
+		// since you haven't even attempted to pay for your ticket...
883
+		EED_Ticket_Sales_Monitor::instance()->_release_all_reserved_tickets_for_transaction($transaction);
884
+	}
885
+
886
+
887
+
888
+	/********************************** PROCESS_FAILED_TRANSACTIONS  **********************************/
889
+
890
+
891
+	/**
892
+	 * releases reserved tickets for absolutely ALL registrations of a FAILED EE_Transaction
893
+	 *
894
+	 * @param EE_Transaction $transaction
895
+	 * @return void
896
+	 * @throws EE_Error
897
+	 * @throws ReflectionException
898
+	 */
899
+	public static function process_failed_transactions(EE_Transaction $transaction)
900
+	{
901
+		// since you haven't even attempted to pay for your ticket...
902
+		EED_Ticket_Sales_Monitor::instance()->_release_all_reserved_tickets_for_transaction($transaction);
903
+	}
904
+
905
+
906
+
907
+	/********************************** RESET RESERVATION COUNTS  *********************************/
908
+
909
+
910
+	/**
911
+	 * Resets the ticket and datetime reserved counts.
912
+	 *
913
+	 * For all the tickets with reservations, recalculates what their actual reserved counts should be based
914
+	 * on the valid transactions.
915
+	 *
916
+	 * @return int number of tickets whose reservations were released.
917
+	 * @throws EE_Error
918
+	 * @throws DomainException
919
+	 * @throws InvalidDataTypeException
920
+	 * @throws InvalidInterfaceException
921
+	 * @throws InvalidArgumentException
922
+	 * @throws UnexpectedEntityException
923
+	 * @throws ReflectionException
924
+	 */
925
+	public static function reset_reservation_counts(): int
926
+	{
927
+		/** @var EE_Line_Item[] $valid_reserved_tickets */
928
+		$valid_reserved_tickets = array();
929
+		/** @var EE_Transaction[] $transactions_in_progress */
930
+		$transactions_in_progress = EEM_Transaction::instance()->get_transactions_in_progress();
931
+		foreach ($transactions_in_progress as $transaction) {
932
+			// if this TXN has been fully completed, then skip it
933
+			if ($transaction->reg_step_completed('finalize_registration')) {
934
+				continue;
935
+			}
936
+			$total_line_item = $transaction->total_line_item();
937
+			// $transaction_in_progress->line
938
+			if (! $total_line_item instanceof EE_Line_Item) {
939
+				throw new DomainException(
940
+					esc_html__(
941
+						'Transaction does not have a valid Total Line Item associated with it.',
942
+						'event_espresso'
943
+					)
944
+				);
945
+			}
946
+			$valid_reserved_tickets += EED_Ticket_Sales_Monitor::get_ticket_line_items_for_grand_total(
947
+				$total_line_item
948
+			);
949
+		}
950
+		$total_line_items = EEM_Line_Item::instance()->get_total_line_items_for_active_carts();
951
+		foreach ($total_line_items as $total_line_item) {
952
+			$valid_reserved_tickets += EED_Ticket_Sales_Monitor::get_ticket_line_items_for_grand_total(
953
+				$total_line_item
954
+			);
955
+		}
956
+		$tickets_with_reservations = EEM_Ticket::instance()->get_tickets_with_reservations();
957
+		return EED_Ticket_Sales_Monitor::release_reservations_for_tickets(
958
+			$tickets_with_reservations,
959
+			$valid_reserved_tickets,
960
+			__FUNCTION__
961
+		);
962
+	}
963
+
964
+
965
+	/**
966
+	 * @param EE_Line_Item $total_line_item
967
+	 * @return EE_Line_Item[]
968
+	 * @throws EE_Error
969
+	 * @throws ReflectionException
970
+	 */
971
+	private static function get_ticket_line_items_for_grand_total(EE_Line_Item $total_line_item): array
972
+	{
973
+		/** @var EE_Line_Item[] $valid_reserved_tickets */
974
+		$valid_reserved_tickets = array();
975
+		$ticket_line_items = EEH_Line_Item::get_ticket_line_items($total_line_item);
976
+		foreach ($ticket_line_items as $ticket_line_item) {
977
+			if ($ticket_line_item instanceof EE_Line_Item) {
978
+				$valid_reserved_tickets[ $ticket_line_item->ID() ] = $ticket_line_item;
979
+			}
980
+		}
981
+		return $valid_reserved_tickets;
982
+	}
983
+
984
+
985
+	/**
986
+	 * Releases ticket and datetime reservations (ie, reduces the number of reserved spots on them).
987
+	 *
988
+	 * Given the list of tickets which have reserved spots on them, uses the complete list of line items for tickets
989
+	 * whose transactions aren't complete and also aren't yet expired (ie, they're incomplete and younger than the
990
+	 * session's expiry time) to update the ticket (and their datetimes') reserved counts.
991
+	 *
992
+	 * @param EE_Ticket[]    $tickets_with_reservations        all tickets with TKT_reserved > 0
993
+	 * @param EE_Line_Item[] $valid_reserved_ticket_line_items all line items for tickets and incomplete transactions
994
+	 *                                                         whose session has NOT expired. We will use these to
995
+	 *                                                         determine the number of ticket reservations are now
996
+	 *                                                         invalid. We don't use the list of invalid ticket line
997
+	 *                                                         items because we don't know which of those have already
998
+	 *                                                         been taken into account when reducing ticket reservation
999
+	 *                                                         counts, and which haven't.
1000
+	 * @param string $source
1001
+	 * @return int
1002
+	 * @throws UnexpectedEntityException
1003
+	 * @throws DomainException
1004
+	 * @throws EE_Error
1005
+	 * @throws ReflectionException
1006
+	 */
1007
+	protected static function release_reservations_for_tickets(
1008
+		array $tickets_with_reservations,
1009
+		array $valid_reserved_ticket_line_items = [],
1010
+		string $source = ''
1011
+	): int {
1012
+		$total_tickets_released = 0;
1013
+		$sold_out_events = array();
1014
+		foreach ($tickets_with_reservations as $ticket_with_reservations) {
1015
+			if (! $ticket_with_reservations instanceof EE_Ticket) {
1016
+				continue;
1017
+			}
1018
+			// The $valid_reserved_ticket_line_items tells us what the reserved count on their tickets (and datetimes)
1019
+			// SHOULD be. Instead of just directly updating the list, we're going to use EE_Ticket::decreaseReserved()
1020
+			// to try to avoid race conditions, so instead of just finding the number to update TO, we're going to find
1021
+			// the number to RELEASE. It's the same end result, just different path.
1022
+			// Begin by assuming we're going to release all the reservations on this ticket.
1023
+			$expired_reservations_count = $ticket_with_reservations->reserved();
1024
+			// Now reduce that number using the list of current valid reservations.
1025
+			foreach ($valid_reserved_ticket_line_items as $valid_reserved_ticket_line_item) {
1026
+				if (
1027
+					$valid_reserved_ticket_line_item instanceof EE_Line_Item
1028
+					&& $valid_reserved_ticket_line_item->OBJ_ID() === $ticket_with_reservations->ID()
1029
+				) {
1030
+					$expired_reservations_count -= $valid_reserved_ticket_line_item->quantity();
1031
+				}
1032
+			}
1033
+			// Only bother saving the tickets and datetimes if we're actually going to release some spots.
1034
+			if ($expired_reservations_count > 0) {
1035
+				$ticket_with_reservations->add_extra_meta(
1036
+					EE_Ticket::META_KEY_TICKET_RESERVATIONS,
1037
+					__LINE__ . ') ' . $source . '()'
1038
+				);
1039
+				$ticket_with_reservations->decreaseReserved($expired_reservations_count, true, 'TicketSalesMonitor:' . __LINE__);
1040
+				$total_tickets_released += $expired_reservations_count;
1041
+				$event = $ticket_with_reservations->get_related_event();
1042
+				// track sold out events
1043
+				if ($event instanceof EE_Event && $event->is_sold_out()) {
1044
+					$sold_out_events[] = $event;
1045
+				}
1046
+			}
1047
+		}
1048
+		// Double check whether sold out events should remain sold out after releasing tickets
1049
+		if ($sold_out_events !== array()) {
1050
+			foreach ($sold_out_events as $sold_out_event) {
1051
+				/** @var EE_Event $sold_out_event */
1052
+				$sold_out_event->perform_sold_out_status_check();
1053
+			}
1054
+		}
1055
+		return $total_tickets_released;
1056
+	}
1057
+
1058
+
1059
+
1060
+	/********************************** SHUTDOWN  **********************************/
1061
+
1062
+
1063
+	/**
1064
+	 * @param int $timestamp
1065
+	 * @return false|int
1066
+	 * @throws EE_Error
1067
+	 * @throws InvalidArgumentException
1068
+	 * @throws InvalidDataTypeException
1069
+	 * @throws InvalidInterfaceException
1070
+	 * @throws ReflectionException
1071
+	 */
1072
+	public static function clear_expired_line_items_with_no_transaction(int $timestamp = 0)
1073
+	{
1074
+		/** @type WPDB $wpdb */
1075
+		global $wpdb;
1076
+		if (! absint($timestamp)) {
1077
+			/** @var EventEspresso\core\domain\values\session\SessionLifespan $session_lifespan */
1078
+			$session_lifespan = LoaderFactory::getLoader()->getShared(
1079
+				'EventEspresso\core\domain\values\session\SessionLifespan'
1080
+			);
1081
+			$timestamp = $session_lifespan->expiration();
1082
+		}
1083
+		return $wpdb->query(
1084
+			$wpdb->prepare(
1085
+				'DELETE FROM ' . EEM_Line_Item::instance()->table() . '
1086 1086
                 WHERE TXN_ID = 0 AND LIN_timestamp <= %s',
1087
-                // use GMT time because that's what LIN_timestamps are in
1088
-                date('Y-m-d H:i:s', $timestamp)
1089
-            )
1090
-        );
1091
-    }
1087
+				// use GMT time because that's what LIN_timestamps are in
1088
+				date('Y-m-d H:i:s', $timestamp)
1089
+			)
1090
+		);
1091
+	}
1092 1092
 }
Please login to merge, or discard this patch.
modules/ticket_selector/DisplayTicketSelector.php 2 patches
Indentation   +873 added lines, -873 removed lines patch added patch discarded remove patch
@@ -41,880 +41,880 @@
 block discarded – undo
41 41
  */
42 42
 class DisplayTicketSelector
43 43
 {
44
-    /**
45
-     * @var RequestInterface
46
-     */
47
-    protected $request;
48
-
49
-    /**
50
-     * @var EE_Ticket_Selector_Config
51
-     */
52
-    protected $config;
53
-
54
-    /**
55
-     * event that ticket selector is being generated for
56
-     *
57
-     * @access protected
58
-     * @var EE_Event $event
59
-     */
60
-    protected $event;
61
-
62
-    /**
63
-     * Used to flag when the ticket selector is being called from an external iframe.
64
-     *
65
-     * @var bool $iframe
66
-     */
67
-    protected $iframe = false;
68
-
69
-    /**
70
-     * max attendees that can register for event at one time
71
-     *
72
-     * @var int $max_attendees
73
-     */
74
-    private $max_attendees = EE_INF;
75
-
76
-    /**
77
-     * @var string $date_format
78
-     */
79
-    private $date_format;
80
-
81
-    /**
82
-     * @var string $time_format
83
-     */
84
-    private $time_format;
85
-
86
-    /**
87
-     * @var boolean $display_full_ui
88
-     */
89
-    private $display_full_ui;
90
-
91
-    /**
92
-     * @var CurrentUser
93
-     */
94
-    private $current_user;
95
-
96
-
97
-    /**
98
-     * DisplayTicketSelector constructor.
99
-     *
100
-     * @param CurrentUser $current_user
101
-     * @param RequestInterface          $request
102
-     * @param EE_Ticket_Selector_Config $config
103
-     * @param bool                      $iframe
104
-     */
105
-    public function __construct(
106
-        CurrentUser $current_user,
107
-        RequestInterface $request,
108
-        EE_Ticket_Selector_Config $config,
109
-        bool $iframe = false
110
-    ) {
111
-        $this->current_user = $current_user;
112
-        $this->request     = $request;
113
-        $this->config      = $config;
114
-        $this->setIframe($iframe);
115
-        $this->date_format = apply_filters(
116
-            'FHEE__EED_Ticket_Selector__display_ticket_selector__date_format',
117
-            get_option('date_format')
118
-        );
119
-        $this->time_format  = apply_filters(
120
-            'FHEE__EED_Ticket_Selector__display_ticket_selector__time_format',
121
-            get_option('time_format')
122
-        );
123
-    }
124
-
125
-
126
-    /**
127
-     * @return bool
128
-     */
129
-    public function isIframe(): bool
130
-    {
131
-        return $this->iframe;
132
-    }
133
-
134
-
135
-    /**
136
-     * @param boolean $iframe
137
-     */
138
-    public function setIframe(bool $iframe = true)
139
-    {
140
-        $this->iframe = filter_var($iframe, FILTER_VALIDATE_BOOLEAN);
141
-    }
142
-
143
-
144
-    /**
145
-     * finds and sets the \EE_Event object for use throughout class
146
-     *
147
-     * @param mixed $event
148
-     * @return bool
149
-     * @throws EE_Error
150
-     * @throws InvalidDataTypeException
151
-     * @throws InvalidInterfaceException
152
-     * @throws InvalidArgumentException
153
-     */
154
-    protected function setEvent($event = null): bool
155
-    {
156
-        global $post;
157
-        if ($event === null) {
158
-            $event = $post;
159
-        }
160
-        if ($event instanceof EE_Event) {
161
-            $this->event = $event;
162
-            return true;
163
-        }
164
-        if ($event instanceof WP_Post) {
165
-            if (
166
-                isset($event->EE_Event)
167
-                && $event->EE_Event instanceof EE_Event
168
-                && ( ! $post instanceof WP_Post || $post->ID === $event->EE_Event->ID() )
169
-            ) {
170
-                $this->event = $event->EE_Event;
171
-                return true;
172
-            }
173
-            if (isset($event->post_type) && $event->post_type === EspressoPostType::EVENTS) {
174
-                $event->EE_Event = EEM_Event::instance()->instantiate_class_from_post_object($event);
175
-                $this->event     = $event->EE_Event;
176
-                return true;
177
-            }
178
-        }
179
-        $user_msg = esc_html__('No Event object or an invalid Event object was supplied.', 'event_espresso');
180
-        $dev_msg  = $user_msg . esc_html__(
181
-            'In order to generate a ticket selector, please ensure you are passing either an EE_Event object or a WP_Post object of the post type "espresso_event" to the EE_Ticket_Selector class constructor.',
182
-            'event_espresso'
183
-        );
184
-        EE_Error::add_error($user_msg . '||' . $dev_msg, __FILE__, __FUNCTION__, __LINE__);
185
-        return false;
186
-    }
187
-
188
-
189
-    /**
190
-     * @return int
191
-     */
192
-    public function getMaxAttendees(): int
193
-    {
194
-        return $this->max_attendees;
195
-    }
196
-
197
-
198
-    /**
199
-     * @param int $max_attendees
200
-     */
201
-    public function setMaxAttendees(int $max_attendees)
202
-    {
203
-        $this->max_attendees = absint(
204
-            apply_filters(
205
-                'FHEE__EE_Ticket_Selector__display_ticket_selector__max_tickets',
206
-                $max_attendees
207
-            )
208
-        );
209
-    }
210
-
211
-
212
-    /**
213
-     * Returns whether or not the full ticket selector should be shown or not.
214
-     * Currently, it displays on the frontend (including ajax requests) but not the backend
215
-     *
216
-     * @return bool
217
-     */
218
-    private function display_full_ui(): bool
219
-    {
220
-        if ($this->display_full_ui === null) {
221
-            $this->display_full_ui = ! is_admin() || (defined('DOING_AJAX') && DOING_AJAX);
222
-        }
223
-        return $this->display_full_ui;
224
-    }
225
-
226
-
227
-    /**
228
-     * creates buttons for selecting number of attendees for an event
229
-     *
230
-     * @param WP_Post|int $event
231
-     * @param bool        $view_details
232
-     * @return string
233
-     * @throws EE_Error
234
-     * @throws InvalidArgumentException
235
-     * @throws InvalidDataTypeException
236
-     * @throws InvalidInterfaceException
237
-     * @throws ReflectionException
238
-     * @throws Exception
239
-     */
240
-    public function display($event = null, bool $view_details = false)
241
-    {
242
-        // reset filter for displaying submit button
243
-        remove_filter('FHEE__EE_Ticket_Selector__display_ticket_selector_submit', '__return_true');
244
-        // poke and prod incoming event till it tells us what it is
245
-        if (! $this->setEvent($event)) {
246
-            return $this->handleMissingEvent();
247
-        }
248
-        // is the event expired ?
249
-        $template_args['event_is_expired'] = ! is_admin() && $this->event->is_expired();
250
-        if ($template_args['event_is_expired']) {
251
-            return is_single()
252
-                ? $this->expiredEventMessage()
253
-                : $this->expiredEventMessage() . $this->displayViewDetailsButton();
254
-        }
255
-        // begin gathering template arguments by getting event status
256
-        $template_args = ['event_status' => $this->event->get_active_status()];
257
-        if (
258
-            $this->activeEventAndShowTicketSelector(
259
-                $event,
260
-                $template_args['event_status'],
261
-                $view_details
262
-            )
263
-        ) {
264
-            return ! is_single() ? $this->displayViewDetailsButton() : '';
265
-        }
266
-        // filter the maximum qty that can appear in the Ticket Selector qty dropdowns
267
-        $this->setMaxAttendees($this->event->additional_limit());
268
-        if ($this->getMaxAttendees() < 1) {
269
-            return $this->ticketSalesClosedMessage();
270
-        }
271
-        // get all tickets for this event ordered by the datetime
272
-        $tickets = $this->getTickets();
273
-        if (count($tickets) < 1) {
274
-            return $this->noTicketAvailableMessage();
275
-        }
276
-        // redirecting to another site for registration ??
277
-        $external_url = (string) $this->event->external_url()
278
-                        && $this->event->external_url() !== get_the_permalink()
279
-            ? $this->event->external_url()
280
-            : '';
281
-        // if redirecting to another site for registration, then we don't load the TS
282
-        $ticket_selector = $external_url
283
-            ? $this->externalEventRegistration()
284
-            : $this->loadTicketSelector($tickets, $template_args);
285
-        // now set up the form (but not for the admin)
286
-        $ticket_selector = $this->display_full_ui()
287
-            ? $this->formOpen($this->event->ID(), $external_url) . $ticket_selector
288
-            : $ticket_selector;
289
-        // submit button and form close tag
290
-        $ticket_selector .= $this->display_full_ui() ? $this->displaySubmitButton($external_url) : '';
291
-        return $ticket_selector;
292
-    }
293
-
294
-
295
-    /**
296
-     * displayTicketSelector
297
-     * examines the event properties and determines whether a Ticket Selector should be displayed
298
-     *
299
-     * @param WP_Post|int $event
300
-     * @param string      $_event_active_status
301
-     * @param bool        $view_details
302
-     * @return bool
303
-     * @throws EE_Error
304
-     * @throws ReflectionException
305
-     */
306
-    protected function activeEventAndShowTicketSelector($event, string $_event_active_status, bool $view_details): bool
307
-    {
308
-        $event_post = $this->event instanceof EE_Event ? $this->event->ID() : $event;
309
-        return $this->display_full_ui()
310
-               && (
311
-                   ! $this->event->display_ticket_selector()
312
-                   || $view_details
313
-                   || post_password_required($event_post)
314
-                   || (
315
-                       $_event_active_status !== EE_Datetime::active
316
-                       && $_event_active_status !== EE_Datetime::upcoming
317
-                       && $_event_active_status !== EE_Datetime::sold_out
318
-                       && ! (
319
-                           $_event_active_status === EE_Datetime::inactive
320
-                           && is_user_logged_in()
321
-                       )
322
-                   )
323
-               );
324
-    }
325
-
326
-
327
-    /**
328
-     * noTicketAvailableMessage
329
-     * notice displayed if event is expired
330
-     *
331
-     * @return string
332
-     */
333
-    protected function expiredEventMessage(): string
334
-    {
335
-        return '<div class="ee-event-expired-notice"><span class="important-notice">'
336
-           . esc_html__(
337
-               'We\'re sorry, but all tickets sales have ended because the event is expired.',
338
-               'event_espresso'
339
-           )
340
-           . '</span></div><!-- .ee-event-expired-notice -->';
341
-    }
342
-
343
-
344
-    /**
345
-     * noTicketAvailableMessage
346
-     * notice displayed if event has no more tickets available
347
-     *
348
-     * @return string
349
-     * @throws EE_Error
350
-     * @throws ReflectionException
351
-     */
352
-    protected function noTicketAvailableMessage(): string
353
-    {
354
-        $no_ticket_available_msg = esc_html__('We\'re sorry, but all ticket sales have ended.', 'event_espresso');
355
-        if (current_user_can('edit_post', $this->event->ID())) {
356
-            $no_ticket_available_msg .= sprintf(
357
-                esc_html__(
358
-                    '%1$sNote to Event Admin:%2$sNo tickets were found for this event. This effectively turns off ticket sales. Please ensure that at least one ticket is available for if you want people to be able to register.%3$s(click to edit this event)%4$s',
359
-                    'event_espresso'
360
-                ),
361
-                '<div class="ee-attention" style="text-align: left;"><b>',
362
-                '</b><br />',
363
-                '<span class="edit-link"><a class="post-edit-link" href="'
364
-                . get_edit_post_link($this->event->ID())
365
-                . '">',
366
-                '</a></span></div><!-- .ee-attention noTicketAvailableMessage -->'
367
-            );
368
-        }
369
-        return '
44
+	/**
45
+	 * @var RequestInterface
46
+	 */
47
+	protected $request;
48
+
49
+	/**
50
+	 * @var EE_Ticket_Selector_Config
51
+	 */
52
+	protected $config;
53
+
54
+	/**
55
+	 * event that ticket selector is being generated for
56
+	 *
57
+	 * @access protected
58
+	 * @var EE_Event $event
59
+	 */
60
+	protected $event;
61
+
62
+	/**
63
+	 * Used to flag when the ticket selector is being called from an external iframe.
64
+	 *
65
+	 * @var bool $iframe
66
+	 */
67
+	protected $iframe = false;
68
+
69
+	/**
70
+	 * max attendees that can register for event at one time
71
+	 *
72
+	 * @var int $max_attendees
73
+	 */
74
+	private $max_attendees = EE_INF;
75
+
76
+	/**
77
+	 * @var string $date_format
78
+	 */
79
+	private $date_format;
80
+
81
+	/**
82
+	 * @var string $time_format
83
+	 */
84
+	private $time_format;
85
+
86
+	/**
87
+	 * @var boolean $display_full_ui
88
+	 */
89
+	private $display_full_ui;
90
+
91
+	/**
92
+	 * @var CurrentUser
93
+	 */
94
+	private $current_user;
95
+
96
+
97
+	/**
98
+	 * DisplayTicketSelector constructor.
99
+	 *
100
+	 * @param CurrentUser $current_user
101
+	 * @param RequestInterface          $request
102
+	 * @param EE_Ticket_Selector_Config $config
103
+	 * @param bool                      $iframe
104
+	 */
105
+	public function __construct(
106
+		CurrentUser $current_user,
107
+		RequestInterface $request,
108
+		EE_Ticket_Selector_Config $config,
109
+		bool $iframe = false
110
+	) {
111
+		$this->current_user = $current_user;
112
+		$this->request     = $request;
113
+		$this->config      = $config;
114
+		$this->setIframe($iframe);
115
+		$this->date_format = apply_filters(
116
+			'FHEE__EED_Ticket_Selector__display_ticket_selector__date_format',
117
+			get_option('date_format')
118
+		);
119
+		$this->time_format  = apply_filters(
120
+			'FHEE__EED_Ticket_Selector__display_ticket_selector__time_format',
121
+			get_option('time_format')
122
+		);
123
+	}
124
+
125
+
126
+	/**
127
+	 * @return bool
128
+	 */
129
+	public function isIframe(): bool
130
+	{
131
+		return $this->iframe;
132
+	}
133
+
134
+
135
+	/**
136
+	 * @param boolean $iframe
137
+	 */
138
+	public function setIframe(bool $iframe = true)
139
+	{
140
+		$this->iframe = filter_var($iframe, FILTER_VALIDATE_BOOLEAN);
141
+	}
142
+
143
+
144
+	/**
145
+	 * finds and sets the \EE_Event object for use throughout class
146
+	 *
147
+	 * @param mixed $event
148
+	 * @return bool
149
+	 * @throws EE_Error
150
+	 * @throws InvalidDataTypeException
151
+	 * @throws InvalidInterfaceException
152
+	 * @throws InvalidArgumentException
153
+	 */
154
+	protected function setEvent($event = null): bool
155
+	{
156
+		global $post;
157
+		if ($event === null) {
158
+			$event = $post;
159
+		}
160
+		if ($event instanceof EE_Event) {
161
+			$this->event = $event;
162
+			return true;
163
+		}
164
+		if ($event instanceof WP_Post) {
165
+			if (
166
+				isset($event->EE_Event)
167
+				&& $event->EE_Event instanceof EE_Event
168
+				&& ( ! $post instanceof WP_Post || $post->ID === $event->EE_Event->ID() )
169
+			) {
170
+				$this->event = $event->EE_Event;
171
+				return true;
172
+			}
173
+			if (isset($event->post_type) && $event->post_type === EspressoPostType::EVENTS) {
174
+				$event->EE_Event = EEM_Event::instance()->instantiate_class_from_post_object($event);
175
+				$this->event     = $event->EE_Event;
176
+				return true;
177
+			}
178
+		}
179
+		$user_msg = esc_html__('No Event object or an invalid Event object was supplied.', 'event_espresso');
180
+		$dev_msg  = $user_msg . esc_html__(
181
+			'In order to generate a ticket selector, please ensure you are passing either an EE_Event object or a WP_Post object of the post type "espresso_event" to the EE_Ticket_Selector class constructor.',
182
+			'event_espresso'
183
+		);
184
+		EE_Error::add_error($user_msg . '||' . $dev_msg, __FILE__, __FUNCTION__, __LINE__);
185
+		return false;
186
+	}
187
+
188
+
189
+	/**
190
+	 * @return int
191
+	 */
192
+	public function getMaxAttendees(): int
193
+	{
194
+		return $this->max_attendees;
195
+	}
196
+
197
+
198
+	/**
199
+	 * @param int $max_attendees
200
+	 */
201
+	public function setMaxAttendees(int $max_attendees)
202
+	{
203
+		$this->max_attendees = absint(
204
+			apply_filters(
205
+				'FHEE__EE_Ticket_Selector__display_ticket_selector__max_tickets',
206
+				$max_attendees
207
+			)
208
+		);
209
+	}
210
+
211
+
212
+	/**
213
+	 * Returns whether or not the full ticket selector should be shown or not.
214
+	 * Currently, it displays on the frontend (including ajax requests) but not the backend
215
+	 *
216
+	 * @return bool
217
+	 */
218
+	private function display_full_ui(): bool
219
+	{
220
+		if ($this->display_full_ui === null) {
221
+			$this->display_full_ui = ! is_admin() || (defined('DOING_AJAX') && DOING_AJAX);
222
+		}
223
+		return $this->display_full_ui;
224
+	}
225
+
226
+
227
+	/**
228
+	 * creates buttons for selecting number of attendees for an event
229
+	 *
230
+	 * @param WP_Post|int $event
231
+	 * @param bool        $view_details
232
+	 * @return string
233
+	 * @throws EE_Error
234
+	 * @throws InvalidArgumentException
235
+	 * @throws InvalidDataTypeException
236
+	 * @throws InvalidInterfaceException
237
+	 * @throws ReflectionException
238
+	 * @throws Exception
239
+	 */
240
+	public function display($event = null, bool $view_details = false)
241
+	{
242
+		// reset filter for displaying submit button
243
+		remove_filter('FHEE__EE_Ticket_Selector__display_ticket_selector_submit', '__return_true');
244
+		// poke and prod incoming event till it tells us what it is
245
+		if (! $this->setEvent($event)) {
246
+			return $this->handleMissingEvent();
247
+		}
248
+		// is the event expired ?
249
+		$template_args['event_is_expired'] = ! is_admin() && $this->event->is_expired();
250
+		if ($template_args['event_is_expired']) {
251
+			return is_single()
252
+				? $this->expiredEventMessage()
253
+				: $this->expiredEventMessage() . $this->displayViewDetailsButton();
254
+		}
255
+		// begin gathering template arguments by getting event status
256
+		$template_args = ['event_status' => $this->event->get_active_status()];
257
+		if (
258
+			$this->activeEventAndShowTicketSelector(
259
+				$event,
260
+				$template_args['event_status'],
261
+				$view_details
262
+			)
263
+		) {
264
+			return ! is_single() ? $this->displayViewDetailsButton() : '';
265
+		}
266
+		// filter the maximum qty that can appear in the Ticket Selector qty dropdowns
267
+		$this->setMaxAttendees($this->event->additional_limit());
268
+		if ($this->getMaxAttendees() < 1) {
269
+			return $this->ticketSalesClosedMessage();
270
+		}
271
+		// get all tickets for this event ordered by the datetime
272
+		$tickets = $this->getTickets();
273
+		if (count($tickets) < 1) {
274
+			return $this->noTicketAvailableMessage();
275
+		}
276
+		// redirecting to another site for registration ??
277
+		$external_url = (string) $this->event->external_url()
278
+						&& $this->event->external_url() !== get_the_permalink()
279
+			? $this->event->external_url()
280
+			: '';
281
+		// if redirecting to another site for registration, then we don't load the TS
282
+		$ticket_selector = $external_url
283
+			? $this->externalEventRegistration()
284
+			: $this->loadTicketSelector($tickets, $template_args);
285
+		// now set up the form (but not for the admin)
286
+		$ticket_selector = $this->display_full_ui()
287
+			? $this->formOpen($this->event->ID(), $external_url) . $ticket_selector
288
+			: $ticket_selector;
289
+		// submit button and form close tag
290
+		$ticket_selector .= $this->display_full_ui() ? $this->displaySubmitButton($external_url) : '';
291
+		return $ticket_selector;
292
+	}
293
+
294
+
295
+	/**
296
+	 * displayTicketSelector
297
+	 * examines the event properties and determines whether a Ticket Selector should be displayed
298
+	 *
299
+	 * @param WP_Post|int $event
300
+	 * @param string      $_event_active_status
301
+	 * @param bool        $view_details
302
+	 * @return bool
303
+	 * @throws EE_Error
304
+	 * @throws ReflectionException
305
+	 */
306
+	protected function activeEventAndShowTicketSelector($event, string $_event_active_status, bool $view_details): bool
307
+	{
308
+		$event_post = $this->event instanceof EE_Event ? $this->event->ID() : $event;
309
+		return $this->display_full_ui()
310
+			   && (
311
+				   ! $this->event->display_ticket_selector()
312
+				   || $view_details
313
+				   || post_password_required($event_post)
314
+				   || (
315
+					   $_event_active_status !== EE_Datetime::active
316
+					   && $_event_active_status !== EE_Datetime::upcoming
317
+					   && $_event_active_status !== EE_Datetime::sold_out
318
+					   && ! (
319
+						   $_event_active_status === EE_Datetime::inactive
320
+						   && is_user_logged_in()
321
+					   )
322
+				   )
323
+			   );
324
+	}
325
+
326
+
327
+	/**
328
+	 * noTicketAvailableMessage
329
+	 * notice displayed if event is expired
330
+	 *
331
+	 * @return string
332
+	 */
333
+	protected function expiredEventMessage(): string
334
+	{
335
+		return '<div class="ee-event-expired-notice"><span class="important-notice">'
336
+		   . esc_html__(
337
+			   'We\'re sorry, but all tickets sales have ended because the event is expired.',
338
+			   'event_espresso'
339
+		   )
340
+		   . '</span></div><!-- .ee-event-expired-notice -->';
341
+	}
342
+
343
+
344
+	/**
345
+	 * noTicketAvailableMessage
346
+	 * notice displayed if event has no more tickets available
347
+	 *
348
+	 * @return string
349
+	 * @throws EE_Error
350
+	 * @throws ReflectionException
351
+	 */
352
+	protected function noTicketAvailableMessage(): string
353
+	{
354
+		$no_ticket_available_msg = esc_html__('We\'re sorry, but all ticket sales have ended.', 'event_espresso');
355
+		if (current_user_can('edit_post', $this->event->ID())) {
356
+			$no_ticket_available_msg .= sprintf(
357
+				esc_html__(
358
+					'%1$sNote to Event Admin:%2$sNo tickets were found for this event. This effectively turns off ticket sales. Please ensure that at least one ticket is available for if you want people to be able to register.%3$s(click to edit this event)%4$s',
359
+					'event_espresso'
360
+				),
361
+				'<div class="ee-attention" style="text-align: left;"><b>',
362
+				'</b><br />',
363
+				'<span class="edit-link"><a class="post-edit-link" href="'
364
+				. get_edit_post_link($this->event->ID())
365
+				. '">',
366
+				'</a></span></div><!-- .ee-attention noTicketAvailableMessage -->'
367
+			);
368
+		}
369
+		return '
370 370
             <div class="ee-event-expired-notice">
371 371
                 <span class="important-notice">' . $no_ticket_available_msg . '</span>
372 372
             </div><!-- .ee-event-expired-notice -->';
373
-    }
374
-
375
-
376
-    /**
377
-     * ticketSalesClosed
378
-     * notice displayed if event ticket sales are turned off
379
-     *
380
-     * @return string
381
-     * @throws EE_Error
382
-     * @throws ReflectionException
383
-     */
384
-    protected function ticketSalesClosedMessage(): string
385
-    {
386
-        $sales_closed_msg = esc_html__(
387
-            'We\'re sorry, but ticket sales have been closed at this time. Please check back again later.',
388
-            'event_espresso'
389
-        );
390
-        if (current_user_can('edit_post', $this->event->ID())) {
391
-            $sales_closed_msg .= sprintf(
392
-                esc_html__(
393
-                    '%sNote to Event Admin:%sThe "Maximum number of tickets allowed per order for this event" in the Event Registration Options has been set to "0". This effectively turns off ticket sales. %s(click to edit this event)%s',
394
-                    'event_espresso'
395
-                ),
396
-                '<div class="ee-attention" style="text-align: left;"><b>',
397
-                '</b><br />',
398
-                '<span class="edit-link"><a class="post-edit-link" href="'
399
-                . get_edit_post_link($this->event->ID())
400
-                . '">',
401
-                '</a></span></div><!-- .ee-attention ticketSalesClosedMessage -->'
402
-            );
403
-        }
404
-        return '<p><span class="important-notice">' . $sales_closed_msg . '</span></p>';
405
-    }
406
-
407
-
408
-    /**
409
-     * getTickets
410
-     *
411
-     * @return EE_Base_Class[]|EE_Ticket[]
412
-     * @throws EE_Error
413
-     * @throws InvalidDataTypeException
414
-     * @throws InvalidInterfaceException
415
-     * @throws InvalidArgumentException
416
-     * @throws ReflectionException
417
-     */
418
-    protected function getTickets()
419
-    {
420
-        $show_expired_tickets = is_admin() || $this->config->show_expired_tickets;
421
-
422
-        $ticket_query_args = [
423
-            [
424
-                'Datetime.EVT_ID' => $this->event->ID(),
425
-                'TKT_visibility'  => ['>', EEM_Ticket::TICKET_VISIBILITY_NONE_VALUE],
426
-            ],
427
-            'order_by' => [
428
-                'TKT_required'           => 'DESC',
429
-                'TKT_order'              => 'ASC',
430
-                'TKT_start_date'         => 'ASC',
431
-                'TKT_end_date'           => 'ASC',
432
-                'Datetime.DTT_EVT_start' => 'DESC',
433
-            ],
434
-        ];
435
-
436
-        $datetime_id = $this->request->getRequestParam('datetime', 0, 'int');
437
-        if ($datetime_id) {
438
-            $ticket_query_args[0]['Datetime.DTT_ID'] = $datetime_id;
439
-        }
440
-
441
-        if (! $show_expired_tickets) {
442
-            // use the correct applicable time query depending on what version of core is being run.
443
-            $current_time                         = method_exists('EEM_Datetime', 'current_time_for_query')
444
-                ? time()
445
-                : current_time('timestamp');
446
-            $ticket_query_args[0]['TKT_end_date'] = ['>', $current_time];
447
-        }
448
-        /** @var EE_Ticket[] $tickets */
449
-        $tickets = EEM_Ticket::instance()->get_all($ticket_query_args);
450
-        // remove tickets based on their visibility and the current user's allowed access (crudely based on roles)
451
-        // and filter the returned results
452
-         $tickets = array_filter($tickets, [$this, 'ticketVisibilityFilter']);
453
-        return (array) apply_filters(
454
-            'FHEE__EventEspresso_modules_ticketSelector_DisplayTicketSelector__getTickets',
455
-            $tickets,
456
-            $ticket_query_args,
457
-            $this
458
-        );
459
-    }
460
-
461
-
462
-    /**
463
-     * returns true if any of the following is true:
464
-     *  - ticket visibility is PUBLIC
465
-     *  - ticket visibility is MEMBERS_ONLY and user is logged in
466
-     *  - ticket visibility is ADMINS_ONLY when user IS logged in as an admin
467
-     *  - ticket visibility is ADMIN_UI_ONLY when ticket selector is being viewed via an admin page UI
468
-     *
469
-     * @param EE_Ticket $ticket
470
-     * @return bool
471
-     * @throws EE_Error
472
-     * @throws ReflectionException
473
-     * @since   5.0.0.p
474
-     */
475
-    public function ticketVisibilityFilter(EE_Ticket $ticket): bool
476
-    {
477
-        return $ticket->isPublicOnly()
478
-               || ($ticket->isMembersOnly() && $this->current_user->isLoggedIn())
479
-               || (
480
-                   $ticket->isAdminsOnly()
481
-                   && ($this->current_user->isEventManager() || $this->current_user->isSuperAdmin())
482
-               )
483
-               || ($ticket->isAdminUiOnly() && is_admin());
484
-    }
485
-
486
-
487
-    /**
488
-     * loadTicketSelector
489
-     * begins to assemble template arguments
490
-     * and decides whether to load a "simple" ticket selector, or the standard
491
-     *
492
-     * @param EE_Ticket[] $tickets
493
-     * @param array       $template_args
494
-     * @return TicketSelectorSimple|TicketSelectorStandard
495
-     * @throws EE_Error
496
-     * @throws ReflectionException
497
-     */
498
-    protected function loadTicketSelector(array $tickets, array $template_args)
499
-    {
500
-        $template_args['event']            = $this->event;
501
-        $template_args['EVT_ID']           = $this->event->ID();
502
-        $template_args['event_is_expired'] = $this->event->is_expired();
503
-        $template_args['max_atndz']        = $this->getMaxAttendees();
504
-        $template_args['date_format']      = $this->date_format;
505
-        $template_args['time_format']      = $this->time_format;
506
-        /**
507
-         * Filters the anchor ID used when redirecting to the Ticket Selector if no quantity selected
508
-         *
509
-         * @param string  '#tkt-slctr-tbl-' . $EVT_ID The html ID to anchor to
510
-         * @param int $EVT_ID The Event ID
511
-         * @since 4.9.13
512
-         */
513
-        $template_args['anchor_id']    = apply_filters(
514
-            'FHEE__EE_Ticket_Selector__redirect_anchor_id',
515
-            '#tkt-slctr-tbl-' . $this->event->ID(),
516
-            $this->event->ID()
517
-        );
518
-        $template_args['tickets']      = $tickets;
519
-        $template_args['ticket_count'] = count($tickets);
520
-        $ticket_selector               = $this->simpleTicketSelector($tickets, $template_args);
521
-        if ($ticket_selector instanceof TicketSelectorSimple) {
522
-            return $ticket_selector;
523
-        }
524
-        return new TicketSelectorStandard(
525
-            $this->config,
526
-            $this->getTaxConfig(),
527
-            $this->event,
528
-            $tickets,
529
-            $this->getMaxAttendees(),
530
-            $template_args,
531
-            $this->date_format,
532
-            $this->time_format
533
-        );
534
-    }
535
-
536
-
537
-    /**
538
-     * simpleTicketSelector
539
-     * there's one ticket, and max attendees is set to one,
540
-     * so if the event is free, then this is a "simple" ticket selector
541
-     * a.k.a. "Dude Where's my Ticket Selector?"
542
-     *
543
-     * @param EE_Ticket[] $tickets
544
-     * @param array       $template_args
545
-     * @return string
546
-     * @throws EE_Error
547
-     * @throws ReflectionException
548
-     */
549
-    protected function simpleTicketSelector(array $tickets, array $template_args)
550
-    {
551
-        // if there is only ONE ticket with a max qty of ONE
552
-        if (count($tickets) > 1 || $this->getMaxAttendees() !== 1) {
553
-            return '';
554
-        }
555
-        /** @var EE_Ticket $ticket */
556
-        $ticket = reset($tickets);
557
-        // if the ticket is free... then not much need for the ticket selector
558
-        if (
559
-            apply_filters(
560
-                'FHEE__ticket_selector_chart_template__hide_ticket_selector',
561
-                $ticket->is_free(),
562
-                $this->event->ID()
563
-            )
564
-        ) {
565
-            return new TicketSelectorSimple(
566
-                $this->event,
567
-                $ticket,
568
-                $this->getMaxAttendees(),
569
-                $template_args
570
-            );
571
-        }
572
-        return '';
573
-    }
574
-
575
-
576
-    /**
577
-     * externalEventRegistration
578
-     *
579
-     * @return string
580
-     */
581
-    public function externalEventRegistration(): string
582
-    {
583
-        // if not we still need to trigger the display of the submit button
584
-        add_filter('FHEE__EE_Ticket_Selector__display_ticket_selector_submit', '__return_true');
585
-        // display notice to admin that registration is external
586
-        return $this->display_full_ui()
587
-            ? esc_html__(
588
-                'Registration is at an external URL for this event.',
589
-                'event_espresso'
590
-            )
591
-            : '';
592
-    }
593
-
594
-
595
-    /**
596
-     * formOpen
597
-     *
598
-     * @param int    $ID
599
-     * @param string $external_url
600
-     * @return        string
601
-     */
602
-    public function formOpen(int $ID = 0, string $external_url = ''): string
603
-    {
604
-        // if redirecting, we don't need any anything else
605
-        if ($external_url) {
606
-            $html = '<form method="GET" ';
607
-            $html .= 'action="' . EEH_URL::refactor_url($external_url) . '" ';
608
-            $html .= 'name="ticket-selector-form-' . $ID . '"';
609
-            // open link in new window ?
610
-            $html       .= apply_filters(
611
-                'FHEE__EventEspresso_modules_ticket_selector_DisplayTicketSelector__formOpen__external_url_target_blank',
612
-                $this->isIframe(),
613
-                $this
614
-            )
615
-                ? ' target="_blank"'
616
-                : '';
617
-            $html       .= '>';
618
-            $query_args = EEH_URL::get_query_string($external_url);
619
-            foreach ((array) $query_args as $query_arg => $value) {
620
-                $html .= '<input type="hidden" name="' . $query_arg . '" value="' . $value . '">';
621
-            }
622
-            return $html;
623
-        }
624
-        // if there is no submit button, then don't start building a form
625
-        // because the "View Details" button will build its own form
626
-        if (! apply_filters('FHEE__EE_Ticket_Selector__display_ticket_selector_submit', false)) {
627
-            return '';
628
-        }
629
-        $checkout_url = EEH_Event_View::event_link_url($ID);
630
-        if (! $checkout_url) {
631
-            EE_Error::add_error(
632
-                esc_html__('The URL for the Event Details page could not be retrieved.', 'event_espresso'),
633
-                __FILE__,
634
-                __FUNCTION__,
635
-                __LINE__
636
-            );
637
-        }
638
-        // set no cache headers and constants
639
-        EE_System::do_not_cache();
640
-        $html = '<form method="POST" ';
641
-        $html .= 'action="' . $checkout_url . '" ';
642
-        $html .= 'name="ticket-selector-form-' . $ID . '"';
643
-        $html .= $this->iframe ? ' target="_blank"' : '';
644
-        $html .= '>';
645
-        $html .= '<input type="hidden" name="ee" value="process_ticket_selections">';
646
-        return apply_filters('FHEE__EE_Ticket_Selector__ticket_selector_form_open__html', $html, $this->event);
647
-    }
648
-
649
-
650
-    /**
651
-     * displaySubmitButton
652
-     *
653
-     * @param string $external_url
654
-     * @return string
655
-     * @throws EE_Error
656
-     * @throws ReflectionException
657
-     */
658
-    public function displaySubmitButton(string $external_url = ''): string
659
-    {
660
-        $html = '';
661
-        if ($this->display_full_ui()) {
662
-            // standard TS displayed with submit button, ie: "Register Now"
663
-            if (apply_filters('FHEE__EE_Ticket_Selector__display_ticket_selector_submit', false)) {
664
-                $html .= $this->displayRegisterNowButton();
665
-                $html .= empty($external_url)
666
-                    ? $this->ticketSelectorEndDiv()
667
-                    : $this->clearTicketSelector();
668
-                $html .= '<br/>' . $this->formClose();
669
-            } elseif ($this->getMaxAttendees() === 1) {
670
-                // its a "Dude Where's my Ticket Selector?" (DWMTS) type event (ie: $_max_atndz === 1)
671
-                if ($this->event->is_sold_out()) {
672
-                    // then instead of a View Details or Submit button, just display a "Sold Out" message
673
-                    $html .= apply_filters(
674
-                        'FHEE__EE_Ticket_Selector__display_ticket_selector_submit__sold_out_msg',
675
-                        sprintf(
676
-                            esc_html__(
677
-                                '%1$s"%2$s" is currently sold out.%4$sPlease check back again later, as spots may become available.%3$s',
678
-                                'event_espresso'
679
-                            ),
680
-                            '<p class="no-ticket-selector-msg clear-float">',
681
-                            $this->event->name(),
682
-                            '</p>',
683
-                            '<br />'
684
-                        ),
685
-                        $this->event
686
-                    );
687
-                    if (
688
-                        apply_filters(
689
-                            'FHEE__EE_Ticket_Selector__display_ticket_selector_submit__no_tickets_but_display_register_now_button',
690
-                            false,
691
-                            $this->event
692
-                        )
693
-                    ) {
694
-                        $html .= $this->displayRegisterNowButton();
695
-                    }
696
-                    // sold out DWMTS event, no TS, no submit or view details button, but has additional content
697
-                    $html .= $this->ticketSelectorEndDiv();
698
-                } elseif (
699
-                    apply_filters('FHEE__EE_Ticket_Selector__hide_ticket_selector', false)
700
-                    && ! is_single()
701
-                ) {
702
-                    // this is a "Dude Where's my Ticket Selector?" (DWMTS) type event,
703
-                    // but no tickets are available, so display event's "View Details" button.
704
-                    // it is being viewed via somewhere other than a single post
705
-                    $html .= $this->displayViewDetailsButton(true);
706
-                } else {
707
-                    $html .= $this->ticketSelectorEndDiv();
708
-                }
709
-            } elseif (is_archive()) {
710
-                // event list, no tickets available so display event's "View Details" button
711
-                $html .= $this->ticketSelectorEndDiv();
712
-                $html .= $this->displayViewDetailsButton();
713
-            } else {
714
-                if (
715
-                    apply_filters(
716
-                        'FHEE__EE_Ticket_Selector__display_ticket_selector_submit__no_tickets_but_display_register_now_button',
717
-                        false,
718
-                        $this->event
719
-                    )
720
-                ) {
721
-                    $html .= $this->displayRegisterNowButton();
722
-                }
723
-                // no submit or view details button, and no additional content
724
-                $html .= $this->ticketSelectorEndDiv();
725
-            }
726
-            if (! $this->iframe && ! is_archive()) {
727
-                $html .= EEH_Template::powered_by_event_espresso('', '', ['utm_content' => 'ticket_selector']);
728
-            }
729
-        }
730
-        return apply_filters(
731
-            'FHEE__EventEspresso_modules_ticket_selector_DisplayTicketSelector__displaySubmitButton__html',
732
-            $html,
733
-            $this->event,
734
-            $this
735
-        );
736
-    }
737
-
738
-
739
-    /**
740
-     * @return string
741
-     * @throws EE_Error
742
-     * @throws ReflectionException
743
-     */
744
-    public function displayRegisterNowButton(): string
745
-    {
746
-        $btn_text     = apply_filters(
747
-            'FHEE__EE_Ticket_Selector__display_ticket_selector_submit__btn_text',
748
-            esc_html__('Register Now', 'event_espresso'),
749
-            $this->event
750
-        );
751
-        $external_url = (string) $this->event->external_url()
752
-                        && $this->event->external_url() !== get_the_permalink()
753
-            ? $this->event->external_url()
754
-            : '';
755
-        $html         = EEH_HTML::div(
756
-            '',
757
-            'ticket-selector-submit-' . $this->event->ID() . '-btn-wrap',
758
-            'ticket-selector-submit-btn-wrap'
759
-        );
760
-        $html         .= '<input id="ticket-selector-submit-' . $this->event->ID() . '-btn"';
761
-        $html         .= ' class="ticket-selector-submit-btn ';
762
-        $html         .= empty($external_url) ? 'ticket-selector-submit-ajax"' : '"';
763
-        $html         .= ' type="submit" value="' . $btn_text . '" data-ee-disable-after-recaptcha="true" />';
764
-        $html         .= EEH_HTML::divx() . '<!-- .ticket-selector-submit-btn-wrap -->';
765
-        $html         .= apply_filters(
766
-            'FHEE__EE_Ticket_Selector__after_ticket_selector_submit',
767
-            '',
768
-            $this->event,
769
-            $this->iframe
770
-        );
771
-        return $html;
772
-    }
773
-
774
-
775
-    /**
776
-     * displayViewDetailsButton
777
-     *
778
-     * @param bool $DWMTS indicates a "Dude Where's my Ticket Selector?" (DWMTS) type event
779
-     *                    (ie: $_max_atndz === 1) where there are no available tickets,
780
-     *                    either because they are sold out, expired, or not yet on sale.
781
-     *                    In this case, we need to close the form BEFORE adding any closing divs
782
-     * @return string
783
-     * @throws EE_Error
784
-     * @throws ReflectionException
785
-     */
786
-    public function displayViewDetailsButton(bool $DWMTS = false): string
787
-    {
788
-        if (! $this->event->get_permalink()) {
789
-            EE_Error::add_error(
790
-                esc_html__('The URL for the Event Details page could not be retrieved.', 'event_espresso'),
791
-                __FILE__,
792
-                __FUNCTION__,
793
-                __LINE__
794
-            );
795
-        }
796
-        $view_details_btn = '<form method="GET" action="';
797
-        $view_details_btn .= apply_filters(
798
-            'FHEE__EE_Ticket_Selector__display_view_details_btn__btn_url',
799
-            $this->event->get_permalink(),
800
-            $this->event
801
-        );
802
-        $view_details_btn .= '"';
803
-        // open link in new window ?
804
-        $view_details_btn .= apply_filters(
805
-            'FHEE__EventEspresso_modules_ticket_selector_DisplayTicketSelector__displayViewDetailsButton__url_target_blank',
806
-            $this->isIframe(),
807
-            $this
808
-        )
809
-            ? ' target="_blank"'
810
-            : '';
811
-        $view_details_btn .= '>';
812
-        $btn_text         = apply_filters(
813
-            'FHEE__EE_Ticket_Selector__display_view_details_btn__btn_text',
814
-            esc_html__('View Details', 'event_espresso'),
815
-            $this->event
816
-        );
817
-        $view_details_btn .= '<input id="ticket-selector-submit-'
818
-                             . $this->event->ID()
819
-                             . '-btn" class="ticket-selector-submit-btn view-details-btn" type="submit" value="'
820
-                             . $btn_text
821
-                             . '" />';
822
-        $view_details_btn .= apply_filters('FHEE__EE_Ticket_Selector__after_view_details_btn', '', $this->event);
823
-        if ($DWMTS) {
824
-            $view_details_btn .= $this->formClose();
825
-            $view_details_btn .= $this->ticketSelectorEndDiv();
826
-            $view_details_btn .= '<br/>';
827
-        } else {
828
-            $view_details_btn .= $this->clearTicketSelector();
829
-            $view_details_btn .= '<br/>';
830
-            $view_details_btn .= $this->formClose();
831
-        }
832
-        return $view_details_btn;
833
-    }
834
-
835
-
836
-    /**
837
-     * @return string
838
-     */
839
-    public function ticketSelectorEndDiv(): string
840
-    {
841
-        return $this->clearTicketSelector() . '</div><!-- ticketSelectorEndDiv -->';
842
-    }
843
-
844
-
845
-    /**
846
-     * @return string
847
-     */
848
-    public function clearTicketSelector(): string
849
-    {
850
-        // standard TS displayed, appears after a "Register Now" or "view Details" button
851
-        return '<div class="clear"></div><!-- clearTicketSelector -->';
852
-    }
853
-
854
-
855
-    /**
856
-     * @access        public
857
-     * @return        string
858
-     */
859
-    public function formClose(): string
860
-    {
861
-        return '</form>';
862
-    }
863
-
864
-
865
-    /**
866
-     * handleMissingEvent
867
-     * Returns either false or an error to display when no valid event is passed.
868
-     *
869
-     * @return string
870
-     * @throws ExceptionStackTraceDisplay
871
-     * @throws InvalidInterfaceException
872
-     * @throws Exception
873
-     */
874
-    protected function handleMissingEvent()
875
-    {
876
-        // If this is not an iFrame request, simply return false.
877
-        if (! $this->isIframe()) {
878
-            return '';
879
-        }
880
-        // This is an iFrame so return an error.
881
-        // Display stack trace if WP_DEBUG is enabled.
882
-        if (WP_DEBUG === true && current_user_can('edit_pages')) {
883
-            $event_id = $this->request->getRequestParam('event', 0, 'int');
884
-            new ExceptionStackTraceDisplay(
885
-                new InvalidArgumentException(
886
-                    sprintf(
887
-                        esc_html__(
888
-                            'A valid Event ID is required to display the ticket selector.%3$sAn Event with an ID of "%1$s" could not be found.%3$sPlease verify that the embed code added to this post\'s content includes an "%2$s" argument and that its value corresponds to a valid Event ID.',
889
-                            'event_espresso'
890
-                        ),
891
-                        $event_id,
892
-                        'event',
893
-                        '<br />'
894
-                    )
895
-                )
896
-            );
897
-            return '';
898
-        }
899
-        // If WP_DEBUG is not enabled, display a message stating the event could not be found.
900
-        return EEH_HTML::p(
901
-            esc_html__(
902
-                'A valid Event could not be found. Please contact the event administrator for assistance.',
903
-                'event_espresso'
904
-            )
905
-        );
906
-    }
907
-
908
-
909
-    /**
910
-     * @return EE_Tax_Config
911
-     * @since   4.10.14.p
912
-     */
913
-    protected function getTaxConfig()
914
-    {
915
-        return isset(EE_Registry::instance()->CFG->tax_settings)
916
-               && EE_Registry::instance()->CFG->tax_settings instanceof EE_Tax_Config
917
-            ? EE_Registry::instance()->CFG->tax_settings
918
-            : new EE_Tax_Config();
919
-    }
373
+	}
374
+
375
+
376
+	/**
377
+	 * ticketSalesClosed
378
+	 * notice displayed if event ticket sales are turned off
379
+	 *
380
+	 * @return string
381
+	 * @throws EE_Error
382
+	 * @throws ReflectionException
383
+	 */
384
+	protected function ticketSalesClosedMessage(): string
385
+	{
386
+		$sales_closed_msg = esc_html__(
387
+			'We\'re sorry, but ticket sales have been closed at this time. Please check back again later.',
388
+			'event_espresso'
389
+		);
390
+		if (current_user_can('edit_post', $this->event->ID())) {
391
+			$sales_closed_msg .= sprintf(
392
+				esc_html__(
393
+					'%sNote to Event Admin:%sThe "Maximum number of tickets allowed per order for this event" in the Event Registration Options has been set to "0". This effectively turns off ticket sales. %s(click to edit this event)%s',
394
+					'event_espresso'
395
+				),
396
+				'<div class="ee-attention" style="text-align: left;"><b>',
397
+				'</b><br />',
398
+				'<span class="edit-link"><a class="post-edit-link" href="'
399
+				. get_edit_post_link($this->event->ID())
400
+				. '">',
401
+				'</a></span></div><!-- .ee-attention ticketSalesClosedMessage -->'
402
+			);
403
+		}
404
+		return '<p><span class="important-notice">' . $sales_closed_msg . '</span></p>';
405
+	}
406
+
407
+
408
+	/**
409
+	 * getTickets
410
+	 *
411
+	 * @return EE_Base_Class[]|EE_Ticket[]
412
+	 * @throws EE_Error
413
+	 * @throws InvalidDataTypeException
414
+	 * @throws InvalidInterfaceException
415
+	 * @throws InvalidArgumentException
416
+	 * @throws ReflectionException
417
+	 */
418
+	protected function getTickets()
419
+	{
420
+		$show_expired_tickets = is_admin() || $this->config->show_expired_tickets;
421
+
422
+		$ticket_query_args = [
423
+			[
424
+				'Datetime.EVT_ID' => $this->event->ID(),
425
+				'TKT_visibility'  => ['>', EEM_Ticket::TICKET_VISIBILITY_NONE_VALUE],
426
+			],
427
+			'order_by' => [
428
+				'TKT_required'           => 'DESC',
429
+				'TKT_order'              => 'ASC',
430
+				'TKT_start_date'         => 'ASC',
431
+				'TKT_end_date'           => 'ASC',
432
+				'Datetime.DTT_EVT_start' => 'DESC',
433
+			],
434
+		];
435
+
436
+		$datetime_id = $this->request->getRequestParam('datetime', 0, 'int');
437
+		if ($datetime_id) {
438
+			$ticket_query_args[0]['Datetime.DTT_ID'] = $datetime_id;
439
+		}
440
+
441
+		if (! $show_expired_tickets) {
442
+			// use the correct applicable time query depending on what version of core is being run.
443
+			$current_time                         = method_exists('EEM_Datetime', 'current_time_for_query')
444
+				? time()
445
+				: current_time('timestamp');
446
+			$ticket_query_args[0]['TKT_end_date'] = ['>', $current_time];
447
+		}
448
+		/** @var EE_Ticket[] $tickets */
449
+		$tickets = EEM_Ticket::instance()->get_all($ticket_query_args);
450
+		// remove tickets based on their visibility and the current user's allowed access (crudely based on roles)
451
+		// and filter the returned results
452
+		 $tickets = array_filter($tickets, [$this, 'ticketVisibilityFilter']);
453
+		return (array) apply_filters(
454
+			'FHEE__EventEspresso_modules_ticketSelector_DisplayTicketSelector__getTickets',
455
+			$tickets,
456
+			$ticket_query_args,
457
+			$this
458
+		);
459
+	}
460
+
461
+
462
+	/**
463
+	 * returns true if any of the following is true:
464
+	 *  - ticket visibility is PUBLIC
465
+	 *  - ticket visibility is MEMBERS_ONLY and user is logged in
466
+	 *  - ticket visibility is ADMINS_ONLY when user IS logged in as an admin
467
+	 *  - ticket visibility is ADMIN_UI_ONLY when ticket selector is being viewed via an admin page UI
468
+	 *
469
+	 * @param EE_Ticket $ticket
470
+	 * @return bool
471
+	 * @throws EE_Error
472
+	 * @throws ReflectionException
473
+	 * @since   5.0.0.p
474
+	 */
475
+	public function ticketVisibilityFilter(EE_Ticket $ticket): bool
476
+	{
477
+		return $ticket->isPublicOnly()
478
+			   || ($ticket->isMembersOnly() && $this->current_user->isLoggedIn())
479
+			   || (
480
+				   $ticket->isAdminsOnly()
481
+				   && ($this->current_user->isEventManager() || $this->current_user->isSuperAdmin())
482
+			   )
483
+			   || ($ticket->isAdminUiOnly() && is_admin());
484
+	}
485
+
486
+
487
+	/**
488
+	 * loadTicketSelector
489
+	 * begins to assemble template arguments
490
+	 * and decides whether to load a "simple" ticket selector, or the standard
491
+	 *
492
+	 * @param EE_Ticket[] $tickets
493
+	 * @param array       $template_args
494
+	 * @return TicketSelectorSimple|TicketSelectorStandard
495
+	 * @throws EE_Error
496
+	 * @throws ReflectionException
497
+	 */
498
+	protected function loadTicketSelector(array $tickets, array $template_args)
499
+	{
500
+		$template_args['event']            = $this->event;
501
+		$template_args['EVT_ID']           = $this->event->ID();
502
+		$template_args['event_is_expired'] = $this->event->is_expired();
503
+		$template_args['max_atndz']        = $this->getMaxAttendees();
504
+		$template_args['date_format']      = $this->date_format;
505
+		$template_args['time_format']      = $this->time_format;
506
+		/**
507
+		 * Filters the anchor ID used when redirecting to the Ticket Selector if no quantity selected
508
+		 *
509
+		 * @param string  '#tkt-slctr-tbl-' . $EVT_ID The html ID to anchor to
510
+		 * @param int $EVT_ID The Event ID
511
+		 * @since 4.9.13
512
+		 */
513
+		$template_args['anchor_id']    = apply_filters(
514
+			'FHEE__EE_Ticket_Selector__redirect_anchor_id',
515
+			'#tkt-slctr-tbl-' . $this->event->ID(),
516
+			$this->event->ID()
517
+		);
518
+		$template_args['tickets']      = $tickets;
519
+		$template_args['ticket_count'] = count($tickets);
520
+		$ticket_selector               = $this->simpleTicketSelector($tickets, $template_args);
521
+		if ($ticket_selector instanceof TicketSelectorSimple) {
522
+			return $ticket_selector;
523
+		}
524
+		return new TicketSelectorStandard(
525
+			$this->config,
526
+			$this->getTaxConfig(),
527
+			$this->event,
528
+			$tickets,
529
+			$this->getMaxAttendees(),
530
+			$template_args,
531
+			$this->date_format,
532
+			$this->time_format
533
+		);
534
+	}
535
+
536
+
537
+	/**
538
+	 * simpleTicketSelector
539
+	 * there's one ticket, and max attendees is set to one,
540
+	 * so if the event is free, then this is a "simple" ticket selector
541
+	 * a.k.a. "Dude Where's my Ticket Selector?"
542
+	 *
543
+	 * @param EE_Ticket[] $tickets
544
+	 * @param array       $template_args
545
+	 * @return string
546
+	 * @throws EE_Error
547
+	 * @throws ReflectionException
548
+	 */
549
+	protected function simpleTicketSelector(array $tickets, array $template_args)
550
+	{
551
+		// if there is only ONE ticket with a max qty of ONE
552
+		if (count($tickets) > 1 || $this->getMaxAttendees() !== 1) {
553
+			return '';
554
+		}
555
+		/** @var EE_Ticket $ticket */
556
+		$ticket = reset($tickets);
557
+		// if the ticket is free... then not much need for the ticket selector
558
+		if (
559
+			apply_filters(
560
+				'FHEE__ticket_selector_chart_template__hide_ticket_selector',
561
+				$ticket->is_free(),
562
+				$this->event->ID()
563
+			)
564
+		) {
565
+			return new TicketSelectorSimple(
566
+				$this->event,
567
+				$ticket,
568
+				$this->getMaxAttendees(),
569
+				$template_args
570
+			);
571
+		}
572
+		return '';
573
+	}
574
+
575
+
576
+	/**
577
+	 * externalEventRegistration
578
+	 *
579
+	 * @return string
580
+	 */
581
+	public function externalEventRegistration(): string
582
+	{
583
+		// if not we still need to trigger the display of the submit button
584
+		add_filter('FHEE__EE_Ticket_Selector__display_ticket_selector_submit', '__return_true');
585
+		// display notice to admin that registration is external
586
+		return $this->display_full_ui()
587
+			? esc_html__(
588
+				'Registration is at an external URL for this event.',
589
+				'event_espresso'
590
+			)
591
+			: '';
592
+	}
593
+
594
+
595
+	/**
596
+	 * formOpen
597
+	 *
598
+	 * @param int    $ID
599
+	 * @param string $external_url
600
+	 * @return        string
601
+	 */
602
+	public function formOpen(int $ID = 0, string $external_url = ''): string
603
+	{
604
+		// if redirecting, we don't need any anything else
605
+		if ($external_url) {
606
+			$html = '<form method="GET" ';
607
+			$html .= 'action="' . EEH_URL::refactor_url($external_url) . '" ';
608
+			$html .= 'name="ticket-selector-form-' . $ID . '"';
609
+			// open link in new window ?
610
+			$html       .= apply_filters(
611
+				'FHEE__EventEspresso_modules_ticket_selector_DisplayTicketSelector__formOpen__external_url_target_blank',
612
+				$this->isIframe(),
613
+				$this
614
+			)
615
+				? ' target="_blank"'
616
+				: '';
617
+			$html       .= '>';
618
+			$query_args = EEH_URL::get_query_string($external_url);
619
+			foreach ((array) $query_args as $query_arg => $value) {
620
+				$html .= '<input type="hidden" name="' . $query_arg . '" value="' . $value . '">';
621
+			}
622
+			return $html;
623
+		}
624
+		// if there is no submit button, then don't start building a form
625
+		// because the "View Details" button will build its own form
626
+		if (! apply_filters('FHEE__EE_Ticket_Selector__display_ticket_selector_submit', false)) {
627
+			return '';
628
+		}
629
+		$checkout_url = EEH_Event_View::event_link_url($ID);
630
+		if (! $checkout_url) {
631
+			EE_Error::add_error(
632
+				esc_html__('The URL for the Event Details page could not be retrieved.', 'event_espresso'),
633
+				__FILE__,
634
+				__FUNCTION__,
635
+				__LINE__
636
+			);
637
+		}
638
+		// set no cache headers and constants
639
+		EE_System::do_not_cache();
640
+		$html = '<form method="POST" ';
641
+		$html .= 'action="' . $checkout_url . '" ';
642
+		$html .= 'name="ticket-selector-form-' . $ID . '"';
643
+		$html .= $this->iframe ? ' target="_blank"' : '';
644
+		$html .= '>';
645
+		$html .= '<input type="hidden" name="ee" value="process_ticket_selections">';
646
+		return apply_filters('FHEE__EE_Ticket_Selector__ticket_selector_form_open__html', $html, $this->event);
647
+	}
648
+
649
+
650
+	/**
651
+	 * displaySubmitButton
652
+	 *
653
+	 * @param string $external_url
654
+	 * @return string
655
+	 * @throws EE_Error
656
+	 * @throws ReflectionException
657
+	 */
658
+	public function displaySubmitButton(string $external_url = ''): string
659
+	{
660
+		$html = '';
661
+		if ($this->display_full_ui()) {
662
+			// standard TS displayed with submit button, ie: "Register Now"
663
+			if (apply_filters('FHEE__EE_Ticket_Selector__display_ticket_selector_submit', false)) {
664
+				$html .= $this->displayRegisterNowButton();
665
+				$html .= empty($external_url)
666
+					? $this->ticketSelectorEndDiv()
667
+					: $this->clearTicketSelector();
668
+				$html .= '<br/>' . $this->formClose();
669
+			} elseif ($this->getMaxAttendees() === 1) {
670
+				// its a "Dude Where's my Ticket Selector?" (DWMTS) type event (ie: $_max_atndz === 1)
671
+				if ($this->event->is_sold_out()) {
672
+					// then instead of a View Details or Submit button, just display a "Sold Out" message
673
+					$html .= apply_filters(
674
+						'FHEE__EE_Ticket_Selector__display_ticket_selector_submit__sold_out_msg',
675
+						sprintf(
676
+							esc_html__(
677
+								'%1$s"%2$s" is currently sold out.%4$sPlease check back again later, as spots may become available.%3$s',
678
+								'event_espresso'
679
+							),
680
+							'<p class="no-ticket-selector-msg clear-float">',
681
+							$this->event->name(),
682
+							'</p>',
683
+							'<br />'
684
+						),
685
+						$this->event
686
+					);
687
+					if (
688
+						apply_filters(
689
+							'FHEE__EE_Ticket_Selector__display_ticket_selector_submit__no_tickets_but_display_register_now_button',
690
+							false,
691
+							$this->event
692
+						)
693
+					) {
694
+						$html .= $this->displayRegisterNowButton();
695
+					}
696
+					// sold out DWMTS event, no TS, no submit or view details button, but has additional content
697
+					$html .= $this->ticketSelectorEndDiv();
698
+				} elseif (
699
+					apply_filters('FHEE__EE_Ticket_Selector__hide_ticket_selector', false)
700
+					&& ! is_single()
701
+				) {
702
+					// this is a "Dude Where's my Ticket Selector?" (DWMTS) type event,
703
+					// but no tickets are available, so display event's "View Details" button.
704
+					// it is being viewed via somewhere other than a single post
705
+					$html .= $this->displayViewDetailsButton(true);
706
+				} else {
707
+					$html .= $this->ticketSelectorEndDiv();
708
+				}
709
+			} elseif (is_archive()) {
710
+				// event list, no tickets available so display event's "View Details" button
711
+				$html .= $this->ticketSelectorEndDiv();
712
+				$html .= $this->displayViewDetailsButton();
713
+			} else {
714
+				if (
715
+					apply_filters(
716
+						'FHEE__EE_Ticket_Selector__display_ticket_selector_submit__no_tickets_but_display_register_now_button',
717
+						false,
718
+						$this->event
719
+					)
720
+				) {
721
+					$html .= $this->displayRegisterNowButton();
722
+				}
723
+				// no submit or view details button, and no additional content
724
+				$html .= $this->ticketSelectorEndDiv();
725
+			}
726
+			if (! $this->iframe && ! is_archive()) {
727
+				$html .= EEH_Template::powered_by_event_espresso('', '', ['utm_content' => 'ticket_selector']);
728
+			}
729
+		}
730
+		return apply_filters(
731
+			'FHEE__EventEspresso_modules_ticket_selector_DisplayTicketSelector__displaySubmitButton__html',
732
+			$html,
733
+			$this->event,
734
+			$this
735
+		);
736
+	}
737
+
738
+
739
+	/**
740
+	 * @return string
741
+	 * @throws EE_Error
742
+	 * @throws ReflectionException
743
+	 */
744
+	public function displayRegisterNowButton(): string
745
+	{
746
+		$btn_text     = apply_filters(
747
+			'FHEE__EE_Ticket_Selector__display_ticket_selector_submit__btn_text',
748
+			esc_html__('Register Now', 'event_espresso'),
749
+			$this->event
750
+		);
751
+		$external_url = (string) $this->event->external_url()
752
+						&& $this->event->external_url() !== get_the_permalink()
753
+			? $this->event->external_url()
754
+			: '';
755
+		$html         = EEH_HTML::div(
756
+			'',
757
+			'ticket-selector-submit-' . $this->event->ID() . '-btn-wrap',
758
+			'ticket-selector-submit-btn-wrap'
759
+		);
760
+		$html         .= '<input id="ticket-selector-submit-' . $this->event->ID() . '-btn"';
761
+		$html         .= ' class="ticket-selector-submit-btn ';
762
+		$html         .= empty($external_url) ? 'ticket-selector-submit-ajax"' : '"';
763
+		$html         .= ' type="submit" value="' . $btn_text . '" data-ee-disable-after-recaptcha="true" />';
764
+		$html         .= EEH_HTML::divx() . '<!-- .ticket-selector-submit-btn-wrap -->';
765
+		$html         .= apply_filters(
766
+			'FHEE__EE_Ticket_Selector__after_ticket_selector_submit',
767
+			'',
768
+			$this->event,
769
+			$this->iframe
770
+		);
771
+		return $html;
772
+	}
773
+
774
+
775
+	/**
776
+	 * displayViewDetailsButton
777
+	 *
778
+	 * @param bool $DWMTS indicates a "Dude Where's my Ticket Selector?" (DWMTS) type event
779
+	 *                    (ie: $_max_atndz === 1) where there are no available tickets,
780
+	 *                    either because they are sold out, expired, or not yet on sale.
781
+	 *                    In this case, we need to close the form BEFORE adding any closing divs
782
+	 * @return string
783
+	 * @throws EE_Error
784
+	 * @throws ReflectionException
785
+	 */
786
+	public function displayViewDetailsButton(bool $DWMTS = false): string
787
+	{
788
+		if (! $this->event->get_permalink()) {
789
+			EE_Error::add_error(
790
+				esc_html__('The URL for the Event Details page could not be retrieved.', 'event_espresso'),
791
+				__FILE__,
792
+				__FUNCTION__,
793
+				__LINE__
794
+			);
795
+		}
796
+		$view_details_btn = '<form method="GET" action="';
797
+		$view_details_btn .= apply_filters(
798
+			'FHEE__EE_Ticket_Selector__display_view_details_btn__btn_url',
799
+			$this->event->get_permalink(),
800
+			$this->event
801
+		);
802
+		$view_details_btn .= '"';
803
+		// open link in new window ?
804
+		$view_details_btn .= apply_filters(
805
+			'FHEE__EventEspresso_modules_ticket_selector_DisplayTicketSelector__displayViewDetailsButton__url_target_blank',
806
+			$this->isIframe(),
807
+			$this
808
+		)
809
+			? ' target="_blank"'
810
+			: '';
811
+		$view_details_btn .= '>';
812
+		$btn_text         = apply_filters(
813
+			'FHEE__EE_Ticket_Selector__display_view_details_btn__btn_text',
814
+			esc_html__('View Details', 'event_espresso'),
815
+			$this->event
816
+		);
817
+		$view_details_btn .= '<input id="ticket-selector-submit-'
818
+							 . $this->event->ID()
819
+							 . '-btn" class="ticket-selector-submit-btn view-details-btn" type="submit" value="'
820
+							 . $btn_text
821
+							 . '" />';
822
+		$view_details_btn .= apply_filters('FHEE__EE_Ticket_Selector__after_view_details_btn', '', $this->event);
823
+		if ($DWMTS) {
824
+			$view_details_btn .= $this->formClose();
825
+			$view_details_btn .= $this->ticketSelectorEndDiv();
826
+			$view_details_btn .= '<br/>';
827
+		} else {
828
+			$view_details_btn .= $this->clearTicketSelector();
829
+			$view_details_btn .= '<br/>';
830
+			$view_details_btn .= $this->formClose();
831
+		}
832
+		return $view_details_btn;
833
+	}
834
+
835
+
836
+	/**
837
+	 * @return string
838
+	 */
839
+	public function ticketSelectorEndDiv(): string
840
+	{
841
+		return $this->clearTicketSelector() . '</div><!-- ticketSelectorEndDiv -->';
842
+	}
843
+
844
+
845
+	/**
846
+	 * @return string
847
+	 */
848
+	public function clearTicketSelector(): string
849
+	{
850
+		// standard TS displayed, appears after a "Register Now" or "view Details" button
851
+		return '<div class="clear"></div><!-- clearTicketSelector -->';
852
+	}
853
+
854
+
855
+	/**
856
+	 * @access        public
857
+	 * @return        string
858
+	 */
859
+	public function formClose(): string
860
+	{
861
+		return '</form>';
862
+	}
863
+
864
+
865
+	/**
866
+	 * handleMissingEvent
867
+	 * Returns either false or an error to display when no valid event is passed.
868
+	 *
869
+	 * @return string
870
+	 * @throws ExceptionStackTraceDisplay
871
+	 * @throws InvalidInterfaceException
872
+	 * @throws Exception
873
+	 */
874
+	protected function handleMissingEvent()
875
+	{
876
+		// If this is not an iFrame request, simply return false.
877
+		if (! $this->isIframe()) {
878
+			return '';
879
+		}
880
+		// This is an iFrame so return an error.
881
+		// Display stack trace if WP_DEBUG is enabled.
882
+		if (WP_DEBUG === true && current_user_can('edit_pages')) {
883
+			$event_id = $this->request->getRequestParam('event', 0, 'int');
884
+			new ExceptionStackTraceDisplay(
885
+				new InvalidArgumentException(
886
+					sprintf(
887
+						esc_html__(
888
+							'A valid Event ID is required to display the ticket selector.%3$sAn Event with an ID of "%1$s" could not be found.%3$sPlease verify that the embed code added to this post\'s content includes an "%2$s" argument and that its value corresponds to a valid Event ID.',
889
+							'event_espresso'
890
+						),
891
+						$event_id,
892
+						'event',
893
+						'<br />'
894
+					)
895
+				)
896
+			);
897
+			return '';
898
+		}
899
+		// If WP_DEBUG is not enabled, display a message stating the event could not be found.
900
+		return EEH_HTML::p(
901
+			esc_html__(
902
+				'A valid Event could not be found. Please contact the event administrator for assistance.',
903
+				'event_espresso'
904
+			)
905
+		);
906
+	}
907
+
908
+
909
+	/**
910
+	 * @return EE_Tax_Config
911
+	 * @since   4.10.14.p
912
+	 */
913
+	protected function getTaxConfig()
914
+	{
915
+		return isset(EE_Registry::instance()->CFG->tax_settings)
916
+			   && EE_Registry::instance()->CFG->tax_settings instanceof EE_Tax_Config
917
+			? EE_Registry::instance()->CFG->tax_settings
918
+			: new EE_Tax_Config();
919
+	}
920 920
 }
Please login to merge, or discard this patch.
Spacing   +33 added lines, -33 removed lines patch added patch discarded remove patch
@@ -116,7 +116,7 @@  discard block
 block discarded – undo
116 116
             'FHEE__EED_Ticket_Selector__display_ticket_selector__date_format',
117 117
             get_option('date_format')
118 118
         );
119
-        $this->time_format  = apply_filters(
119
+        $this->time_format = apply_filters(
120 120
             'FHEE__EED_Ticket_Selector__display_ticket_selector__time_format',
121 121
             get_option('time_format')
122 122
         );
@@ -165,7 +165,7 @@  discard block
 block discarded – undo
165 165
             if (
166 166
                 isset($event->EE_Event)
167 167
                 && $event->EE_Event instanceof EE_Event
168
-                && ( ! $post instanceof WP_Post || $post->ID === $event->EE_Event->ID() )
168
+                && ( ! $post instanceof WP_Post || $post->ID === $event->EE_Event->ID())
169 169
             ) {
170 170
                 $this->event = $event->EE_Event;
171 171
                 return true;
@@ -177,11 +177,11 @@  discard block
 block discarded – undo
177 177
             }
178 178
         }
179 179
         $user_msg = esc_html__('No Event object or an invalid Event object was supplied.', 'event_espresso');
180
-        $dev_msg  = $user_msg . esc_html__(
180
+        $dev_msg  = $user_msg.esc_html__(
181 181
             'In order to generate a ticket selector, please ensure you are passing either an EE_Event object or a WP_Post object of the post type "espresso_event" to the EE_Ticket_Selector class constructor.',
182 182
             'event_espresso'
183 183
         );
184
-        EE_Error::add_error($user_msg . '||' . $dev_msg, __FILE__, __FUNCTION__, __LINE__);
184
+        EE_Error::add_error($user_msg.'||'.$dev_msg, __FILE__, __FUNCTION__, __LINE__);
185 185
         return false;
186 186
     }
187 187
 
@@ -242,7 +242,7 @@  discard block
 block discarded – undo
242 242
         // reset filter for displaying submit button
243 243
         remove_filter('FHEE__EE_Ticket_Selector__display_ticket_selector_submit', '__return_true');
244 244
         // poke and prod incoming event till it tells us what it is
245
-        if (! $this->setEvent($event)) {
245
+        if ( ! $this->setEvent($event)) {
246 246
             return $this->handleMissingEvent();
247 247
         }
248 248
         // is the event expired ?
@@ -250,7 +250,7 @@  discard block
 block discarded – undo
250 250
         if ($template_args['event_is_expired']) {
251 251
             return is_single()
252 252
                 ? $this->expiredEventMessage()
253
-                : $this->expiredEventMessage() . $this->displayViewDetailsButton();
253
+                : $this->expiredEventMessage().$this->displayViewDetailsButton();
254 254
         }
255 255
         // begin gathering template arguments by getting event status
256 256
         $template_args = ['event_status' => $this->event->get_active_status()];
@@ -284,7 +284,7 @@  discard block
 block discarded – undo
284 284
             : $this->loadTicketSelector($tickets, $template_args);
285 285
         // now set up the form (but not for the admin)
286 286
         $ticket_selector = $this->display_full_ui()
287
-            ? $this->formOpen($this->event->ID(), $external_url) . $ticket_selector
287
+            ? $this->formOpen($this->event->ID(), $external_url).$ticket_selector
288 288
             : $ticket_selector;
289 289
         // submit button and form close tag
290 290
         $ticket_selector .= $this->display_full_ui() ? $this->displaySubmitButton($external_url) : '';
@@ -368,7 +368,7 @@  discard block
 block discarded – undo
368 368
         }
369 369
         return '
370 370
             <div class="ee-event-expired-notice">
371
-                <span class="important-notice">' . $no_ticket_available_msg . '</span>
371
+                <span class="important-notice">' . $no_ticket_available_msg.'</span>
372 372
             </div><!-- .ee-event-expired-notice -->';
373 373
     }
374 374
 
@@ -401,7 +401,7 @@  discard block
 block discarded – undo
401 401
                 '</a></span></div><!-- .ee-attention ticketSalesClosedMessage -->'
402 402
             );
403 403
         }
404
-        return '<p><span class="important-notice">' . $sales_closed_msg . '</span></p>';
404
+        return '<p><span class="important-notice">'.$sales_closed_msg.'</span></p>';
405 405
     }
406 406
 
407 407
 
@@ -438,7 +438,7 @@  discard block
 block discarded – undo
438 438
             $ticket_query_args[0]['Datetime.DTT_ID'] = $datetime_id;
439 439
         }
440 440
 
441
-        if (! $show_expired_tickets) {
441
+        if ( ! $show_expired_tickets) {
442 442
             // use the correct applicable time query depending on what version of core is being run.
443 443
             $current_time                         = method_exists('EEM_Datetime', 'current_time_for_query')
444 444
                 ? time()
@@ -510,9 +510,9 @@  discard block
 block discarded – undo
510 510
          * @param int $EVT_ID The Event ID
511 511
          * @since 4.9.13
512 512
          */
513
-        $template_args['anchor_id']    = apply_filters(
513
+        $template_args['anchor_id'] = apply_filters(
514 514
             'FHEE__EE_Ticket_Selector__redirect_anchor_id',
515
-            '#tkt-slctr-tbl-' . $this->event->ID(),
515
+            '#tkt-slctr-tbl-'.$this->event->ID(),
516 516
             $this->event->ID()
517 517
         );
518 518
         $template_args['tickets']      = $tickets;
@@ -604,30 +604,30 @@  discard block
 block discarded – undo
604 604
         // if redirecting, we don't need any anything else
605 605
         if ($external_url) {
606 606
             $html = '<form method="GET" ';
607
-            $html .= 'action="' . EEH_URL::refactor_url($external_url) . '" ';
608
-            $html .= 'name="ticket-selector-form-' . $ID . '"';
607
+            $html .= 'action="'.EEH_URL::refactor_url($external_url).'" ';
608
+            $html .= 'name="ticket-selector-form-'.$ID.'"';
609 609
             // open link in new window ?
610
-            $html       .= apply_filters(
610
+            $html .= apply_filters(
611 611
                 'FHEE__EventEspresso_modules_ticket_selector_DisplayTicketSelector__formOpen__external_url_target_blank',
612 612
                 $this->isIframe(),
613 613
                 $this
614 614
             )
615 615
                 ? ' target="_blank"'
616 616
                 : '';
617
-            $html       .= '>';
617
+            $html .= '>';
618 618
             $query_args = EEH_URL::get_query_string($external_url);
619 619
             foreach ((array) $query_args as $query_arg => $value) {
620
-                $html .= '<input type="hidden" name="' . $query_arg . '" value="' . $value . '">';
620
+                $html .= '<input type="hidden" name="'.$query_arg.'" value="'.$value.'">';
621 621
             }
622 622
             return $html;
623 623
         }
624 624
         // if there is no submit button, then don't start building a form
625 625
         // because the "View Details" button will build its own form
626
-        if (! apply_filters('FHEE__EE_Ticket_Selector__display_ticket_selector_submit', false)) {
626
+        if ( ! apply_filters('FHEE__EE_Ticket_Selector__display_ticket_selector_submit', false)) {
627 627
             return '';
628 628
         }
629 629
         $checkout_url = EEH_Event_View::event_link_url($ID);
630
-        if (! $checkout_url) {
630
+        if ( ! $checkout_url) {
631 631
             EE_Error::add_error(
632 632
                 esc_html__('The URL for the Event Details page could not be retrieved.', 'event_espresso'),
633 633
                 __FILE__,
@@ -638,8 +638,8 @@  discard block
 block discarded – undo
638 638
         // set no cache headers and constants
639 639
         EE_System::do_not_cache();
640 640
         $html = '<form method="POST" ';
641
-        $html .= 'action="' . $checkout_url . '" ';
642
-        $html .= 'name="ticket-selector-form-' . $ID . '"';
641
+        $html .= 'action="'.$checkout_url.'" ';
642
+        $html .= 'name="ticket-selector-form-'.$ID.'"';
643 643
         $html .= $this->iframe ? ' target="_blank"' : '';
644 644
         $html .= '>';
645 645
         $html .= '<input type="hidden" name="ee" value="process_ticket_selections">';
@@ -665,7 +665,7 @@  discard block
 block discarded – undo
665 665
                 $html .= empty($external_url)
666 666
                     ? $this->ticketSelectorEndDiv()
667 667
                     : $this->clearTicketSelector();
668
-                $html .= '<br/>' . $this->formClose();
668
+                $html .= '<br/>'.$this->formClose();
669 669
             } elseif ($this->getMaxAttendees() === 1) {
670 670
                 // its a "Dude Where's my Ticket Selector?" (DWMTS) type event (ie: $_max_atndz === 1)
671 671
                 if ($this->event->is_sold_out()) {
@@ -723,7 +723,7 @@  discard block
 block discarded – undo
723 723
                 // no submit or view details button, and no additional content
724 724
                 $html .= $this->ticketSelectorEndDiv();
725 725
             }
726
-            if (! $this->iframe && ! is_archive()) {
726
+            if ( ! $this->iframe && ! is_archive()) {
727 727
                 $html .= EEH_Template::powered_by_event_espresso('', '', ['utm_content' => 'ticket_selector']);
728 728
             }
729 729
         }
@@ -743,7 +743,7 @@  discard block
 block discarded – undo
743 743
      */
744 744
     public function displayRegisterNowButton(): string
745 745
     {
746
-        $btn_text     = apply_filters(
746
+        $btn_text = apply_filters(
747 747
             'FHEE__EE_Ticket_Selector__display_ticket_selector_submit__btn_text',
748 748
             esc_html__('Register Now', 'event_espresso'),
749 749
             $this->event
@@ -752,16 +752,16 @@  discard block
 block discarded – undo
752 752
                         && $this->event->external_url() !== get_the_permalink()
753 753
             ? $this->event->external_url()
754 754
             : '';
755
-        $html         = EEH_HTML::div(
755
+        $html = EEH_HTML::div(
756 756
             '',
757
-            'ticket-selector-submit-' . $this->event->ID() . '-btn-wrap',
757
+            'ticket-selector-submit-'.$this->event->ID().'-btn-wrap',
758 758
             'ticket-selector-submit-btn-wrap'
759 759
         );
760
-        $html         .= '<input id="ticket-selector-submit-' . $this->event->ID() . '-btn"';
760
+        $html         .= '<input id="ticket-selector-submit-'.$this->event->ID().'-btn"';
761 761
         $html         .= ' class="ticket-selector-submit-btn ';
762 762
         $html         .= empty($external_url) ? 'ticket-selector-submit-ajax"' : '"';
763
-        $html         .= ' type="submit" value="' . $btn_text . '" data-ee-disable-after-recaptcha="true" />';
764
-        $html         .= EEH_HTML::divx() . '<!-- .ticket-selector-submit-btn-wrap -->';
763
+        $html         .= ' type="submit" value="'.$btn_text.'" data-ee-disable-after-recaptcha="true" />';
764
+        $html         .= EEH_HTML::divx().'<!-- .ticket-selector-submit-btn-wrap -->';
765 765
         $html         .= apply_filters(
766 766
             'FHEE__EE_Ticket_Selector__after_ticket_selector_submit',
767 767
             '',
@@ -785,7 +785,7 @@  discard block
 block discarded – undo
785 785
      */
786 786
     public function displayViewDetailsButton(bool $DWMTS = false): string
787 787
     {
788
-        if (! $this->event->get_permalink()) {
788
+        if ( ! $this->event->get_permalink()) {
789 789
             EE_Error::add_error(
790 790
                 esc_html__('The URL for the Event Details page could not be retrieved.', 'event_espresso'),
791 791
                 __FILE__,
@@ -809,7 +809,7 @@  discard block
 block discarded – undo
809 809
             ? ' target="_blank"'
810 810
             : '';
811 811
         $view_details_btn .= '>';
812
-        $btn_text         = apply_filters(
812
+        $btn_text = apply_filters(
813 813
             'FHEE__EE_Ticket_Selector__display_view_details_btn__btn_text',
814 814
             esc_html__('View Details', 'event_espresso'),
815 815
             $this->event
@@ -838,7 +838,7 @@  discard block
 block discarded – undo
838 838
      */
839 839
     public function ticketSelectorEndDiv(): string
840 840
     {
841
-        return $this->clearTicketSelector() . '</div><!-- ticketSelectorEndDiv -->';
841
+        return $this->clearTicketSelector().'</div><!-- ticketSelectorEndDiv -->';
842 842
     }
843 843
 
844 844
 
@@ -874,7 +874,7 @@  discard block
 block discarded – undo
874 874
     protected function handleMissingEvent()
875 875
     {
876 876
         // If this is not an iFrame request, simply return false.
877
-        if (! $this->isIframe()) {
877
+        if ( ! $this->isIframe()) {
878 878
             return '';
879 879
         }
880 880
         // This is an iFrame so return an error.
Please login to merge, or discard this patch.
reg_steps/payment_options/EE_SPCO_Reg_Step_Payment_Options.class.php 2 patches
Indentation   +2844 added lines, -2844 removed lines patch added patch discarded remove patch
@@ -21,2848 +21,2848 @@
 block discarded – undo
21 21
  */
22 22
 class EE_SPCO_Reg_Step_Payment_Options extends EE_SPCO_Reg_Step
23 23
 {
24
-    /**
25
-     * @var EE_Line_Item_Display $Line_Item_Display
26
-     */
27
-    protected $line_item_display;
28
-
29
-    /**
30
-     * @var boolean $handle_IPN_in_this_request
31
-     */
32
-    protected $handle_IPN_in_this_request = false;
33
-
34
-
35
-    /**
36
-     *    set_hooks - for hooking into EE Core, other modules, etc
37
-     *
38
-     * @access    public
39
-     * @return    void
40
-     */
41
-    public static function set_hooks()
42
-    {
43
-        add_filter(
44
-            'FHEE__SPCO__EE_Line_Item_Filter_Collection',
45
-            ['EE_SPCO_Reg_Step_Payment_Options', 'add_spco_line_item_filters']
46
-        );
47
-        add_action(
48
-            'wp_ajax_switch_spco_billing_form',
49
-            ['EE_SPCO_Reg_Step_Payment_Options', 'switch_spco_billing_form']
50
-        );
51
-        add_action(
52
-            'wp_ajax_nopriv_switch_spco_billing_form',
53
-            ['EE_SPCO_Reg_Step_Payment_Options', 'switch_spco_billing_form']
54
-        );
55
-        add_action('wp_ajax_save_payer_details', ['EE_SPCO_Reg_Step_Payment_Options', 'save_payer_details']);
56
-        add_action(
57
-            'wp_ajax_nopriv_save_payer_details',
58
-            ['EE_SPCO_Reg_Step_Payment_Options', 'save_payer_details']
59
-        );
60
-        add_action(
61
-            'wp_ajax_get_transaction_details_for_gateways',
62
-            ['EE_SPCO_Reg_Step_Payment_Options', 'get_transaction_details']
63
-        );
64
-        add_action(
65
-            'wp_ajax_nopriv_get_transaction_details_for_gateways',
66
-            ['EE_SPCO_Reg_Step_Payment_Options', 'get_transaction_details']
67
-        );
68
-        add_filter(
69
-            'FHEE__EED_Recaptcha___bypass_recaptcha__bypass_request_params_array',
70
-            ['EE_SPCO_Reg_Step_Payment_Options', 'bypass_recaptcha_for_load_payment_method'],
71
-            10,
72
-            1
73
-        );
74
-    }
75
-
76
-
77
-    /**
78
-     *    ajax switch_spco_billing_form
79
-     *
80
-     */
81
-    public static function switch_spco_billing_form()
82
-    {
83
-        EED_Single_Page_Checkout::process_ajax_request('switch_payment_method');
84
-    }
85
-
86
-
87
-    /**
88
-     *    ajax save_payer_details
89
-     *
90
-     */
91
-    public static function save_payer_details()
92
-    {
93
-        EED_Single_Page_Checkout::process_ajax_request('save_payer_details_via_ajax');
94
-    }
95
-
96
-
97
-    /**
98
-     *    ajax get_transaction_details
99
-     *
100
-     */
101
-    public static function get_transaction_details()
102
-    {
103
-        EED_Single_Page_Checkout::process_ajax_request('get_transaction_details_for_gateways');
104
-    }
105
-
106
-
107
-    /**
108
-     * bypass_recaptcha_for_load_payment_method
109
-     *
110
-     * @access public
111
-     * @return array
112
-     * @throws InvalidArgumentException
113
-     * @throws InvalidDataTypeException
114
-     * @throws InvalidInterfaceException
115
-     */
116
-    public static function bypass_recaptcha_for_load_payment_method()
117
-    {
118
-        return [
119
-            'EESID'  => EE_Registry::instance()->SSN->id(),
120
-            'step'   => 'payment_options',
121
-            'action' => 'spco_billing_form',
122
-        ];
123
-    }
124
-
125
-
126
-    /**
127
-     *    class constructor
128
-     *
129
-     * @access    public
130
-     * @param EE_Checkout $checkout
131
-     */
132
-    public function __construct(EE_Checkout $checkout)
133
-    {
134
-        $this->request   = EED_Single_Page_Checkout::getRequest();
135
-        $this->_slug     = 'payment_options';
136
-        $this->_name     = esc_html__('Payment Options', 'event_espresso');
137
-        $this->_template = SPCO_REG_STEPS_PATH . $this->_slug . '/payment_options_main.template.php';
138
-        $this->checkout  = $checkout;
139
-        $this->_reset_success_message();
140
-        $this->set_instructions(
141
-            esc_html__(
142
-                'Please select a method of payment and provide any necessary billing information before proceeding.',
143
-                'event_espresso'
144
-            )
145
-        );
146
-    }
147
-
148
-
149
-    /**
150
-     * @return null
151
-     */
152
-    public function line_item_display()
153
-    {
154
-        return $this->line_item_display;
155
-    }
156
-
157
-
158
-    /**
159
-     * @param null $line_item_display
160
-     */
161
-    public function set_line_item_display($line_item_display)
162
-    {
163
-        $this->line_item_display = $line_item_display;
164
-    }
165
-
166
-
167
-    /**
168
-     * @return boolean
169
-     */
170
-    public function handle_IPN_in_this_request()
171
-    {
172
-        return $this->handle_IPN_in_this_request;
173
-    }
174
-
175
-
176
-    /**
177
-     * @param boolean $handle_IPN_in_this_request
178
-     */
179
-    public function set_handle_IPN_in_this_request($handle_IPN_in_this_request)
180
-    {
181
-        $this->handle_IPN_in_this_request = filter_var($handle_IPN_in_this_request, FILTER_VALIDATE_BOOLEAN);
182
-    }
183
-
184
-
185
-    /**
186
-     * translate_js_strings
187
-     *
188
-     * @return void
189
-     */
190
-    public function translate_js_strings()
191
-    {
192
-        EE_Registry::$i18n_js_strings['no_payment_method']      = esc_html__(
193
-            'Please select a method of payment in order to continue.',
194
-            'event_espresso'
195
-        );
196
-        EE_Registry::$i18n_js_strings['invalid_payment_method'] = esc_html__(
197
-            'A valid method of payment could not be determined. Please refresh the page and try again.',
198
-            'event_espresso'
199
-        );
200
-        EE_Registry::$i18n_js_strings['forwarding_to_offsite']  = esc_html__(
201
-            'Forwarding to Secure Payment Provider.',
202
-            'event_espresso'
203
-        );
204
-    }
205
-
206
-
207
-    /**
208
-     * enqueue_styles_and_scripts
209
-     *
210
-     * @return void
211
-     * @throws EE_Error
212
-     * @throws InvalidArgumentException
213
-     * @throws InvalidDataTypeException
214
-     * @throws InvalidInterfaceException
215
-     * @throws ReflectionException
216
-     */
217
-    public function enqueue_styles_and_scripts()
218
-    {
219
-        $transaction = $this->checkout->transaction;
220
-        // if the transaction isn't set or nothing is owed on it, don't enqueue any JS
221
-        if (! $transaction instanceof EE_Transaction || EEH_Money::compare_floats($transaction->remaining(), 0)) {
222
-            return;
223
-        }
224
-        foreach (
225
-            EEM_Payment_Method::instance()->get_all_for_transaction(
226
-                $transaction,
227
-                EEM_Payment_Method::scope_cart
228
-            ) as $payment_method
229
-        ) {
230
-            $type_obj = $payment_method->type_obj();
231
-            if ($type_obj instanceof EE_PMT_Base) {
232
-                $billing_form = $type_obj->generate_new_billing_form($transaction);
233
-                if ($billing_form instanceof EE_Form_Section_Proper) {
234
-                    $billing_form->enqueue_js();
235
-                }
236
-            }
237
-        }
238
-    }
239
-
240
-
241
-    /**
242
-     * initialize_reg_step
243
-     *
244
-     * @return bool
245
-     * @throws EE_Error
246
-     * @throws InvalidArgumentException
247
-     * @throws ReflectionException
248
-     * @throws InvalidDataTypeException
249
-     * @throws InvalidInterfaceException
250
-     */
251
-    public function initialize_reg_step()
252
-    {
253
-        // TODO: if /when we implement donations, then this will need overriding
254
-        if (
255
-            // don't need payment options for:
256
-            // registrations made via the admin
257
-            // completed transactions
258
-            // overpaid transactions
259
-            // $ 0.00 transactions(no payment required)
260
-            ! $this->checkout->payment_required()
261
-            // but do NOT remove if current action being called belongs to this reg step
262
-            && ! is_callable([$this, $this->checkout->action])
263
-            && ! $this->completed()
264
-        ) {
265
-            // and if so, then we no longer need the Payment Options step
266
-            if ($this->is_current_step()) {
267
-                $this->checkout->generate_reg_form = false;
268
-            }
269
-            $this->checkout->remove_reg_step($this->_slug);
270
-            // DEBUG LOG
271
-            // $this->checkout->log( __CLASS__, __FUNCTION__, __LINE__ );
272
-            return false;
273
-        }
274
-        // load EEM_Payment_Method
275
-        EE_Registry::instance()->load_model('Payment_Method');
276
-        // get all active payment methods
277
-        $this->checkout->available_payment_methods = EEM_Payment_Method::instance()->get_all_for_transaction(
278
-            $this->checkout->transaction,
279
-            EEM_Payment_Method::scope_cart
280
-        );
281
-        return true;
282
-    }
283
-
284
-
285
-    /**
286
-     * @return EE_Form_Section_Proper
287
-     * @throws EE_Error
288
-     * @throws InvalidArgumentException
289
-     * @throws ReflectionException
290
-     * @throws EntityNotFoundException
291
-     * @throws InvalidDataTypeException
292
-     * @throws InvalidInterfaceException
293
-     * @throws InvalidStatusException
294
-     */
295
-    public function generate_reg_form()
296
-    {
297
-        // reset in case someone changes their mind
298
-        $this->_reset_selected_method_of_payment();
299
-        // set some defaults
300
-        $this->checkout->selected_method_of_payment = 'payments_closed';
301
-        $registrations_requiring_payment            = [];
302
-        $registrations_for_free_events              = [];
303
-        $registrations_requiring_pre_approval       = [];
304
-        $sold_out_events                            = [];
305
-        $insufficient_spaces_available              = [];
306
-        $no_payment_required                        = true;
307
-        // loop thru registrations to gather info
308
-        $registrations         = $this->checkout->transaction->registrations($this->checkout->reg_cache_where_params);
309
-        $ejected_registrations = EE_SPCO_Reg_Step_Payment_Options::find_registrations_that_lost_their_space(
310
-            $registrations,
311
-            $this->checkout->revisit
312
-        );
313
-        foreach ($registrations as $REG_ID => $registration) {
314
-            /** @var $registration EE_Registration */
315
-            // Skip if the registration has been moved
316
-            if ($registration->wasMoved()) {
317
-                continue;
318
-            }
319
-            // has this registration lost it's space ?
320
-            if (isset($ejected_registrations[ $REG_ID ])) {
321
-                if ($registration->event()->is_sold_out() || $registration->event()->is_sold_out(true)) {
322
-                    $sold_out_events[ $registration->event()->ID() ] = $registration->event();
323
-                } else {
324
-                    $insufficient_spaces_available[ $registration->event()->ID() ] = $registration->event();
325
-                }
326
-                continue;
327
-            }
328
-            // event requires admin approval
329
-            if ($registration->status_ID() === RegStatus::AWAITING_REVIEW) {
330
-                // add event to list of events with pre-approval reg status
331
-                $registrations_requiring_pre_approval[ $REG_ID ] = $registration;
332
-                do_action(
333
-                    'AHEE__EE_SPCO_Reg_Step_Payment_Options__generate_reg_form__event_requires_pre_approval',
334
-                    $registration->event(),
335
-                    $this
336
-                );
337
-                continue;
338
-            }
339
-            if (
340
-                $this->checkout->revisit
341
-                && $registration->status_ID() !== RegStatus::APPROVED
342
-                && (
343
-                    $registration->event()->is_sold_out()
344
-                    || $registration->event()->is_sold_out(true)
345
-                )
346
-            ) {
347
-                // add event to list of events that are sold out
348
-                $sold_out_events[ $registration->event()->ID() ] = $registration->event();
349
-                do_action(
350
-                    'AHEE__EE_SPCO_Reg_Step_Payment_Options__generate_reg_form__sold_out_event',
351
-                    $registration->event(),
352
-                    $this
353
-                );
354
-                continue;
355
-            }
356
-            // are they allowed to pay now and is there monies owing?
357
-            if ($registration->owes_monies_and_can_pay()) {
358
-                $registrations_requiring_payment[ $REG_ID ] = $registration;
359
-                do_action(
360
-                    'AHEE__EE_SPCO_Reg_Step_Payment_Options__generate_reg_form__event_requires_payment',
361
-                    $registration->event(),
362
-                    $this
363
-                );
364
-            } elseif (
365
-                ! $this->checkout->revisit
366
-                      && $registration->status_ID() !== RegStatus::AWAITING_REVIEW
367
-                      && $registration->ticket()->is_free()
368
-            ) {
369
-                $registrations_for_free_events[ $registration->ticket()->ID() ] = $registration;
370
-            }
371
-        }
372
-        $subsections = [];
373
-        // now decide which template to load
374
-        if (! empty($sold_out_events)) {
375
-            $subsections['sold_out_events'] = $this->_sold_out_events($sold_out_events);
376
-        }
377
-        if (! empty($insufficient_spaces_available)) {
378
-            $subsections['insufficient_space'] = $this->_insufficient_spaces_available(
379
-                $insufficient_spaces_available
380
-            );
381
-        }
382
-        if (! empty($registrations_requiring_pre_approval)) {
383
-            $subsections['registrations_requiring_pre_approval'] = $this->_registrations_requiring_pre_approval(
384
-                $registrations_requiring_pre_approval
385
-            );
386
-        }
387
-        if (! empty($registrations_for_free_events)) {
388
-            $subsections['no_payment_required'] = $this->_no_payment_required($registrations_for_free_events);
389
-        }
390
-        if (! empty($registrations_requiring_payment)) {
391
-            if ($this->checkout->amount_owing > 0) {
392
-                // autoload Line_Item_Display classes
393
-                EEH_Autoloader::register_line_item_filter_autoloaders();
394
-                $line_item_filter_processor = new EE_Line_Item_Filter_Processor(
395
-                    apply_filters(
396
-                        'FHEE__SPCO__EE_Line_Item_Filter_Collection',
397
-                        new EE_Line_Item_Filter_Collection()
398
-                    ),
399
-                    $this->checkout->cart->get_grand_total()
400
-                );
401
-                /** @var EE_Line_Item $filtered_line_item_tree */
402
-                $filtered_line_item_tree = $line_item_filter_processor->process();
403
-                EEH_Autoloader::register_line_item_display_autoloaders();
404
-                $this->set_line_item_display(new EE_Line_Item_Display('spco'));
405
-                $subsections['payment_options'] = $this->_display_payment_options(
406
-                    $this->line_item_display->display_line_item(
407
-                        $filtered_line_item_tree,
408
-                        ['registrations' => $registrations]
409
-                    )
410
-                );
411
-                $this->checkout->amount_owing   = $filtered_line_item_tree->total();
412
-                $this->_apply_registration_payments_to_amount_owing($registrations);
413
-            }
414
-            $no_payment_required = false;
415
-        } else {
416
-            $this->_hide_reg_step_submit_button_if_revisit();
417
-        }
418
-        $this->_save_selected_method_of_payment();
419
-
420
-        $subsections['default_hidden_inputs'] = $this->reg_step_hidden_inputs();
421
-        $subsections['extra_hidden_inputs']   = $this->_extra_hidden_inputs($no_payment_required);
422
-
423
-        return new EE_Form_Section_Proper(
424
-            [
425
-                'name'            => $this->reg_form_name(),
426
-                'html_id'         => $this->reg_form_name(),
427
-                'subsections'     => $subsections,
428
-                'layout_strategy' => new EE_No_Layout(),
429
-            ]
430
-        );
431
-    }
432
-
433
-
434
-    /**
435
-     * add line item filters required for this reg step
436
-     * these filters are applied via this line in EE_SPCO_Reg_Step_Payment_Options::set_hooks():
437
-     *        add_filter( 'FHEE__SPCO__EE_Line_Item_Filter_Collection', array( 'EE_SPCO_Reg_Step_Payment_Options',
438
-     *        'add_spco_line_item_filters' ) ); so any code that wants to use the same set of filters during the
439
-     *        payment options reg step, can apply these filters via the following: apply_filters(
440
-     *        'FHEE__SPCO__EE_Line_Item_Filter_Collection', new EE_Line_Item_Filter_Collection() ) or to an existing
441
-     *        filter collection by passing that instead of instantiating a new collection
442
-     *
443
-     * @param EE_Line_Item_Filter_Collection $line_item_filter_collection
444
-     * @return EE_Line_Item_Filter_Collection
445
-     * @throws EE_Error
446
-     * @throws InvalidArgumentException
447
-     * @throws ReflectionException
448
-     * @throws EntityNotFoundException
449
-     * @throws InvalidDataTypeException
450
-     * @throws InvalidInterfaceException
451
-     * @throws InvalidStatusException
452
-     */
453
-    public static function add_spco_line_item_filters(EE_Line_Item_Filter_Collection $line_item_filter_collection)
454
-    {
455
-        if (! EE_Registry::instance()->SSN instanceof EE_Session) {
456
-            return $line_item_filter_collection;
457
-        }
458
-        if (! EE_Registry::instance()->SSN->checkout() instanceof EE_Checkout) {
459
-            return $line_item_filter_collection;
460
-        }
461
-        if (! EE_Registry::instance()->SSN->checkout()->transaction instanceof EE_Transaction) {
462
-            return $line_item_filter_collection;
463
-        }
464
-        $line_item_filter_collection->add(
465
-            new EE_Billable_Line_Item_Filter(
466
-                EE_SPCO_Reg_Step_Payment_Options::remove_ejected_registrations(
467
-                    EE_Registry::instance()->SSN->checkout()->transaction->registrations(
468
-                        EE_Registry::instance()->SSN->checkout()->reg_cache_where_params
469
-                    )
470
-                )
471
-            )
472
-        );
473
-        $line_item_filter_collection->add(new EE_Non_Zero_Line_Item_Filter());
474
-        return $line_item_filter_collection;
475
-    }
476
-
477
-
478
-    /**
479
-     * remove_ejected_registrations
480
-     * if a registrant has lost their potential space at an event due to lack of payment,
481
-     * then this method removes them from the list of registrations being paid for during this request
482
-     *
483
-     * @param EE_Registration[] $registrations
484
-     * @return EE_Registration[]
485
-     * @throws EE_Error
486
-     * @throws InvalidArgumentException
487
-     * @throws ReflectionException
488
-     * @throws EntityNotFoundException
489
-     * @throws InvalidDataTypeException
490
-     * @throws InvalidInterfaceException
491
-     * @throws InvalidStatusException
492
-     */
493
-    public static function remove_ejected_registrations(array $registrations)
494
-    {
495
-        $ejected_registrations = EE_SPCO_Reg_Step_Payment_Options::find_registrations_that_lost_their_space(
496
-            $registrations,
497
-            EE_Registry::instance()->SSN->checkout()->revisit
498
-        );
499
-        foreach ($registrations as $REG_ID => $registration) {
500
-            // has this registration lost it's space ?
501
-            if (isset($ejected_registrations[ $REG_ID ])) {
502
-                unset($registrations[ $REG_ID ]);
503
-            }
504
-        }
505
-        return $registrations;
506
-    }
507
-
508
-
509
-    /**
510
-     * find_registrations_that_lost_their_space
511
-     * If a registrant chooses an offline payment method like Invoice,
512
-     * then no space is reserved for them at the event until they fully pay fo that site
513
-     * (unless the event's default reg status is set to APPROVED)
514
-     * if a registrant then later returns to pay, but the number of spaces available has been reduced due to sales,
515
-     * then this method will determine which registrations have lost the ability to complete the reg process.
516
-     *
517
-     * @param EE_Registration[] $registrations
518
-     * @param bool              $revisit
519
-     * @return array
520
-     * @throws EE_Error
521
-     * @throws InvalidArgumentException
522
-     * @throws ReflectionException
523
-     * @throws EntityNotFoundException
524
-     * @throws InvalidDataTypeException
525
-     * @throws InvalidInterfaceException
526
-     * @throws InvalidStatusException
527
-     */
528
-    public static function find_registrations_that_lost_their_space(array $registrations, $revisit = false)
529
-    {
530
-        // registrations per event
531
-        $event_reg_count = [];
532
-        // spaces left per event
533
-        $event_spaces_remaining = [];
534
-        // tickets left sorted by ID
535
-        $tickets_remaining = [];
536
-        // registrations that have lost their space
537
-        $ejected_registrations = [];
538
-        foreach ($registrations as $REG_ID => $registration) {
539
-            if (
540
-                $registration->status_ID() === RegStatus::APPROVED
541
-                || apply_filters(
542
-                    'FHEE__EE_SPCO_Reg_Step_Payment_Options__find_registrations_that_lost_their_space__allow_reg_payment',
543
-                    false,
544
-                    $registration,
545
-                    $revisit
546
-                )
547
-            ) {
548
-                continue;
549
-            }
550
-            $EVT_ID = $registration->event_ID();
551
-            $ticket = $registration->ticket();
552
-            if (! isset($tickets_remaining[ $ticket->ID() ])) {
553
-                $tickets_remaining[ $ticket->ID() ] = $ticket->remaining();
554
-            }
555
-            if ($tickets_remaining[ $ticket->ID() ] > 0) {
556
-                if (! isset($event_reg_count[ $EVT_ID ])) {
557
-                    $event_reg_count[ $EVT_ID ] = 0;
558
-                }
559
-                $event_reg_count[ $EVT_ID ]++;
560
-                if (! isset($event_spaces_remaining[ $EVT_ID ])) {
561
-                    $event_spaces_remaining[ $EVT_ID ] = $registration->event()->spaces_remaining_for_sale();
562
-                }
563
-            }
564
-            if (
565
-                $revisit
566
-                && ($tickets_remaining[ $ticket->ID() ] === 0
567
-                    || $event_reg_count[ $EVT_ID ] > $event_spaces_remaining[ $EVT_ID ]
568
-                )
569
-            ) {
570
-                $ejected_registrations[ $REG_ID ] = $registration->event();
571
-                if ($registration->status_ID() !== RegStatus::WAIT_LIST) {
572
-                    /** @type EE_Registration_Processor $registration_processor */
573
-                    $registration_processor = EE_Registry::instance()->load_class('Registration_Processor');
574
-                    // at this point, we should have enough details about the registrant to consider the registration
575
-                    // NOT incomplete
576
-                    $registration_processor->manually_update_registration_status(
577
-                        $registration,
578
-                        RegStatus::WAIT_LIST
579
-                    );
580
-                }
581
-            }
582
-        }
583
-        return $ejected_registrations;
584
-    }
585
-
586
-
587
-    /**
588
-     * _hide_reg_step_submit_button
589
-     * removes the html for the reg step submit button
590
-     * by replacing it with an empty string via filter callback
591
-     *
592
-     * @return void
593
-     */
594
-    protected function _adjust_registration_status_if_event_old_sold()
595
-    {
596
-    }
597
-
598
-
599
-    /**
600
-     * _hide_reg_step_submit_button
601
-     * removes the html for the reg step submit button
602
-     * by replacing it with an empty string via filter callback
603
-     *
604
-     * @return void
605
-     */
606
-    protected function _hide_reg_step_submit_button_if_revisit()
607
-    {
608
-        if ($this->checkout->revisit) {
609
-            add_filter('FHEE__EE_SPCO_Reg_Step__reg_step_submit_button__sbmt_btn_html', '__return_empty_string');
610
-        }
611
-    }
612
-
613
-
614
-    /**
615
-     * sold_out_events
616
-     * displays notices regarding events that have sold out since hte registrant first signed up
617
-     *
618
-     * @param EE_Event[] $sold_out_events_array
619
-     * @return EE_Form_Section_Proper
620
-     * @throws EE_Error
621
-     */
622
-    private function _sold_out_events($sold_out_events_array = [])
623
-    {
624
-        // set some defaults
625
-        $this->checkout->selected_method_of_payment = 'events_sold_out';
626
-        $sold_out_events                            = '';
627
-        foreach ($sold_out_events_array as $sold_out_event) {
628
-            $sold_out_events .= EEH_HTML::li(
629
-                EEH_HTML::span(
630
-                    '  ' . $sold_out_event->name(),
631
-                    '',
632
-                    'dashicons dashicons-marker ee-icon-size-16 pink-text'
633
-                )
634
-            );
635
-        }
636
-        return new EE_Form_Section_Proper(
637
-            [
638
-                'layout_strategy' => new EE_Template_Layout(
639
-                    [
640
-                        'layout_template_file' => SPCO_REG_STEPS_PATH
641
-                                                  . $this->_slug
642
-                                                  . '/sold_out_events.template.php',
643
-                        'template_args'        => apply_filters(
644
-                            'FHEE__EE_SPCO_Reg_Step_Payment_Options___sold_out_events__template_args',
645
-                            [
646
-                                'sold_out_events'     => $sold_out_events,
647
-                                'sold_out_events_msg' => apply_filters(
648
-                                    'FHEE__EE_SPCO_Reg_Step_Payment_Options___sold_out_events__sold_out_events_msg',
649
-                                    sprintf(
650
-                                        esc_html__(
651
-                                            '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',
652
-                                            'event_espresso'
653
-                                        ),
654
-                                        '<strong>',
655
-                                        '</strong>',
656
-                                        '<br />'
657
-                                    )
658
-                                ),
659
-                            ]
660
-                        ),
661
-                    ]
662
-                ),
663
-            ]
664
-        );
665
-    }
666
-
667
-
668
-    /**
669
-     * _insufficient_spaces_available
670
-     * displays notices regarding events that do not have enough remaining spaces
671
-     * to satisfy the current number of registrations looking to pay
672
-     *
673
-     * @param EE_Event[] $insufficient_spaces_events_array
674
-     * @return EE_Form_Section_Proper
675
-     * @throws EE_Error
676
-     * @throws ReflectionException
677
-     */
678
-    private function _insufficient_spaces_available($insufficient_spaces_events_array = [])
679
-    {
680
-        // set some defaults
681
-        $this->checkout->selected_method_of_payment = 'invoice';
682
-        $insufficient_space_events                  = '';
683
-        foreach ($insufficient_spaces_events_array as $event) {
684
-            if ($event instanceof EE_Event) {
685
-                $insufficient_space_events .= EEH_HTML::li(
686
-                    EEH_HTML::span(' ' . $event->name(), '', 'dashicons dashicons-marker ee-icon-size-16 pink-text')
687
-                );
688
-            }
689
-        }
690
-        return new EE_Form_Section_Proper(
691
-            [
692
-                'subsections'     => [
693
-                    'default_hidden_inputs' => $this->reg_step_hidden_inputs(),
694
-                    'extra_hidden_inputs'   => $this->_extra_hidden_inputs(),
695
-                ],
696
-                'layout_strategy' => new EE_Template_Layout(
697
-                    [
698
-                        'layout_template_file' => SPCO_REG_STEPS_PATH
699
-                                                  . $this->_slug
700
-                                                  . '/sold_out_events.template.php',
701
-                        'template_args'        => apply_filters(
702
-                            'FHEE__EE_SPCO_Reg_Step_Payment_Options___insufficient_spaces_available__template_args',
703
-                            [
704
-                                'sold_out_events'     => $insufficient_space_events,
705
-                                'sold_out_events_msg' => apply_filters(
706
-                                    'FHEE__EE_SPCO_Reg_Step_Payment_Options___insufficient_spaces_available__insufficient_space_msg',
707
-                                    esc_html__(
708
-                                        '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.',
709
-                                        'event_espresso'
710
-                                    )
711
-                                ),
712
-                            ]
713
-                        ),
714
-                    ]
715
-                ),
716
-            ]
717
-        );
718
-    }
719
-
720
-
721
-    /**
722
-     * registrations_requiring_pre_approval
723
-     *
724
-     * @param array $registrations_requiring_pre_approval
725
-     * @return EE_Form_Section_Proper
726
-     * @throws EE_Error
727
-     * @throws EntityNotFoundException
728
-     * @throws ReflectionException
729
-     */
730
-    private function _registrations_requiring_pre_approval($registrations_requiring_pre_approval = [])
731
-    {
732
-        $events_requiring_pre_approval = [];
733
-        foreach ($registrations_requiring_pre_approval as $registration) {
734
-            if ($registration instanceof EE_Registration && $registration->event() instanceof EE_Event) {
735
-                $events_requiring_pre_approval[ $registration->event()->ID() ] = EEH_HTML::li(
736
-                    EEH_HTML::span(
737
-                        '',
738
-                        '',
739
-                        'dashicons dashicons-marker ee-icon-size-16 orange-text'
740
-                    )
741
-                    . EEH_HTML::span($registration->event()->name(), '', 'orange-text')
742
-                );
743
-            }
744
-        }
745
-        return new EE_Form_Section_Proper(
746
-            [
747
-                'layout_strategy' => new EE_Template_Layout(
748
-                    [
749
-                        'layout_template_file' => SPCO_REG_STEPS_PATH
750
-                                                  . $this->_slug
751
-                                                  . '/events_requiring_pre_approval.template.php', // layout_template
752
-                        'template_args'        => apply_filters(
753
-                            'FHEE__EE_SPCO_Reg_Step_Payment_Options___sold_out_events__template_args',
754
-                            [
755
-                                'events_requiring_pre_approval'     => implode('', $events_requiring_pre_approval),
756
-                                'events_requiring_pre_approval_msg' => apply_filters(
757
-                                    'FHEE__EE_SPCO_Reg_Step_Payment_Options___events_requiring_pre_approval__events_requiring_pre_approval_msg',
758
-                                    esc_html__(
759
-                                        '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.',
760
-                                        'event_espresso'
761
-                                    )
762
-                                ),
763
-                            ]
764
-                        ),
765
-                    ]
766
-                ),
767
-            ]
768
-        );
769
-    }
770
-
771
-
772
-    /**
773
-     * _no_payment_required
774
-     *
775
-     * @param EE_Event[] $registrations_for_free_events
776
-     * @return EE_Form_Section_Proper
777
-     * @throws EE_Error
778
-     */
779
-    private function _no_payment_required($registrations_for_free_events = [])
780
-    {
781
-        // set some defaults
782
-        $this->checkout->selected_method_of_payment = 'no_payment_required';
783
-        // generate no_payment_required form
784
-        return new EE_Form_Section_Proper(
785
-            [
786
-                'layout_strategy' => new EE_Template_Layout(
787
-                    [
788
-                        'layout_template_file' => SPCO_REG_STEPS_PATH
789
-                                                  . $this->_slug
790
-                                                  . '/no_payment_required.template.php', // layout_template
791
-                        'template_args'        => apply_filters(
792
-                            'FHEE__EE_SPCO_Reg_Step_Payment_Options___no_payment_required__template_args',
793
-                            [
794
-                                'revisit'                       => $this->checkout->revisit,
795
-                                'registrations'                 => [],
796
-                                'ticket_count'                  => [],
797
-                                'registrations_for_free_events' => $registrations_for_free_events,
798
-                                'no_payment_required_msg'       => EEH_HTML::p(
799
-                                    esc_html__('This is a free event, so no billing will occur.', 'event_espresso')
800
-                                ),
801
-                            ]
802
-                        ),
803
-                    ]
804
-                ),
805
-            ]
806
-        );
807
-    }
808
-
809
-
810
-    /**
811
-     * _display_payment_options
812
-     *
813
-     * @param string $transaction_details
814
-     * @return EE_Form_Section_Proper
815
-     * @throws EE_Error
816
-     * @throws InvalidArgumentException
817
-     * @throws InvalidDataTypeException
818
-     * @throws InvalidInterfaceException
819
-     */
820
-    private function _display_payment_options($transaction_details = '')
821
-    {
822
-        // has method_of_payment been set by no-js user?
823
-        $this->checkout->selected_method_of_payment = $this->_get_selected_method_of_payment();
824
-        // build payment options form
825
-        return apply_filters(
826
-            'FHEE__EE_SPCO_Reg_Step_Payment_Options___display_payment_options__payment_options_form',
827
-            new EE_Form_Section_Proper(
828
-                [
829
-                    'subsections'     => [
830
-                        'before_payment_options' => apply_filters(
831
-                            'FHEE__EE_SPCO_Reg_Step_Payment_Options___display_payment_options__before_payment_options',
832
-                            new EE_Form_Section_Proper(
833
-                                ['layout_strategy' => new EE_Div_Per_Section_Layout()]
834
-                            )
835
-                        ),
836
-                        'payment_options'        => $this->_setup_payment_options(),
837
-                        'after_payment_options'  => apply_filters(
838
-                            'FHEE__EE_SPCO_Reg_Step_Payment_Options___display_payment_options__after_payment_options',
839
-                            new EE_Form_Section_Proper(
840
-                                ['layout_strategy' => new EE_Div_Per_Section_Layout()]
841
-                            )
842
-                        ),
843
-                    ],
844
-                    'layout_strategy' => new EE_Template_Layout(
845
-                        [
846
-                            'layout_template_file' => $this->_template,
847
-                            'template_args'        => apply_filters(
848
-                                'FHEE__EE_SPCO_Reg_Step_Payment_Options___display_payment_options__template_args',
849
-                                [
850
-                                    'reg_count'                 => $this->line_item_display->total_items(),
851
-                                    'transaction_details'       => $transaction_details,
852
-                                    'available_payment_methods' => [],
853
-                                ]
854
-                            ),
855
-                        ]
856
-                    ),
857
-                ]
858
-            )
859
-        );
860
-    }
861
-
862
-
863
-    /**
864
-     * _extra_hidden_inputs
865
-     *
866
-     * @param bool $no_payment_required
867
-     * @return EE_Form_Section_Proper
868
-     * @throws EE_Error
869
-     * @throws ReflectionException
870
-     */
871
-    private function _extra_hidden_inputs($no_payment_required = true)
872
-    {
873
-        return new EE_Form_Section_Proper(
874
-            [
875
-                'html_id'         => 'ee-' . $this->slug() . '-extra-hidden-inputs',
876
-                'layout_strategy' => new EE_Div_Per_Section_Layout(),
877
-                'subsections'     => [
878
-                    'spco_no_payment_required' => new EE_Hidden_Input(
879
-                        [
880
-                            'normalization_strategy' => new EE_Boolean_Normalization(),
881
-                            'html_name'              => 'spco_no_payment_required',
882
-                            'html_id'                => 'spco-no-payment-required-payment_options',
883
-                            'default'                => $no_payment_required,
884
-                        ]
885
-                    ),
886
-                    'spco_transaction_id'      => new EE_Fixed_Hidden_Input(
887
-                        [
888
-                            'normalization_strategy' => new EE_Int_Normalization(),
889
-                            'html_name'              => 'spco_transaction_id',
890
-                            'html_id'                => 'spco-transaction-id',
891
-                            'default'                => $this->checkout->transaction->ID(),
892
-                        ]
893
-                    ),
894
-                ],
895
-            ]
896
-        );
897
-    }
898
-
899
-
900
-    /**
901
-     *    _apply_registration_payments_to_amount_owing
902
-     *
903
-     * @param array $registrations
904
-     * @throws EE_Error
905
-     */
906
-    protected function _apply_registration_payments_to_amount_owing(array $registrations)
907
-    {
908
-        $payments = [];
909
-        foreach ($registrations as $registration) {
910
-            if ($registration instanceof EE_Registration && $registration->owes_monies_and_can_pay()) {
911
-                $payments += $registration->registration_payments();
912
-            }
913
-        }
914
-        if (! empty($payments)) {
915
-            foreach ($payments as $payment) {
916
-                if ($payment instanceof EE_Registration_Payment) {
917
-                    $this->checkout->amount_owing -= $payment->amount();
918
-                }
919
-            }
920
-        }
921
-    }
922
-
923
-
924
-    /**
925
-     *    _reset_selected_method_of_payment
926
-     *
927
-     * @access    private
928
-     * @param bool $force_reset
929
-     * @return void
930
-     * @throws InvalidArgumentException
931
-     * @throws InvalidDataTypeException
932
-     * @throws InvalidInterfaceException
933
-     */
934
-    private function _reset_selected_method_of_payment($force_reset = false)
935
-    {
936
-        /** @var RequestInterface $request */
937
-        $request              = LoaderFactory::getLoader()->getShared(RequestInterface::class);
938
-        $reset_payment_method = $request->getRequestParam('reset_payment_method', $force_reset, 'bool');
939
-        if ($reset_payment_method) {
940
-            $this->checkout->selected_method_of_payment = null;
941
-            $this->checkout->payment_method             = null;
942
-            $this->checkout->billing_form               = null;
943
-            $this->_save_selected_method_of_payment();
944
-        }
945
-    }
946
-
947
-
948
-    /**
949
-     * _save_selected_method_of_payment
950
-     * stores the selected_method_of_payment in the session
951
-     * so that it's available for all subsequent requests including AJAX
952
-     *
953
-     * @access        private
954
-     * @param string $selected_method_of_payment
955
-     * @return void
956
-     * @throws InvalidArgumentException
957
-     * @throws InvalidDataTypeException
958
-     * @throws InvalidInterfaceException
959
-     */
960
-    private function _save_selected_method_of_payment($selected_method_of_payment = '')
961
-    {
962
-        $selected_method_of_payment = ! empty($selected_method_of_payment)
963
-            ? $selected_method_of_payment
964
-            : $this->checkout->selected_method_of_payment;
965
-        EE_Registry::instance()->SSN->set_session_data(
966
-            ['selected_method_of_payment' => $selected_method_of_payment]
967
-        );
968
-    }
969
-
970
-
971
-    /**
972
-     * _setup_payment_options
973
-     *
974
-     * @return EE_Form_Section_Proper
975
-     * @throws EE_Error
976
-     * @throws InvalidArgumentException
977
-     * @throws InvalidDataTypeException
978
-     * @throws InvalidInterfaceException
979
-     */
980
-    public function _setup_payment_options()
981
-    {
982
-        // load payment method classes
983
-        $this->checkout->available_payment_methods = $this->_get_available_payment_methods();
984
-        if (empty($this->checkout->available_payment_methods)) {
985
-            EE_Error::add_error(
986
-                apply_filters(
987
-                    'FHEE__EE_SPCO_Reg_Step_Payment_Options___setup_payment_options__error_message_no_payment_methods',
988
-                    sprintf(
989
-                        esc_html__(
990
-                            '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.',
991
-                            'event_espresso'
992
-                        ),
993
-                        '<br>',
994
-                        EE_Registry::instance()->CFG->organization->get_pretty('email')
995
-                    )
996
-                ),
997
-                __FILE__,
998
-                __FUNCTION__,
999
-                __LINE__
1000
-            );
1001
-        }
1002
-        // switch up header depending on number of available payment methods
1003
-        $payment_method_header     = count($this->checkout->available_payment_methods) > 1
1004
-            ? apply_filters(
1005
-                'FHEE__registration_page_payment_options__method_of_payment_hdr',
1006
-                esc_html__('Please Select Your Method of Payment', 'event_espresso')
1007
-            )
1008
-            : apply_filters(
1009
-                'FHEE__registration_page_payment_options__method_of_payment_hdr',
1010
-                esc_html__('Method of Payment', 'event_espresso')
1011
-            );
1012
-        $available_payment_methods = [
1013
-            // display the "Payment Method" header
1014
-            'payment_method_header' => new EE_Form_Section_HTML(
1015
-                apply_filters(
1016
-                    'FHEE__EE_SPCO_Reg_Step_Payment_Options___setup_payment_options__payment_method_header',
1017
-                    EEH_HTML::h4($payment_method_header, 'method-of-payment-hdr'),
1018
-                    $payment_method_header
1019
-                )
1020
-            ),
1021
-        ];
1022
-        // the list of actual payment methods ( invoice, paypal, etc ) in a  ( slug => HTML )  format
1023
-        $available_payment_method_options = [];
1024
-        $default_payment_method_option    = [];
1025
-        // additional instructions to be displayed and hidden below payment methods (adding a clearing div to start)
1026
-        $payment_methods_billing_info = [
1027
-            new EE_Form_Section_HTML(
1028
-                EEH_HTML::div('<br />', '', '', 'clear:both;')
1029
-            ),
1030
-        ];
1031
-        // loop through payment methods
1032
-        foreach ($this->checkout->available_payment_methods as $payment_method) {
1033
-            if ($payment_method instanceof EE_Payment_Method) {
1034
-                $payment_method_button = EEH_HTML::img(
1035
-                    $payment_method->button_url(),
1036
-                    $payment_method->name(),
1037
-                    'spco-payment-method-' . $payment_method->slug() . '-btn-img',
1038
-                    'spco-payment-method-btn-img'
1039
-                );
1040
-                // check if any payment methods are set as default
1041
-                // if payment method is already selected OR nothing is selected and this payment method should be
1042
-                // open_by_default
1043
-                if (
1044
-                    ($this->checkout->selected_method_of_payment === $payment_method->slug())
1045
-                    || (! $this->checkout->selected_method_of_payment && $payment_method->open_by_default())
1046
-                ) {
1047
-                    $this->checkout->selected_method_of_payment = $payment_method->slug();
1048
-                    $this->_save_selected_method_of_payment();
1049
-                    $default_payment_method_option[ $payment_method->slug() ] = $payment_method_button;
1050
-                } else {
1051
-                    $available_payment_method_options[ $payment_method->slug() ] = $payment_method_button;
1052
-                }
1053
-                $payment_methods_billing_info[ $payment_method->slug() . '-info' ] =
1054
-                    $this->_payment_method_billing_info(
1055
-                        $payment_method
1056
-                    );
1057
-            }
1058
-        }
1059
-        // prepend available_payment_method_options with default_payment_method_option so that it appears first in list
1060
-        // of PMs
1061
-        $available_payment_method_options = $default_payment_method_option + $available_payment_method_options;
1062
-        // now generate the actual form  inputs
1063
-        $available_payment_methods['available_payment_methods'] = $this->_available_payment_method_inputs(
1064
-            $available_payment_method_options
1065
-        );
1066
-        $available_payment_methods                              += $payment_methods_billing_info;
1067
-        // build the available payment methods form
1068
-        return new EE_Form_Section_Proper(
1069
-            [
1070
-                'html_id'         => 'spco-available-methods-of-payment-dv',
1071
-                'subsections'     => $available_payment_methods,
1072
-                'layout_strategy' => new EE_Div_Per_Section_Layout(),
1073
-            ]
1074
-        );
1075
-    }
1076
-
1077
-
1078
-    /**
1079
-     * _get_available_payment_methods
1080
-     *
1081
-     * @return EE_Payment_Method[]
1082
-     * @throws EE_Error
1083
-     * @throws InvalidArgumentException
1084
-     * @throws InvalidDataTypeException
1085
-     * @throws InvalidInterfaceException
1086
-     */
1087
-    protected function _get_available_payment_methods()
1088
-    {
1089
-        if (! empty($this->checkout->available_payment_methods)) {
1090
-            return $this->checkout->available_payment_methods;
1091
-        }
1092
-        $available_payment_methods = [];
1093
-        $EEM_Payment_Method        = EEM_Payment_Method::instance();
1094
-        // get all active payment methods
1095
-        $payment_methods = $EEM_Payment_Method->get_all_for_transaction(
1096
-            $this->checkout->transaction,
1097
-            EEM_Payment_Method::scope_cart
1098
-        );
1099
-        foreach ($payment_methods as $payment_method) {
1100
-            if ($payment_method instanceof EE_Payment_Method) {
1101
-                $available_payment_methods[ $payment_method->slug() ] = $payment_method;
1102
-            }
1103
-        }
1104
-        return $available_payment_methods;
1105
-    }
1106
-
1107
-
1108
-    /**
1109
-     *    _available_payment_method_inputs
1110
-     *
1111
-     * @access    private
1112
-     * @param array $available_payment_method_options
1113
-     * @return    EE_Form_Section_Proper
1114
-     * @throws EE_Error
1115
-     * @throws EE_Error
1116
-     */
1117
-    private function _available_payment_method_inputs($available_payment_method_options = [])
1118
-    {
1119
-        // generate inputs
1120
-        return new EE_Form_Section_Proper(
1121
-            [
1122
-                'html_id'         => 'ee-available-payment-method-inputs',
1123
-                'layout_strategy' => new EE_Div_Per_Section_Layout(),
1124
-                'subsections'     => [
1125
-                    '' => new EE_Radio_Button_Input(
1126
-                        $available_payment_method_options,
1127
-                        [
1128
-                            'html_name'          => 'selected_method_of_payment',
1129
-                            'html_class'         => 'spco-payment-method',
1130
-                            'default'            => $this->checkout->selected_method_of_payment,
1131
-                            'label_size'         => 11,
1132
-                            'enforce_label_size' => true,
1133
-                        ]
1134
-                    ),
1135
-                ],
1136
-            ]
1137
-        );
1138
-    }
1139
-
1140
-
1141
-    /**
1142
-     *    _payment_method_billing_info
1143
-     *
1144
-     * @access    private
1145
-     * @param EE_Payment_Method $payment_method
1146
-     * @return EE_Form_Section_Proper
1147
-     * @throws EE_Error
1148
-     * @throws InvalidArgumentException
1149
-     * @throws InvalidDataTypeException
1150
-     * @throws InvalidInterfaceException
1151
-     */
1152
-    private function _payment_method_billing_info(EE_Payment_Method $payment_method)
1153
-    {
1154
-        $currently_selected = $this->checkout->selected_method_of_payment === $payment_method->slug();
1155
-        // generate the billing form for payment method
1156
-        $billing_form                 = $currently_selected
1157
-            ? $this->_get_billing_form_for_payment_method($payment_method)
1158
-            : new EE_Form_Section_HTML();
1159
-        $this->checkout->billing_form = $currently_selected
1160
-            ? $billing_form
1161
-            : $this->checkout->billing_form;
1162
-        // it's all in the details
1163
-        $info_html = EEH_HTML::h3(
1164
-            esc_html__('Important information regarding your payment', 'event_espresso'),
1165
-            '',
1166
-            'spco-payment-method-hdr'
1167
-        );
1168
-        // add some info regarding the step, either from what's saved in the admin,
1169
-        // or a default string depending on whether the PM has a billing form or not
1170
-        if ($payment_method->description()) {
1171
-            $payment_method_info = $payment_method->description();
1172
-        } elseif ($billing_form instanceof EE_Billing_Info_Form) {
1173
-            $payment_method_info = sprintf(
1174
-                esc_html__(
1175
-                    'Please provide the following billing information, then click the "%1$s" button below in order to proceed.',
1176
-                    'event_espresso'
1177
-                ),
1178
-                $this->submit_button_text()
1179
-            );
1180
-        } else {
1181
-            $payment_method_info = sprintf(
1182
-                esc_html__('Please click the "%1$s" button below in order to proceed.', 'event_espresso'),
1183
-                $this->submit_button_text()
1184
-            );
1185
-        }
1186
-        $info_html .= EEH_HTML::div(
1187
-            apply_filters(
1188
-                'FHEE__EE_SPCO_Reg_Step_Payment_Options___payment_method_billing_info__payment_method_info',
1189
-                $payment_method_info
1190
-            ),
1191
-            '',
1192
-            'spco-payment-method-desc ee-attention'
1193
-        );
1194
-        return new EE_Form_Section_Proper(
1195
-            [
1196
-                'html_id'         => 'spco-payment-method-info-' . $payment_method->slug(),
1197
-                'html_class'      => 'spco-payment-method-info-dv',
1198
-                // only display the selected or default PM
1199
-                'html_style'      => $currently_selected ? '' : 'display:none;',
1200
-                'layout_strategy' => new EE_Div_Per_Section_Layout(),
1201
-                'subsections'     => [
1202
-                    'info'         => new EE_Form_Section_HTML($info_html),
1203
-                    'billing_form' => $currently_selected ? $billing_form : new EE_Form_Section_HTML(),
1204
-                ],
1205
-            ]
1206
-        );
1207
-    }
1208
-
1209
-
1210
-    /**
1211
-     * get_billing_form_html_for_payment_method
1212
-     *
1213
-     * @return bool
1214
-     * @throws EE_Error
1215
-     * @throws InvalidArgumentException
1216
-     * @throws ReflectionException
1217
-     * @throws InvalidDataTypeException
1218
-     * @throws InvalidInterfaceException
1219
-     */
1220
-    public function get_billing_form_html_for_payment_method()
1221
-    {
1222
-        // how have they chosen to pay?
1223
-        $this->checkout->selected_method_of_payment = $this->_get_selected_method_of_payment(true);
1224
-        $this->checkout->payment_method             = $this->_get_payment_method_for_selected_method_of_payment();
1225
-        if (! $this->checkout->payment_method instanceof EE_Payment_Method) {
1226
-            return false;
1227
-        }
1228
-        if (
1229
-            apply_filters(
1230
-                'FHEE__EE_SPCO_Reg_Step_Payment_Options__registration_checkout__selected_payment_method__display_success',
1231
-                false
1232
-            )
1233
-        ) {
1234
-            EE_Error::add_success(
1235
-                apply_filters(
1236
-                    'FHEE__Single_Page_Checkout__registration_checkout__selected_payment_method',
1237
-                    sprintf(
1238
-                        esc_html__(
1239
-                            'You have selected "%s" as your method of payment. Please note the important payment information below.',
1240
-                            'event_espresso'
1241
-                        ),
1242
-                        $this->checkout->payment_method->name()
1243
-                    )
1244
-                )
1245
-            );
1246
-        }
1247
-        // now generate billing form for selected method of payment
1248
-        $payment_method_billing_form = $this->_get_billing_form_for_payment_method($this->checkout->payment_method);
1249
-        // fill form with attendee info if applicable
1250
-        if (
1251
-            $payment_method_billing_form instanceof EE_Billing_Attendee_Info_Form
1252
-            && $this->checkout->transaction_has_primary_registrant()
1253
-        ) {
1254
-            $payment_method_billing_form->populate_from_attendee(
1255
-                $this->checkout->transaction->primary_registration()->attendee()
1256
-            );
1257
-        }
1258
-        // and debug content
1259
-        if (
1260
-            $payment_method_billing_form instanceof EE_Billing_Info_Form
1261
-            && $this->checkout->payment_method->type_obj() instanceof EE_PMT_Base
1262
-        ) {
1263
-            $payment_method_billing_form =
1264
-                $this->checkout->payment_method->type_obj()->apply_billing_form_debug_settings(
1265
-                    $payment_method_billing_form
1266
-                );
1267
-        }
1268
-        $billing_info = $payment_method_billing_form instanceof EE_Form_Section_Proper
1269
-            ? $payment_method_billing_form->get_html()
1270
-            : '';
1271
-        $this->checkout->json_response->set_return_data(['payment_method_info' => $billing_info]);
1272
-        // localize validation rules for main form
1273
-        $this->checkout->current_step->reg_form->localize_validation_rules();
1274
-        $this->checkout->json_response->add_validation_rules(EE_Form_Section_Proper::js_localization());
1275
-        return true;
1276
-    }
1277
-
1278
-
1279
-    /**
1280
-     * _get_billing_form_for_payment_method
1281
-     *
1282
-     * @param EE_Payment_Method $payment_method
1283
-     * @return EE_Billing_Info_Form|EE_Billing_Attendee_Info_Form|EE_Form_Section_HTML
1284
-     * @throws EE_Error
1285
-     * @throws InvalidArgumentException
1286
-     * @throws InvalidDataTypeException
1287
-     * @throws InvalidInterfaceException
1288
-     */
1289
-    private function _get_billing_form_for_payment_method(EE_Payment_Method $payment_method)
1290
-    {
1291
-        $billing_form = $payment_method->type_obj()->billing_form(
1292
-            $this->checkout->transaction,
1293
-            ['amount_owing' => $this->checkout->amount_owing]
1294
-        );
1295
-        if ($billing_form instanceof EE_Billing_Info_Form) {
1296
-            if (
1297
-                apply_filters(
1298
-                    'FHEE__EE_SPCO_Reg_Step_Payment_Options__registration_checkout__selected_payment_method__display_success',
1299
-                    false
1300
-                )
1301
-                && $this->request->requestParamIsSet('payment_method')
1302
-            ) {
1303
-                EE_Error::add_success(
1304
-                    apply_filters(
1305
-                        'FHEE__Single_Page_Checkout__registration_checkout__selected_payment_method',
1306
-                        sprintf(
1307
-                            esc_html__(
1308
-                                'You have selected "%s" as your method of payment. Please note the important payment information below.',
1309
-                                'event_espresso'
1310
-                            ),
1311
-                            $payment_method->name()
1312
-                        )
1313
-                    )
1314
-                );
1315
-            }
1316
-            return apply_filters(
1317
-                'FHEE__EE_SPCO_Reg_Step_Payment_Options___get_billing_form_for_payment_method__billing_form',
1318
-                $billing_form,
1319
-                $payment_method
1320
-            );
1321
-        }
1322
-        // no actual billing form, so return empty HTML form section
1323
-        return new EE_Form_Section_HTML();
1324
-    }
1325
-
1326
-
1327
-    /**
1328
-     * _get_selected_method_of_payment
1329
-     *
1330
-     * @param boolean $required whether to throw an error if the "selected_method_of_payment"
1331
-     *                          is not found in the incoming request
1332
-     * @param string  $request_param
1333
-     * @return NULL|string
1334
-     * @throws EE_Error
1335
-     * @throws InvalidArgumentException
1336
-     * @throws InvalidDataTypeException
1337
-     * @throws InvalidInterfaceException
1338
-     */
1339
-    private function _get_selected_method_of_payment(
1340
-        $required = false,
1341
-        $request_param = 'selected_method_of_payment'
1342
-    ) {
1343
-        // is selected_method_of_payment set in the request ?
1344
-        $selected_method_of_payment = $this->request->getRequestParam($request_param);
1345
-        if ($selected_method_of_payment) {
1346
-            // sanitize it
1347
-            $selected_method_of_payment = is_array($selected_method_of_payment)
1348
-                ? array_shift($selected_method_of_payment)
1349
-                : $selected_method_of_payment;
1350
-            $selected_method_of_payment = sanitize_text_field($selected_method_of_payment);
1351
-            // store it in the session so that it's available for all subsequent requests including AJAX
1352
-            $this->_save_selected_method_of_payment($selected_method_of_payment);
1353
-        } else {
1354
-            // or is is set in the session ?
1355
-            $selected_method_of_payment = EE_Registry::instance()->SSN->get_session_data(
1356
-                'selected_method_of_payment'
1357
-            );
1358
-        }
1359
-        // do ya really really gotta have it?
1360
-        if (empty($selected_method_of_payment) && $required) {
1361
-            EE_Error::add_error(
1362
-                sprintf(
1363
-                    esc_html__(
1364
-                        '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.',
1365
-                        'event_espresso'
1366
-                    ),
1367
-                    '<br/>',
1368
-                    '<br/>',
1369
-                    EE_Registry::instance()->CFG->organization->get_pretty('email')
1370
-                ),
1371
-                __FILE__,
1372
-                __FUNCTION__,
1373
-                __LINE__
1374
-            );
1375
-            return null;
1376
-        }
1377
-        return $selected_method_of_payment;
1378
-    }
1379
-
1380
-
1381
-
1382
-
1383
-
1384
-
1385
-    /********************************************************************************************************/
1386
-    /***********************************  SWITCH PAYMENT METHOD  ************************************/
1387
-    /********************************************************************************************************/
1388
-    /**
1389
-     * switch_payment_method
1390
-     *
1391
-     * @return bool
1392
-     * @throws EE_Error
1393
-     * @throws InvalidArgumentException
1394
-     * @throws InvalidDataTypeException
1395
-     * @throws InvalidInterfaceException
1396
-     * @throws ReflectionException
1397
-     */
1398
-    public function switch_payment_method()
1399
-    {
1400
-        if (! $this->_verify_payment_method_is_set()) {
1401
-            return false;
1402
-        }
1403
-        if (
1404
-            apply_filters(
1405
-                'FHEE__EE_SPCO_Reg_Step_Payment_Options__registration_checkout__selected_payment_method__display_success',
1406
-                false
1407
-            )
1408
-        ) {
1409
-            EE_Error::add_success(
1410
-                apply_filters(
1411
-                    'FHEE__Single_Page_Checkout__registration_checkout__selected_payment_method',
1412
-                    sprintf(
1413
-                        esc_html__(
1414
-                            'You have selected "%s" as your method of payment. Please note the important payment information below.',
1415
-                            'event_espresso'
1416
-                        ),
1417
-                        $this->checkout->payment_method->name()
1418
-                    )
1419
-                )
1420
-            );
1421
-        }
1422
-        // generate billing form for selected method of payment if it hasn't been done already
1423
-        if ($this->checkout->payment_method->type_obj()->has_billing_form()) {
1424
-            $this->checkout->billing_form = $this->_get_billing_form_for_payment_method(
1425
-                $this->checkout->payment_method
1426
-            );
1427
-        }
1428
-        // fill form with attendee info if applicable
1429
-        if (
1430
-            apply_filters(
1431
-                'FHEE__populate_billing_form_fields_from_attendee',
1432
-                (
1433
-                    $this->checkout->billing_form instanceof EE_Billing_Attendee_Info_Form
1434
-                    && $this->checkout->transaction_has_primary_registrant()
1435
-                ),
1436
-                $this->checkout->billing_form,
1437
-                $this->checkout->transaction
1438
-            )
1439
-        ) {
1440
-            $this->checkout->billing_form->populate_from_attendee(
1441
-                $this->checkout->transaction->primary_registration()->attendee()
1442
-            );
1443
-        }
1444
-        // and debug content
1445
-        if (
1446
-            $this->checkout->billing_form instanceof EE_Billing_Info_Form
1447
-            && $this->checkout->payment_method->type_obj() instanceof EE_PMT_Base
1448
-        ) {
1449
-            $this->checkout->billing_form =
1450
-                $this->checkout->payment_method->type_obj()->apply_billing_form_debug_settings(
1451
-                    $this->checkout->billing_form
1452
-                );
1453
-        }
1454
-        // get html and validation rules for form
1455
-        if ($this->checkout->billing_form instanceof EE_Form_Section_Proper) {
1456
-            $this->checkout->json_response->set_return_data(
1457
-                ['payment_method_info' => $this->checkout->billing_form->get_html()]
1458
-            );
1459
-            // localize validation rules for main form
1460
-            $this->checkout->billing_form->localize_validation_rules(true);
1461
-            $this->checkout->json_response->add_validation_rules(EE_Form_Section_Proper::js_localization());
1462
-        } else {
1463
-            $this->checkout->json_response->set_return_data(['payment_method_info' => '']);
1464
-        }
1465
-        // prevents advancement to next step
1466
-        $this->checkout->continue_reg = false;
1467
-        return true;
1468
-    }
1469
-
1470
-
1471
-    /**
1472
-     * _verify_payment_method_is_set
1473
-     *
1474
-     * @return bool
1475
-     * @throws EE_Error
1476
-     * @throws InvalidArgumentException
1477
-     * @throws ReflectionException
1478
-     * @throws InvalidDataTypeException
1479
-     * @throws InvalidInterfaceException
1480
-     */
1481
-    protected function _verify_payment_method_is_set()
1482
-    {
1483
-        // generate billing form for selected method of payment if it hasn't been done already
1484
-        if (empty($this->checkout->selected_method_of_payment)) {
1485
-            // how have they chosen to pay?
1486
-            $this->checkout->selected_method_of_payment = $this->_get_selected_method_of_payment(true);
1487
-        } else {
1488
-            // choose your own adventure based on method_of_payment
1489
-            switch ($this->checkout->selected_method_of_payment) {
1490
-                case 'events_sold_out':
1491
-                    EE_Error::add_attention(
1492
-                        apply_filters(
1493
-                            'FHEE__EE_SPCO_Reg_Step_Payment_Options___verify_payment_method_is_set__sold_out_events_msg',
1494
-                            esc_html__(
1495
-                                '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.',
1496
-                                'event_espresso'
1497
-                            )
1498
-                        ),
1499
-                        __FILE__,
1500
-                        __FUNCTION__,
1501
-                        __LINE__
1502
-                    );
1503
-                    return false;
1504
-                case 'payments_closed':
1505
-                    EE_Error::add_attention(
1506
-                        apply_filters(
1507
-                            'FHEE__EE_SPCO_Reg_Step_Payment_Options___verify_payment_method_is_set__payments_closed_msg',
1508
-                            esc_html__(
1509
-                                '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.',
1510
-                                'event_espresso'
1511
-                            )
1512
-                        ),
1513
-                        __FILE__,
1514
-                        __FUNCTION__,
1515
-                        __LINE__
1516
-                    );
1517
-                    return false;
1518
-                case 'no_payment_required':
1519
-                    EE_Error::add_attention(
1520
-                        apply_filters(
1521
-                            'FHEE__EE_SPCO_Reg_Step_Payment_Options___verify_payment_method_is_set__no_payment_required_msg',
1522
-                            esc_html__(
1523
-                                '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.',
1524
-                                'event_espresso'
1525
-                            )
1526
-                        ),
1527
-                        __FILE__,
1528
-                        __FUNCTION__,
1529
-                        __LINE__
1530
-                    );
1531
-                    return false;
1532
-                default:
1533
-            }
1534
-        }
1535
-        // verify payment method
1536
-        if (! $this->checkout->payment_method instanceof EE_Payment_Method) {
1537
-            // get payment method for selected method of payment
1538
-            $this->checkout->payment_method = $this->_get_payment_method_for_selected_method_of_payment();
1539
-        }
1540
-        return $this->checkout->payment_method instanceof EE_Payment_Method;
1541
-    }
1542
-
1543
-
1544
-
1545
-    /********************************************************************************************************/
1546
-    /***************************************  SAVE PAYER DETAILS  ****************************************/
1547
-    /********************************************************************************************************/
1548
-    /**
1549
-     * save_payer_details_via_ajax
1550
-     *
1551
-     * @return void
1552
-     * @throws EE_Error
1553
-     * @throws InvalidArgumentException
1554
-     * @throws ReflectionException
1555
-     * @throws RuntimeException
1556
-     * @throws InvalidDataTypeException
1557
-     * @throws InvalidInterfaceException
1558
-     */
1559
-    public function save_payer_details_via_ajax()
1560
-    {
1561
-        if (! $this->_verify_payment_method_is_set()) {
1562
-            return;
1563
-        }
1564
-        // generate billing form for selected method of payment if it hasn't been done already
1565
-        if ($this->checkout->payment_method->type_obj()->has_billing_form()) {
1566
-            $this->checkout->billing_form = $this->_get_billing_form_for_payment_method(
1567
-                $this->checkout->payment_method
1568
-            );
1569
-        }
1570
-        // generate primary attendee from payer info if applicable
1571
-        if (! $this->checkout->transaction_has_primary_registrant()) {
1572
-            $attendee = $this->_create_attendee_from_request_data();
1573
-            if ($attendee instanceof EE_Attendee) {
1574
-                foreach ($this->checkout->transaction->registrations() as $registration) {
1575
-                    if ($registration->is_primary_registrant()) {
1576
-                        $this->checkout->primary_attendee_obj = $attendee;
1577
-                        $registration->_add_relation_to($attendee, 'Attendee');
1578
-                        $registration->set_attendee_id($attendee->ID());
1579
-                        $registration->update_cache_after_object_save('Attendee', $attendee);
1580
-                    }
1581
-                }
1582
-            }
1583
-        }
1584
-    }
1585
-
1586
-
1587
-    /**
1588
-     * create_attendee_from_request_data
1589
-     * uses info from alternate GET or POST data (such as AJAX) to create a new attendee
1590
-     *
1591
-     * @return EE_Attendee
1592
-     * @throws EE_Error
1593
-     * @throws InvalidArgumentException
1594
-     * @throws ReflectionException
1595
-     * @throws InvalidDataTypeException
1596
-     * @throws InvalidInterfaceException
1597
-     */
1598
-    protected function _create_attendee_from_request_data()
1599
-    {
1600
-        // get State ID
1601
-        $STA_ID = $this->request->getRequestParam('state');
1602
-        if (! empty($STA_ID)) {
1603
-            // can we get state object from name ?
1604
-            EE_Registry::instance()->load_model('State');
1605
-            $state  = EEM_State::instance()->get_col([['STA_name' => $STA_ID], 'limit' => 1], 'STA_ID');
1606
-            $STA_ID = is_array($state) && ! empty($state) ? reset($state) : $STA_ID;
1607
-        }
1608
-        // get Country ISO
1609
-        $CNT_ISO = $this->request->getRequestParam('country');
1610
-        if (! empty($CNT_ISO)) {
1611
-            // can we get country object from name ?
1612
-            EE_Registry::instance()->load_model('Country');
1613
-            $country = EEM_Country::instance()->get_col(
1614
-                [['CNT_name' => $CNT_ISO], 'limit' => 1],
1615
-                'CNT_ISO'
1616
-            );
1617
-            $CNT_ISO = is_array($country) && ! empty($country) ? reset($country) : $CNT_ISO;
1618
-        }
1619
-        // grab attendee data
1620
-        $attendee_data = [
1621
-            'ATT_fname'    => $this->request->getRequestParam('first_name'),
1622
-            'ATT_lname'    => $this->request->getRequestParam('last_name'),
1623
-            'ATT_email'    => $this->request->getRequestParam('email'),
1624
-            'ATT_address'  => $this->request->getRequestParam('address'),
1625
-            'ATT_address2' => $this->request->getRequestParam('address2'),
1626
-            'ATT_city'     => $this->request->getRequestParam('city'),
1627
-            'STA_ID'       => $STA_ID,
1628
-            'CNT_ISO'      => $CNT_ISO,
1629
-            'ATT_zip'      => $this->request->getRequestParam('zip'),
1630
-            'ATT_phone'    => $this->request->getRequestParam('phone'),
1631
-        ];
1632
-        // validate the email address since it is the most important piece of info
1633
-        if (empty($attendee_data['ATT_email'])) {
1634
-            EE_Error::add_error(
1635
-                esc_html__('An invalid email address was submitted.', 'event_espresso'),
1636
-                __FILE__,
1637
-                __FUNCTION__,
1638
-                __LINE__
1639
-            );
1640
-        }
1641
-        // does this attendee already exist in the db ? we're searching using a combination of first name, last name,
1642
-        // AND email address
1643
-        if (
1644
-            ! empty($attendee_data['ATT_fname'])
1645
-            && ! empty($attendee_data['ATT_lname'])
1646
-            && ! empty($attendee_data['ATT_email'])
1647
-        ) {
1648
-            $existing_attendee = EEM_Attendee::instance()->find_existing_attendee(
1649
-                [
1650
-                    'ATT_fname' => $attendee_data['ATT_fname'],
1651
-                    'ATT_lname' => $attendee_data['ATT_lname'],
1652
-                    'ATT_email' => $attendee_data['ATT_email'],
1653
-                ]
1654
-            );
1655
-            if ($existing_attendee instanceof EE_Attendee) {
1656
-                return $existing_attendee;
1657
-            }
1658
-        }
1659
-        // no existing attendee? kk let's create a new one
1660
-        // kinda lame, but we need a first and last name to create an attendee, so use the email address if those
1661
-        // don't exist
1662
-        $attendee_data['ATT_fname'] = ! empty($attendee_data['ATT_fname'])
1663
-            ? $attendee_data['ATT_fname']
1664
-            : $attendee_data['ATT_email'];
1665
-        $attendee_data['ATT_lname'] = ! empty($attendee_data['ATT_lname'])
1666
-            ? $attendee_data['ATT_lname']
1667
-            : $attendee_data['ATT_email'];
1668
-        return EE_Attendee::new_instance($attendee_data);
1669
-    }
1670
-
1671
-
1672
-
1673
-    /********************************************************************************************************/
1674
-    /****************************************  PROCESS REG STEP  *****************************************/
1675
-    /********************************************************************************************************/
1676
-    /**
1677
-     * process_reg_step
1678
-     *
1679
-     * @return bool
1680
-     * @throws EE_Error
1681
-     * @throws InvalidArgumentException
1682
-     * @throws ReflectionException
1683
-     * @throws EntityNotFoundException
1684
-     * @throws InvalidDataTypeException
1685
-     * @throws InvalidInterfaceException
1686
-     * @throws InvalidStatusException
1687
-     */
1688
-    public function process_reg_step()
1689
-    {
1690
-        // how have they chosen to pay?
1691
-        $this->checkout->selected_method_of_payment = $this->checkout->transaction->is_free()
1692
-            ? 'no_payment_required'
1693
-            : $this->_get_selected_method_of_payment(true);
1694
-        // choose your own adventure based on method_of_payment
1695
-        switch ($this->checkout->selected_method_of_payment) {
1696
-            case 'events_sold_out':
1697
-                $this->checkout->redirect     = true;
1698
-                $this->checkout->redirect_url = $this->checkout->cancel_page_url;
1699
-                $this->checkout->json_response->set_redirect_url($this->checkout->redirect_url);
1700
-                // mark this reg step as completed
1701
-                $this->set_completed();
1702
-                return false;
1703
-
1704
-            case 'payments_closed':
1705
-                if (
1706
-                    apply_filters(
1707
-                        'FHEE__EE_SPCO_Reg_Step_Payment_Options__process_reg_step__payments_closed__display_success',
1708
-                        false
1709
-                    )
1710
-                ) {
1711
-                    EE_Error::add_success(
1712
-                        esc_html__('no payment required at this time.', 'event_espresso'),
1713
-                        __FILE__,
1714
-                        __FUNCTION__,
1715
-                        __LINE__
1716
-                    );
1717
-                }
1718
-                // mark this reg step as completed
1719
-                $this->set_completed();
1720
-                return true;
1721
-
1722
-            case 'no_payment_required':
1723
-                if (
1724
-                    apply_filters(
1725
-                        'FHEE__EE_SPCO_Reg_Step_Payment_Options__process_reg_step__no_payment_required__display_success',
1726
-                        false
1727
-                    )
1728
-                ) {
1729
-                    EE_Error::add_success(
1730
-                        esc_html__('no payment required.', 'event_espresso'),
1731
-                        __FILE__,
1732
-                        __FUNCTION__,
1733
-                        __LINE__
1734
-                    );
1735
-                }
1736
-                // mark this reg step as completed
1737
-                $this->set_completed();
1738
-                return true;
1739
-
1740
-            default:
1741
-                $registrations         = EE_Registry::instance()->SSN->checkout()->transaction->registrations(
1742
-                    EE_Registry::instance()->SSN->checkout()->reg_cache_where_params
1743
-                );
1744
-                $ejected_registrations = EE_SPCO_Reg_Step_Payment_Options::find_registrations_that_lost_their_space(
1745
-                    $registrations,
1746
-                    EE_Registry::instance()->SSN->checkout()->revisit
1747
-                );
1748
-                // calculate difference between the two arrays
1749
-                $registrations = array_diff($registrations, $ejected_registrations);
1750
-                if (empty($registrations)) {
1751
-                    $this->_redirect_because_event_sold_out();
1752
-                    return false;
1753
-                }
1754
-                $payment = $this->_process_payment();
1755
-                if ($payment instanceof EE_Payment) {
1756
-                    $this->checkout->continue_reg = true;
1757
-                    $this->_maybe_set_completed($payment);
1758
-                } else {
1759
-                    $this->checkout->continue_reg = false;
1760
-                }
1761
-                return $payment instanceof EE_Payment;
1762
-        }
1763
-    }
1764
-
1765
-
1766
-    /**
1767
-     * _redirect_because_event_sold_out
1768
-     *
1769
-     * @return void
1770
-     */
1771
-    protected function _redirect_because_event_sold_out()
1772
-    {
1773
-        $this->checkout->continue_reg = false;
1774
-        // set redirect URL
1775
-        $this->checkout->redirect_url = add_query_arg(
1776
-            ['e_reg_url_link' => $this->checkout->reg_url_link],
1777
-            $this->checkout->current_step->reg_step_url()
1778
-        );
1779
-        $this->checkout->json_response->set_redirect_url($this->checkout->redirect_url);
1780
-    }
1781
-
1782
-
1783
-    /**
1784
-     * @param EE_Payment $payment
1785
-     * @return void
1786
-     * @throws EE_Error
1787
-     */
1788
-    protected function _maybe_set_completed(EE_Payment $payment)
1789
-    {
1790
-        // Do we need to redirect them? If so, there's more work to be done.
1791
-        if (! $payment->redirect_url()) {
1792
-            $this->set_completed();
1793
-        }
1794
-    }
1795
-
1796
-
1797
-    /**
1798
-     *    update_reg_step
1799
-     *    this is the final step after a user  revisits the site to retry a payment
1800
-     *
1801
-     * @return bool
1802
-     * @throws EE_Error
1803
-     * @throws InvalidArgumentException
1804
-     * @throws ReflectionException
1805
-     * @throws EntityNotFoundException
1806
-     * @throws InvalidDataTypeException
1807
-     * @throws InvalidInterfaceException
1808
-     * @throws InvalidStatusException
1809
-     */
1810
-    public function update_reg_step()
1811
-    {
1812
-        $success = true;
1813
-        // if payment required
1814
-        if ($this->checkout->transaction->total() > 0) {
1815
-            do_action(
1816
-                'AHEE__EE_Single_Page_Checkout__process_finalize_registration__before_gateway',
1817
-                $this->checkout->transaction
1818
-            );
1819
-            // attempt payment via payment method
1820
-            $success = $this->process_reg_step();
1821
-        }
1822
-        if ($success && ! $this->checkout->redirect) {
1823
-            $this->checkout->cart->get_grand_total()->save_this_and_descendants_to_txn(
1824
-                $this->checkout->transaction->ID()
1825
-            );
1826
-            // set return URL
1827
-            $this->checkout->redirect_url = add_query_arg(
1828
-                ['e_reg_url_link' => $this->checkout->reg_url_link],
1829
-                $this->checkout->thank_you_page_url
1830
-            );
1831
-        }
1832
-        return $success;
1833
-    }
1834
-
1835
-
1836
-    /**
1837
-     * @return EE_Payment|null
1838
-     * @throws EE_Error
1839
-     * @throws InvalidArgumentException
1840
-     * @throws ReflectionException
1841
-     * @throws RuntimeException
1842
-     * @throws InvalidDataTypeException
1843
-     * @throws InvalidInterfaceException
1844
-     */
1845
-    private function _process_payment()
1846
-    {
1847
-        // basically confirm that the event hasn't sold out since they hit the page
1848
-        if (! $this->_last_second_ticket_verifications()) {
1849
-            return null;
1850
-        }
1851
-        // ya gotta make a choice man
1852
-        if (empty($this->checkout->selected_method_of_payment)) {
1853
-            $this->checkout->json_response->set_plz_select_method_of_payment(
1854
-                esc_html__('Please select a method of payment before proceeding.', 'event_espresso')
1855
-            );
1856
-            return null;
1857
-        }
1858
-        // get EE_Payment_Method object
1859
-        if (! $this->checkout->payment_method = $this->_get_payment_method_for_selected_method_of_payment()) {
1860
-            return null;
1861
-        }
1862
-        // setup billing form
1863
-        if ($this->checkout->payment_method->type_obj()->has_billing_form()) {
1864
-            $this->checkout->billing_form = $this->_get_billing_form_for_payment_method(
1865
-                $this->checkout->payment_method
1866
-            );
1867
-            // bad billing form ?
1868
-            if (! $this->_billing_form_is_valid()) {
1869
-                return null;
1870
-            }
1871
-        }
1872
-        // ensure primary registrant has been fully processed
1873
-        if (! $this->_setup_primary_registrant_prior_to_payment()) {
1874
-            return null;
1875
-        }
1876
-        // if session is close to expiring (under 10 minutes by default)
1877
-        if ((time() - EE_Registry::instance()->SSN->expiration()) < EE_Registry::instance()->SSN->extension()) {
1878
-            // add some time to session expiration so that payment can be completed
1879
-            EE_Registry::instance()->SSN->extend_expiration();
1880
-        }
1881
-        /** @type EE_Transaction_Processor $transaction_processor */
1882
-        // $transaction_processor = EE_Registry::instance()->load_class( 'Transaction_Processor' );
1883
-        // in case a registrant leaves to an Off-Site Gateway and never returns, we want to approve any registrations
1884
-        // for events with a default reg status of Approved
1885
-        // $transaction_processor->toggle_registration_statuses_for_default_approved_events(
1886
-        //      $this->checkout->transaction, $this->checkout->reg_cache_where_params
1887
-        // );
1888
-        // attempt payment
1889
-        $payment = $this->_attempt_payment($this->checkout->payment_method);
1890
-        // process results
1891
-        $payment = $this->_validate_payment($payment);
1892
-        $payment = $this->_post_payment_processing($payment);
1893
-        // verify payment
1894
-        if ($payment instanceof EE_Payment) {
1895
-            // store that for later
1896
-            $this->checkout->payment = $payment;
1897
-            // we can also consider the TXN to not have been failed, so temporarily upgrade it's status to abandoned
1898
-            $this->checkout->transaction->toggle_failed_transaction_status();
1899
-            $payment_status = $payment->status();
1900
-            if (
1901
-                $payment_status === EEM_Payment::status_id_approved
1902
-                || $payment_status === EEM_Payment::status_id_pending
1903
-            ) {
1904
-                return $payment;
1905
-            }
1906
-            return null;
1907
-        }
1908
-        if ($payment === true) {
1909
-            // please note that offline payment methods will NOT make a payment,
1910
-            // but instead just mark themselves as the PMD_ID on the transaction, and return true
1911
-            $this->checkout->payment = $payment;
1912
-            return $payment;
1913
-        }
1914
-        // where's my money?
1915
-        return null;
1916
-    }
1917
-
1918
-
1919
-    /**
1920
-     * _last_second_ticket_verifications
1921
-     *
1922
-     * @return bool
1923
-     * @throws EE_Error
1924
-     * @throws ReflectionException
1925
-     */
1926
-    protected function _last_second_ticket_verifications()
1927
-    {
1928
-        // don't bother re-validating if not a return visit
1929
-        if (! $this->checkout->revisit) {
1930
-            return true;
1931
-        }
1932
-        $registrations = $this->checkout->transaction->registrations();
1933
-        if (empty($registrations)) {
1934
-            return false;
1935
-        }
1936
-        foreach ($registrations as $registration) {
1937
-            if ($registration instanceof EE_Registration && ! $registration->is_approved()) {
1938
-                $event = $registration->event_obj();
1939
-                if ($event instanceof EE_Event && $event->is_sold_out(true)) {
1940
-                    EE_Error::add_error(
1941
-                        apply_filters(
1942
-                            'FHEE__EE_SPCO_Reg_Step_Payment_Options___last_second_ticket_verifications__sold_out_events_msg',
1943
-                            sprintf(
1944
-                                esc_html__(
1945
-                                    '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.',
1946
-                                    'event_espresso'
1947
-                                ),
1948
-                                $event->name()
1949
-                            )
1950
-                        ),
1951
-                        __FILE__,
1952
-                        __FUNCTION__,
1953
-                        __LINE__
1954
-                    );
1955
-                    return false;
1956
-                }
1957
-            }
1958
-        }
1959
-        return true;
1960
-    }
1961
-
1962
-
1963
-    /**
1964
-     * redirect_form
1965
-     *
1966
-     * @return bool
1967
-     * @throws EE_Error
1968
-     * @throws InvalidArgumentException
1969
-     * @throws ReflectionException
1970
-     * @throws InvalidDataTypeException
1971
-     * @throws InvalidInterfaceException
1972
-     */
1973
-    public function redirect_form()
1974
-    {
1975
-        $payment_method_billing_info = $this->_payment_method_billing_info(
1976
-            $this->_get_payment_method_for_selected_method_of_payment()
1977
-        );
1978
-        $html                        = $payment_method_billing_info->get_html();
1979
-        $html                        .= $this->checkout->redirect_form;
1980
-        /** @var ResponseInterface $response */
1981
-        $response = LoaderFactory::getLoader()->getShared(ResponseInterface::class);
1982
-        $response->addOutput($html);
1983
-        return true;
1984
-    }
1985
-
1986
-
1987
-    /**
1988
-     * _billing_form_is_valid
1989
-     *
1990
-     * @return bool
1991
-     * @throws EE_Error
1992
-     */
1993
-    private function _billing_form_is_valid()
1994
-    {
1995
-        if (! $this->checkout->payment_method->type_obj()->has_billing_form()) {
1996
-            return true;
1997
-        }
1998
-        if ($this->checkout->billing_form instanceof EE_Billing_Info_Form) {
1999
-            if ($this->checkout->billing_form->was_submitted()) {
2000
-                $this->checkout->billing_form->receive_form_submission();
2001
-                if ($this->checkout->billing_form->is_valid()) {
2002
-                    return true;
2003
-                }
2004
-                $validation_errors = $this->checkout->billing_form->get_validation_errors_accumulated();
2005
-                $error_strings     = [];
2006
-                foreach ($validation_errors as $validation_error) {
2007
-                    if ($validation_error instanceof EE_Validation_Error) {
2008
-                        $form_section = $validation_error->get_form_section();
2009
-                        if ($form_section instanceof EE_Form_Input_Base) {
2010
-                            $label = $form_section->html_label_text();
2011
-                        } elseif ($form_section instanceof EE_Form_Section_Base) {
2012
-                            $label = $form_section->name();
2013
-                        } else {
2014
-                            $label = esc_html__('Validation Error', 'event_espresso');
2015
-                        }
2016
-                        $error_strings[] = sprintf('%1$s: %2$s', $label, $validation_error->getMessage());
2017
-                    }
2018
-                }
2019
-                EE_Error::add_error(
2020
-                    sprintf(
2021
-                        esc_html__(
2022
-                            'One or more billing form inputs are invalid and require correction before proceeding. %1$s %2$s',
2023
-                            'event_espresso'
2024
-                        ),
2025
-                        '<br/>',
2026
-                        implode('<br/>', $error_strings)
2027
-                    ),
2028
-                    __FILE__,
2029
-                    __FUNCTION__,
2030
-                    __LINE__
2031
-                );
2032
-            } else {
2033
-                EE_Error::add_error(
2034
-                    esc_html__(
2035
-                        'The billing form was not submitted or something prevented it\'s submission.',
2036
-                        'event_espresso'
2037
-                    ),
2038
-                    __FILE__,
2039
-                    __FUNCTION__,
2040
-                    __LINE__
2041
-                );
2042
-            }
2043
-        } else {
2044
-            EE_Error::add_error(
2045
-                esc_html__(
2046
-                    'The submitted billing form is invalid possibly due to a technical reason.',
2047
-                    'event_espresso'
2048
-                ),
2049
-                __FILE__,
2050
-                __FUNCTION__,
2051
-                __LINE__
2052
-            );
2053
-        }
2054
-        return false;
2055
-    }
2056
-
2057
-
2058
-    /**
2059
-     * _setup_primary_registrant_prior_to_payment
2060
-     * ensures that the primary registrant has a valid attendee object created with the critical details populated
2061
-     * (first & last name & email) and that both the transaction object and primary registration object have been saved
2062
-     * plz note that any other registrations will NOT be saved at this point (because they may not have any details
2063
-     * yet)
2064
-     *
2065
-     * @return bool
2066
-     * @throws EE_Error
2067
-     * @throws InvalidArgumentException
2068
-     * @throws ReflectionException
2069
-     * @throws RuntimeException
2070
-     * @throws InvalidDataTypeException
2071
-     * @throws InvalidInterfaceException
2072
-     */
2073
-    private function _setup_primary_registrant_prior_to_payment()
2074
-    {
2075
-        // check if transaction has a primary registrant and that it has a related Attendee object
2076
-        // if not, then we need to at least gather some primary registrant data before attempting payment
2077
-        if (
2078
-            $this->checkout->billing_form instanceof EE_Billing_Attendee_Info_Form
2079
-            && ! $this->checkout->transaction_has_primary_registrant()
2080
-            && ! $this->_capture_primary_registration_data_from_billing_form()
2081
-        ) {
2082
-            return false;
2083
-        }
2084
-        // because saving an object clears it's cache, we need to do the chevy shuffle
2085
-        // grab the primary_registration object
2086
-        $primary_registration = $this->checkout->transaction->primary_registration();
2087
-        // at this point we'll consider a TXN to not have been failed
2088
-        $this->checkout->transaction->toggle_failed_transaction_status();
2089
-        // save the TXN ( which clears cached copy of primary_registration)
2090
-        $this->checkout->transaction->save();
2091
-        // grab TXN ID and save it to the primary_registration
2092
-        $primary_registration->set_transaction_id($this->checkout->transaction->ID());
2093
-        // save what we have so far
2094
-        $primary_registration->save();
2095
-        return true;
2096
-    }
2097
-
2098
-
2099
-    /**
2100
-     * _capture_primary_registration_data_from_billing_form
2101
-     *
2102
-     * @return bool
2103
-     * @throws EE_Error
2104
-     * @throws InvalidArgumentException
2105
-     * @throws ReflectionException
2106
-     * @throws InvalidDataTypeException
2107
-     * @throws InvalidInterfaceException
2108
-     */
2109
-    private function _capture_primary_registration_data_from_billing_form()
2110
-    {
2111
-        // convert billing form data into an attendee
2112
-        $this->checkout->primary_attendee_obj = $this->checkout->billing_form->create_attendee_from_billing_form_data();
2113
-        if (! $this->checkout->primary_attendee_obj instanceof EE_Attendee) {
2114
-            EE_Error::add_error(
2115
-                sprintf(
2116
-                    esc_html__(
2117
-                        'The billing form details could not be used for attendee details due to a technical issue.%sPlease try again or contact %s for assistance.',
2118
-                        'event_espresso'
2119
-                    ),
2120
-                    '<br/>',
2121
-                    EE_Registry::instance()->CFG->organization->get_pretty('email')
2122
-                ),
2123
-                __FILE__,
2124
-                __FUNCTION__,
2125
-                __LINE__
2126
-            );
2127
-            return false;
2128
-        }
2129
-        $primary_registration = $this->checkout->transaction->primary_registration();
2130
-        if (! $primary_registration instanceof EE_Registration) {
2131
-            EE_Error::add_error(
2132
-                sprintf(
2133
-                    esc_html__(
2134
-                        'The primary registrant for this transaction could not be determined due to a technical issue.%sPlease try again or contact %s for assistance.',
2135
-                        'event_espresso'
2136
-                    ),
2137
-                    '<br/>',
2138
-                    EE_Registry::instance()->CFG->organization->get_pretty('email')
2139
-                ),
2140
-                __FILE__,
2141
-                __FUNCTION__,
2142
-                __LINE__
2143
-            );
2144
-            return false;
2145
-        }
2146
-        if (
2147
-            ! $primary_registration->_add_relation_to($this->checkout->primary_attendee_obj, 'Attendee')
2148
-              instanceof
2149
-              EE_Attendee
2150
-        ) {
2151
-            EE_Error::add_error(
2152
-                sprintf(
2153
-                    esc_html__(
2154
-                        'The primary registrant could not be associated with this transaction due to a technical issue.%sPlease try again or contact %s for assistance.',
2155
-                        'event_espresso'
2156
-                    ),
2157
-                    '<br/>',
2158
-                    EE_Registry::instance()->CFG->organization->get_pretty('email')
2159
-                ),
2160
-                __FILE__,
2161
-                __FUNCTION__,
2162
-                __LINE__
2163
-            );
2164
-            return false;
2165
-        }
2166
-        /** @type EE_Registration_Processor $registration_processor */
2167
-        $registration_processor = EE_Registry::instance()->load_class('Registration_Processor');
2168
-        // at this point, we should have enough details about the registrant to consider the registration NOT incomplete
2169
-        $registration_processor->toggle_incomplete_registration_status_to_default(
2170
-            $primary_registration,
2171
-            false,
2172
-            new Context(
2173
-                __METHOD__,
2174
-                esc_html__(
2175
-                    'Executed when the primary registrant\'s status is updated during the registration process when processing a billing form.',
2176
-                    'event_espresso'
2177
-                )
2178
-            )
2179
-        );
2180
-        return true;
2181
-    }
2182
-
2183
-
2184
-    /**
2185
-     * _get_payment_method_for_selected_method_of_payment
2186
-     * retrieves a valid payment method
2187
-     *
2188
-     * @return EE_Payment_Method
2189
-     * @throws EE_Error
2190
-     * @throws InvalidArgumentException
2191
-     * @throws ReflectionException
2192
-     * @throws InvalidDataTypeException
2193
-     * @throws InvalidInterfaceException
2194
-     */
2195
-    private function _get_payment_method_for_selected_method_of_payment()
2196
-    {
2197
-        if ($this->checkout->selected_method_of_payment === 'events_sold_out') {
2198
-            $this->_redirect_because_event_sold_out();
2199
-            return null;
2200
-        }
2201
-        // get EE_Payment_Method object
2202
-        if (isset($this->checkout->available_payment_methods[ $this->checkout->selected_method_of_payment ])) {
2203
-            $payment_method = $this->checkout->available_payment_methods[ $this->checkout->selected_method_of_payment ];
2204
-        } else {
2205
-            // load EEM_Payment_Method
2206
-            EE_Registry::instance()->load_model('Payment_Method');
2207
-            $EEM_Payment_Method = EEM_Payment_Method::instance();
2208
-            $payment_method     = $EEM_Payment_Method->get_one_by_slug($this->checkout->selected_method_of_payment);
2209
-        }
2210
-        // verify $payment_method
2211
-        if (! $payment_method instanceof EE_Payment_Method) {
2212
-            // not a payment
2213
-            EE_Error::add_error(
2214
-                sprintf(
2215
-                    esc_html__(
2216
-                        'The selected method of payment could not be determined due to a technical issue.%sPlease try again or contact %s for assistance.',
2217
-                        'event_espresso'
2218
-                    ),
2219
-                    '<br/>',
2220
-                    EE_Registry::instance()->CFG->organization->get_pretty('email')
2221
-                ),
2222
-                __FILE__,
2223
-                __FUNCTION__,
2224
-                __LINE__
2225
-            );
2226
-            return null;
2227
-        }
2228
-        // and verify it has a valid Payment_Method Type object
2229
-        if (! $payment_method->type_obj() instanceof EE_PMT_Base) {
2230
-            // not a payment
2231
-            EE_Error::add_error(
2232
-                sprintf(
2233
-                    esc_html__(
2234
-                        'A valid payment method could not be determined due to a technical issue.%sPlease try again or contact %s for assistance.',
2235
-                        'event_espresso'
2236
-                    ),
2237
-                    '<br/>',
2238
-                    EE_Registry::instance()->CFG->organization->get_pretty('email')
2239
-                ),
2240
-                __FILE__,
2241
-                __FUNCTION__,
2242
-                __LINE__
2243
-            );
2244
-            return null;
2245
-        }
2246
-        return $payment_method;
2247
-    }
2248
-
2249
-
2250
-    /**
2251
-     *    _attempt_payment
2252
-     *
2253
-     * @access    private
2254
-     * @type    EE_Payment_Method $payment_method
2255
-     * @return mixed EE_Payment | boolean
2256
-     * @throws EE_Error
2257
-     * @throws InvalidArgumentException
2258
-     * @throws ReflectionException
2259
-     * @throws InvalidDataTypeException
2260
-     * @throws InvalidInterfaceException
2261
-     */
2262
-    private function _attempt_payment(EE_Payment_Method $payment_method)
2263
-    {
2264
-        $payment = null;
2265
-        $this->checkout->transaction->save();
2266
-        $payment_processor = EE_Registry::instance()->load_core('Payment_Processor');
2267
-        if (! $payment_processor instanceof EE_Payment_Processor) {
2268
-            return false;
2269
-        }
2270
-        try {
2271
-            $payment_processor->set_revisit($this->checkout->revisit);
2272
-            // generate payment object
2273
-            $payment = $payment_processor->process_payment(
2274
-                $payment_method,
2275
-                $this->checkout->transaction,
2276
-                $this->checkout->amount_owing,
2277
-                $this->checkout->billing_form instanceof EE_Billing_Info_Form
2278
-                    ? $this->checkout->billing_form
2279
-                    : null,
2280
-                $this->_get_return_url($payment_method),
2281
-                'CART',
2282
-                $this->checkout->admin_request,
2283
-                true,
2284
-                $this->reg_step_url()
2285
-            );
2286
-        } catch (Exception $e) {
2287
-            $this->_handle_payment_processor_exception($e);
2288
-        }
2289
-        return $payment;
2290
-    }
2291
-
2292
-
2293
-    /**
2294
-     * _handle_payment_processor_exception
2295
-     *
2296
-     * @param Exception $e
2297
-     * @return void
2298
-     * @throws EE_Error
2299
-     * @throws InvalidArgumentException
2300
-     * @throws InvalidDataTypeException
2301
-     * @throws InvalidInterfaceException
2302
-     */
2303
-    protected function _handle_payment_processor_exception(Exception $e)
2304
-    {
2305
-        EE_Error::add_error(
2306
-            sprintf(
2307
-                esc_html__(
2308
-                    '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',
2309
-                    'event_espresso'
2310
-                ),
2311
-                '<br/>',
2312
-                EE_Registry::instance()->CFG->organization->get_pretty('email'),
2313
-                $e->getMessage(),
2314
-                $e->getFile(),
2315
-                $e->getLine()
2316
-            ),
2317
-            __FILE__,
2318
-            __FUNCTION__,
2319
-            __LINE__
2320
-        );
2321
-    }
2322
-
2323
-
2324
-    /**
2325
-     * _get_return_url
2326
-     *
2327
-     * @param EE_Payment_Method $payment_method
2328
-     * @return string
2329
-     * @throws EE_Error
2330
-     * @throws ReflectionException
2331
-     */
2332
-    protected function _get_return_url(EE_Payment_Method $payment_method)
2333
-    {
2334
-        $return_url = '';
2335
-        switch ($payment_method->type_obj()->payment_occurs()) {
2336
-            case EE_PMT_Base::offsite:
2337
-                $return_url = add_query_arg(
2338
-                    [
2339
-                        'action'                     => 'process_gateway_response',
2340
-                        'selected_method_of_payment' => $this->checkout->selected_method_of_payment,
2341
-                        'spco_txn'                   => $this->checkout->transaction->ID(),
2342
-                    ],
2343
-                    $this->reg_step_url()
2344
-                );
2345
-                break;
2346
-            case EE_PMT_Base::onsite:
2347
-            case EE_PMT_Base::offline:
2348
-                $return_url = $this->checkout->next_step->reg_step_url();
2349
-                break;
2350
-        }
2351
-        return $return_url;
2352
-    }
2353
-
2354
-
2355
-    /**
2356
-     * _validate_payment
2357
-     *
2358
-     * @param EE_Payment $payment
2359
-     * @return EE_Payment|FALSE
2360
-     * @throws EE_Error
2361
-     * @throws InvalidArgumentException
2362
-     * @throws InvalidDataTypeException
2363
-     * @throws InvalidInterfaceException
2364
-     */
2365
-    private function _validate_payment($payment = null)
2366
-    {
2367
-        if ($this->checkout->payment_method->is_off_line()) {
2368
-            return true;
2369
-        }
2370
-        // verify payment object
2371
-        if (! $payment instanceof EE_Payment) {
2372
-            // not a payment
2373
-            EE_Error::add_error(
2374
-                sprintf(
2375
-                    esc_html__(
2376
-                        'A valid payment was not generated due to a technical issue.%1$sPlease try again or contact %2$s for assistance.',
2377
-                        'event_espresso'
2378
-                    ),
2379
-                    '<br/>',
2380
-                    EE_Registry::instance()->CFG->organization->get_pretty('email')
2381
-                ),
2382
-                __FILE__,
2383
-                __FUNCTION__,
2384
-                __LINE__
2385
-            );
2386
-            return false;
2387
-        }
2388
-        return $payment;
2389
-    }
2390
-
2391
-
2392
-    /**
2393
-     * _post_payment_processing
2394
-     *
2395
-     * @param EE_Payment|bool $payment
2396
-     * @return bool|EE_Payment
2397
-     * @throws EE_Error
2398
-     * @throws InvalidArgumentException
2399
-     * @throws InvalidDataTypeException
2400
-     * @throws InvalidInterfaceException
2401
-     * @throws ReflectionException
2402
-     */
2403
-    private function _post_payment_processing($payment = null)
2404
-    {
2405
-        // Off-Line payment?
2406
-        if ($payment === true) {
2407
-            return true;
2408
-        }
2409
-        if ($payment instanceof EE_Payment) {
2410
-            // Should the user be redirected?
2411
-            if ($payment->redirect_url()) {
2412
-                do_action('AHEE_log', __CLASS__, __FUNCTION__, $payment->redirect_url(), '$payment->redirect_url()');
2413
-                $this->checkout->redirect      = true;
2414
-                $this->checkout->redirect_form = $payment->redirect_form();
2415
-                $this->checkout->redirect_url  = $this->reg_step_url('redirect_form');
2416
-                // set JSON response
2417
-                $this->checkout->json_response->set_redirect_form($this->checkout->redirect_form);
2418
-                // and lastly, let's bump the payment status to pending
2419
-                $payment->set_status(EEM_Payment::status_id_pending);
2420
-                $payment->save();
2421
-            } elseif (! $this->_process_payment_status($payment, EE_PMT_Base::onsite)) {
2422
-                // User shouldn't be redirected. So let's process it here.
2423
-                // $this->_setup_redirect_for_next_step();
2424
-                $this->checkout->continue_reg = false;
2425
-            }
2426
-            return $payment;
2427
-        }
2428
-        // ummm ya... not Off-Line, not On-Site, not off-Site ????
2429
-        $this->checkout->continue_reg = false;
2430
-        return false;
2431
-    }
2432
-
2433
-
2434
-    /**
2435
-     *    _process_payment_status
2436
-     *
2437
-     * @type    EE_Payment $payment
2438
-     * @param string       $payment_occurs
2439
-     * @return bool
2440
-     * @throws EE_Error
2441
-     * @throws InvalidArgumentException
2442
-     * @throws InvalidDataTypeException
2443
-     * @throws InvalidInterfaceException
2444
-     */
2445
-    private function _process_payment_status($payment, $payment_occurs = EE_PMT_Base::offline)
2446
-    {
2447
-        // off-line payment? carry on
2448
-        if ($payment_occurs === EE_PMT_Base::offline) {
2449
-            return true;
2450
-        }
2451
-        // verify payment validity
2452
-        if ($payment instanceof EE_Payment) {
2453
-            do_action('AHEE_log', __CLASS__, __FUNCTION__, $payment->status(), '$payment->status()');
2454
-            $msg = $payment->gateway_response();
2455
-            // check results
2456
-            switch ($payment->status()) {
2457
-                // good payment
2458
-                case EEM_Payment::status_id_approved:
2459
-                    EE_Error::add_success(
2460
-                        esc_html__('Your payment was processed successfully.', 'event_espresso'),
2461
-                        __FILE__,
2462
-                        __FUNCTION__,
2463
-                        __LINE__
2464
-                    );
2465
-                    return true;
2466
-                // slow payment
2467
-                case EEM_Payment::status_id_pending:
2468
-                    if (empty($msg)) {
2469
-                        $msg = esc_html__(
2470
-                            'Your payment appears to have been processed successfully, but the Instant Payment Notification has not yet been received. It should arrive shortly.',
2471
-                            'event_espresso'
2472
-                        );
2473
-                    }
2474
-                    EE_Error::add_success($msg, __FILE__, __FUNCTION__, __LINE__);
2475
-                    return true;
2476
-                // don't wanna payment
2477
-                case EEM_Payment::status_id_cancelled:
2478
-                    if (empty($msg)) {
2479
-                        $msg = _n(
2480
-                            'Payment cancelled. Please try again.',
2481
-                            'Payment cancelled. Please try again or select another method of payment.',
2482
-                            count($this->checkout->available_payment_methods),
2483
-                            'event_espresso'
2484
-                        );
2485
-                    }
2486
-                    EE_Error::add_attention($msg, __FILE__, __FUNCTION__, __LINE__);
2487
-                    return false;
2488
-                // not enough payment
2489
-                case EEM_Payment::status_id_declined:
2490
-                    if (empty($msg)) {
2491
-                        $msg = _n(
2492
-                            'We\'re sorry but your payment was declined. Please try again.',
2493
-                            'We\'re sorry but your payment was declined. Please try again or select another method of payment.',
2494
-                            count($this->checkout->available_payment_methods),
2495
-                            'event_espresso'
2496
-                        );
2497
-                    }
2498
-                    EE_Error::add_attention($msg, __FILE__, __FUNCTION__, __LINE__);
2499
-                    return false;
2500
-                // bad payment
2501
-                case EEM_Payment::status_id_failed:
2502
-                    if (! empty($msg)) {
2503
-                        EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
2504
-                        return false;
2505
-                    }
2506
-                    // default to error below
2507
-                    break;
2508
-            }
2509
-        }
2510
-        // off-site payment gateway responses are too unreliable, so let's just assume that
2511
-        // the payment processing is just running slower than the registrant's request
2512
-        if ($payment_occurs === EE_PMT_Base::offsite) {
2513
-            return true;
2514
-        }
2515
-        EE_Error::add_error(
2516
-            sprintf(
2517
-                esc_html__(
2518
-                    'Your payment could not be processed successfully due to a technical issue.%sPlease try again or contact %s for assistance.',
2519
-                    'event_espresso'
2520
-                ),
2521
-                '<br/>',
2522
-                EE_Registry::instance()->CFG->organization->get_pretty('email')
2523
-            ),
2524
-            __FILE__,
2525
-            __FUNCTION__,
2526
-            __LINE__
2527
-        );
2528
-        return false;
2529
-    }
2530
-
2531
-
2532
-
2533
-
2534
-
2535
-
2536
-    /********************************************************************************************************/
2537
-    /**********************************  PROCESS GATEWAY RESPONSE  **********************************/
2538
-    /********************************************************************************************************/
2539
-    /**
2540
-     * process_gateway_response
2541
-     * this is the return point for Off-Site Payment Methods
2542
-     * It will attempt to "handle the IPN" if it appears that this has not already occurred,
2543
-     * otherwise, it will load up the last payment made for the TXN.
2544
-     * If the payment retrieved looks good, it will then either:
2545
-     *    complete the current step and allow advancement to the next reg step
2546
-     *        or present the payment options again
2547
-     *
2548
-     * @return bool
2549
-     * @throws EE_Error
2550
-     * @throws InvalidArgumentException
2551
-     * @throws ReflectionException
2552
-     * @throws InvalidDataTypeException
2553
-     * @throws InvalidInterfaceException
2554
-     */
2555
-    public function process_gateway_response()
2556
-    {
2557
-        // how have they chosen to pay?
2558
-        $this->checkout->selected_method_of_payment = $this->_get_selected_method_of_payment(true);
2559
-        // get EE_Payment_Method object
2560
-        if (! $this->checkout->payment_method = $this->_get_payment_method_for_selected_method_of_payment()) {
2561
-            $this->checkout->continue_reg = false;
2562
-            return false;
2563
-        }
2564
-        if (! $this->checkout->payment_method->is_off_site()) {
2565
-            return false;
2566
-        }
2567
-        $this->_validate_offsite_return();
2568
-        // verify TXN
2569
-        if ($this->checkout->transaction instanceof EE_Transaction) {
2570
-            $gateway = $this->checkout->payment_method->type_obj()->get_gateway();
2571
-            if (! $gateway instanceof EE_Offsite_Gateway) {
2572
-                $this->checkout->continue_reg = false;
2573
-                return false;
2574
-            }
2575
-            $payment = $this->_process_off_site_payment($gateway);
2576
-            $payment = $this->_process_cancelled_payments($payment);
2577
-            $payment = $this->_validate_payment($payment);
2578
-            // if payment was not declined by the payment gateway or cancelled by the registrant
2579
-            if ($this->_process_payment_status($payment, EE_PMT_Base::offsite)) {
2580
-                // $this->_setup_redirect_for_next_step();
2581
-                // store that for later
2582
-                $this->checkout->payment = $payment;
2583
-                // mark this reg step as completed, as long as gateway doesn't use a separate IPN request,
2584
-                // because we will complete this step during the IPN processing then
2585
-                if (! $this->handle_IPN_in_this_request()) {
2586
-                    $this->set_completed();
2587
-                }
2588
-                return true;
2589
-            }
2590
-        }
2591
-        // DEBUG LOG
2592
-        // $this->checkout->log(
2593
-        //     __CLASS__,
2594
-        //     __FUNCTION__,
2595
-        //     __LINE__,
2596
-        //     array('payment' => $payment)
2597
-        // );
2598
-        $this->checkout->continue_reg = false;
2599
-        return false;
2600
-    }
2601
-
2602
-
2603
-    /**
2604
-     * _validate_return
2605
-     *
2606
-     * @return void
2607
-     * @throws EE_Error
2608
-     * @throws InvalidArgumentException
2609
-     * @throws InvalidDataTypeException
2610
-     * @throws InvalidInterfaceException
2611
-     * @throws ReflectionException
2612
-     */
2613
-    private function _validate_offsite_return()
2614
-    {
2615
-        $TXN_ID = $this->request->getRequestParam('spco_txn', 0, 'int');
2616
-        if ($TXN_ID !== $this->checkout->transaction->ID()) {
2617
-            // Houston... we might have a problem
2618
-            $invalid_TXN = false;
2619
-            // first gather some info
2620
-            $valid_TXN          = EEM_Transaction::instance()->get_one_by_ID($TXN_ID);
2621
-            $primary_registrant = $valid_TXN instanceof EE_Transaction
2622
-                ? $valid_TXN->primary_registration()
2623
-                : null;
2624
-            // let's start by retrieving the cart for this TXN
2625
-            $cart = $this->checkout->get_cart_for_transaction($this->checkout->transaction);
2626
-            if ($cart instanceof EE_Cart) {
2627
-                // verify that the current cart has tickets
2628
-                $tickets = $cart->get_tickets();
2629
-                if (empty($tickets)) {
2630
-                    $invalid_TXN = true;
2631
-                }
2632
-            } else {
2633
-                $invalid_TXN = true;
2634
-            }
2635
-            $valid_TXN_SID = $primary_registrant instanceof EE_Registration
2636
-                ? $primary_registrant->session_ID()
2637
-                : null;
2638
-            // validate current Session ID and compare against valid TXN session ID
2639
-            if (
2640
-                $invalid_TXN // if this is already true, then skip other checks
2641
-                || EE_Session::instance()->id() === null
2642
-                || (
2643
-                    // WARNING !!!
2644
-                    // this could be PayPal sending back duplicate requests (ya they do that)
2645
-                    // or it **could** mean someone is simply registering AGAIN after having just done so
2646
-                    // so now we need to determine if this current TXN looks valid or not
2647
-                    // and whether this reg step has even been started ?
2648
-                    EE_Session::instance()->id() === $valid_TXN_SID
2649
-                    // really? you're half way through this reg step, but you never started it ?
2650
-                    && $this->checkout->transaction->reg_step_completed($this->slug()) === false
2651
-                )
2652
-            ) {
2653
-                $invalid_TXN = true;
2654
-            }
2655
-            if ($invalid_TXN) {
2656
-                // is the valid TXN completed ?
2657
-                if ($valid_TXN instanceof EE_Transaction) {
2658
-                    // has this step even been started ?
2659
-                    $reg_step_completed = $valid_TXN->reg_step_completed($this->slug());
2660
-                    if ($reg_step_completed !== false && $reg_step_completed !== true) {
2661
-                        // so it **looks** like this is a double request from PayPal
2662
-                        // so let's try to pick up where we left off
2663
-                        $this->checkout->transaction = $valid_TXN;
2664
-                        $this->checkout->refresh_all_entities(true);
2665
-                        return;
2666
-                    }
2667
-                }
2668
-                // you appear to be lost?
2669
-                $this->_redirect_wayward_request($primary_registrant);
2670
-            }
2671
-        }
2672
-    }
2673
-
2674
-
2675
-    /**
2676
-     * _redirect_wayward_request
2677
-     *
2678
-     * @param EE_Registration|null $primary_registrant
2679
-     * @return void
2680
-     * @throws EE_Error
2681
-     * @throws InvalidArgumentException
2682
-     * @throws InvalidDataTypeException
2683
-     * @throws InvalidInterfaceException
2684
-     * @throws ReflectionException
2685
-     */
2686
-    private function _redirect_wayward_request(EE_Registration $primary_registrant)
2687
-    {
2688
-        if (! $primary_registrant instanceof EE_Registration) {
2689
-            // try redirecting based on the current TXN
2690
-            $primary_registrant = $this->checkout->transaction instanceof EE_Transaction
2691
-                ? $this->checkout->transaction->primary_registration()
2692
-                : null;
2693
-        }
2694
-        if (! $primary_registrant instanceof EE_Registration) {
2695
-            EE_Error::add_error(
2696
-                sprintf(
2697
-                    esc_html__(
2698
-                        '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.',
2699
-                        'event_espresso'
2700
-                    ),
2701
-                    '<br/>',
2702
-                    EE_Registry::instance()->CFG->organization->get_pretty('email')
2703
-                ),
2704
-                __FILE__,
2705
-                __FUNCTION__,
2706
-                __LINE__
2707
-            );
2708
-            return;
2709
-        }
2710
-        // make sure transaction is not locked
2711
-        $this->checkout->transaction->unlock();
2712
-        wp_safe_redirect(
2713
-            add_query_arg(
2714
-                [
2715
-                    'e_reg_url_link' => $primary_registrant->reg_url_link(),
2716
-                ],
2717
-                $this->checkout->thank_you_page_url
2718
-            )
2719
-        );
2720
-        exit();
2721
-    }
2722
-
2723
-
2724
-    /**
2725
-     * _process_off_site_payment
2726
-     *
2727
-     * @param EE_Offsite_Gateway $gateway
2728
-     * @return EE_Payment
2729
-     * @throws EE_Error
2730
-     * @throws InvalidArgumentException
2731
-     * @throws InvalidDataTypeException
2732
-     * @throws InvalidInterfaceException
2733
-     * @throws ReflectionException
2734
-     */
2735
-    private function _process_off_site_payment(EE_Offsite_Gateway $gateway)
2736
-    {
2737
-        try {
2738
-            $request      = LoaderFactory::getLoader()->getShared(RequestInterface::class);
2739
-            $request_data = $request->requestParams();
2740
-            // if gateway uses_separate_IPN_request, then we don't have to process the IPN manually
2741
-            $this->set_handle_IPN_in_this_request(
2742
-                $gateway->handle_IPN_in_this_request($request_data, false)
2743
-            );
2744
-            if ($this->handle_IPN_in_this_request()) {
2745
-                // get payment details and process results
2746
-                /** @type EE_Payment_Processor $payment_processor */
2747
-                $payment_processor = EE_Registry::instance()->load_core('Payment_Processor');
2748
-                $payment           = $payment_processor->process_ipn(
2749
-                    $request_data,
2750
-                    $this->checkout->transaction,
2751
-                    $this->checkout->payment_method,
2752
-                    true,
2753
-                    false
2754
-                );
2755
-                // $payment_source = 'process_ipn';
2756
-            } else {
2757
-                $payment = $this->checkout->transaction->last_payment();
2758
-                // $payment_source = 'last_payment';
2759
-            }
2760
-        } catch (Exception $e) {
2761
-            // let's just eat the exception and try to move on using any previously set payment info
2762
-            $payment = $this->checkout->transaction->last_payment();
2763
-            // $payment_source = 'last_payment after Exception';
2764
-            // but if we STILL don't have a payment object
2765
-            if (! $payment instanceof EE_Payment) {
2766
-                // then we'll object ! ( not object like a thing... but object like what a lawyer says ! )
2767
-                $this->_handle_payment_processor_exception($e);
2768
-            }
2769
-        }
2770
-        return $payment;
2771
-    }
2772
-
2773
-
2774
-    /**
2775
-     * _process_cancelled_payments
2776
-     * just makes sure that the payment status gets updated correctly
2777
-     * so tha tan error isn't generated during payment validation
2778
-     *
2779
-     * @param EE_Payment $payment
2780
-     * @return EE_Payment|null
2781
-     * @throws EE_Error
2782
-     */
2783
-    private function _process_cancelled_payments($payment = null)
2784
-    {
2785
-        if (
2786
-            $payment instanceof EE_Payment
2787
-            && $this->request->requestParamIsSet('ee_cancel_payment')
2788
-            && $payment->status() === EEM_Payment::status_id_failed
2789
-        ) {
2790
-            $payment->set_status(EEM_Payment::status_id_cancelled);
2791
-        }
2792
-        return $payment;
2793
-    }
2794
-
2795
-
2796
-    /**
2797
-     *    get_transaction_details_for_gateways
2798
-     *
2799
-     * @access    public
2800
-     * @return void
2801
-     * @throws EE_Error
2802
-     * @throws InvalidArgumentException
2803
-     * @throws ReflectionException
2804
-     * @throws InvalidDataTypeException
2805
-     * @throws InvalidInterfaceException
2806
-     */
2807
-    public function get_transaction_details_for_gateways()
2808
-    {
2809
-        $txn_details = [];
2810
-        // ya gotta make a choice man
2811
-        if (empty($this->checkout->selected_method_of_payment)) {
2812
-            $txn_details = [
2813
-                'error' => esc_html__('Please select a method of payment before proceeding.', 'event_espresso'),
2814
-            ];
2815
-        }
2816
-        // get EE_Payment_Method object
2817
-        if (
2818
-            empty($txn_details)
2819
-            && ! $this->checkout->payment_method = $this->_get_payment_method_for_selected_method_of_payment()
2820
-        ) {
2821
-            $txn_details = [
2822
-                'selected_method_of_payment' => $this->checkout->selected_method_of_payment,
2823
-                'error'                      => esc_html__(
2824
-                    'A valid Payment Method could not be determined.',
2825
-                    'event_espresso'
2826
-                ),
2827
-            ];
2828
-        }
2829
-        if (empty($txn_details) && $this->checkout->transaction instanceof EE_Transaction) {
2830
-            $return_url  = $this->_get_return_url($this->checkout->payment_method);
2831
-            $txn_details = [
2832
-                'TXN_ID'         => $this->checkout->transaction->ID(),
2833
-                'TXN_timestamp'  => $this->checkout->transaction->datetime(),
2834
-                'TXN_total'      => $this->checkout->transaction->total(),
2835
-                'TXN_paid'       => $this->checkout->transaction->paid(),
2836
-                'TXN_reg_steps'  => $this->checkout->transaction->reg_steps(),
2837
-                'STS_ID'         => $this->checkout->transaction->status_ID(),
2838
-                'PMD_ID'         => $this->checkout->transaction->payment_method_ID(),
2839
-                'payment_amount' => $this->checkout->amount_owing,
2840
-                'return_url'     => $return_url,
2841
-                'cancel_url'     => add_query_arg(['ee_cancel_payment' => true], $return_url),
2842
-                'notify_url'     => EE_Config::instance()->core->txn_page_url(
2843
-                    [
2844
-                        'e_reg_url_link'    => $this->checkout->transaction->primary_registration()->reg_url_link(),
2845
-                        'ee_payment_method' => $this->checkout->payment_method->slug(),
2846
-                    ]
2847
-                ),
2848
-            ];
2849
-        }
2850
-        echo wp_json_encode($txn_details);
2851
-        exit();
2852
-    }
2853
-
2854
-
2855
-    /**
2856
-     *    __sleep
2857
-     * to conserve db space, let's remove the reg_form and the EE_Checkout object from EE_SPCO_Reg_Step objects upon
2858
-     * serialization EE_Checkout will handle the reimplementation of itself upon waking, but we won't bother with the
2859
-     * reg form, because if needed, it will be regenerated anyways
2860
-     *
2861
-     * @return array
2862
-     */
2863
-    public function __sleep()
2864
-    {
2865
-        // remove the reg form and the checkout
2866
-        return array_diff(array_keys(get_object_vars($this)), ['reg_form', 'checkout', 'line_item_display']);
2867
-    }
24
+	/**
25
+	 * @var EE_Line_Item_Display $Line_Item_Display
26
+	 */
27
+	protected $line_item_display;
28
+
29
+	/**
30
+	 * @var boolean $handle_IPN_in_this_request
31
+	 */
32
+	protected $handle_IPN_in_this_request = false;
33
+
34
+
35
+	/**
36
+	 *    set_hooks - for hooking into EE Core, other modules, etc
37
+	 *
38
+	 * @access    public
39
+	 * @return    void
40
+	 */
41
+	public static function set_hooks()
42
+	{
43
+		add_filter(
44
+			'FHEE__SPCO__EE_Line_Item_Filter_Collection',
45
+			['EE_SPCO_Reg_Step_Payment_Options', 'add_spco_line_item_filters']
46
+		);
47
+		add_action(
48
+			'wp_ajax_switch_spco_billing_form',
49
+			['EE_SPCO_Reg_Step_Payment_Options', 'switch_spco_billing_form']
50
+		);
51
+		add_action(
52
+			'wp_ajax_nopriv_switch_spco_billing_form',
53
+			['EE_SPCO_Reg_Step_Payment_Options', 'switch_spco_billing_form']
54
+		);
55
+		add_action('wp_ajax_save_payer_details', ['EE_SPCO_Reg_Step_Payment_Options', 'save_payer_details']);
56
+		add_action(
57
+			'wp_ajax_nopriv_save_payer_details',
58
+			['EE_SPCO_Reg_Step_Payment_Options', 'save_payer_details']
59
+		);
60
+		add_action(
61
+			'wp_ajax_get_transaction_details_for_gateways',
62
+			['EE_SPCO_Reg_Step_Payment_Options', 'get_transaction_details']
63
+		);
64
+		add_action(
65
+			'wp_ajax_nopriv_get_transaction_details_for_gateways',
66
+			['EE_SPCO_Reg_Step_Payment_Options', 'get_transaction_details']
67
+		);
68
+		add_filter(
69
+			'FHEE__EED_Recaptcha___bypass_recaptcha__bypass_request_params_array',
70
+			['EE_SPCO_Reg_Step_Payment_Options', 'bypass_recaptcha_for_load_payment_method'],
71
+			10,
72
+			1
73
+		);
74
+	}
75
+
76
+
77
+	/**
78
+	 *    ajax switch_spco_billing_form
79
+	 *
80
+	 */
81
+	public static function switch_spco_billing_form()
82
+	{
83
+		EED_Single_Page_Checkout::process_ajax_request('switch_payment_method');
84
+	}
85
+
86
+
87
+	/**
88
+	 *    ajax save_payer_details
89
+	 *
90
+	 */
91
+	public static function save_payer_details()
92
+	{
93
+		EED_Single_Page_Checkout::process_ajax_request('save_payer_details_via_ajax');
94
+	}
95
+
96
+
97
+	/**
98
+	 *    ajax get_transaction_details
99
+	 *
100
+	 */
101
+	public static function get_transaction_details()
102
+	{
103
+		EED_Single_Page_Checkout::process_ajax_request('get_transaction_details_for_gateways');
104
+	}
105
+
106
+
107
+	/**
108
+	 * bypass_recaptcha_for_load_payment_method
109
+	 *
110
+	 * @access public
111
+	 * @return array
112
+	 * @throws InvalidArgumentException
113
+	 * @throws InvalidDataTypeException
114
+	 * @throws InvalidInterfaceException
115
+	 */
116
+	public static function bypass_recaptcha_for_load_payment_method()
117
+	{
118
+		return [
119
+			'EESID'  => EE_Registry::instance()->SSN->id(),
120
+			'step'   => 'payment_options',
121
+			'action' => 'spco_billing_form',
122
+		];
123
+	}
124
+
125
+
126
+	/**
127
+	 *    class constructor
128
+	 *
129
+	 * @access    public
130
+	 * @param EE_Checkout $checkout
131
+	 */
132
+	public function __construct(EE_Checkout $checkout)
133
+	{
134
+		$this->request   = EED_Single_Page_Checkout::getRequest();
135
+		$this->_slug     = 'payment_options';
136
+		$this->_name     = esc_html__('Payment Options', 'event_espresso');
137
+		$this->_template = SPCO_REG_STEPS_PATH . $this->_slug . '/payment_options_main.template.php';
138
+		$this->checkout  = $checkout;
139
+		$this->_reset_success_message();
140
+		$this->set_instructions(
141
+			esc_html__(
142
+				'Please select a method of payment and provide any necessary billing information before proceeding.',
143
+				'event_espresso'
144
+			)
145
+		);
146
+	}
147
+
148
+
149
+	/**
150
+	 * @return null
151
+	 */
152
+	public function line_item_display()
153
+	{
154
+		return $this->line_item_display;
155
+	}
156
+
157
+
158
+	/**
159
+	 * @param null $line_item_display
160
+	 */
161
+	public function set_line_item_display($line_item_display)
162
+	{
163
+		$this->line_item_display = $line_item_display;
164
+	}
165
+
166
+
167
+	/**
168
+	 * @return boolean
169
+	 */
170
+	public function handle_IPN_in_this_request()
171
+	{
172
+		return $this->handle_IPN_in_this_request;
173
+	}
174
+
175
+
176
+	/**
177
+	 * @param boolean $handle_IPN_in_this_request
178
+	 */
179
+	public function set_handle_IPN_in_this_request($handle_IPN_in_this_request)
180
+	{
181
+		$this->handle_IPN_in_this_request = filter_var($handle_IPN_in_this_request, FILTER_VALIDATE_BOOLEAN);
182
+	}
183
+
184
+
185
+	/**
186
+	 * translate_js_strings
187
+	 *
188
+	 * @return void
189
+	 */
190
+	public function translate_js_strings()
191
+	{
192
+		EE_Registry::$i18n_js_strings['no_payment_method']      = esc_html__(
193
+			'Please select a method of payment in order to continue.',
194
+			'event_espresso'
195
+		);
196
+		EE_Registry::$i18n_js_strings['invalid_payment_method'] = esc_html__(
197
+			'A valid method of payment could not be determined. Please refresh the page and try again.',
198
+			'event_espresso'
199
+		);
200
+		EE_Registry::$i18n_js_strings['forwarding_to_offsite']  = esc_html__(
201
+			'Forwarding to Secure Payment Provider.',
202
+			'event_espresso'
203
+		);
204
+	}
205
+
206
+
207
+	/**
208
+	 * enqueue_styles_and_scripts
209
+	 *
210
+	 * @return void
211
+	 * @throws EE_Error
212
+	 * @throws InvalidArgumentException
213
+	 * @throws InvalidDataTypeException
214
+	 * @throws InvalidInterfaceException
215
+	 * @throws ReflectionException
216
+	 */
217
+	public function enqueue_styles_and_scripts()
218
+	{
219
+		$transaction = $this->checkout->transaction;
220
+		// if the transaction isn't set or nothing is owed on it, don't enqueue any JS
221
+		if (! $transaction instanceof EE_Transaction || EEH_Money::compare_floats($transaction->remaining(), 0)) {
222
+			return;
223
+		}
224
+		foreach (
225
+			EEM_Payment_Method::instance()->get_all_for_transaction(
226
+				$transaction,
227
+				EEM_Payment_Method::scope_cart
228
+			) as $payment_method
229
+		) {
230
+			$type_obj = $payment_method->type_obj();
231
+			if ($type_obj instanceof EE_PMT_Base) {
232
+				$billing_form = $type_obj->generate_new_billing_form($transaction);
233
+				if ($billing_form instanceof EE_Form_Section_Proper) {
234
+					$billing_form->enqueue_js();
235
+				}
236
+			}
237
+		}
238
+	}
239
+
240
+
241
+	/**
242
+	 * initialize_reg_step
243
+	 *
244
+	 * @return bool
245
+	 * @throws EE_Error
246
+	 * @throws InvalidArgumentException
247
+	 * @throws ReflectionException
248
+	 * @throws InvalidDataTypeException
249
+	 * @throws InvalidInterfaceException
250
+	 */
251
+	public function initialize_reg_step()
252
+	{
253
+		// TODO: if /when we implement donations, then this will need overriding
254
+		if (
255
+			// don't need payment options for:
256
+			// registrations made via the admin
257
+			// completed transactions
258
+			// overpaid transactions
259
+			// $ 0.00 transactions(no payment required)
260
+			! $this->checkout->payment_required()
261
+			// but do NOT remove if current action being called belongs to this reg step
262
+			&& ! is_callable([$this, $this->checkout->action])
263
+			&& ! $this->completed()
264
+		) {
265
+			// and if so, then we no longer need the Payment Options step
266
+			if ($this->is_current_step()) {
267
+				$this->checkout->generate_reg_form = false;
268
+			}
269
+			$this->checkout->remove_reg_step($this->_slug);
270
+			// DEBUG LOG
271
+			// $this->checkout->log( __CLASS__, __FUNCTION__, __LINE__ );
272
+			return false;
273
+		}
274
+		// load EEM_Payment_Method
275
+		EE_Registry::instance()->load_model('Payment_Method');
276
+		// get all active payment methods
277
+		$this->checkout->available_payment_methods = EEM_Payment_Method::instance()->get_all_for_transaction(
278
+			$this->checkout->transaction,
279
+			EEM_Payment_Method::scope_cart
280
+		);
281
+		return true;
282
+	}
283
+
284
+
285
+	/**
286
+	 * @return EE_Form_Section_Proper
287
+	 * @throws EE_Error
288
+	 * @throws InvalidArgumentException
289
+	 * @throws ReflectionException
290
+	 * @throws EntityNotFoundException
291
+	 * @throws InvalidDataTypeException
292
+	 * @throws InvalidInterfaceException
293
+	 * @throws InvalidStatusException
294
+	 */
295
+	public function generate_reg_form()
296
+	{
297
+		// reset in case someone changes their mind
298
+		$this->_reset_selected_method_of_payment();
299
+		// set some defaults
300
+		$this->checkout->selected_method_of_payment = 'payments_closed';
301
+		$registrations_requiring_payment            = [];
302
+		$registrations_for_free_events              = [];
303
+		$registrations_requiring_pre_approval       = [];
304
+		$sold_out_events                            = [];
305
+		$insufficient_spaces_available              = [];
306
+		$no_payment_required                        = true;
307
+		// loop thru registrations to gather info
308
+		$registrations         = $this->checkout->transaction->registrations($this->checkout->reg_cache_where_params);
309
+		$ejected_registrations = EE_SPCO_Reg_Step_Payment_Options::find_registrations_that_lost_their_space(
310
+			$registrations,
311
+			$this->checkout->revisit
312
+		);
313
+		foreach ($registrations as $REG_ID => $registration) {
314
+			/** @var $registration EE_Registration */
315
+			// Skip if the registration has been moved
316
+			if ($registration->wasMoved()) {
317
+				continue;
318
+			}
319
+			// has this registration lost it's space ?
320
+			if (isset($ejected_registrations[ $REG_ID ])) {
321
+				if ($registration->event()->is_sold_out() || $registration->event()->is_sold_out(true)) {
322
+					$sold_out_events[ $registration->event()->ID() ] = $registration->event();
323
+				} else {
324
+					$insufficient_spaces_available[ $registration->event()->ID() ] = $registration->event();
325
+				}
326
+				continue;
327
+			}
328
+			// event requires admin approval
329
+			if ($registration->status_ID() === RegStatus::AWAITING_REVIEW) {
330
+				// add event to list of events with pre-approval reg status
331
+				$registrations_requiring_pre_approval[ $REG_ID ] = $registration;
332
+				do_action(
333
+					'AHEE__EE_SPCO_Reg_Step_Payment_Options__generate_reg_form__event_requires_pre_approval',
334
+					$registration->event(),
335
+					$this
336
+				);
337
+				continue;
338
+			}
339
+			if (
340
+				$this->checkout->revisit
341
+				&& $registration->status_ID() !== RegStatus::APPROVED
342
+				&& (
343
+					$registration->event()->is_sold_out()
344
+					|| $registration->event()->is_sold_out(true)
345
+				)
346
+			) {
347
+				// add event to list of events that are sold out
348
+				$sold_out_events[ $registration->event()->ID() ] = $registration->event();
349
+				do_action(
350
+					'AHEE__EE_SPCO_Reg_Step_Payment_Options__generate_reg_form__sold_out_event',
351
+					$registration->event(),
352
+					$this
353
+				);
354
+				continue;
355
+			}
356
+			// are they allowed to pay now and is there monies owing?
357
+			if ($registration->owes_monies_and_can_pay()) {
358
+				$registrations_requiring_payment[ $REG_ID ] = $registration;
359
+				do_action(
360
+					'AHEE__EE_SPCO_Reg_Step_Payment_Options__generate_reg_form__event_requires_payment',
361
+					$registration->event(),
362
+					$this
363
+				);
364
+			} elseif (
365
+				! $this->checkout->revisit
366
+					  && $registration->status_ID() !== RegStatus::AWAITING_REVIEW
367
+					  && $registration->ticket()->is_free()
368
+			) {
369
+				$registrations_for_free_events[ $registration->ticket()->ID() ] = $registration;
370
+			}
371
+		}
372
+		$subsections = [];
373
+		// now decide which template to load
374
+		if (! empty($sold_out_events)) {
375
+			$subsections['sold_out_events'] = $this->_sold_out_events($sold_out_events);
376
+		}
377
+		if (! empty($insufficient_spaces_available)) {
378
+			$subsections['insufficient_space'] = $this->_insufficient_spaces_available(
379
+				$insufficient_spaces_available
380
+			);
381
+		}
382
+		if (! empty($registrations_requiring_pre_approval)) {
383
+			$subsections['registrations_requiring_pre_approval'] = $this->_registrations_requiring_pre_approval(
384
+				$registrations_requiring_pre_approval
385
+			);
386
+		}
387
+		if (! empty($registrations_for_free_events)) {
388
+			$subsections['no_payment_required'] = $this->_no_payment_required($registrations_for_free_events);
389
+		}
390
+		if (! empty($registrations_requiring_payment)) {
391
+			if ($this->checkout->amount_owing > 0) {
392
+				// autoload Line_Item_Display classes
393
+				EEH_Autoloader::register_line_item_filter_autoloaders();
394
+				$line_item_filter_processor = new EE_Line_Item_Filter_Processor(
395
+					apply_filters(
396
+						'FHEE__SPCO__EE_Line_Item_Filter_Collection',
397
+						new EE_Line_Item_Filter_Collection()
398
+					),
399
+					$this->checkout->cart->get_grand_total()
400
+				);
401
+				/** @var EE_Line_Item $filtered_line_item_tree */
402
+				$filtered_line_item_tree = $line_item_filter_processor->process();
403
+				EEH_Autoloader::register_line_item_display_autoloaders();
404
+				$this->set_line_item_display(new EE_Line_Item_Display('spco'));
405
+				$subsections['payment_options'] = $this->_display_payment_options(
406
+					$this->line_item_display->display_line_item(
407
+						$filtered_line_item_tree,
408
+						['registrations' => $registrations]
409
+					)
410
+				);
411
+				$this->checkout->amount_owing   = $filtered_line_item_tree->total();
412
+				$this->_apply_registration_payments_to_amount_owing($registrations);
413
+			}
414
+			$no_payment_required = false;
415
+		} else {
416
+			$this->_hide_reg_step_submit_button_if_revisit();
417
+		}
418
+		$this->_save_selected_method_of_payment();
419
+
420
+		$subsections['default_hidden_inputs'] = $this->reg_step_hidden_inputs();
421
+		$subsections['extra_hidden_inputs']   = $this->_extra_hidden_inputs($no_payment_required);
422
+
423
+		return new EE_Form_Section_Proper(
424
+			[
425
+				'name'            => $this->reg_form_name(),
426
+				'html_id'         => $this->reg_form_name(),
427
+				'subsections'     => $subsections,
428
+				'layout_strategy' => new EE_No_Layout(),
429
+			]
430
+		);
431
+	}
432
+
433
+
434
+	/**
435
+	 * add line item filters required for this reg step
436
+	 * these filters are applied via this line in EE_SPCO_Reg_Step_Payment_Options::set_hooks():
437
+	 *        add_filter( 'FHEE__SPCO__EE_Line_Item_Filter_Collection', array( 'EE_SPCO_Reg_Step_Payment_Options',
438
+	 *        'add_spco_line_item_filters' ) ); so any code that wants to use the same set of filters during the
439
+	 *        payment options reg step, can apply these filters via the following: apply_filters(
440
+	 *        'FHEE__SPCO__EE_Line_Item_Filter_Collection', new EE_Line_Item_Filter_Collection() ) or to an existing
441
+	 *        filter collection by passing that instead of instantiating a new collection
442
+	 *
443
+	 * @param EE_Line_Item_Filter_Collection $line_item_filter_collection
444
+	 * @return EE_Line_Item_Filter_Collection
445
+	 * @throws EE_Error
446
+	 * @throws InvalidArgumentException
447
+	 * @throws ReflectionException
448
+	 * @throws EntityNotFoundException
449
+	 * @throws InvalidDataTypeException
450
+	 * @throws InvalidInterfaceException
451
+	 * @throws InvalidStatusException
452
+	 */
453
+	public static function add_spco_line_item_filters(EE_Line_Item_Filter_Collection $line_item_filter_collection)
454
+	{
455
+		if (! EE_Registry::instance()->SSN instanceof EE_Session) {
456
+			return $line_item_filter_collection;
457
+		}
458
+		if (! EE_Registry::instance()->SSN->checkout() instanceof EE_Checkout) {
459
+			return $line_item_filter_collection;
460
+		}
461
+		if (! EE_Registry::instance()->SSN->checkout()->transaction instanceof EE_Transaction) {
462
+			return $line_item_filter_collection;
463
+		}
464
+		$line_item_filter_collection->add(
465
+			new EE_Billable_Line_Item_Filter(
466
+				EE_SPCO_Reg_Step_Payment_Options::remove_ejected_registrations(
467
+					EE_Registry::instance()->SSN->checkout()->transaction->registrations(
468
+						EE_Registry::instance()->SSN->checkout()->reg_cache_where_params
469
+					)
470
+				)
471
+			)
472
+		);
473
+		$line_item_filter_collection->add(new EE_Non_Zero_Line_Item_Filter());
474
+		return $line_item_filter_collection;
475
+	}
476
+
477
+
478
+	/**
479
+	 * remove_ejected_registrations
480
+	 * if a registrant has lost their potential space at an event due to lack of payment,
481
+	 * then this method removes them from the list of registrations being paid for during this request
482
+	 *
483
+	 * @param EE_Registration[] $registrations
484
+	 * @return EE_Registration[]
485
+	 * @throws EE_Error
486
+	 * @throws InvalidArgumentException
487
+	 * @throws ReflectionException
488
+	 * @throws EntityNotFoundException
489
+	 * @throws InvalidDataTypeException
490
+	 * @throws InvalidInterfaceException
491
+	 * @throws InvalidStatusException
492
+	 */
493
+	public static function remove_ejected_registrations(array $registrations)
494
+	{
495
+		$ejected_registrations = EE_SPCO_Reg_Step_Payment_Options::find_registrations_that_lost_their_space(
496
+			$registrations,
497
+			EE_Registry::instance()->SSN->checkout()->revisit
498
+		);
499
+		foreach ($registrations as $REG_ID => $registration) {
500
+			// has this registration lost it's space ?
501
+			if (isset($ejected_registrations[ $REG_ID ])) {
502
+				unset($registrations[ $REG_ID ]);
503
+			}
504
+		}
505
+		return $registrations;
506
+	}
507
+
508
+
509
+	/**
510
+	 * find_registrations_that_lost_their_space
511
+	 * If a registrant chooses an offline payment method like Invoice,
512
+	 * then no space is reserved for them at the event until they fully pay fo that site
513
+	 * (unless the event's default reg status is set to APPROVED)
514
+	 * if a registrant then later returns to pay, but the number of spaces available has been reduced due to sales,
515
+	 * then this method will determine which registrations have lost the ability to complete the reg process.
516
+	 *
517
+	 * @param EE_Registration[] $registrations
518
+	 * @param bool              $revisit
519
+	 * @return array
520
+	 * @throws EE_Error
521
+	 * @throws InvalidArgumentException
522
+	 * @throws ReflectionException
523
+	 * @throws EntityNotFoundException
524
+	 * @throws InvalidDataTypeException
525
+	 * @throws InvalidInterfaceException
526
+	 * @throws InvalidStatusException
527
+	 */
528
+	public static function find_registrations_that_lost_their_space(array $registrations, $revisit = false)
529
+	{
530
+		// registrations per event
531
+		$event_reg_count = [];
532
+		// spaces left per event
533
+		$event_spaces_remaining = [];
534
+		// tickets left sorted by ID
535
+		$tickets_remaining = [];
536
+		// registrations that have lost their space
537
+		$ejected_registrations = [];
538
+		foreach ($registrations as $REG_ID => $registration) {
539
+			if (
540
+				$registration->status_ID() === RegStatus::APPROVED
541
+				|| apply_filters(
542
+					'FHEE__EE_SPCO_Reg_Step_Payment_Options__find_registrations_that_lost_their_space__allow_reg_payment',
543
+					false,
544
+					$registration,
545
+					$revisit
546
+				)
547
+			) {
548
+				continue;
549
+			}
550
+			$EVT_ID = $registration->event_ID();
551
+			$ticket = $registration->ticket();
552
+			if (! isset($tickets_remaining[ $ticket->ID() ])) {
553
+				$tickets_remaining[ $ticket->ID() ] = $ticket->remaining();
554
+			}
555
+			if ($tickets_remaining[ $ticket->ID() ] > 0) {
556
+				if (! isset($event_reg_count[ $EVT_ID ])) {
557
+					$event_reg_count[ $EVT_ID ] = 0;
558
+				}
559
+				$event_reg_count[ $EVT_ID ]++;
560
+				if (! isset($event_spaces_remaining[ $EVT_ID ])) {
561
+					$event_spaces_remaining[ $EVT_ID ] = $registration->event()->spaces_remaining_for_sale();
562
+				}
563
+			}
564
+			if (
565
+				$revisit
566
+				&& ($tickets_remaining[ $ticket->ID() ] === 0
567
+					|| $event_reg_count[ $EVT_ID ] > $event_spaces_remaining[ $EVT_ID ]
568
+				)
569
+			) {
570
+				$ejected_registrations[ $REG_ID ] = $registration->event();
571
+				if ($registration->status_ID() !== RegStatus::WAIT_LIST) {
572
+					/** @type EE_Registration_Processor $registration_processor */
573
+					$registration_processor = EE_Registry::instance()->load_class('Registration_Processor');
574
+					// at this point, we should have enough details about the registrant to consider the registration
575
+					// NOT incomplete
576
+					$registration_processor->manually_update_registration_status(
577
+						$registration,
578
+						RegStatus::WAIT_LIST
579
+					);
580
+				}
581
+			}
582
+		}
583
+		return $ejected_registrations;
584
+	}
585
+
586
+
587
+	/**
588
+	 * _hide_reg_step_submit_button
589
+	 * removes the html for the reg step submit button
590
+	 * by replacing it with an empty string via filter callback
591
+	 *
592
+	 * @return void
593
+	 */
594
+	protected function _adjust_registration_status_if_event_old_sold()
595
+	{
596
+	}
597
+
598
+
599
+	/**
600
+	 * _hide_reg_step_submit_button
601
+	 * removes the html for the reg step submit button
602
+	 * by replacing it with an empty string via filter callback
603
+	 *
604
+	 * @return void
605
+	 */
606
+	protected function _hide_reg_step_submit_button_if_revisit()
607
+	{
608
+		if ($this->checkout->revisit) {
609
+			add_filter('FHEE__EE_SPCO_Reg_Step__reg_step_submit_button__sbmt_btn_html', '__return_empty_string');
610
+		}
611
+	}
612
+
613
+
614
+	/**
615
+	 * sold_out_events
616
+	 * displays notices regarding events that have sold out since hte registrant first signed up
617
+	 *
618
+	 * @param EE_Event[] $sold_out_events_array
619
+	 * @return EE_Form_Section_Proper
620
+	 * @throws EE_Error
621
+	 */
622
+	private function _sold_out_events($sold_out_events_array = [])
623
+	{
624
+		// set some defaults
625
+		$this->checkout->selected_method_of_payment = 'events_sold_out';
626
+		$sold_out_events                            = '';
627
+		foreach ($sold_out_events_array as $sold_out_event) {
628
+			$sold_out_events .= EEH_HTML::li(
629
+				EEH_HTML::span(
630
+					'  ' . $sold_out_event->name(),
631
+					'',
632
+					'dashicons dashicons-marker ee-icon-size-16 pink-text'
633
+				)
634
+			);
635
+		}
636
+		return new EE_Form_Section_Proper(
637
+			[
638
+				'layout_strategy' => new EE_Template_Layout(
639
+					[
640
+						'layout_template_file' => SPCO_REG_STEPS_PATH
641
+												  . $this->_slug
642
+												  . '/sold_out_events.template.php',
643
+						'template_args'        => apply_filters(
644
+							'FHEE__EE_SPCO_Reg_Step_Payment_Options___sold_out_events__template_args',
645
+							[
646
+								'sold_out_events'     => $sold_out_events,
647
+								'sold_out_events_msg' => apply_filters(
648
+									'FHEE__EE_SPCO_Reg_Step_Payment_Options___sold_out_events__sold_out_events_msg',
649
+									sprintf(
650
+										esc_html__(
651
+											'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',
652
+											'event_espresso'
653
+										),
654
+										'<strong>',
655
+										'</strong>',
656
+										'<br />'
657
+									)
658
+								),
659
+							]
660
+						),
661
+					]
662
+				),
663
+			]
664
+		);
665
+	}
666
+
667
+
668
+	/**
669
+	 * _insufficient_spaces_available
670
+	 * displays notices regarding events that do not have enough remaining spaces
671
+	 * to satisfy the current number of registrations looking to pay
672
+	 *
673
+	 * @param EE_Event[] $insufficient_spaces_events_array
674
+	 * @return EE_Form_Section_Proper
675
+	 * @throws EE_Error
676
+	 * @throws ReflectionException
677
+	 */
678
+	private function _insufficient_spaces_available($insufficient_spaces_events_array = [])
679
+	{
680
+		// set some defaults
681
+		$this->checkout->selected_method_of_payment = 'invoice';
682
+		$insufficient_space_events                  = '';
683
+		foreach ($insufficient_spaces_events_array as $event) {
684
+			if ($event instanceof EE_Event) {
685
+				$insufficient_space_events .= EEH_HTML::li(
686
+					EEH_HTML::span(' ' . $event->name(), '', 'dashicons dashicons-marker ee-icon-size-16 pink-text')
687
+				);
688
+			}
689
+		}
690
+		return new EE_Form_Section_Proper(
691
+			[
692
+				'subsections'     => [
693
+					'default_hidden_inputs' => $this->reg_step_hidden_inputs(),
694
+					'extra_hidden_inputs'   => $this->_extra_hidden_inputs(),
695
+				],
696
+				'layout_strategy' => new EE_Template_Layout(
697
+					[
698
+						'layout_template_file' => SPCO_REG_STEPS_PATH
699
+												  . $this->_slug
700
+												  . '/sold_out_events.template.php',
701
+						'template_args'        => apply_filters(
702
+							'FHEE__EE_SPCO_Reg_Step_Payment_Options___insufficient_spaces_available__template_args',
703
+							[
704
+								'sold_out_events'     => $insufficient_space_events,
705
+								'sold_out_events_msg' => apply_filters(
706
+									'FHEE__EE_SPCO_Reg_Step_Payment_Options___insufficient_spaces_available__insufficient_space_msg',
707
+									esc_html__(
708
+										'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.',
709
+										'event_espresso'
710
+									)
711
+								),
712
+							]
713
+						),
714
+					]
715
+				),
716
+			]
717
+		);
718
+	}
719
+
720
+
721
+	/**
722
+	 * registrations_requiring_pre_approval
723
+	 *
724
+	 * @param array $registrations_requiring_pre_approval
725
+	 * @return EE_Form_Section_Proper
726
+	 * @throws EE_Error
727
+	 * @throws EntityNotFoundException
728
+	 * @throws ReflectionException
729
+	 */
730
+	private function _registrations_requiring_pre_approval($registrations_requiring_pre_approval = [])
731
+	{
732
+		$events_requiring_pre_approval = [];
733
+		foreach ($registrations_requiring_pre_approval as $registration) {
734
+			if ($registration instanceof EE_Registration && $registration->event() instanceof EE_Event) {
735
+				$events_requiring_pre_approval[ $registration->event()->ID() ] = EEH_HTML::li(
736
+					EEH_HTML::span(
737
+						'',
738
+						'',
739
+						'dashicons dashicons-marker ee-icon-size-16 orange-text'
740
+					)
741
+					. EEH_HTML::span($registration->event()->name(), '', 'orange-text')
742
+				);
743
+			}
744
+		}
745
+		return new EE_Form_Section_Proper(
746
+			[
747
+				'layout_strategy' => new EE_Template_Layout(
748
+					[
749
+						'layout_template_file' => SPCO_REG_STEPS_PATH
750
+												  . $this->_slug
751
+												  . '/events_requiring_pre_approval.template.php', // layout_template
752
+						'template_args'        => apply_filters(
753
+							'FHEE__EE_SPCO_Reg_Step_Payment_Options___sold_out_events__template_args',
754
+							[
755
+								'events_requiring_pre_approval'     => implode('', $events_requiring_pre_approval),
756
+								'events_requiring_pre_approval_msg' => apply_filters(
757
+									'FHEE__EE_SPCO_Reg_Step_Payment_Options___events_requiring_pre_approval__events_requiring_pre_approval_msg',
758
+									esc_html__(
759
+										'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.',
760
+										'event_espresso'
761
+									)
762
+								),
763
+							]
764
+						),
765
+					]
766
+				),
767
+			]
768
+		);
769
+	}
770
+
771
+
772
+	/**
773
+	 * _no_payment_required
774
+	 *
775
+	 * @param EE_Event[] $registrations_for_free_events
776
+	 * @return EE_Form_Section_Proper
777
+	 * @throws EE_Error
778
+	 */
779
+	private function _no_payment_required($registrations_for_free_events = [])
780
+	{
781
+		// set some defaults
782
+		$this->checkout->selected_method_of_payment = 'no_payment_required';
783
+		// generate no_payment_required form
784
+		return new EE_Form_Section_Proper(
785
+			[
786
+				'layout_strategy' => new EE_Template_Layout(
787
+					[
788
+						'layout_template_file' => SPCO_REG_STEPS_PATH
789
+												  . $this->_slug
790
+												  . '/no_payment_required.template.php', // layout_template
791
+						'template_args'        => apply_filters(
792
+							'FHEE__EE_SPCO_Reg_Step_Payment_Options___no_payment_required__template_args',
793
+							[
794
+								'revisit'                       => $this->checkout->revisit,
795
+								'registrations'                 => [],
796
+								'ticket_count'                  => [],
797
+								'registrations_for_free_events' => $registrations_for_free_events,
798
+								'no_payment_required_msg'       => EEH_HTML::p(
799
+									esc_html__('This is a free event, so no billing will occur.', 'event_espresso')
800
+								),
801
+							]
802
+						),
803
+					]
804
+				),
805
+			]
806
+		);
807
+	}
808
+
809
+
810
+	/**
811
+	 * _display_payment_options
812
+	 *
813
+	 * @param string $transaction_details
814
+	 * @return EE_Form_Section_Proper
815
+	 * @throws EE_Error
816
+	 * @throws InvalidArgumentException
817
+	 * @throws InvalidDataTypeException
818
+	 * @throws InvalidInterfaceException
819
+	 */
820
+	private function _display_payment_options($transaction_details = '')
821
+	{
822
+		// has method_of_payment been set by no-js user?
823
+		$this->checkout->selected_method_of_payment = $this->_get_selected_method_of_payment();
824
+		// build payment options form
825
+		return apply_filters(
826
+			'FHEE__EE_SPCO_Reg_Step_Payment_Options___display_payment_options__payment_options_form',
827
+			new EE_Form_Section_Proper(
828
+				[
829
+					'subsections'     => [
830
+						'before_payment_options' => apply_filters(
831
+							'FHEE__EE_SPCO_Reg_Step_Payment_Options___display_payment_options__before_payment_options',
832
+							new EE_Form_Section_Proper(
833
+								['layout_strategy' => new EE_Div_Per_Section_Layout()]
834
+							)
835
+						),
836
+						'payment_options'        => $this->_setup_payment_options(),
837
+						'after_payment_options'  => apply_filters(
838
+							'FHEE__EE_SPCO_Reg_Step_Payment_Options___display_payment_options__after_payment_options',
839
+							new EE_Form_Section_Proper(
840
+								['layout_strategy' => new EE_Div_Per_Section_Layout()]
841
+							)
842
+						),
843
+					],
844
+					'layout_strategy' => new EE_Template_Layout(
845
+						[
846
+							'layout_template_file' => $this->_template,
847
+							'template_args'        => apply_filters(
848
+								'FHEE__EE_SPCO_Reg_Step_Payment_Options___display_payment_options__template_args',
849
+								[
850
+									'reg_count'                 => $this->line_item_display->total_items(),
851
+									'transaction_details'       => $transaction_details,
852
+									'available_payment_methods' => [],
853
+								]
854
+							),
855
+						]
856
+					),
857
+				]
858
+			)
859
+		);
860
+	}
861
+
862
+
863
+	/**
864
+	 * _extra_hidden_inputs
865
+	 *
866
+	 * @param bool $no_payment_required
867
+	 * @return EE_Form_Section_Proper
868
+	 * @throws EE_Error
869
+	 * @throws ReflectionException
870
+	 */
871
+	private function _extra_hidden_inputs($no_payment_required = true)
872
+	{
873
+		return new EE_Form_Section_Proper(
874
+			[
875
+				'html_id'         => 'ee-' . $this->slug() . '-extra-hidden-inputs',
876
+				'layout_strategy' => new EE_Div_Per_Section_Layout(),
877
+				'subsections'     => [
878
+					'spco_no_payment_required' => new EE_Hidden_Input(
879
+						[
880
+							'normalization_strategy' => new EE_Boolean_Normalization(),
881
+							'html_name'              => 'spco_no_payment_required',
882
+							'html_id'                => 'spco-no-payment-required-payment_options',
883
+							'default'                => $no_payment_required,
884
+						]
885
+					),
886
+					'spco_transaction_id'      => new EE_Fixed_Hidden_Input(
887
+						[
888
+							'normalization_strategy' => new EE_Int_Normalization(),
889
+							'html_name'              => 'spco_transaction_id',
890
+							'html_id'                => 'spco-transaction-id',
891
+							'default'                => $this->checkout->transaction->ID(),
892
+						]
893
+					),
894
+				],
895
+			]
896
+		);
897
+	}
898
+
899
+
900
+	/**
901
+	 *    _apply_registration_payments_to_amount_owing
902
+	 *
903
+	 * @param array $registrations
904
+	 * @throws EE_Error
905
+	 */
906
+	protected function _apply_registration_payments_to_amount_owing(array $registrations)
907
+	{
908
+		$payments = [];
909
+		foreach ($registrations as $registration) {
910
+			if ($registration instanceof EE_Registration && $registration->owes_monies_and_can_pay()) {
911
+				$payments += $registration->registration_payments();
912
+			}
913
+		}
914
+		if (! empty($payments)) {
915
+			foreach ($payments as $payment) {
916
+				if ($payment instanceof EE_Registration_Payment) {
917
+					$this->checkout->amount_owing -= $payment->amount();
918
+				}
919
+			}
920
+		}
921
+	}
922
+
923
+
924
+	/**
925
+	 *    _reset_selected_method_of_payment
926
+	 *
927
+	 * @access    private
928
+	 * @param bool $force_reset
929
+	 * @return void
930
+	 * @throws InvalidArgumentException
931
+	 * @throws InvalidDataTypeException
932
+	 * @throws InvalidInterfaceException
933
+	 */
934
+	private function _reset_selected_method_of_payment($force_reset = false)
935
+	{
936
+		/** @var RequestInterface $request */
937
+		$request              = LoaderFactory::getLoader()->getShared(RequestInterface::class);
938
+		$reset_payment_method = $request->getRequestParam('reset_payment_method', $force_reset, 'bool');
939
+		if ($reset_payment_method) {
940
+			$this->checkout->selected_method_of_payment = null;
941
+			$this->checkout->payment_method             = null;
942
+			$this->checkout->billing_form               = null;
943
+			$this->_save_selected_method_of_payment();
944
+		}
945
+	}
946
+
947
+
948
+	/**
949
+	 * _save_selected_method_of_payment
950
+	 * stores the selected_method_of_payment in the session
951
+	 * so that it's available for all subsequent requests including AJAX
952
+	 *
953
+	 * @access        private
954
+	 * @param string $selected_method_of_payment
955
+	 * @return void
956
+	 * @throws InvalidArgumentException
957
+	 * @throws InvalidDataTypeException
958
+	 * @throws InvalidInterfaceException
959
+	 */
960
+	private function _save_selected_method_of_payment($selected_method_of_payment = '')
961
+	{
962
+		$selected_method_of_payment = ! empty($selected_method_of_payment)
963
+			? $selected_method_of_payment
964
+			: $this->checkout->selected_method_of_payment;
965
+		EE_Registry::instance()->SSN->set_session_data(
966
+			['selected_method_of_payment' => $selected_method_of_payment]
967
+		);
968
+	}
969
+
970
+
971
+	/**
972
+	 * _setup_payment_options
973
+	 *
974
+	 * @return EE_Form_Section_Proper
975
+	 * @throws EE_Error
976
+	 * @throws InvalidArgumentException
977
+	 * @throws InvalidDataTypeException
978
+	 * @throws InvalidInterfaceException
979
+	 */
980
+	public function _setup_payment_options()
981
+	{
982
+		// load payment method classes
983
+		$this->checkout->available_payment_methods = $this->_get_available_payment_methods();
984
+		if (empty($this->checkout->available_payment_methods)) {
985
+			EE_Error::add_error(
986
+				apply_filters(
987
+					'FHEE__EE_SPCO_Reg_Step_Payment_Options___setup_payment_options__error_message_no_payment_methods',
988
+					sprintf(
989
+						esc_html__(
990
+							'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.',
991
+							'event_espresso'
992
+						),
993
+						'<br>',
994
+						EE_Registry::instance()->CFG->organization->get_pretty('email')
995
+					)
996
+				),
997
+				__FILE__,
998
+				__FUNCTION__,
999
+				__LINE__
1000
+			);
1001
+		}
1002
+		// switch up header depending on number of available payment methods
1003
+		$payment_method_header     = count($this->checkout->available_payment_methods) > 1
1004
+			? apply_filters(
1005
+				'FHEE__registration_page_payment_options__method_of_payment_hdr',
1006
+				esc_html__('Please Select Your Method of Payment', 'event_espresso')
1007
+			)
1008
+			: apply_filters(
1009
+				'FHEE__registration_page_payment_options__method_of_payment_hdr',
1010
+				esc_html__('Method of Payment', 'event_espresso')
1011
+			);
1012
+		$available_payment_methods = [
1013
+			// display the "Payment Method" header
1014
+			'payment_method_header' => new EE_Form_Section_HTML(
1015
+				apply_filters(
1016
+					'FHEE__EE_SPCO_Reg_Step_Payment_Options___setup_payment_options__payment_method_header',
1017
+					EEH_HTML::h4($payment_method_header, 'method-of-payment-hdr'),
1018
+					$payment_method_header
1019
+				)
1020
+			),
1021
+		];
1022
+		// the list of actual payment methods ( invoice, paypal, etc ) in a  ( slug => HTML )  format
1023
+		$available_payment_method_options = [];
1024
+		$default_payment_method_option    = [];
1025
+		// additional instructions to be displayed and hidden below payment methods (adding a clearing div to start)
1026
+		$payment_methods_billing_info = [
1027
+			new EE_Form_Section_HTML(
1028
+				EEH_HTML::div('<br />', '', '', 'clear:both;')
1029
+			),
1030
+		];
1031
+		// loop through payment methods
1032
+		foreach ($this->checkout->available_payment_methods as $payment_method) {
1033
+			if ($payment_method instanceof EE_Payment_Method) {
1034
+				$payment_method_button = EEH_HTML::img(
1035
+					$payment_method->button_url(),
1036
+					$payment_method->name(),
1037
+					'spco-payment-method-' . $payment_method->slug() . '-btn-img',
1038
+					'spco-payment-method-btn-img'
1039
+				);
1040
+				// check if any payment methods are set as default
1041
+				// if payment method is already selected OR nothing is selected and this payment method should be
1042
+				// open_by_default
1043
+				if (
1044
+					($this->checkout->selected_method_of_payment === $payment_method->slug())
1045
+					|| (! $this->checkout->selected_method_of_payment && $payment_method->open_by_default())
1046
+				) {
1047
+					$this->checkout->selected_method_of_payment = $payment_method->slug();
1048
+					$this->_save_selected_method_of_payment();
1049
+					$default_payment_method_option[ $payment_method->slug() ] = $payment_method_button;
1050
+				} else {
1051
+					$available_payment_method_options[ $payment_method->slug() ] = $payment_method_button;
1052
+				}
1053
+				$payment_methods_billing_info[ $payment_method->slug() . '-info' ] =
1054
+					$this->_payment_method_billing_info(
1055
+						$payment_method
1056
+					);
1057
+			}
1058
+		}
1059
+		// prepend available_payment_method_options with default_payment_method_option so that it appears first in list
1060
+		// of PMs
1061
+		$available_payment_method_options = $default_payment_method_option + $available_payment_method_options;
1062
+		// now generate the actual form  inputs
1063
+		$available_payment_methods['available_payment_methods'] = $this->_available_payment_method_inputs(
1064
+			$available_payment_method_options
1065
+		);
1066
+		$available_payment_methods                              += $payment_methods_billing_info;
1067
+		// build the available payment methods form
1068
+		return new EE_Form_Section_Proper(
1069
+			[
1070
+				'html_id'         => 'spco-available-methods-of-payment-dv',
1071
+				'subsections'     => $available_payment_methods,
1072
+				'layout_strategy' => new EE_Div_Per_Section_Layout(),
1073
+			]
1074
+		);
1075
+	}
1076
+
1077
+
1078
+	/**
1079
+	 * _get_available_payment_methods
1080
+	 *
1081
+	 * @return EE_Payment_Method[]
1082
+	 * @throws EE_Error
1083
+	 * @throws InvalidArgumentException
1084
+	 * @throws InvalidDataTypeException
1085
+	 * @throws InvalidInterfaceException
1086
+	 */
1087
+	protected function _get_available_payment_methods()
1088
+	{
1089
+		if (! empty($this->checkout->available_payment_methods)) {
1090
+			return $this->checkout->available_payment_methods;
1091
+		}
1092
+		$available_payment_methods = [];
1093
+		$EEM_Payment_Method        = EEM_Payment_Method::instance();
1094
+		// get all active payment methods
1095
+		$payment_methods = $EEM_Payment_Method->get_all_for_transaction(
1096
+			$this->checkout->transaction,
1097
+			EEM_Payment_Method::scope_cart
1098
+		);
1099
+		foreach ($payment_methods as $payment_method) {
1100
+			if ($payment_method instanceof EE_Payment_Method) {
1101
+				$available_payment_methods[ $payment_method->slug() ] = $payment_method;
1102
+			}
1103
+		}
1104
+		return $available_payment_methods;
1105
+	}
1106
+
1107
+
1108
+	/**
1109
+	 *    _available_payment_method_inputs
1110
+	 *
1111
+	 * @access    private
1112
+	 * @param array $available_payment_method_options
1113
+	 * @return    EE_Form_Section_Proper
1114
+	 * @throws EE_Error
1115
+	 * @throws EE_Error
1116
+	 */
1117
+	private function _available_payment_method_inputs($available_payment_method_options = [])
1118
+	{
1119
+		// generate inputs
1120
+		return new EE_Form_Section_Proper(
1121
+			[
1122
+				'html_id'         => 'ee-available-payment-method-inputs',
1123
+				'layout_strategy' => new EE_Div_Per_Section_Layout(),
1124
+				'subsections'     => [
1125
+					'' => new EE_Radio_Button_Input(
1126
+						$available_payment_method_options,
1127
+						[
1128
+							'html_name'          => 'selected_method_of_payment',
1129
+							'html_class'         => 'spco-payment-method',
1130
+							'default'            => $this->checkout->selected_method_of_payment,
1131
+							'label_size'         => 11,
1132
+							'enforce_label_size' => true,
1133
+						]
1134
+					),
1135
+				],
1136
+			]
1137
+		);
1138
+	}
1139
+
1140
+
1141
+	/**
1142
+	 *    _payment_method_billing_info
1143
+	 *
1144
+	 * @access    private
1145
+	 * @param EE_Payment_Method $payment_method
1146
+	 * @return EE_Form_Section_Proper
1147
+	 * @throws EE_Error
1148
+	 * @throws InvalidArgumentException
1149
+	 * @throws InvalidDataTypeException
1150
+	 * @throws InvalidInterfaceException
1151
+	 */
1152
+	private function _payment_method_billing_info(EE_Payment_Method $payment_method)
1153
+	{
1154
+		$currently_selected = $this->checkout->selected_method_of_payment === $payment_method->slug();
1155
+		// generate the billing form for payment method
1156
+		$billing_form                 = $currently_selected
1157
+			? $this->_get_billing_form_for_payment_method($payment_method)
1158
+			: new EE_Form_Section_HTML();
1159
+		$this->checkout->billing_form = $currently_selected
1160
+			? $billing_form
1161
+			: $this->checkout->billing_form;
1162
+		// it's all in the details
1163
+		$info_html = EEH_HTML::h3(
1164
+			esc_html__('Important information regarding your payment', 'event_espresso'),
1165
+			'',
1166
+			'spco-payment-method-hdr'
1167
+		);
1168
+		// add some info regarding the step, either from what's saved in the admin,
1169
+		// or a default string depending on whether the PM has a billing form or not
1170
+		if ($payment_method->description()) {
1171
+			$payment_method_info = $payment_method->description();
1172
+		} elseif ($billing_form instanceof EE_Billing_Info_Form) {
1173
+			$payment_method_info = sprintf(
1174
+				esc_html__(
1175
+					'Please provide the following billing information, then click the "%1$s" button below in order to proceed.',
1176
+					'event_espresso'
1177
+				),
1178
+				$this->submit_button_text()
1179
+			);
1180
+		} else {
1181
+			$payment_method_info = sprintf(
1182
+				esc_html__('Please click the "%1$s" button below in order to proceed.', 'event_espresso'),
1183
+				$this->submit_button_text()
1184
+			);
1185
+		}
1186
+		$info_html .= EEH_HTML::div(
1187
+			apply_filters(
1188
+				'FHEE__EE_SPCO_Reg_Step_Payment_Options___payment_method_billing_info__payment_method_info',
1189
+				$payment_method_info
1190
+			),
1191
+			'',
1192
+			'spco-payment-method-desc ee-attention'
1193
+		);
1194
+		return new EE_Form_Section_Proper(
1195
+			[
1196
+				'html_id'         => 'spco-payment-method-info-' . $payment_method->slug(),
1197
+				'html_class'      => 'spco-payment-method-info-dv',
1198
+				// only display the selected or default PM
1199
+				'html_style'      => $currently_selected ? '' : 'display:none;',
1200
+				'layout_strategy' => new EE_Div_Per_Section_Layout(),
1201
+				'subsections'     => [
1202
+					'info'         => new EE_Form_Section_HTML($info_html),
1203
+					'billing_form' => $currently_selected ? $billing_form : new EE_Form_Section_HTML(),
1204
+				],
1205
+			]
1206
+		);
1207
+	}
1208
+
1209
+
1210
+	/**
1211
+	 * get_billing_form_html_for_payment_method
1212
+	 *
1213
+	 * @return bool
1214
+	 * @throws EE_Error
1215
+	 * @throws InvalidArgumentException
1216
+	 * @throws ReflectionException
1217
+	 * @throws InvalidDataTypeException
1218
+	 * @throws InvalidInterfaceException
1219
+	 */
1220
+	public function get_billing_form_html_for_payment_method()
1221
+	{
1222
+		// how have they chosen to pay?
1223
+		$this->checkout->selected_method_of_payment = $this->_get_selected_method_of_payment(true);
1224
+		$this->checkout->payment_method             = $this->_get_payment_method_for_selected_method_of_payment();
1225
+		if (! $this->checkout->payment_method instanceof EE_Payment_Method) {
1226
+			return false;
1227
+		}
1228
+		if (
1229
+			apply_filters(
1230
+				'FHEE__EE_SPCO_Reg_Step_Payment_Options__registration_checkout__selected_payment_method__display_success',
1231
+				false
1232
+			)
1233
+		) {
1234
+			EE_Error::add_success(
1235
+				apply_filters(
1236
+					'FHEE__Single_Page_Checkout__registration_checkout__selected_payment_method',
1237
+					sprintf(
1238
+						esc_html__(
1239
+							'You have selected "%s" as your method of payment. Please note the important payment information below.',
1240
+							'event_espresso'
1241
+						),
1242
+						$this->checkout->payment_method->name()
1243
+					)
1244
+				)
1245
+			);
1246
+		}
1247
+		// now generate billing form for selected method of payment
1248
+		$payment_method_billing_form = $this->_get_billing_form_for_payment_method($this->checkout->payment_method);
1249
+		// fill form with attendee info if applicable
1250
+		if (
1251
+			$payment_method_billing_form instanceof EE_Billing_Attendee_Info_Form
1252
+			&& $this->checkout->transaction_has_primary_registrant()
1253
+		) {
1254
+			$payment_method_billing_form->populate_from_attendee(
1255
+				$this->checkout->transaction->primary_registration()->attendee()
1256
+			);
1257
+		}
1258
+		// and debug content
1259
+		if (
1260
+			$payment_method_billing_form instanceof EE_Billing_Info_Form
1261
+			&& $this->checkout->payment_method->type_obj() instanceof EE_PMT_Base
1262
+		) {
1263
+			$payment_method_billing_form =
1264
+				$this->checkout->payment_method->type_obj()->apply_billing_form_debug_settings(
1265
+					$payment_method_billing_form
1266
+				);
1267
+		}
1268
+		$billing_info = $payment_method_billing_form instanceof EE_Form_Section_Proper
1269
+			? $payment_method_billing_form->get_html()
1270
+			: '';
1271
+		$this->checkout->json_response->set_return_data(['payment_method_info' => $billing_info]);
1272
+		// localize validation rules for main form
1273
+		$this->checkout->current_step->reg_form->localize_validation_rules();
1274
+		$this->checkout->json_response->add_validation_rules(EE_Form_Section_Proper::js_localization());
1275
+		return true;
1276
+	}
1277
+
1278
+
1279
+	/**
1280
+	 * _get_billing_form_for_payment_method
1281
+	 *
1282
+	 * @param EE_Payment_Method $payment_method
1283
+	 * @return EE_Billing_Info_Form|EE_Billing_Attendee_Info_Form|EE_Form_Section_HTML
1284
+	 * @throws EE_Error
1285
+	 * @throws InvalidArgumentException
1286
+	 * @throws InvalidDataTypeException
1287
+	 * @throws InvalidInterfaceException
1288
+	 */
1289
+	private function _get_billing_form_for_payment_method(EE_Payment_Method $payment_method)
1290
+	{
1291
+		$billing_form = $payment_method->type_obj()->billing_form(
1292
+			$this->checkout->transaction,
1293
+			['amount_owing' => $this->checkout->amount_owing]
1294
+		);
1295
+		if ($billing_form instanceof EE_Billing_Info_Form) {
1296
+			if (
1297
+				apply_filters(
1298
+					'FHEE__EE_SPCO_Reg_Step_Payment_Options__registration_checkout__selected_payment_method__display_success',
1299
+					false
1300
+				)
1301
+				&& $this->request->requestParamIsSet('payment_method')
1302
+			) {
1303
+				EE_Error::add_success(
1304
+					apply_filters(
1305
+						'FHEE__Single_Page_Checkout__registration_checkout__selected_payment_method',
1306
+						sprintf(
1307
+							esc_html__(
1308
+								'You have selected "%s" as your method of payment. Please note the important payment information below.',
1309
+								'event_espresso'
1310
+							),
1311
+							$payment_method->name()
1312
+						)
1313
+					)
1314
+				);
1315
+			}
1316
+			return apply_filters(
1317
+				'FHEE__EE_SPCO_Reg_Step_Payment_Options___get_billing_form_for_payment_method__billing_form',
1318
+				$billing_form,
1319
+				$payment_method
1320
+			);
1321
+		}
1322
+		// no actual billing form, so return empty HTML form section
1323
+		return new EE_Form_Section_HTML();
1324
+	}
1325
+
1326
+
1327
+	/**
1328
+	 * _get_selected_method_of_payment
1329
+	 *
1330
+	 * @param boolean $required whether to throw an error if the "selected_method_of_payment"
1331
+	 *                          is not found in the incoming request
1332
+	 * @param string  $request_param
1333
+	 * @return NULL|string
1334
+	 * @throws EE_Error
1335
+	 * @throws InvalidArgumentException
1336
+	 * @throws InvalidDataTypeException
1337
+	 * @throws InvalidInterfaceException
1338
+	 */
1339
+	private function _get_selected_method_of_payment(
1340
+		$required = false,
1341
+		$request_param = 'selected_method_of_payment'
1342
+	) {
1343
+		// is selected_method_of_payment set in the request ?
1344
+		$selected_method_of_payment = $this->request->getRequestParam($request_param);
1345
+		if ($selected_method_of_payment) {
1346
+			// sanitize it
1347
+			$selected_method_of_payment = is_array($selected_method_of_payment)
1348
+				? array_shift($selected_method_of_payment)
1349
+				: $selected_method_of_payment;
1350
+			$selected_method_of_payment = sanitize_text_field($selected_method_of_payment);
1351
+			// store it in the session so that it's available for all subsequent requests including AJAX
1352
+			$this->_save_selected_method_of_payment($selected_method_of_payment);
1353
+		} else {
1354
+			// or is is set in the session ?
1355
+			$selected_method_of_payment = EE_Registry::instance()->SSN->get_session_data(
1356
+				'selected_method_of_payment'
1357
+			);
1358
+		}
1359
+		// do ya really really gotta have it?
1360
+		if (empty($selected_method_of_payment) && $required) {
1361
+			EE_Error::add_error(
1362
+				sprintf(
1363
+					esc_html__(
1364
+						'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.',
1365
+						'event_espresso'
1366
+					),
1367
+					'<br/>',
1368
+					'<br/>',
1369
+					EE_Registry::instance()->CFG->organization->get_pretty('email')
1370
+				),
1371
+				__FILE__,
1372
+				__FUNCTION__,
1373
+				__LINE__
1374
+			);
1375
+			return null;
1376
+		}
1377
+		return $selected_method_of_payment;
1378
+	}
1379
+
1380
+
1381
+
1382
+
1383
+
1384
+
1385
+	/********************************************************************************************************/
1386
+	/***********************************  SWITCH PAYMENT METHOD  ************************************/
1387
+	/********************************************************************************************************/
1388
+	/**
1389
+	 * switch_payment_method
1390
+	 *
1391
+	 * @return bool
1392
+	 * @throws EE_Error
1393
+	 * @throws InvalidArgumentException
1394
+	 * @throws InvalidDataTypeException
1395
+	 * @throws InvalidInterfaceException
1396
+	 * @throws ReflectionException
1397
+	 */
1398
+	public function switch_payment_method()
1399
+	{
1400
+		if (! $this->_verify_payment_method_is_set()) {
1401
+			return false;
1402
+		}
1403
+		if (
1404
+			apply_filters(
1405
+				'FHEE__EE_SPCO_Reg_Step_Payment_Options__registration_checkout__selected_payment_method__display_success',
1406
+				false
1407
+			)
1408
+		) {
1409
+			EE_Error::add_success(
1410
+				apply_filters(
1411
+					'FHEE__Single_Page_Checkout__registration_checkout__selected_payment_method',
1412
+					sprintf(
1413
+						esc_html__(
1414
+							'You have selected "%s" as your method of payment. Please note the important payment information below.',
1415
+							'event_espresso'
1416
+						),
1417
+						$this->checkout->payment_method->name()
1418
+					)
1419
+				)
1420
+			);
1421
+		}
1422
+		// generate billing form for selected method of payment if it hasn't been done already
1423
+		if ($this->checkout->payment_method->type_obj()->has_billing_form()) {
1424
+			$this->checkout->billing_form = $this->_get_billing_form_for_payment_method(
1425
+				$this->checkout->payment_method
1426
+			);
1427
+		}
1428
+		// fill form with attendee info if applicable
1429
+		if (
1430
+			apply_filters(
1431
+				'FHEE__populate_billing_form_fields_from_attendee',
1432
+				(
1433
+					$this->checkout->billing_form instanceof EE_Billing_Attendee_Info_Form
1434
+					&& $this->checkout->transaction_has_primary_registrant()
1435
+				),
1436
+				$this->checkout->billing_form,
1437
+				$this->checkout->transaction
1438
+			)
1439
+		) {
1440
+			$this->checkout->billing_form->populate_from_attendee(
1441
+				$this->checkout->transaction->primary_registration()->attendee()
1442
+			);
1443
+		}
1444
+		// and debug content
1445
+		if (
1446
+			$this->checkout->billing_form instanceof EE_Billing_Info_Form
1447
+			&& $this->checkout->payment_method->type_obj() instanceof EE_PMT_Base
1448
+		) {
1449
+			$this->checkout->billing_form =
1450
+				$this->checkout->payment_method->type_obj()->apply_billing_form_debug_settings(
1451
+					$this->checkout->billing_form
1452
+				);
1453
+		}
1454
+		// get html and validation rules for form
1455
+		if ($this->checkout->billing_form instanceof EE_Form_Section_Proper) {
1456
+			$this->checkout->json_response->set_return_data(
1457
+				['payment_method_info' => $this->checkout->billing_form->get_html()]
1458
+			);
1459
+			// localize validation rules for main form
1460
+			$this->checkout->billing_form->localize_validation_rules(true);
1461
+			$this->checkout->json_response->add_validation_rules(EE_Form_Section_Proper::js_localization());
1462
+		} else {
1463
+			$this->checkout->json_response->set_return_data(['payment_method_info' => '']);
1464
+		}
1465
+		// prevents advancement to next step
1466
+		$this->checkout->continue_reg = false;
1467
+		return true;
1468
+	}
1469
+
1470
+
1471
+	/**
1472
+	 * _verify_payment_method_is_set
1473
+	 *
1474
+	 * @return bool
1475
+	 * @throws EE_Error
1476
+	 * @throws InvalidArgumentException
1477
+	 * @throws ReflectionException
1478
+	 * @throws InvalidDataTypeException
1479
+	 * @throws InvalidInterfaceException
1480
+	 */
1481
+	protected function _verify_payment_method_is_set()
1482
+	{
1483
+		// generate billing form for selected method of payment if it hasn't been done already
1484
+		if (empty($this->checkout->selected_method_of_payment)) {
1485
+			// how have they chosen to pay?
1486
+			$this->checkout->selected_method_of_payment = $this->_get_selected_method_of_payment(true);
1487
+		} else {
1488
+			// choose your own adventure based on method_of_payment
1489
+			switch ($this->checkout->selected_method_of_payment) {
1490
+				case 'events_sold_out':
1491
+					EE_Error::add_attention(
1492
+						apply_filters(
1493
+							'FHEE__EE_SPCO_Reg_Step_Payment_Options___verify_payment_method_is_set__sold_out_events_msg',
1494
+							esc_html__(
1495
+								'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.',
1496
+								'event_espresso'
1497
+							)
1498
+						),
1499
+						__FILE__,
1500
+						__FUNCTION__,
1501
+						__LINE__
1502
+					);
1503
+					return false;
1504
+				case 'payments_closed':
1505
+					EE_Error::add_attention(
1506
+						apply_filters(
1507
+							'FHEE__EE_SPCO_Reg_Step_Payment_Options___verify_payment_method_is_set__payments_closed_msg',
1508
+							esc_html__(
1509
+								'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.',
1510
+								'event_espresso'
1511
+							)
1512
+						),
1513
+						__FILE__,
1514
+						__FUNCTION__,
1515
+						__LINE__
1516
+					);
1517
+					return false;
1518
+				case 'no_payment_required':
1519
+					EE_Error::add_attention(
1520
+						apply_filters(
1521
+							'FHEE__EE_SPCO_Reg_Step_Payment_Options___verify_payment_method_is_set__no_payment_required_msg',
1522
+							esc_html__(
1523
+								'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.',
1524
+								'event_espresso'
1525
+							)
1526
+						),
1527
+						__FILE__,
1528
+						__FUNCTION__,
1529
+						__LINE__
1530
+					);
1531
+					return false;
1532
+				default:
1533
+			}
1534
+		}
1535
+		// verify payment method
1536
+		if (! $this->checkout->payment_method instanceof EE_Payment_Method) {
1537
+			// get payment method for selected method of payment
1538
+			$this->checkout->payment_method = $this->_get_payment_method_for_selected_method_of_payment();
1539
+		}
1540
+		return $this->checkout->payment_method instanceof EE_Payment_Method;
1541
+	}
1542
+
1543
+
1544
+
1545
+	/********************************************************************************************************/
1546
+	/***************************************  SAVE PAYER DETAILS  ****************************************/
1547
+	/********************************************************************************************************/
1548
+	/**
1549
+	 * save_payer_details_via_ajax
1550
+	 *
1551
+	 * @return void
1552
+	 * @throws EE_Error
1553
+	 * @throws InvalidArgumentException
1554
+	 * @throws ReflectionException
1555
+	 * @throws RuntimeException
1556
+	 * @throws InvalidDataTypeException
1557
+	 * @throws InvalidInterfaceException
1558
+	 */
1559
+	public function save_payer_details_via_ajax()
1560
+	{
1561
+		if (! $this->_verify_payment_method_is_set()) {
1562
+			return;
1563
+		}
1564
+		// generate billing form for selected method of payment if it hasn't been done already
1565
+		if ($this->checkout->payment_method->type_obj()->has_billing_form()) {
1566
+			$this->checkout->billing_form = $this->_get_billing_form_for_payment_method(
1567
+				$this->checkout->payment_method
1568
+			);
1569
+		}
1570
+		// generate primary attendee from payer info if applicable
1571
+		if (! $this->checkout->transaction_has_primary_registrant()) {
1572
+			$attendee = $this->_create_attendee_from_request_data();
1573
+			if ($attendee instanceof EE_Attendee) {
1574
+				foreach ($this->checkout->transaction->registrations() as $registration) {
1575
+					if ($registration->is_primary_registrant()) {
1576
+						$this->checkout->primary_attendee_obj = $attendee;
1577
+						$registration->_add_relation_to($attendee, 'Attendee');
1578
+						$registration->set_attendee_id($attendee->ID());
1579
+						$registration->update_cache_after_object_save('Attendee', $attendee);
1580
+					}
1581
+				}
1582
+			}
1583
+		}
1584
+	}
1585
+
1586
+
1587
+	/**
1588
+	 * create_attendee_from_request_data
1589
+	 * uses info from alternate GET or POST data (such as AJAX) to create a new attendee
1590
+	 *
1591
+	 * @return EE_Attendee
1592
+	 * @throws EE_Error
1593
+	 * @throws InvalidArgumentException
1594
+	 * @throws ReflectionException
1595
+	 * @throws InvalidDataTypeException
1596
+	 * @throws InvalidInterfaceException
1597
+	 */
1598
+	protected function _create_attendee_from_request_data()
1599
+	{
1600
+		// get State ID
1601
+		$STA_ID = $this->request->getRequestParam('state');
1602
+		if (! empty($STA_ID)) {
1603
+			// can we get state object from name ?
1604
+			EE_Registry::instance()->load_model('State');
1605
+			$state  = EEM_State::instance()->get_col([['STA_name' => $STA_ID], 'limit' => 1], 'STA_ID');
1606
+			$STA_ID = is_array($state) && ! empty($state) ? reset($state) : $STA_ID;
1607
+		}
1608
+		// get Country ISO
1609
+		$CNT_ISO = $this->request->getRequestParam('country');
1610
+		if (! empty($CNT_ISO)) {
1611
+			// can we get country object from name ?
1612
+			EE_Registry::instance()->load_model('Country');
1613
+			$country = EEM_Country::instance()->get_col(
1614
+				[['CNT_name' => $CNT_ISO], 'limit' => 1],
1615
+				'CNT_ISO'
1616
+			);
1617
+			$CNT_ISO = is_array($country) && ! empty($country) ? reset($country) : $CNT_ISO;
1618
+		}
1619
+		// grab attendee data
1620
+		$attendee_data = [
1621
+			'ATT_fname'    => $this->request->getRequestParam('first_name'),
1622
+			'ATT_lname'    => $this->request->getRequestParam('last_name'),
1623
+			'ATT_email'    => $this->request->getRequestParam('email'),
1624
+			'ATT_address'  => $this->request->getRequestParam('address'),
1625
+			'ATT_address2' => $this->request->getRequestParam('address2'),
1626
+			'ATT_city'     => $this->request->getRequestParam('city'),
1627
+			'STA_ID'       => $STA_ID,
1628
+			'CNT_ISO'      => $CNT_ISO,
1629
+			'ATT_zip'      => $this->request->getRequestParam('zip'),
1630
+			'ATT_phone'    => $this->request->getRequestParam('phone'),
1631
+		];
1632
+		// validate the email address since it is the most important piece of info
1633
+		if (empty($attendee_data['ATT_email'])) {
1634
+			EE_Error::add_error(
1635
+				esc_html__('An invalid email address was submitted.', 'event_espresso'),
1636
+				__FILE__,
1637
+				__FUNCTION__,
1638
+				__LINE__
1639
+			);
1640
+		}
1641
+		// does this attendee already exist in the db ? we're searching using a combination of first name, last name,
1642
+		// AND email address
1643
+		if (
1644
+			! empty($attendee_data['ATT_fname'])
1645
+			&& ! empty($attendee_data['ATT_lname'])
1646
+			&& ! empty($attendee_data['ATT_email'])
1647
+		) {
1648
+			$existing_attendee = EEM_Attendee::instance()->find_existing_attendee(
1649
+				[
1650
+					'ATT_fname' => $attendee_data['ATT_fname'],
1651
+					'ATT_lname' => $attendee_data['ATT_lname'],
1652
+					'ATT_email' => $attendee_data['ATT_email'],
1653
+				]
1654
+			);
1655
+			if ($existing_attendee instanceof EE_Attendee) {
1656
+				return $existing_attendee;
1657
+			}
1658
+		}
1659
+		// no existing attendee? kk let's create a new one
1660
+		// kinda lame, but we need a first and last name to create an attendee, so use the email address if those
1661
+		// don't exist
1662
+		$attendee_data['ATT_fname'] = ! empty($attendee_data['ATT_fname'])
1663
+			? $attendee_data['ATT_fname']
1664
+			: $attendee_data['ATT_email'];
1665
+		$attendee_data['ATT_lname'] = ! empty($attendee_data['ATT_lname'])
1666
+			? $attendee_data['ATT_lname']
1667
+			: $attendee_data['ATT_email'];
1668
+		return EE_Attendee::new_instance($attendee_data);
1669
+	}
1670
+
1671
+
1672
+
1673
+	/********************************************************************************************************/
1674
+	/****************************************  PROCESS REG STEP  *****************************************/
1675
+	/********************************************************************************************************/
1676
+	/**
1677
+	 * process_reg_step
1678
+	 *
1679
+	 * @return bool
1680
+	 * @throws EE_Error
1681
+	 * @throws InvalidArgumentException
1682
+	 * @throws ReflectionException
1683
+	 * @throws EntityNotFoundException
1684
+	 * @throws InvalidDataTypeException
1685
+	 * @throws InvalidInterfaceException
1686
+	 * @throws InvalidStatusException
1687
+	 */
1688
+	public function process_reg_step()
1689
+	{
1690
+		// how have they chosen to pay?
1691
+		$this->checkout->selected_method_of_payment = $this->checkout->transaction->is_free()
1692
+			? 'no_payment_required'
1693
+			: $this->_get_selected_method_of_payment(true);
1694
+		// choose your own adventure based on method_of_payment
1695
+		switch ($this->checkout->selected_method_of_payment) {
1696
+			case 'events_sold_out':
1697
+				$this->checkout->redirect     = true;
1698
+				$this->checkout->redirect_url = $this->checkout->cancel_page_url;
1699
+				$this->checkout->json_response->set_redirect_url($this->checkout->redirect_url);
1700
+				// mark this reg step as completed
1701
+				$this->set_completed();
1702
+				return false;
1703
+
1704
+			case 'payments_closed':
1705
+				if (
1706
+					apply_filters(
1707
+						'FHEE__EE_SPCO_Reg_Step_Payment_Options__process_reg_step__payments_closed__display_success',
1708
+						false
1709
+					)
1710
+				) {
1711
+					EE_Error::add_success(
1712
+						esc_html__('no payment required at this time.', 'event_espresso'),
1713
+						__FILE__,
1714
+						__FUNCTION__,
1715
+						__LINE__
1716
+					);
1717
+				}
1718
+				// mark this reg step as completed
1719
+				$this->set_completed();
1720
+				return true;
1721
+
1722
+			case 'no_payment_required':
1723
+				if (
1724
+					apply_filters(
1725
+						'FHEE__EE_SPCO_Reg_Step_Payment_Options__process_reg_step__no_payment_required__display_success',
1726
+						false
1727
+					)
1728
+				) {
1729
+					EE_Error::add_success(
1730
+						esc_html__('no payment required.', 'event_espresso'),
1731
+						__FILE__,
1732
+						__FUNCTION__,
1733
+						__LINE__
1734
+					);
1735
+				}
1736
+				// mark this reg step as completed
1737
+				$this->set_completed();
1738
+				return true;
1739
+
1740
+			default:
1741
+				$registrations         = EE_Registry::instance()->SSN->checkout()->transaction->registrations(
1742
+					EE_Registry::instance()->SSN->checkout()->reg_cache_where_params
1743
+				);
1744
+				$ejected_registrations = EE_SPCO_Reg_Step_Payment_Options::find_registrations_that_lost_their_space(
1745
+					$registrations,
1746
+					EE_Registry::instance()->SSN->checkout()->revisit
1747
+				);
1748
+				// calculate difference between the two arrays
1749
+				$registrations = array_diff($registrations, $ejected_registrations);
1750
+				if (empty($registrations)) {
1751
+					$this->_redirect_because_event_sold_out();
1752
+					return false;
1753
+				}
1754
+				$payment = $this->_process_payment();
1755
+				if ($payment instanceof EE_Payment) {
1756
+					$this->checkout->continue_reg = true;
1757
+					$this->_maybe_set_completed($payment);
1758
+				} else {
1759
+					$this->checkout->continue_reg = false;
1760
+				}
1761
+				return $payment instanceof EE_Payment;
1762
+		}
1763
+	}
1764
+
1765
+
1766
+	/**
1767
+	 * _redirect_because_event_sold_out
1768
+	 *
1769
+	 * @return void
1770
+	 */
1771
+	protected function _redirect_because_event_sold_out()
1772
+	{
1773
+		$this->checkout->continue_reg = false;
1774
+		// set redirect URL
1775
+		$this->checkout->redirect_url = add_query_arg(
1776
+			['e_reg_url_link' => $this->checkout->reg_url_link],
1777
+			$this->checkout->current_step->reg_step_url()
1778
+		);
1779
+		$this->checkout->json_response->set_redirect_url($this->checkout->redirect_url);
1780
+	}
1781
+
1782
+
1783
+	/**
1784
+	 * @param EE_Payment $payment
1785
+	 * @return void
1786
+	 * @throws EE_Error
1787
+	 */
1788
+	protected function _maybe_set_completed(EE_Payment $payment)
1789
+	{
1790
+		// Do we need to redirect them? If so, there's more work to be done.
1791
+		if (! $payment->redirect_url()) {
1792
+			$this->set_completed();
1793
+		}
1794
+	}
1795
+
1796
+
1797
+	/**
1798
+	 *    update_reg_step
1799
+	 *    this is the final step after a user  revisits the site to retry a payment
1800
+	 *
1801
+	 * @return bool
1802
+	 * @throws EE_Error
1803
+	 * @throws InvalidArgumentException
1804
+	 * @throws ReflectionException
1805
+	 * @throws EntityNotFoundException
1806
+	 * @throws InvalidDataTypeException
1807
+	 * @throws InvalidInterfaceException
1808
+	 * @throws InvalidStatusException
1809
+	 */
1810
+	public function update_reg_step()
1811
+	{
1812
+		$success = true;
1813
+		// if payment required
1814
+		if ($this->checkout->transaction->total() > 0) {
1815
+			do_action(
1816
+				'AHEE__EE_Single_Page_Checkout__process_finalize_registration__before_gateway',
1817
+				$this->checkout->transaction
1818
+			);
1819
+			// attempt payment via payment method
1820
+			$success = $this->process_reg_step();
1821
+		}
1822
+		if ($success && ! $this->checkout->redirect) {
1823
+			$this->checkout->cart->get_grand_total()->save_this_and_descendants_to_txn(
1824
+				$this->checkout->transaction->ID()
1825
+			);
1826
+			// set return URL
1827
+			$this->checkout->redirect_url = add_query_arg(
1828
+				['e_reg_url_link' => $this->checkout->reg_url_link],
1829
+				$this->checkout->thank_you_page_url
1830
+			);
1831
+		}
1832
+		return $success;
1833
+	}
1834
+
1835
+
1836
+	/**
1837
+	 * @return EE_Payment|null
1838
+	 * @throws EE_Error
1839
+	 * @throws InvalidArgumentException
1840
+	 * @throws ReflectionException
1841
+	 * @throws RuntimeException
1842
+	 * @throws InvalidDataTypeException
1843
+	 * @throws InvalidInterfaceException
1844
+	 */
1845
+	private function _process_payment()
1846
+	{
1847
+		// basically confirm that the event hasn't sold out since they hit the page
1848
+		if (! $this->_last_second_ticket_verifications()) {
1849
+			return null;
1850
+		}
1851
+		// ya gotta make a choice man
1852
+		if (empty($this->checkout->selected_method_of_payment)) {
1853
+			$this->checkout->json_response->set_plz_select_method_of_payment(
1854
+				esc_html__('Please select a method of payment before proceeding.', 'event_espresso')
1855
+			);
1856
+			return null;
1857
+		}
1858
+		// get EE_Payment_Method object
1859
+		if (! $this->checkout->payment_method = $this->_get_payment_method_for_selected_method_of_payment()) {
1860
+			return null;
1861
+		}
1862
+		// setup billing form
1863
+		if ($this->checkout->payment_method->type_obj()->has_billing_form()) {
1864
+			$this->checkout->billing_form = $this->_get_billing_form_for_payment_method(
1865
+				$this->checkout->payment_method
1866
+			);
1867
+			// bad billing form ?
1868
+			if (! $this->_billing_form_is_valid()) {
1869
+				return null;
1870
+			}
1871
+		}
1872
+		// ensure primary registrant has been fully processed
1873
+		if (! $this->_setup_primary_registrant_prior_to_payment()) {
1874
+			return null;
1875
+		}
1876
+		// if session is close to expiring (under 10 minutes by default)
1877
+		if ((time() - EE_Registry::instance()->SSN->expiration()) < EE_Registry::instance()->SSN->extension()) {
1878
+			// add some time to session expiration so that payment can be completed
1879
+			EE_Registry::instance()->SSN->extend_expiration();
1880
+		}
1881
+		/** @type EE_Transaction_Processor $transaction_processor */
1882
+		// $transaction_processor = EE_Registry::instance()->load_class( 'Transaction_Processor' );
1883
+		// in case a registrant leaves to an Off-Site Gateway and never returns, we want to approve any registrations
1884
+		// for events with a default reg status of Approved
1885
+		// $transaction_processor->toggle_registration_statuses_for_default_approved_events(
1886
+		//      $this->checkout->transaction, $this->checkout->reg_cache_where_params
1887
+		// );
1888
+		// attempt payment
1889
+		$payment = $this->_attempt_payment($this->checkout->payment_method);
1890
+		// process results
1891
+		$payment = $this->_validate_payment($payment);
1892
+		$payment = $this->_post_payment_processing($payment);
1893
+		// verify payment
1894
+		if ($payment instanceof EE_Payment) {
1895
+			// store that for later
1896
+			$this->checkout->payment = $payment;
1897
+			// we can also consider the TXN to not have been failed, so temporarily upgrade it's status to abandoned
1898
+			$this->checkout->transaction->toggle_failed_transaction_status();
1899
+			$payment_status = $payment->status();
1900
+			if (
1901
+				$payment_status === EEM_Payment::status_id_approved
1902
+				|| $payment_status === EEM_Payment::status_id_pending
1903
+			) {
1904
+				return $payment;
1905
+			}
1906
+			return null;
1907
+		}
1908
+		if ($payment === true) {
1909
+			// please note that offline payment methods will NOT make a payment,
1910
+			// but instead just mark themselves as the PMD_ID on the transaction, and return true
1911
+			$this->checkout->payment = $payment;
1912
+			return $payment;
1913
+		}
1914
+		// where's my money?
1915
+		return null;
1916
+	}
1917
+
1918
+
1919
+	/**
1920
+	 * _last_second_ticket_verifications
1921
+	 *
1922
+	 * @return bool
1923
+	 * @throws EE_Error
1924
+	 * @throws ReflectionException
1925
+	 */
1926
+	protected function _last_second_ticket_verifications()
1927
+	{
1928
+		// don't bother re-validating if not a return visit
1929
+		if (! $this->checkout->revisit) {
1930
+			return true;
1931
+		}
1932
+		$registrations = $this->checkout->transaction->registrations();
1933
+		if (empty($registrations)) {
1934
+			return false;
1935
+		}
1936
+		foreach ($registrations as $registration) {
1937
+			if ($registration instanceof EE_Registration && ! $registration->is_approved()) {
1938
+				$event = $registration->event_obj();
1939
+				if ($event instanceof EE_Event && $event->is_sold_out(true)) {
1940
+					EE_Error::add_error(
1941
+						apply_filters(
1942
+							'FHEE__EE_SPCO_Reg_Step_Payment_Options___last_second_ticket_verifications__sold_out_events_msg',
1943
+							sprintf(
1944
+								esc_html__(
1945
+									'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.',
1946
+									'event_espresso'
1947
+								),
1948
+								$event->name()
1949
+							)
1950
+						),
1951
+						__FILE__,
1952
+						__FUNCTION__,
1953
+						__LINE__
1954
+					);
1955
+					return false;
1956
+				}
1957
+			}
1958
+		}
1959
+		return true;
1960
+	}
1961
+
1962
+
1963
+	/**
1964
+	 * redirect_form
1965
+	 *
1966
+	 * @return bool
1967
+	 * @throws EE_Error
1968
+	 * @throws InvalidArgumentException
1969
+	 * @throws ReflectionException
1970
+	 * @throws InvalidDataTypeException
1971
+	 * @throws InvalidInterfaceException
1972
+	 */
1973
+	public function redirect_form()
1974
+	{
1975
+		$payment_method_billing_info = $this->_payment_method_billing_info(
1976
+			$this->_get_payment_method_for_selected_method_of_payment()
1977
+		);
1978
+		$html                        = $payment_method_billing_info->get_html();
1979
+		$html                        .= $this->checkout->redirect_form;
1980
+		/** @var ResponseInterface $response */
1981
+		$response = LoaderFactory::getLoader()->getShared(ResponseInterface::class);
1982
+		$response->addOutput($html);
1983
+		return true;
1984
+	}
1985
+
1986
+
1987
+	/**
1988
+	 * _billing_form_is_valid
1989
+	 *
1990
+	 * @return bool
1991
+	 * @throws EE_Error
1992
+	 */
1993
+	private function _billing_form_is_valid()
1994
+	{
1995
+		if (! $this->checkout->payment_method->type_obj()->has_billing_form()) {
1996
+			return true;
1997
+		}
1998
+		if ($this->checkout->billing_form instanceof EE_Billing_Info_Form) {
1999
+			if ($this->checkout->billing_form->was_submitted()) {
2000
+				$this->checkout->billing_form->receive_form_submission();
2001
+				if ($this->checkout->billing_form->is_valid()) {
2002
+					return true;
2003
+				}
2004
+				$validation_errors = $this->checkout->billing_form->get_validation_errors_accumulated();
2005
+				$error_strings     = [];
2006
+				foreach ($validation_errors as $validation_error) {
2007
+					if ($validation_error instanceof EE_Validation_Error) {
2008
+						$form_section = $validation_error->get_form_section();
2009
+						if ($form_section instanceof EE_Form_Input_Base) {
2010
+							$label = $form_section->html_label_text();
2011
+						} elseif ($form_section instanceof EE_Form_Section_Base) {
2012
+							$label = $form_section->name();
2013
+						} else {
2014
+							$label = esc_html__('Validation Error', 'event_espresso');
2015
+						}
2016
+						$error_strings[] = sprintf('%1$s: %2$s', $label, $validation_error->getMessage());
2017
+					}
2018
+				}
2019
+				EE_Error::add_error(
2020
+					sprintf(
2021
+						esc_html__(
2022
+							'One or more billing form inputs are invalid and require correction before proceeding. %1$s %2$s',
2023
+							'event_espresso'
2024
+						),
2025
+						'<br/>',
2026
+						implode('<br/>', $error_strings)
2027
+					),
2028
+					__FILE__,
2029
+					__FUNCTION__,
2030
+					__LINE__
2031
+				);
2032
+			} else {
2033
+				EE_Error::add_error(
2034
+					esc_html__(
2035
+						'The billing form was not submitted or something prevented it\'s submission.',
2036
+						'event_espresso'
2037
+					),
2038
+					__FILE__,
2039
+					__FUNCTION__,
2040
+					__LINE__
2041
+				);
2042
+			}
2043
+		} else {
2044
+			EE_Error::add_error(
2045
+				esc_html__(
2046
+					'The submitted billing form is invalid possibly due to a technical reason.',
2047
+					'event_espresso'
2048
+				),
2049
+				__FILE__,
2050
+				__FUNCTION__,
2051
+				__LINE__
2052
+			);
2053
+		}
2054
+		return false;
2055
+	}
2056
+
2057
+
2058
+	/**
2059
+	 * _setup_primary_registrant_prior_to_payment
2060
+	 * ensures that the primary registrant has a valid attendee object created with the critical details populated
2061
+	 * (first & last name & email) and that both the transaction object and primary registration object have been saved
2062
+	 * plz note that any other registrations will NOT be saved at this point (because they may not have any details
2063
+	 * yet)
2064
+	 *
2065
+	 * @return bool
2066
+	 * @throws EE_Error
2067
+	 * @throws InvalidArgumentException
2068
+	 * @throws ReflectionException
2069
+	 * @throws RuntimeException
2070
+	 * @throws InvalidDataTypeException
2071
+	 * @throws InvalidInterfaceException
2072
+	 */
2073
+	private function _setup_primary_registrant_prior_to_payment()
2074
+	{
2075
+		// check if transaction has a primary registrant and that it has a related Attendee object
2076
+		// if not, then we need to at least gather some primary registrant data before attempting payment
2077
+		if (
2078
+			$this->checkout->billing_form instanceof EE_Billing_Attendee_Info_Form
2079
+			&& ! $this->checkout->transaction_has_primary_registrant()
2080
+			&& ! $this->_capture_primary_registration_data_from_billing_form()
2081
+		) {
2082
+			return false;
2083
+		}
2084
+		// because saving an object clears it's cache, we need to do the chevy shuffle
2085
+		// grab the primary_registration object
2086
+		$primary_registration = $this->checkout->transaction->primary_registration();
2087
+		// at this point we'll consider a TXN to not have been failed
2088
+		$this->checkout->transaction->toggle_failed_transaction_status();
2089
+		// save the TXN ( which clears cached copy of primary_registration)
2090
+		$this->checkout->transaction->save();
2091
+		// grab TXN ID and save it to the primary_registration
2092
+		$primary_registration->set_transaction_id($this->checkout->transaction->ID());
2093
+		// save what we have so far
2094
+		$primary_registration->save();
2095
+		return true;
2096
+	}
2097
+
2098
+
2099
+	/**
2100
+	 * _capture_primary_registration_data_from_billing_form
2101
+	 *
2102
+	 * @return bool
2103
+	 * @throws EE_Error
2104
+	 * @throws InvalidArgumentException
2105
+	 * @throws ReflectionException
2106
+	 * @throws InvalidDataTypeException
2107
+	 * @throws InvalidInterfaceException
2108
+	 */
2109
+	private function _capture_primary_registration_data_from_billing_form()
2110
+	{
2111
+		// convert billing form data into an attendee
2112
+		$this->checkout->primary_attendee_obj = $this->checkout->billing_form->create_attendee_from_billing_form_data();
2113
+		if (! $this->checkout->primary_attendee_obj instanceof EE_Attendee) {
2114
+			EE_Error::add_error(
2115
+				sprintf(
2116
+					esc_html__(
2117
+						'The billing form details could not be used for attendee details due to a technical issue.%sPlease try again or contact %s for assistance.',
2118
+						'event_espresso'
2119
+					),
2120
+					'<br/>',
2121
+					EE_Registry::instance()->CFG->organization->get_pretty('email')
2122
+				),
2123
+				__FILE__,
2124
+				__FUNCTION__,
2125
+				__LINE__
2126
+			);
2127
+			return false;
2128
+		}
2129
+		$primary_registration = $this->checkout->transaction->primary_registration();
2130
+		if (! $primary_registration instanceof EE_Registration) {
2131
+			EE_Error::add_error(
2132
+				sprintf(
2133
+					esc_html__(
2134
+						'The primary registrant for this transaction could not be determined due to a technical issue.%sPlease try again or contact %s for assistance.',
2135
+						'event_espresso'
2136
+					),
2137
+					'<br/>',
2138
+					EE_Registry::instance()->CFG->organization->get_pretty('email')
2139
+				),
2140
+				__FILE__,
2141
+				__FUNCTION__,
2142
+				__LINE__
2143
+			);
2144
+			return false;
2145
+		}
2146
+		if (
2147
+			! $primary_registration->_add_relation_to($this->checkout->primary_attendee_obj, 'Attendee')
2148
+			  instanceof
2149
+			  EE_Attendee
2150
+		) {
2151
+			EE_Error::add_error(
2152
+				sprintf(
2153
+					esc_html__(
2154
+						'The primary registrant could not be associated with this transaction due to a technical issue.%sPlease try again or contact %s for assistance.',
2155
+						'event_espresso'
2156
+					),
2157
+					'<br/>',
2158
+					EE_Registry::instance()->CFG->organization->get_pretty('email')
2159
+				),
2160
+				__FILE__,
2161
+				__FUNCTION__,
2162
+				__LINE__
2163
+			);
2164
+			return false;
2165
+		}
2166
+		/** @type EE_Registration_Processor $registration_processor */
2167
+		$registration_processor = EE_Registry::instance()->load_class('Registration_Processor');
2168
+		// at this point, we should have enough details about the registrant to consider the registration NOT incomplete
2169
+		$registration_processor->toggle_incomplete_registration_status_to_default(
2170
+			$primary_registration,
2171
+			false,
2172
+			new Context(
2173
+				__METHOD__,
2174
+				esc_html__(
2175
+					'Executed when the primary registrant\'s status is updated during the registration process when processing a billing form.',
2176
+					'event_espresso'
2177
+				)
2178
+			)
2179
+		);
2180
+		return true;
2181
+	}
2182
+
2183
+
2184
+	/**
2185
+	 * _get_payment_method_for_selected_method_of_payment
2186
+	 * retrieves a valid payment method
2187
+	 *
2188
+	 * @return EE_Payment_Method
2189
+	 * @throws EE_Error
2190
+	 * @throws InvalidArgumentException
2191
+	 * @throws ReflectionException
2192
+	 * @throws InvalidDataTypeException
2193
+	 * @throws InvalidInterfaceException
2194
+	 */
2195
+	private function _get_payment_method_for_selected_method_of_payment()
2196
+	{
2197
+		if ($this->checkout->selected_method_of_payment === 'events_sold_out') {
2198
+			$this->_redirect_because_event_sold_out();
2199
+			return null;
2200
+		}
2201
+		// get EE_Payment_Method object
2202
+		if (isset($this->checkout->available_payment_methods[ $this->checkout->selected_method_of_payment ])) {
2203
+			$payment_method = $this->checkout->available_payment_methods[ $this->checkout->selected_method_of_payment ];
2204
+		} else {
2205
+			// load EEM_Payment_Method
2206
+			EE_Registry::instance()->load_model('Payment_Method');
2207
+			$EEM_Payment_Method = EEM_Payment_Method::instance();
2208
+			$payment_method     = $EEM_Payment_Method->get_one_by_slug($this->checkout->selected_method_of_payment);
2209
+		}
2210
+		// verify $payment_method
2211
+		if (! $payment_method instanceof EE_Payment_Method) {
2212
+			// not a payment
2213
+			EE_Error::add_error(
2214
+				sprintf(
2215
+					esc_html__(
2216
+						'The selected method of payment could not be determined due to a technical issue.%sPlease try again or contact %s for assistance.',
2217
+						'event_espresso'
2218
+					),
2219
+					'<br/>',
2220
+					EE_Registry::instance()->CFG->organization->get_pretty('email')
2221
+				),
2222
+				__FILE__,
2223
+				__FUNCTION__,
2224
+				__LINE__
2225
+			);
2226
+			return null;
2227
+		}
2228
+		// and verify it has a valid Payment_Method Type object
2229
+		if (! $payment_method->type_obj() instanceof EE_PMT_Base) {
2230
+			// not a payment
2231
+			EE_Error::add_error(
2232
+				sprintf(
2233
+					esc_html__(
2234
+						'A valid payment method could not be determined due to a technical issue.%sPlease try again or contact %s for assistance.',
2235
+						'event_espresso'
2236
+					),
2237
+					'<br/>',
2238
+					EE_Registry::instance()->CFG->organization->get_pretty('email')
2239
+				),
2240
+				__FILE__,
2241
+				__FUNCTION__,
2242
+				__LINE__
2243
+			);
2244
+			return null;
2245
+		}
2246
+		return $payment_method;
2247
+	}
2248
+
2249
+
2250
+	/**
2251
+	 *    _attempt_payment
2252
+	 *
2253
+	 * @access    private
2254
+	 * @type    EE_Payment_Method $payment_method
2255
+	 * @return mixed EE_Payment | boolean
2256
+	 * @throws EE_Error
2257
+	 * @throws InvalidArgumentException
2258
+	 * @throws ReflectionException
2259
+	 * @throws InvalidDataTypeException
2260
+	 * @throws InvalidInterfaceException
2261
+	 */
2262
+	private function _attempt_payment(EE_Payment_Method $payment_method)
2263
+	{
2264
+		$payment = null;
2265
+		$this->checkout->transaction->save();
2266
+		$payment_processor = EE_Registry::instance()->load_core('Payment_Processor');
2267
+		if (! $payment_processor instanceof EE_Payment_Processor) {
2268
+			return false;
2269
+		}
2270
+		try {
2271
+			$payment_processor->set_revisit($this->checkout->revisit);
2272
+			// generate payment object
2273
+			$payment = $payment_processor->process_payment(
2274
+				$payment_method,
2275
+				$this->checkout->transaction,
2276
+				$this->checkout->amount_owing,
2277
+				$this->checkout->billing_form instanceof EE_Billing_Info_Form
2278
+					? $this->checkout->billing_form
2279
+					: null,
2280
+				$this->_get_return_url($payment_method),
2281
+				'CART',
2282
+				$this->checkout->admin_request,
2283
+				true,
2284
+				$this->reg_step_url()
2285
+			);
2286
+		} catch (Exception $e) {
2287
+			$this->_handle_payment_processor_exception($e);
2288
+		}
2289
+		return $payment;
2290
+	}
2291
+
2292
+
2293
+	/**
2294
+	 * _handle_payment_processor_exception
2295
+	 *
2296
+	 * @param Exception $e
2297
+	 * @return void
2298
+	 * @throws EE_Error
2299
+	 * @throws InvalidArgumentException
2300
+	 * @throws InvalidDataTypeException
2301
+	 * @throws InvalidInterfaceException
2302
+	 */
2303
+	protected function _handle_payment_processor_exception(Exception $e)
2304
+	{
2305
+		EE_Error::add_error(
2306
+			sprintf(
2307
+				esc_html__(
2308
+					'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',
2309
+					'event_espresso'
2310
+				),
2311
+				'<br/>',
2312
+				EE_Registry::instance()->CFG->organization->get_pretty('email'),
2313
+				$e->getMessage(),
2314
+				$e->getFile(),
2315
+				$e->getLine()
2316
+			),
2317
+			__FILE__,
2318
+			__FUNCTION__,
2319
+			__LINE__
2320
+		);
2321
+	}
2322
+
2323
+
2324
+	/**
2325
+	 * _get_return_url
2326
+	 *
2327
+	 * @param EE_Payment_Method $payment_method
2328
+	 * @return string
2329
+	 * @throws EE_Error
2330
+	 * @throws ReflectionException
2331
+	 */
2332
+	protected function _get_return_url(EE_Payment_Method $payment_method)
2333
+	{
2334
+		$return_url = '';
2335
+		switch ($payment_method->type_obj()->payment_occurs()) {
2336
+			case EE_PMT_Base::offsite:
2337
+				$return_url = add_query_arg(
2338
+					[
2339
+						'action'                     => 'process_gateway_response',
2340
+						'selected_method_of_payment' => $this->checkout->selected_method_of_payment,
2341
+						'spco_txn'                   => $this->checkout->transaction->ID(),
2342
+					],
2343
+					$this->reg_step_url()
2344
+				);
2345
+				break;
2346
+			case EE_PMT_Base::onsite:
2347
+			case EE_PMT_Base::offline:
2348
+				$return_url = $this->checkout->next_step->reg_step_url();
2349
+				break;
2350
+		}
2351
+		return $return_url;
2352
+	}
2353
+
2354
+
2355
+	/**
2356
+	 * _validate_payment
2357
+	 *
2358
+	 * @param EE_Payment $payment
2359
+	 * @return EE_Payment|FALSE
2360
+	 * @throws EE_Error
2361
+	 * @throws InvalidArgumentException
2362
+	 * @throws InvalidDataTypeException
2363
+	 * @throws InvalidInterfaceException
2364
+	 */
2365
+	private function _validate_payment($payment = null)
2366
+	{
2367
+		if ($this->checkout->payment_method->is_off_line()) {
2368
+			return true;
2369
+		}
2370
+		// verify payment object
2371
+		if (! $payment instanceof EE_Payment) {
2372
+			// not a payment
2373
+			EE_Error::add_error(
2374
+				sprintf(
2375
+					esc_html__(
2376
+						'A valid payment was not generated due to a technical issue.%1$sPlease try again or contact %2$s for assistance.',
2377
+						'event_espresso'
2378
+					),
2379
+					'<br/>',
2380
+					EE_Registry::instance()->CFG->organization->get_pretty('email')
2381
+				),
2382
+				__FILE__,
2383
+				__FUNCTION__,
2384
+				__LINE__
2385
+			);
2386
+			return false;
2387
+		}
2388
+		return $payment;
2389
+	}
2390
+
2391
+
2392
+	/**
2393
+	 * _post_payment_processing
2394
+	 *
2395
+	 * @param EE_Payment|bool $payment
2396
+	 * @return bool|EE_Payment
2397
+	 * @throws EE_Error
2398
+	 * @throws InvalidArgumentException
2399
+	 * @throws InvalidDataTypeException
2400
+	 * @throws InvalidInterfaceException
2401
+	 * @throws ReflectionException
2402
+	 */
2403
+	private function _post_payment_processing($payment = null)
2404
+	{
2405
+		// Off-Line payment?
2406
+		if ($payment === true) {
2407
+			return true;
2408
+		}
2409
+		if ($payment instanceof EE_Payment) {
2410
+			// Should the user be redirected?
2411
+			if ($payment->redirect_url()) {
2412
+				do_action('AHEE_log', __CLASS__, __FUNCTION__, $payment->redirect_url(), '$payment->redirect_url()');
2413
+				$this->checkout->redirect      = true;
2414
+				$this->checkout->redirect_form = $payment->redirect_form();
2415
+				$this->checkout->redirect_url  = $this->reg_step_url('redirect_form');
2416
+				// set JSON response
2417
+				$this->checkout->json_response->set_redirect_form($this->checkout->redirect_form);
2418
+				// and lastly, let's bump the payment status to pending
2419
+				$payment->set_status(EEM_Payment::status_id_pending);
2420
+				$payment->save();
2421
+			} elseif (! $this->_process_payment_status($payment, EE_PMT_Base::onsite)) {
2422
+				// User shouldn't be redirected. So let's process it here.
2423
+				// $this->_setup_redirect_for_next_step();
2424
+				$this->checkout->continue_reg = false;
2425
+			}
2426
+			return $payment;
2427
+		}
2428
+		// ummm ya... not Off-Line, not On-Site, not off-Site ????
2429
+		$this->checkout->continue_reg = false;
2430
+		return false;
2431
+	}
2432
+
2433
+
2434
+	/**
2435
+	 *    _process_payment_status
2436
+	 *
2437
+	 * @type    EE_Payment $payment
2438
+	 * @param string       $payment_occurs
2439
+	 * @return bool
2440
+	 * @throws EE_Error
2441
+	 * @throws InvalidArgumentException
2442
+	 * @throws InvalidDataTypeException
2443
+	 * @throws InvalidInterfaceException
2444
+	 */
2445
+	private function _process_payment_status($payment, $payment_occurs = EE_PMT_Base::offline)
2446
+	{
2447
+		// off-line payment? carry on
2448
+		if ($payment_occurs === EE_PMT_Base::offline) {
2449
+			return true;
2450
+		}
2451
+		// verify payment validity
2452
+		if ($payment instanceof EE_Payment) {
2453
+			do_action('AHEE_log', __CLASS__, __FUNCTION__, $payment->status(), '$payment->status()');
2454
+			$msg = $payment->gateway_response();
2455
+			// check results
2456
+			switch ($payment->status()) {
2457
+				// good payment
2458
+				case EEM_Payment::status_id_approved:
2459
+					EE_Error::add_success(
2460
+						esc_html__('Your payment was processed successfully.', 'event_espresso'),
2461
+						__FILE__,
2462
+						__FUNCTION__,
2463
+						__LINE__
2464
+					);
2465
+					return true;
2466
+				// slow payment
2467
+				case EEM_Payment::status_id_pending:
2468
+					if (empty($msg)) {
2469
+						$msg = esc_html__(
2470
+							'Your payment appears to have been processed successfully, but the Instant Payment Notification has not yet been received. It should arrive shortly.',
2471
+							'event_espresso'
2472
+						);
2473
+					}
2474
+					EE_Error::add_success($msg, __FILE__, __FUNCTION__, __LINE__);
2475
+					return true;
2476
+				// don't wanna payment
2477
+				case EEM_Payment::status_id_cancelled:
2478
+					if (empty($msg)) {
2479
+						$msg = _n(
2480
+							'Payment cancelled. Please try again.',
2481
+							'Payment cancelled. Please try again or select another method of payment.',
2482
+							count($this->checkout->available_payment_methods),
2483
+							'event_espresso'
2484
+						);
2485
+					}
2486
+					EE_Error::add_attention($msg, __FILE__, __FUNCTION__, __LINE__);
2487
+					return false;
2488
+				// not enough payment
2489
+				case EEM_Payment::status_id_declined:
2490
+					if (empty($msg)) {
2491
+						$msg = _n(
2492
+							'We\'re sorry but your payment was declined. Please try again.',
2493
+							'We\'re sorry but your payment was declined. Please try again or select another method of payment.',
2494
+							count($this->checkout->available_payment_methods),
2495
+							'event_espresso'
2496
+						);
2497
+					}
2498
+					EE_Error::add_attention($msg, __FILE__, __FUNCTION__, __LINE__);
2499
+					return false;
2500
+				// bad payment
2501
+				case EEM_Payment::status_id_failed:
2502
+					if (! empty($msg)) {
2503
+						EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
2504
+						return false;
2505
+					}
2506
+					// default to error below
2507
+					break;
2508
+			}
2509
+		}
2510
+		// off-site payment gateway responses are too unreliable, so let's just assume that
2511
+		// the payment processing is just running slower than the registrant's request
2512
+		if ($payment_occurs === EE_PMT_Base::offsite) {
2513
+			return true;
2514
+		}
2515
+		EE_Error::add_error(
2516
+			sprintf(
2517
+				esc_html__(
2518
+					'Your payment could not be processed successfully due to a technical issue.%sPlease try again or contact %s for assistance.',
2519
+					'event_espresso'
2520
+				),
2521
+				'<br/>',
2522
+				EE_Registry::instance()->CFG->organization->get_pretty('email')
2523
+			),
2524
+			__FILE__,
2525
+			__FUNCTION__,
2526
+			__LINE__
2527
+		);
2528
+		return false;
2529
+	}
2530
+
2531
+
2532
+
2533
+
2534
+
2535
+
2536
+	/********************************************************************************************************/
2537
+	/**********************************  PROCESS GATEWAY RESPONSE  **********************************/
2538
+	/********************************************************************************************************/
2539
+	/**
2540
+	 * process_gateway_response
2541
+	 * this is the return point for Off-Site Payment Methods
2542
+	 * It will attempt to "handle the IPN" if it appears that this has not already occurred,
2543
+	 * otherwise, it will load up the last payment made for the TXN.
2544
+	 * If the payment retrieved looks good, it will then either:
2545
+	 *    complete the current step and allow advancement to the next reg step
2546
+	 *        or present the payment options again
2547
+	 *
2548
+	 * @return bool
2549
+	 * @throws EE_Error
2550
+	 * @throws InvalidArgumentException
2551
+	 * @throws ReflectionException
2552
+	 * @throws InvalidDataTypeException
2553
+	 * @throws InvalidInterfaceException
2554
+	 */
2555
+	public function process_gateway_response()
2556
+	{
2557
+		// how have they chosen to pay?
2558
+		$this->checkout->selected_method_of_payment = $this->_get_selected_method_of_payment(true);
2559
+		// get EE_Payment_Method object
2560
+		if (! $this->checkout->payment_method = $this->_get_payment_method_for_selected_method_of_payment()) {
2561
+			$this->checkout->continue_reg = false;
2562
+			return false;
2563
+		}
2564
+		if (! $this->checkout->payment_method->is_off_site()) {
2565
+			return false;
2566
+		}
2567
+		$this->_validate_offsite_return();
2568
+		// verify TXN
2569
+		if ($this->checkout->transaction instanceof EE_Transaction) {
2570
+			$gateway = $this->checkout->payment_method->type_obj()->get_gateway();
2571
+			if (! $gateway instanceof EE_Offsite_Gateway) {
2572
+				$this->checkout->continue_reg = false;
2573
+				return false;
2574
+			}
2575
+			$payment = $this->_process_off_site_payment($gateway);
2576
+			$payment = $this->_process_cancelled_payments($payment);
2577
+			$payment = $this->_validate_payment($payment);
2578
+			// if payment was not declined by the payment gateway or cancelled by the registrant
2579
+			if ($this->_process_payment_status($payment, EE_PMT_Base::offsite)) {
2580
+				// $this->_setup_redirect_for_next_step();
2581
+				// store that for later
2582
+				$this->checkout->payment = $payment;
2583
+				// mark this reg step as completed, as long as gateway doesn't use a separate IPN request,
2584
+				// because we will complete this step during the IPN processing then
2585
+				if (! $this->handle_IPN_in_this_request()) {
2586
+					$this->set_completed();
2587
+				}
2588
+				return true;
2589
+			}
2590
+		}
2591
+		// DEBUG LOG
2592
+		// $this->checkout->log(
2593
+		//     __CLASS__,
2594
+		//     __FUNCTION__,
2595
+		//     __LINE__,
2596
+		//     array('payment' => $payment)
2597
+		// );
2598
+		$this->checkout->continue_reg = false;
2599
+		return false;
2600
+	}
2601
+
2602
+
2603
+	/**
2604
+	 * _validate_return
2605
+	 *
2606
+	 * @return void
2607
+	 * @throws EE_Error
2608
+	 * @throws InvalidArgumentException
2609
+	 * @throws InvalidDataTypeException
2610
+	 * @throws InvalidInterfaceException
2611
+	 * @throws ReflectionException
2612
+	 */
2613
+	private function _validate_offsite_return()
2614
+	{
2615
+		$TXN_ID = $this->request->getRequestParam('spco_txn', 0, 'int');
2616
+		if ($TXN_ID !== $this->checkout->transaction->ID()) {
2617
+			// Houston... we might have a problem
2618
+			$invalid_TXN = false;
2619
+			// first gather some info
2620
+			$valid_TXN          = EEM_Transaction::instance()->get_one_by_ID($TXN_ID);
2621
+			$primary_registrant = $valid_TXN instanceof EE_Transaction
2622
+				? $valid_TXN->primary_registration()
2623
+				: null;
2624
+			// let's start by retrieving the cart for this TXN
2625
+			$cart = $this->checkout->get_cart_for_transaction($this->checkout->transaction);
2626
+			if ($cart instanceof EE_Cart) {
2627
+				// verify that the current cart has tickets
2628
+				$tickets = $cart->get_tickets();
2629
+				if (empty($tickets)) {
2630
+					$invalid_TXN = true;
2631
+				}
2632
+			} else {
2633
+				$invalid_TXN = true;
2634
+			}
2635
+			$valid_TXN_SID = $primary_registrant instanceof EE_Registration
2636
+				? $primary_registrant->session_ID()
2637
+				: null;
2638
+			// validate current Session ID and compare against valid TXN session ID
2639
+			if (
2640
+				$invalid_TXN // if this is already true, then skip other checks
2641
+				|| EE_Session::instance()->id() === null
2642
+				|| (
2643
+					// WARNING !!!
2644
+					// this could be PayPal sending back duplicate requests (ya they do that)
2645
+					// or it **could** mean someone is simply registering AGAIN after having just done so
2646
+					// so now we need to determine if this current TXN looks valid or not
2647
+					// and whether this reg step has even been started ?
2648
+					EE_Session::instance()->id() === $valid_TXN_SID
2649
+					// really? you're half way through this reg step, but you never started it ?
2650
+					&& $this->checkout->transaction->reg_step_completed($this->slug()) === false
2651
+				)
2652
+			) {
2653
+				$invalid_TXN = true;
2654
+			}
2655
+			if ($invalid_TXN) {
2656
+				// is the valid TXN completed ?
2657
+				if ($valid_TXN instanceof EE_Transaction) {
2658
+					// has this step even been started ?
2659
+					$reg_step_completed = $valid_TXN->reg_step_completed($this->slug());
2660
+					if ($reg_step_completed !== false && $reg_step_completed !== true) {
2661
+						// so it **looks** like this is a double request from PayPal
2662
+						// so let's try to pick up where we left off
2663
+						$this->checkout->transaction = $valid_TXN;
2664
+						$this->checkout->refresh_all_entities(true);
2665
+						return;
2666
+					}
2667
+				}
2668
+				// you appear to be lost?
2669
+				$this->_redirect_wayward_request($primary_registrant);
2670
+			}
2671
+		}
2672
+	}
2673
+
2674
+
2675
+	/**
2676
+	 * _redirect_wayward_request
2677
+	 *
2678
+	 * @param EE_Registration|null $primary_registrant
2679
+	 * @return void
2680
+	 * @throws EE_Error
2681
+	 * @throws InvalidArgumentException
2682
+	 * @throws InvalidDataTypeException
2683
+	 * @throws InvalidInterfaceException
2684
+	 * @throws ReflectionException
2685
+	 */
2686
+	private function _redirect_wayward_request(EE_Registration $primary_registrant)
2687
+	{
2688
+		if (! $primary_registrant instanceof EE_Registration) {
2689
+			// try redirecting based on the current TXN
2690
+			$primary_registrant = $this->checkout->transaction instanceof EE_Transaction
2691
+				? $this->checkout->transaction->primary_registration()
2692
+				: null;
2693
+		}
2694
+		if (! $primary_registrant instanceof EE_Registration) {
2695
+			EE_Error::add_error(
2696
+				sprintf(
2697
+					esc_html__(
2698
+						'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.',
2699
+						'event_espresso'
2700
+					),
2701
+					'<br/>',
2702
+					EE_Registry::instance()->CFG->organization->get_pretty('email')
2703
+				),
2704
+				__FILE__,
2705
+				__FUNCTION__,
2706
+				__LINE__
2707
+			);
2708
+			return;
2709
+		}
2710
+		// make sure transaction is not locked
2711
+		$this->checkout->transaction->unlock();
2712
+		wp_safe_redirect(
2713
+			add_query_arg(
2714
+				[
2715
+					'e_reg_url_link' => $primary_registrant->reg_url_link(),
2716
+				],
2717
+				$this->checkout->thank_you_page_url
2718
+			)
2719
+		);
2720
+		exit();
2721
+	}
2722
+
2723
+
2724
+	/**
2725
+	 * _process_off_site_payment
2726
+	 *
2727
+	 * @param EE_Offsite_Gateway $gateway
2728
+	 * @return EE_Payment
2729
+	 * @throws EE_Error
2730
+	 * @throws InvalidArgumentException
2731
+	 * @throws InvalidDataTypeException
2732
+	 * @throws InvalidInterfaceException
2733
+	 * @throws ReflectionException
2734
+	 */
2735
+	private function _process_off_site_payment(EE_Offsite_Gateway $gateway)
2736
+	{
2737
+		try {
2738
+			$request      = LoaderFactory::getLoader()->getShared(RequestInterface::class);
2739
+			$request_data = $request->requestParams();
2740
+			// if gateway uses_separate_IPN_request, then we don't have to process the IPN manually
2741
+			$this->set_handle_IPN_in_this_request(
2742
+				$gateway->handle_IPN_in_this_request($request_data, false)
2743
+			);
2744
+			if ($this->handle_IPN_in_this_request()) {
2745
+				// get payment details and process results
2746
+				/** @type EE_Payment_Processor $payment_processor */
2747
+				$payment_processor = EE_Registry::instance()->load_core('Payment_Processor');
2748
+				$payment           = $payment_processor->process_ipn(
2749
+					$request_data,
2750
+					$this->checkout->transaction,
2751
+					$this->checkout->payment_method,
2752
+					true,
2753
+					false
2754
+				);
2755
+				// $payment_source = 'process_ipn';
2756
+			} else {
2757
+				$payment = $this->checkout->transaction->last_payment();
2758
+				// $payment_source = 'last_payment';
2759
+			}
2760
+		} catch (Exception $e) {
2761
+			// let's just eat the exception and try to move on using any previously set payment info
2762
+			$payment = $this->checkout->transaction->last_payment();
2763
+			// $payment_source = 'last_payment after Exception';
2764
+			// but if we STILL don't have a payment object
2765
+			if (! $payment instanceof EE_Payment) {
2766
+				// then we'll object ! ( not object like a thing... but object like what a lawyer says ! )
2767
+				$this->_handle_payment_processor_exception($e);
2768
+			}
2769
+		}
2770
+		return $payment;
2771
+	}
2772
+
2773
+
2774
+	/**
2775
+	 * _process_cancelled_payments
2776
+	 * just makes sure that the payment status gets updated correctly
2777
+	 * so tha tan error isn't generated during payment validation
2778
+	 *
2779
+	 * @param EE_Payment $payment
2780
+	 * @return EE_Payment|null
2781
+	 * @throws EE_Error
2782
+	 */
2783
+	private function _process_cancelled_payments($payment = null)
2784
+	{
2785
+		if (
2786
+			$payment instanceof EE_Payment
2787
+			&& $this->request->requestParamIsSet('ee_cancel_payment')
2788
+			&& $payment->status() === EEM_Payment::status_id_failed
2789
+		) {
2790
+			$payment->set_status(EEM_Payment::status_id_cancelled);
2791
+		}
2792
+		return $payment;
2793
+	}
2794
+
2795
+
2796
+	/**
2797
+	 *    get_transaction_details_for_gateways
2798
+	 *
2799
+	 * @access    public
2800
+	 * @return void
2801
+	 * @throws EE_Error
2802
+	 * @throws InvalidArgumentException
2803
+	 * @throws ReflectionException
2804
+	 * @throws InvalidDataTypeException
2805
+	 * @throws InvalidInterfaceException
2806
+	 */
2807
+	public function get_transaction_details_for_gateways()
2808
+	{
2809
+		$txn_details = [];
2810
+		// ya gotta make a choice man
2811
+		if (empty($this->checkout->selected_method_of_payment)) {
2812
+			$txn_details = [
2813
+				'error' => esc_html__('Please select a method of payment before proceeding.', 'event_espresso'),
2814
+			];
2815
+		}
2816
+		// get EE_Payment_Method object
2817
+		if (
2818
+			empty($txn_details)
2819
+			&& ! $this->checkout->payment_method = $this->_get_payment_method_for_selected_method_of_payment()
2820
+		) {
2821
+			$txn_details = [
2822
+				'selected_method_of_payment' => $this->checkout->selected_method_of_payment,
2823
+				'error'                      => esc_html__(
2824
+					'A valid Payment Method could not be determined.',
2825
+					'event_espresso'
2826
+				),
2827
+			];
2828
+		}
2829
+		if (empty($txn_details) && $this->checkout->transaction instanceof EE_Transaction) {
2830
+			$return_url  = $this->_get_return_url($this->checkout->payment_method);
2831
+			$txn_details = [
2832
+				'TXN_ID'         => $this->checkout->transaction->ID(),
2833
+				'TXN_timestamp'  => $this->checkout->transaction->datetime(),
2834
+				'TXN_total'      => $this->checkout->transaction->total(),
2835
+				'TXN_paid'       => $this->checkout->transaction->paid(),
2836
+				'TXN_reg_steps'  => $this->checkout->transaction->reg_steps(),
2837
+				'STS_ID'         => $this->checkout->transaction->status_ID(),
2838
+				'PMD_ID'         => $this->checkout->transaction->payment_method_ID(),
2839
+				'payment_amount' => $this->checkout->amount_owing,
2840
+				'return_url'     => $return_url,
2841
+				'cancel_url'     => add_query_arg(['ee_cancel_payment' => true], $return_url),
2842
+				'notify_url'     => EE_Config::instance()->core->txn_page_url(
2843
+					[
2844
+						'e_reg_url_link'    => $this->checkout->transaction->primary_registration()->reg_url_link(),
2845
+						'ee_payment_method' => $this->checkout->payment_method->slug(),
2846
+					]
2847
+				),
2848
+			];
2849
+		}
2850
+		echo wp_json_encode($txn_details);
2851
+		exit();
2852
+	}
2853
+
2854
+
2855
+	/**
2856
+	 *    __sleep
2857
+	 * to conserve db space, let's remove the reg_form and the EE_Checkout object from EE_SPCO_Reg_Step objects upon
2858
+	 * serialization EE_Checkout will handle the reimplementation of itself upon waking, but we won't bother with the
2859
+	 * reg form, because if needed, it will be regenerated anyways
2860
+	 *
2861
+	 * @return array
2862
+	 */
2863
+	public function __sleep()
2864
+	{
2865
+		// remove the reg form and the checkout
2866
+		return array_diff(array_keys(get_object_vars($this)), ['reg_form', 'checkout', 'line_item_display']);
2867
+	}
2868 2868
 }
Please login to merge, or discard this patch.
Spacing   +80 added lines, -80 removed lines patch added patch discarded remove patch
@@ -134,7 +134,7 @@  discard block
 block discarded – undo
134 134
         $this->request   = EED_Single_Page_Checkout::getRequest();
135 135
         $this->_slug     = 'payment_options';
136 136
         $this->_name     = esc_html__('Payment Options', 'event_espresso');
137
-        $this->_template = SPCO_REG_STEPS_PATH . $this->_slug . '/payment_options_main.template.php';
137
+        $this->_template = SPCO_REG_STEPS_PATH.$this->_slug.'/payment_options_main.template.php';
138 138
         $this->checkout  = $checkout;
139 139
         $this->_reset_success_message();
140 140
         $this->set_instructions(
@@ -189,7 +189,7 @@  discard block
 block discarded – undo
189 189
      */
190 190
     public function translate_js_strings()
191 191
     {
192
-        EE_Registry::$i18n_js_strings['no_payment_method']      = esc_html__(
192
+        EE_Registry::$i18n_js_strings['no_payment_method'] = esc_html__(
193 193
             'Please select a method of payment in order to continue.',
194 194
             'event_espresso'
195 195
         );
@@ -197,7 +197,7 @@  discard block
 block discarded – undo
197 197
             'A valid method of payment could not be determined. Please refresh the page and try again.',
198 198
             'event_espresso'
199 199
         );
200
-        EE_Registry::$i18n_js_strings['forwarding_to_offsite']  = esc_html__(
200
+        EE_Registry::$i18n_js_strings['forwarding_to_offsite'] = esc_html__(
201 201
             'Forwarding to Secure Payment Provider.',
202 202
             'event_espresso'
203 203
         );
@@ -218,7 +218,7 @@  discard block
 block discarded – undo
218 218
     {
219 219
         $transaction = $this->checkout->transaction;
220 220
         // if the transaction isn't set or nothing is owed on it, don't enqueue any JS
221
-        if (! $transaction instanceof EE_Transaction || EEH_Money::compare_floats($transaction->remaining(), 0)) {
221
+        if ( ! $transaction instanceof EE_Transaction || EEH_Money::compare_floats($transaction->remaining(), 0)) {
222 222
             return;
223 223
         }
224 224
         foreach (
@@ -317,18 +317,18 @@  discard block
 block discarded – undo
317 317
                 continue;
318 318
             }
319 319
             // has this registration lost it's space ?
320
-            if (isset($ejected_registrations[ $REG_ID ])) {
320
+            if (isset($ejected_registrations[$REG_ID])) {
321 321
                 if ($registration->event()->is_sold_out() || $registration->event()->is_sold_out(true)) {
322
-                    $sold_out_events[ $registration->event()->ID() ] = $registration->event();
322
+                    $sold_out_events[$registration->event()->ID()] = $registration->event();
323 323
                 } else {
324
-                    $insufficient_spaces_available[ $registration->event()->ID() ] = $registration->event();
324
+                    $insufficient_spaces_available[$registration->event()->ID()] = $registration->event();
325 325
                 }
326 326
                 continue;
327 327
             }
328 328
             // event requires admin approval
329 329
             if ($registration->status_ID() === RegStatus::AWAITING_REVIEW) {
330 330
                 // add event to list of events with pre-approval reg status
331
-                $registrations_requiring_pre_approval[ $REG_ID ] = $registration;
331
+                $registrations_requiring_pre_approval[$REG_ID] = $registration;
332 332
                 do_action(
333 333
                     'AHEE__EE_SPCO_Reg_Step_Payment_Options__generate_reg_form__event_requires_pre_approval',
334 334
                     $registration->event(),
@@ -345,7 +345,7 @@  discard block
 block discarded – undo
345 345
                 )
346 346
             ) {
347 347
                 // add event to list of events that are sold out
348
-                $sold_out_events[ $registration->event()->ID() ] = $registration->event();
348
+                $sold_out_events[$registration->event()->ID()] = $registration->event();
349 349
                 do_action(
350 350
                     'AHEE__EE_SPCO_Reg_Step_Payment_Options__generate_reg_form__sold_out_event',
351 351
                     $registration->event(),
@@ -355,7 +355,7 @@  discard block
 block discarded – undo
355 355
             }
356 356
             // are they allowed to pay now and is there monies owing?
357 357
             if ($registration->owes_monies_and_can_pay()) {
358
-                $registrations_requiring_payment[ $REG_ID ] = $registration;
358
+                $registrations_requiring_payment[$REG_ID] = $registration;
359 359
                 do_action(
360 360
                     'AHEE__EE_SPCO_Reg_Step_Payment_Options__generate_reg_form__event_requires_payment',
361 361
                     $registration->event(),
@@ -366,28 +366,28 @@  discard block
 block discarded – undo
366 366
                       && $registration->status_ID() !== RegStatus::AWAITING_REVIEW
367 367
                       && $registration->ticket()->is_free()
368 368
             ) {
369
-                $registrations_for_free_events[ $registration->ticket()->ID() ] = $registration;
369
+                $registrations_for_free_events[$registration->ticket()->ID()] = $registration;
370 370
             }
371 371
         }
372 372
         $subsections = [];
373 373
         // now decide which template to load
374
-        if (! empty($sold_out_events)) {
374
+        if ( ! empty($sold_out_events)) {
375 375
             $subsections['sold_out_events'] = $this->_sold_out_events($sold_out_events);
376 376
         }
377
-        if (! empty($insufficient_spaces_available)) {
377
+        if ( ! empty($insufficient_spaces_available)) {
378 378
             $subsections['insufficient_space'] = $this->_insufficient_spaces_available(
379 379
                 $insufficient_spaces_available
380 380
             );
381 381
         }
382
-        if (! empty($registrations_requiring_pre_approval)) {
382
+        if ( ! empty($registrations_requiring_pre_approval)) {
383 383
             $subsections['registrations_requiring_pre_approval'] = $this->_registrations_requiring_pre_approval(
384 384
                 $registrations_requiring_pre_approval
385 385
             );
386 386
         }
387
-        if (! empty($registrations_for_free_events)) {
387
+        if ( ! empty($registrations_for_free_events)) {
388 388
             $subsections['no_payment_required'] = $this->_no_payment_required($registrations_for_free_events);
389 389
         }
390
-        if (! empty($registrations_requiring_payment)) {
390
+        if ( ! empty($registrations_requiring_payment)) {
391 391
             if ($this->checkout->amount_owing > 0) {
392 392
                 // autoload Line_Item_Display classes
393 393
                 EEH_Autoloader::register_line_item_filter_autoloaders();
@@ -408,7 +408,7 @@  discard block
 block discarded – undo
408 408
                         ['registrations' => $registrations]
409 409
                     )
410 410
                 );
411
-                $this->checkout->amount_owing   = $filtered_line_item_tree->total();
411
+                $this->checkout->amount_owing = $filtered_line_item_tree->total();
412 412
                 $this->_apply_registration_payments_to_amount_owing($registrations);
413 413
             }
414 414
             $no_payment_required = false;
@@ -452,13 +452,13 @@  discard block
 block discarded – undo
452 452
      */
453 453
     public static function add_spco_line_item_filters(EE_Line_Item_Filter_Collection $line_item_filter_collection)
454 454
     {
455
-        if (! EE_Registry::instance()->SSN instanceof EE_Session) {
455
+        if ( ! EE_Registry::instance()->SSN instanceof EE_Session) {
456 456
             return $line_item_filter_collection;
457 457
         }
458
-        if (! EE_Registry::instance()->SSN->checkout() instanceof EE_Checkout) {
458
+        if ( ! EE_Registry::instance()->SSN->checkout() instanceof EE_Checkout) {
459 459
             return $line_item_filter_collection;
460 460
         }
461
-        if (! EE_Registry::instance()->SSN->checkout()->transaction instanceof EE_Transaction) {
461
+        if ( ! EE_Registry::instance()->SSN->checkout()->transaction instanceof EE_Transaction) {
462 462
             return $line_item_filter_collection;
463 463
         }
464 464
         $line_item_filter_collection->add(
@@ -498,8 +498,8 @@  discard block
 block discarded – undo
498 498
         );
499 499
         foreach ($registrations as $REG_ID => $registration) {
500 500
             // has this registration lost it's space ?
501
-            if (isset($ejected_registrations[ $REG_ID ])) {
502
-                unset($registrations[ $REG_ID ]);
501
+            if (isset($ejected_registrations[$REG_ID])) {
502
+                unset($registrations[$REG_ID]);
503 503
             }
504 504
         }
505 505
         return $registrations;
@@ -549,25 +549,25 @@  discard block
 block discarded – undo
549 549
             }
550 550
             $EVT_ID = $registration->event_ID();
551 551
             $ticket = $registration->ticket();
552
-            if (! isset($tickets_remaining[ $ticket->ID() ])) {
553
-                $tickets_remaining[ $ticket->ID() ] = $ticket->remaining();
552
+            if ( ! isset($tickets_remaining[$ticket->ID()])) {
553
+                $tickets_remaining[$ticket->ID()] = $ticket->remaining();
554 554
             }
555
-            if ($tickets_remaining[ $ticket->ID() ] > 0) {
556
-                if (! isset($event_reg_count[ $EVT_ID ])) {
557
-                    $event_reg_count[ $EVT_ID ] = 0;
555
+            if ($tickets_remaining[$ticket->ID()] > 0) {
556
+                if ( ! isset($event_reg_count[$EVT_ID])) {
557
+                    $event_reg_count[$EVT_ID] = 0;
558 558
                 }
559
-                $event_reg_count[ $EVT_ID ]++;
560
-                if (! isset($event_spaces_remaining[ $EVT_ID ])) {
561
-                    $event_spaces_remaining[ $EVT_ID ] = $registration->event()->spaces_remaining_for_sale();
559
+                $event_reg_count[$EVT_ID]++;
560
+                if ( ! isset($event_spaces_remaining[$EVT_ID])) {
561
+                    $event_spaces_remaining[$EVT_ID] = $registration->event()->spaces_remaining_for_sale();
562 562
                 }
563 563
             }
564 564
             if (
565 565
                 $revisit
566
-                && ($tickets_remaining[ $ticket->ID() ] === 0
567
-                    || $event_reg_count[ $EVT_ID ] > $event_spaces_remaining[ $EVT_ID ]
566
+                && ($tickets_remaining[$ticket->ID()] === 0
567
+                    || $event_reg_count[$EVT_ID] > $event_spaces_remaining[$EVT_ID]
568 568
                 )
569 569
             ) {
570
-                $ejected_registrations[ $REG_ID ] = $registration->event();
570
+                $ejected_registrations[$REG_ID] = $registration->event();
571 571
                 if ($registration->status_ID() !== RegStatus::WAIT_LIST) {
572 572
                     /** @type EE_Registration_Processor $registration_processor */
573 573
                     $registration_processor = EE_Registry::instance()->load_class('Registration_Processor');
@@ -627,7 +627,7 @@  discard block
 block discarded – undo
627 627
         foreach ($sold_out_events_array as $sold_out_event) {
628 628
             $sold_out_events .= EEH_HTML::li(
629 629
                 EEH_HTML::span(
630
-                    '  ' . $sold_out_event->name(),
630
+                    '  '.$sold_out_event->name(),
631 631
                     '',
632 632
                     'dashicons dashicons-marker ee-icon-size-16 pink-text'
633 633
                 )
@@ -683,7 +683,7 @@  discard block
 block discarded – undo
683 683
         foreach ($insufficient_spaces_events_array as $event) {
684 684
             if ($event instanceof EE_Event) {
685 685
                 $insufficient_space_events .= EEH_HTML::li(
686
-                    EEH_HTML::span(' ' . $event->name(), '', 'dashicons dashicons-marker ee-icon-size-16 pink-text')
686
+                    EEH_HTML::span(' '.$event->name(), '', 'dashicons dashicons-marker ee-icon-size-16 pink-text')
687 687
                 );
688 688
             }
689 689
         }
@@ -732,7 +732,7 @@  discard block
 block discarded – undo
732 732
         $events_requiring_pre_approval = [];
733 733
         foreach ($registrations_requiring_pre_approval as $registration) {
734 734
             if ($registration instanceof EE_Registration && $registration->event() instanceof EE_Event) {
735
-                $events_requiring_pre_approval[ $registration->event()->ID() ] = EEH_HTML::li(
735
+                $events_requiring_pre_approval[$registration->event()->ID()] = EEH_HTML::li(
736 736
                     EEH_HTML::span(
737 737
                         '',
738 738
                         '',
@@ -872,7 +872,7 @@  discard block
 block discarded – undo
872 872
     {
873 873
         return new EE_Form_Section_Proper(
874 874
             [
875
-                'html_id'         => 'ee-' . $this->slug() . '-extra-hidden-inputs',
875
+                'html_id'         => 'ee-'.$this->slug().'-extra-hidden-inputs',
876 876
                 'layout_strategy' => new EE_Div_Per_Section_Layout(),
877 877
                 'subsections'     => [
878 878
                     'spco_no_payment_required' => new EE_Hidden_Input(
@@ -911,7 +911,7 @@  discard block
 block discarded – undo
911 911
                 $payments += $registration->registration_payments();
912 912
             }
913 913
         }
914
-        if (! empty($payments)) {
914
+        if ( ! empty($payments)) {
915 915
             foreach ($payments as $payment) {
916 916
                 if ($payment instanceof EE_Registration_Payment) {
917 917
                     $this->checkout->amount_owing -= $payment->amount();
@@ -1000,7 +1000,7 @@  discard block
 block discarded – undo
1000 1000
             );
1001 1001
         }
1002 1002
         // switch up header depending on number of available payment methods
1003
-        $payment_method_header     = count($this->checkout->available_payment_methods) > 1
1003
+        $payment_method_header = count($this->checkout->available_payment_methods) > 1
1004 1004
             ? apply_filters(
1005 1005
                 'FHEE__registration_page_payment_options__method_of_payment_hdr',
1006 1006
                 esc_html__('Please Select Your Method of Payment', 'event_espresso')
@@ -1034,7 +1034,7 @@  discard block
 block discarded – undo
1034 1034
                 $payment_method_button = EEH_HTML::img(
1035 1035
                     $payment_method->button_url(),
1036 1036
                     $payment_method->name(),
1037
-                    'spco-payment-method-' . $payment_method->slug() . '-btn-img',
1037
+                    'spco-payment-method-'.$payment_method->slug().'-btn-img',
1038 1038
                     'spco-payment-method-btn-img'
1039 1039
                 );
1040 1040
                 // check if any payment methods are set as default
@@ -1042,15 +1042,15 @@  discard block
 block discarded – undo
1042 1042
                 // open_by_default
1043 1043
                 if (
1044 1044
                     ($this->checkout->selected_method_of_payment === $payment_method->slug())
1045
-                    || (! $this->checkout->selected_method_of_payment && $payment_method->open_by_default())
1045
+                    || ( ! $this->checkout->selected_method_of_payment && $payment_method->open_by_default())
1046 1046
                 ) {
1047 1047
                     $this->checkout->selected_method_of_payment = $payment_method->slug();
1048 1048
                     $this->_save_selected_method_of_payment();
1049
-                    $default_payment_method_option[ $payment_method->slug() ] = $payment_method_button;
1049
+                    $default_payment_method_option[$payment_method->slug()] = $payment_method_button;
1050 1050
                 } else {
1051
-                    $available_payment_method_options[ $payment_method->slug() ] = $payment_method_button;
1051
+                    $available_payment_method_options[$payment_method->slug()] = $payment_method_button;
1052 1052
                 }
1053
-                $payment_methods_billing_info[ $payment_method->slug() . '-info' ] =
1053
+                $payment_methods_billing_info[$payment_method->slug().'-info'] =
1054 1054
                     $this->_payment_method_billing_info(
1055 1055
                         $payment_method
1056 1056
                     );
@@ -1063,7 +1063,7 @@  discard block
 block discarded – undo
1063 1063
         $available_payment_methods['available_payment_methods'] = $this->_available_payment_method_inputs(
1064 1064
             $available_payment_method_options
1065 1065
         );
1066
-        $available_payment_methods                              += $payment_methods_billing_info;
1066
+        $available_payment_methods += $payment_methods_billing_info;
1067 1067
         // build the available payment methods form
1068 1068
         return new EE_Form_Section_Proper(
1069 1069
             [
@@ -1086,7 +1086,7 @@  discard block
 block discarded – undo
1086 1086
      */
1087 1087
     protected function _get_available_payment_methods()
1088 1088
     {
1089
-        if (! empty($this->checkout->available_payment_methods)) {
1089
+        if ( ! empty($this->checkout->available_payment_methods)) {
1090 1090
             return $this->checkout->available_payment_methods;
1091 1091
         }
1092 1092
         $available_payment_methods = [];
@@ -1098,7 +1098,7 @@  discard block
 block discarded – undo
1098 1098
         );
1099 1099
         foreach ($payment_methods as $payment_method) {
1100 1100
             if ($payment_method instanceof EE_Payment_Method) {
1101
-                $available_payment_methods[ $payment_method->slug() ] = $payment_method;
1101
+                $available_payment_methods[$payment_method->slug()] = $payment_method;
1102 1102
             }
1103 1103
         }
1104 1104
         return $available_payment_methods;
@@ -1193,7 +1193,7 @@  discard block
 block discarded – undo
1193 1193
         );
1194 1194
         return new EE_Form_Section_Proper(
1195 1195
             [
1196
-                'html_id'         => 'spco-payment-method-info-' . $payment_method->slug(),
1196
+                'html_id'         => 'spco-payment-method-info-'.$payment_method->slug(),
1197 1197
                 'html_class'      => 'spco-payment-method-info-dv',
1198 1198
                 // only display the selected or default PM
1199 1199
                 'html_style'      => $currently_selected ? '' : 'display:none;',
@@ -1222,7 +1222,7 @@  discard block
 block discarded – undo
1222 1222
         // how have they chosen to pay?
1223 1223
         $this->checkout->selected_method_of_payment = $this->_get_selected_method_of_payment(true);
1224 1224
         $this->checkout->payment_method             = $this->_get_payment_method_for_selected_method_of_payment();
1225
-        if (! $this->checkout->payment_method instanceof EE_Payment_Method) {
1225
+        if ( ! $this->checkout->payment_method instanceof EE_Payment_Method) {
1226 1226
             return false;
1227 1227
         }
1228 1228
         if (
@@ -1397,7 +1397,7 @@  discard block
 block discarded – undo
1397 1397
      */
1398 1398
     public function switch_payment_method()
1399 1399
     {
1400
-        if (! $this->_verify_payment_method_is_set()) {
1400
+        if ( ! $this->_verify_payment_method_is_set()) {
1401 1401
             return false;
1402 1402
         }
1403 1403
         if (
@@ -1533,7 +1533,7 @@  discard block
 block discarded – undo
1533 1533
             }
1534 1534
         }
1535 1535
         // verify payment method
1536
-        if (! $this->checkout->payment_method instanceof EE_Payment_Method) {
1536
+        if ( ! $this->checkout->payment_method instanceof EE_Payment_Method) {
1537 1537
             // get payment method for selected method of payment
1538 1538
             $this->checkout->payment_method = $this->_get_payment_method_for_selected_method_of_payment();
1539 1539
         }
@@ -1558,7 +1558,7 @@  discard block
 block discarded – undo
1558 1558
      */
1559 1559
     public function save_payer_details_via_ajax()
1560 1560
     {
1561
-        if (! $this->_verify_payment_method_is_set()) {
1561
+        if ( ! $this->_verify_payment_method_is_set()) {
1562 1562
             return;
1563 1563
         }
1564 1564
         // generate billing form for selected method of payment if it hasn't been done already
@@ -1568,7 +1568,7 @@  discard block
 block discarded – undo
1568 1568
             );
1569 1569
         }
1570 1570
         // generate primary attendee from payer info if applicable
1571
-        if (! $this->checkout->transaction_has_primary_registrant()) {
1571
+        if ( ! $this->checkout->transaction_has_primary_registrant()) {
1572 1572
             $attendee = $this->_create_attendee_from_request_data();
1573 1573
             if ($attendee instanceof EE_Attendee) {
1574 1574
                 foreach ($this->checkout->transaction->registrations() as $registration) {
@@ -1599,7 +1599,7 @@  discard block
 block discarded – undo
1599 1599
     {
1600 1600
         // get State ID
1601 1601
         $STA_ID = $this->request->getRequestParam('state');
1602
-        if (! empty($STA_ID)) {
1602
+        if ( ! empty($STA_ID)) {
1603 1603
             // can we get state object from name ?
1604 1604
             EE_Registry::instance()->load_model('State');
1605 1605
             $state  = EEM_State::instance()->get_col([['STA_name' => $STA_ID], 'limit' => 1], 'STA_ID');
@@ -1607,7 +1607,7 @@  discard block
 block discarded – undo
1607 1607
         }
1608 1608
         // get Country ISO
1609 1609
         $CNT_ISO = $this->request->getRequestParam('country');
1610
-        if (! empty($CNT_ISO)) {
1610
+        if ( ! empty($CNT_ISO)) {
1611 1611
             // can we get country object from name ?
1612 1612
             EE_Registry::instance()->load_model('Country');
1613 1613
             $country = EEM_Country::instance()->get_col(
@@ -1788,7 +1788,7 @@  discard block
 block discarded – undo
1788 1788
     protected function _maybe_set_completed(EE_Payment $payment)
1789 1789
     {
1790 1790
         // Do we need to redirect them? If so, there's more work to be done.
1791
-        if (! $payment->redirect_url()) {
1791
+        if ( ! $payment->redirect_url()) {
1792 1792
             $this->set_completed();
1793 1793
         }
1794 1794
     }
@@ -1845,7 +1845,7 @@  discard block
 block discarded – undo
1845 1845
     private function _process_payment()
1846 1846
     {
1847 1847
         // basically confirm that the event hasn't sold out since they hit the page
1848
-        if (! $this->_last_second_ticket_verifications()) {
1848
+        if ( ! $this->_last_second_ticket_verifications()) {
1849 1849
             return null;
1850 1850
         }
1851 1851
         // ya gotta make a choice man
@@ -1856,7 +1856,7 @@  discard block
 block discarded – undo
1856 1856
             return null;
1857 1857
         }
1858 1858
         // get EE_Payment_Method object
1859
-        if (! $this->checkout->payment_method = $this->_get_payment_method_for_selected_method_of_payment()) {
1859
+        if ( ! $this->checkout->payment_method = $this->_get_payment_method_for_selected_method_of_payment()) {
1860 1860
             return null;
1861 1861
         }
1862 1862
         // setup billing form
@@ -1865,12 +1865,12 @@  discard block
 block discarded – undo
1865 1865
                 $this->checkout->payment_method
1866 1866
             );
1867 1867
             // bad billing form ?
1868
-            if (! $this->_billing_form_is_valid()) {
1868
+            if ( ! $this->_billing_form_is_valid()) {
1869 1869
                 return null;
1870 1870
             }
1871 1871
         }
1872 1872
         // ensure primary registrant has been fully processed
1873
-        if (! $this->_setup_primary_registrant_prior_to_payment()) {
1873
+        if ( ! $this->_setup_primary_registrant_prior_to_payment()) {
1874 1874
             return null;
1875 1875
         }
1876 1876
         // if session is close to expiring (under 10 minutes by default)
@@ -1926,7 +1926,7 @@  discard block
 block discarded – undo
1926 1926
     protected function _last_second_ticket_verifications()
1927 1927
     {
1928 1928
         // don't bother re-validating if not a return visit
1929
-        if (! $this->checkout->revisit) {
1929
+        if ( ! $this->checkout->revisit) {
1930 1930
             return true;
1931 1931
         }
1932 1932
         $registrations = $this->checkout->transaction->registrations();
@@ -1976,7 +1976,7 @@  discard block
 block discarded – undo
1976 1976
             $this->_get_payment_method_for_selected_method_of_payment()
1977 1977
         );
1978 1978
         $html                        = $payment_method_billing_info->get_html();
1979
-        $html                        .= $this->checkout->redirect_form;
1979
+        $html .= $this->checkout->redirect_form;
1980 1980
         /** @var ResponseInterface $response */
1981 1981
         $response = LoaderFactory::getLoader()->getShared(ResponseInterface::class);
1982 1982
         $response->addOutput($html);
@@ -1992,7 +1992,7 @@  discard block
 block discarded – undo
1992 1992
      */
1993 1993
     private function _billing_form_is_valid()
1994 1994
     {
1995
-        if (! $this->checkout->payment_method->type_obj()->has_billing_form()) {
1995
+        if ( ! $this->checkout->payment_method->type_obj()->has_billing_form()) {
1996 1996
             return true;
1997 1997
         }
1998 1998
         if ($this->checkout->billing_form instanceof EE_Billing_Info_Form) {
@@ -2110,7 +2110,7 @@  discard block
 block discarded – undo
2110 2110
     {
2111 2111
         // convert billing form data into an attendee
2112 2112
         $this->checkout->primary_attendee_obj = $this->checkout->billing_form->create_attendee_from_billing_form_data();
2113
-        if (! $this->checkout->primary_attendee_obj instanceof EE_Attendee) {
2113
+        if ( ! $this->checkout->primary_attendee_obj instanceof EE_Attendee) {
2114 2114
             EE_Error::add_error(
2115 2115
                 sprintf(
2116 2116
                     esc_html__(
@@ -2127,7 +2127,7 @@  discard block
 block discarded – undo
2127 2127
             return false;
2128 2128
         }
2129 2129
         $primary_registration = $this->checkout->transaction->primary_registration();
2130
-        if (! $primary_registration instanceof EE_Registration) {
2130
+        if ( ! $primary_registration instanceof EE_Registration) {
2131 2131
             EE_Error::add_error(
2132 2132
                 sprintf(
2133 2133
                     esc_html__(
@@ -2199,8 +2199,8 @@  discard block
 block discarded – undo
2199 2199
             return null;
2200 2200
         }
2201 2201
         // get EE_Payment_Method object
2202
-        if (isset($this->checkout->available_payment_methods[ $this->checkout->selected_method_of_payment ])) {
2203
-            $payment_method = $this->checkout->available_payment_methods[ $this->checkout->selected_method_of_payment ];
2202
+        if (isset($this->checkout->available_payment_methods[$this->checkout->selected_method_of_payment])) {
2203
+            $payment_method = $this->checkout->available_payment_methods[$this->checkout->selected_method_of_payment];
2204 2204
         } else {
2205 2205
             // load EEM_Payment_Method
2206 2206
             EE_Registry::instance()->load_model('Payment_Method');
@@ -2208,7 +2208,7 @@  discard block
 block discarded – undo
2208 2208
             $payment_method     = $EEM_Payment_Method->get_one_by_slug($this->checkout->selected_method_of_payment);
2209 2209
         }
2210 2210
         // verify $payment_method
2211
-        if (! $payment_method instanceof EE_Payment_Method) {
2211
+        if ( ! $payment_method instanceof EE_Payment_Method) {
2212 2212
             // not a payment
2213 2213
             EE_Error::add_error(
2214 2214
                 sprintf(
@@ -2226,7 +2226,7 @@  discard block
 block discarded – undo
2226 2226
             return null;
2227 2227
         }
2228 2228
         // and verify it has a valid Payment_Method Type object
2229
-        if (! $payment_method->type_obj() instanceof EE_PMT_Base) {
2229
+        if ( ! $payment_method->type_obj() instanceof EE_PMT_Base) {
2230 2230
             // not a payment
2231 2231
             EE_Error::add_error(
2232 2232
                 sprintf(
@@ -2264,7 +2264,7 @@  discard block
 block discarded – undo
2264 2264
         $payment = null;
2265 2265
         $this->checkout->transaction->save();
2266 2266
         $payment_processor = EE_Registry::instance()->load_core('Payment_Processor');
2267
-        if (! $payment_processor instanceof EE_Payment_Processor) {
2267
+        if ( ! $payment_processor instanceof EE_Payment_Processor) {
2268 2268
             return false;
2269 2269
         }
2270 2270
         try {
@@ -2368,7 +2368,7 @@  discard block
 block discarded – undo
2368 2368
             return true;
2369 2369
         }
2370 2370
         // verify payment object
2371
-        if (! $payment instanceof EE_Payment) {
2371
+        if ( ! $payment instanceof EE_Payment) {
2372 2372
             // not a payment
2373 2373
             EE_Error::add_error(
2374 2374
                 sprintf(
@@ -2418,7 +2418,7 @@  discard block
 block discarded – undo
2418 2418
                 // and lastly, let's bump the payment status to pending
2419 2419
                 $payment->set_status(EEM_Payment::status_id_pending);
2420 2420
                 $payment->save();
2421
-            } elseif (! $this->_process_payment_status($payment, EE_PMT_Base::onsite)) {
2421
+            } elseif ( ! $this->_process_payment_status($payment, EE_PMT_Base::onsite)) {
2422 2422
                 // User shouldn't be redirected. So let's process it here.
2423 2423
                 // $this->_setup_redirect_for_next_step();
2424 2424
                 $this->checkout->continue_reg = false;
@@ -2499,7 +2499,7 @@  discard block
 block discarded – undo
2499 2499
                     return false;
2500 2500
                 // bad payment
2501 2501
                 case EEM_Payment::status_id_failed:
2502
-                    if (! empty($msg)) {
2502
+                    if ( ! empty($msg)) {
2503 2503
                         EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
2504 2504
                         return false;
2505 2505
                     }
@@ -2557,18 +2557,18 @@  discard block
 block discarded – undo
2557 2557
         // how have they chosen to pay?
2558 2558
         $this->checkout->selected_method_of_payment = $this->_get_selected_method_of_payment(true);
2559 2559
         // get EE_Payment_Method object
2560
-        if (! $this->checkout->payment_method = $this->_get_payment_method_for_selected_method_of_payment()) {
2560
+        if ( ! $this->checkout->payment_method = $this->_get_payment_method_for_selected_method_of_payment()) {
2561 2561
             $this->checkout->continue_reg = false;
2562 2562
             return false;
2563 2563
         }
2564
-        if (! $this->checkout->payment_method->is_off_site()) {
2564
+        if ( ! $this->checkout->payment_method->is_off_site()) {
2565 2565
             return false;
2566 2566
         }
2567 2567
         $this->_validate_offsite_return();
2568 2568
         // verify TXN
2569 2569
         if ($this->checkout->transaction instanceof EE_Transaction) {
2570 2570
             $gateway = $this->checkout->payment_method->type_obj()->get_gateway();
2571
-            if (! $gateway instanceof EE_Offsite_Gateway) {
2571
+            if ( ! $gateway instanceof EE_Offsite_Gateway) {
2572 2572
                 $this->checkout->continue_reg = false;
2573 2573
                 return false;
2574 2574
             }
@@ -2582,7 +2582,7 @@  discard block
 block discarded – undo
2582 2582
                 $this->checkout->payment = $payment;
2583 2583
                 // mark this reg step as completed, as long as gateway doesn't use a separate IPN request,
2584 2584
                 // because we will complete this step during the IPN processing then
2585
-                if (! $this->handle_IPN_in_this_request()) {
2585
+                if ( ! $this->handle_IPN_in_this_request()) {
2586 2586
                     $this->set_completed();
2587 2587
                 }
2588 2588
                 return true;
@@ -2685,13 +2685,13 @@  discard block
 block discarded – undo
2685 2685
      */
2686 2686
     private function _redirect_wayward_request(EE_Registration $primary_registrant)
2687 2687
     {
2688
-        if (! $primary_registrant instanceof EE_Registration) {
2688
+        if ( ! $primary_registrant instanceof EE_Registration) {
2689 2689
             // try redirecting based on the current TXN
2690 2690
             $primary_registrant = $this->checkout->transaction instanceof EE_Transaction
2691 2691
                 ? $this->checkout->transaction->primary_registration()
2692 2692
                 : null;
2693 2693
         }
2694
-        if (! $primary_registrant instanceof EE_Registration) {
2694
+        if ( ! $primary_registrant instanceof EE_Registration) {
2695 2695
             EE_Error::add_error(
2696 2696
                 sprintf(
2697 2697
                     esc_html__(
@@ -2762,7 +2762,7 @@  discard block
 block discarded – undo
2762 2762
             $payment = $this->checkout->transaction->last_payment();
2763 2763
             // $payment_source = 'last_payment after Exception';
2764 2764
             // but if we STILL don't have a payment object
2765
-            if (! $payment instanceof EE_Payment) {
2765
+            if ( ! $payment instanceof EE_Payment) {
2766 2766
                 // then we'll object ! ( not object like a thing... but object like what a lawyer says ! )
2767 2767
                 $this->_handle_payment_processor_exception($e);
2768 2768
             }
Please login to merge, or discard this patch.
single_page_checkout/templates/registration_page_wrapper.template.php 2 patches
Indentation   +43 added lines, -43 removed lines patch added patch discarded remove patch
@@ -17,37 +17,37 @@  discard block
 block discarded – undo
17 17
 
18 18
 <div id="ee-single-page-checkout-dv" class="">
19 19
     <?php
20
-    if (! $empty_cart) {
21
-        if ($use_session_countdown) { ?>
20
+	if (! $empty_cart) {
21
+		if ($use_session_countdown) { ?>
22 22
             <p id="spco-registration-time-limit-pg" class="spco-steps-pg ee-attention important-notice">
23 23
                 <?php echo sprintf(
24
-                    apply_filters(
25
-                        'FHEE__registration_page_wrapper_template___time_limit',
26
-                        esc_html__('You have %1$s to complete your registration.', 'event_espresso')
27
-                    ),
28
-                    '<span id="spco-registration-time-limit-spn" class="spco-registration-time-limit-spn">'
29
-                    . $registration_time_limit
30
-                    . '</span>'
31
-                );
32
-                ?>
24
+					apply_filters(
25
+						'FHEE__registration_page_wrapper_template___time_limit',
26
+						esc_html__('You have %1$s to complete your registration.', 'event_espresso')
27
+					),
28
+					'<span id="spco-registration-time-limit-spn" class="spco-registration-time-limit-spn">'
29
+					. $registration_time_limit
30
+					. '</span>'
31
+				);
32
+				?>
33 33
                 <span id="spco-registration-expiration-spn" class="" style="display:none;"></span>
34 34
             </p>
35 35
         <?php }
36
-        if (! $revisit && apply_filters('FHEE__registration_page_wrapper_template__steps_display', true)) {
37
-            ?>
36
+		if (! $revisit && apply_filters('FHEE__registration_page_wrapper_template__steps_display', true)) {
37
+			?>
38 38
             <h2 id="spco-steps-big-hdr" class="spco-steps-big-hdr">
39 39
                 <?php esc_html_e(' Steps', 'event_espresso'); ?>
40 40
             </h2>
41 41
 
42 42
             <div id="spco-steps-display-dv">
43 43
                 <?php
44
-                $step_nmbr = 1;
45
-                $total_steps = count($reg_steps) - 1;
46
-                foreach ($reg_steps as $reg_step) {
47
-                    if ($reg_step instanceof EE_SPCO_Reg_Step && $reg_step->slug() != 'finalize_registration') {
48
-                        $slug = $reg_step->slug();
49
-                        $step_display_dv_class = $reg_step->is_current_step() ? 'active-step' : 'inactive-step';
50
-                        ?>
44
+				$step_nmbr = 1;
45
+				$total_steps = count($reg_steps) - 1;
46
+				foreach ($reg_steps as $reg_step) {
47
+					if ($reg_step instanceof EE_SPCO_Reg_Step && $reg_step->slug() != 'finalize_registration') {
48
+						$slug = $reg_step->slug();
49
+						$step_display_dv_class = $reg_step->is_current_step() ? 'active-step' : 'inactive-step';
50
+						?>
51 51
                         <div id="spco-step-<?php echo esc_attr($slug); ?>-display-dv"
52 52
                              class="spco-step-display-dv <?php echo esc_attr($step_display_dv_class); ?> steps-<?php echo esc_attr($total_steps); ?>"
53 53
                         >
@@ -60,28 +60,28 @@  discard block
 block discarded – undo
60 60
                         </div>
61 61
 
62 62
                         <?php
63
-                        if ($step_nmbr < $total_steps) { ?>
63
+						if ($step_nmbr < $total_steps) { ?>
64 64
                             <div class="spco-step-arrow-dv">&raquo;</div>
65 65
                             <?php
66
-                        }
67
-                        $step_nmbr++;
68
-                    }
69
-                }
70
-                ?>
66
+						}
67
+						$step_nmbr++;
68
+					}
69
+				}
70
+				?>
71 71
                 <div class="clear-float"></div>
72 72
             </div>
73 73
 
74 74
             <?php
75
-        }
75
+		}
76 76
 
77
-        do_action('AHEE__SPCO__before_registration_steps');
78
-        $step_nmbr = 1;
79
-        foreach ($reg_steps as $reg_step) {
80
-            if ($reg_step instanceof EE_SPCO_Reg_Step && $reg_step->slug() != 'finalize_registration') {
81
-                $slug = $reg_step->slug();
82
-                do_action('AHEE__' . $slug . '__reg_step_start', $reg_step);
83
-                // todo: deprecate hook AHEE__registration_page_attendee_information__start
84
-                ?>
77
+		do_action('AHEE__SPCO__before_registration_steps');
78
+		$step_nmbr = 1;
79
+		foreach ($reg_steps as $reg_step) {
80
+			if ($reg_step instanceof EE_SPCO_Reg_Step && $reg_step->slug() != 'finalize_registration') {
81
+				$slug = $reg_step->slug();
82
+				do_action('AHEE__' . $slug . '__reg_step_start', $reg_step);
83
+				// todo: deprecate hook AHEE__registration_page_attendee_information__start
84
+				?>
85 85
                 <div id="spco-<?php echo esc_attr($slug); ?>-dv"
86 86
                      class="spco-step-dv <?php echo esc_attr($reg_step->div_class()); ?>"
87 87
                 >
@@ -89,19 +89,19 @@  discard block
 block discarded – undo
89 89
                     <?php do_action('AHEE__SPCO_after_reg_step_form', $slug, $next_step); ?>
90 90
                 </div>
91 91
                 <?php $step_nmbr++;
92
-            }
93
-        }
94
-        do_action('AHEE__SPCO__after_registration_steps');
95
-    } else {
96
-        ?>
92
+			}
93
+		}
94
+		do_action('AHEE__SPCO__after_registration_steps');
95
+	} else {
96
+		?>
97 97
         <h3 id="spco-empty-cart-hdr" class="spco-step-title-hdr">
98 98
             <?php esc_html_e('Nothing in your Event Queue', 'event_espresso'); ?>
99 99
         </h3>
100 100
         <p><?php echo wp_kses($empty_msg, AllowedTags::getWithFormTags()); ?></p>
101 101
         <?php echo wp_kses($cookies_not_set_msg, AllowedTags::getWithFormTags()); ?>
102 102
         <?php
103
-    }
104
-    do_action('AHEE__SPCO__reg_form_footer');
105
-    ?>
103
+	}
104
+	do_action('AHEE__SPCO__reg_form_footer');
105
+	?>
106 106
 
107 107
 </div>
Please login to merge, or discard this patch.
Spacing   +3 added lines, -3 removed lines patch added patch discarded remove patch
@@ -17,7 +17,7 @@  discard block
 block discarded – undo
17 17
 
18 18
 <div id="ee-single-page-checkout-dv" class="">
19 19
     <?php
20
-    if (! $empty_cart) {
20
+    if ( ! $empty_cart) {
21 21
         if ($use_session_countdown) { ?>
22 22
             <p id="spco-registration-time-limit-pg" class="spco-steps-pg ee-attention important-notice">
23 23
                 <?php echo sprintf(
@@ -33,7 +33,7 @@  discard block
 block discarded – undo
33 33
                 <span id="spco-registration-expiration-spn" class="" style="display:none;"></span>
34 34
             </p>
35 35
         <?php }
36
-        if (! $revisit && apply_filters('FHEE__registration_page_wrapper_template__steps_display', true)) {
36
+        if ( ! $revisit && apply_filters('FHEE__registration_page_wrapper_template__steps_display', true)) {
37 37
             ?>
38 38
             <h2 id="spco-steps-big-hdr" class="spco-steps-big-hdr">
39 39
                 <?php esc_html_e(' Steps', 'event_espresso'); ?>
@@ -79,7 +79,7 @@  discard block
 block discarded – undo
79 79
         foreach ($reg_steps as $reg_step) {
80 80
             if ($reg_step instanceof EE_SPCO_Reg_Step && $reg_step->slug() != 'finalize_registration') {
81 81
                 $slug = $reg_step->slug();
82
-                do_action('AHEE__' . $slug . '__reg_step_start', $reg_step);
82
+                do_action('AHEE__'.$slug.'__reg_step_start', $reg_step);
83 83
                 // todo: deprecate hook AHEE__registration_page_attendee_information__start
84 84
                 ?>
85 85
                 <div id="spco-<?php echo esc_attr($slug); ?>-dv"
Please login to merge, or discard this patch.
modules/single_page_checkout/EED_Single_Page_Checkout.module.php 2 patches
Indentation   +1691 added lines, -1691 removed lines patch added patch discarded remove patch
@@ -20,1695 +20,1695 @@
 block discarded – undo
20 20
  */
21 21
 class EED_Single_Page_Checkout extends EED_Module
22 22
 {
23
-    /**
24
-     * $_initialized - has the SPCO controller already been initialized ?
25
-     */
26
-    private static bool $_initialized = false;
27
-
28
-
29
-    /**
30
-     * $_checkout_verified - is the EE_Checkout verified as correct for this request ?
31
-     */
32
-    private static bool $_checkout_verified = true;
33
-
34
-    /**
35
-     * $_reg_steps_array - holds initial array of reg steps
36
-     *
37
-     * @var array $_reg_steps_array
38
-     */
39
-    private static array $_reg_steps_array = [];
40
-
41
-    /**
42
-     * $checkout - EE_Checkout object for handling the properties of the current checkout process
43
-     */
44
-    public ?EE_Checkout $checkout = null;
45
-
46
-    protected ?RequestInterface $request = null;
47
-
48
-    private bool $debug = false;    //  true    false
49
-
50
-
51
-    /**
52
-     * @return EED_Single_Page_Checkout|EED_Module
53
-     * @throws EE_Error
54
-     * @throws ReflectionException
55
-     */
56
-    public static function instance()
57
-    {
58
-        add_filter('EED_Single_Page_Checkout__SPCO_active', '__return_true');
59
-        return parent::get_instance(__CLASS__);
60
-    }
61
-
62
-
63
-    /**
64
-     * @return EE_CART
65
-     */
66
-    public function cart(): EE_CART
67
-    {
68
-        return $this->checkout->cart;
69
-    }
70
-
71
-
72
-    /**
73
-     * @return RequestInterface
74
-     * @since   4.10.14.p
75
-     */
76
-    public static function getRequest(): RequestInterface
77
-    {
78
-        return LoaderFactory::getLoader()->getShared(RequestInterface::class);
79
-    }
80
-
81
-
82
-    /**
83
-     * @return EE_Transaction
84
-     */
85
-    public function transaction(): EE_Transaction
86
-    {
87
-        return $this->checkout->transaction;
88
-    }
89
-
90
-
91
-    /**
92
-     *    set_hooks - for hooking into EE Core, other modules, etc
93
-     *
94
-     * @return    void
95
-     * @throws EE_Error
96
-     */
97
-    public static function set_hooks()
98
-    {
99
-        EED_Single_Page_Checkout::set_definitions();
100
-    }
101
-
102
-
103
-    /**
104
-     *    set_hooks_admin - for hooking into EE Admin Core, other modules, etc
105
-     *
106
-     * @return    void
107
-     * @throws EE_Error
108
-     */
109
-    public static function set_hooks_admin()
110
-    {
111
-        EED_Single_Page_Checkout::set_definitions();
112
-        if (! (defined('DOING_AJAX') && DOING_AJAX)) {
113
-            return;
114
-        }
115
-        // going to start an output buffer in case anything gets accidentally output
116
-        // that might disrupt our JSON response
117
-        ob_start();
118
-        EED_Single_Page_Checkout::load_reg_steps();
119
-        // set ajax hooks
120
-        add_action('wp_ajax_process_reg_step', ['EED_Single_Page_Checkout', 'process_reg_step']);
121
-        add_action('wp_ajax_nopriv_process_reg_step', ['EED_Single_Page_Checkout', 'process_reg_step']);
122
-        add_action('wp_ajax_display_spco_reg_step', ['EED_Single_Page_Checkout', 'display_reg_step']);
123
-        add_action('wp_ajax_nopriv_display_spco_reg_step', ['EED_Single_Page_Checkout', 'display_reg_step']);
124
-        add_action('wp_ajax_update_reg_step', ['EED_Single_Page_Checkout', 'update_reg_step']);
125
-        add_action('wp_ajax_nopriv_update_reg_step', ['EED_Single_Page_Checkout', 'update_reg_step']);
126
-    }
127
-
128
-
129
-    /**
130
-     *    process ajax request
131
-     *
132
-     * @param string $ajax_action
133
-     * @throws EE_Error
134
-     * @throws ReflectionException
135
-     */
136
-    public static function process_ajax_request(string $ajax_action)
137
-    {
138
-        $request = EED_Single_Page_Checkout::getRequest();
139
-        $request->setRequestParam('action', $ajax_action);
140
-        EED_Single_Page_Checkout::instance()->_initialize();
141
-    }
142
-
143
-
144
-    /**
145
-     * ajax display registration step
146
-     *
147
-     * @throws EE_Error
148
-     * @throws ReflectionException
149
-     */
150
-    public static function display_reg_step()
151
-    {
152
-        EED_Single_Page_Checkout::process_ajax_request('display_spco_reg_step');
153
-    }
154
-
155
-
156
-    /**
157
-     * ajax process registration step
158
-     *
159
-     * @throws EE_Error
160
-     * @throws ReflectionException
161
-     */
162
-    public static function process_reg_step()
163
-    {
164
-        EED_Single_Page_Checkout::process_ajax_request('process_reg_step');
165
-    }
166
-
167
-
168
-    /**
169
-     * ajax process registration step
170
-     *
171
-     * @throws EE_Error
172
-     * @throws ReflectionException
173
-     */
174
-    public static function update_reg_step()
175
-    {
176
-        EED_Single_Page_Checkout::process_ajax_request('update_reg_step');
177
-    }
178
-
179
-
180
-    /**
181
-     * update_checkout
182
-     *
183
-     * @return void
184
-     * @throws ReflectionException
185
-     * @throws EE_Error
186
-     */
187
-    public static function update_checkout()
188
-    {
189
-        EED_Single_Page_Checkout::process_ajax_request('update_checkout');
190
-    }
191
-
192
-
193
-    /**
194
-     *    set_definitions
195
-     *
196
-     * @return    void
197
-     * @throws EE_Error
198
-     */
199
-    public static function set_definitions()
200
-    {
201
-        if (defined('SPCO_BASE_PATH')) {
202
-            return;
203
-        }
204
-        define(
205
-            'SPCO_BASE_PATH',
206
-            rtrim(str_replace(['\\', '/'], '/', plugin_dir_path(__FILE__)), '/') . '/'
207
-        );
208
-        define('SPCO_CSS_URL', plugin_dir_url(__FILE__) . 'css/');
209
-        define('SPCO_IMG_URL', plugin_dir_url(__FILE__) . 'img/');
210
-        define('SPCO_JS_URL', plugin_dir_url(__FILE__) . 'js/');
211
-        define('SPCO_INC_PATH', SPCO_BASE_PATH . 'inc/');
212
-        define('SPCO_REG_STEPS_PATH', SPCO_BASE_PATH . 'reg_steps/');
213
-        define('SPCO_TEMPLATES_PATH', SPCO_BASE_PATH . 'templates/');
214
-        EEH_Autoloader::register_autoloaders_for_each_file_in_folder(SPCO_BASE_PATH, true);
215
-        EE_Registry::$i18n_js_strings['registration_expiration_notice'] =
216
-            EED_Single_Page_Checkout::getRegistrationExpirationNotice();
217
-    }
218
-
219
-
220
-    /**
221
-     * load_reg_steps
222
-     * loads and instantiates each reg step based on the EE_Registry::instance()->CFG->registration->reg_steps array
223
-     *
224
-     * @throws EE_Error
225
-     */
226
-    public static function load_reg_steps()
227
-    {
228
-        static $reg_steps_loaded = false;
229
-        if ($reg_steps_loaded) {
230
-            return;
231
-        }
232
-        // filter list of reg_steps
233
-        $reg_steps_to_load = (array) apply_filters(
234
-            'AHEE__SPCO__load_reg_steps__reg_steps_to_load',
235
-            EED_Single_Page_Checkout::get_reg_steps()
236
-        );
237
-        // sort by key (order)
238
-        ksort($reg_steps_to_load);
239
-        // loop through folders
240
-        foreach ($reg_steps_to_load as $order => $reg_step) {
241
-            // we need a
242
-            if (isset($reg_step['file_path'], $reg_step['class_name'], $reg_step['slug'])) {
243
-                // copy over to the reg_steps_array
244
-                EED_Single_Page_Checkout::$_reg_steps_array[ $order ] = $reg_step;
245
-                // register custom key route for each reg step
246
-                // ie: step=>"slug" - this is the entire reason we load the reg steps array now
247
-                EE_Config::register_route(
248
-                    $reg_step['slug'],
249
-                    'EED_Single_Page_Checkout',
250
-                    'run',
251
-                    'step'
252
-                );
253
-                // add AJAX or other hooks
254
-                if (isset($reg_step['has_hooks']) && $reg_step['has_hooks']) {
255
-                    // setup autoloaders if necessary
256
-                    if (! class_exists($reg_step['class_name'])) {
257
-                        EEH_Autoloader::register_autoloaders_for_each_file_in_folder(
258
-                            $reg_step['file_path'],
259
-                            true
260
-                        );
261
-                    }
262
-                    if (is_callable($reg_step['class_name'], 'set_hooks')) {
263
-                        call_user_func([$reg_step['class_name'], 'set_hooks']);
264
-                    }
265
-                }
266
-            }
267
-        }
268
-        $reg_steps_loaded = true;
269
-    }
270
-
271
-
272
-    /**
273
-     *    get_reg_steps
274
-     *
275
-     * @return    array
276
-     */
277
-    public static function get_reg_steps(): array
278
-    {
279
-        $reg_steps = EE_Registry::instance()->CFG->registration->reg_steps;
280
-        if (empty($reg_steps)) {
281
-            $reg_steps = [
282
-                10  => [
283
-                    'file_path'  => SPCO_REG_STEPS_PATH . 'attendee_information',
284
-                    'class_name' => 'EE_SPCO_Reg_Step_Attendee_Information',
285
-                    'slug'       => 'attendee_information',
286
-                    'has_hooks'  => false,
287
-                ],
288
-                30  => [
289
-                    'file_path'  => SPCO_REG_STEPS_PATH . 'payment_options',
290
-                    'class_name' => 'EE_SPCO_Reg_Step_Payment_Options',
291
-                    'slug'       => 'payment_options',
292
-                    'has_hooks'  => true,
293
-                ],
294
-                999 => [
295
-                    'file_path'  => SPCO_REG_STEPS_PATH . 'finalize_registration',
296
-                    'class_name' => 'EE_SPCO_Reg_Step_Finalize_Registration',
297
-                    'slug'       => 'finalize_registration',
298
-                    'has_hooks'  => false,
299
-                ],
300
-            ];
301
-        }
302
-        return $reg_steps;
303
-    }
304
-
305
-
306
-    /**
307
-     * @return array|string
308
-     * @throws EE_Error
309
-     * @throws ReflectionException
310
-     */
311
-    public static function registration_checkout_for_admin()
312
-    {
313
-        $request = EED_Single_Page_Checkout::getRequest();
314
-        $request->setRequestParam('step', 'attendee_information');
315
-        $request->setRequestParam('action', 'display_spco_reg_step');
316
-        $request->setRequestParam('process_form_submission', false);
317
-        EED_Single_Page_Checkout::instance()->_initialize();
318
-        EED_Single_Page_Checkout::instance()->_display_spco_reg_form();
319
-        return EED_Single_Page_Checkout::getResponse()->getOutput();
320
-    }
321
-
322
-
323
-    /**
324
-     * @return EE_Transaction|null
325
-     * @throws EE_Error
326
-     * @throws ReflectionException
327
-     */
328
-    public static function process_registration_from_admin(): ?EE_Transaction
329
-    {
330
-        $request = EED_Single_Page_Checkout::getRequest();
331
-        $request->setRequestParam('step', 'attendee_information');
332
-        $request->setRequestParam('action', 'process_reg_step');
333
-        $request->setRequestParam('process_form_submission', true);
334
-        EED_Single_Page_Checkout::instance()->_initialize();
335
-        if (EED_Single_Page_Checkout::instance()->checkout->current_step->completed()) {
336
-            $final_reg_step = end(EED_Single_Page_Checkout::instance()->checkout->reg_steps);
337
-            if ($final_reg_step instanceof EE_SPCO_Reg_Step_Finalize_Registration) {
338
-                EED_Single_Page_Checkout::instance()->checkout->set_reg_step_initiated($final_reg_step);
339
-                if ($final_reg_step->process_reg_step()) {
340
-                    $final_reg_step->set_completed();
341
-                    EED_Single_Page_Checkout::instance()->checkout->update_txn_reg_steps_array();
342
-                    return EED_Single_Page_Checkout::instance()->checkout->transaction;
343
-                }
344
-            }
345
-        }
346
-        return null;
347
-    }
348
-
349
-
350
-    /**
351
-     *    run
352
-     *
353
-     * @param WP_Query|null $WP_Query
354
-     * @return    void
355
-     */
356
-    public function run($WP_Query)
357
-    {
358
-        if (
359
-            $WP_Query instanceof WP_Query
360
-            && $WP_Query->is_main_query()
361
-            && apply_filters('FHEE__EED_Single_Page_Checkout__run', true)
362
-            && $this->_is_reg_checkout()
363
-        ) {
364
-            $this->_initialize();
365
-        }
366
-    }
367
-
368
-
369
-    /**
370
-     * determines whether current url matches reg page url
371
-     *
372
-     * @return bool
373
-     */
374
-    protected function _is_reg_checkout(): bool
375
-    {
376
-        // get current permalink for reg page without any extra query args
377
-        $reg_page_url = get_permalink(EE_Config::instance()->core->reg_page_id);
378
-        // get request URI for current request, but without the scheme or host
379
-        $current_request_uri = EEH_URL::filter_input_server_url();
380
-        $current_request_uri = html_entity_decode($current_request_uri);
381
-        // get array of query args from the current request URI
382
-        $query_args = EEH_URL::get_query_string($current_request_uri);
383
-        // grab page id if it is set
384
-        $page_id = isset($query_args['page_id']) ? absint($query_args['page_id']) : 0;
385
-        // and remove the page id from the query args (we will re-add it later)
386
-        unset($query_args['page_id']);
387
-        // now strip all query args from current request URI
388
-        $current_request_uri = remove_query_arg(array_keys($query_args), $current_request_uri);
389
-        // and re-add the page id if it was set
390
-        if ($page_id) {
391
-            $current_request_uri = add_query_arg('page_id', $page_id, $current_request_uri);
392
-        }
393
-        // remove slashes and ?
394
-        $current_request_uri = trim($current_request_uri, '?/');
395
-        // is current request URI part of the known full reg page URL ?
396
-        return ! empty($current_request_uri) && strpos($reg_page_url, $current_request_uri) !== false;
397
-    }
398
-
399
-
400
-    /**
401
-     * @param WP_Query $wp_query
402
-     * @return    void
403
-     * @throws EE_Error
404
-     * @throws ReflectionException
405
-     */
406
-    public static function init(WP_Query $wp_query)
407
-    {
408
-        EED_Single_Page_Checkout::instance()->run($wp_query);
409
-    }
410
-
411
-
412
-    /**
413
-     * @return void
414
-     */
415
-    private function _initialize()
416
-    {
417
-        // ensure SPCO doesn't run twice
418
-        if (EED_Single_Page_Checkout::$_initialized) {
419
-            return;
420
-        }
421
-        try {
422
-            $this->request = EED_Single_Page_Checkout::getRequest();
423
-            EED_Single_Page_Checkout::load_reg_steps();
424
-            $this->_verify_session();
425
-            // set up the EE_Checkout object
426
-            $this->checkout = $this->_initialize_checkout();
427
-            // filter checkout
428
-            $this->checkout = apply_filters('FHEE__EED_Single_Page_Checkout___initialize__checkout', $this->checkout);
429
-            // get the $_GET
430
-            $this->_get_request_vars();
431
-            if ($this->_block_bots()) {
432
-                return;
433
-            }
434
-            // filter continue_reg
435
-            $this->checkout->continue_reg = apply_filters(
436
-                'FHEE__EED_Single_Page_Checkout__init___continue_reg',
437
-                true,
438
-                $this->checkout
439
-            );
440
-            // load the reg steps array
441
-            if (! $this->_load_and_instantiate_reg_steps()) {
442
-                EED_Single_Page_Checkout::$_initialized = true;
443
-                return;
444
-            }
445
-            // set the current step
446
-            $this->checkout->set_current_step($this->checkout->step);
447
-            // and the next step
448
-            $this->checkout->set_next_step();
449
-            // verify that everything has been set up correctly
450
-            if (! ($this->_verify_transaction_and_get_registrations() && $this->_final_verifications())) {
451
-                EED_Single_Page_Checkout::$_initialized = true;
452
-                return;
453
-            }
454
-            do_action('AHEE__Single_Page_Checkout___initialize__after_final_verifications', $this->checkout);
455
-            // lock the transaction
456
-            $this->checkout->transaction->lock();
457
-            // make sure all of our cached objects are added to their respective model entity mappers
458
-            $this->checkout->refresh_all_entities();
459
-            // set amount owing
460
-            $this->checkout->amount_owing = $this->checkout->transaction->remaining();
461
-            // initialize each reg step, which gives them the chance to potentially alter the process
462
-            $this->_initialize_reg_steps();
463
-            // DEBUG LOG
464
-            // $this->checkout->log( __CLASS__, __FUNCTION__, __LINE__ );
465
-            // get reg form
466
-            if (! $this->_check_form_submission()) {
467
-                EED_Single_Page_Checkout::$_initialized = true;
468
-                return;
469
-            }
470
-            // checkout the action!!!
471
-            $this->_process_form_action();
472
-            // add some style and make it dance
473
-            $this->add_styles_and_scripts($this);
474
-            // kk... SPCO has successfully run
475
-            EED_Single_Page_Checkout::$_initialized = true;
476
-            // set no cache headers and constants
477
-            EE_System::do_not_cache();
478
-            // add anchor
479
-            add_action('loop_start', [$this, 'set_checkout_anchor'], 1);
480
-            // remove transaction lock
481
-            add_action('shutdown', [$this, 'unlock_transaction'], 1);
482
-        } catch (Exception $e) {
483
-            EE_Error::add_error($e->getMessage(), __FILE__, __FUNCTION__, __LINE__);
484
-        }
485
-    }
486
-
487
-
488
-    /**
489
-     * checks that the session is valid and not expired
490
-     *
491
-     * @throws EE_Error
492
-     * @throws ReflectionException
493
-     */
494
-    private function _verify_session()
495
-    {
496
-        if (! EE_Registry::instance()->SSN instanceof EE_Session) {
497
-            throw new EE_Error(esc_html__('The EE_Session class could not be loaded.', 'event_espresso'));
498
-        }
499
-        $clear_session_requested = $this->request->getRequestParam('clear_session', false, 'bool');
500
-        // is session still valid ?
501
-        if (
502
-            $clear_session_requested
503
-            || (
504
-                EE_Registry::instance()->SSN->expired()
505
-                && $this->request->getRequestParam('e_reg_url_link') === ''
506
-            )
507
-        ) {
508
-            $this->checkout = new EE_Checkout();
509
-            EE_Registry::instance()->SSN->clear_session(__CLASS__, __FUNCTION__);
510
-            // EE_Registry::instance()->SSN->reset_cart();
511
-            // EE_Registry::instance()->SSN->reset_checkout();
512
-            // EE_Registry::instance()->SSN->reset_transaction();
513
-            if (! $clear_session_requested) {
514
-                EE_Error::add_attention(
515
-                    EE_Registry::$i18n_js_strings['registration_expiration_notice'],
516
-                    __FILE__,
517
-                    __FUNCTION__,
518
-                    __LINE__
519
-                );
520
-            }
521
-            // EE_Registry::instance()->SSN->reset_expired();
522
-        }
523
-    }
524
-
525
-
526
-    /**
527
-     * loads and instantiates EE_Checkout
528
-     *
529
-     * @return EE_Checkout
530
-     * @throws EE_Error
531
-     * @throws ReflectionException
532
-     */
533
-    private function _initialize_checkout(): EE_Checkout
534
-    {
535
-        // look in session for existing checkout
536
-        /** @type EE_Checkout $checkout */
537
-        $checkout = EE_Registry::instance()->SSN->checkout();
538
-        // verify
539
-        if (! $checkout instanceof EE_Checkout) {
540
-            // instantiate EE_Checkout object for handling the properties of the current checkout process
541
-            $checkout = EE_Registry::instance()->load_file(
542
-                SPCO_INC_PATH,
543
-                'EE_Checkout',
544
-                'class',
545
-                [],
546
-                false
547
-            );
548
-        } else {
549
-            if ($checkout->current_step->is_final_step() && $checkout->exit_spco() === true) {
550
-                $this->unlock_transaction();
551
-                wp_safe_redirect($checkout->redirect_url);
552
-                exit();
553
-            }
554
-        }
555
-        $checkout = apply_filters('FHEE__EED_Single_Page_Checkout___initialize_checkout__checkout', $checkout);
556
-        // verify again
557
-        if (! $checkout instanceof EE_Checkout) {
558
-            throw new EE_Error(esc_html__('The EE_Checkout class could not be loaded.', 'event_espresso'));
559
-        }
560
-        // reset anything that needs a clean slate for each request
561
-        $checkout->reset_for_current_request();
562
-        return $checkout;
563
-    }
564
-
565
-
566
-    /**
567
-     * @return void
568
-     */
569
-    private function _get_request_vars()
570
-    {
571
-        // make sure this request is marked as belonging to EE
572
-        /** @var CurrentPage $current_page */
573
-        $current_page = LoaderFactory::getLoader()->getShared(CurrentPage::class);
574
-        $current_page->setEspressoPage(true);
575
-        // which step is being requested ?
576
-        $this->checkout->step = $this->request->getRequestParam('step', $this->_get_first_step());
577
-        // which step is being edited ?
578
-        $this->checkout->edit_step = $this->request->getRequestParam('edit_step');
579
-        // and what we're doing on the current step
580
-        $this->checkout->action = $this->request->getRequestParam('action', 'display_spco_reg_step');
581
-        // timestamp
582
-        $this->checkout->uts = $this->request->getRequestParam('uts', 0, 'int');
583
-        // returning to edit ?
584
-        $this->checkout->reg_url_link = $this->request->getRequestParam('e_reg_url_link');
585
-        // add reg url link to registration query params
586
-        if ($this->checkout->reg_url_link && strpos($this->checkout->reg_url_link, '1-') !== 0) {
587
-            $this->checkout->reg_cache_where_params[0]['REG_url_link'] = $this->checkout->reg_url_link;
588
-        }
589
-        // or some other kind of revisit ?
590
-        $this->checkout->revisit = $this->request->getRequestParam('revisit', false, 'bool');
591
-        // and whether to generate a reg form for this request
592
-        $this->checkout->generate_reg_form = $this->request->getRequestParam('generate_reg_form', true, 'bool');
593
-        // and whether to process a reg form submission for this request
594
-        $this->checkout->process_form_submission = $this->request->getRequestParam(
595
-            'process_form_submission',
596
-            $this->checkout->action === 'process_reg_step',
597
-            'bool'
598
-        );
599
-        $this->checkout->process_form_submission = filter_var(
600
-            $this->checkout->action !== 'display_spco_reg_step'
601
-                ? $this->checkout->process_form_submission
602
-                : false,
603
-            FILTER_VALIDATE_BOOLEAN
604
-        );
605
-        $this->_display_request_vars();
606
-    }
607
-
608
-
609
-    /**
610
-     * @return void
611
-     */
612
-    protected function _display_request_vars()
613
-    {
614
-        if (! ($this->debug && defined('WP_DEBUG') && WP_DEBUG)) {
615
-            return;
616
-        }
617
-        EEH_Debug_Tools::printr($this->request->requestParams(), 'requestParams', __FILE__, __LINE__);
618
-        EEH_Debug_Tools::printr($this->checkout->step, '$this->checkout->step', __FILE__, __LINE__);
619
-        EEH_Debug_Tools::printr($this->checkout->edit_step, '$this->checkout->edit_step', __FILE__, __LINE__);
620
-        EEH_Debug_Tools::printr($this->checkout->action, '$this->checkout->action', __FILE__, __LINE__);
621
-        EEH_Debug_Tools::printr($this->checkout->reg_url_link, '$this->checkout->reg_url_link', __FILE__, __LINE__);
622
-        EEH_Debug_Tools::printr($this->checkout->revisit, '$this->checkout->revisit', __FILE__, __LINE__);
623
-        EEH_Debug_Tools::printr(
624
-            $this->checkout->generate_reg_form,
625
-            '$this->checkout->generate_reg_form',
626
-            __FILE__,
627
-            __LINE__
628
-        );
629
-        EEH_Debug_Tools::printr(
630
-            $this->checkout->process_form_submission,
631
-            '$this->checkout->process_form_submission',
632
-            __FILE__,
633
-            __LINE__
634
-        );
635
-    }
636
-
637
-
638
-    /**
639
-     * _block_bots
640
-     * checks that the incoming request has either of the following set:
641
-     *  a UTS (unix timestamp) which indicates that the request was redirected from the Ticket Selector
642
-     *  a REG URL Link, which indicates that the request is a return visit to SPCO for a valid TXN
643
-     * so if you're not coming from the Ticket Selector nor returning for a valid IP...
644
-     * then where you coming from man?
645
-     *
646
-     * @return boolean
647
-     */
648
-    private function _block_bots(): bool
649
-    {
650
-        return EED_Invalid_Checkout_Access::getInvalidCheckoutAccess()->checkoutAccessIsInvalid($this->checkout);
651
-    }
652
-
653
-
654
-    /**
655
-     *  gets slug for first step in $_reg_steps_array
656
-     *
657
-     * @return string
658
-     */
659
-    private function _get_first_step(): string
660
-    {
661
-        $first_step = reset(EED_Single_Page_Checkout::$_reg_steps_array);
662
-        return $first_step['slug'] ?? 'attendee_information';
663
-    }
664
-
665
-
666
-    /**
667
-     * instantiates each reg step based on the loaded reg_steps array
668
-     *
669
-     * @return bool
670
-     * @throws EE_Error
671
-     * @throws InvalidArgumentException
672
-     * @throws InvalidDataTypeException
673
-     * @throws InvalidInterfaceException
674
-     * @throws ReflectionException
675
-     */
676
-    private function _load_and_instantiate_reg_steps(): bool
677
-    {
678
-        do_action('AHEE__Single_Page_Checkout___load_and_instantiate_reg_steps__start', $this->checkout);
679
-        // have reg_steps already been instantiated ?
680
-        if (
681
-            empty($this->checkout->reg_steps)
682
-            || apply_filters('FHEE__Single_Page_Checkout__load_reg_steps__reload_reg_steps', false, $this->checkout)
683
-        ) {
684
-            // if not, then loop through raw reg steps array
685
-            foreach (EED_Single_Page_Checkout::$_reg_steps_array as $order => $reg_step) {
686
-                if (! $this->_load_and_instantiate_reg_step($reg_step, $order)) {
687
-                    return false;
688
-                }
689
-            }
690
-            if (isset($this->checkout->reg_steps['registration_confirmation'])) {
691
-                // skip the registration_confirmation page ?
692
-                // just remove it from the reg steps array
693
-                $this->checkout->remove_reg_step('registration_confirmation', false);
694
-            }
695
-            // filter the array for good luck
696
-            $this->checkout->reg_steps = apply_filters(
697
-                'FHEE__Single_Page_Checkout__load_reg_steps__reg_steps',
698
-                $this->checkout->reg_steps
699
-            );
700
-            // finally re-sort based on the reg step class order properties
701
-            $this->checkout->sort_reg_steps();
702
-        } else {
703
-            foreach ($this->checkout->reg_steps as $reg_step) {
704
-                // set all current step stati to FALSE
705
-                $reg_step->set_is_current_step(false);
706
-            }
707
-        }
708
-        if (empty($this->checkout->reg_steps)) {
709
-            EE_Error::add_error(
710
-                esc_html__('No Reg Steps were loaded..', 'event_espresso'),
711
-                __FILE__,
712
-                __FUNCTION__,
713
-                __LINE__
714
-            );
715
-            return false;
716
-        }
717
-        // make reg step details available to JS
718
-        $this->checkout->set_reg_step_JSON_info();
719
-        return true;
720
-    }
721
-
722
-
723
-    /**
724
-     * @param array $reg_step
725
-     * @param int   $order
726
-     * @return bool
727
-     * @throws EE_Error
728
-     * @throws ReflectionException
729
-     */
730
-    private function _load_and_instantiate_reg_step(array $reg_step = [], int $order = 0): bool
731
-    {
732
-        // we need a file_path, class_name, and slug to add a reg step
733
-        if (isset($reg_step['file_path'], $reg_step['class_name'], $reg_step['slug'])) {
734
-            // if editing a specific step, but this is NOT that step... (and it's not the 'finalize_registration' step)
735
-            if (
736
-                $this->checkout->reg_url_link
737
-                && $this->checkout->step !== $reg_step['slug']
738
-                && $reg_step['slug'] !== 'finalize_registration'
739
-                // normally at this point we would NOT load the reg step, but this filter can change that
740
-                && apply_filters(
741
-                    'FHEE__Single_Page_Checkout___load_and_instantiate_reg_step__bypass_reg_step',
742
-                    true,
743
-                    $reg_step,
744
-                    $this->checkout
745
-                )
746
-            ) {
747
-                return true;
748
-            }
749
-
750
-            // instantiate step class using file path and class name
751
-            $reg_step_obj = EE_Registry::instance()->load_file(
752
-                $reg_step['file_path'],
753
-                $reg_step['class_name'],
754
-                'class',
755
-                [$this->checkout],
756
-                false
757
-            );
758
-            // did we get the goods ?
759
-            if ($reg_step_obj instanceof EE_SPCO_Reg_Step) {
760
-                // set reg step order based on config
761
-                $reg_step_obj->set_order($order);
762
-                // add instantiated reg step object to the master reg steps array
763
-                $this->checkout->add_reg_step($reg_step_obj);
764
-            } else {
765
-                EE_Error::add_error(
766
-                    esc_html__('The current step could not be set.', 'event_espresso'),
767
-                    __FILE__,
768
-                    __FUNCTION__,
769
-                    __LINE__
770
-                );
771
-                return false;
772
-            }
773
-        } else {
774
-            if (WP_DEBUG) {
775
-                EE_Error::add_error(
776
-                    sprintf(
777
-                        esc_html__(
778
-                            'A registration step could not be loaded. One or more of the following data points is invalid:%4$s%5$sFile Path: %1$s%6$s%5$sClass Name: %2$s%6$s%5$sSlug: %3$s%6$s%7$s',
779
-                            'event_espresso'
780
-                        ),
781
-                        $reg_step['file_path'] ?? '',
782
-                        $reg_step['class_name'] ?? '',
783
-                        $reg_step['slug'] ?? '',
784
-                        '<ul>',
785
-                        '<li>',
786
-                        '</li>',
787
-                        '</ul>'
788
-                    ),
789
-                    __FILE__,
790
-                    __FUNCTION__,
791
-                    __LINE__
792
-                );
793
-            }
794
-            return false;
795
-        }
796
-        return true;
797
-    }
798
-
799
-
800
-    /**
801
-     * @return bool
802
-     * @throws EE_Error
803
-     * @throws ReflectionException
804
-     */
805
-    private function _verify_transaction_and_get_registrations(): bool
806
-    {
807
-        // was there already a valid transaction in the checkout from the session ?
808
-        if (! $this->checkout->transaction instanceof EE_Transaction) {
809
-            // get transaction from db or session
810
-            $this->checkout->transaction = $this->checkout->reg_url_link && ! is_admin()
811
-                ? $this->_get_transaction_and_cart_for_previous_visit()
812
-                : $this->_get_cart_for_current_session_and_setup_new_transaction();
813
-            if (! $this->checkout->transaction instanceof EE_Transaction) {
814
-                EE_Error::add_error(
815
-                    esc_html__(
816
-                        'Your Registration and Transaction information could not be retrieved from the db.',
817
-                        'event_espresso'
818
-                    ),
819
-                    __FILE__,
820
-                    __FUNCTION__,
821
-                    __LINE__
822
-                );
823
-                $this->checkout->transaction = EE_Transaction::new_instance();
824
-                // add some style and make it dance
825
-                $this->add_styles_and_scripts($this);
826
-                EED_Single_Page_Checkout::$_initialized = true;
827
-                return false;
828
-            }
829
-            // and the registrations for the transaction
830
-            $this->_get_registrations($this->checkout->transaction);
831
-        }
832
-        return true;
833
-    }
834
-
835
-
836
-    /**
837
-     * @return EE_Transaction|null
838
-     * @throws EE_Error
839
-     * @throws ReflectionException
840
-     */
841
-    private function _get_transaction_and_cart_for_previous_visit(): ?EE_Transaction
842
-    {
843
-        /** @var $TXN_model EEM_Transaction */
844
-        $TXN_model = EE_Registry::instance()->load_model('Transaction');
845
-        // because the reg_url_link is present in the request,
846
-        // this is a return visit to SPCO, so we'll get the transaction data from the db
847
-        $transaction = $TXN_model->get_transaction_from_reg_url_link($this->checkout->reg_url_link);
848
-        // verify transaction
849
-        if ($transaction instanceof EE_Transaction) {
850
-            // and get the cart that was used for that transaction
851
-            $this->checkout->cart = $this->_get_cart_for_transaction($transaction);
852
-            return $transaction;
853
-        }
854
-        EE_Error::add_error(
855
-            esc_html__(
856
-                'Your Registration and Transaction information could not be retrieved from the db.',
857
-                'event_espresso'
858
-            ),
859
-            __FILE__,
860
-            __FUNCTION__,
861
-            __LINE__
862
-        );
863
-        return null;
864
-    }
865
-
866
-
867
-    /**
868
-     * @param EE_Transaction|null $transaction
869
-     * @return EE_Cart
870
-     */
871
-    private function _get_cart_for_transaction(?EE_Transaction $transaction): EE_Cart
872
-    {
873
-        return $this->checkout->get_cart_for_transaction($transaction);
874
-    }
875
-
876
-
877
-    /**
878
-     * @param EE_Transaction|null $transaction
879
-     * @return EE_Cart
880
-     */
881
-    public function get_cart_for_transaction(?EE_Transaction $transaction): EE_Cart
882
-    {
883
-        return $this->checkout->get_cart_for_transaction($transaction);
884
-    }
885
-
886
-
887
-    /**
888
-     * generates a new EE_Transaction object and adds it to the $_transaction property.
889
-     *
890
-     * @return EE_Transaction|null
891
-     * @throws EE_Error
892
-     * @throws ReflectionException
893
-     */
894
-    private function _get_cart_for_current_session_and_setup_new_transaction(): ?EE_Transaction
895
-    {
896
-        //  if there's no transaction, then this is the FIRST visit to SPCO
897
-        // so load up the cart ( passing nothing for the TXN because it doesn't exist yet )
898
-        $this->checkout->cart = $this->_get_cart_for_transaction(null);
899
-        // and then create a new transaction
900
-        $transaction = $this->_initialize_transaction();
901
-        // verify transaction
902
-        if ($transaction instanceof EE_Transaction) {
903
-            // and save TXN data to the cart
904
-            $this->checkout->cart->get_grand_total()->save_this_and_descendants_to_txn($transaction->ID());
905
-        } else {
906
-            EE_Error::add_error(
907
-                esc_html__('A Valid Transaction could not be initialized.', 'event_espresso'),
908
-                __FILE__,
909
-                __FUNCTION__,
910
-                __LINE__
911
-            );
912
-        }
913
-        return $transaction;
914
-    }
915
-
916
-
917
-    /**
918
-     * generates a new EE_Transaction object and adds it to the $_transaction property.
919
-     *
920
-     * @return EE_Transaction|null
921
-     */
922
-    private function _initialize_transaction(): ?EE_Transaction
923
-    {
924
-        try {
925
-            // ensure cart totals have been calculated
926
-            $this->checkout->cart->get_grand_total()->recalculate_total_including_taxes();
927
-            // grab the cart grand total
928
-            $cart_total = $this->checkout->cart->get_cart_grand_total();
929
-            // create new TXN
930
-            $transaction = EE_Transaction::new_instance(
931
-                [
932
-                    'TXN_reg_steps' => $this->checkout->initialize_txn_reg_steps_array(),
933
-                    'TXN_total'     => max($cart_total, 0),
934
-                    'TXN_paid'      => 0,
935
-                    'STS_ID'        => EEM_Transaction::failed_status_code,
936
-                ]
937
-            );
938
-            // save it so that we have an ID for other objects to use
939
-            $transaction->save();
940
-            // set cron job for following up on TXNs after their session has expired
941
-            EE_Cron_Tasks::schedule_expired_transaction_check(
942
-                EE_Registry::instance()->SSN->expiration() + 1,
943
-                $transaction->ID()
944
-            );
945
-            return $transaction;
946
-        } catch (Exception $e) {
947
-            EE_Error::add_error($e->getMessage(), __FILE__, __FUNCTION__, __LINE__);
948
-        }
949
-        return null;
950
-    }
951
-
952
-
953
-    /**
954
-     * _get_registrations
955
-     *
956
-     * @param EE_Transaction $transaction
957
-     * @return void
958
-     * @throws EE_Error
959
-     * @throws ReflectionException
960
-     */
961
-    private function _get_registrations(EE_Transaction $transaction)
962
-    {
963
-        // first step: grab the registrants  { : o
964
-        $registrations                      = $transaction->registrations($this->checkout->reg_cache_where_params);
965
-        $this->checkout->total_ticket_count = count($registrations);
966
-        // verify registrations have been set
967
-        if (empty($registrations)) {
968
-            // if no cached registrations, then check the db
969
-            $registrations = $transaction->registrations($this->checkout->reg_cache_where_params);
970
-            // still nothing ? well as long as this isn't a revisit
971
-            if (empty($registrations) && ! $this->checkout->revisit) {
972
-                // generate new registrations from scratch
973
-                $registrations = $this->_initialize_registrations($transaction);
974
-            }
975
-        }
976
-        // sort by their original registration order
977
-        usort($registrations, ['EED_Single_Page_Checkout', 'sort_registrations_by_REG_count']);
978
-        // then loop thru the array
979
-        foreach ($registrations as $registration) {
980
-            // verify each registration
981
-            if ($registration instanceof EE_Registration) {
982
-                // we display all attendee info for the primary registrant
983
-                if (
984
-                    $this->checkout->reg_url_link === $registration->reg_url_link()
985
-                    && $registration->is_primary_registrant()
986
-                ) {
987
-                    $this->checkout->primary_revisit = true;
988
-                    break;
989
-                }
990
-                if ($this->checkout->revisit && $this->checkout->reg_url_link !== $registration->reg_url_link()) {
991
-                    // but hide info if it doesn't belong to you
992
-                    $transaction->clear_cache('Registration', $registration->ID());
993
-                    $this->checkout->total_ticket_count--;
994
-                }
995
-                $this->checkout->set_reg_status_updated($registration->ID(), false);
996
-            }
997
-        }
998
-    }
999
-
1000
-
1001
-    /**
1002
-     * adds related EE_Registration objects for each ticket in the cart to the current EE_Transaction object
1003
-     *
1004
-     * @param EE_Transaction $transaction
1005
-     * @return array
1006
-     * @throws EE_Error
1007
-     * @throws ReflectionException
1008
-     */
1009
-    private function _initialize_registrations(EE_Transaction $transaction): array
1010
-    {
1011
-        $att_nmbr      = 0;
1012
-        $registrations = [];
1013
-        /** @type EE_Registration_Processor $registration_processor */
1014
-        $registration_processor             = EE_Registry::instance()->load_class('Registration_Processor');
1015
-        $this->checkout->total_ticket_count = $this->checkout->cart->all_ticket_quantity_count();
1016
-        // now let's add the cart items to the $transaction
1017
-        foreach ($this->checkout->cart->get_tickets() as $line_item) {
1018
-            // do the following for each ticket of this type they selected
1019
-            for ($x = 1; $x <= $line_item->quantity(); $x++) {
1020
-                $att_nmbr++;
1021
-                /** @var CreateRegistrationCommand $CreateRegistrationCommand */
1022
-                $CreateRegistrationCommand = EE_Registry::instance()->create(
1023
-                    CreateRegistrationCommand::class,
1024
-                    [
1025
-                        $transaction,
1026
-                        $line_item,
1027
-                        $att_nmbr,
1028
-                        $this->checkout->total_ticket_count,
1029
-                    ]
1030
-                );
1031
-                // override capabilities for frontend registrations
1032
-                if ($this->request->isFrontend()) {
1033
-                    $CreateRegistrationCommand->setCapCheck(
1034
-                        new PublicCapabilities('', 'create_new_registration')
1035
-                    );
1036
-                }
1037
-                $registration = EE_Registry::instance()->BUS->execute($CreateRegistrationCommand);
1038
-                if (! $registration instanceof EE_Registration) {
1039
-                    throw new InvalidEntityException($registration, 'EE_Registration');
1040
-                }
1041
-                $registrations[ $registration->ID() ] = $registration;
1042
-            }
1043
-        }
1044
-        $registration_processor->fix_reg_final_price_rounding_issue($transaction);
1045
-        return $registrations;
1046
-    }
1047
-
1048
-
1049
-    /**
1050
-     * @param EE_Registration $reg_A
1051
-     * @param EE_Registration $reg_B
1052
-     * @return int
1053
-     * @throws EE_Error
1054
-     * @throws ReflectionException
1055
-     */
1056
-    public static function sort_registrations_by_REG_count(EE_Registration $reg_A, EE_Registration $reg_B): int
1057
-    {
1058
-        // this shouldn't ever happen within the same TXN, but oh well
1059
-        if ($reg_A->count() === $reg_B->count()) {
1060
-            return 0;
1061
-        }
1062
-        return ($reg_A->count() > $reg_B->count()) ? 1 : -1;
1063
-    }
1064
-
1065
-
1066
-    /**
1067
-     * just makes sure that everything is set up correctly before proceeding
1068
-     *
1069
-     * @return bool
1070
-     * @throws EE_Error
1071
-     * @throws ReflectionException
1072
-     */
1073
-    private function _final_verifications(): bool
1074
-    {
1075
-        // filter checkout
1076
-        $this->checkout = apply_filters(
1077
-            'FHEE__EED_Single_Page_Checkout___final_verifications__checkout',
1078
-            $this->checkout
1079
-        );
1080
-        // verify that current step is still set correctly
1081
-        if (! $this->checkout->current_step instanceof EE_SPCO_Reg_Step) {
1082
-            EE_Error::add_error(
1083
-                esc_html__(
1084
-                    'We\'re sorry but the registration process can not proceed because one or more registration steps were not setup correctly. Please refresh the page and try again or contact support.',
1085
-                    'event_espresso'
1086
-                ),
1087
-                __FILE__,
1088
-                __FUNCTION__,
1089
-                __LINE__
1090
-            );
1091
-            return false;
1092
-        }
1093
-        // if returning to SPCO, then verify that primary registrant is set
1094
-        if (! empty($this->checkout->reg_url_link)) {
1095
-            $valid_registrant = $this->checkout->transaction->primary_registration();
1096
-            if (! $valid_registrant instanceof EE_Registration) {
1097
-                EE_Error::add_error(
1098
-                    esc_html__(
1099
-                        'We\'re sorry but there appears to be an error with the "reg_url_link" or the primary registrant for this transaction. Please refresh the page and try again or contact support.',
1100
-                        'event_espresso'
1101
-                    ),
1102
-                    __FILE__,
1103
-                    __FUNCTION__,
1104
-                    __LINE__
1105
-                );
1106
-                return false;
1107
-            }
1108
-            $valid_registrant = null;
1109
-            foreach (
1110
-                $this->checkout->transaction->registrations(
1111
-                    $this->checkout->reg_cache_where_params
1112
-                ) as $registration
1113
-            ) {
1114
-                if (
1115
-                    $registration instanceof EE_Registration
1116
-                    && $registration->reg_url_link() === $this->checkout->reg_url_link
1117
-                ) {
1118
-                    $valid_registrant = $registration;
1119
-                }
1120
-            }
1121
-            if (! $valid_registrant instanceof EE_Registration) {
1122
-                // hmmm... maybe we have the wrong session because the user is opening multiple tabs ?
1123
-                if (EED_Single_Page_Checkout::$_checkout_verified) {
1124
-                    // clear the session, mark the checkout as unverified, and try again
1125
-                    EE_Registry::instance()->SSN->clear_session(__CLASS__, __FUNCTION__);
1126
-                    EED_Single_Page_Checkout::$_initialized       = false;
1127
-                    EED_Single_Page_Checkout::$_checkout_verified = false;
1128
-                    $this->_initialize();
1129
-                    EE_Error::reset_notices();
1130
-                    return false;
1131
-                }
1132
-                EE_Error::add_error(
1133
-                    esc_html__(
1134
-                        'We\'re sorry but there appears to be an error with the "reg_url_link" or the transaction itself. Please refresh the page and try again or contact support.',
1135
-                        'event_espresso'
1136
-                    ),
1137
-                    __FILE__,
1138
-                    __FUNCTION__,
1139
-                    __LINE__
1140
-                );
1141
-                return false;
1142
-            }
1143
-        }
1144
-        // now that things have been kinda sufficiently verified,
1145
-        // let's add the checkout to the session so that it's available to other systems
1146
-        EE_Registry::instance()->SSN->set_checkout($this->checkout);
1147
-        return true;
1148
-    }
1149
-
1150
-
1151
-    /**
1152
-     * first makes sure that EE_Transaction_Processor::set_reg_step_initiated() is called as required
1153
-     * then loops thru all the active reg steps and calls the initialize_reg_step() method
1154
-     *
1155
-     * @param bool $reinitializing
1156
-     * @throws EE_Error
1157
-     */
1158
-    private function _initialize_reg_steps(bool $reinitializing = false)
1159
-    {
1160
-        $this->checkout->set_reg_step_initiated($this->checkout->current_step);
1161
-        // loop thru all steps to call their individual "initialize" methods and set i18n strings for JS
1162
-        foreach ($this->checkout->reg_steps as $reg_step) {
1163
-            if (! $reg_step->initialize_reg_step()) {
1164
-                // if not initialized then maybe this step is being removed...
1165
-                if (! $reinitializing && $reg_step->is_current_step()) {
1166
-                    // if it was the current step, then we need to start over here
1167
-                    $this->_initialize_reg_steps(true);
1168
-                    return;
1169
-                }
1170
-                continue;
1171
-            }
1172
-            // add css and JS for current step
1173
-            $this->add_styles_and_scripts($reg_step);
1174
-            if ($reg_step->is_current_step()) {
1175
-                // the text that appears on the reg step form submit button
1176
-                $reg_step->set_submit_button_text();
1177
-            }
1178
-        }
1179
-        // dynamically creates hook point like: AHEE__Single_Page_Checkout___initialize_reg_step__attendee_information
1180
-        do_action(
1181
-            "AHEE__Single_Page_Checkout___initialize_reg_step__{$this->checkout->current_step->slug()}",
1182
-            $this->checkout->current_step
1183
-        );
1184
-    }
1185
-
1186
-
1187
-    /**
1188
-     * @return boolean
1189
-     * @throws EE_Error
1190
-     * @throws ReflectionException
1191
-     */
1192
-    private function _check_form_submission(): bool
1193
-    {
1194
-        // does this request require the reg form to be generated ?
1195
-        if ($this->checkout->generate_reg_form) {
1196
-            // ever heard that song by Blue Rodeo ?
1197
-            try {
1198
-                $this->checkout->current_step->reg_form = $this->checkout->current_step->generate_reg_form();
1199
-                // if not displaying a form, then check for form submission
1200
-                if (
1201
-                    $this->checkout->process_form_submission
1202
-                    && $this->checkout->current_step->reg_form->was_submitted()
1203
-                ) {
1204
-                    // clear out any old data in case this step is being run again
1205
-                    $this->checkout->current_step->set_valid_data([]);
1206
-                    // capture submitted form data
1207
-                    $request_data = $this->request->requestParams();
1208
-                    $this->checkout->current_step->reg_form->receive_form_submission(
1209
-                        (array) apply_filters(
1210
-                            'FHEE__Single_Page_Checkout___check_form_submission__request_params',
1211
-                            $request_data,
1212
-                            $this->checkout
1213
-                        )
1214
-                    );
1215
-                    // validate submitted form data
1216
-                    if (! $this->checkout->continue_reg || ! $this->checkout->current_step->reg_form->is_valid()) {
1217
-                        // thou shall not pass !!!
1218
-                        $this->checkout->continue_reg = false;
1219
-                        // any form validation errors?
1220
-                        if ($this->checkout->current_step->reg_form->submission_error_message() !== '') {
1221
-                            EE_Error::add_error(
1222
-                                $this->checkout->current_step->reg_form->submission_error_message(),
1223
-                                __FILE__,
1224
-                                __FUNCTION__,
1225
-                                __LINE__
1226
-                            );
1227
-                        }
1228
-                        // well not really... what will happen is
1229
-                        // we'll just get redirected back to redo the current step
1230
-                        $this->go_to_next_step();
1231
-                        return false;
1232
-                    }
1233
-                }
1234
-            } catch (EE_Error $e) {
1235
-                $e->get_error();
1236
-            }
1237
-        }
1238
-        return true;
1239
-    }
1240
-
1241
-
1242
-    /**
1243
-     * @return void
1244
-     * @throws EE_Error
1245
-     * @throws ReflectionException
1246
-     */
1247
-    private function _process_form_action()
1248
-    {
1249
-        // what cha wanna do?
1250
-        switch ($this->checkout->action) {
1251
-            // AJAX next step reg form
1252
-            case 'display_spco_reg_step':
1253
-                $this->checkout->redirect = false;
1254
-                if ($this->request->isAjax()) {
1255
-                    $this->checkout->json_response->set_reg_step_html(
1256
-                        $this->checkout->current_step->display_reg_form()
1257
-                    );
1258
-                }
1259
-                break;
1260
-            default:
1261
-                // meh... do one of those other steps first
1262
-                if (
1263
-                    ! empty($this->checkout->action)
1264
-                    && is_callable([$this->checkout->current_step, $this->checkout->action])
1265
-                ) {
1266
-                    // dynamically creates hook point like:
1267
-                    //   AHEE__Single_Page_Checkout__before_attendee_information__process_reg_step
1268
-                    do_action(
1269
-                        "AHEE__Single_Page_Checkout__before_{$this->checkout->current_step->slug()}__{$this->checkout->action}",
1270
-                        $this->checkout->current_step
1271
-                    );
1272
-                    $process_reg_step = apply_filters(
1273
-                        "AHEE__Single_Page_Checkout__process_reg_step__{$this->checkout->current_step->slug()}__{$this->checkout->action}",
1274
-                        true,
1275
-                        $this->checkout->current_step,
1276
-                        $this
1277
-                    );
1278
-                    // call action on current step
1279
-                    if ($process_reg_step && call_user_func([$this->checkout->current_step, $this->checkout->action])) {
1280
-                        // good registrant, you get to proceed
1281
-                        if (
1282
-                            $this->checkout->current_step->success_message() !== ''
1283
-                            && apply_filters(
1284
-                                'FHEE__Single_Page_Checkout___process_form_action__display_success',
1285
-                                false
1286
-                            )
1287
-                        ) {
1288
-                            EE_Error::add_success(
1289
-                                $this->checkout->current_step->success_message()
1290
-                                . '<br />' . $this->checkout->next_step->_instructions()
1291
-                            );
1292
-                        }
1293
-                        // pack it up, pack it in...
1294
-                        $this->_setup_redirect();
1295
-                    }
1296
-                    // dynamically creates hook point like:
1297
-                    //  AHEE__Single_Page_Checkout__after_payment_options__process_reg_step
1298
-                    do_action(
1299
-                        "AHEE__Single_Page_Checkout__after_{$this->checkout->current_step->slug()}__{$this->checkout->action}",
1300
-                        $this->checkout->current_step
1301
-                    );
1302
-                } else {
1303
-                    EE_Error::add_error(
1304
-                        sprintf(
1305
-                            esc_html__(
1306
-                                'The requested form action "%s" does not exist for the current "%s" registration step.',
1307
-                                'event_espresso'
1308
-                            ),
1309
-                            $this->checkout->action,
1310
-                            $this->checkout->current_step->name()
1311
-                        ),
1312
-                        __FILE__,
1313
-                        __FUNCTION__,
1314
-                        __LINE__
1315
-                    );
1316
-                }
1317
-            // end default
1318
-        }
1319
-        // store our progress so far
1320
-        $this->checkout->stash_transaction_and_checkout();
1321
-        // advance to the next step! If you pass GO, collect $200
1322
-        $this->go_to_next_step();
1323
-    }
1324
-
1325
-
1326
-    /**
1327
-     * @param EED_Single_Page_Checkout|EE_SPCO_Reg_Step $target an object with the method `translate_js_strings` and
1328
-     *                                                          `enqueue_styles_and_scripts`.
1329
-     * @return void
1330
-     */
1331
-    public function add_styles_and_scripts($target)
1332
-    {
1333
-        // i18n
1334
-        $target->translate_js_strings();
1335
-        if ($this->checkout->admin_request) {
1336
-            add_action('admin_enqueue_scripts', [$target, 'enqueue_styles_and_scripts']);
1337
-        } else {
1338
-            add_action('wp_enqueue_scripts', [$target, 'enqueue_styles_and_scripts']);
1339
-        }
1340
-    }
1341
-
1342
-
1343
-    /**
1344
-     * @return void
1345
-     */
1346
-    public function translate_js_strings()
1347
-    {
1348
-        EE_Registry::$i18n_js_strings['revisit']                        = $this->checkout->revisit;
1349
-        EE_Registry::$i18n_js_strings['e_reg_url_link']                 = $this->checkout->reg_url_link;
1350
-        EE_Registry::$i18n_js_strings['server_error']                   = esc_html__(
1351
-            'An unknown error occurred on the server while attempting to process your request. Please refresh the page and try again or contact support.',
1352
-            'event_espresso'
1353
-        );
1354
-        EE_Registry::$i18n_js_strings['invalid_json_response']          = esc_html__(
1355
-            'An invalid response was returned from the server while attempting to process your request. Please refresh the page and try again or contact support.',
1356
-            'event_espresso'
1357
-        );
1358
-        EE_Registry::$i18n_js_strings['validation_error']               = esc_html__(
1359
-            'There appears to be a problem with the form validation configuration! Please check the admin settings or contact support.',
1360
-            'event_espresso'
1361
-        );
1362
-        EE_Registry::$i18n_js_strings['invalid_payment_method']         = esc_html__(
1363
-            'There appears to be a problem with the payment method configuration! Please refresh the page and try again or contact support.',
1364
-            'event_espresso'
1365
-        );
1366
-        EE_Registry::$i18n_js_strings['reg_step_error']                 = esc_html__(
1367
-            'This registration step could not be completed. Please refresh the page and try again.',
1368
-            'event_espresso'
1369
-        );
1370
-        EE_Registry::$i18n_js_strings['invalid_coupon']                 = esc_html__(
1371
-            'We\'re sorry but that coupon code does not appear to be valid. If this is incorrect, please contact the site administrator.',
1372
-            'event_espresso'
1373
-        );
1374
-        EE_Registry::$i18n_js_strings['process_registration']           = sprintf(
1375
-            esc_html__(
1376
-                'Please wait while we process your registration.%sDo not refresh the page or navigate away while this is happening.%sThank you for your patience.',
1377
-                'event_espresso'
1378
-            ),
1379
-            '<br/>',
1380
-            '<br/>'
1381
-        );
1382
-        EE_Registry::$i18n_js_strings['language']                       = get_bloginfo('language');
1383
-        EE_Registry::$i18n_js_strings['EESID']                          = EE_Registry::instance()->SSN->id();
1384
-        EE_Registry::$i18n_js_strings['currency']                       = EE_Registry::instance()->CFG->currency;
1385
-        EE_Registry::$i18n_js_strings['datepicker_yearRange']           = '-150:+20';
1386
-        EE_Registry::$i18n_js_strings['timer_years']                    = esc_html__('years', 'event_espresso');
1387
-        EE_Registry::$i18n_js_strings['timer_months']                   = esc_html__('months', 'event_espresso');
1388
-        EE_Registry::$i18n_js_strings['timer_weeks']                    = esc_html__('weeks', 'event_espresso');
1389
-        EE_Registry::$i18n_js_strings['timer_days']                     = esc_html__('days', 'event_espresso');
1390
-        EE_Registry::$i18n_js_strings['timer_hours']                    = esc_html__('hours', 'event_espresso');
1391
-        EE_Registry::$i18n_js_strings['timer_minutes']                  = esc_html__('minutes', 'event_espresso');
1392
-        EE_Registry::$i18n_js_strings['timer_seconds']                  = esc_html__('seconds', 'event_espresso');
1393
-        EE_Registry::$i18n_js_strings['timer_year']                     = esc_html__('year', 'event_espresso');
1394
-        EE_Registry::$i18n_js_strings['timer_month']                    = esc_html__('month', 'event_espresso');
1395
-        EE_Registry::$i18n_js_strings['timer_week']                     = esc_html__('week', 'event_espresso');
1396
-        EE_Registry::$i18n_js_strings['timer_day']                      = esc_html__('day', 'event_espresso');
1397
-        EE_Registry::$i18n_js_strings['timer_hour']                     = esc_html__('hour', 'event_espresso');
1398
-        EE_Registry::$i18n_js_strings['timer_minute']                   = esc_html__('minute', 'event_espresso');
1399
-        EE_Registry::$i18n_js_strings['timer_second']                   = esc_html__('second', 'event_espresso');
1400
-        EE_Registry::$i18n_js_strings['registration_expiration_notice'] =
1401
-            EED_Single_Page_Checkout::getRegistrationExpirationNotice();
1402
-        EE_Registry::$i18n_js_strings['ajax_submit']                    = apply_filters(
1403
-            'FHEE__Single_Page_Checkout__translate_js_strings__ajax_submit',
1404
-            true
1405
-        );
1406
-        EE_Registry::$i18n_js_strings['session_extension']              = absint(
1407
-            apply_filters('FHEE__EE_Session__extend_expiration__seconds_added', 10 * MINUTE_IN_SECONDS)
1408
-        );
1409
-        EE_Registry::$i18n_js_strings['session_expiration']    = EE_Registry::instance()->SSN->expiration();
1410
-        EE_Registry::$i18n_js_strings['use_session_countdown'] = EE_Registry::instance()->CFG->registration->useSessionCountdown();
1411
-    }
1412
-
1413
-
1414
-    /**
1415
-     * @return void
1416
-     * @throws EE_Error
1417
-     */
1418
-    public function enqueue_styles_and_scripts()
1419
-    {
1420
-        // load css
1421
-        wp_register_style(
1422
-            'single_page_checkout',
1423
-            SPCO_CSS_URL . 'single_page_checkout.css',
1424
-            ['espresso_default'],
1425
-            EVENT_ESPRESSO_VERSION
1426
-        );
1427
-        wp_enqueue_style('single_page_checkout');
1428
-        // load JS
1429
-        wp_register_script(
1430
-            'single_page_checkout',
1431
-            SPCO_JS_URL . 'single_page_checkout.js',
1432
-            ['espresso_core', 'underscore', 'ee_form_section_validation'],
1433
-            EVENT_ESPRESSO_VERSION,
1434
-            true
1435
-        );
1436
-        if ($this->checkout->registration_form instanceof EE_Form_Section_Proper) {
1437
-            $this->checkout->registration_form->enqueue_js();
1438
-        }
1439
-        if ($this->checkout->current_step->reg_form instanceof EE_Form_Section_Proper) {
1440
-            $this->checkout->current_step->reg_form->enqueue_js();
1441
-        }
1442
-        wp_enqueue_script('single_page_checkout');
1443
-        /**
1444
-         * global action hook for enqueueing styles and scripts with
1445
-         * spco calls.
1446
-         */
1447
-        do_action('AHEE__EED_Single_Page_Checkout__enqueue_styles_and_scripts', $this);
1448
-        /**
1449
-         * dynamic action hook for enqueueing styles and scripts with spco calls.
1450
-         * The hook will end up being something like:
1451
-         *      AHEE__EED_Single_Page_Checkout__enqueue_styles_and_scripts__attendee_information
1452
-         */
1453
-        do_action(
1454
-            'AHEE__EED_Single_Page_Checkout__enqueue_styles_and_scripts__' . $this->checkout->current_step->slug(),
1455
-            $this
1456
-        );
1457
-    }
1458
-
1459
-
1460
-    /**
1461
-     * display the Registration Single Page Checkout Form
1462
-     *
1463
-     * @return void
1464
-     * @throws EE_Error
1465
-     * @throws ReflectionException
1466
-     */
1467
-    private function _display_spco_reg_form()
1468
-    {
1469
-        // if registering via the admin, just display the reg form for the current step
1470
-        if ($this->checkout->admin_request) {
1471
-            EED_Single_Page_Checkout::getResponse()->addOutput($this->checkout->current_step->display_reg_form());
1472
-        } else {
1473
-            // add powered by EE msg
1474
-            add_action('AHEE__SPCO__reg_form_footer', ['EED_Single_Page_Checkout', 'display_registration_footer']);
1475
-            $empty_cart                                 = count(
1476
-                    $this->checkout->transaction->registrations($this->checkout->reg_cache_where_params)
1477
-                ) < 1;
1478
-            EE_Registry::$i18n_js_strings['empty_cart'] = $empty_cart;
1479
-            $cookies_not_set_msg                        = '';
1480
-            if ($empty_cart) {
1481
-                $cookies_not_set_msg = apply_filters(
1482
-                    'FHEE__Single_Page_Checkout__display_spco_reg_form__cookies_not_set_msg',
1483
-                    sprintf(
1484
-                        esc_html__(
1485
-                            '%1$s%3$sIt appears your browser is not currently set to accept Cookies%4$s%5$sIn order to register for events, you need to enable cookies.%7$sIf you require assistance, then click the following link to learn how to %8$senable cookies%9$s%6$s%2$s',
1486
-                            'event_espresso'
1487
-                        ),
1488
-                        '<div class="ee-attention hidden" id="ee-cookies-not-set-msg">',
1489
-                        '</div>',
1490
-                        '<h6 class="important-notice">',
1491
-                        '</h6>',
1492
-                        '<p>',
1493
-                        '</p>',
1494
-                        '<br />',
1495
-                        '<a href="https://www.whatismybrowser.com/guides/how-to-enable-cookies/" target="_blank" rel="noopener noreferrer">',
1496
-                        '</a>'
1497
-                    )
1498
-                );
1499
-            }
1500
-            $this->checkout->registration_form = new EE_Form_Section_Proper(
1501
-                [
1502
-                    'name'            => 'single-page-checkout',
1503
-                    'html_id'         => 'ee-single-page-checkout-dv',
1504
-                    'layout_strategy' =>
1505
-                        new EE_Template_Layout(
1506
-                            [
1507
-                                'layout_template_file' => SPCO_TEMPLATES_PATH . 'registration_page_wrapper.template.php',
1508
-                                'template_args'        => [
1509
-                                    'empty_cart'              => $empty_cart,
1510
-                                    'revisit'                 => $this->checkout->revisit,
1511
-                                    'reg_steps'               => $this->checkout->reg_steps,
1512
-                                    'next_step'               => $this->checkout->next_step instanceof EE_SPCO_Reg_Step
1513
-                                        ? $this->checkout->next_step->slug()
1514
-                                        : '',
1515
-                                    'empty_msg'               => apply_filters(
1516
-                                        'FHEE__Single_Page_Checkout__display_spco_reg_form__empty_msg',
1517
-                                        sprintf(
1518
-                                            esc_html__(
1519
-                                                'You need to %1$sReturn to Events list%2$sselect at least one event%3$s before you can proceed with the registration process.',
1520
-                                                'event_espresso'
1521
-                                            ),
1522
-                                            '<a href="'
1523
-                                            . get_post_type_archive_link(EspressoPostType::EVENTS)
1524
-                                            . '" title="',
1525
-                                            '">',
1526
-                                            '</a>'
1527
-                                        )
1528
-                                    ),
1529
-                                    'cookies_not_set_msg'     => $cookies_not_set_msg,
1530
-                                    'registration_time_limit' => $this->checkout->get_registration_time_limit(),
1531
-                                    'use_session_countdown'   => EE_Registry::instance()->CFG->registration->useSessionCountdown(),
1532
-                                ],
1533
-                            ]
1534
-                        ),
1535
-                ]
1536
-            );
1537
-            // load template and add to output sent that gets filtered into the_content()
1538
-            EED_Single_Page_Checkout::getResponse()->addOutput($this->checkout->registration_form->get_html());
1539
-        }
1540
-    }
1541
-
1542
-
1543
-    /**
1544
-     * @param $next_step
1545
-     * @return void
1546
-     */
1547
-    public function add_extra_finalize_registration_inputs($next_step)
1548
-    {
1549
-        if ($next_step === 'finalize_registration') {
1550
-            echo '<div id="spco-extra-finalize_registration-inputs-dv"></div>';
1551
-        }
1552
-    }
1553
-
1554
-
1555
-    /**
1556
-     * @return void
1557
-     */
1558
-    public static function display_registration_footer()
1559
-    {
1560
-        if (
1561
-            apply_filters(
1562
-                'FHEE__EE_Front__Controller__show_reg_footer',
1563
-                EE_Registry::instance()->CFG->admin->show_reg_footer
1564
-            )
1565
-        ) {
1566
-            add_filter(
1567
-                'FHEE__EEH_Template__powered_by_event_espresso__url',
1568
-                function ($url) {
1569
-                    return apply_filters('FHEE__EE_Front_Controller__registration_footer__url', $url);
1570
-                }
1571
-            );
1572
-            echo apply_filters(
1573
-                'FHEE__EE_Front_Controller__display_registration_footer',
1574
-                EEH_Template::powered_by_event_espresso(
1575
-                    '',
1576
-                    'espresso-registration-footer-dv',
1577
-                    ['utm_content' => 'registration_checkout']
1578
-                )
1579
-            );
1580
-        }
1581
-    }
1582
-
1583
-
1584
-    /**
1585
-     * @return void
1586
-     * @throws EE_Error
1587
-     * @throws ReflectionException
1588
-     */
1589
-    public function unlock_transaction()
1590
-    {
1591
-        if ($this->checkout->transaction instanceof EE_Transaction) {
1592
-            $this->checkout->transaction->unlock();
1593
-        }
1594
-    }
1595
-
1596
-
1597
-    /**
1598
-     * @return void
1599
-     */
1600
-    private function _setup_redirect()
1601
-    {
1602
-        if ($this->checkout->continue_reg && $this->checkout->next_step instanceof EE_SPCO_Reg_Step) {
1603
-            $this->checkout->redirect = true;
1604
-            if (empty($this->checkout->redirect_url)) {
1605
-                $this->checkout->redirect_url = $this->checkout->next_step->reg_step_url();
1606
-            }
1607
-            $this->checkout->redirect_url = apply_filters(
1608
-                'FHEE__EED_Single_Page_Checkout___setup_redirect__checkout_redirect_url',
1609
-                $this->checkout->redirect_url,
1610
-                $this->checkout
1611
-            );
1612
-        }
1613
-    }
1614
-
1615
-
1616
-    /**
1617
-     * handle ajax message responses and redirects
1618
-     *
1619
-     * @return void
1620
-     * @throws EE_Error
1621
-     * @throws ReflectionException
1622
-     */
1623
-    public function go_to_next_step()
1624
-    {
1625
-        if ($this->request->isAjax()) {
1626
-            // capture contents of output buffer we started earlier in the request, and insert into JSON response
1627
-            $this->checkout->json_response->set_unexpected_errors(ob_get_clean());
1628
-        }
1629
-        $this->unlock_transaction();
1630
-        // just return for these conditions
1631
-        if (
1632
-            $this->checkout->admin_request
1633
-            || $this->checkout->action === 'redirect_form'
1634
-            || $this->checkout->action === 'update_checkout'
1635
-        ) {
1636
-            return;
1637
-        }
1638
-        // AJAX response
1639
-        $this->_handle_json_response();
1640
-        // redirect to next step or the Thank-You page
1641
-        $this->_handle_html_redirects();
1642
-        // hmmm... must be something wrong, so let's just display the form again !
1643
-        $this->_display_spco_reg_form();
1644
-    }
1645
-
1646
-
1647
-    /**
1648
-     * @return void
1649
-     * @throws EE_Error
1650
-     */
1651
-    protected function _handle_json_response()
1652
-    {
1653
-        // if this is an ajax request
1654
-        if ($this->request->isAjax()) {
1655
-            $this->checkout->json_response->set_registration_time_limit(
1656
-                $this->checkout->get_registration_time_limit()
1657
-            );
1658
-            $this->checkout->json_response->set_payment_amount($this->checkout->amount_owing);
1659
-            // just send the ajax (
1660
-            $json_response = apply_filters(
1661
-                'FHEE__EE_Single_Page_Checkout__JSON_response',
1662
-                $this->checkout->json_response
1663
-            );
1664
-            exit($json_response);
1665
-        }
1666
-    }
1667
-
1668
-
1669
-    /**
1670
-     * @return void
1671
-     */
1672
-    protected function _handle_html_redirects()
1673
-    {
1674
-        // going somewhere ?
1675
-        if ($this->checkout->redirect && ! empty($this->checkout->redirect_url)) {
1676
-            // store notices in a transient
1677
-            EE_Error::get_notices(false, true);
1678
-            wp_safe_redirect($this->checkout->redirect_url);
1679
-            exit();
1680
-        }
1681
-    }
1682
-
1683
-
1684
-    /**
1685
-     * @return void
1686
-     */
1687
-    public function set_checkout_anchor()
1688
-    {
1689
-        echo '<a id="checkout" style="float: left; margin-left: -999em;"></a>';
1690
-    }
1691
-
1692
-
1693
-    /**
1694
-     * @return string
1695
-     * @since 4.9.59.p
1696
-     */
1697
-    public static function getRegistrationExpirationNotice(): string
1698
-    {
1699
-        return sprintf(
1700
-            esc_html__(
1701
-                '%1$sWe\'re sorry, but your registration time has expired.%2$s%3$s%4$sIf you still wish to complete your registration, please return to the %5$sEvent List%6$sEvent List%7$s and reselect your tickets if available. Please accept our apologies for any inconvenience this may have caused.%8$s',
1702
-                'event_espresso'
1703
-            ),
1704
-            '<h4 class="important-notice">',
1705
-            '</h4>',
1706
-            '<br />',
1707
-            '<p>',
1708
-            '<a href="' . get_post_type_archive_link(EspressoPostType::EVENTS) . '" title="',
1709
-            '">',
1710
-            '</a>',
1711
-            '</p>'
1712
-        );
1713
-    }
23
+	/**
24
+	 * $_initialized - has the SPCO controller already been initialized ?
25
+	 */
26
+	private static bool $_initialized = false;
27
+
28
+
29
+	/**
30
+	 * $_checkout_verified - is the EE_Checkout verified as correct for this request ?
31
+	 */
32
+	private static bool $_checkout_verified = true;
33
+
34
+	/**
35
+	 * $_reg_steps_array - holds initial array of reg steps
36
+	 *
37
+	 * @var array $_reg_steps_array
38
+	 */
39
+	private static array $_reg_steps_array = [];
40
+
41
+	/**
42
+	 * $checkout - EE_Checkout object for handling the properties of the current checkout process
43
+	 */
44
+	public ?EE_Checkout $checkout = null;
45
+
46
+	protected ?RequestInterface $request = null;
47
+
48
+	private bool $debug = false;    //  true    false
49
+
50
+
51
+	/**
52
+	 * @return EED_Single_Page_Checkout|EED_Module
53
+	 * @throws EE_Error
54
+	 * @throws ReflectionException
55
+	 */
56
+	public static function instance()
57
+	{
58
+		add_filter('EED_Single_Page_Checkout__SPCO_active', '__return_true');
59
+		return parent::get_instance(__CLASS__);
60
+	}
61
+
62
+
63
+	/**
64
+	 * @return EE_CART
65
+	 */
66
+	public function cart(): EE_CART
67
+	{
68
+		return $this->checkout->cart;
69
+	}
70
+
71
+
72
+	/**
73
+	 * @return RequestInterface
74
+	 * @since   4.10.14.p
75
+	 */
76
+	public static function getRequest(): RequestInterface
77
+	{
78
+		return LoaderFactory::getLoader()->getShared(RequestInterface::class);
79
+	}
80
+
81
+
82
+	/**
83
+	 * @return EE_Transaction
84
+	 */
85
+	public function transaction(): EE_Transaction
86
+	{
87
+		return $this->checkout->transaction;
88
+	}
89
+
90
+
91
+	/**
92
+	 *    set_hooks - for hooking into EE Core, other modules, etc
93
+	 *
94
+	 * @return    void
95
+	 * @throws EE_Error
96
+	 */
97
+	public static function set_hooks()
98
+	{
99
+		EED_Single_Page_Checkout::set_definitions();
100
+	}
101
+
102
+
103
+	/**
104
+	 *    set_hooks_admin - for hooking into EE Admin Core, other modules, etc
105
+	 *
106
+	 * @return    void
107
+	 * @throws EE_Error
108
+	 */
109
+	public static function set_hooks_admin()
110
+	{
111
+		EED_Single_Page_Checkout::set_definitions();
112
+		if (! (defined('DOING_AJAX') && DOING_AJAX)) {
113
+			return;
114
+		}
115
+		// going to start an output buffer in case anything gets accidentally output
116
+		// that might disrupt our JSON response
117
+		ob_start();
118
+		EED_Single_Page_Checkout::load_reg_steps();
119
+		// set ajax hooks
120
+		add_action('wp_ajax_process_reg_step', ['EED_Single_Page_Checkout', 'process_reg_step']);
121
+		add_action('wp_ajax_nopriv_process_reg_step', ['EED_Single_Page_Checkout', 'process_reg_step']);
122
+		add_action('wp_ajax_display_spco_reg_step', ['EED_Single_Page_Checkout', 'display_reg_step']);
123
+		add_action('wp_ajax_nopriv_display_spco_reg_step', ['EED_Single_Page_Checkout', 'display_reg_step']);
124
+		add_action('wp_ajax_update_reg_step', ['EED_Single_Page_Checkout', 'update_reg_step']);
125
+		add_action('wp_ajax_nopriv_update_reg_step', ['EED_Single_Page_Checkout', 'update_reg_step']);
126
+	}
127
+
128
+
129
+	/**
130
+	 *    process ajax request
131
+	 *
132
+	 * @param string $ajax_action
133
+	 * @throws EE_Error
134
+	 * @throws ReflectionException
135
+	 */
136
+	public static function process_ajax_request(string $ajax_action)
137
+	{
138
+		$request = EED_Single_Page_Checkout::getRequest();
139
+		$request->setRequestParam('action', $ajax_action);
140
+		EED_Single_Page_Checkout::instance()->_initialize();
141
+	}
142
+
143
+
144
+	/**
145
+	 * ajax display registration step
146
+	 *
147
+	 * @throws EE_Error
148
+	 * @throws ReflectionException
149
+	 */
150
+	public static function display_reg_step()
151
+	{
152
+		EED_Single_Page_Checkout::process_ajax_request('display_spco_reg_step');
153
+	}
154
+
155
+
156
+	/**
157
+	 * ajax process registration step
158
+	 *
159
+	 * @throws EE_Error
160
+	 * @throws ReflectionException
161
+	 */
162
+	public static function process_reg_step()
163
+	{
164
+		EED_Single_Page_Checkout::process_ajax_request('process_reg_step');
165
+	}
166
+
167
+
168
+	/**
169
+	 * ajax process registration step
170
+	 *
171
+	 * @throws EE_Error
172
+	 * @throws ReflectionException
173
+	 */
174
+	public static function update_reg_step()
175
+	{
176
+		EED_Single_Page_Checkout::process_ajax_request('update_reg_step');
177
+	}
178
+
179
+
180
+	/**
181
+	 * update_checkout
182
+	 *
183
+	 * @return void
184
+	 * @throws ReflectionException
185
+	 * @throws EE_Error
186
+	 */
187
+	public static function update_checkout()
188
+	{
189
+		EED_Single_Page_Checkout::process_ajax_request('update_checkout');
190
+	}
191
+
192
+
193
+	/**
194
+	 *    set_definitions
195
+	 *
196
+	 * @return    void
197
+	 * @throws EE_Error
198
+	 */
199
+	public static function set_definitions()
200
+	{
201
+		if (defined('SPCO_BASE_PATH')) {
202
+			return;
203
+		}
204
+		define(
205
+			'SPCO_BASE_PATH',
206
+			rtrim(str_replace(['\\', '/'], '/', plugin_dir_path(__FILE__)), '/') . '/'
207
+		);
208
+		define('SPCO_CSS_URL', plugin_dir_url(__FILE__) . 'css/');
209
+		define('SPCO_IMG_URL', plugin_dir_url(__FILE__) . 'img/');
210
+		define('SPCO_JS_URL', plugin_dir_url(__FILE__) . 'js/');
211
+		define('SPCO_INC_PATH', SPCO_BASE_PATH . 'inc/');
212
+		define('SPCO_REG_STEPS_PATH', SPCO_BASE_PATH . 'reg_steps/');
213
+		define('SPCO_TEMPLATES_PATH', SPCO_BASE_PATH . 'templates/');
214
+		EEH_Autoloader::register_autoloaders_for_each_file_in_folder(SPCO_BASE_PATH, true);
215
+		EE_Registry::$i18n_js_strings['registration_expiration_notice'] =
216
+			EED_Single_Page_Checkout::getRegistrationExpirationNotice();
217
+	}
218
+
219
+
220
+	/**
221
+	 * load_reg_steps
222
+	 * loads and instantiates each reg step based on the EE_Registry::instance()->CFG->registration->reg_steps array
223
+	 *
224
+	 * @throws EE_Error
225
+	 */
226
+	public static function load_reg_steps()
227
+	{
228
+		static $reg_steps_loaded = false;
229
+		if ($reg_steps_loaded) {
230
+			return;
231
+		}
232
+		// filter list of reg_steps
233
+		$reg_steps_to_load = (array) apply_filters(
234
+			'AHEE__SPCO__load_reg_steps__reg_steps_to_load',
235
+			EED_Single_Page_Checkout::get_reg_steps()
236
+		);
237
+		// sort by key (order)
238
+		ksort($reg_steps_to_load);
239
+		// loop through folders
240
+		foreach ($reg_steps_to_load as $order => $reg_step) {
241
+			// we need a
242
+			if (isset($reg_step['file_path'], $reg_step['class_name'], $reg_step['slug'])) {
243
+				// copy over to the reg_steps_array
244
+				EED_Single_Page_Checkout::$_reg_steps_array[ $order ] = $reg_step;
245
+				// register custom key route for each reg step
246
+				// ie: step=>"slug" - this is the entire reason we load the reg steps array now
247
+				EE_Config::register_route(
248
+					$reg_step['slug'],
249
+					'EED_Single_Page_Checkout',
250
+					'run',
251
+					'step'
252
+				);
253
+				// add AJAX or other hooks
254
+				if (isset($reg_step['has_hooks']) && $reg_step['has_hooks']) {
255
+					// setup autoloaders if necessary
256
+					if (! class_exists($reg_step['class_name'])) {
257
+						EEH_Autoloader::register_autoloaders_for_each_file_in_folder(
258
+							$reg_step['file_path'],
259
+							true
260
+						);
261
+					}
262
+					if (is_callable($reg_step['class_name'], 'set_hooks')) {
263
+						call_user_func([$reg_step['class_name'], 'set_hooks']);
264
+					}
265
+				}
266
+			}
267
+		}
268
+		$reg_steps_loaded = true;
269
+	}
270
+
271
+
272
+	/**
273
+	 *    get_reg_steps
274
+	 *
275
+	 * @return    array
276
+	 */
277
+	public static function get_reg_steps(): array
278
+	{
279
+		$reg_steps = EE_Registry::instance()->CFG->registration->reg_steps;
280
+		if (empty($reg_steps)) {
281
+			$reg_steps = [
282
+				10  => [
283
+					'file_path'  => SPCO_REG_STEPS_PATH . 'attendee_information',
284
+					'class_name' => 'EE_SPCO_Reg_Step_Attendee_Information',
285
+					'slug'       => 'attendee_information',
286
+					'has_hooks'  => false,
287
+				],
288
+				30  => [
289
+					'file_path'  => SPCO_REG_STEPS_PATH . 'payment_options',
290
+					'class_name' => 'EE_SPCO_Reg_Step_Payment_Options',
291
+					'slug'       => 'payment_options',
292
+					'has_hooks'  => true,
293
+				],
294
+				999 => [
295
+					'file_path'  => SPCO_REG_STEPS_PATH . 'finalize_registration',
296
+					'class_name' => 'EE_SPCO_Reg_Step_Finalize_Registration',
297
+					'slug'       => 'finalize_registration',
298
+					'has_hooks'  => false,
299
+				],
300
+			];
301
+		}
302
+		return $reg_steps;
303
+	}
304
+
305
+
306
+	/**
307
+	 * @return array|string
308
+	 * @throws EE_Error
309
+	 * @throws ReflectionException
310
+	 */
311
+	public static function registration_checkout_for_admin()
312
+	{
313
+		$request = EED_Single_Page_Checkout::getRequest();
314
+		$request->setRequestParam('step', 'attendee_information');
315
+		$request->setRequestParam('action', 'display_spco_reg_step');
316
+		$request->setRequestParam('process_form_submission', false);
317
+		EED_Single_Page_Checkout::instance()->_initialize();
318
+		EED_Single_Page_Checkout::instance()->_display_spco_reg_form();
319
+		return EED_Single_Page_Checkout::getResponse()->getOutput();
320
+	}
321
+
322
+
323
+	/**
324
+	 * @return EE_Transaction|null
325
+	 * @throws EE_Error
326
+	 * @throws ReflectionException
327
+	 */
328
+	public static function process_registration_from_admin(): ?EE_Transaction
329
+	{
330
+		$request = EED_Single_Page_Checkout::getRequest();
331
+		$request->setRequestParam('step', 'attendee_information');
332
+		$request->setRequestParam('action', 'process_reg_step');
333
+		$request->setRequestParam('process_form_submission', true);
334
+		EED_Single_Page_Checkout::instance()->_initialize();
335
+		if (EED_Single_Page_Checkout::instance()->checkout->current_step->completed()) {
336
+			$final_reg_step = end(EED_Single_Page_Checkout::instance()->checkout->reg_steps);
337
+			if ($final_reg_step instanceof EE_SPCO_Reg_Step_Finalize_Registration) {
338
+				EED_Single_Page_Checkout::instance()->checkout->set_reg_step_initiated($final_reg_step);
339
+				if ($final_reg_step->process_reg_step()) {
340
+					$final_reg_step->set_completed();
341
+					EED_Single_Page_Checkout::instance()->checkout->update_txn_reg_steps_array();
342
+					return EED_Single_Page_Checkout::instance()->checkout->transaction;
343
+				}
344
+			}
345
+		}
346
+		return null;
347
+	}
348
+
349
+
350
+	/**
351
+	 *    run
352
+	 *
353
+	 * @param WP_Query|null $WP_Query
354
+	 * @return    void
355
+	 */
356
+	public function run($WP_Query)
357
+	{
358
+		if (
359
+			$WP_Query instanceof WP_Query
360
+			&& $WP_Query->is_main_query()
361
+			&& apply_filters('FHEE__EED_Single_Page_Checkout__run', true)
362
+			&& $this->_is_reg_checkout()
363
+		) {
364
+			$this->_initialize();
365
+		}
366
+	}
367
+
368
+
369
+	/**
370
+	 * determines whether current url matches reg page url
371
+	 *
372
+	 * @return bool
373
+	 */
374
+	protected function _is_reg_checkout(): bool
375
+	{
376
+		// get current permalink for reg page without any extra query args
377
+		$reg_page_url = get_permalink(EE_Config::instance()->core->reg_page_id);
378
+		// get request URI for current request, but without the scheme or host
379
+		$current_request_uri = EEH_URL::filter_input_server_url();
380
+		$current_request_uri = html_entity_decode($current_request_uri);
381
+		// get array of query args from the current request URI
382
+		$query_args = EEH_URL::get_query_string($current_request_uri);
383
+		// grab page id if it is set
384
+		$page_id = isset($query_args['page_id']) ? absint($query_args['page_id']) : 0;
385
+		// and remove the page id from the query args (we will re-add it later)
386
+		unset($query_args['page_id']);
387
+		// now strip all query args from current request URI
388
+		$current_request_uri = remove_query_arg(array_keys($query_args), $current_request_uri);
389
+		// and re-add the page id if it was set
390
+		if ($page_id) {
391
+			$current_request_uri = add_query_arg('page_id', $page_id, $current_request_uri);
392
+		}
393
+		// remove slashes and ?
394
+		$current_request_uri = trim($current_request_uri, '?/');
395
+		// is current request URI part of the known full reg page URL ?
396
+		return ! empty($current_request_uri) && strpos($reg_page_url, $current_request_uri) !== false;
397
+	}
398
+
399
+
400
+	/**
401
+	 * @param WP_Query $wp_query
402
+	 * @return    void
403
+	 * @throws EE_Error
404
+	 * @throws ReflectionException
405
+	 */
406
+	public static function init(WP_Query $wp_query)
407
+	{
408
+		EED_Single_Page_Checkout::instance()->run($wp_query);
409
+	}
410
+
411
+
412
+	/**
413
+	 * @return void
414
+	 */
415
+	private function _initialize()
416
+	{
417
+		// ensure SPCO doesn't run twice
418
+		if (EED_Single_Page_Checkout::$_initialized) {
419
+			return;
420
+		}
421
+		try {
422
+			$this->request = EED_Single_Page_Checkout::getRequest();
423
+			EED_Single_Page_Checkout::load_reg_steps();
424
+			$this->_verify_session();
425
+			// set up the EE_Checkout object
426
+			$this->checkout = $this->_initialize_checkout();
427
+			// filter checkout
428
+			$this->checkout = apply_filters('FHEE__EED_Single_Page_Checkout___initialize__checkout', $this->checkout);
429
+			// get the $_GET
430
+			$this->_get_request_vars();
431
+			if ($this->_block_bots()) {
432
+				return;
433
+			}
434
+			// filter continue_reg
435
+			$this->checkout->continue_reg = apply_filters(
436
+				'FHEE__EED_Single_Page_Checkout__init___continue_reg',
437
+				true,
438
+				$this->checkout
439
+			);
440
+			// load the reg steps array
441
+			if (! $this->_load_and_instantiate_reg_steps()) {
442
+				EED_Single_Page_Checkout::$_initialized = true;
443
+				return;
444
+			}
445
+			// set the current step
446
+			$this->checkout->set_current_step($this->checkout->step);
447
+			// and the next step
448
+			$this->checkout->set_next_step();
449
+			// verify that everything has been set up correctly
450
+			if (! ($this->_verify_transaction_and_get_registrations() && $this->_final_verifications())) {
451
+				EED_Single_Page_Checkout::$_initialized = true;
452
+				return;
453
+			}
454
+			do_action('AHEE__Single_Page_Checkout___initialize__after_final_verifications', $this->checkout);
455
+			// lock the transaction
456
+			$this->checkout->transaction->lock();
457
+			// make sure all of our cached objects are added to their respective model entity mappers
458
+			$this->checkout->refresh_all_entities();
459
+			// set amount owing
460
+			$this->checkout->amount_owing = $this->checkout->transaction->remaining();
461
+			// initialize each reg step, which gives them the chance to potentially alter the process
462
+			$this->_initialize_reg_steps();
463
+			// DEBUG LOG
464
+			// $this->checkout->log( __CLASS__, __FUNCTION__, __LINE__ );
465
+			// get reg form
466
+			if (! $this->_check_form_submission()) {
467
+				EED_Single_Page_Checkout::$_initialized = true;
468
+				return;
469
+			}
470
+			// checkout the action!!!
471
+			$this->_process_form_action();
472
+			// add some style and make it dance
473
+			$this->add_styles_and_scripts($this);
474
+			// kk... SPCO has successfully run
475
+			EED_Single_Page_Checkout::$_initialized = true;
476
+			// set no cache headers and constants
477
+			EE_System::do_not_cache();
478
+			// add anchor
479
+			add_action('loop_start', [$this, 'set_checkout_anchor'], 1);
480
+			// remove transaction lock
481
+			add_action('shutdown', [$this, 'unlock_transaction'], 1);
482
+		} catch (Exception $e) {
483
+			EE_Error::add_error($e->getMessage(), __FILE__, __FUNCTION__, __LINE__);
484
+		}
485
+	}
486
+
487
+
488
+	/**
489
+	 * checks that the session is valid and not expired
490
+	 *
491
+	 * @throws EE_Error
492
+	 * @throws ReflectionException
493
+	 */
494
+	private function _verify_session()
495
+	{
496
+		if (! EE_Registry::instance()->SSN instanceof EE_Session) {
497
+			throw new EE_Error(esc_html__('The EE_Session class could not be loaded.', 'event_espresso'));
498
+		}
499
+		$clear_session_requested = $this->request->getRequestParam('clear_session', false, 'bool');
500
+		// is session still valid ?
501
+		if (
502
+			$clear_session_requested
503
+			|| (
504
+				EE_Registry::instance()->SSN->expired()
505
+				&& $this->request->getRequestParam('e_reg_url_link') === ''
506
+			)
507
+		) {
508
+			$this->checkout = new EE_Checkout();
509
+			EE_Registry::instance()->SSN->clear_session(__CLASS__, __FUNCTION__);
510
+			// EE_Registry::instance()->SSN->reset_cart();
511
+			// EE_Registry::instance()->SSN->reset_checkout();
512
+			// EE_Registry::instance()->SSN->reset_transaction();
513
+			if (! $clear_session_requested) {
514
+				EE_Error::add_attention(
515
+					EE_Registry::$i18n_js_strings['registration_expiration_notice'],
516
+					__FILE__,
517
+					__FUNCTION__,
518
+					__LINE__
519
+				);
520
+			}
521
+			// EE_Registry::instance()->SSN->reset_expired();
522
+		}
523
+	}
524
+
525
+
526
+	/**
527
+	 * loads and instantiates EE_Checkout
528
+	 *
529
+	 * @return EE_Checkout
530
+	 * @throws EE_Error
531
+	 * @throws ReflectionException
532
+	 */
533
+	private function _initialize_checkout(): EE_Checkout
534
+	{
535
+		// look in session for existing checkout
536
+		/** @type EE_Checkout $checkout */
537
+		$checkout = EE_Registry::instance()->SSN->checkout();
538
+		// verify
539
+		if (! $checkout instanceof EE_Checkout) {
540
+			// instantiate EE_Checkout object for handling the properties of the current checkout process
541
+			$checkout = EE_Registry::instance()->load_file(
542
+				SPCO_INC_PATH,
543
+				'EE_Checkout',
544
+				'class',
545
+				[],
546
+				false
547
+			);
548
+		} else {
549
+			if ($checkout->current_step->is_final_step() && $checkout->exit_spco() === true) {
550
+				$this->unlock_transaction();
551
+				wp_safe_redirect($checkout->redirect_url);
552
+				exit();
553
+			}
554
+		}
555
+		$checkout = apply_filters('FHEE__EED_Single_Page_Checkout___initialize_checkout__checkout', $checkout);
556
+		// verify again
557
+		if (! $checkout instanceof EE_Checkout) {
558
+			throw new EE_Error(esc_html__('The EE_Checkout class could not be loaded.', 'event_espresso'));
559
+		}
560
+		// reset anything that needs a clean slate for each request
561
+		$checkout->reset_for_current_request();
562
+		return $checkout;
563
+	}
564
+
565
+
566
+	/**
567
+	 * @return void
568
+	 */
569
+	private function _get_request_vars()
570
+	{
571
+		// make sure this request is marked as belonging to EE
572
+		/** @var CurrentPage $current_page */
573
+		$current_page = LoaderFactory::getLoader()->getShared(CurrentPage::class);
574
+		$current_page->setEspressoPage(true);
575
+		// which step is being requested ?
576
+		$this->checkout->step = $this->request->getRequestParam('step', $this->_get_first_step());
577
+		// which step is being edited ?
578
+		$this->checkout->edit_step = $this->request->getRequestParam('edit_step');
579
+		// and what we're doing on the current step
580
+		$this->checkout->action = $this->request->getRequestParam('action', 'display_spco_reg_step');
581
+		// timestamp
582
+		$this->checkout->uts = $this->request->getRequestParam('uts', 0, 'int');
583
+		// returning to edit ?
584
+		$this->checkout->reg_url_link = $this->request->getRequestParam('e_reg_url_link');
585
+		// add reg url link to registration query params
586
+		if ($this->checkout->reg_url_link && strpos($this->checkout->reg_url_link, '1-') !== 0) {
587
+			$this->checkout->reg_cache_where_params[0]['REG_url_link'] = $this->checkout->reg_url_link;
588
+		}
589
+		// or some other kind of revisit ?
590
+		$this->checkout->revisit = $this->request->getRequestParam('revisit', false, 'bool');
591
+		// and whether to generate a reg form for this request
592
+		$this->checkout->generate_reg_form = $this->request->getRequestParam('generate_reg_form', true, 'bool');
593
+		// and whether to process a reg form submission for this request
594
+		$this->checkout->process_form_submission = $this->request->getRequestParam(
595
+			'process_form_submission',
596
+			$this->checkout->action === 'process_reg_step',
597
+			'bool'
598
+		);
599
+		$this->checkout->process_form_submission = filter_var(
600
+			$this->checkout->action !== 'display_spco_reg_step'
601
+				? $this->checkout->process_form_submission
602
+				: false,
603
+			FILTER_VALIDATE_BOOLEAN
604
+		);
605
+		$this->_display_request_vars();
606
+	}
607
+
608
+
609
+	/**
610
+	 * @return void
611
+	 */
612
+	protected function _display_request_vars()
613
+	{
614
+		if (! ($this->debug && defined('WP_DEBUG') && WP_DEBUG)) {
615
+			return;
616
+		}
617
+		EEH_Debug_Tools::printr($this->request->requestParams(), 'requestParams', __FILE__, __LINE__);
618
+		EEH_Debug_Tools::printr($this->checkout->step, '$this->checkout->step', __FILE__, __LINE__);
619
+		EEH_Debug_Tools::printr($this->checkout->edit_step, '$this->checkout->edit_step', __FILE__, __LINE__);
620
+		EEH_Debug_Tools::printr($this->checkout->action, '$this->checkout->action', __FILE__, __LINE__);
621
+		EEH_Debug_Tools::printr($this->checkout->reg_url_link, '$this->checkout->reg_url_link', __FILE__, __LINE__);
622
+		EEH_Debug_Tools::printr($this->checkout->revisit, '$this->checkout->revisit', __FILE__, __LINE__);
623
+		EEH_Debug_Tools::printr(
624
+			$this->checkout->generate_reg_form,
625
+			'$this->checkout->generate_reg_form',
626
+			__FILE__,
627
+			__LINE__
628
+		);
629
+		EEH_Debug_Tools::printr(
630
+			$this->checkout->process_form_submission,
631
+			'$this->checkout->process_form_submission',
632
+			__FILE__,
633
+			__LINE__
634
+		);
635
+	}
636
+
637
+
638
+	/**
639
+	 * _block_bots
640
+	 * checks that the incoming request has either of the following set:
641
+	 *  a UTS (unix timestamp) which indicates that the request was redirected from the Ticket Selector
642
+	 *  a REG URL Link, which indicates that the request is a return visit to SPCO for a valid TXN
643
+	 * so if you're not coming from the Ticket Selector nor returning for a valid IP...
644
+	 * then where you coming from man?
645
+	 *
646
+	 * @return boolean
647
+	 */
648
+	private function _block_bots(): bool
649
+	{
650
+		return EED_Invalid_Checkout_Access::getInvalidCheckoutAccess()->checkoutAccessIsInvalid($this->checkout);
651
+	}
652
+
653
+
654
+	/**
655
+	 *  gets slug for first step in $_reg_steps_array
656
+	 *
657
+	 * @return string
658
+	 */
659
+	private function _get_first_step(): string
660
+	{
661
+		$first_step = reset(EED_Single_Page_Checkout::$_reg_steps_array);
662
+		return $first_step['slug'] ?? 'attendee_information';
663
+	}
664
+
665
+
666
+	/**
667
+	 * instantiates each reg step based on the loaded reg_steps array
668
+	 *
669
+	 * @return bool
670
+	 * @throws EE_Error
671
+	 * @throws InvalidArgumentException
672
+	 * @throws InvalidDataTypeException
673
+	 * @throws InvalidInterfaceException
674
+	 * @throws ReflectionException
675
+	 */
676
+	private function _load_and_instantiate_reg_steps(): bool
677
+	{
678
+		do_action('AHEE__Single_Page_Checkout___load_and_instantiate_reg_steps__start', $this->checkout);
679
+		// have reg_steps already been instantiated ?
680
+		if (
681
+			empty($this->checkout->reg_steps)
682
+			|| apply_filters('FHEE__Single_Page_Checkout__load_reg_steps__reload_reg_steps', false, $this->checkout)
683
+		) {
684
+			// if not, then loop through raw reg steps array
685
+			foreach (EED_Single_Page_Checkout::$_reg_steps_array as $order => $reg_step) {
686
+				if (! $this->_load_and_instantiate_reg_step($reg_step, $order)) {
687
+					return false;
688
+				}
689
+			}
690
+			if (isset($this->checkout->reg_steps['registration_confirmation'])) {
691
+				// skip the registration_confirmation page ?
692
+				// just remove it from the reg steps array
693
+				$this->checkout->remove_reg_step('registration_confirmation', false);
694
+			}
695
+			// filter the array for good luck
696
+			$this->checkout->reg_steps = apply_filters(
697
+				'FHEE__Single_Page_Checkout__load_reg_steps__reg_steps',
698
+				$this->checkout->reg_steps
699
+			);
700
+			// finally re-sort based on the reg step class order properties
701
+			$this->checkout->sort_reg_steps();
702
+		} else {
703
+			foreach ($this->checkout->reg_steps as $reg_step) {
704
+				// set all current step stati to FALSE
705
+				$reg_step->set_is_current_step(false);
706
+			}
707
+		}
708
+		if (empty($this->checkout->reg_steps)) {
709
+			EE_Error::add_error(
710
+				esc_html__('No Reg Steps were loaded..', 'event_espresso'),
711
+				__FILE__,
712
+				__FUNCTION__,
713
+				__LINE__
714
+			);
715
+			return false;
716
+		}
717
+		// make reg step details available to JS
718
+		$this->checkout->set_reg_step_JSON_info();
719
+		return true;
720
+	}
721
+
722
+
723
+	/**
724
+	 * @param array $reg_step
725
+	 * @param int   $order
726
+	 * @return bool
727
+	 * @throws EE_Error
728
+	 * @throws ReflectionException
729
+	 */
730
+	private function _load_and_instantiate_reg_step(array $reg_step = [], int $order = 0): bool
731
+	{
732
+		// we need a file_path, class_name, and slug to add a reg step
733
+		if (isset($reg_step['file_path'], $reg_step['class_name'], $reg_step['slug'])) {
734
+			// if editing a specific step, but this is NOT that step... (and it's not the 'finalize_registration' step)
735
+			if (
736
+				$this->checkout->reg_url_link
737
+				&& $this->checkout->step !== $reg_step['slug']
738
+				&& $reg_step['slug'] !== 'finalize_registration'
739
+				// normally at this point we would NOT load the reg step, but this filter can change that
740
+				&& apply_filters(
741
+					'FHEE__Single_Page_Checkout___load_and_instantiate_reg_step__bypass_reg_step',
742
+					true,
743
+					$reg_step,
744
+					$this->checkout
745
+				)
746
+			) {
747
+				return true;
748
+			}
749
+
750
+			// instantiate step class using file path and class name
751
+			$reg_step_obj = EE_Registry::instance()->load_file(
752
+				$reg_step['file_path'],
753
+				$reg_step['class_name'],
754
+				'class',
755
+				[$this->checkout],
756
+				false
757
+			);
758
+			// did we get the goods ?
759
+			if ($reg_step_obj instanceof EE_SPCO_Reg_Step) {
760
+				// set reg step order based on config
761
+				$reg_step_obj->set_order($order);
762
+				// add instantiated reg step object to the master reg steps array
763
+				$this->checkout->add_reg_step($reg_step_obj);
764
+			} else {
765
+				EE_Error::add_error(
766
+					esc_html__('The current step could not be set.', 'event_espresso'),
767
+					__FILE__,
768
+					__FUNCTION__,
769
+					__LINE__
770
+				);
771
+				return false;
772
+			}
773
+		} else {
774
+			if (WP_DEBUG) {
775
+				EE_Error::add_error(
776
+					sprintf(
777
+						esc_html__(
778
+							'A registration step could not be loaded. One or more of the following data points is invalid:%4$s%5$sFile Path: %1$s%6$s%5$sClass Name: %2$s%6$s%5$sSlug: %3$s%6$s%7$s',
779
+							'event_espresso'
780
+						),
781
+						$reg_step['file_path'] ?? '',
782
+						$reg_step['class_name'] ?? '',
783
+						$reg_step['slug'] ?? '',
784
+						'<ul>',
785
+						'<li>',
786
+						'</li>',
787
+						'</ul>'
788
+					),
789
+					__FILE__,
790
+					__FUNCTION__,
791
+					__LINE__
792
+				);
793
+			}
794
+			return false;
795
+		}
796
+		return true;
797
+	}
798
+
799
+
800
+	/**
801
+	 * @return bool
802
+	 * @throws EE_Error
803
+	 * @throws ReflectionException
804
+	 */
805
+	private function _verify_transaction_and_get_registrations(): bool
806
+	{
807
+		// was there already a valid transaction in the checkout from the session ?
808
+		if (! $this->checkout->transaction instanceof EE_Transaction) {
809
+			// get transaction from db or session
810
+			$this->checkout->transaction = $this->checkout->reg_url_link && ! is_admin()
811
+				? $this->_get_transaction_and_cart_for_previous_visit()
812
+				: $this->_get_cart_for_current_session_and_setup_new_transaction();
813
+			if (! $this->checkout->transaction instanceof EE_Transaction) {
814
+				EE_Error::add_error(
815
+					esc_html__(
816
+						'Your Registration and Transaction information could not be retrieved from the db.',
817
+						'event_espresso'
818
+					),
819
+					__FILE__,
820
+					__FUNCTION__,
821
+					__LINE__
822
+				);
823
+				$this->checkout->transaction = EE_Transaction::new_instance();
824
+				// add some style and make it dance
825
+				$this->add_styles_and_scripts($this);
826
+				EED_Single_Page_Checkout::$_initialized = true;
827
+				return false;
828
+			}
829
+			// and the registrations for the transaction
830
+			$this->_get_registrations($this->checkout->transaction);
831
+		}
832
+		return true;
833
+	}
834
+
835
+
836
+	/**
837
+	 * @return EE_Transaction|null
838
+	 * @throws EE_Error
839
+	 * @throws ReflectionException
840
+	 */
841
+	private function _get_transaction_and_cart_for_previous_visit(): ?EE_Transaction
842
+	{
843
+		/** @var $TXN_model EEM_Transaction */
844
+		$TXN_model = EE_Registry::instance()->load_model('Transaction');
845
+		// because the reg_url_link is present in the request,
846
+		// this is a return visit to SPCO, so we'll get the transaction data from the db
847
+		$transaction = $TXN_model->get_transaction_from_reg_url_link($this->checkout->reg_url_link);
848
+		// verify transaction
849
+		if ($transaction instanceof EE_Transaction) {
850
+			// and get the cart that was used for that transaction
851
+			$this->checkout->cart = $this->_get_cart_for_transaction($transaction);
852
+			return $transaction;
853
+		}
854
+		EE_Error::add_error(
855
+			esc_html__(
856
+				'Your Registration and Transaction information could not be retrieved from the db.',
857
+				'event_espresso'
858
+			),
859
+			__FILE__,
860
+			__FUNCTION__,
861
+			__LINE__
862
+		);
863
+		return null;
864
+	}
865
+
866
+
867
+	/**
868
+	 * @param EE_Transaction|null $transaction
869
+	 * @return EE_Cart
870
+	 */
871
+	private function _get_cart_for_transaction(?EE_Transaction $transaction): EE_Cart
872
+	{
873
+		return $this->checkout->get_cart_for_transaction($transaction);
874
+	}
875
+
876
+
877
+	/**
878
+	 * @param EE_Transaction|null $transaction
879
+	 * @return EE_Cart
880
+	 */
881
+	public function get_cart_for_transaction(?EE_Transaction $transaction): EE_Cart
882
+	{
883
+		return $this->checkout->get_cart_for_transaction($transaction);
884
+	}
885
+
886
+
887
+	/**
888
+	 * generates a new EE_Transaction object and adds it to the $_transaction property.
889
+	 *
890
+	 * @return EE_Transaction|null
891
+	 * @throws EE_Error
892
+	 * @throws ReflectionException
893
+	 */
894
+	private function _get_cart_for_current_session_and_setup_new_transaction(): ?EE_Transaction
895
+	{
896
+		//  if there's no transaction, then this is the FIRST visit to SPCO
897
+		// so load up the cart ( passing nothing for the TXN because it doesn't exist yet )
898
+		$this->checkout->cart = $this->_get_cart_for_transaction(null);
899
+		// and then create a new transaction
900
+		$transaction = $this->_initialize_transaction();
901
+		// verify transaction
902
+		if ($transaction instanceof EE_Transaction) {
903
+			// and save TXN data to the cart
904
+			$this->checkout->cart->get_grand_total()->save_this_and_descendants_to_txn($transaction->ID());
905
+		} else {
906
+			EE_Error::add_error(
907
+				esc_html__('A Valid Transaction could not be initialized.', 'event_espresso'),
908
+				__FILE__,
909
+				__FUNCTION__,
910
+				__LINE__
911
+			);
912
+		}
913
+		return $transaction;
914
+	}
915
+
916
+
917
+	/**
918
+	 * generates a new EE_Transaction object and adds it to the $_transaction property.
919
+	 *
920
+	 * @return EE_Transaction|null
921
+	 */
922
+	private function _initialize_transaction(): ?EE_Transaction
923
+	{
924
+		try {
925
+			// ensure cart totals have been calculated
926
+			$this->checkout->cart->get_grand_total()->recalculate_total_including_taxes();
927
+			// grab the cart grand total
928
+			$cart_total = $this->checkout->cart->get_cart_grand_total();
929
+			// create new TXN
930
+			$transaction = EE_Transaction::new_instance(
931
+				[
932
+					'TXN_reg_steps' => $this->checkout->initialize_txn_reg_steps_array(),
933
+					'TXN_total'     => max($cart_total, 0),
934
+					'TXN_paid'      => 0,
935
+					'STS_ID'        => EEM_Transaction::failed_status_code,
936
+				]
937
+			);
938
+			// save it so that we have an ID for other objects to use
939
+			$transaction->save();
940
+			// set cron job for following up on TXNs after their session has expired
941
+			EE_Cron_Tasks::schedule_expired_transaction_check(
942
+				EE_Registry::instance()->SSN->expiration() + 1,
943
+				$transaction->ID()
944
+			);
945
+			return $transaction;
946
+		} catch (Exception $e) {
947
+			EE_Error::add_error($e->getMessage(), __FILE__, __FUNCTION__, __LINE__);
948
+		}
949
+		return null;
950
+	}
951
+
952
+
953
+	/**
954
+	 * _get_registrations
955
+	 *
956
+	 * @param EE_Transaction $transaction
957
+	 * @return void
958
+	 * @throws EE_Error
959
+	 * @throws ReflectionException
960
+	 */
961
+	private function _get_registrations(EE_Transaction $transaction)
962
+	{
963
+		// first step: grab the registrants  { : o
964
+		$registrations                      = $transaction->registrations($this->checkout->reg_cache_where_params);
965
+		$this->checkout->total_ticket_count = count($registrations);
966
+		// verify registrations have been set
967
+		if (empty($registrations)) {
968
+			// if no cached registrations, then check the db
969
+			$registrations = $transaction->registrations($this->checkout->reg_cache_where_params);
970
+			// still nothing ? well as long as this isn't a revisit
971
+			if (empty($registrations) && ! $this->checkout->revisit) {
972
+				// generate new registrations from scratch
973
+				$registrations = $this->_initialize_registrations($transaction);
974
+			}
975
+		}
976
+		// sort by their original registration order
977
+		usort($registrations, ['EED_Single_Page_Checkout', 'sort_registrations_by_REG_count']);
978
+		// then loop thru the array
979
+		foreach ($registrations as $registration) {
980
+			// verify each registration
981
+			if ($registration instanceof EE_Registration) {
982
+				// we display all attendee info for the primary registrant
983
+				if (
984
+					$this->checkout->reg_url_link === $registration->reg_url_link()
985
+					&& $registration->is_primary_registrant()
986
+				) {
987
+					$this->checkout->primary_revisit = true;
988
+					break;
989
+				}
990
+				if ($this->checkout->revisit && $this->checkout->reg_url_link !== $registration->reg_url_link()) {
991
+					// but hide info if it doesn't belong to you
992
+					$transaction->clear_cache('Registration', $registration->ID());
993
+					$this->checkout->total_ticket_count--;
994
+				}
995
+				$this->checkout->set_reg_status_updated($registration->ID(), false);
996
+			}
997
+		}
998
+	}
999
+
1000
+
1001
+	/**
1002
+	 * adds related EE_Registration objects for each ticket in the cart to the current EE_Transaction object
1003
+	 *
1004
+	 * @param EE_Transaction $transaction
1005
+	 * @return array
1006
+	 * @throws EE_Error
1007
+	 * @throws ReflectionException
1008
+	 */
1009
+	private function _initialize_registrations(EE_Transaction $transaction): array
1010
+	{
1011
+		$att_nmbr      = 0;
1012
+		$registrations = [];
1013
+		/** @type EE_Registration_Processor $registration_processor */
1014
+		$registration_processor             = EE_Registry::instance()->load_class('Registration_Processor');
1015
+		$this->checkout->total_ticket_count = $this->checkout->cart->all_ticket_quantity_count();
1016
+		// now let's add the cart items to the $transaction
1017
+		foreach ($this->checkout->cart->get_tickets() as $line_item) {
1018
+			// do the following for each ticket of this type they selected
1019
+			for ($x = 1; $x <= $line_item->quantity(); $x++) {
1020
+				$att_nmbr++;
1021
+				/** @var CreateRegistrationCommand $CreateRegistrationCommand */
1022
+				$CreateRegistrationCommand = EE_Registry::instance()->create(
1023
+					CreateRegistrationCommand::class,
1024
+					[
1025
+						$transaction,
1026
+						$line_item,
1027
+						$att_nmbr,
1028
+						$this->checkout->total_ticket_count,
1029
+					]
1030
+				);
1031
+				// override capabilities for frontend registrations
1032
+				if ($this->request->isFrontend()) {
1033
+					$CreateRegistrationCommand->setCapCheck(
1034
+						new PublicCapabilities('', 'create_new_registration')
1035
+					);
1036
+				}
1037
+				$registration = EE_Registry::instance()->BUS->execute($CreateRegistrationCommand);
1038
+				if (! $registration instanceof EE_Registration) {
1039
+					throw new InvalidEntityException($registration, 'EE_Registration');
1040
+				}
1041
+				$registrations[ $registration->ID() ] = $registration;
1042
+			}
1043
+		}
1044
+		$registration_processor->fix_reg_final_price_rounding_issue($transaction);
1045
+		return $registrations;
1046
+	}
1047
+
1048
+
1049
+	/**
1050
+	 * @param EE_Registration $reg_A
1051
+	 * @param EE_Registration $reg_B
1052
+	 * @return int
1053
+	 * @throws EE_Error
1054
+	 * @throws ReflectionException
1055
+	 */
1056
+	public static function sort_registrations_by_REG_count(EE_Registration $reg_A, EE_Registration $reg_B): int
1057
+	{
1058
+		// this shouldn't ever happen within the same TXN, but oh well
1059
+		if ($reg_A->count() === $reg_B->count()) {
1060
+			return 0;
1061
+		}
1062
+		return ($reg_A->count() > $reg_B->count()) ? 1 : -1;
1063
+	}
1064
+
1065
+
1066
+	/**
1067
+	 * just makes sure that everything is set up correctly before proceeding
1068
+	 *
1069
+	 * @return bool
1070
+	 * @throws EE_Error
1071
+	 * @throws ReflectionException
1072
+	 */
1073
+	private function _final_verifications(): bool
1074
+	{
1075
+		// filter checkout
1076
+		$this->checkout = apply_filters(
1077
+			'FHEE__EED_Single_Page_Checkout___final_verifications__checkout',
1078
+			$this->checkout
1079
+		);
1080
+		// verify that current step is still set correctly
1081
+		if (! $this->checkout->current_step instanceof EE_SPCO_Reg_Step) {
1082
+			EE_Error::add_error(
1083
+				esc_html__(
1084
+					'We\'re sorry but the registration process can not proceed because one or more registration steps were not setup correctly. Please refresh the page and try again or contact support.',
1085
+					'event_espresso'
1086
+				),
1087
+				__FILE__,
1088
+				__FUNCTION__,
1089
+				__LINE__
1090
+			);
1091
+			return false;
1092
+		}
1093
+		// if returning to SPCO, then verify that primary registrant is set
1094
+		if (! empty($this->checkout->reg_url_link)) {
1095
+			$valid_registrant = $this->checkout->transaction->primary_registration();
1096
+			if (! $valid_registrant instanceof EE_Registration) {
1097
+				EE_Error::add_error(
1098
+					esc_html__(
1099
+						'We\'re sorry but there appears to be an error with the "reg_url_link" or the primary registrant for this transaction. Please refresh the page and try again or contact support.',
1100
+						'event_espresso'
1101
+					),
1102
+					__FILE__,
1103
+					__FUNCTION__,
1104
+					__LINE__
1105
+				);
1106
+				return false;
1107
+			}
1108
+			$valid_registrant = null;
1109
+			foreach (
1110
+				$this->checkout->transaction->registrations(
1111
+					$this->checkout->reg_cache_where_params
1112
+				) as $registration
1113
+			) {
1114
+				if (
1115
+					$registration instanceof EE_Registration
1116
+					&& $registration->reg_url_link() === $this->checkout->reg_url_link
1117
+				) {
1118
+					$valid_registrant = $registration;
1119
+				}
1120
+			}
1121
+			if (! $valid_registrant instanceof EE_Registration) {
1122
+				// hmmm... maybe we have the wrong session because the user is opening multiple tabs ?
1123
+				if (EED_Single_Page_Checkout::$_checkout_verified) {
1124
+					// clear the session, mark the checkout as unverified, and try again
1125
+					EE_Registry::instance()->SSN->clear_session(__CLASS__, __FUNCTION__);
1126
+					EED_Single_Page_Checkout::$_initialized       = false;
1127
+					EED_Single_Page_Checkout::$_checkout_verified = false;
1128
+					$this->_initialize();
1129
+					EE_Error::reset_notices();
1130
+					return false;
1131
+				}
1132
+				EE_Error::add_error(
1133
+					esc_html__(
1134
+						'We\'re sorry but there appears to be an error with the "reg_url_link" or the transaction itself. Please refresh the page and try again or contact support.',
1135
+						'event_espresso'
1136
+					),
1137
+					__FILE__,
1138
+					__FUNCTION__,
1139
+					__LINE__
1140
+				);
1141
+				return false;
1142
+			}
1143
+		}
1144
+		// now that things have been kinda sufficiently verified,
1145
+		// let's add the checkout to the session so that it's available to other systems
1146
+		EE_Registry::instance()->SSN->set_checkout($this->checkout);
1147
+		return true;
1148
+	}
1149
+
1150
+
1151
+	/**
1152
+	 * first makes sure that EE_Transaction_Processor::set_reg_step_initiated() is called as required
1153
+	 * then loops thru all the active reg steps and calls the initialize_reg_step() method
1154
+	 *
1155
+	 * @param bool $reinitializing
1156
+	 * @throws EE_Error
1157
+	 */
1158
+	private function _initialize_reg_steps(bool $reinitializing = false)
1159
+	{
1160
+		$this->checkout->set_reg_step_initiated($this->checkout->current_step);
1161
+		// loop thru all steps to call their individual "initialize" methods and set i18n strings for JS
1162
+		foreach ($this->checkout->reg_steps as $reg_step) {
1163
+			if (! $reg_step->initialize_reg_step()) {
1164
+				// if not initialized then maybe this step is being removed...
1165
+				if (! $reinitializing && $reg_step->is_current_step()) {
1166
+					// if it was the current step, then we need to start over here
1167
+					$this->_initialize_reg_steps(true);
1168
+					return;
1169
+				}
1170
+				continue;
1171
+			}
1172
+			// add css and JS for current step
1173
+			$this->add_styles_and_scripts($reg_step);
1174
+			if ($reg_step->is_current_step()) {
1175
+				// the text that appears on the reg step form submit button
1176
+				$reg_step->set_submit_button_text();
1177
+			}
1178
+		}
1179
+		// dynamically creates hook point like: AHEE__Single_Page_Checkout___initialize_reg_step__attendee_information
1180
+		do_action(
1181
+			"AHEE__Single_Page_Checkout___initialize_reg_step__{$this->checkout->current_step->slug()}",
1182
+			$this->checkout->current_step
1183
+		);
1184
+	}
1185
+
1186
+
1187
+	/**
1188
+	 * @return boolean
1189
+	 * @throws EE_Error
1190
+	 * @throws ReflectionException
1191
+	 */
1192
+	private function _check_form_submission(): bool
1193
+	{
1194
+		// does this request require the reg form to be generated ?
1195
+		if ($this->checkout->generate_reg_form) {
1196
+			// ever heard that song by Blue Rodeo ?
1197
+			try {
1198
+				$this->checkout->current_step->reg_form = $this->checkout->current_step->generate_reg_form();
1199
+				// if not displaying a form, then check for form submission
1200
+				if (
1201
+					$this->checkout->process_form_submission
1202
+					&& $this->checkout->current_step->reg_form->was_submitted()
1203
+				) {
1204
+					// clear out any old data in case this step is being run again
1205
+					$this->checkout->current_step->set_valid_data([]);
1206
+					// capture submitted form data
1207
+					$request_data = $this->request->requestParams();
1208
+					$this->checkout->current_step->reg_form->receive_form_submission(
1209
+						(array) apply_filters(
1210
+							'FHEE__Single_Page_Checkout___check_form_submission__request_params',
1211
+							$request_data,
1212
+							$this->checkout
1213
+						)
1214
+					);
1215
+					// validate submitted form data
1216
+					if (! $this->checkout->continue_reg || ! $this->checkout->current_step->reg_form->is_valid()) {
1217
+						// thou shall not pass !!!
1218
+						$this->checkout->continue_reg = false;
1219
+						// any form validation errors?
1220
+						if ($this->checkout->current_step->reg_form->submission_error_message() !== '') {
1221
+							EE_Error::add_error(
1222
+								$this->checkout->current_step->reg_form->submission_error_message(),
1223
+								__FILE__,
1224
+								__FUNCTION__,
1225
+								__LINE__
1226
+							);
1227
+						}
1228
+						// well not really... what will happen is
1229
+						// we'll just get redirected back to redo the current step
1230
+						$this->go_to_next_step();
1231
+						return false;
1232
+					}
1233
+				}
1234
+			} catch (EE_Error $e) {
1235
+				$e->get_error();
1236
+			}
1237
+		}
1238
+		return true;
1239
+	}
1240
+
1241
+
1242
+	/**
1243
+	 * @return void
1244
+	 * @throws EE_Error
1245
+	 * @throws ReflectionException
1246
+	 */
1247
+	private function _process_form_action()
1248
+	{
1249
+		// what cha wanna do?
1250
+		switch ($this->checkout->action) {
1251
+			// AJAX next step reg form
1252
+			case 'display_spco_reg_step':
1253
+				$this->checkout->redirect = false;
1254
+				if ($this->request->isAjax()) {
1255
+					$this->checkout->json_response->set_reg_step_html(
1256
+						$this->checkout->current_step->display_reg_form()
1257
+					);
1258
+				}
1259
+				break;
1260
+			default:
1261
+				// meh... do one of those other steps first
1262
+				if (
1263
+					! empty($this->checkout->action)
1264
+					&& is_callable([$this->checkout->current_step, $this->checkout->action])
1265
+				) {
1266
+					// dynamically creates hook point like:
1267
+					//   AHEE__Single_Page_Checkout__before_attendee_information__process_reg_step
1268
+					do_action(
1269
+						"AHEE__Single_Page_Checkout__before_{$this->checkout->current_step->slug()}__{$this->checkout->action}",
1270
+						$this->checkout->current_step
1271
+					);
1272
+					$process_reg_step = apply_filters(
1273
+						"AHEE__Single_Page_Checkout__process_reg_step__{$this->checkout->current_step->slug()}__{$this->checkout->action}",
1274
+						true,
1275
+						$this->checkout->current_step,
1276
+						$this
1277
+					);
1278
+					// call action on current step
1279
+					if ($process_reg_step && call_user_func([$this->checkout->current_step, $this->checkout->action])) {
1280
+						// good registrant, you get to proceed
1281
+						if (
1282
+							$this->checkout->current_step->success_message() !== ''
1283
+							&& apply_filters(
1284
+								'FHEE__Single_Page_Checkout___process_form_action__display_success',
1285
+								false
1286
+							)
1287
+						) {
1288
+							EE_Error::add_success(
1289
+								$this->checkout->current_step->success_message()
1290
+								. '<br />' . $this->checkout->next_step->_instructions()
1291
+							);
1292
+						}
1293
+						// pack it up, pack it in...
1294
+						$this->_setup_redirect();
1295
+					}
1296
+					// dynamically creates hook point like:
1297
+					//  AHEE__Single_Page_Checkout__after_payment_options__process_reg_step
1298
+					do_action(
1299
+						"AHEE__Single_Page_Checkout__after_{$this->checkout->current_step->slug()}__{$this->checkout->action}",
1300
+						$this->checkout->current_step
1301
+					);
1302
+				} else {
1303
+					EE_Error::add_error(
1304
+						sprintf(
1305
+							esc_html__(
1306
+								'The requested form action "%s" does not exist for the current "%s" registration step.',
1307
+								'event_espresso'
1308
+							),
1309
+							$this->checkout->action,
1310
+							$this->checkout->current_step->name()
1311
+						),
1312
+						__FILE__,
1313
+						__FUNCTION__,
1314
+						__LINE__
1315
+					);
1316
+				}
1317
+			// end default
1318
+		}
1319
+		// store our progress so far
1320
+		$this->checkout->stash_transaction_and_checkout();
1321
+		// advance to the next step! If you pass GO, collect $200
1322
+		$this->go_to_next_step();
1323
+	}
1324
+
1325
+
1326
+	/**
1327
+	 * @param EED_Single_Page_Checkout|EE_SPCO_Reg_Step $target an object with the method `translate_js_strings` and
1328
+	 *                                                          `enqueue_styles_and_scripts`.
1329
+	 * @return void
1330
+	 */
1331
+	public function add_styles_and_scripts($target)
1332
+	{
1333
+		// i18n
1334
+		$target->translate_js_strings();
1335
+		if ($this->checkout->admin_request) {
1336
+			add_action('admin_enqueue_scripts', [$target, 'enqueue_styles_and_scripts']);
1337
+		} else {
1338
+			add_action('wp_enqueue_scripts', [$target, 'enqueue_styles_and_scripts']);
1339
+		}
1340
+	}
1341
+
1342
+
1343
+	/**
1344
+	 * @return void
1345
+	 */
1346
+	public function translate_js_strings()
1347
+	{
1348
+		EE_Registry::$i18n_js_strings['revisit']                        = $this->checkout->revisit;
1349
+		EE_Registry::$i18n_js_strings['e_reg_url_link']                 = $this->checkout->reg_url_link;
1350
+		EE_Registry::$i18n_js_strings['server_error']                   = esc_html__(
1351
+			'An unknown error occurred on the server while attempting to process your request. Please refresh the page and try again or contact support.',
1352
+			'event_espresso'
1353
+		);
1354
+		EE_Registry::$i18n_js_strings['invalid_json_response']          = esc_html__(
1355
+			'An invalid response was returned from the server while attempting to process your request. Please refresh the page and try again or contact support.',
1356
+			'event_espresso'
1357
+		);
1358
+		EE_Registry::$i18n_js_strings['validation_error']               = esc_html__(
1359
+			'There appears to be a problem with the form validation configuration! Please check the admin settings or contact support.',
1360
+			'event_espresso'
1361
+		);
1362
+		EE_Registry::$i18n_js_strings['invalid_payment_method']         = esc_html__(
1363
+			'There appears to be a problem with the payment method configuration! Please refresh the page and try again or contact support.',
1364
+			'event_espresso'
1365
+		);
1366
+		EE_Registry::$i18n_js_strings['reg_step_error']                 = esc_html__(
1367
+			'This registration step could not be completed. Please refresh the page and try again.',
1368
+			'event_espresso'
1369
+		);
1370
+		EE_Registry::$i18n_js_strings['invalid_coupon']                 = esc_html__(
1371
+			'We\'re sorry but that coupon code does not appear to be valid. If this is incorrect, please contact the site administrator.',
1372
+			'event_espresso'
1373
+		);
1374
+		EE_Registry::$i18n_js_strings['process_registration']           = sprintf(
1375
+			esc_html__(
1376
+				'Please wait while we process your registration.%sDo not refresh the page or navigate away while this is happening.%sThank you for your patience.',
1377
+				'event_espresso'
1378
+			),
1379
+			'<br/>',
1380
+			'<br/>'
1381
+		);
1382
+		EE_Registry::$i18n_js_strings['language']                       = get_bloginfo('language');
1383
+		EE_Registry::$i18n_js_strings['EESID']                          = EE_Registry::instance()->SSN->id();
1384
+		EE_Registry::$i18n_js_strings['currency']                       = EE_Registry::instance()->CFG->currency;
1385
+		EE_Registry::$i18n_js_strings['datepicker_yearRange']           = '-150:+20';
1386
+		EE_Registry::$i18n_js_strings['timer_years']                    = esc_html__('years', 'event_espresso');
1387
+		EE_Registry::$i18n_js_strings['timer_months']                   = esc_html__('months', 'event_espresso');
1388
+		EE_Registry::$i18n_js_strings['timer_weeks']                    = esc_html__('weeks', 'event_espresso');
1389
+		EE_Registry::$i18n_js_strings['timer_days']                     = esc_html__('days', 'event_espresso');
1390
+		EE_Registry::$i18n_js_strings['timer_hours']                    = esc_html__('hours', 'event_espresso');
1391
+		EE_Registry::$i18n_js_strings['timer_minutes']                  = esc_html__('minutes', 'event_espresso');
1392
+		EE_Registry::$i18n_js_strings['timer_seconds']                  = esc_html__('seconds', 'event_espresso');
1393
+		EE_Registry::$i18n_js_strings['timer_year']                     = esc_html__('year', 'event_espresso');
1394
+		EE_Registry::$i18n_js_strings['timer_month']                    = esc_html__('month', 'event_espresso');
1395
+		EE_Registry::$i18n_js_strings['timer_week']                     = esc_html__('week', 'event_espresso');
1396
+		EE_Registry::$i18n_js_strings['timer_day']                      = esc_html__('day', 'event_espresso');
1397
+		EE_Registry::$i18n_js_strings['timer_hour']                     = esc_html__('hour', 'event_espresso');
1398
+		EE_Registry::$i18n_js_strings['timer_minute']                   = esc_html__('minute', 'event_espresso');
1399
+		EE_Registry::$i18n_js_strings['timer_second']                   = esc_html__('second', 'event_espresso');
1400
+		EE_Registry::$i18n_js_strings['registration_expiration_notice'] =
1401
+			EED_Single_Page_Checkout::getRegistrationExpirationNotice();
1402
+		EE_Registry::$i18n_js_strings['ajax_submit']                    = apply_filters(
1403
+			'FHEE__Single_Page_Checkout__translate_js_strings__ajax_submit',
1404
+			true
1405
+		);
1406
+		EE_Registry::$i18n_js_strings['session_extension']              = absint(
1407
+			apply_filters('FHEE__EE_Session__extend_expiration__seconds_added', 10 * MINUTE_IN_SECONDS)
1408
+		);
1409
+		EE_Registry::$i18n_js_strings['session_expiration']    = EE_Registry::instance()->SSN->expiration();
1410
+		EE_Registry::$i18n_js_strings['use_session_countdown'] = EE_Registry::instance()->CFG->registration->useSessionCountdown();
1411
+	}
1412
+
1413
+
1414
+	/**
1415
+	 * @return void
1416
+	 * @throws EE_Error
1417
+	 */
1418
+	public function enqueue_styles_and_scripts()
1419
+	{
1420
+		// load css
1421
+		wp_register_style(
1422
+			'single_page_checkout',
1423
+			SPCO_CSS_URL . 'single_page_checkout.css',
1424
+			['espresso_default'],
1425
+			EVENT_ESPRESSO_VERSION
1426
+		);
1427
+		wp_enqueue_style('single_page_checkout');
1428
+		// load JS
1429
+		wp_register_script(
1430
+			'single_page_checkout',
1431
+			SPCO_JS_URL . 'single_page_checkout.js',
1432
+			['espresso_core', 'underscore', 'ee_form_section_validation'],
1433
+			EVENT_ESPRESSO_VERSION,
1434
+			true
1435
+		);
1436
+		if ($this->checkout->registration_form instanceof EE_Form_Section_Proper) {
1437
+			$this->checkout->registration_form->enqueue_js();
1438
+		}
1439
+		if ($this->checkout->current_step->reg_form instanceof EE_Form_Section_Proper) {
1440
+			$this->checkout->current_step->reg_form->enqueue_js();
1441
+		}
1442
+		wp_enqueue_script('single_page_checkout');
1443
+		/**
1444
+		 * global action hook for enqueueing styles and scripts with
1445
+		 * spco calls.
1446
+		 */
1447
+		do_action('AHEE__EED_Single_Page_Checkout__enqueue_styles_and_scripts', $this);
1448
+		/**
1449
+		 * dynamic action hook for enqueueing styles and scripts with spco calls.
1450
+		 * The hook will end up being something like:
1451
+		 *      AHEE__EED_Single_Page_Checkout__enqueue_styles_and_scripts__attendee_information
1452
+		 */
1453
+		do_action(
1454
+			'AHEE__EED_Single_Page_Checkout__enqueue_styles_and_scripts__' . $this->checkout->current_step->slug(),
1455
+			$this
1456
+		);
1457
+	}
1458
+
1459
+
1460
+	/**
1461
+	 * display the Registration Single Page Checkout Form
1462
+	 *
1463
+	 * @return void
1464
+	 * @throws EE_Error
1465
+	 * @throws ReflectionException
1466
+	 */
1467
+	private function _display_spco_reg_form()
1468
+	{
1469
+		// if registering via the admin, just display the reg form for the current step
1470
+		if ($this->checkout->admin_request) {
1471
+			EED_Single_Page_Checkout::getResponse()->addOutput($this->checkout->current_step->display_reg_form());
1472
+		} else {
1473
+			// add powered by EE msg
1474
+			add_action('AHEE__SPCO__reg_form_footer', ['EED_Single_Page_Checkout', 'display_registration_footer']);
1475
+			$empty_cart                                 = count(
1476
+					$this->checkout->transaction->registrations($this->checkout->reg_cache_where_params)
1477
+				) < 1;
1478
+			EE_Registry::$i18n_js_strings['empty_cart'] = $empty_cart;
1479
+			$cookies_not_set_msg                        = '';
1480
+			if ($empty_cart) {
1481
+				$cookies_not_set_msg = apply_filters(
1482
+					'FHEE__Single_Page_Checkout__display_spco_reg_form__cookies_not_set_msg',
1483
+					sprintf(
1484
+						esc_html__(
1485
+							'%1$s%3$sIt appears your browser is not currently set to accept Cookies%4$s%5$sIn order to register for events, you need to enable cookies.%7$sIf you require assistance, then click the following link to learn how to %8$senable cookies%9$s%6$s%2$s',
1486
+							'event_espresso'
1487
+						),
1488
+						'<div class="ee-attention hidden" id="ee-cookies-not-set-msg">',
1489
+						'</div>',
1490
+						'<h6 class="important-notice">',
1491
+						'</h6>',
1492
+						'<p>',
1493
+						'</p>',
1494
+						'<br />',
1495
+						'<a href="https://www.whatismybrowser.com/guides/how-to-enable-cookies/" target="_blank" rel="noopener noreferrer">',
1496
+						'</a>'
1497
+					)
1498
+				);
1499
+			}
1500
+			$this->checkout->registration_form = new EE_Form_Section_Proper(
1501
+				[
1502
+					'name'            => 'single-page-checkout',
1503
+					'html_id'         => 'ee-single-page-checkout-dv',
1504
+					'layout_strategy' =>
1505
+						new EE_Template_Layout(
1506
+							[
1507
+								'layout_template_file' => SPCO_TEMPLATES_PATH . 'registration_page_wrapper.template.php',
1508
+								'template_args'        => [
1509
+									'empty_cart'              => $empty_cart,
1510
+									'revisit'                 => $this->checkout->revisit,
1511
+									'reg_steps'               => $this->checkout->reg_steps,
1512
+									'next_step'               => $this->checkout->next_step instanceof EE_SPCO_Reg_Step
1513
+										? $this->checkout->next_step->slug()
1514
+										: '',
1515
+									'empty_msg'               => apply_filters(
1516
+										'FHEE__Single_Page_Checkout__display_spco_reg_form__empty_msg',
1517
+										sprintf(
1518
+											esc_html__(
1519
+												'You need to %1$sReturn to Events list%2$sselect at least one event%3$s before you can proceed with the registration process.',
1520
+												'event_espresso'
1521
+											),
1522
+											'<a href="'
1523
+											. get_post_type_archive_link(EspressoPostType::EVENTS)
1524
+											. '" title="',
1525
+											'">',
1526
+											'</a>'
1527
+										)
1528
+									),
1529
+									'cookies_not_set_msg'     => $cookies_not_set_msg,
1530
+									'registration_time_limit' => $this->checkout->get_registration_time_limit(),
1531
+									'use_session_countdown'   => EE_Registry::instance()->CFG->registration->useSessionCountdown(),
1532
+								],
1533
+							]
1534
+						),
1535
+				]
1536
+			);
1537
+			// load template and add to output sent that gets filtered into the_content()
1538
+			EED_Single_Page_Checkout::getResponse()->addOutput($this->checkout->registration_form->get_html());
1539
+		}
1540
+	}
1541
+
1542
+
1543
+	/**
1544
+	 * @param $next_step
1545
+	 * @return void
1546
+	 */
1547
+	public function add_extra_finalize_registration_inputs($next_step)
1548
+	{
1549
+		if ($next_step === 'finalize_registration') {
1550
+			echo '<div id="spco-extra-finalize_registration-inputs-dv"></div>';
1551
+		}
1552
+	}
1553
+
1554
+
1555
+	/**
1556
+	 * @return void
1557
+	 */
1558
+	public static function display_registration_footer()
1559
+	{
1560
+		if (
1561
+			apply_filters(
1562
+				'FHEE__EE_Front__Controller__show_reg_footer',
1563
+				EE_Registry::instance()->CFG->admin->show_reg_footer
1564
+			)
1565
+		) {
1566
+			add_filter(
1567
+				'FHEE__EEH_Template__powered_by_event_espresso__url',
1568
+				function ($url) {
1569
+					return apply_filters('FHEE__EE_Front_Controller__registration_footer__url', $url);
1570
+				}
1571
+			);
1572
+			echo apply_filters(
1573
+				'FHEE__EE_Front_Controller__display_registration_footer',
1574
+				EEH_Template::powered_by_event_espresso(
1575
+					'',
1576
+					'espresso-registration-footer-dv',
1577
+					['utm_content' => 'registration_checkout']
1578
+				)
1579
+			);
1580
+		}
1581
+	}
1582
+
1583
+
1584
+	/**
1585
+	 * @return void
1586
+	 * @throws EE_Error
1587
+	 * @throws ReflectionException
1588
+	 */
1589
+	public function unlock_transaction()
1590
+	{
1591
+		if ($this->checkout->transaction instanceof EE_Transaction) {
1592
+			$this->checkout->transaction->unlock();
1593
+		}
1594
+	}
1595
+
1596
+
1597
+	/**
1598
+	 * @return void
1599
+	 */
1600
+	private function _setup_redirect()
1601
+	{
1602
+		if ($this->checkout->continue_reg && $this->checkout->next_step instanceof EE_SPCO_Reg_Step) {
1603
+			$this->checkout->redirect = true;
1604
+			if (empty($this->checkout->redirect_url)) {
1605
+				$this->checkout->redirect_url = $this->checkout->next_step->reg_step_url();
1606
+			}
1607
+			$this->checkout->redirect_url = apply_filters(
1608
+				'FHEE__EED_Single_Page_Checkout___setup_redirect__checkout_redirect_url',
1609
+				$this->checkout->redirect_url,
1610
+				$this->checkout
1611
+			);
1612
+		}
1613
+	}
1614
+
1615
+
1616
+	/**
1617
+	 * handle ajax message responses and redirects
1618
+	 *
1619
+	 * @return void
1620
+	 * @throws EE_Error
1621
+	 * @throws ReflectionException
1622
+	 */
1623
+	public function go_to_next_step()
1624
+	{
1625
+		if ($this->request->isAjax()) {
1626
+			// capture contents of output buffer we started earlier in the request, and insert into JSON response
1627
+			$this->checkout->json_response->set_unexpected_errors(ob_get_clean());
1628
+		}
1629
+		$this->unlock_transaction();
1630
+		// just return for these conditions
1631
+		if (
1632
+			$this->checkout->admin_request
1633
+			|| $this->checkout->action === 'redirect_form'
1634
+			|| $this->checkout->action === 'update_checkout'
1635
+		) {
1636
+			return;
1637
+		}
1638
+		// AJAX response
1639
+		$this->_handle_json_response();
1640
+		// redirect to next step or the Thank-You page
1641
+		$this->_handle_html_redirects();
1642
+		// hmmm... must be something wrong, so let's just display the form again !
1643
+		$this->_display_spco_reg_form();
1644
+	}
1645
+
1646
+
1647
+	/**
1648
+	 * @return void
1649
+	 * @throws EE_Error
1650
+	 */
1651
+	protected function _handle_json_response()
1652
+	{
1653
+		// if this is an ajax request
1654
+		if ($this->request->isAjax()) {
1655
+			$this->checkout->json_response->set_registration_time_limit(
1656
+				$this->checkout->get_registration_time_limit()
1657
+			);
1658
+			$this->checkout->json_response->set_payment_amount($this->checkout->amount_owing);
1659
+			// just send the ajax (
1660
+			$json_response = apply_filters(
1661
+				'FHEE__EE_Single_Page_Checkout__JSON_response',
1662
+				$this->checkout->json_response
1663
+			);
1664
+			exit($json_response);
1665
+		}
1666
+	}
1667
+
1668
+
1669
+	/**
1670
+	 * @return void
1671
+	 */
1672
+	protected function _handle_html_redirects()
1673
+	{
1674
+		// going somewhere ?
1675
+		if ($this->checkout->redirect && ! empty($this->checkout->redirect_url)) {
1676
+			// store notices in a transient
1677
+			EE_Error::get_notices(false, true);
1678
+			wp_safe_redirect($this->checkout->redirect_url);
1679
+			exit();
1680
+		}
1681
+	}
1682
+
1683
+
1684
+	/**
1685
+	 * @return void
1686
+	 */
1687
+	public function set_checkout_anchor()
1688
+	{
1689
+		echo '<a id="checkout" style="float: left; margin-left: -999em;"></a>';
1690
+	}
1691
+
1692
+
1693
+	/**
1694
+	 * @return string
1695
+	 * @since 4.9.59.p
1696
+	 */
1697
+	public static function getRegistrationExpirationNotice(): string
1698
+	{
1699
+		return sprintf(
1700
+			esc_html__(
1701
+				'%1$sWe\'re sorry, but your registration time has expired.%2$s%3$s%4$sIf you still wish to complete your registration, please return to the %5$sEvent List%6$sEvent List%7$s and reselect your tickets if available. Please accept our apologies for any inconvenience this may have caused.%8$s',
1702
+				'event_espresso'
1703
+			),
1704
+			'<h4 class="important-notice">',
1705
+			'</h4>',
1706
+			'<br />',
1707
+			'<p>',
1708
+			'<a href="' . get_post_type_archive_link(EspressoPostType::EVENTS) . '" title="',
1709
+			'">',
1710
+			'</a>',
1711
+			'</p>'
1712
+		);
1713
+	}
1714 1714
 }
Please login to merge, or discard this patch.
Spacing   +48 added lines, -48 removed lines patch added patch discarded remove patch
@@ -45,7 +45,7 @@  discard block
 block discarded – undo
45 45
 
46 46
     protected ?RequestInterface $request = null;
47 47
 
48
-    private bool $debug = false;    //  true    false
48
+    private bool $debug = false; //  true    false
49 49
 
50 50
 
51 51
     /**
@@ -109,7 +109,7 @@  discard block
 block discarded – undo
109 109
     public static function set_hooks_admin()
110 110
     {
111 111
         EED_Single_Page_Checkout::set_definitions();
112
-        if (! (defined('DOING_AJAX') && DOING_AJAX)) {
112
+        if ( ! (defined('DOING_AJAX') && DOING_AJAX)) {
113 113
             return;
114 114
         }
115 115
         // going to start an output buffer in case anything gets accidentally output
@@ -203,14 +203,14 @@  discard block
 block discarded – undo
203 203
         }
204 204
         define(
205 205
             'SPCO_BASE_PATH',
206
-            rtrim(str_replace(['\\', '/'], '/', plugin_dir_path(__FILE__)), '/') . '/'
206
+            rtrim(str_replace(['\\', '/'], '/', plugin_dir_path(__FILE__)), '/').'/'
207 207
         );
208
-        define('SPCO_CSS_URL', plugin_dir_url(__FILE__) . 'css/');
209
-        define('SPCO_IMG_URL', plugin_dir_url(__FILE__) . 'img/');
210
-        define('SPCO_JS_URL', plugin_dir_url(__FILE__) . 'js/');
211
-        define('SPCO_INC_PATH', SPCO_BASE_PATH . 'inc/');
212
-        define('SPCO_REG_STEPS_PATH', SPCO_BASE_PATH . 'reg_steps/');
213
-        define('SPCO_TEMPLATES_PATH', SPCO_BASE_PATH . 'templates/');
208
+        define('SPCO_CSS_URL', plugin_dir_url(__FILE__).'css/');
209
+        define('SPCO_IMG_URL', plugin_dir_url(__FILE__).'img/');
210
+        define('SPCO_JS_URL', plugin_dir_url(__FILE__).'js/');
211
+        define('SPCO_INC_PATH', SPCO_BASE_PATH.'inc/');
212
+        define('SPCO_REG_STEPS_PATH', SPCO_BASE_PATH.'reg_steps/');
213
+        define('SPCO_TEMPLATES_PATH', SPCO_BASE_PATH.'templates/');
214 214
         EEH_Autoloader::register_autoloaders_for_each_file_in_folder(SPCO_BASE_PATH, true);
215 215
         EE_Registry::$i18n_js_strings['registration_expiration_notice'] =
216 216
             EED_Single_Page_Checkout::getRegistrationExpirationNotice();
@@ -241,7 +241,7 @@  discard block
 block discarded – undo
241 241
             // we need a
242 242
             if (isset($reg_step['file_path'], $reg_step['class_name'], $reg_step['slug'])) {
243 243
                 // copy over to the reg_steps_array
244
-                EED_Single_Page_Checkout::$_reg_steps_array[ $order ] = $reg_step;
244
+                EED_Single_Page_Checkout::$_reg_steps_array[$order] = $reg_step;
245 245
                 // register custom key route for each reg step
246 246
                 // ie: step=>"slug" - this is the entire reason we load the reg steps array now
247 247
                 EE_Config::register_route(
@@ -253,7 +253,7 @@  discard block
 block discarded – undo
253 253
                 // add AJAX or other hooks
254 254
                 if (isset($reg_step['has_hooks']) && $reg_step['has_hooks']) {
255 255
                     // setup autoloaders if necessary
256
-                    if (! class_exists($reg_step['class_name'])) {
256
+                    if ( ! class_exists($reg_step['class_name'])) {
257 257
                         EEH_Autoloader::register_autoloaders_for_each_file_in_folder(
258 258
                             $reg_step['file_path'],
259 259
                             true
@@ -280,19 +280,19 @@  discard block
 block discarded – undo
280 280
         if (empty($reg_steps)) {
281 281
             $reg_steps = [
282 282
                 10  => [
283
-                    'file_path'  => SPCO_REG_STEPS_PATH . 'attendee_information',
283
+                    'file_path'  => SPCO_REG_STEPS_PATH.'attendee_information',
284 284
                     'class_name' => 'EE_SPCO_Reg_Step_Attendee_Information',
285 285
                     'slug'       => 'attendee_information',
286 286
                     'has_hooks'  => false,
287 287
                 ],
288 288
                 30  => [
289
-                    'file_path'  => SPCO_REG_STEPS_PATH . 'payment_options',
289
+                    'file_path'  => SPCO_REG_STEPS_PATH.'payment_options',
290 290
                     'class_name' => 'EE_SPCO_Reg_Step_Payment_Options',
291 291
                     'slug'       => 'payment_options',
292 292
                     'has_hooks'  => true,
293 293
                 ],
294 294
                 999 => [
295
-                    'file_path'  => SPCO_REG_STEPS_PATH . 'finalize_registration',
295
+                    'file_path'  => SPCO_REG_STEPS_PATH.'finalize_registration',
296 296
                     'class_name' => 'EE_SPCO_Reg_Step_Finalize_Registration',
297 297
                     'slug'       => 'finalize_registration',
298 298
                     'has_hooks'  => false,
@@ -438,7 +438,7 @@  discard block
 block discarded – undo
438 438
                 $this->checkout
439 439
             );
440 440
             // load the reg steps array
441
-            if (! $this->_load_and_instantiate_reg_steps()) {
441
+            if ( ! $this->_load_and_instantiate_reg_steps()) {
442 442
                 EED_Single_Page_Checkout::$_initialized = true;
443 443
                 return;
444 444
             }
@@ -447,7 +447,7 @@  discard block
 block discarded – undo
447 447
             // and the next step
448 448
             $this->checkout->set_next_step();
449 449
             // verify that everything has been set up correctly
450
-            if (! ($this->_verify_transaction_and_get_registrations() && $this->_final_verifications())) {
450
+            if ( ! ($this->_verify_transaction_and_get_registrations() && $this->_final_verifications())) {
451 451
                 EED_Single_Page_Checkout::$_initialized = true;
452 452
                 return;
453 453
             }
@@ -463,7 +463,7 @@  discard block
 block discarded – undo
463 463
             // DEBUG LOG
464 464
             // $this->checkout->log( __CLASS__, __FUNCTION__, __LINE__ );
465 465
             // get reg form
466
-            if (! $this->_check_form_submission()) {
466
+            if ( ! $this->_check_form_submission()) {
467 467
                 EED_Single_Page_Checkout::$_initialized = true;
468 468
                 return;
469 469
             }
@@ -493,7 +493,7 @@  discard block
 block discarded – undo
493 493
      */
494 494
     private function _verify_session()
495 495
     {
496
-        if (! EE_Registry::instance()->SSN instanceof EE_Session) {
496
+        if ( ! EE_Registry::instance()->SSN instanceof EE_Session) {
497 497
             throw new EE_Error(esc_html__('The EE_Session class could not be loaded.', 'event_espresso'));
498 498
         }
499 499
         $clear_session_requested = $this->request->getRequestParam('clear_session', false, 'bool');
@@ -510,7 +510,7 @@  discard block
 block discarded – undo
510 510
             // EE_Registry::instance()->SSN->reset_cart();
511 511
             // EE_Registry::instance()->SSN->reset_checkout();
512 512
             // EE_Registry::instance()->SSN->reset_transaction();
513
-            if (! $clear_session_requested) {
513
+            if ( ! $clear_session_requested) {
514 514
                 EE_Error::add_attention(
515 515
                     EE_Registry::$i18n_js_strings['registration_expiration_notice'],
516 516
                     __FILE__,
@@ -536,7 +536,7 @@  discard block
 block discarded – undo
536 536
         /** @type EE_Checkout $checkout */
537 537
         $checkout = EE_Registry::instance()->SSN->checkout();
538 538
         // verify
539
-        if (! $checkout instanceof EE_Checkout) {
539
+        if ( ! $checkout instanceof EE_Checkout) {
540 540
             // instantiate EE_Checkout object for handling the properties of the current checkout process
541 541
             $checkout = EE_Registry::instance()->load_file(
542 542
                 SPCO_INC_PATH,
@@ -554,7 +554,7 @@  discard block
 block discarded – undo
554 554
         }
555 555
         $checkout = apply_filters('FHEE__EED_Single_Page_Checkout___initialize_checkout__checkout', $checkout);
556 556
         // verify again
557
-        if (! $checkout instanceof EE_Checkout) {
557
+        if ( ! $checkout instanceof EE_Checkout) {
558 558
             throw new EE_Error(esc_html__('The EE_Checkout class could not be loaded.', 'event_espresso'));
559 559
         }
560 560
         // reset anything that needs a clean slate for each request
@@ -611,7 +611,7 @@  discard block
 block discarded – undo
611 611
      */
612 612
     protected function _display_request_vars()
613 613
     {
614
-        if (! ($this->debug && defined('WP_DEBUG') && WP_DEBUG)) {
614
+        if ( ! ($this->debug && defined('WP_DEBUG') && WP_DEBUG)) {
615 615
             return;
616 616
         }
617 617
         EEH_Debug_Tools::printr($this->request->requestParams(), 'requestParams', __FILE__, __LINE__);
@@ -683,7 +683,7 @@  discard block
 block discarded – undo
683 683
         ) {
684 684
             // if not, then loop through raw reg steps array
685 685
             foreach (EED_Single_Page_Checkout::$_reg_steps_array as $order => $reg_step) {
686
-                if (! $this->_load_and_instantiate_reg_step($reg_step, $order)) {
686
+                if ( ! $this->_load_and_instantiate_reg_step($reg_step, $order)) {
687 687
                     return false;
688 688
                 }
689 689
             }
@@ -805,12 +805,12 @@  discard block
 block discarded – undo
805 805
     private function _verify_transaction_and_get_registrations(): bool
806 806
     {
807 807
         // was there already a valid transaction in the checkout from the session ?
808
-        if (! $this->checkout->transaction instanceof EE_Transaction) {
808
+        if ( ! $this->checkout->transaction instanceof EE_Transaction) {
809 809
             // get transaction from db or session
810 810
             $this->checkout->transaction = $this->checkout->reg_url_link && ! is_admin()
811 811
                 ? $this->_get_transaction_and_cart_for_previous_visit()
812 812
                 : $this->_get_cart_for_current_session_and_setup_new_transaction();
813
-            if (! $this->checkout->transaction instanceof EE_Transaction) {
813
+            if ( ! $this->checkout->transaction instanceof EE_Transaction) {
814 814
                 EE_Error::add_error(
815 815
                     esc_html__(
816 816
                         'Your Registration and Transaction information could not be retrieved from the db.',
@@ -1035,10 +1035,10 @@  discard block
 block discarded – undo
1035 1035
                     );
1036 1036
                 }
1037 1037
                 $registration = EE_Registry::instance()->BUS->execute($CreateRegistrationCommand);
1038
-                if (! $registration instanceof EE_Registration) {
1038
+                if ( ! $registration instanceof EE_Registration) {
1039 1039
                     throw new InvalidEntityException($registration, 'EE_Registration');
1040 1040
                 }
1041
-                $registrations[ $registration->ID() ] = $registration;
1041
+                $registrations[$registration->ID()] = $registration;
1042 1042
             }
1043 1043
         }
1044 1044
         $registration_processor->fix_reg_final_price_rounding_issue($transaction);
@@ -1078,7 +1078,7 @@  discard block
 block discarded – undo
1078 1078
             $this->checkout
1079 1079
         );
1080 1080
         // verify that current step is still set correctly
1081
-        if (! $this->checkout->current_step instanceof EE_SPCO_Reg_Step) {
1081
+        if ( ! $this->checkout->current_step instanceof EE_SPCO_Reg_Step) {
1082 1082
             EE_Error::add_error(
1083 1083
                 esc_html__(
1084 1084
                     'We\'re sorry but the registration process can not proceed because one or more registration steps were not setup correctly. Please refresh the page and try again or contact support.',
@@ -1091,9 +1091,9 @@  discard block
 block discarded – undo
1091 1091
             return false;
1092 1092
         }
1093 1093
         // if returning to SPCO, then verify that primary registrant is set
1094
-        if (! empty($this->checkout->reg_url_link)) {
1094
+        if ( ! empty($this->checkout->reg_url_link)) {
1095 1095
             $valid_registrant = $this->checkout->transaction->primary_registration();
1096
-            if (! $valid_registrant instanceof EE_Registration) {
1096
+            if ( ! $valid_registrant instanceof EE_Registration) {
1097 1097
                 EE_Error::add_error(
1098 1098
                     esc_html__(
1099 1099
                         'We\'re sorry but there appears to be an error with the "reg_url_link" or the primary registrant for this transaction. Please refresh the page and try again or contact support.',
@@ -1118,7 +1118,7 @@  discard block
 block discarded – undo
1118 1118
                     $valid_registrant = $registration;
1119 1119
                 }
1120 1120
             }
1121
-            if (! $valid_registrant instanceof EE_Registration) {
1121
+            if ( ! $valid_registrant instanceof EE_Registration) {
1122 1122
                 // hmmm... maybe we have the wrong session because the user is opening multiple tabs ?
1123 1123
                 if (EED_Single_Page_Checkout::$_checkout_verified) {
1124 1124
                     // clear the session, mark the checkout as unverified, and try again
@@ -1160,9 +1160,9 @@  discard block
 block discarded – undo
1160 1160
         $this->checkout->set_reg_step_initiated($this->checkout->current_step);
1161 1161
         // loop thru all steps to call their individual "initialize" methods and set i18n strings for JS
1162 1162
         foreach ($this->checkout->reg_steps as $reg_step) {
1163
-            if (! $reg_step->initialize_reg_step()) {
1163
+            if ( ! $reg_step->initialize_reg_step()) {
1164 1164
                 // if not initialized then maybe this step is being removed...
1165
-                if (! $reinitializing && $reg_step->is_current_step()) {
1165
+                if ( ! $reinitializing && $reg_step->is_current_step()) {
1166 1166
                     // if it was the current step, then we need to start over here
1167 1167
                     $this->_initialize_reg_steps(true);
1168 1168
                     return;
@@ -1213,7 +1213,7 @@  discard block
 block discarded – undo
1213 1213
                         )
1214 1214
                     );
1215 1215
                     // validate submitted form data
1216
-                    if (! $this->checkout->continue_reg || ! $this->checkout->current_step->reg_form->is_valid()) {
1216
+                    if ( ! $this->checkout->continue_reg || ! $this->checkout->current_step->reg_form->is_valid()) {
1217 1217
                         // thou shall not pass !!!
1218 1218
                         $this->checkout->continue_reg = false;
1219 1219
                         // any form validation errors?
@@ -1287,7 +1287,7 @@  discard block
 block discarded – undo
1287 1287
                         ) {
1288 1288
                             EE_Error::add_success(
1289 1289
                                 $this->checkout->current_step->success_message()
1290
-                                . '<br />' . $this->checkout->next_step->_instructions()
1290
+                                . '<br />'.$this->checkout->next_step->_instructions()
1291 1291
                             );
1292 1292
                         }
1293 1293
                         // pack it up, pack it in...
@@ -1351,27 +1351,27 @@  discard block
 block discarded – undo
1351 1351
             'An unknown error occurred on the server while attempting to process your request. Please refresh the page and try again or contact support.',
1352 1352
             'event_espresso'
1353 1353
         );
1354
-        EE_Registry::$i18n_js_strings['invalid_json_response']          = esc_html__(
1354
+        EE_Registry::$i18n_js_strings['invalid_json_response'] = esc_html__(
1355 1355
             'An invalid response was returned from the server while attempting to process your request. Please refresh the page and try again or contact support.',
1356 1356
             'event_espresso'
1357 1357
         );
1358
-        EE_Registry::$i18n_js_strings['validation_error']               = esc_html__(
1358
+        EE_Registry::$i18n_js_strings['validation_error'] = esc_html__(
1359 1359
             'There appears to be a problem with the form validation configuration! Please check the admin settings or contact support.',
1360 1360
             'event_espresso'
1361 1361
         );
1362
-        EE_Registry::$i18n_js_strings['invalid_payment_method']         = esc_html__(
1362
+        EE_Registry::$i18n_js_strings['invalid_payment_method'] = esc_html__(
1363 1363
             'There appears to be a problem with the payment method configuration! Please refresh the page and try again or contact support.',
1364 1364
             'event_espresso'
1365 1365
         );
1366
-        EE_Registry::$i18n_js_strings['reg_step_error']                 = esc_html__(
1366
+        EE_Registry::$i18n_js_strings['reg_step_error'] = esc_html__(
1367 1367
             'This registration step could not be completed. Please refresh the page and try again.',
1368 1368
             'event_espresso'
1369 1369
         );
1370
-        EE_Registry::$i18n_js_strings['invalid_coupon']                 = esc_html__(
1370
+        EE_Registry::$i18n_js_strings['invalid_coupon'] = esc_html__(
1371 1371
             'We\'re sorry but that coupon code does not appear to be valid. If this is incorrect, please contact the site administrator.',
1372 1372
             'event_espresso'
1373 1373
         );
1374
-        EE_Registry::$i18n_js_strings['process_registration']           = sprintf(
1374
+        EE_Registry::$i18n_js_strings['process_registration'] = sprintf(
1375 1375
             esc_html__(
1376 1376
                 'Please wait while we process your registration.%sDo not refresh the page or navigate away while this is happening.%sThank you for your patience.',
1377 1377
                 'event_espresso'
@@ -1403,7 +1403,7 @@  discard block
 block discarded – undo
1403 1403
             'FHEE__Single_Page_Checkout__translate_js_strings__ajax_submit',
1404 1404
             true
1405 1405
         );
1406
-        EE_Registry::$i18n_js_strings['session_extension']              = absint(
1406
+        EE_Registry::$i18n_js_strings['session_extension'] = absint(
1407 1407
             apply_filters('FHEE__EE_Session__extend_expiration__seconds_added', 10 * MINUTE_IN_SECONDS)
1408 1408
         );
1409 1409
         EE_Registry::$i18n_js_strings['session_expiration']    = EE_Registry::instance()->SSN->expiration();
@@ -1420,7 +1420,7 @@  discard block
 block discarded – undo
1420 1420
         // load css
1421 1421
         wp_register_style(
1422 1422
             'single_page_checkout',
1423
-            SPCO_CSS_URL . 'single_page_checkout.css',
1423
+            SPCO_CSS_URL.'single_page_checkout.css',
1424 1424
             ['espresso_default'],
1425 1425
             EVENT_ESPRESSO_VERSION
1426 1426
         );
@@ -1428,7 +1428,7 @@  discard block
 block discarded – undo
1428 1428
         // load JS
1429 1429
         wp_register_script(
1430 1430
             'single_page_checkout',
1431
-            SPCO_JS_URL . 'single_page_checkout.js',
1431
+            SPCO_JS_URL.'single_page_checkout.js',
1432 1432
             ['espresso_core', 'underscore', 'ee_form_section_validation'],
1433 1433
             EVENT_ESPRESSO_VERSION,
1434 1434
             true
@@ -1451,7 +1451,7 @@  discard block
 block discarded – undo
1451 1451
          *      AHEE__EED_Single_Page_Checkout__enqueue_styles_and_scripts__attendee_information
1452 1452
          */
1453 1453
         do_action(
1454
-            'AHEE__EED_Single_Page_Checkout__enqueue_styles_and_scripts__' . $this->checkout->current_step->slug(),
1454
+            'AHEE__EED_Single_Page_Checkout__enqueue_styles_and_scripts__'.$this->checkout->current_step->slug(),
1455 1455
             $this
1456 1456
         );
1457 1457
     }
@@ -1504,7 +1504,7 @@  discard block
 block discarded – undo
1504 1504
                     'layout_strategy' =>
1505 1505
                         new EE_Template_Layout(
1506 1506
                             [
1507
-                                'layout_template_file' => SPCO_TEMPLATES_PATH . 'registration_page_wrapper.template.php',
1507
+                                'layout_template_file' => SPCO_TEMPLATES_PATH.'registration_page_wrapper.template.php',
1508 1508
                                 'template_args'        => [
1509 1509
                                     'empty_cart'              => $empty_cart,
1510 1510
                                     'revisit'                 => $this->checkout->revisit,
@@ -1565,7 +1565,7 @@  discard block
 block discarded – undo
1565 1565
         ) {
1566 1566
             add_filter(
1567 1567
                 'FHEE__EEH_Template__powered_by_event_espresso__url',
1568
-                function ($url) {
1568
+                function($url) {
1569 1569
                     return apply_filters('FHEE__EE_Front_Controller__registration_footer__url', $url);
1570 1570
                 }
1571 1571
             );
@@ -1705,7 +1705,7 @@  discard block
 block discarded – undo
1705 1705
             '</h4>',
1706 1706
             '<br />',
1707 1707
             '<p>',
1708
-            '<a href="' . get_post_type_archive_link(EspressoPostType::EVENTS) . '" title="',
1708
+            '<a href="'.get_post_type_archive_link(EspressoPostType::EVENTS).'" title="',
1709 1709
             '">',
1710 1710
             '</a>',
1711 1711
             '</p>'
Please login to merge, or discard this patch.