Completed
Branch BUG-11107-submit-button-text (d7f554)
by
unknown
43:53 queued 32:22
created
core/services/notifications/PersistentAdminNoticeManager.php 2 patches
Spacing   +8 added lines, -8 removed lines patch added patch discarded remove patch
@@ -86,7 +86,7 @@  discard block
 block discarded – undo
86 86
      */
87 87
     private function setReturnUrl($return_url)
88 88
     {
89
-        if (! is_string($return_url)) {
89
+        if ( ! is_string($return_url)) {
90 90
             throw new InvalidDataTypeException('$return_url', $return_url, 'string');
91 91
         }
92 92
         $this->return_url = $return_url;
@@ -103,7 +103,7 @@  discard block
 block discarded – undo
103 103
      */
104 104
     protected function getPersistentAdminNoticeCollection()
105 105
     {
106
-        if (! $this->notice_collection instanceof Collection) {
106
+        if ( ! $this->notice_collection instanceof Collection) {
107 107
             $this->notice_collection = new Collection(
108 108
                 'EventEspresso\core\domain\entities\notifications\PersistentAdminNotice'
109 109
             );
@@ -127,7 +127,7 @@  discard block
 block discarded – undo
127 127
     {
128 128
         $persistent_admin_notices = get_option(PersistentAdminNoticeManager::WP_OPTION_KEY, array());
129 129
         // \EEH_Debug_Tools::printr($persistent_admin_notices, '$persistent_admin_notices', __FILE__, __LINE__);
130
-        if (! empty($persistent_admin_notices)) {
130
+        if ( ! empty($persistent_admin_notices)) {
131 131
             foreach ($persistent_admin_notices as $name => $details) {
132 132
                 if (is_array($details)) {
133 133
                     if (
@@ -166,7 +166,7 @@  discard block
 block discarded – undo
166 166
                         $this->notice_collection->add(
167 167
                             new PersistentAdminNotice(
168 168
                                 $name,
169
-                                (string)$details,
169
+                                (string) $details,
170 170
                                 false,
171 171
                                 '',
172 172
                                 '',
@@ -244,14 +244,14 @@  discard block
 block discarded – undo
244 244
     {
245 245
         wp_register_script(
246 246
             'espresso_core',
247
-            EE_GLOBAL_ASSETS_URL . 'scripts/espresso_core.js',
247
+            EE_GLOBAL_ASSETS_URL.'scripts/espresso_core.js',
248 248
             array('jquery'),
249 249
             EVENT_ESPRESSO_VERSION,
250 250
             true
251 251
         );
252 252
         wp_register_script(
253 253
             'ee_error_js',
254
-            EE_GLOBAL_ASSETS_URL . 'scripts/EE_Error.js',
254
+            EE_GLOBAL_ASSETS_URL.'scripts/EE_Error.js',
255 255
             array('espresso_core'),
256 256
             EVENT_ESPRESSO_VERSION,
257 257
             true
@@ -283,7 +283,7 @@  discard block
 block discarded – undo
283 283
         // used in template
284 284
         $persistent_admin_notice_name    = $persistent_admin_notice->getName();
285 285
         $persistent_admin_notice_message = $persistent_admin_notice->getMessage();
286
-        require EE_TEMPLATES . DS . 'notifications' . DS . 'persistent_admin_notice.template.php';
286
+        require EE_TEMPLATES.DS.'notifications'.DS.'persistent_admin_notice.template.php';
287 287
     }
288 288
 
289 289
 
@@ -304,7 +304,7 @@  discard block
 block discarded – undo
304 304
     {
305 305
         $pan_name                = $this->request->get('ee_nag_notice', $pan_name);
306 306
         $this->notice_collection = $this->getPersistentAdminNoticeCollection();
307
-        if (! empty($pan_name) && $this->notice_collection->has($pan_name)) {
307
+        if ( ! empty($pan_name) && $this->notice_collection->has($pan_name)) {
308 308
             /** @var PersistentAdminNotice $persistent_admin_notice */
309 309
             $persistent_admin_notice = $this->notice_collection->get($pan_name);
310 310
             $persistent_admin_notice->setDismissed(true);
Please login to merge, or discard this patch.
Indentation   +360 added lines, -360 removed lines patch added patch discarded remove patch
@@ -30,366 +30,366 @@
 block discarded – undo
30 30
 class PersistentAdminNoticeManager
31 31
 {
32 32
 
33
-    const WP_OPTION_KEY = 'ee_pers_admin_notices';
34
-
35
-    /**
36
-     * @var Collection|PersistentAdminNotice[] $notice_collection
37
-     */
38
-    private $notice_collection;
39
-
40
-    /**
41
-     * if AJAX is not enabled, then the return URL will be used for redirecting back to the admin page where the
42
-     * persistent admin notice was displayed, and ultimately dismissed from.
43
-     *
44
-     * @type string $return_url
45
-     */
46
-    private $return_url;
47
-
48
-    /**
49
-     * @type CapabilitiesChecker $capabilities_checker
50
-     */
51
-    private $capabilities_checker;
52
-
53
-    /**
54
-     * @type EE_Request $request
55
-     */
56
-    private $request;
57
-
58
-
59
-
60
-    /**
61
-     * CapChecker constructor
62
-     *
63
-     * @param string              $return_url  where to  redirect to after dismissing notices
64
-     * @param CapabilitiesChecker $capabilities_checker
65
-     * @param EE_Request          $request
66
-     * @throws InvalidDataTypeException
67
-     */
68
-    public function __construct($return_url = '', CapabilitiesChecker $capabilities_checker, EE_Request $request)
69
-    {
70
-        $this->setReturnUrl($return_url);
71
-        $this->capabilities_checker = $capabilities_checker;
72
-        $this->request              = $request;
73
-        // setup up notices at priority 9 because `EE_Admin::display_admin_notices()` runs at priority 10,
74
-        // and we want to retrieve and generate any nag notices at the last possible moment
75
-        add_action('admin_notices', array($this, 'displayNotices'), 9);
76
-        add_action('network_admin_notices', array($this, 'displayNotices'), 9);
77
-        add_action('wp_ajax_dismiss_ee_nag_notice', array($this, 'dismissNotice'));
78
-        add_action('shutdown', array($this, 'registerAndSaveNotices'), 998);
79
-    }
80
-
81
-
82
-
83
-    /**
84
-     * @param string $return_url
85
-     * @throws InvalidDataTypeException
86
-     */
87
-    private function setReturnUrl($return_url)
88
-    {
89
-        if (! is_string($return_url)) {
90
-            throw new InvalidDataTypeException('$return_url', $return_url, 'string');
91
-        }
92
-        $this->return_url = $return_url;
93
-    }
94
-
95
-
96
-
97
-    /**
98
-     * @return Collection
99
-     * @throws InvalidEntityException
100
-     * @throws InvalidInterfaceException
101
-     * @throws InvalidDataTypeException
102
-     * @throws DomainException
103
-     */
104
-    protected function getPersistentAdminNoticeCollection()
105
-    {
106
-        if (! $this->notice_collection instanceof Collection) {
107
-            $this->notice_collection = new Collection(
108
-                'EventEspresso\core\domain\entities\notifications\PersistentAdminNotice'
109
-            );
110
-            $this->retrieveStoredNotices();
111
-            $this->registerNotices();
112
-        }
113
-        return $this->notice_collection;
114
-    }
115
-
116
-
117
-
118
-    /**
119
-     * generates PersistentAdminNotice objects for all non-dismissed notices saved to the db
120
-     *
121
-     * @return void
122
-     * @throws InvalidEntityException
123
-     * @throws DomainException
124
-     * @throws InvalidDataTypeException
125
-     */
126
-    protected function retrieveStoredNotices()
127
-    {
128
-        $persistent_admin_notices = get_option(PersistentAdminNoticeManager::WP_OPTION_KEY, array());
129
-        // \EEH_Debug_Tools::printr($persistent_admin_notices, '$persistent_admin_notices', __FILE__, __LINE__);
130
-        if (! empty($persistent_admin_notices)) {
131
-            foreach ($persistent_admin_notices as $name => $details) {
132
-                if (is_array($details)) {
133
-                    if (
134
-                        ! isset(
135
-                            $details['message'],
136
-                            $details['capability'],
137
-                            $details['cap_context'],
138
-                            $details['dismissed']
139
-                        )
140
-                    ) {
141
-                        throw new DomainException(
142
-                            sprintf(
143
-                                esc_html__(
144
-                                    'The "%1$s" PersistentAdminNotice could not be retrieved from the database.',
145
-                                    'event_espresso'
146
-                                ),
147
-                                $name
148
-                            )
149
-                        );
150
-                    }
151
-                    // new format for nag notices
152
-                    $this->notice_collection->add(
153
-                        new PersistentAdminNotice(
154
-                            $name,
155
-                            $details['message'],
156
-                            false,
157
-                            $details['capability'],
158
-                            $details['cap_context'],
159
-                            $details['dismissed']
160
-                        ),
161
-                        $name
162
-                    );
163
-                } else {
164
-                    try {
165
-                        // old nag notices, that we want to convert to the new format
166
-                        $this->notice_collection->add(
167
-                            new PersistentAdminNotice(
168
-                                $name,
169
-                                (string)$details,
170
-                                false,
171
-                                '',
172
-                                '',
173
-                                empty($details)
174
-                            ),
175
-                            $name
176
-                        );
177
-                    } catch (Exception $e) {
178
-                        EE_Error::add_error($e->getMessage(), __FILE__, __FUNCTION__, __LINE__);
179
-                    }
180
-                }
181
-                // each notice will self register when the action hook in registerNotices is triggered
182
-            }
183
-        }
184
-    }
185
-
186
-
187
-
188
-    /**
189
-     * exposes the Persistent Admin Notice Collection via an action
190
-     * so that PersistentAdminNotice objects can be added and/or removed
191
-     * without compromising the actual collection like a filter would
192
-     */
193
-    protected function registerNotices()
194
-    {
195
-        do_action(
196
-            'AHEE__EventEspresso_core_services_notifications_PersistentAdminNoticeManager__registerNotices',
197
-            $this->notice_collection
198
-        );
199
-    }
200
-
201
-
202
-
203
-    /**
204
-     * @throws DomainException
205
-     * @throws InvalidClassException
206
-     * @throws InvalidDataTypeException
207
-     * @throws InvalidInterfaceException
208
-     * @throws InvalidEntityException
209
-     */
210
-    public function displayNotices()
211
-    {
212
-        $this->notice_collection = $this->getPersistentAdminNoticeCollection();
213
-        if ($this->notice_collection->hasObjects()) {
214
-            $enqueue_assets = false;
215
-            // and display notices
216
-            foreach ($this->notice_collection as $persistent_admin_notice) {
217
-                /** @var PersistentAdminNotice $persistent_admin_notice */
218
-                // don't display notices that have already been dismissed
219
-                if ($persistent_admin_notice->getDismissed()) {
220
-                    continue;
221
-                }
222
-                try {
223
-                    $this->capabilities_checker->processCapCheck(
224
-                        $persistent_admin_notice->getCapCheck()
225
-                    );
226
-                } catch (InsufficientPermissionsException $e) {
227
-                    // user does not have required cap, so skip to next notice
228
-                    // and just eat the exception - nom nom nom nom
229
-                    continue;
230
-                }
231
-                if ($persistent_admin_notice->getMessage() === '') {
232
-                    continue;
233
-                }
234
-                $this->displayPersistentAdminNotice($persistent_admin_notice);
235
-                $enqueue_assets = true;
236
-            }
237
-            if ($enqueue_assets) {
238
-                $this->enqueueAssets();
239
-            }
240
-        }
241
-    }
242
-
243
-
244
-
245
-    /**
246
-     * does what it's named
247
-     *
248
-     * @return void
249
-     */
250
-    public function enqueueAssets()
251
-    {
252
-        wp_register_script(
253
-            'espresso_core',
254
-            EE_GLOBAL_ASSETS_URL . 'scripts/espresso_core.js',
255
-            array('jquery'),
256
-            EVENT_ESPRESSO_VERSION,
257
-            true
258
-        );
259
-        wp_register_script(
260
-            'ee_error_js',
261
-            EE_GLOBAL_ASSETS_URL . 'scripts/EE_Error.js',
262
-            array('espresso_core'),
263
-            EVENT_ESPRESSO_VERSION,
264
-            true
265
-        );
266
-        wp_localize_script(
267
-            'ee_error_js',
268
-            'ee_dismiss',
269
-            array(
270
-                'return_url'    => urlencode($this->return_url),
271
-                'ajax_url'      => WP_AJAX_URL,
272
-                'unknown_error' => esc_html__(
273
-                    'An unknown error has occurred on the server while attempting to dismiss this notice.',
274
-                    'event_espresso'
275
-                ),
276
-            )
277
-        );
278
-        wp_enqueue_script('ee_error_js');
279
-    }
280
-
281
-
282
-
283
-    /**
284
-     * displayPersistentAdminNoticeHtml
285
-     *
286
-     * @param  PersistentAdminNotice $persistent_admin_notice
287
-     */
288
-    protected function displayPersistentAdminNotice(PersistentAdminNotice $persistent_admin_notice)
289
-    {
290
-        // used in template
291
-        $persistent_admin_notice_name    = $persistent_admin_notice->getName();
292
-        $persistent_admin_notice_message = $persistent_admin_notice->getMessage();
293
-        require EE_TEMPLATES . DS . 'notifications' . DS . 'persistent_admin_notice.template.php';
294
-    }
295
-
296
-
297
-
298
-    /**
299
-     * dismissNotice
300
-     *
301
-     * @param string $pan_name the name, or key of the Persistent Admin Notice to be dismissed
302
-     * @param bool   $purge    if true, then delete it from the db
303
-     * @param bool   $return   forget all of this AJAX or redirect nonsense, and just return
304
-     * @return void
305
-     * @throws InvalidEntityException
306
-     * @throws InvalidInterfaceException
307
-     * @throws InvalidDataTypeException
308
-     * @throws DomainException
309
-     */
310
-    public function dismissNotice($pan_name = '', $purge = false, $return = false)
311
-    {
312
-        $pan_name                = $this->request->get('ee_nag_notice', $pan_name);
313
-        $this->notice_collection = $this->getPersistentAdminNoticeCollection();
314
-        if (! empty($pan_name) && $this->notice_collection->has($pan_name)) {
315
-            /** @var PersistentAdminNotice $persistent_admin_notice */
316
-            $persistent_admin_notice = $this->notice_collection->get($pan_name);
317
-            $persistent_admin_notice->setDismissed(true);
318
-            $persistent_admin_notice->setPurge($purge);
319
-            $this->saveNotices();
320
-        }
321
-        if ($return) {
322
-            return;
323
-        }
324
-        if ($this->request->ajax) {
325
-            // grab any notices and concatenate into string
326
-            echo wp_json_encode(
327
-                array(
328
-                    'errors' => implode('<br />', EE_Error::get_notices(false)),
329
-                )
330
-            );
331
-            exit();
332
-        }
333
-        // save errors to a transient to be displayed on next request (after redirect)
334
-        EE_Error::get_notices(false, true);
335
-        wp_safe_redirect(
336
-            urldecode(
337
-                $this->request->get('return_url', '')
338
-            )
339
-        );
340
-    }
341
-
342
-
343
-
344
-    /**
345
-     * saveNotices
346
-     *
347
-     * @throws DomainException
348
-     * @throws InvalidDataTypeException
349
-     * @throws InvalidInterfaceException
350
-     * @throws InvalidEntityException
351
-     */
352
-    public function saveNotices()
353
-    {
354
-        $this->notice_collection = $this->getPersistentAdminNoticeCollection();
355
-        if ($this->notice_collection->hasObjects()) {
356
-            $persistent_admin_notices = get_option(PersistentAdminNoticeManager::WP_OPTION_KEY, array());
357
-            //maybe initialize persistent_admin_notices
358
-            if (empty($persistent_admin_notices)) {
359
-                add_option(PersistentAdminNoticeManager::WP_OPTION_KEY, array(), '', 'no');
360
-            }
361
-            foreach ($this->notice_collection as $persistent_admin_notice) {
362
-                // are we deleting this notice ?
363
-                if ($persistent_admin_notice->getPurge()) {
364
-                    unset($persistent_admin_notices[$persistent_admin_notice->getName()]);
365
-                } else {
366
-                    /** @var PersistentAdminNotice $persistent_admin_notice */
367
-                    $persistent_admin_notices[$persistent_admin_notice->getName()] = array(
368
-                        'message'     => $persistent_admin_notice->getMessage(),
369
-                        'capability'  => $persistent_admin_notice->getCapability(),
370
-                        'cap_context' => $persistent_admin_notice->getCapContext(),
371
-                        'dismissed'   => $persistent_admin_notice->getDismissed(),
372
-                    );
373
-                }
374
-            }
375
-            update_option(PersistentAdminNoticeManager::WP_OPTION_KEY, $persistent_admin_notices);
376
-        }
377
-    }
378
-
379
-
380
-
381
-    /**
382
-     * @throws DomainException
383
-     * @throws InvalidDataTypeException
384
-     * @throws InvalidEntityException
385
-     * @throws InvalidInterfaceException
386
-     */
387
-    public function registerAndSaveNotices()
388
-    {
389
-        $this->getPersistentAdminNoticeCollection();
390
-        $this->registerNotices();
391
-        $this->saveNotices();
392
-    }
33
+	const WP_OPTION_KEY = 'ee_pers_admin_notices';
34
+
35
+	/**
36
+	 * @var Collection|PersistentAdminNotice[] $notice_collection
37
+	 */
38
+	private $notice_collection;
39
+
40
+	/**
41
+	 * if AJAX is not enabled, then the return URL will be used for redirecting back to the admin page where the
42
+	 * persistent admin notice was displayed, and ultimately dismissed from.
43
+	 *
44
+	 * @type string $return_url
45
+	 */
46
+	private $return_url;
47
+
48
+	/**
49
+	 * @type CapabilitiesChecker $capabilities_checker
50
+	 */
51
+	private $capabilities_checker;
52
+
53
+	/**
54
+	 * @type EE_Request $request
55
+	 */
56
+	private $request;
57
+
58
+
59
+
60
+	/**
61
+	 * CapChecker constructor
62
+	 *
63
+	 * @param string              $return_url  where to  redirect to after dismissing notices
64
+	 * @param CapabilitiesChecker $capabilities_checker
65
+	 * @param EE_Request          $request
66
+	 * @throws InvalidDataTypeException
67
+	 */
68
+	public function __construct($return_url = '', CapabilitiesChecker $capabilities_checker, EE_Request $request)
69
+	{
70
+		$this->setReturnUrl($return_url);
71
+		$this->capabilities_checker = $capabilities_checker;
72
+		$this->request              = $request;
73
+		// setup up notices at priority 9 because `EE_Admin::display_admin_notices()` runs at priority 10,
74
+		// and we want to retrieve and generate any nag notices at the last possible moment
75
+		add_action('admin_notices', array($this, 'displayNotices'), 9);
76
+		add_action('network_admin_notices', array($this, 'displayNotices'), 9);
77
+		add_action('wp_ajax_dismiss_ee_nag_notice', array($this, 'dismissNotice'));
78
+		add_action('shutdown', array($this, 'registerAndSaveNotices'), 998);
79
+	}
80
+
81
+
82
+
83
+	/**
84
+	 * @param string $return_url
85
+	 * @throws InvalidDataTypeException
86
+	 */
87
+	private function setReturnUrl($return_url)
88
+	{
89
+		if (! is_string($return_url)) {
90
+			throw new InvalidDataTypeException('$return_url', $return_url, 'string');
91
+		}
92
+		$this->return_url = $return_url;
93
+	}
94
+
95
+
96
+
97
+	/**
98
+	 * @return Collection
99
+	 * @throws InvalidEntityException
100
+	 * @throws InvalidInterfaceException
101
+	 * @throws InvalidDataTypeException
102
+	 * @throws DomainException
103
+	 */
104
+	protected function getPersistentAdminNoticeCollection()
105
+	{
106
+		if (! $this->notice_collection instanceof Collection) {
107
+			$this->notice_collection = new Collection(
108
+				'EventEspresso\core\domain\entities\notifications\PersistentAdminNotice'
109
+			);
110
+			$this->retrieveStoredNotices();
111
+			$this->registerNotices();
112
+		}
113
+		return $this->notice_collection;
114
+	}
115
+
116
+
117
+
118
+	/**
119
+	 * generates PersistentAdminNotice objects for all non-dismissed notices saved to the db
120
+	 *
121
+	 * @return void
122
+	 * @throws InvalidEntityException
123
+	 * @throws DomainException
124
+	 * @throws InvalidDataTypeException
125
+	 */
126
+	protected function retrieveStoredNotices()
127
+	{
128
+		$persistent_admin_notices = get_option(PersistentAdminNoticeManager::WP_OPTION_KEY, array());
129
+		// \EEH_Debug_Tools::printr($persistent_admin_notices, '$persistent_admin_notices', __FILE__, __LINE__);
130
+		if (! empty($persistent_admin_notices)) {
131
+			foreach ($persistent_admin_notices as $name => $details) {
132
+				if (is_array($details)) {
133
+					if (
134
+						! isset(
135
+							$details['message'],
136
+							$details['capability'],
137
+							$details['cap_context'],
138
+							$details['dismissed']
139
+						)
140
+					) {
141
+						throw new DomainException(
142
+							sprintf(
143
+								esc_html__(
144
+									'The "%1$s" PersistentAdminNotice could not be retrieved from the database.',
145
+									'event_espresso'
146
+								),
147
+								$name
148
+							)
149
+						);
150
+					}
151
+					// new format for nag notices
152
+					$this->notice_collection->add(
153
+						new PersistentAdminNotice(
154
+							$name,
155
+							$details['message'],
156
+							false,
157
+							$details['capability'],
158
+							$details['cap_context'],
159
+							$details['dismissed']
160
+						),
161
+						$name
162
+					);
163
+				} else {
164
+					try {
165
+						// old nag notices, that we want to convert to the new format
166
+						$this->notice_collection->add(
167
+							new PersistentAdminNotice(
168
+								$name,
169
+								(string)$details,
170
+								false,
171
+								'',
172
+								'',
173
+								empty($details)
174
+							),
175
+							$name
176
+						);
177
+					} catch (Exception $e) {
178
+						EE_Error::add_error($e->getMessage(), __FILE__, __FUNCTION__, __LINE__);
179
+					}
180
+				}
181
+				// each notice will self register when the action hook in registerNotices is triggered
182
+			}
183
+		}
184
+	}
185
+
186
+
187
+
188
+	/**
189
+	 * exposes the Persistent Admin Notice Collection via an action
190
+	 * so that PersistentAdminNotice objects can be added and/or removed
191
+	 * without compromising the actual collection like a filter would
192
+	 */
193
+	protected function registerNotices()
194
+	{
195
+		do_action(
196
+			'AHEE__EventEspresso_core_services_notifications_PersistentAdminNoticeManager__registerNotices',
197
+			$this->notice_collection
198
+		);
199
+	}
200
+
201
+
202
+
203
+	/**
204
+	 * @throws DomainException
205
+	 * @throws InvalidClassException
206
+	 * @throws InvalidDataTypeException
207
+	 * @throws InvalidInterfaceException
208
+	 * @throws InvalidEntityException
209
+	 */
210
+	public function displayNotices()
211
+	{
212
+		$this->notice_collection = $this->getPersistentAdminNoticeCollection();
213
+		if ($this->notice_collection->hasObjects()) {
214
+			$enqueue_assets = false;
215
+			// and display notices
216
+			foreach ($this->notice_collection as $persistent_admin_notice) {
217
+				/** @var PersistentAdminNotice $persistent_admin_notice */
218
+				// don't display notices that have already been dismissed
219
+				if ($persistent_admin_notice->getDismissed()) {
220
+					continue;
221
+				}
222
+				try {
223
+					$this->capabilities_checker->processCapCheck(
224
+						$persistent_admin_notice->getCapCheck()
225
+					);
226
+				} catch (InsufficientPermissionsException $e) {
227
+					// user does not have required cap, so skip to next notice
228
+					// and just eat the exception - nom nom nom nom
229
+					continue;
230
+				}
231
+				if ($persistent_admin_notice->getMessage() === '') {
232
+					continue;
233
+				}
234
+				$this->displayPersistentAdminNotice($persistent_admin_notice);
235
+				$enqueue_assets = true;
236
+			}
237
+			if ($enqueue_assets) {
238
+				$this->enqueueAssets();
239
+			}
240
+		}
241
+	}
242
+
243
+
244
+
245
+	/**
246
+	 * does what it's named
247
+	 *
248
+	 * @return void
249
+	 */
250
+	public function enqueueAssets()
251
+	{
252
+		wp_register_script(
253
+			'espresso_core',
254
+			EE_GLOBAL_ASSETS_URL . 'scripts/espresso_core.js',
255
+			array('jquery'),
256
+			EVENT_ESPRESSO_VERSION,
257
+			true
258
+		);
259
+		wp_register_script(
260
+			'ee_error_js',
261
+			EE_GLOBAL_ASSETS_URL . 'scripts/EE_Error.js',
262
+			array('espresso_core'),
263
+			EVENT_ESPRESSO_VERSION,
264
+			true
265
+		);
266
+		wp_localize_script(
267
+			'ee_error_js',
268
+			'ee_dismiss',
269
+			array(
270
+				'return_url'    => urlencode($this->return_url),
271
+				'ajax_url'      => WP_AJAX_URL,
272
+				'unknown_error' => esc_html__(
273
+					'An unknown error has occurred on the server while attempting to dismiss this notice.',
274
+					'event_espresso'
275
+				),
276
+			)
277
+		);
278
+		wp_enqueue_script('ee_error_js');
279
+	}
280
+
281
+
282
+
283
+	/**
284
+	 * displayPersistentAdminNoticeHtml
285
+	 *
286
+	 * @param  PersistentAdminNotice $persistent_admin_notice
287
+	 */
288
+	protected function displayPersistentAdminNotice(PersistentAdminNotice $persistent_admin_notice)
289
+	{
290
+		// used in template
291
+		$persistent_admin_notice_name    = $persistent_admin_notice->getName();
292
+		$persistent_admin_notice_message = $persistent_admin_notice->getMessage();
293
+		require EE_TEMPLATES . DS . 'notifications' . DS . 'persistent_admin_notice.template.php';
294
+	}
295
+
296
+
297
+
298
+	/**
299
+	 * dismissNotice
300
+	 *
301
+	 * @param string $pan_name the name, or key of the Persistent Admin Notice to be dismissed
302
+	 * @param bool   $purge    if true, then delete it from the db
303
+	 * @param bool   $return   forget all of this AJAX or redirect nonsense, and just return
304
+	 * @return void
305
+	 * @throws InvalidEntityException
306
+	 * @throws InvalidInterfaceException
307
+	 * @throws InvalidDataTypeException
308
+	 * @throws DomainException
309
+	 */
310
+	public function dismissNotice($pan_name = '', $purge = false, $return = false)
311
+	{
312
+		$pan_name                = $this->request->get('ee_nag_notice', $pan_name);
313
+		$this->notice_collection = $this->getPersistentAdminNoticeCollection();
314
+		if (! empty($pan_name) && $this->notice_collection->has($pan_name)) {
315
+			/** @var PersistentAdminNotice $persistent_admin_notice */
316
+			$persistent_admin_notice = $this->notice_collection->get($pan_name);
317
+			$persistent_admin_notice->setDismissed(true);
318
+			$persistent_admin_notice->setPurge($purge);
319
+			$this->saveNotices();
320
+		}
321
+		if ($return) {
322
+			return;
323
+		}
324
+		if ($this->request->ajax) {
325
+			// grab any notices and concatenate into string
326
+			echo wp_json_encode(
327
+				array(
328
+					'errors' => implode('<br />', EE_Error::get_notices(false)),
329
+				)
330
+			);
331
+			exit();
332
+		}
333
+		// save errors to a transient to be displayed on next request (after redirect)
334
+		EE_Error::get_notices(false, true);
335
+		wp_safe_redirect(
336
+			urldecode(
337
+				$this->request->get('return_url', '')
338
+			)
339
+		);
340
+	}
341
+
342
+
343
+
344
+	/**
345
+	 * saveNotices
346
+	 *
347
+	 * @throws DomainException
348
+	 * @throws InvalidDataTypeException
349
+	 * @throws InvalidInterfaceException
350
+	 * @throws InvalidEntityException
351
+	 */
352
+	public function saveNotices()
353
+	{
354
+		$this->notice_collection = $this->getPersistentAdminNoticeCollection();
355
+		if ($this->notice_collection->hasObjects()) {
356
+			$persistent_admin_notices = get_option(PersistentAdminNoticeManager::WP_OPTION_KEY, array());
357
+			//maybe initialize persistent_admin_notices
358
+			if (empty($persistent_admin_notices)) {
359
+				add_option(PersistentAdminNoticeManager::WP_OPTION_KEY, array(), '', 'no');
360
+			}
361
+			foreach ($this->notice_collection as $persistent_admin_notice) {
362
+				// are we deleting this notice ?
363
+				if ($persistent_admin_notice->getPurge()) {
364
+					unset($persistent_admin_notices[$persistent_admin_notice->getName()]);
365
+				} else {
366
+					/** @var PersistentAdminNotice $persistent_admin_notice */
367
+					$persistent_admin_notices[$persistent_admin_notice->getName()] = array(
368
+						'message'     => $persistent_admin_notice->getMessage(),
369
+						'capability'  => $persistent_admin_notice->getCapability(),
370
+						'cap_context' => $persistent_admin_notice->getCapContext(),
371
+						'dismissed'   => $persistent_admin_notice->getDismissed(),
372
+					);
373
+				}
374
+			}
375
+			update_option(PersistentAdminNoticeManager::WP_OPTION_KEY, $persistent_admin_notices);
376
+		}
377
+	}
378
+
379
+
380
+
381
+	/**
382
+	 * @throws DomainException
383
+	 * @throws InvalidDataTypeException
384
+	 * @throws InvalidEntityException
385
+	 * @throws InvalidInterfaceException
386
+	 */
387
+	public function registerAndSaveNotices()
388
+	{
389
+		$this->getPersistentAdminNoticeCollection();
390
+		$this->registerNotices();
391
+		$this->saveNotices();
392
+	}
393 393
 
394 394
 
395 395
 }
Please login to merge, or discard this patch.
core/admin/EE_Admin_Page_Loader.core.php 4 patches
Unused Use Statements   -1 removed lines patch added patch discarded remove patch
@@ -1,6 +1,5 @@
 block discarded – undo
1 1
 <?php
2 2
 use EventEspresso\core\domain\entities\notifications\PersistentAdminNotice;
3
-use EventEspresso\core\exceptions\InvalidDataTypeException;
4 3
 
5 4
 if (!defined('EVENT_ESPRESSO_VERSION') )
6 5
 	exit('NO direct script access allowed');
Please login to merge, or discard this patch.
Indentation   +743 added lines, -743 removed lines patch added patch discarded remove patch
@@ -28,749 +28,749 @@
 block discarded – undo
28 28
 class EE_Admin_Page_Loader
29 29
 {
30 30
 
31
-    /**
32
-     * _installed_pages
33
-     * objects for page_init objects detected and loaded
34
-     *
35
-     * @access private
36
-     * @var \EE_Admin_Page_Init[]
37
-     */
38
-    private $_installed_pages = array();
39
-
40
-
41
-
42
-    /**
43
-     * this is used to hold the registry of menu slugs for all the installed admin pages
44
-     *
45
-     * @var array
46
-     */
47
-    private $_menu_slugs = array();
48
-
49
-
50
-    /**
51
-     * _caffeinated_extends
52
-     * This array is the generated configuration array for which core EE_Admin pages are extended (and the bits and
53
-     * pieces needed to do so).  This property is defined in the _set_caffeinated method.
54
-     *
55
-     * @var array
56
-     */
57
-    private $_caffeinated_extends = array();
58
-
59
-
60
-
61
-    /**
62
-     * _current_caf_extend_slug
63
-     * This property is used for holding the page slug that is required for referencing the correct
64
-     * _caffeinated_extends index when the corresponding core child EE_Admin_Page_init hooks are executed.
65
-     *
66
-     * @var array
67
-     */
68
-    private $_current_caf_extend_slug;
69
-
70
-
71
-
72
-    /**
73
-     * _caf_autoloader
74
-     * This property is used for holding an array of folder names of any NEW EE_Admin_Pages found in the
75
-     * caffeinated/new directory.  This array is then used to setup a corresponding dynamic autoloader for these pages
76
-     * classes.
77
-     *
78
-     * @var array
79
-     */
80
-    //	private $_caf_autoloader = array();
81
-    /**
82
-     * _prepped_menu_maps
83
-     * This is the prepared array of EE_Admin_Page_Menu_Maps for adding to the admin_menu.
84
-     *
85
-     * @since  4.4.0
86
-     * @var EE_Admin_Page_Menu_Map[]
87
-     */
88
-    private $_prepped_menu_maps = array();
89
-
90
-
91
-
92
-    /**
93
-     * _admin_menu_groups
94
-     * array that holds the group headings and details for
95
-     *
96
-     * @access private
97
-     * @var array
98
-     */
99
-    private $_admin_menu_groups = array();
100
-
101
-
102
-
103
-    /**
104
-     * This property will hold the hook file for setting up the filter that does all the connections between admin
105
-     * pages.
106
-     *
107
-     * @var string
108
-     */
109
-    public $hook_file;
110
-
111
-
112
-
113
-    /**
114
-     * constructor
115
-     *
116
-     * @access public
117
-     * @return \EE_Admin_Page_Loader
118
-     */
119
-    public function __construct()
120
-    {
121
-        //load menu_map classes
122
-        EE_Registry::instance()->load_file(EE_ADMIN, 'EE_Admin_Page_Menu_Map', 'core');
123
-        //define the default "groups" for the admin_pages
124
-        $this->_set_menu_groups();
125
-        //let's set default autoloaders.  Note that this just sets autoloaders for root admin files.
126
-        //		spl_autoload_register( array( $this, 'init_autoloaders') );
127
-        //let's do a scan and see what installed pages we have
128
-        $this->_get_installed_pages();
129
-        //set menus (has to be done on every load - we're not actually loading the page just setting the menus and where they point to).
130
-        add_action('admin_menu', array($this, 'set_menus'));
131
-        add_action('network_admin_menu', array($this, 'set_network_menus'));
132
-    }
133
-
134
-
135
-
136
-    /**
137
-     * When caffeinated system is detected, this method is called to setup the caffeinated directory constants used by
138
-     * files in the caffeinated folder.
139
-     *
140
-     * @access private
141
-     * @return void
142
-     */
143
-    private function _define_caffeinated_constants()
144
-    {
145
-        if (! defined('EE_CORE_CAF_ADMIN')) {
146
-            define('EE_CORE_CAF_ADMIN', EE_PLUGIN_DIR_PATH . 'caffeinated/admin/');
147
-            define('EE_CORE_CAF_ADMIN_URL', EE_PLUGIN_DIR_URL . 'caffeinated/admin/');
148
-            define('EE_CORE_CAF_ADMIN_NEW', EE_CORE_CAF_ADMIN . 'new/');
149
-            define('EE_CORE_CAF_ADMIN_EXTEND', EE_CORE_CAF_ADMIN . 'extend/');
150
-            define('EE_CORE_CAF_ADMIN_EXTEND_URL', EE_CORE_CAF_ADMIN_URL . 'extend/');
151
-            define('EE_CORE_CAF_ADMIN_HOOKS', EE_CORE_CAF_ADMIN . 'hooks/');
152
-        }
153
-    }
154
-
155
-
156
-
157
-    /**
158
-     * _set_menu_groups
159
-     * sets the filterable _admin_menu_groups property (list of various "groupings" within the EE admin menu array)
160
-     *
161
-     * @access private
162
-     * @return void
163
-     */
164
-    private function _set_menu_groups()
165
-    {
166
-
167
-        //set array of EE_Admin_Page_Menu_Group objects
168
-        $groups = array(
169
-            'main'       => new EE_Admin_Page_Menu_Group(
170
-                array(
171
-                    'menu_label'   => __('Main', 'event_espresso'),
172
-                    'show_on_menu' => EE_Admin_Page_Menu_Map::NONE,
173
-                    'menu_slug'    => 'main',
174
-                    'capability'   => 'ee_read_ee',
175
-                    'menu_order'   => 0,
176
-                    'parent_slug'  => 'espresso_events',
177
-                )
178
-            ),
179
-            'management' => new EE_Admin_Page_Menu_Group(
180
-                array(
181
-                    'menu_label'   => __('Management', 'event_espresso'),
182
-                    'show_on_menu' => EE_Admin_Page_Menu_Map::BLOG_ADMIN_ONLY,
183
-                    'menu_slug'    => 'management',
184
-                    'capability'   => 'ee_read_ee',
185
-                    'menu_order'   => 10,
186
-                    'parent_slug'  => 'espresso_events',
187
-                )
188
-            ),
189
-            'settings'   => new EE_Admin_Page_Menu_Group(
190
-                array(
191
-                    'menu_label'   => __('Settings', 'event_espresso'),
192
-                    'show_on_menu' => EE_Admin_Page_Menu_Map::BLOG_ADMIN_ONLY,
193
-                    'menu_slug'    => 'settings',
194
-                    'capability'   => 'ee_read_ee',
195
-                    'menu_order'   => 30,
196
-                    'parent_slug'  => 'espresso_events',
197
-                )
198
-            ),
199
-            'templates'  => new EE_Admin_Page_Menu_Group(
200
-                array(
201
-                    'menu_label'   => __('Templates', 'event_espresso'),
202
-                    'show_on_menu' => EE_Admin_Page_Menu_Map::BLOG_ADMIN_ONLY,
203
-                    'menu_slug'    => 'templates',
204
-                    'capability'   => 'ee_read_ee',
205
-                    'menu_order'   => 40,
206
-                    'parent_slug'  => 'espresso_events',
207
-                )
208
-            ),
209
-            'extras'     => new EE_Admin_Page_Menu_Group(
210
-                array(
211
-                    'menu_label'              => __('Extras', 'event_espresso'),
212
-                    'show_on_menu'            => EE_Admin_Page_Menu_Map::BLOG_AND_NETWORK_ADMIN,
213
-                    'menu_slug'               => 'extras',
214
-                    'capability'              => 'ee_read_ee',
215
-                    'menu_order'              => 50,
216
-                    'parent_slug'             => 'espresso_events',
217
-                    'maintenance_mode_parent' => 'espresso_maintenance_settings',
218
-                )
219
-            ),
220
-            'tools'      => new EE_Admin_Page_Menu_Group(
221
-                array(
222
-                    'menu_label'   => __("Tools", "event_espresso"),
223
-                    'show_on_menu' => EE_Admin_Page_Menu_Map::BLOG_ADMIN_ONLY,
224
-                    'menu_slug'    => 'tools',
225
-                    'capability'   => 'ee_read_ee',
226
-                    'menu_order'   => 60,
227
-                    'parent_slug'  => 'espresso_events',
228
-                )
229
-            ),
230
-            'addons'     => new EE_Admin_Page_Menu_Group(
231
-                array(
232
-                    'show_on_menu' => EE_Admin_Page_Menu_Map::BLOG_AND_NETWORK_ADMIN,
233
-                    'menu_label'   => __('Add-ons', 'event_espresso'),
234
-                    'menu_slug'    => 'addons',
235
-                    'capability'   => 'ee_read_ee',
236
-                    'menu_order'   => 20,
237
-                    'parent_slug'  => 'espresso_events',
238
-                )
239
-            ),
240
-        );
241
-        $this->_admin_menu_groups = apply_filters(
242
-            'FHEE__EE_Admin_Page_Loader___set_menu_groups__admin_menu_groups',
243
-            $groups
244
-        );
245
-    }
246
-
247
-
248
-
249
-    /**
250
-     * This takes all the groups in the _admin_menu_groups array and returns the array indexed by group
251
-     * slug.  The other utility with this function is it validates that all the groups are instances of
252
-     * EE_Admin_Page_Menu_Group (cause some invalid things might have slipped in via addons).
253
-     *
254
-     * @since  4.4.0
255
-     * @throws \EE_Error
256
-     * @return EE_Admin_Page_Menu_Group[]
257
-     */
258
-    private function _rearrange_menu_groups()
259
-    {
260
-        $groups = array();
261
-        //first let's order the menu groups by their internal menu order (note usort type hinting to ensure the incoming array is EE_Admin_Page_Menu_Map objects )
262
-        usort($this->_admin_menu_groups, array($this, '_sort_menu_maps'));
263
-        foreach ($this->_admin_menu_groups as $group) {
264
-            if (! $group instanceof EE_Admin_Page_Menu_Group) {
265
-                throw new EE_Error(
266
-                    sprintf(
267
-                        __(
268
-                            'Unable to continue sorting the menu groups array because there is an invalid value for the menu groups.  All values in this array are required to be a EE_Admin_Page_Menu_Group object.  Instead there was: %s',
269
-                            'event_espresso'
270
-                        ),
271
-                        print_r($group, true)
272
-                    )
273
-                );
274
-            }
275
-            $groups[$group->menu_slug] = $group;
276
-        }
277
-        return $groups;
278
-    }
279
-
280
-
281
-
282
-    /**
283
-     * _get_installed_pages
284
-     * This just gets the list of installed EE_Admin_pages.
285
-     *
286
-     * @access private
287
-     * @throws EE_Error
288
-     * @return void
289
-     */
290
-    private function _get_installed_pages()
291
-    {
292
-        $installed_refs = array();
293
-        $exclude        = array('assets', 'templates');
294
-        // grab everything in the  admin core directory
295
-        $admin_screens = glob(EE_ADMIN_PAGES . '*', GLOB_ONLYDIR);
296
-        if ($admin_screens) {
297
-            foreach ($admin_screens as $admin_screen) {
298
-                // files and anything in the exclude array need not apply
299
-                if (is_dir($admin_screen) && ! in_array(basename($admin_screen), $exclude)) {
300
-                    // these folders represent the different EE admin pages
301
-                    $installed_refs[basename($admin_screen)] = $admin_screen;
302
-                }
303
-            }
304
-        }
305
-        if (empty($installed_refs)) {
306
-            $error_msg[] = __(
307
-                'There are no EE_Admin pages detected, it looks like EE did not install properly',
308
-                'event_espresso'
309
-            );
310
-            $error_msg[] = $error_msg[0] . "\r\n" . sprintf(
311
-                    __(
312
-                        'Check that the %s folder exists and is writable. Maybe try deactivating, then reactivating Event Espresso again.',
313
-                        'event_espresso'
314
-                    ),
315
-                    EE_ADMIN_PAGES
316
-                );
317
-            throw new EE_Error(implode('||', $error_msg));
318
-        }
319
-        //this just checks the caffeinated folder and takes care of setting up any caffeinated stuff.
320
-        $installed_refs = $this->_set_caffeinated($installed_refs);
321
-        //allow plugins to add in their own pages (note at this point they will need to have an autoloader defined for their class) OR hook into EEH_Autoloader::load_admin_page() to add their path.;
322
-        $installed_refs             = apply_filters(
323
-            'FHEE__EE_Admin_Page_Loader___get_installed_pages__installed_refs',
324
-            $installed_refs
325
-        );
326
-        $this->_caffeinated_extends = apply_filters(
327
-            'FHEE__EE_Admin_Page_Loader___get_installed_pages__caffeinated_extends',
328
-            $this->_caffeinated_extends
329
-        );
330
-        //loop through admin pages and setup the $_installed_pages array.
331
-        $hooks_ref = array();
332
-        foreach ($installed_refs as $page => $path) {
333
-            // set autoloaders for our admin page classes based on included path information
334
-            EEH_Autoloader::instance()->register_autoloaders_for_each_file_in_folder($path);
335
-            // build list of installed pages
336
-            $this->_installed_pages[$page] = $this->_load_admin_page($page, $path);
337
-            // verify returned object
338
-            if ($this->_installed_pages[$page] instanceof EE_Admin_Page_Init) {
339
-                if (! $this->_installed_pages[$page]->get_menu_map() instanceof EE_Admin_Page_Menu_Map) {
340
-                    continue;
341
-                }
342
-                //skip if in full maintenance mode and maintenance_mode_parent is set
343
-                $maintenance_mode_parent = $this->_installed_pages[$page]->get_menu_map()->maintenance_mode_parent;
344
-                if (empty($maintenance_mode_parent)
345
-                    && EE_Maintenance_Mode::instance()->level()
346
-                       == EE_Maintenance_Mode::level_2_complete_maintenance) {
347
-                    unset($installed_refs[$page]);
348
-                    continue;
349
-                }
350
-                $this->_menu_slugs[$this->_installed_pages[$page]->get_menu_map()->menu_slug] = $page;
351
-                //flag for register hooks on extended pages b/c extended pages use the default INIT.
352
-                $extend = false;
353
-                //now that we've got the admin_init objects... lets see if there are any caffeinated pages extending the originals.  If there are then let's hook into the init admin filter and load our extend instead.
354
-                if (isset($this->_caffeinated_extends[$page])) {
355
-                    $this->_current_caf_extend_slug = $page;
356
-                    $path_hook                      = 'FHEE__EE_Admin_Page_Init___initialize_admin_page__path_to_file__'
357
-                                                      . $this->_installed_pages[$page]->get_menu_map()->menu_slug
358
-                                                      . '_'
359
-                                                      . $this->_installed_pages[$page]->get_admin_page_name();
360
-                    $path_runtime                   = 'return "'
361
-                                                      . $this->_caffeinated_extends[$this->_current_caf_extend_slug]["path"]
362
-                                                      . '";';
363
-                    $page_hook                      = 'FHEE__EE_Admin_Page_Init___initialize_admin_page__admin_page__'
364
-                                                      . $this->_installed_pages[$page]->get_menu_map()->menu_slug
365
-                                                      . '_'
366
-                                                      . $this->_installed_pages[$page]->get_admin_page_name();
367
-                    $page_runtime                   = 'return "'
368
-                                                      . $this->_caffeinated_extends[$this->_current_caf_extend_slug]["admin_page"]
369
-                                                      . '";';
370
-                    $hook_function_path = create_function('$path_to_file', $path_runtime);
371
-                    $hook_function_page = create_function('$admin_page', $page_runtime);
372
-                    add_filter($path_hook, $hook_function_path);
373
-                    add_filter($page_hook, $hook_function_page);
374
-                    $extend = true;
375
-                }
376
-                //let's do the registered hooks
377
-                $extended_hooks = $this->_installed_pages[$page]->register_hooks($extend);
378
-                $hooks_ref      = array_merge($hooks_ref, $extended_hooks);
379
-            }
380
-        }
381
-        //the hooks_ref is all the pages where we have $extended _Hooks files that will extend a class in a different folder.  So we want to make sure we load the file for the parent.
382
-        //first make sure we've got unique values
383
-        $hooks_ref = array_unique($hooks_ref);
384
-        //now let's loop and require!
385
-        foreach ($hooks_ref as $path) {
386
-            require_once($path);
387
-        }
388
-        //make sure we have menu slugs global setup. Used in EE_Admin_Page->page_setup() to ensure we don't do a full class load for an admin page that isn't requested.
389
-        global $ee_menu_slugs;
390
-        $ee_menu_slugs = $this->_menu_slugs;
391
-        //we need to loop again to run any early code
392
-        foreach ($installed_refs as $page => $path) {
393
-            if ($this->_installed_pages[$page] instanceof EE_Admin_Page_Init) {
394
-                $this->_installed_pages[$page]->do_initial_loads();
395
-            }
396
-        }
397
-        do_action('AHEE__EE_Admin_Page_Loader___get_installed_pages_loaded', $this->_installed_pages);
398
-    }
399
-
400
-
401
-
402
-    /**
403
-     * get_admin_page_object
404
-     *
405
-     * @param string $page_slug
406
-     * @return EE_Admin_Page
407
-     */
408
-    public function get_admin_page_object($page_slug = '')
409
-    {
410
-        if (isset($this->_installed_pages[$page_slug])) {
411
-            return $this->_installed_pages[$page_slug]->loaded_page_object();
412
-        }
413
-        return null;
414
-    }
415
-
416
-
417
-
418
-    /**
419
-     * _get_classname_for_admin_page
420
-     * generates an "Admin Page" class based on the directory  name
421
-     *
422
-     * @param $dir_name
423
-     * @return string
424
-     */
425
-    private function _get_classname_for_admin_page($dir_name = '')
426
-    {
427
-        $class_name = str_replace('_', ' ', strtolower($dir_name));
428
-        return str_replace(' ', '_', ucwords($class_name)) . '_Admin_Page';
429
-    }
430
-
431
-
432
-
433
-    /**
434
-     * _get_classname_for_admin_init_page
435
-     * generates an "Admin Page Init" class based on the directory  name
436
-     *
437
-     * @param $dir_name
438
-     * @return string
439
-     */
440
-    private function _get_classname_for_admin_init_page($dir_name = '')
441
-    {
442
-        $class_name = str_replace('_', ' ', strtolower($dir_name));
443
-        return str_replace(' ', '_', ucwords($class_name)) . '_Admin_Page_Init';
444
-    }
445
-
446
-
447
-
448
-    /**
449
-     * _load_admin_page
450
-     * Loads and instantiates page_init object for a single EE_admin page.
451
-     *
452
-     * @param  string $page page_reference
453
-     * @param string  $path
454
-     * @throws EE_Error
455
-     * @return object|bool  return page object if valid, bool false if not.
456
-     */
457
-    private function _load_admin_page($page = '', $path = '')
458
-    {
459
-        $class_name = $this->_get_classname_for_admin_init_page($page);
460
-        EE_Registry::instance()->load_file($path, $class_name, 'core');
461
-        if (! class_exists($class_name)) {
462
-            $inner_error_msg = '<br />' . sprintf(
463
-                    esc_html__(
464
-                        'Make sure you have %1$s defined. If this is a non-EE-core admin page then you also must have an autoloader in place for your class',
465
-                        'event_espresso'
466
-                    ),
467
-                    '<strong>' . $class_name . '</strong>'
468
-                );
469
-            $error_msg[]     = sprintf(
470
-                __('Something went wrong with loading the %s admin page.', 'event_espresso'),
471
-                $page
472
-            );
473
-            $error_msg[]     = $error_msg[0]
474
-                               . "\r\n"
475
-                               . sprintf(
476
-                                   esc_html__(
477
-                                       'There is no Init class in place for the %s admin page.',
478
-                                       'event_espresso'
479
-                                   ),
480
-                                   $page
481
-                               )
482
-                               . $inner_error_msg;
483
-            throw new EE_Error(implode('||', $error_msg));
484
-        }
485
-        $a = new ReflectionClass($class_name);
486
-        return $a->newInstance();
487
-    }
488
-
489
-
490
-
491
-    /**
492
-     * set_menus
493
-     * This method sets up the menus for EE Admin Pages
494
-     *
495
-     * @access private
496
-     * @return void
497
-     */
498
-    public function set_menus()
499
-    {
500
-        //prep the menu pages (sort, group.)
501
-        $this->_prep_pages();
502
-        foreach ($this->_prepped_menu_maps as $menu_map) {
503
-            if (EE_Registry::instance()->CAP->current_user_can($menu_map->capability, $menu_map->menu_slug)) {
504
-                $menu_map->add_menu_page(false);
505
-            }
506
-        }
507
-    }
508
-
509
-
510
-    /**
511
-     * set_network_menus
512
-     * This method sets up the menus for network EE Admin Pages.
513
-     * Almost identical to EE_Admin_Page_Loader::set_menus() except pages
514
-     * are only added to the menu map if they are intended for the admin menu
515
-     *
516
-     * @return void
517
-     */
518
-    public function set_network_menus()
519
-    {
520
-        $this->_prep_pages();
521
-        foreach ($this->_prepped_menu_maps as $menu_map) {
522
-            if (EE_Registry::instance()->CAP->current_user_can($menu_map->capability, $menu_map->menu_slug)) {
523
-                $menu_map->add_menu_page(true);
524
-            }
525
-        }
526
-    }
527
-
528
-
529
-
530
-    /**
531
-     * _prep_pages
532
-     * sets the _prepped_menu_maps property
533
-     *
534
-     * @access private
535
-     * @throws EE_Error
536
-     * @return void
537
-     */
538
-    private function _prep_pages()
539
-    {
540
-        $pages_array = array();
541
-        //rearrange _admin_menu_groups to be indexed by group slug.
542
-        $menu_groups = $this->_rearrange_menu_groups();
543
-        foreach ($this->_installed_pages as $page) {
544
-            if ($page instanceof EE_Admin_page_Init) {
545
-                $page_map = $page->get_menu_map();
546
-                //if we've got an array then the menu map is in the old format so let's throw a persistent notice that the admin system isn't setup correctly for this item.
547
-                if (is_array($page_map) || empty($page_map)) {
548
-                      new PersistentAdminNotice(
549
-                        'menu_map_warning_' . str_replace(' ', '_', $page->label) . '_' . EVENT_ESPRESSO_VERSION,
550
-                        sprintf(
551
-                            __(
552
-                                'The admin page for %s was not correctly setup because it is using an older method for integrating with Event Espresso Core.  This means that full functionality for this component is not available.  This error message usually appears with an Add-on that is out of date.  Make sure you update all your Event Espresso 4 add-ons to the latest version to ensure they have necessary compatibility updates in place.',
553
-                                'event_espresso'
554
-                            ),
555
-                            $page->label
556
-                        )
557
-                    );
558
-                    continue;
559
-                }
560
-                //if page map is NOT a EE_Admin_Page_Menu_Map object then throw error.
561
-                if (! $page_map instanceof EE_Admin_Page_Menu_Map) {
562
-                    throw new EE_Error(
563
-                        sprintf(
564
-                            __(
565
-                                'The menu map for %s must be an EE_Admin_Page_Menu_Map object.  Instead it is %s.  Please double check that the menu map has been configured correctly.',
566
-                                'event_espresso'
567
-                            ),
568
-                            $page->label,
569
-                            $page_map
570
-                        )
571
-                    );
572
-                }
573
-                //use the maintenance_mode_parent property and maintenance mode status to determine if this page even gets added to array.
574
-                if (empty($page_map->maintenance_mode_parent)
575
-                    && EE_Maintenance_Mode::instance()->level()
576
-                       == EE_Maintenance_Mode::level_2_complete_maintenance) {
577
-                    continue;
578
-                }
579
-                //assign to group (remember $page_map has the admin page stored in it).
580
-                $pages_array[$page_map->menu_group][] = $page_map;
581
-            }
582
-        }
583
-        if (empty($pages_array)) {
584
-            throw new EE_Error(__('Something went wrong when prepping the admin pages', 'event_espresso'));
585
-        }
586
-        //let's sort the groups, make sure it's a valid group, add header (if to show).
587
-        foreach ($pages_array as $group => $menu_maps) {
588
-            //valid_group?
589
-            if (! array_key_exists($group, $menu_groups)) {
590
-                continue;
591
-            }
592
-            //sort pages.
593
-            usort($menu_maps, array($this, '_sort_menu_maps'));
594
-            //prepend header
595
-            array_unshift($menu_maps, $menu_groups[$group]);
596
-            //reset $pages_array with prepped data
597
-            $pages_array[$group] = $menu_maps;
598
-        }
599
-        //now let's setup the _prepped_menu_maps property
600
-        foreach ($menu_groups as $group => $group_objs) {
601
-            if (isset($pages_array[$group])) {
602
-                $this->_prepped_menu_maps = array_merge($this->_prepped_menu_maps, $pages_array[$group]);
603
-            }
604
-        }/**/
605
-    }
606
-
607
-
608
-    /**
609
-     * This method is the "workhorse" for detecting and setting up caffeinated functionality.
610
-     * In this method there are three checks being done:
611
-     * 1. Do we have any NEW admin page sets.  If we do, lets add them into the menu setup (via the $installed_refs
612
-     * array) etc.  (new page sets are found in caffeinated/new/{page})
613
-     * 2. Do we have any EXTENDED page sets.  Basically an extended EE_Admin Page extends the core {child}_Admin_Page
614
-     * class.  eg. would be caffeinated/extend/events/Extend_Events_Admin_Page.core.php and in there would be a class:
615
-     * Extend_Events_Admin_Page extends Events_Admin_Page.
616
-     * 3. Do we have any files just for setting up hooks into other core pages.  The files can be any name in
617
-     * "caffeinated/hooks" EXCEPT they need a ".class.php" extension and the file name must correspond with the
618
-     * classname inside.  These classes are instantiated really early so that any hooks in them are run before the
619
-     * corresponding apply_filters/do_actions that are found in any future loaded EE_Admin pages (INCLUDING caffeinated
620
-     * admin_pages)
621
-     *
622
-     * @param array $installed_refs the original installed_refs array that may contain our NEW EE_Admin_Pages to be
623
-     *                              loaded.
624
-     * @return array
625
-     */
626
-    private function _set_caffeinated($installed_refs)
627
-    {
628
-
629
-        //first let's check if there IS a caffeinated folder. If there is not then lets get out.
630
-        if (! is_dir(EE_PLUGIN_DIR_PATH . 'caffeinated' . DS . 'admin') || (defined('EE_DECAF') && EE_DECAF)) {
631
-            return $installed_refs;
632
-        }
633
-        $this->_define_caffeinated_constants();
634
-        $exclude = array('tickets');
635
-        //okay let's setup an "New" pages first (we'll return installed refs later)
636
-        $new_admin_screens = glob(EE_CORE_CAF_ADMIN . 'new/*', GLOB_ONLYDIR);
637
-        if ($new_admin_screens) {
638
-            foreach ($new_admin_screens as $admin_screen) {
639
-                // files and anything in the exclude array need not apply
640
-                if (is_dir($admin_screen) && ! in_array(basename($admin_screen), $exclude)) {
641
-                    // these folders represent the different NEW EE admin pages
642
-                    $installed_refs[basename($admin_screen)] = $admin_screen;
643
-                    // set autoloaders for our admin page classes based on included path information
644
-                    EEH_Autoloader::instance()->register_autoloaders_for_each_file_in_folder($admin_screen);
645
-                    //					$this->_caf_autoloader[] = array(
646
-                    //						'dir' => 'new',
647
-                    //						'folder' => basename( $admin_screen )
648
-                    //					);
649
-                }
650
-            }
651
-        }
652
-        //let's see if there are any EXTENDS to setup in the $_caffeinated_extends array (that will be used later for hooking into the _initialize_admin_age in the related core_init admin page)
653
-        $extends = glob(EE_CORE_CAF_ADMIN . 'extend/*', GLOB_ONLYDIR);
654
-        if ($extends) {
655
-            foreach ($extends as $extend) {
656
-                if (is_dir($extend)) {
657
-                    $extend_ref = basename($extend);
658
-                    //now let's make sure there is a file that matches the expected format
659
-                    $filename                                              = str_replace(
660
-                        ' ',
661
-                        '_',
662
-                        ucwords(
663
-                            str_replace(
664
-                                '_',
665
-                                ' ',
666
-                                $extend_ref
667
-                            )
668
-                        )
669
-                    );
670
-                    $filename                                              = 'Extend_' . $filename . '_Admin_Page';
671
-                    $this->_caffeinated_extends[$extend_ref]['path']       = str_replace(
672
-                        array('\\', '/'),
673
-                        DS,
674
-                        EE_CORE_CAF_ADMIN
675
-                        . 'extend'
676
-                        . DS
677
-                        . $extend_ref
678
-                        . DS
679
-                        . $filename
680
-                        . '.core.php'
681
-                    );
682
-                    $this->_caffeinated_extends[$extend_ref]['admin_page'] = $filename;
683
-                    // set autoloaders for our admin page classes based on included path information
684
-                    EEH_Autoloader::instance()->register_autoloaders_for_each_file_in_folder($extend);
685
-                    //					$this->_caf_autoloader[] = array(
686
-                    //						'dir' => 'extend',
687
-                    //						'folder' => $extend_ref
688
-                    //					);
689
-                }
690
-            }
691
-        }
692
-        //let's see if there are any HOOK files and instantiate them if there are (so that hooks are loaded early!).
693
-        $ee_admin_hooks = array();
694
-        $hooks          = glob(EE_CORE_CAF_ADMIN . 'hooks/*.class.php');
695
-        if ($hooks) {
696
-            foreach ($hooks as $hook) {
697
-                if (is_readable($hook)) {
698
-                    require_once $hook;
699
-                    $classname = str_replace(EE_CORE_CAF_ADMIN . 'hooks/', '', $hook);
700
-                    $classname = str_replace('.class.php', '', $classname);
701
-                    if (class_exists($classname)) {
702
-                        $a                = new ReflectionClass($classname);
703
-                        $ee_admin_hooks[] = $a->newInstance();
704
-                    }
705
-                }
706
-            }
707
-        }/**/
708
-        $ee_admin_hooks = apply_filters('FHEE__EE_Admin_Page_Loader__set_caffeinated__ee_admin_hooks', $ee_admin_hooks);
709
-        return $installed_refs;
710
-    }
711
-
712
-
713
-
714
-
715
-
716
-    /**
717
-     * Initial autoloader registration
718
-     * This just sets up the autoloader for the root admin files
719
-     *
720
-     * @param  string $className incoming classname to check for autoload
721
-     * @return void
722
-     */
723
-    //	public function init_autoloaders( $className ) {
724
-    //		$dir_ref = array(
725
-    //			EE_ADMIN => array('core', 'class')
726
-    //		);
727
-    //		EEH_Autoloader::try_autoload($dir_ref, $className );
728
-    //	}
729
-    /**
730
-     * This method takes care of setting up the autoloader dynamically for any NEW EE_Admin pages found in the
731
-     * caffeinated folders.
732
-     *
733
-     * @access public
734
-     * @param  string $className in coming classname being called
735
-     * @return void
736
-     */
737
-    //	public function caffeinated_autoloaders( $className ) {
738
-    //		//let's setup an array of paths to check (for each subsystem)
739
-    //		$dir_ref = array();
740
-    //		foreach ( $this->_caf_autoloader as $pathinfo) {
741
-    //			$dir_ref[ EE_CORE_CAF_ADMIN . $pathinfo['dir'] . DS . $pathinfo['folder'] . DS] = array('core', 'class');
742
-    //		}
743
-    //
744
-    //		EEH_Autoloader::try_autoload($dir_ref, $className );
745
-    //	}
746
-    /**
747
-     * Utility method for sorting the _menu_maps (callback for usort php function)
748
-     *
749
-     * @since  4.4.0
750
-     * @param  EE_Admin_Page_Menu_Map $a menu_map object
751
-     * @param  EE_Admin_Page_Menu_Map $b being compared to
752
-     * @return int    sort order
753
-     */
754
-    private function _sort_menu_maps(EE_Admin_Page_Menu_Map $a, EE_Admin_Page_Menu_Map $b)
755
-    {
756
-        if ($a->menu_order == $b->menu_order) {
757
-            return 0;
758
-        }
759
-        return ($a->menu_order < $b->menu_order) ? -1 : 1;
760
-    }
761
-
762
-
763
-
764
-    /**
765
-     * _default_header_link
766
-     * This is just a dummy method to use with header submenu items
767
-     *
768
-     * @return bool false
769
-     */
770
-    public function _default_header_link()
771
-    {
772
-        return false;
773
-    }
31
+	/**
32
+	 * _installed_pages
33
+	 * objects for page_init objects detected and loaded
34
+	 *
35
+	 * @access private
36
+	 * @var \EE_Admin_Page_Init[]
37
+	 */
38
+	private $_installed_pages = array();
39
+
40
+
41
+
42
+	/**
43
+	 * this is used to hold the registry of menu slugs for all the installed admin pages
44
+	 *
45
+	 * @var array
46
+	 */
47
+	private $_menu_slugs = array();
48
+
49
+
50
+	/**
51
+	 * _caffeinated_extends
52
+	 * This array is the generated configuration array for which core EE_Admin pages are extended (and the bits and
53
+	 * pieces needed to do so).  This property is defined in the _set_caffeinated method.
54
+	 *
55
+	 * @var array
56
+	 */
57
+	private $_caffeinated_extends = array();
58
+
59
+
60
+
61
+	/**
62
+	 * _current_caf_extend_slug
63
+	 * This property is used for holding the page slug that is required for referencing the correct
64
+	 * _caffeinated_extends index when the corresponding core child EE_Admin_Page_init hooks are executed.
65
+	 *
66
+	 * @var array
67
+	 */
68
+	private $_current_caf_extend_slug;
69
+
70
+
71
+
72
+	/**
73
+	 * _caf_autoloader
74
+	 * This property is used for holding an array of folder names of any NEW EE_Admin_Pages found in the
75
+	 * caffeinated/new directory.  This array is then used to setup a corresponding dynamic autoloader for these pages
76
+	 * classes.
77
+	 *
78
+	 * @var array
79
+	 */
80
+	//	private $_caf_autoloader = array();
81
+	/**
82
+	 * _prepped_menu_maps
83
+	 * This is the prepared array of EE_Admin_Page_Menu_Maps for adding to the admin_menu.
84
+	 *
85
+	 * @since  4.4.0
86
+	 * @var EE_Admin_Page_Menu_Map[]
87
+	 */
88
+	private $_prepped_menu_maps = array();
89
+
90
+
91
+
92
+	/**
93
+	 * _admin_menu_groups
94
+	 * array that holds the group headings and details for
95
+	 *
96
+	 * @access private
97
+	 * @var array
98
+	 */
99
+	private $_admin_menu_groups = array();
100
+
101
+
102
+
103
+	/**
104
+	 * This property will hold the hook file for setting up the filter that does all the connections between admin
105
+	 * pages.
106
+	 *
107
+	 * @var string
108
+	 */
109
+	public $hook_file;
110
+
111
+
112
+
113
+	/**
114
+	 * constructor
115
+	 *
116
+	 * @access public
117
+	 * @return \EE_Admin_Page_Loader
118
+	 */
119
+	public function __construct()
120
+	{
121
+		//load menu_map classes
122
+		EE_Registry::instance()->load_file(EE_ADMIN, 'EE_Admin_Page_Menu_Map', 'core');
123
+		//define the default "groups" for the admin_pages
124
+		$this->_set_menu_groups();
125
+		//let's set default autoloaders.  Note that this just sets autoloaders for root admin files.
126
+		//		spl_autoload_register( array( $this, 'init_autoloaders') );
127
+		//let's do a scan and see what installed pages we have
128
+		$this->_get_installed_pages();
129
+		//set menus (has to be done on every load - we're not actually loading the page just setting the menus and where they point to).
130
+		add_action('admin_menu', array($this, 'set_menus'));
131
+		add_action('network_admin_menu', array($this, 'set_network_menus'));
132
+	}
133
+
134
+
135
+
136
+	/**
137
+	 * When caffeinated system is detected, this method is called to setup the caffeinated directory constants used by
138
+	 * files in the caffeinated folder.
139
+	 *
140
+	 * @access private
141
+	 * @return void
142
+	 */
143
+	private function _define_caffeinated_constants()
144
+	{
145
+		if (! defined('EE_CORE_CAF_ADMIN')) {
146
+			define('EE_CORE_CAF_ADMIN', EE_PLUGIN_DIR_PATH . 'caffeinated/admin/');
147
+			define('EE_CORE_CAF_ADMIN_URL', EE_PLUGIN_DIR_URL . 'caffeinated/admin/');
148
+			define('EE_CORE_CAF_ADMIN_NEW', EE_CORE_CAF_ADMIN . 'new/');
149
+			define('EE_CORE_CAF_ADMIN_EXTEND', EE_CORE_CAF_ADMIN . 'extend/');
150
+			define('EE_CORE_CAF_ADMIN_EXTEND_URL', EE_CORE_CAF_ADMIN_URL . 'extend/');
151
+			define('EE_CORE_CAF_ADMIN_HOOKS', EE_CORE_CAF_ADMIN . 'hooks/');
152
+		}
153
+	}
154
+
155
+
156
+
157
+	/**
158
+	 * _set_menu_groups
159
+	 * sets the filterable _admin_menu_groups property (list of various "groupings" within the EE admin menu array)
160
+	 *
161
+	 * @access private
162
+	 * @return void
163
+	 */
164
+	private function _set_menu_groups()
165
+	{
166
+
167
+		//set array of EE_Admin_Page_Menu_Group objects
168
+		$groups = array(
169
+			'main'       => new EE_Admin_Page_Menu_Group(
170
+				array(
171
+					'menu_label'   => __('Main', 'event_espresso'),
172
+					'show_on_menu' => EE_Admin_Page_Menu_Map::NONE,
173
+					'menu_slug'    => 'main',
174
+					'capability'   => 'ee_read_ee',
175
+					'menu_order'   => 0,
176
+					'parent_slug'  => 'espresso_events',
177
+				)
178
+			),
179
+			'management' => new EE_Admin_Page_Menu_Group(
180
+				array(
181
+					'menu_label'   => __('Management', 'event_espresso'),
182
+					'show_on_menu' => EE_Admin_Page_Menu_Map::BLOG_ADMIN_ONLY,
183
+					'menu_slug'    => 'management',
184
+					'capability'   => 'ee_read_ee',
185
+					'menu_order'   => 10,
186
+					'parent_slug'  => 'espresso_events',
187
+				)
188
+			),
189
+			'settings'   => new EE_Admin_Page_Menu_Group(
190
+				array(
191
+					'menu_label'   => __('Settings', 'event_espresso'),
192
+					'show_on_menu' => EE_Admin_Page_Menu_Map::BLOG_ADMIN_ONLY,
193
+					'menu_slug'    => 'settings',
194
+					'capability'   => 'ee_read_ee',
195
+					'menu_order'   => 30,
196
+					'parent_slug'  => 'espresso_events',
197
+				)
198
+			),
199
+			'templates'  => new EE_Admin_Page_Menu_Group(
200
+				array(
201
+					'menu_label'   => __('Templates', 'event_espresso'),
202
+					'show_on_menu' => EE_Admin_Page_Menu_Map::BLOG_ADMIN_ONLY,
203
+					'menu_slug'    => 'templates',
204
+					'capability'   => 'ee_read_ee',
205
+					'menu_order'   => 40,
206
+					'parent_slug'  => 'espresso_events',
207
+				)
208
+			),
209
+			'extras'     => new EE_Admin_Page_Menu_Group(
210
+				array(
211
+					'menu_label'              => __('Extras', 'event_espresso'),
212
+					'show_on_menu'            => EE_Admin_Page_Menu_Map::BLOG_AND_NETWORK_ADMIN,
213
+					'menu_slug'               => 'extras',
214
+					'capability'              => 'ee_read_ee',
215
+					'menu_order'              => 50,
216
+					'parent_slug'             => 'espresso_events',
217
+					'maintenance_mode_parent' => 'espresso_maintenance_settings',
218
+				)
219
+			),
220
+			'tools'      => new EE_Admin_Page_Menu_Group(
221
+				array(
222
+					'menu_label'   => __("Tools", "event_espresso"),
223
+					'show_on_menu' => EE_Admin_Page_Menu_Map::BLOG_ADMIN_ONLY,
224
+					'menu_slug'    => 'tools',
225
+					'capability'   => 'ee_read_ee',
226
+					'menu_order'   => 60,
227
+					'parent_slug'  => 'espresso_events',
228
+				)
229
+			),
230
+			'addons'     => new EE_Admin_Page_Menu_Group(
231
+				array(
232
+					'show_on_menu' => EE_Admin_Page_Menu_Map::BLOG_AND_NETWORK_ADMIN,
233
+					'menu_label'   => __('Add-ons', 'event_espresso'),
234
+					'menu_slug'    => 'addons',
235
+					'capability'   => 'ee_read_ee',
236
+					'menu_order'   => 20,
237
+					'parent_slug'  => 'espresso_events',
238
+				)
239
+			),
240
+		);
241
+		$this->_admin_menu_groups = apply_filters(
242
+			'FHEE__EE_Admin_Page_Loader___set_menu_groups__admin_menu_groups',
243
+			$groups
244
+		);
245
+	}
246
+
247
+
248
+
249
+	/**
250
+	 * This takes all the groups in the _admin_menu_groups array and returns the array indexed by group
251
+	 * slug.  The other utility with this function is it validates that all the groups are instances of
252
+	 * EE_Admin_Page_Menu_Group (cause some invalid things might have slipped in via addons).
253
+	 *
254
+	 * @since  4.4.0
255
+	 * @throws \EE_Error
256
+	 * @return EE_Admin_Page_Menu_Group[]
257
+	 */
258
+	private function _rearrange_menu_groups()
259
+	{
260
+		$groups = array();
261
+		//first let's order the menu groups by their internal menu order (note usort type hinting to ensure the incoming array is EE_Admin_Page_Menu_Map objects )
262
+		usort($this->_admin_menu_groups, array($this, '_sort_menu_maps'));
263
+		foreach ($this->_admin_menu_groups as $group) {
264
+			if (! $group instanceof EE_Admin_Page_Menu_Group) {
265
+				throw new EE_Error(
266
+					sprintf(
267
+						__(
268
+							'Unable to continue sorting the menu groups array because there is an invalid value for the menu groups.  All values in this array are required to be a EE_Admin_Page_Menu_Group object.  Instead there was: %s',
269
+							'event_espresso'
270
+						),
271
+						print_r($group, true)
272
+					)
273
+				);
274
+			}
275
+			$groups[$group->menu_slug] = $group;
276
+		}
277
+		return $groups;
278
+	}
279
+
280
+
281
+
282
+	/**
283
+	 * _get_installed_pages
284
+	 * This just gets the list of installed EE_Admin_pages.
285
+	 *
286
+	 * @access private
287
+	 * @throws EE_Error
288
+	 * @return void
289
+	 */
290
+	private function _get_installed_pages()
291
+	{
292
+		$installed_refs = array();
293
+		$exclude        = array('assets', 'templates');
294
+		// grab everything in the  admin core directory
295
+		$admin_screens = glob(EE_ADMIN_PAGES . '*', GLOB_ONLYDIR);
296
+		if ($admin_screens) {
297
+			foreach ($admin_screens as $admin_screen) {
298
+				// files and anything in the exclude array need not apply
299
+				if (is_dir($admin_screen) && ! in_array(basename($admin_screen), $exclude)) {
300
+					// these folders represent the different EE admin pages
301
+					$installed_refs[basename($admin_screen)] = $admin_screen;
302
+				}
303
+			}
304
+		}
305
+		if (empty($installed_refs)) {
306
+			$error_msg[] = __(
307
+				'There are no EE_Admin pages detected, it looks like EE did not install properly',
308
+				'event_espresso'
309
+			);
310
+			$error_msg[] = $error_msg[0] . "\r\n" . sprintf(
311
+					__(
312
+						'Check that the %s folder exists and is writable. Maybe try deactivating, then reactivating Event Espresso again.',
313
+						'event_espresso'
314
+					),
315
+					EE_ADMIN_PAGES
316
+				);
317
+			throw new EE_Error(implode('||', $error_msg));
318
+		}
319
+		//this just checks the caffeinated folder and takes care of setting up any caffeinated stuff.
320
+		$installed_refs = $this->_set_caffeinated($installed_refs);
321
+		//allow plugins to add in their own pages (note at this point they will need to have an autoloader defined for their class) OR hook into EEH_Autoloader::load_admin_page() to add their path.;
322
+		$installed_refs             = apply_filters(
323
+			'FHEE__EE_Admin_Page_Loader___get_installed_pages__installed_refs',
324
+			$installed_refs
325
+		);
326
+		$this->_caffeinated_extends = apply_filters(
327
+			'FHEE__EE_Admin_Page_Loader___get_installed_pages__caffeinated_extends',
328
+			$this->_caffeinated_extends
329
+		);
330
+		//loop through admin pages and setup the $_installed_pages array.
331
+		$hooks_ref = array();
332
+		foreach ($installed_refs as $page => $path) {
333
+			// set autoloaders for our admin page classes based on included path information
334
+			EEH_Autoloader::instance()->register_autoloaders_for_each_file_in_folder($path);
335
+			// build list of installed pages
336
+			$this->_installed_pages[$page] = $this->_load_admin_page($page, $path);
337
+			// verify returned object
338
+			if ($this->_installed_pages[$page] instanceof EE_Admin_Page_Init) {
339
+				if (! $this->_installed_pages[$page]->get_menu_map() instanceof EE_Admin_Page_Menu_Map) {
340
+					continue;
341
+				}
342
+				//skip if in full maintenance mode and maintenance_mode_parent is set
343
+				$maintenance_mode_parent = $this->_installed_pages[$page]->get_menu_map()->maintenance_mode_parent;
344
+				if (empty($maintenance_mode_parent)
345
+					&& EE_Maintenance_Mode::instance()->level()
346
+					   == EE_Maintenance_Mode::level_2_complete_maintenance) {
347
+					unset($installed_refs[$page]);
348
+					continue;
349
+				}
350
+				$this->_menu_slugs[$this->_installed_pages[$page]->get_menu_map()->menu_slug] = $page;
351
+				//flag for register hooks on extended pages b/c extended pages use the default INIT.
352
+				$extend = false;
353
+				//now that we've got the admin_init objects... lets see if there are any caffeinated pages extending the originals.  If there are then let's hook into the init admin filter and load our extend instead.
354
+				if (isset($this->_caffeinated_extends[$page])) {
355
+					$this->_current_caf_extend_slug = $page;
356
+					$path_hook                      = 'FHEE__EE_Admin_Page_Init___initialize_admin_page__path_to_file__'
357
+													  . $this->_installed_pages[$page]->get_menu_map()->menu_slug
358
+													  . '_'
359
+													  . $this->_installed_pages[$page]->get_admin_page_name();
360
+					$path_runtime                   = 'return "'
361
+													  . $this->_caffeinated_extends[$this->_current_caf_extend_slug]["path"]
362
+													  . '";';
363
+					$page_hook                      = 'FHEE__EE_Admin_Page_Init___initialize_admin_page__admin_page__'
364
+													  . $this->_installed_pages[$page]->get_menu_map()->menu_slug
365
+													  . '_'
366
+													  . $this->_installed_pages[$page]->get_admin_page_name();
367
+					$page_runtime                   = 'return "'
368
+													  . $this->_caffeinated_extends[$this->_current_caf_extend_slug]["admin_page"]
369
+													  . '";';
370
+					$hook_function_path = create_function('$path_to_file', $path_runtime);
371
+					$hook_function_page = create_function('$admin_page', $page_runtime);
372
+					add_filter($path_hook, $hook_function_path);
373
+					add_filter($page_hook, $hook_function_page);
374
+					$extend = true;
375
+				}
376
+				//let's do the registered hooks
377
+				$extended_hooks = $this->_installed_pages[$page]->register_hooks($extend);
378
+				$hooks_ref      = array_merge($hooks_ref, $extended_hooks);
379
+			}
380
+		}
381
+		//the hooks_ref is all the pages where we have $extended _Hooks files that will extend a class in a different folder.  So we want to make sure we load the file for the parent.
382
+		//first make sure we've got unique values
383
+		$hooks_ref = array_unique($hooks_ref);
384
+		//now let's loop and require!
385
+		foreach ($hooks_ref as $path) {
386
+			require_once($path);
387
+		}
388
+		//make sure we have menu slugs global setup. Used in EE_Admin_Page->page_setup() to ensure we don't do a full class load for an admin page that isn't requested.
389
+		global $ee_menu_slugs;
390
+		$ee_menu_slugs = $this->_menu_slugs;
391
+		//we need to loop again to run any early code
392
+		foreach ($installed_refs as $page => $path) {
393
+			if ($this->_installed_pages[$page] instanceof EE_Admin_Page_Init) {
394
+				$this->_installed_pages[$page]->do_initial_loads();
395
+			}
396
+		}
397
+		do_action('AHEE__EE_Admin_Page_Loader___get_installed_pages_loaded', $this->_installed_pages);
398
+	}
399
+
400
+
401
+
402
+	/**
403
+	 * get_admin_page_object
404
+	 *
405
+	 * @param string $page_slug
406
+	 * @return EE_Admin_Page
407
+	 */
408
+	public function get_admin_page_object($page_slug = '')
409
+	{
410
+		if (isset($this->_installed_pages[$page_slug])) {
411
+			return $this->_installed_pages[$page_slug]->loaded_page_object();
412
+		}
413
+		return null;
414
+	}
415
+
416
+
417
+
418
+	/**
419
+	 * _get_classname_for_admin_page
420
+	 * generates an "Admin Page" class based on the directory  name
421
+	 *
422
+	 * @param $dir_name
423
+	 * @return string
424
+	 */
425
+	private function _get_classname_for_admin_page($dir_name = '')
426
+	{
427
+		$class_name = str_replace('_', ' ', strtolower($dir_name));
428
+		return str_replace(' ', '_', ucwords($class_name)) . '_Admin_Page';
429
+	}
430
+
431
+
432
+
433
+	/**
434
+	 * _get_classname_for_admin_init_page
435
+	 * generates an "Admin Page Init" class based on the directory  name
436
+	 *
437
+	 * @param $dir_name
438
+	 * @return string
439
+	 */
440
+	private function _get_classname_for_admin_init_page($dir_name = '')
441
+	{
442
+		$class_name = str_replace('_', ' ', strtolower($dir_name));
443
+		return str_replace(' ', '_', ucwords($class_name)) . '_Admin_Page_Init';
444
+	}
445
+
446
+
447
+
448
+	/**
449
+	 * _load_admin_page
450
+	 * Loads and instantiates page_init object for a single EE_admin page.
451
+	 *
452
+	 * @param  string $page page_reference
453
+	 * @param string  $path
454
+	 * @throws EE_Error
455
+	 * @return object|bool  return page object if valid, bool false if not.
456
+	 */
457
+	private function _load_admin_page($page = '', $path = '')
458
+	{
459
+		$class_name = $this->_get_classname_for_admin_init_page($page);
460
+		EE_Registry::instance()->load_file($path, $class_name, 'core');
461
+		if (! class_exists($class_name)) {
462
+			$inner_error_msg = '<br />' . sprintf(
463
+					esc_html__(
464
+						'Make sure you have %1$s defined. If this is a non-EE-core admin page then you also must have an autoloader in place for your class',
465
+						'event_espresso'
466
+					),
467
+					'<strong>' . $class_name . '</strong>'
468
+				);
469
+			$error_msg[]     = sprintf(
470
+				__('Something went wrong with loading the %s admin page.', 'event_espresso'),
471
+				$page
472
+			);
473
+			$error_msg[]     = $error_msg[0]
474
+							   . "\r\n"
475
+							   . sprintf(
476
+								   esc_html__(
477
+									   'There is no Init class in place for the %s admin page.',
478
+									   'event_espresso'
479
+								   ),
480
+								   $page
481
+							   )
482
+							   . $inner_error_msg;
483
+			throw new EE_Error(implode('||', $error_msg));
484
+		}
485
+		$a = new ReflectionClass($class_name);
486
+		return $a->newInstance();
487
+	}
488
+
489
+
490
+
491
+	/**
492
+	 * set_menus
493
+	 * This method sets up the menus for EE Admin Pages
494
+	 *
495
+	 * @access private
496
+	 * @return void
497
+	 */
498
+	public function set_menus()
499
+	{
500
+		//prep the menu pages (sort, group.)
501
+		$this->_prep_pages();
502
+		foreach ($this->_prepped_menu_maps as $menu_map) {
503
+			if (EE_Registry::instance()->CAP->current_user_can($menu_map->capability, $menu_map->menu_slug)) {
504
+				$menu_map->add_menu_page(false);
505
+			}
506
+		}
507
+	}
508
+
509
+
510
+	/**
511
+	 * set_network_menus
512
+	 * This method sets up the menus for network EE Admin Pages.
513
+	 * Almost identical to EE_Admin_Page_Loader::set_menus() except pages
514
+	 * are only added to the menu map if they are intended for the admin menu
515
+	 *
516
+	 * @return void
517
+	 */
518
+	public function set_network_menus()
519
+	{
520
+		$this->_prep_pages();
521
+		foreach ($this->_prepped_menu_maps as $menu_map) {
522
+			if (EE_Registry::instance()->CAP->current_user_can($menu_map->capability, $menu_map->menu_slug)) {
523
+				$menu_map->add_menu_page(true);
524
+			}
525
+		}
526
+	}
527
+
528
+
529
+
530
+	/**
531
+	 * _prep_pages
532
+	 * sets the _prepped_menu_maps property
533
+	 *
534
+	 * @access private
535
+	 * @throws EE_Error
536
+	 * @return void
537
+	 */
538
+	private function _prep_pages()
539
+	{
540
+		$pages_array = array();
541
+		//rearrange _admin_menu_groups to be indexed by group slug.
542
+		$menu_groups = $this->_rearrange_menu_groups();
543
+		foreach ($this->_installed_pages as $page) {
544
+			if ($page instanceof EE_Admin_page_Init) {
545
+				$page_map = $page->get_menu_map();
546
+				//if we've got an array then the menu map is in the old format so let's throw a persistent notice that the admin system isn't setup correctly for this item.
547
+				if (is_array($page_map) || empty($page_map)) {
548
+					  new PersistentAdminNotice(
549
+						'menu_map_warning_' . str_replace(' ', '_', $page->label) . '_' . EVENT_ESPRESSO_VERSION,
550
+						sprintf(
551
+							__(
552
+								'The admin page for %s was not correctly setup because it is using an older method for integrating with Event Espresso Core.  This means that full functionality for this component is not available.  This error message usually appears with an Add-on that is out of date.  Make sure you update all your Event Espresso 4 add-ons to the latest version to ensure they have necessary compatibility updates in place.',
553
+								'event_espresso'
554
+							),
555
+							$page->label
556
+						)
557
+					);
558
+					continue;
559
+				}
560
+				//if page map is NOT a EE_Admin_Page_Menu_Map object then throw error.
561
+				if (! $page_map instanceof EE_Admin_Page_Menu_Map) {
562
+					throw new EE_Error(
563
+						sprintf(
564
+							__(
565
+								'The menu map for %s must be an EE_Admin_Page_Menu_Map object.  Instead it is %s.  Please double check that the menu map has been configured correctly.',
566
+								'event_espresso'
567
+							),
568
+							$page->label,
569
+							$page_map
570
+						)
571
+					);
572
+				}
573
+				//use the maintenance_mode_parent property and maintenance mode status to determine if this page even gets added to array.
574
+				if (empty($page_map->maintenance_mode_parent)
575
+					&& EE_Maintenance_Mode::instance()->level()
576
+					   == EE_Maintenance_Mode::level_2_complete_maintenance) {
577
+					continue;
578
+				}
579
+				//assign to group (remember $page_map has the admin page stored in it).
580
+				$pages_array[$page_map->menu_group][] = $page_map;
581
+			}
582
+		}
583
+		if (empty($pages_array)) {
584
+			throw new EE_Error(__('Something went wrong when prepping the admin pages', 'event_espresso'));
585
+		}
586
+		//let's sort the groups, make sure it's a valid group, add header (if to show).
587
+		foreach ($pages_array as $group => $menu_maps) {
588
+			//valid_group?
589
+			if (! array_key_exists($group, $menu_groups)) {
590
+				continue;
591
+			}
592
+			//sort pages.
593
+			usort($menu_maps, array($this, '_sort_menu_maps'));
594
+			//prepend header
595
+			array_unshift($menu_maps, $menu_groups[$group]);
596
+			//reset $pages_array with prepped data
597
+			$pages_array[$group] = $menu_maps;
598
+		}
599
+		//now let's setup the _prepped_menu_maps property
600
+		foreach ($menu_groups as $group => $group_objs) {
601
+			if (isset($pages_array[$group])) {
602
+				$this->_prepped_menu_maps = array_merge($this->_prepped_menu_maps, $pages_array[$group]);
603
+			}
604
+		}/**/
605
+	}
606
+
607
+
608
+	/**
609
+	 * This method is the "workhorse" for detecting and setting up caffeinated functionality.
610
+	 * In this method there are three checks being done:
611
+	 * 1. Do we have any NEW admin page sets.  If we do, lets add them into the menu setup (via the $installed_refs
612
+	 * array) etc.  (new page sets are found in caffeinated/new/{page})
613
+	 * 2. Do we have any EXTENDED page sets.  Basically an extended EE_Admin Page extends the core {child}_Admin_Page
614
+	 * class.  eg. would be caffeinated/extend/events/Extend_Events_Admin_Page.core.php and in there would be a class:
615
+	 * Extend_Events_Admin_Page extends Events_Admin_Page.
616
+	 * 3. Do we have any files just for setting up hooks into other core pages.  The files can be any name in
617
+	 * "caffeinated/hooks" EXCEPT they need a ".class.php" extension and the file name must correspond with the
618
+	 * classname inside.  These classes are instantiated really early so that any hooks in them are run before the
619
+	 * corresponding apply_filters/do_actions that are found in any future loaded EE_Admin pages (INCLUDING caffeinated
620
+	 * admin_pages)
621
+	 *
622
+	 * @param array $installed_refs the original installed_refs array that may contain our NEW EE_Admin_Pages to be
623
+	 *                              loaded.
624
+	 * @return array
625
+	 */
626
+	private function _set_caffeinated($installed_refs)
627
+	{
628
+
629
+		//first let's check if there IS a caffeinated folder. If there is not then lets get out.
630
+		if (! is_dir(EE_PLUGIN_DIR_PATH . 'caffeinated' . DS . 'admin') || (defined('EE_DECAF') && EE_DECAF)) {
631
+			return $installed_refs;
632
+		}
633
+		$this->_define_caffeinated_constants();
634
+		$exclude = array('tickets');
635
+		//okay let's setup an "New" pages first (we'll return installed refs later)
636
+		$new_admin_screens = glob(EE_CORE_CAF_ADMIN . 'new/*', GLOB_ONLYDIR);
637
+		if ($new_admin_screens) {
638
+			foreach ($new_admin_screens as $admin_screen) {
639
+				// files and anything in the exclude array need not apply
640
+				if (is_dir($admin_screen) && ! in_array(basename($admin_screen), $exclude)) {
641
+					// these folders represent the different NEW EE admin pages
642
+					$installed_refs[basename($admin_screen)] = $admin_screen;
643
+					// set autoloaders for our admin page classes based on included path information
644
+					EEH_Autoloader::instance()->register_autoloaders_for_each_file_in_folder($admin_screen);
645
+					//					$this->_caf_autoloader[] = array(
646
+					//						'dir' => 'new',
647
+					//						'folder' => basename( $admin_screen )
648
+					//					);
649
+				}
650
+			}
651
+		}
652
+		//let's see if there are any EXTENDS to setup in the $_caffeinated_extends array (that will be used later for hooking into the _initialize_admin_age in the related core_init admin page)
653
+		$extends = glob(EE_CORE_CAF_ADMIN . 'extend/*', GLOB_ONLYDIR);
654
+		if ($extends) {
655
+			foreach ($extends as $extend) {
656
+				if (is_dir($extend)) {
657
+					$extend_ref = basename($extend);
658
+					//now let's make sure there is a file that matches the expected format
659
+					$filename                                              = str_replace(
660
+						' ',
661
+						'_',
662
+						ucwords(
663
+							str_replace(
664
+								'_',
665
+								' ',
666
+								$extend_ref
667
+							)
668
+						)
669
+					);
670
+					$filename                                              = 'Extend_' . $filename . '_Admin_Page';
671
+					$this->_caffeinated_extends[$extend_ref]['path']       = str_replace(
672
+						array('\\', '/'),
673
+						DS,
674
+						EE_CORE_CAF_ADMIN
675
+						. 'extend'
676
+						. DS
677
+						. $extend_ref
678
+						. DS
679
+						. $filename
680
+						. '.core.php'
681
+					);
682
+					$this->_caffeinated_extends[$extend_ref]['admin_page'] = $filename;
683
+					// set autoloaders for our admin page classes based on included path information
684
+					EEH_Autoloader::instance()->register_autoloaders_for_each_file_in_folder($extend);
685
+					//					$this->_caf_autoloader[] = array(
686
+					//						'dir' => 'extend',
687
+					//						'folder' => $extend_ref
688
+					//					);
689
+				}
690
+			}
691
+		}
692
+		//let's see if there are any HOOK files and instantiate them if there are (so that hooks are loaded early!).
693
+		$ee_admin_hooks = array();
694
+		$hooks          = glob(EE_CORE_CAF_ADMIN . 'hooks/*.class.php');
695
+		if ($hooks) {
696
+			foreach ($hooks as $hook) {
697
+				if (is_readable($hook)) {
698
+					require_once $hook;
699
+					$classname = str_replace(EE_CORE_CAF_ADMIN . 'hooks/', '', $hook);
700
+					$classname = str_replace('.class.php', '', $classname);
701
+					if (class_exists($classname)) {
702
+						$a                = new ReflectionClass($classname);
703
+						$ee_admin_hooks[] = $a->newInstance();
704
+					}
705
+				}
706
+			}
707
+		}/**/
708
+		$ee_admin_hooks = apply_filters('FHEE__EE_Admin_Page_Loader__set_caffeinated__ee_admin_hooks', $ee_admin_hooks);
709
+		return $installed_refs;
710
+	}
711
+
712
+
713
+
714
+
715
+
716
+	/**
717
+	 * Initial autoloader registration
718
+	 * This just sets up the autoloader for the root admin files
719
+	 *
720
+	 * @param  string $className incoming classname to check for autoload
721
+	 * @return void
722
+	 */
723
+	//	public function init_autoloaders( $className ) {
724
+	//		$dir_ref = array(
725
+	//			EE_ADMIN => array('core', 'class')
726
+	//		);
727
+	//		EEH_Autoloader::try_autoload($dir_ref, $className );
728
+	//	}
729
+	/**
730
+	 * This method takes care of setting up the autoloader dynamically for any NEW EE_Admin pages found in the
731
+	 * caffeinated folders.
732
+	 *
733
+	 * @access public
734
+	 * @param  string $className in coming classname being called
735
+	 * @return void
736
+	 */
737
+	//	public function caffeinated_autoloaders( $className ) {
738
+	//		//let's setup an array of paths to check (for each subsystem)
739
+	//		$dir_ref = array();
740
+	//		foreach ( $this->_caf_autoloader as $pathinfo) {
741
+	//			$dir_ref[ EE_CORE_CAF_ADMIN . $pathinfo['dir'] . DS . $pathinfo['folder'] . DS] = array('core', 'class');
742
+	//		}
743
+	//
744
+	//		EEH_Autoloader::try_autoload($dir_ref, $className );
745
+	//	}
746
+	/**
747
+	 * Utility method for sorting the _menu_maps (callback for usort php function)
748
+	 *
749
+	 * @since  4.4.0
750
+	 * @param  EE_Admin_Page_Menu_Map $a menu_map object
751
+	 * @param  EE_Admin_Page_Menu_Map $b being compared to
752
+	 * @return int    sort order
753
+	 */
754
+	private function _sort_menu_maps(EE_Admin_Page_Menu_Map $a, EE_Admin_Page_Menu_Map $b)
755
+	{
756
+		if ($a->menu_order == $b->menu_order) {
757
+			return 0;
758
+		}
759
+		return ($a->menu_order < $b->menu_order) ? -1 : 1;
760
+	}
761
+
762
+
763
+
764
+	/**
765
+	 * _default_header_link
766
+	 * This is just a dummy method to use with header submenu items
767
+	 *
768
+	 * @return bool false
769
+	 */
770
+	public function _default_header_link()
771
+	{
772
+		return false;
773
+	}
774 774
 
775 775
 
776 776
 }
Please login to merge, or discard this patch.
Spacing   +31 added lines, -31 removed lines patch added patch discarded remove patch
@@ -2,7 +2,7 @@  discard block
 block discarded – undo
2 2
 use EventEspresso\core\domain\entities\notifications\PersistentAdminNotice;
3 3
 use EventEspresso\core\exceptions\InvalidDataTypeException;
4 4
 
5
-if (!defined('EVENT_ESPRESSO_VERSION') )
5
+if ( ! defined('EVENT_ESPRESSO_VERSION'))
6 6
 	exit('NO direct script access allowed');
7 7
 
8 8
 
@@ -142,13 +142,13 @@  discard block
 block discarded – undo
142 142
      */
143 143
     private function _define_caffeinated_constants()
144 144
     {
145
-        if (! defined('EE_CORE_CAF_ADMIN')) {
146
-            define('EE_CORE_CAF_ADMIN', EE_PLUGIN_DIR_PATH . 'caffeinated/admin/');
147
-            define('EE_CORE_CAF_ADMIN_URL', EE_PLUGIN_DIR_URL . 'caffeinated/admin/');
148
-            define('EE_CORE_CAF_ADMIN_NEW', EE_CORE_CAF_ADMIN . 'new/');
149
-            define('EE_CORE_CAF_ADMIN_EXTEND', EE_CORE_CAF_ADMIN . 'extend/');
150
-            define('EE_CORE_CAF_ADMIN_EXTEND_URL', EE_CORE_CAF_ADMIN_URL . 'extend/');
151
-            define('EE_CORE_CAF_ADMIN_HOOKS', EE_CORE_CAF_ADMIN . 'hooks/');
145
+        if ( ! defined('EE_CORE_CAF_ADMIN')) {
146
+            define('EE_CORE_CAF_ADMIN', EE_PLUGIN_DIR_PATH.'caffeinated/admin/');
147
+            define('EE_CORE_CAF_ADMIN_URL', EE_PLUGIN_DIR_URL.'caffeinated/admin/');
148
+            define('EE_CORE_CAF_ADMIN_NEW', EE_CORE_CAF_ADMIN.'new/');
149
+            define('EE_CORE_CAF_ADMIN_EXTEND', EE_CORE_CAF_ADMIN.'extend/');
150
+            define('EE_CORE_CAF_ADMIN_EXTEND_URL', EE_CORE_CAF_ADMIN_URL.'extend/');
151
+            define('EE_CORE_CAF_ADMIN_HOOKS', EE_CORE_CAF_ADMIN.'hooks/');
152 152
         }
153 153
     }
154 154
 
@@ -261,7 +261,7 @@  discard block
 block discarded – undo
261 261
         //first let's order the menu groups by their internal menu order (note usort type hinting to ensure the incoming array is EE_Admin_Page_Menu_Map objects )
262 262
         usort($this->_admin_menu_groups, array($this, '_sort_menu_maps'));
263 263
         foreach ($this->_admin_menu_groups as $group) {
264
-            if (! $group instanceof EE_Admin_Page_Menu_Group) {
264
+            if ( ! $group instanceof EE_Admin_Page_Menu_Group) {
265 265
                 throw new EE_Error(
266 266
                     sprintf(
267 267
                         __(
@@ -292,7 +292,7 @@  discard block
 block discarded – undo
292 292
         $installed_refs = array();
293 293
         $exclude        = array('assets', 'templates');
294 294
         // grab everything in the  admin core directory
295
-        $admin_screens = glob(EE_ADMIN_PAGES . '*', GLOB_ONLYDIR);
295
+        $admin_screens = glob(EE_ADMIN_PAGES.'*', GLOB_ONLYDIR);
296 296
         if ($admin_screens) {
297 297
             foreach ($admin_screens as $admin_screen) {
298 298
                 // files and anything in the exclude array need not apply
@@ -307,7 +307,7 @@  discard block
 block discarded – undo
307 307
                 'There are no EE_Admin pages detected, it looks like EE did not install properly',
308 308
                 'event_espresso'
309 309
             );
310
-            $error_msg[] = $error_msg[0] . "\r\n" . sprintf(
310
+            $error_msg[] = $error_msg[0]."\r\n".sprintf(
311 311
                     __(
312 312
                         'Check that the %s folder exists and is writable. Maybe try deactivating, then reactivating Event Espresso again.',
313 313
                         'event_espresso'
@@ -319,7 +319,7 @@  discard block
 block discarded – undo
319 319
         //this just checks the caffeinated folder and takes care of setting up any caffeinated stuff.
320 320
         $installed_refs = $this->_set_caffeinated($installed_refs);
321 321
         //allow plugins to add in their own pages (note at this point they will need to have an autoloader defined for their class) OR hook into EEH_Autoloader::load_admin_page() to add their path.;
322
-        $installed_refs             = apply_filters(
322
+        $installed_refs = apply_filters(
323 323
             'FHEE__EE_Admin_Page_Loader___get_installed_pages__installed_refs',
324 324
             $installed_refs
325 325
         );
@@ -336,7 +336,7 @@  discard block
 block discarded – undo
336 336
             $this->_installed_pages[$page] = $this->_load_admin_page($page, $path);
337 337
             // verify returned object
338 338
             if ($this->_installed_pages[$page] instanceof EE_Admin_Page_Init) {
339
-                if (! $this->_installed_pages[$page]->get_menu_map() instanceof EE_Admin_Page_Menu_Map) {
339
+                if ( ! $this->_installed_pages[$page]->get_menu_map() instanceof EE_Admin_Page_Menu_Map) {
340 340
                     continue;
341 341
                 }
342 342
                 //skip if in full maintenance mode and maintenance_mode_parent is set
@@ -364,7 +364,7 @@  discard block
 block discarded – undo
364 364
                                                       . $this->_installed_pages[$page]->get_menu_map()->menu_slug
365 365
                                                       . '_'
366 366
                                                       . $this->_installed_pages[$page]->get_admin_page_name();
367
-                    $page_runtime                   = 'return "'
367
+                    $page_runtime = 'return "'
368 368
                                                       . $this->_caffeinated_extends[$this->_current_caf_extend_slug]["admin_page"]
369 369
                                                       . '";';
370 370
                     $hook_function_path = create_function('$path_to_file', $path_runtime);
@@ -425,7 +425,7 @@  discard block
 block discarded – undo
425 425
     private function _get_classname_for_admin_page($dir_name = '')
426 426
     {
427 427
         $class_name = str_replace('_', ' ', strtolower($dir_name));
428
-        return str_replace(' ', '_', ucwords($class_name)) . '_Admin_Page';
428
+        return str_replace(' ', '_', ucwords($class_name)).'_Admin_Page';
429 429
     }
430 430
 
431 431
 
@@ -440,7 +440,7 @@  discard block
 block discarded – undo
440 440
     private function _get_classname_for_admin_init_page($dir_name = '')
441 441
     {
442 442
         $class_name = str_replace('_', ' ', strtolower($dir_name));
443
-        return str_replace(' ', '_', ucwords($class_name)) . '_Admin_Page_Init';
443
+        return str_replace(' ', '_', ucwords($class_name)).'_Admin_Page_Init';
444 444
     }
445 445
 
446 446
 
@@ -458,19 +458,19 @@  discard block
 block discarded – undo
458 458
     {
459 459
         $class_name = $this->_get_classname_for_admin_init_page($page);
460 460
         EE_Registry::instance()->load_file($path, $class_name, 'core');
461
-        if (! class_exists($class_name)) {
462
-            $inner_error_msg = '<br />' . sprintf(
461
+        if ( ! class_exists($class_name)) {
462
+            $inner_error_msg = '<br />'.sprintf(
463 463
                     esc_html__(
464 464
                         'Make sure you have %1$s defined. If this is a non-EE-core admin page then you also must have an autoloader in place for your class',
465 465
                         'event_espresso'
466 466
                     ),
467
-                    '<strong>' . $class_name . '</strong>'
467
+                    '<strong>'.$class_name.'</strong>'
468 468
                 );
469
-            $error_msg[]     = sprintf(
469
+            $error_msg[] = sprintf(
470 470
                 __('Something went wrong with loading the %s admin page.', 'event_espresso'),
471 471
                 $page
472 472
             );
473
-            $error_msg[]     = $error_msg[0]
473
+            $error_msg[] = $error_msg[0]
474 474
                                . "\r\n"
475 475
                                . sprintf(
476 476
                                    esc_html__(
@@ -546,7 +546,7 @@  discard block
 block discarded – undo
546 546
                 //if we've got an array then the menu map is in the old format so let's throw a persistent notice that the admin system isn't setup correctly for this item.
547 547
                 if (is_array($page_map) || empty($page_map)) {
548 548
                       new PersistentAdminNotice(
549
-                        'menu_map_warning_' . str_replace(' ', '_', $page->label) . '_' . EVENT_ESPRESSO_VERSION,
549
+                        'menu_map_warning_'.str_replace(' ', '_', $page->label).'_'.EVENT_ESPRESSO_VERSION,
550 550
                         sprintf(
551 551
                             __(
552 552
                                 'The admin page for %s was not correctly setup because it is using an older method for integrating with Event Espresso Core.  This means that full functionality for this component is not available.  This error message usually appears with an Add-on that is out of date.  Make sure you update all your Event Espresso 4 add-ons to the latest version to ensure they have necessary compatibility updates in place.',
@@ -558,7 +558,7 @@  discard block
 block discarded – undo
558 558
                     continue;
559 559
                 }
560 560
                 //if page map is NOT a EE_Admin_Page_Menu_Map object then throw error.
561
-                if (! $page_map instanceof EE_Admin_Page_Menu_Map) {
561
+                if ( ! $page_map instanceof EE_Admin_Page_Menu_Map) {
562 562
                     throw new EE_Error(
563 563
                         sprintf(
564 564
                             __(
@@ -586,7 +586,7 @@  discard block
 block discarded – undo
586 586
         //let's sort the groups, make sure it's a valid group, add header (if to show).
587 587
         foreach ($pages_array as $group => $menu_maps) {
588 588
             //valid_group?
589
-            if (! array_key_exists($group, $menu_groups)) {
589
+            if ( ! array_key_exists($group, $menu_groups)) {
590 590
                 continue;
591 591
             }
592 592
             //sort pages.
@@ -627,13 +627,13 @@  discard block
 block discarded – undo
627 627
     {
628 628
 
629 629
         //first let's check if there IS a caffeinated folder. If there is not then lets get out.
630
-        if (! is_dir(EE_PLUGIN_DIR_PATH . 'caffeinated' . DS . 'admin') || (defined('EE_DECAF') && EE_DECAF)) {
630
+        if ( ! is_dir(EE_PLUGIN_DIR_PATH.'caffeinated'.DS.'admin') || (defined('EE_DECAF') && EE_DECAF)) {
631 631
             return $installed_refs;
632 632
         }
633 633
         $this->_define_caffeinated_constants();
634 634
         $exclude = array('tickets');
635 635
         //okay let's setup an "New" pages first (we'll return installed refs later)
636
-        $new_admin_screens = glob(EE_CORE_CAF_ADMIN . 'new/*', GLOB_ONLYDIR);
636
+        $new_admin_screens = glob(EE_CORE_CAF_ADMIN.'new/*', GLOB_ONLYDIR);
637 637
         if ($new_admin_screens) {
638 638
             foreach ($new_admin_screens as $admin_screen) {
639 639
                 // files and anything in the exclude array need not apply
@@ -650,13 +650,13 @@  discard block
 block discarded – undo
650 650
             }
651 651
         }
652 652
         //let's see if there are any EXTENDS to setup in the $_caffeinated_extends array (that will be used later for hooking into the _initialize_admin_age in the related core_init admin page)
653
-        $extends = glob(EE_CORE_CAF_ADMIN . 'extend/*', GLOB_ONLYDIR);
653
+        $extends = glob(EE_CORE_CAF_ADMIN.'extend/*', GLOB_ONLYDIR);
654 654
         if ($extends) {
655 655
             foreach ($extends as $extend) {
656 656
                 if (is_dir($extend)) {
657 657
                     $extend_ref = basename($extend);
658 658
                     //now let's make sure there is a file that matches the expected format
659
-                    $filename                                              = str_replace(
659
+                    $filename = str_replace(
660 660
                         ' ',
661 661
                         '_',
662 662
                         ucwords(
@@ -667,7 +667,7 @@  discard block
 block discarded – undo
667 667
                             )
668 668
                         )
669 669
                     );
670
-                    $filename                                              = 'Extend_' . $filename . '_Admin_Page';
670
+                    $filename                                              = 'Extend_'.$filename.'_Admin_Page';
671 671
                     $this->_caffeinated_extends[$extend_ref]['path']       = str_replace(
672 672
                         array('\\', '/'),
673 673
                         DS,
@@ -691,12 +691,12 @@  discard block
 block discarded – undo
691 691
         }
692 692
         //let's see if there are any HOOK files and instantiate them if there are (so that hooks are loaded early!).
693 693
         $ee_admin_hooks = array();
694
-        $hooks          = glob(EE_CORE_CAF_ADMIN . 'hooks/*.class.php');
694
+        $hooks          = glob(EE_CORE_CAF_ADMIN.'hooks/*.class.php');
695 695
         if ($hooks) {
696 696
             foreach ($hooks as $hook) {
697 697
                 if (is_readable($hook)) {
698 698
                     require_once $hook;
699
-                    $classname = str_replace(EE_CORE_CAF_ADMIN . 'hooks/', '', $hook);
699
+                    $classname = str_replace(EE_CORE_CAF_ADMIN.'hooks/', '', $hook);
700 700
                     $classname = str_replace('.class.php', '', $classname);
701 701
                     if (class_exists($classname)) {
702 702
                         $a                = new ReflectionClass($classname);
Please login to merge, or discard this patch.
Braces   +2 added lines, -1 removed lines patch added patch discarded remove patch
@@ -2,8 +2,9 @@
 block discarded – undo
2 2
 use EventEspresso\core\domain\entities\notifications\PersistentAdminNotice;
3 3
 use EventEspresso\core\exceptions\InvalidDataTypeException;
4 4
 
5
-if (!defined('EVENT_ESPRESSO_VERSION') )
5
+if (!defined('EVENT_ESPRESSO_VERSION') ) {
6 6
 	exit('NO direct script access allowed');
7
+}
7 8
 
8 9
 
9 10
 
Please login to merge, or discard this patch.
libraries/form_sections/inputs/EE_Form_Input_With_Options_Base.input.php 2 patches
Indentation   +319 added lines, -319 removed lines patch added patch discarded remove patch
@@ -1,5 +1,5 @@  discard block
 block discarded – undo
1 1
 <?php if ( ! defined('EVENT_ESPRESSO_VERSION')) {
2
-    exit('No direct script access allowed');
2
+	exit('No direct script access allowed');
3 3
 }
4 4
 
5 5
 
@@ -16,324 +16,324 @@  discard block
 block discarded – undo
16 16
 class EE_Form_Input_With_Options_Base extends EE_Form_Input_Base
17 17
 {
18 18
 
19
-    /**
20
-     * array of available options to choose as an answer
21
-     *
22
-     * @var array
23
-     */
24
-    protected $_options = array();
25
-
26
-    /**
27
-     * whether to display the html_label_text above the checkbox/radio button options
28
-     *
29
-     * @var boolean
30
-     */
31
-    protected $_display_html_label_text = true;
32
-
33
-    /**
34
-     * whether to display an question option description as part of the input label
35
-     *
36
-     * @var boolean
37
-     */
38
-    protected $_use_desc_in_label = true;
39
-
40
-    /**
41
-     * strlen() result for the longest input value (what gets displayed in the label)
42
-     * this is used to apply a css class to the input label
43
-     *
44
-     * @var int
45
-     */
46
-    protected $_label_size = 0;
47
-
48
-    /**
49
-     * whether to enforce the label size value passed in the constructor
50
-     *
51
-     * @var boolean
52
-     */
53
-    protected $_enforce_label_size = false;
54
-
55
-    /**
56
-     * whether to allow multiple selections (ie, the value of this input should be an array)
57
-     * or not (ie, the value should be a simple int, string, etc)
58
-     *
59
-     * @var boolean
60
-     */
61
-    protected $_multiple_selections = false;
62
-
63
-
64
-
65
-    /**
66
-     * @param array     $answer_options
67
-     * @param array     $input_settings {
68
-     * @type int|string $label_size
69
-     * @type boolean    $display_html_label_text
70
-     *                                  }
71
-     *                                  And all the options accepted by EE_Form_Input_Base
72
-     */
73
-    public function __construct($answer_options = array(), $input_settings = array())
74
-    {
75
-        if (isset($input_settings['label_size'])) {
76
-            $this->_set_label_size($input_settings['label_size']);
77
-            if (isset($input_settings['enforce_label_size']) && $input_settings['enforce_label_size']) {
78
-                $this->_enforce_label_size = true;
79
-            }
80
-        }
81
-        if (isset($input_settings['display_html_label_text'])) {
82
-            $this->set_display_html_label_text($input_settings['display_html_label_text']);
83
-        }
84
-        $this->set_select_options($answer_options);
85
-        parent::__construct($input_settings);
86
-    }
87
-
88
-
89
-
90
-    /**
91
-     * Sets the allowed options for this input. Also has the side-effect of
92
-     * updating the normalization strategy to match the keys provided in the array
93
-     *
94
-     * @param array $answer_options
95
-     * @return void  just has the side-effect of setting the options for this input
96
-     */
97
-    public function set_select_options($answer_options = array())
98
-    {
99
-        $answer_options = is_array($answer_options) ? $answer_options : array($answer_options);
100
-        //get the first item in the select options and check it's type
101
-        $this->_options = reset($answer_options) instanceof EE_Question_Option
102
-            ? $this->_process_question_options($answer_options)
103
-            : $answer_options;
104
-        //d( $this->_options );
105
-        $select_option_keys = array_keys($this->_options);
106
-        // attempt to determine data type for values in order to set normalization type
107
-        //purposefully only
108
-        if (
109
-            count($this->_options) === 2
110
-            && (
111
-                (in_array(true, $select_option_keys, true) && in_array(false, $select_option_keys, true))
112
-                || (in_array(1, $select_option_keys, true) && in_array(0, $select_option_keys, true))
113
-            )
114
-        ) {
115
-            // values appear to be boolean, like TRUE, FALSE, 1, 0
116
-            $normalization = new EE_Boolean_Normalization();
117
-        } else {
118
-            //are ALL the options ints (even if we're using a multi-dimensional array)? If so use int validation
119
-            $all_ints = true;
120
-            array_walk_recursive(
121
-                $this->_options,
122
-                function($value,$key) use (&$all_ints){
123
-                    //is this a top-level key? ignore it
124
-                    if(! is_array($value)
125
-                        && ! is_int($key)
126
-                       && $key !== ''
127
-                       && $key !== null){
128
-                        $all_ints = false;
129
-                    }
130
-                }
131
-            );
132
-            if ($all_ints) {
133
-                $normalization = new EE_Int_Normalization();
134
-            } else {
135
-                $normalization = new EE_Text_Normalization();
136
-            }
137
-        }
138
-        // does input type have multiple options ?
139
-        if ($this->_multiple_selections) {
140
-            $this->_set_normalization_strategy(new EE_Many_Valued_Normalization($normalization));
141
-        } else {
142
-            $this->_set_normalization_strategy($normalization);
143
-        }
144
-    }
145
-
146
-
147
-
148
-    /**
149
-     * @return array
150
-     */
151
-    public function options()
152
-    {
153
-        return $this->_options;
154
-    }
155
-
156
-
157
-
158
-    /**
159
-     * Returns an array which is guaranteed to not be multidimensional
160
-     *
161
-     * @return array
162
-     */
163
-    public function flat_options()
164
-    {
165
-        return $this->_flatten_select_options($this->options());
166
-    }
167
-
168
-
169
-
170
-    /**
171
-     * Makes sure $arr is a flat array, not a multidimensional one
172
-     *
173
-     * @param array $arr
174
-     * @return array
175
-     */
176
-    protected function _flatten_select_options($arr)
177
-    {
178
-        $flat_array = array();
179
-        if (EEH_Array::is_multi_dimensional_array($arr)) {
180
-            foreach ($arr as $sub_array) {
181
-                foreach ((array)$sub_array as $key => $value) {
182
-                    $flat_array[$key] = $value;
183
-                    $this->_set_label_size($value);
184
-                }
185
-            }
186
-        } else {
187
-            foreach ($arr as $key => $value) {
188
-                $flat_array[$key] = $value;
189
-                $this->_set_label_size($value);
190
-            }
191
-        }
192
-        return $flat_array;
193
-    }
194
-
195
-
196
-
197
-    /**
198
-     * @param EE_Question_Option[] $question_options_array
199
-     * @return array
200
-     */
201
-    protected function _process_question_options($question_options_array = array())
202
-    {
203
-        $flat_array = array();
204
-        foreach ($question_options_array as $question_option) {
205
-            if ($question_option instanceof EE_Question_Option) {
206
-                $desc = '';
207
-                if ($this->_use_desc_in_label) {
208
-                    $desc = $question_option->desc();
209
-                    $desc = ! empty($desc) ? '<span class="ee-question-option-desc">' . $desc . '</span>' : '';
210
-                }
211
-                $value = $question_option->value();
212
-                // add value even if it's empty
213
-                $flat_array[$value] = $value;
214
-                // if both value and desc are not empty, then separate with a dash
215
-                if ( ! empty($value) && ! empty($desc)) {
216
-                    $flat_array[$value] .= ' - ' . $desc;
217
-                } else {
218
-                    // otherwise, just add desc, since either or both of the vars is empty, and no dash is necessary
219
-                    $flat_array[$value] .= $desc;
220
-                }
221
-            } elseif (is_array($question_option)) {
222
-                $flat_array += $this->_flatten_select_options($question_option);
223
-            }
224
-        }
225
-        return $flat_array;
226
-    }
227
-
228
-
229
-
230
-    /**
231
-     *    set_label_sizes
232
-     *
233
-     * @return void
234
-     */
235
-    public function set_label_sizes()
236
-    {
237
-        // did the input settings specifically say to NOT set the label size dynamically ?
238
-        if ( ! $this->_enforce_label_size) {
239
-            foreach ($this->_options as $option) {
240
-                // calculate the strlen of the label
241
-                $this->_set_label_size($option);
242
-            }
243
-        }
244
-    }
245
-
246
-
247
-
248
-    /**
249
-     *    _set_label_size_class
250
-     *
251
-     * @param int|string $value
252
-     * @return void
253
-     */
254
-    private function _set_label_size($value = '')
255
-    {
256
-        // don't change label size if it has already been set and is being enforced
257
-        if($this->_enforce_label_size && $this->_label_size >  0) {
258
-            return;
259
-        }
260
-        // determine length of option value
261
-        $val_size = is_int($value) ? $value : strlen($value);
262
-        // use new value if bigger than existing
263
-        $this->_label_size = $val_size > $this->_label_size ? $val_size : $this->_label_size;
264
-    }
265
-
266
-
267
-
268
-    /**
269
-     *    get_label_size_class
270
-     *
271
-     * @return string
272
-     */
273
-    public function get_label_size_class()
274
-    {
275
-        $size = ' medium-lbl';
276
-        // use maximum option value length to determine label size
277
-        if ($this->_label_size < 3) {
278
-            $size = ' nano-lbl';
279
-        } else if ($this->_label_size < 6) {
280
-            $size = ' micro-lbl';
281
-        } else if ($this->_label_size < 12) {
282
-            $size = ' tiny-lbl';
283
-        } else if ($this->_label_size < 25) {
284
-            $size = ' small-lbl';
285
-        } else if ($this->_label_size < 50) {
286
-            $size = ' medium-lbl';
287
-        } else if ($this->_label_size >= 100) {
288
-            $size = ' big-lbl';
289
-        }
290
-        return $size;
291
-    }
292
-
293
-
294
-
295
-    /**
296
-     * Returns the pretty value for the normalized value
297
-     *
298
-     * @return string
299
-     */
300
-    public function pretty_value()
301
-    {
302
-        $options = $this->flat_options();
303
-        $unnormalized_value_choices = $this->get_normalization_strategy()->unnormalize($this->_normalized_value);
304
-        if ( ! $this->_multiple_selections) {
305
-            $unnormalized_value_choices = array($unnormalized_value_choices);
306
-        }
307
-        $pretty_strings = array();
308
-        foreach ((array)$unnormalized_value_choices as $unnormalized_value_choice) {
309
-            if (isset($options[$unnormalized_value_choice])) {
310
-                $pretty_strings[] = $options[$unnormalized_value_choice];
311
-            } else {
312
-                $pretty_strings[] = $this->normalized_value();
313
-            }
314
-        }
315
-        return implode(', ', $pretty_strings);
316
-    }
317
-
318
-
319
-
320
-    /**
321
-     * @return boolean
322
-     */
323
-    public function display_html_label_text()
324
-    {
325
-        return $this->_display_html_label_text;
326
-    }
327
-
328
-
329
-
330
-    /**
331
-     * @param boolean $display_html_label_text
332
-     */
333
-    public function set_display_html_label_text($display_html_label_text)
334
-    {
335
-        $this->_display_html_label_text = filter_var($display_html_label_text, FILTER_VALIDATE_BOOLEAN);
336
-    }
19
+	/**
20
+	 * array of available options to choose as an answer
21
+	 *
22
+	 * @var array
23
+	 */
24
+	protected $_options = array();
25
+
26
+	/**
27
+	 * whether to display the html_label_text above the checkbox/radio button options
28
+	 *
29
+	 * @var boolean
30
+	 */
31
+	protected $_display_html_label_text = true;
32
+
33
+	/**
34
+	 * whether to display an question option description as part of the input label
35
+	 *
36
+	 * @var boolean
37
+	 */
38
+	protected $_use_desc_in_label = true;
39
+
40
+	/**
41
+	 * strlen() result for the longest input value (what gets displayed in the label)
42
+	 * this is used to apply a css class to the input label
43
+	 *
44
+	 * @var int
45
+	 */
46
+	protected $_label_size = 0;
47
+
48
+	/**
49
+	 * whether to enforce the label size value passed in the constructor
50
+	 *
51
+	 * @var boolean
52
+	 */
53
+	protected $_enforce_label_size = false;
54
+
55
+	/**
56
+	 * whether to allow multiple selections (ie, the value of this input should be an array)
57
+	 * or not (ie, the value should be a simple int, string, etc)
58
+	 *
59
+	 * @var boolean
60
+	 */
61
+	protected $_multiple_selections = false;
62
+
63
+
64
+
65
+	/**
66
+	 * @param array     $answer_options
67
+	 * @param array     $input_settings {
68
+	 * @type int|string $label_size
69
+	 * @type boolean    $display_html_label_text
70
+	 *                                  }
71
+	 *                                  And all the options accepted by EE_Form_Input_Base
72
+	 */
73
+	public function __construct($answer_options = array(), $input_settings = array())
74
+	{
75
+		if (isset($input_settings['label_size'])) {
76
+			$this->_set_label_size($input_settings['label_size']);
77
+			if (isset($input_settings['enforce_label_size']) && $input_settings['enforce_label_size']) {
78
+				$this->_enforce_label_size = true;
79
+			}
80
+		}
81
+		if (isset($input_settings['display_html_label_text'])) {
82
+			$this->set_display_html_label_text($input_settings['display_html_label_text']);
83
+		}
84
+		$this->set_select_options($answer_options);
85
+		parent::__construct($input_settings);
86
+	}
87
+
88
+
89
+
90
+	/**
91
+	 * Sets the allowed options for this input. Also has the side-effect of
92
+	 * updating the normalization strategy to match the keys provided in the array
93
+	 *
94
+	 * @param array $answer_options
95
+	 * @return void  just has the side-effect of setting the options for this input
96
+	 */
97
+	public function set_select_options($answer_options = array())
98
+	{
99
+		$answer_options = is_array($answer_options) ? $answer_options : array($answer_options);
100
+		//get the first item in the select options and check it's type
101
+		$this->_options = reset($answer_options) instanceof EE_Question_Option
102
+			? $this->_process_question_options($answer_options)
103
+			: $answer_options;
104
+		//d( $this->_options );
105
+		$select_option_keys = array_keys($this->_options);
106
+		// attempt to determine data type for values in order to set normalization type
107
+		//purposefully only
108
+		if (
109
+			count($this->_options) === 2
110
+			&& (
111
+				(in_array(true, $select_option_keys, true) && in_array(false, $select_option_keys, true))
112
+				|| (in_array(1, $select_option_keys, true) && in_array(0, $select_option_keys, true))
113
+			)
114
+		) {
115
+			// values appear to be boolean, like TRUE, FALSE, 1, 0
116
+			$normalization = new EE_Boolean_Normalization();
117
+		} else {
118
+			//are ALL the options ints (even if we're using a multi-dimensional array)? If so use int validation
119
+			$all_ints = true;
120
+			array_walk_recursive(
121
+				$this->_options,
122
+				function($value,$key) use (&$all_ints){
123
+					//is this a top-level key? ignore it
124
+					if(! is_array($value)
125
+						&& ! is_int($key)
126
+					   && $key !== ''
127
+					   && $key !== null){
128
+						$all_ints = false;
129
+					}
130
+				}
131
+			);
132
+			if ($all_ints) {
133
+				$normalization = new EE_Int_Normalization();
134
+			} else {
135
+				$normalization = new EE_Text_Normalization();
136
+			}
137
+		}
138
+		// does input type have multiple options ?
139
+		if ($this->_multiple_selections) {
140
+			$this->_set_normalization_strategy(new EE_Many_Valued_Normalization($normalization));
141
+		} else {
142
+			$this->_set_normalization_strategy($normalization);
143
+		}
144
+	}
145
+
146
+
147
+
148
+	/**
149
+	 * @return array
150
+	 */
151
+	public function options()
152
+	{
153
+		return $this->_options;
154
+	}
155
+
156
+
157
+
158
+	/**
159
+	 * Returns an array which is guaranteed to not be multidimensional
160
+	 *
161
+	 * @return array
162
+	 */
163
+	public function flat_options()
164
+	{
165
+		return $this->_flatten_select_options($this->options());
166
+	}
167
+
168
+
169
+
170
+	/**
171
+	 * Makes sure $arr is a flat array, not a multidimensional one
172
+	 *
173
+	 * @param array $arr
174
+	 * @return array
175
+	 */
176
+	protected function _flatten_select_options($arr)
177
+	{
178
+		$flat_array = array();
179
+		if (EEH_Array::is_multi_dimensional_array($arr)) {
180
+			foreach ($arr as $sub_array) {
181
+				foreach ((array)$sub_array as $key => $value) {
182
+					$flat_array[$key] = $value;
183
+					$this->_set_label_size($value);
184
+				}
185
+			}
186
+		} else {
187
+			foreach ($arr as $key => $value) {
188
+				$flat_array[$key] = $value;
189
+				$this->_set_label_size($value);
190
+			}
191
+		}
192
+		return $flat_array;
193
+	}
194
+
195
+
196
+
197
+	/**
198
+	 * @param EE_Question_Option[] $question_options_array
199
+	 * @return array
200
+	 */
201
+	protected function _process_question_options($question_options_array = array())
202
+	{
203
+		$flat_array = array();
204
+		foreach ($question_options_array as $question_option) {
205
+			if ($question_option instanceof EE_Question_Option) {
206
+				$desc = '';
207
+				if ($this->_use_desc_in_label) {
208
+					$desc = $question_option->desc();
209
+					$desc = ! empty($desc) ? '<span class="ee-question-option-desc">' . $desc . '</span>' : '';
210
+				}
211
+				$value = $question_option->value();
212
+				// add value even if it's empty
213
+				$flat_array[$value] = $value;
214
+				// if both value and desc are not empty, then separate with a dash
215
+				if ( ! empty($value) && ! empty($desc)) {
216
+					$flat_array[$value] .= ' - ' . $desc;
217
+				} else {
218
+					// otherwise, just add desc, since either or both of the vars is empty, and no dash is necessary
219
+					$flat_array[$value] .= $desc;
220
+				}
221
+			} elseif (is_array($question_option)) {
222
+				$flat_array += $this->_flatten_select_options($question_option);
223
+			}
224
+		}
225
+		return $flat_array;
226
+	}
227
+
228
+
229
+
230
+	/**
231
+	 *    set_label_sizes
232
+	 *
233
+	 * @return void
234
+	 */
235
+	public function set_label_sizes()
236
+	{
237
+		// did the input settings specifically say to NOT set the label size dynamically ?
238
+		if ( ! $this->_enforce_label_size) {
239
+			foreach ($this->_options as $option) {
240
+				// calculate the strlen of the label
241
+				$this->_set_label_size($option);
242
+			}
243
+		}
244
+	}
245
+
246
+
247
+
248
+	/**
249
+	 *    _set_label_size_class
250
+	 *
251
+	 * @param int|string $value
252
+	 * @return void
253
+	 */
254
+	private function _set_label_size($value = '')
255
+	{
256
+		// don't change label size if it has already been set and is being enforced
257
+		if($this->_enforce_label_size && $this->_label_size >  0) {
258
+			return;
259
+		}
260
+		// determine length of option value
261
+		$val_size = is_int($value) ? $value : strlen($value);
262
+		// use new value if bigger than existing
263
+		$this->_label_size = $val_size > $this->_label_size ? $val_size : $this->_label_size;
264
+	}
265
+
266
+
267
+
268
+	/**
269
+	 *    get_label_size_class
270
+	 *
271
+	 * @return string
272
+	 */
273
+	public function get_label_size_class()
274
+	{
275
+		$size = ' medium-lbl';
276
+		// use maximum option value length to determine label size
277
+		if ($this->_label_size < 3) {
278
+			$size = ' nano-lbl';
279
+		} else if ($this->_label_size < 6) {
280
+			$size = ' micro-lbl';
281
+		} else if ($this->_label_size < 12) {
282
+			$size = ' tiny-lbl';
283
+		} else if ($this->_label_size < 25) {
284
+			$size = ' small-lbl';
285
+		} else if ($this->_label_size < 50) {
286
+			$size = ' medium-lbl';
287
+		} else if ($this->_label_size >= 100) {
288
+			$size = ' big-lbl';
289
+		}
290
+		return $size;
291
+	}
292
+
293
+
294
+
295
+	/**
296
+	 * Returns the pretty value for the normalized value
297
+	 *
298
+	 * @return string
299
+	 */
300
+	public function pretty_value()
301
+	{
302
+		$options = $this->flat_options();
303
+		$unnormalized_value_choices = $this->get_normalization_strategy()->unnormalize($this->_normalized_value);
304
+		if ( ! $this->_multiple_selections) {
305
+			$unnormalized_value_choices = array($unnormalized_value_choices);
306
+		}
307
+		$pretty_strings = array();
308
+		foreach ((array)$unnormalized_value_choices as $unnormalized_value_choice) {
309
+			if (isset($options[$unnormalized_value_choice])) {
310
+				$pretty_strings[] = $options[$unnormalized_value_choice];
311
+			} else {
312
+				$pretty_strings[] = $this->normalized_value();
313
+			}
314
+		}
315
+		return implode(', ', $pretty_strings);
316
+	}
317
+
318
+
319
+
320
+	/**
321
+	 * @return boolean
322
+	 */
323
+	public function display_html_label_text()
324
+	{
325
+		return $this->_display_html_label_text;
326
+	}
327
+
328
+
329
+
330
+	/**
331
+	 * @param boolean $display_html_label_text
332
+	 */
333
+	public function set_display_html_label_text($display_html_label_text)
334
+	{
335
+		$this->_display_html_label_text = filter_var($display_html_label_text, FILTER_VALIDATE_BOOLEAN);
336
+	}
337 337
 
338 338
 
339 339
 
Please login to merge, or discard this patch.
Spacing   +8 added lines, -8 removed lines patch added patch discarded remove patch
@@ -119,12 +119,12 @@  discard block
 block discarded – undo
119 119
             $all_ints = true;
120 120
             array_walk_recursive(
121 121
                 $this->_options,
122
-                function($value,$key) use (&$all_ints){
122
+                function($value, $key) use (&$all_ints){
123 123
                     //is this a top-level key? ignore it
124
-                    if(! is_array($value)
124
+                    if ( ! is_array($value)
125 125
                         && ! is_int($key)
126 126
                        && $key !== ''
127
-                       && $key !== null){
127
+                       && $key !== null) {
128 128
                         $all_ints = false;
129 129
                     }
130 130
                 }
@@ -178,7 +178,7 @@  discard block
 block discarded – undo
178 178
         $flat_array = array();
179 179
         if (EEH_Array::is_multi_dimensional_array($arr)) {
180 180
             foreach ($arr as $sub_array) {
181
-                foreach ((array)$sub_array as $key => $value) {
181
+                foreach ((array) $sub_array as $key => $value) {
182 182
                     $flat_array[$key] = $value;
183 183
                     $this->_set_label_size($value);
184 184
                 }
@@ -206,14 +206,14 @@  discard block
 block discarded – undo
206 206
                 $desc = '';
207 207
                 if ($this->_use_desc_in_label) {
208 208
                     $desc = $question_option->desc();
209
-                    $desc = ! empty($desc) ? '<span class="ee-question-option-desc">' . $desc . '</span>' : '';
209
+                    $desc = ! empty($desc) ? '<span class="ee-question-option-desc">'.$desc.'</span>' : '';
210 210
                 }
211 211
                 $value = $question_option->value();
212 212
                 // add value even if it's empty
213 213
                 $flat_array[$value] = $value;
214 214
                 // if both value and desc are not empty, then separate with a dash
215 215
                 if ( ! empty($value) && ! empty($desc)) {
216
-                    $flat_array[$value] .= ' - ' . $desc;
216
+                    $flat_array[$value] .= ' - '.$desc;
217 217
                 } else {
218 218
                     // otherwise, just add desc, since either or both of the vars is empty, and no dash is necessary
219 219
                     $flat_array[$value] .= $desc;
@@ -254,7 +254,7 @@  discard block
 block discarded – undo
254 254
     private function _set_label_size($value = '')
255 255
     {
256 256
         // don't change label size if it has already been set and is being enforced
257
-        if($this->_enforce_label_size && $this->_label_size >  0) {
257
+        if ($this->_enforce_label_size && $this->_label_size > 0) {
258 258
             return;
259 259
         }
260 260
         // determine length of option value
@@ -305,7 +305,7 @@  discard block
 block discarded – undo
305 305
             $unnormalized_value_choices = array($unnormalized_value_choices);
306 306
         }
307 307
         $pretty_strings = array();
308
-        foreach ((array)$unnormalized_value_choices as $unnormalized_value_choice) {
308
+        foreach ((array) $unnormalized_value_choices as $unnormalized_value_choice) {
309 309
             if (isset($options[$unnormalized_value_choice])) {
310 310
                 $pretty_strings[] = $options[$unnormalized_value_choice];
311 311
             } else {
Please login to merge, or discard this patch.
core/libraries/batch/JobHandlers/DatetimeOffsetFix.php 2 patches
Spacing   +11 added lines, -11 removed lines patch added patch discarded remove patch
@@ -158,19 +158,19 @@  discard block
 block discarded – undo
158 158
         $date_ranges = array();
159 159
         //since some affected models might have two tables, we have to get our tables and set up a query for each table.
160 160
         foreach ($model->get_tables() as $table) {
161
-            $query = 'UPDATE ' . $table->get_table_name();
161
+            $query = 'UPDATE '.$table->get_table_name();
162 162
             $fields_affected = array();
163 163
             $inner_query = array();
164 164
             foreach ($model->_get_fields_for_table($table->get_table_alias()) as $model_field) {
165 165
                 if ($model_field instanceof EE_Datetime_Field) {
166
-                    $inner_query[$model_field->get_table_column()] = $model_field->get_table_column() . ' = '
167
-                                     . $sql_date_function . '('
166
+                    $inner_query[$model_field->get_table_column()] = $model_field->get_table_column().' = '
167
+                                     . $sql_date_function.'('
168 168
                                      . $model_field->get_table_column()
169 169
                                      . ", INTERVAL {$offset} MINUTE)";
170 170
                     $fields_affected[] = $model_field;
171 171
                 }
172 172
             }
173
-            if (! $fields_affected) {
173
+            if ( ! $fields_affected) {
174 174
                 continue;
175 175
             }
176 176
             //do we do one query per column/field or one query for all fields on the model? It all depends on whether
@@ -187,7 +187,7 @@  discard block
 block discarded – undo
187 187
                     //record error.
188 188
                     $error_message = $wpdb->last_error;
189 189
                     //handle the edgecases where last_error might be empty.
190
-                    if (! $error_message) {
190
+                    if ( ! $error_message) {
191 191
                         $error_message = esc_html__('Unknown mysql error occured.', 'event_espresso');
192 192
                     }
193 193
                     $this->recordChangeLog($model, $original_offset, $table, $fields_affected, $error_message);
@@ -220,7 +220,7 @@  discard block
 block discarded – undo
220 220
         foreach ($inner_query as $field_name => $field_query) {
221 221
             $query_to_run = $query;
222 222
             $where_conditions = array();
223
-            $query_to_run .= ' SET ' . $field_query;
223
+            $query_to_run .= ' SET '.$field_query;
224 224
             if ($start_date_range instanceof DbSafeDateTime) {
225 225
                 $start_date = $start_date_range->format(EE_Datetime_Field::mysql_timestamp_format);
226 226
                 $where_conditions[] = "{$field_name} > '{$start_date}'";
@@ -230,14 +230,14 @@  discard block
 block discarded – undo
230 230
                 $where_conditions[] = "{$field_name} < '{$end_date}'";
231 231
             }
232 232
             if ($where_conditions) {
233
-                $query_to_run .= ' WHERE ' . implode(' AND ', $where_conditions);
233
+                $query_to_run .= ' WHERE '.implode(' AND ', $where_conditions);
234 234
             }
235 235
             $result = $wpdb->query($query_to_run);
236 236
             if ($result === false) {
237 237
                 //record error.
238 238
                 $error_message = $wpdb->last_error;
239 239
                 //handle the edgecases where last_error might be empty.
240
-                if (! $error_message) {
240
+                if ( ! $error_message) {
241 241
                     $error_message = esc_html__('Unknown mysql error occured.', 'event_espresso');
242 242
                 }
243 243
                 $errors[$field_name] = $error_message;
@@ -257,7 +257,7 @@  discard block
 block discarded – undo
257 257
     private function doQueryForAllFields($query, array $inner_query)
258 258
     {
259 259
         global $wpdb;
260
-        $query .= ' SET ' . implode(',', $inner_query);
260
+        $query .= ' SET '.implode(',', $inner_query);
261 261
         return $wpdb->query($query);
262 262
     }
263 263
 
@@ -283,7 +283,7 @@  discard block
 block discarded – undo
283 283
         $fields = array();
284 284
         /** @var EE_Datetime_Field $model_field */
285 285
         foreach ($model_fields_affected as $model_field) {
286
-            if (! $model_field instanceof EE_Datetime_Field) {
286
+            if ( ! $model_field instanceof EE_Datetime_Field) {
287 287
                 continue;
288 288
             }
289 289
             $fields[] = $model_field->get_name();
@@ -335,7 +335,7 @@  discard block
 block discarded – undo
335 335
     private function getModelsWithDatetimeFields()
336 336
     {
337 337
         $this->getModelsToProcess();
338
-        if (! empty($this->models_with_datetime_fields)) {
338
+        if ( ! empty($this->models_with_datetime_fields)) {
339 339
             return $this->models_with_datetime_fields;
340 340
         }
341 341
 
Please login to merge, or discard this patch.
Indentation   +458 added lines, -458 removed lines patch added patch discarded remove patch
@@ -26,462 +26,462 @@
 block discarded – undo
26 26
 class DatetimeOffsetFix extends JobHandler
27 27
 {
28 28
 
29
-    /**
30
-     * Key for the option used to track which models have been processed when doing the batches.
31
-     */
32
-    const MODELS_TO_PROCESS_OPTION_KEY = 'ee_models_processed_for_datetime_offset_fix';
33
-
34
-
35
-    const COUNT_OF_MODELS_PROCESSED = 'ee_count_of_ee_models_processed_for_datetime_offset_fixed';
36
-
37
-    /**
38
-     * Key for the option used to track what the current offset is that will be applied when this tool is executed.
39
-     */
40
-    const OFFSET_TO_APPLY_OPTION_KEY = 'ee_datetime_offset_fix_offset_to_apply';
41
-
42
-
43
-    const OPTION_KEY_OFFSET_RANGE_START_DATE = 'ee_datetime_offset_start_date_range';
44
-
45
-
46
-    const OPTION_KEY_OFFSET_RANGE_END_DATE = 'ee_datetime_offset_end_date_range';
47
-
48
-
49
-    /**
50
-     * String labelling the datetime offset fix type for change-log entries.
51
-     */
52
-    const DATETIME_OFFSET_FIX_CHANGELOG_TYPE = 'datetime_offset_fix';
53
-
54
-
55
-    /**
56
-     * String labelling a datetime offset fix error for change-log entries.
57
-     */
58
-    const DATETIME_OFFSET_FIX_CHANGELOG_ERROR_TYPE = 'datetime_offset_fix_error';
59
-
60
-    /**
61
-     * @var EEM_Base[]
62
-     */
63
-    protected $models_with_datetime_fields = array();
64
-
65
-
66
-    /**
67
-     * Performs any necessary setup for starting the job. This is also a good
68
-     * place to setup the $job_arguments which will be used for subsequent HTTP requests
69
-     * when continue_job will be called
70
-     *
71
-     * @param JobParameters $job_parameters
72
-     * @return JobStepResponse
73
-     * @throws EE_Error
74
-     * @throws InvalidArgumentException
75
-     * @throws InvalidDataTypeException
76
-     * @throws InvalidInterfaceException
77
-     */
78
-    public function create_job(JobParameters $job_parameters)
79
-    {
80
-        $models_with_datetime_fields = $this->getModelsWithDatetimeFields();
81
-        //we'll be doing each model as a batch.
82
-        $job_parameters->set_job_size(count($models_with_datetime_fields));
83
-        return new JobStepResponse(
84
-            $job_parameters,
85
-            esc_html__('Starting Datetime Offset Fix', 'event_espresso')
86
-        );
87
-    }
88
-
89
-    /**
90
-     * Performs another step of the job
91
-     *
92
-     * @param JobParameters $job_parameters
93
-     * @param int           $batch_size
94
-     * @return JobStepResponse
95
-     * @throws EE_Error
96
-     * @throws InvalidArgumentException
97
-     * @throws InvalidDataTypeException
98
-     * @throws InvalidInterfaceException
99
-     */
100
-    public function continue_job(JobParameters $job_parameters, $batch_size = 50)
101
-    {
102
-        $models_to_process = $this->getModelsWithDatetimeFields();
103
-        //let's pop off the a model and do the query to apply the offset.
104
-        $model_to_process = array_pop($models_to_process);
105
-        //update our record
106
-        $this->setModelsToProcess($models_to_process);
107
-        $this->processModel($model_to_process);
108
-        $this->updateCountOfModelsProcessed();
109
-        $job_parameters->set_units_processed($this->getCountOfModelsProcessed());
110
-        if (count($models_to_process) > 0) {
111
-            $job_parameters->set_status(JobParameters::status_continue);
112
-        } else {
113
-            $job_parameters->set_status(JobParameters::status_complete);
114
-        }
115
-        return new JobStepResponse(
116
-            $job_parameters,
117
-            sprintf(
118
-                esc_html__('Updated the offset for all datetime fields on the %s model.', 'event_espresso'),
119
-                $model_to_process
120
-            )
121
-        );
122
-    }
123
-
124
-    /**
125
-     * Performs any clean-up logic when we know the job is completed
126
-     *
127
-     * @param JobParameters $job_parameters
128
-     * @return JobStepResponse
129
-     * @throws BatchRequestException
130
-     */
131
-    public function cleanup_job(JobParameters $job_parameters)
132
-    {
133
-        //delete important saved options.
134
-        delete_option(self::MODELS_TO_PROCESS_OPTION_KEY);
135
-        delete_option(self::COUNT_OF_MODELS_PROCESSED);
136
-        delete_option(self::OPTION_KEY_OFFSET_RANGE_START_DATE);
137
-        delete_option(self::OPTION_KEY_OFFSET_RANGE_END_DATE);
138
-        return new JobStepResponse($job_parameters, esc_html__(
139
-            'Offset has been applied to all affected fields.',
140
-            'event_espresso'
141
-        ));
142
-    }
143
-
144
-
145
-    /**
146
-     * Contains the logic for processing a model and applying the datetime offset to affected fields on that model.
147
-     * @param string $model_class_name
148
-     * @throws EE_Error
149
-     */
150
-    protected function processModel($model_class_name)
151
-    {
152
-        global $wpdb;
153
-        /** @var EEM_Base $model */
154
-        $model = $model_class_name::instance();
155
-        $original_offset = self::getOffset();
156
-        $start_date_range = self::getStartDateRange();
157
-        $end_date_range = self::getEndDateRange();
158
-        $sql_date_function = $original_offset > 0 ? 'DATE_ADD' : 'DATE_SUB';
159
-        $offset = abs($original_offset) * 60;
160
-        $date_ranges = array();
161
-        //since some affected models might have two tables, we have to get our tables and set up a query for each table.
162
-        foreach ($model->get_tables() as $table) {
163
-            $query = 'UPDATE ' . $table->get_table_name();
164
-            $fields_affected = array();
165
-            $inner_query = array();
166
-            foreach ($model->_get_fields_for_table($table->get_table_alias()) as $model_field) {
167
-                if ($model_field instanceof EE_Datetime_Field) {
168
-                    $inner_query[$model_field->get_table_column()] = $model_field->get_table_column() . ' = '
169
-                                     . $sql_date_function . '('
170
-                                     . $model_field->get_table_column()
171
-                                     . ", INTERVAL {$offset} MINUTE)";
172
-                    $fields_affected[] = $model_field;
173
-                }
174
-            }
175
-            if (! $fields_affected) {
176
-                continue;
177
-            }
178
-            //do we do one query per column/field or one query for all fields on the model? It all depends on whether
179
-            //there is a date range applied or not.
180
-            if ($start_date_range instanceof DbSafeDateTime || $end_date_range instanceof DbSafeDateTime) {
181
-                $result = $this->doQueryForEachField($query, $inner_query, $start_date_range, $end_date_range);
182
-            } else {
183
-                $result = $this->doQueryForAllFields($query, $inner_query);
184
-            }
185
-
186
-            //record appropriate logs for the query
187
-            switch (true) {
188
-                case $result === false:
189
-                    //record error.
190
-                    $error_message = $wpdb->last_error;
191
-                    //handle the edgecases where last_error might be empty.
192
-                    if (! $error_message) {
193
-                        $error_message = esc_html__('Unknown mysql error occured.', 'event_espresso');
194
-                    }
195
-                    $this->recordChangeLog($model, $original_offset, $table, $fields_affected, $error_message);
196
-                    break;
197
-                case is_array($result) && ! empty($result):
198
-                    foreach ($result as $field_name => $error_message) {
199
-                        $this->recordChangeLog($model, $original_offset, $table, array($field_name), $error_message);
200
-                    }
201
-                    break;
202
-                default:
203
-                    $this->recordChangeLog($model, $original_offset, $table, $fields_affected);
204
-            }
205
-        }
206
-    }
207
-
208
-
209
-    /**
210
-     * Does the query on each $inner_query individually.
211
-     *
212
-     * @param string              $query
213
-     * @param array               $inner_query
214
-     * @param DbSafeDateTime|null $start_date_range
215
-     * @param DbSafeDateTime|null $end_date_range
216
-     * @return array  An array of any errors encountered and the fields they were for.
217
-     */
218
-    private function doQueryForEachField($query, array $inner_query, $start_date_range, $end_date_range)
219
-    {
220
-        global $wpdb;
221
-        $errors = array();
222
-        foreach ($inner_query as $field_name => $field_query) {
223
-            $query_to_run = $query;
224
-            $where_conditions = array();
225
-            $query_to_run .= ' SET ' . $field_query;
226
-            if ($start_date_range instanceof DbSafeDateTime) {
227
-                $start_date = $start_date_range->format(EE_Datetime_Field::mysql_timestamp_format);
228
-                $where_conditions[] = "{$field_name} > '{$start_date}'";
229
-            }
230
-            if ($end_date_range instanceof DbSafeDateTime) {
231
-                $end_date = $end_date_range->format(EE_Datetime_Field::mysql_timestamp_format);
232
-                $where_conditions[] = "{$field_name} < '{$end_date}'";
233
-            }
234
-            if ($where_conditions) {
235
-                $query_to_run .= ' WHERE ' . implode(' AND ', $where_conditions);
236
-            }
237
-            $result = $wpdb->query($query_to_run);
238
-            if ($result === false) {
239
-                //record error.
240
-                $error_message = $wpdb->last_error;
241
-                //handle the edgecases where last_error might be empty.
242
-                if (! $error_message) {
243
-                    $error_message = esc_html__('Unknown mysql error occured.', 'event_espresso');
244
-                }
245
-                $errors[$field_name] = $error_message;
246
-            }
247
-        }
248
-        return $errors;
249
-    }
250
-
251
-
252
-    /**
253
-     * Performs the query for all fields within the inner_query
254
-     *
255
-     * @param string $query
256
-     * @param array  $inner_query
257
-     * @return false|int
258
-     */
259
-    private function doQueryForAllFields($query, array $inner_query)
260
-    {
261
-        global $wpdb;
262
-        $query .= ' SET ' . implode(',', $inner_query);
263
-        return $wpdb->query($query);
264
-    }
265
-
266
-
267
-    /**
268
-     * Records a changelog entry using the given information.
269
-     *
270
-     * @param EEM_Base              $model
271
-     * @param float                 $offset
272
-     * @param EE_Table_Base         $table
273
-     * @param EE_Model_Field_Base[] $model_fields_affected
274
-     * @param string                $error_message   If present then there was an error so let's record that instead.
275
-     * @throws EE_Error
276
-     */
277
-    private function recordChangeLog(
278
-        EEM_Base $model,
279
-        $offset,
280
-        EE_Table_Base $table,
281
-        $model_fields_affected,
282
-        $error_message = ''
283
-    ) {
284
-        //setup $fields list.
285
-        $fields = array();
286
-        /** @var EE_Datetime_Field $model_field */
287
-        foreach ($model_fields_affected as $model_field) {
288
-            if (! $model_field instanceof EE_Datetime_Field) {
289
-                continue;
290
-            }
291
-            $fields[] = $model_field->get_name();
292
-        }
293
-        //setup the message for the changelog entry.
294
-        $message = $error_message
295
-            ? sprintf(
296
-                esc_html__(
297
-                    'The %1$s table for the %2$s model did not have the offset of %3$f applied to its fields (%4$s), because of the following error:%5$s',
298
-                    'event_espresso'
299
-                ),
300
-                $table->get_table_name(),
301
-                $model->get_this_model_name(),
302
-                $offset,
303
-                implode(',', $fields),
304
-                $error_message
305
-            )
306
-            : sprintf(
307
-                esc_html__(
308
-                    'The %1$s table for the %2$s model has had the offset of %3$f applied to its following fields: %4$s',
309
-                    'event_espresso'
310
-                ),
311
-                $table->get_table_name(),
312
-                $model->get_this_model_name(),
313
-                $offset,
314
-                implode(',', $fields)
315
-            );
316
-        //write to the log
317
-        $changelog = EE_Change_Log::new_instance(array(
318
-            'LOG_type' => $error_message
319
-                ? self::DATETIME_OFFSET_FIX_CHANGELOG_ERROR_TYPE
320
-                : self::DATETIME_OFFSET_FIX_CHANGELOG_TYPE,
321
-            'LOG_message' => $message
322
-        ));
323
-        $changelog->save();
324
-    }
325
-
326
-
327
-    /**
328
-     * Returns an array of models that have datetime fields.
329
-     * This array is added to a short lived transient cache to keep having to build this list to a minimum.
330
-     *
331
-     * @return array an array of model class names.
332
-     * @throws EE_Error
333
-     * @throws InvalidDataTypeException
334
-     * @throws InvalidInterfaceException
335
-     * @throws InvalidArgumentException
336
-     */
337
-    private function getModelsWithDatetimeFields()
338
-    {
339
-        $this->getModelsToProcess();
340
-        if (! empty($this->models_with_datetime_fields)) {
341
-            return $this->models_with_datetime_fields;
342
-        }
343
-
344
-        $all_non_abstract_models = EE_Registry::instance()->non_abstract_db_models;
345
-        foreach ($all_non_abstract_models as $non_abstract_model) {
346
-            //get model instance
347
-            /** @var EEM_Base $non_abstract_model */
348
-            $non_abstract_model = $non_abstract_model::instance();
349
-            if ($non_abstract_model->get_a_field_of_type('EE_Datetime_Field') instanceof EE_Datetime_Field) {
350
-                $this->models_with_datetime_fields[] = get_class($non_abstract_model);
351
-            }
352
-        }
353
-        $this->setModelsToProcess($this->models_with_datetime_fields);
354
-        return $this->models_with_datetime_fields;
355
-    }
356
-
357
-
358
-    /**
359
-     * This simply records the models that have been processed with our tracking option.
360
-     * @param array $models_to_set  array of model class names.
361
-     */
362
-    private function setModelsToProcess($models_to_set)
363
-    {
364
-        update_option(self::MODELS_TO_PROCESS_OPTION_KEY, $models_to_set);
365
-    }
366
-
367
-
368
-    /**
369
-     * Used to keep track of how many models have been processed for the batch
370
-     * @param $count
371
-     */
372
-    private function updateCountOfModelsProcessed($count = 1)
373
-    {
374
-        $count = $this->getCountOfModelsProcessed() + (int) $count;
375
-        update_option(self::COUNT_OF_MODELS_PROCESSED, $count);
376
-    }
377
-
378
-
379
-    /**
380
-     * Retrieve the tracked number of models processed between requests.
381
-     * @return int
382
-     */
383
-    private function getCountOfModelsProcessed()
384
-    {
385
-        return (int) get_option(self::COUNT_OF_MODELS_PROCESSED, 0);
386
-    }
387
-
388
-
389
-    /**
390
-     * Returns the models that are left to process.
391
-     * @return array  an array of model class names.
392
-     */
393
-    private function getModelsToProcess()
394
-    {
395
-        if (empty($this->models_with_datetime_fields)) {
396
-            $this->models_with_datetime_fields = get_option(self::MODELS_TO_PROCESS_OPTION_KEY, array());
397
-        }
398
-        return $this->models_with_datetime_fields;
399
-    }
400
-
401
-
402
-    /**
403
-     * Used to record the offset that will be applied to dates and times for EE_Datetime_Field columns.
404
-     * @param float $offset
405
-     */
406
-    public static function updateOffset($offset)
407
-    {
408
-        update_option(self::OFFSET_TO_APPLY_OPTION_KEY, $offset);
409
-    }
410
-
411
-
412
-    /**
413
-     * Used to retrieve the saved offset that will be applied to dates and times for EE_Datetime_Field columns.
414
-     *
415
-     * @return float
416
-     */
417
-    public static function getOffset()
418
-    {
419
-        return (float) get_option(self::OFFSET_TO_APPLY_OPTION_KEY, 0);
420
-    }
421
-
422
-
423
-    /**
424
-     * Used to set the saved offset range start date.
425
-     * @param DbSafeDateTime|null $start_date
426
-     */
427
-    public static function updateStartDateRange(DbSafeDateTime $start_date = null)
428
-    {
429
-        $date_to_save = $start_date instanceof DbSafeDateTime
430
-            ? $start_date->format('U')
431
-            : '';
432
-        update_option(self::OPTION_KEY_OFFSET_RANGE_START_DATE, $date_to_save);
433
-    }
434
-
435
-
436
-    /**
437
-     * Used to get the saved offset range start date.
438
-     * @return DbSafeDateTime|null
439
-     */
440
-    public static function getStartDateRange()
441
-    {
442
-        $start_date = get_option(self::OPTION_KEY_OFFSET_RANGE_START_DATE, null);
443
-        try {
444
-            $datetime = DateTime::createFromFormat('U', $start_date, new DateTimeZone('UTC'));
445
-            $start_date = $datetime instanceof DateTime
446
-                ? DbSafeDateTime::createFromDateTime($datetime)
447
-                : null;
448
-
449
-        } catch (Exception $e) {
450
-            $start_date = null;
451
-        }
452
-        return $start_date;
453
-    }
454
-
455
-
456
-
457
-    /**
458
-     * Used to set the saved offset range end date.
459
-     * @param DbSafeDateTime|null $end_date
460
-     */
461
-    public static function updateEndDateRange(DbSafeDateTime $end_date = null)
462
-    {
463
-        $date_to_save = $end_date instanceof DbSafeDateTime
464
-            ? $end_date->format('U')
465
-            : '';
466
-        update_option(self::OPTION_KEY_OFFSET_RANGE_END_DATE, $date_to_save);
467
-    }
468
-
469
-
470
-    /**
471
-     * Used to get the saved offset range end date.
472
-     * @return DbSafeDateTime|null
473
-     */
474
-    public static function getEndDateRange()
475
-    {
476
-        $end_date = get_option(self::OPTION_KEY_OFFSET_RANGE_END_DATE, null);
477
-        try {
478
-            $datetime = DateTime::createFromFormat('U', $end_date, new DateTimeZone('UTC'));
479
-            $end_date = $datetime instanceof Datetime
480
-                ? DbSafeDateTime::createFromDateTime($datetime)
481
-                : null;
482
-        } catch (Exception $e) {
483
-            $end_date = null;
484
-        }
485
-        return $end_date;
486
-    }
29
+	/**
30
+	 * Key for the option used to track which models have been processed when doing the batches.
31
+	 */
32
+	const MODELS_TO_PROCESS_OPTION_KEY = 'ee_models_processed_for_datetime_offset_fix';
33
+
34
+
35
+	const COUNT_OF_MODELS_PROCESSED = 'ee_count_of_ee_models_processed_for_datetime_offset_fixed';
36
+
37
+	/**
38
+	 * Key for the option used to track what the current offset is that will be applied when this tool is executed.
39
+	 */
40
+	const OFFSET_TO_APPLY_OPTION_KEY = 'ee_datetime_offset_fix_offset_to_apply';
41
+
42
+
43
+	const OPTION_KEY_OFFSET_RANGE_START_DATE = 'ee_datetime_offset_start_date_range';
44
+
45
+
46
+	const OPTION_KEY_OFFSET_RANGE_END_DATE = 'ee_datetime_offset_end_date_range';
47
+
48
+
49
+	/**
50
+	 * String labelling the datetime offset fix type for change-log entries.
51
+	 */
52
+	const DATETIME_OFFSET_FIX_CHANGELOG_TYPE = 'datetime_offset_fix';
53
+
54
+
55
+	/**
56
+	 * String labelling a datetime offset fix error for change-log entries.
57
+	 */
58
+	const DATETIME_OFFSET_FIX_CHANGELOG_ERROR_TYPE = 'datetime_offset_fix_error';
59
+
60
+	/**
61
+	 * @var EEM_Base[]
62
+	 */
63
+	protected $models_with_datetime_fields = array();
64
+
65
+
66
+	/**
67
+	 * Performs any necessary setup for starting the job. This is also a good
68
+	 * place to setup the $job_arguments which will be used for subsequent HTTP requests
69
+	 * when continue_job will be called
70
+	 *
71
+	 * @param JobParameters $job_parameters
72
+	 * @return JobStepResponse
73
+	 * @throws EE_Error
74
+	 * @throws InvalidArgumentException
75
+	 * @throws InvalidDataTypeException
76
+	 * @throws InvalidInterfaceException
77
+	 */
78
+	public function create_job(JobParameters $job_parameters)
79
+	{
80
+		$models_with_datetime_fields = $this->getModelsWithDatetimeFields();
81
+		//we'll be doing each model as a batch.
82
+		$job_parameters->set_job_size(count($models_with_datetime_fields));
83
+		return new JobStepResponse(
84
+			$job_parameters,
85
+			esc_html__('Starting Datetime Offset Fix', 'event_espresso')
86
+		);
87
+	}
88
+
89
+	/**
90
+	 * Performs another step of the job
91
+	 *
92
+	 * @param JobParameters $job_parameters
93
+	 * @param int           $batch_size
94
+	 * @return JobStepResponse
95
+	 * @throws EE_Error
96
+	 * @throws InvalidArgumentException
97
+	 * @throws InvalidDataTypeException
98
+	 * @throws InvalidInterfaceException
99
+	 */
100
+	public function continue_job(JobParameters $job_parameters, $batch_size = 50)
101
+	{
102
+		$models_to_process = $this->getModelsWithDatetimeFields();
103
+		//let's pop off the a model and do the query to apply the offset.
104
+		$model_to_process = array_pop($models_to_process);
105
+		//update our record
106
+		$this->setModelsToProcess($models_to_process);
107
+		$this->processModel($model_to_process);
108
+		$this->updateCountOfModelsProcessed();
109
+		$job_parameters->set_units_processed($this->getCountOfModelsProcessed());
110
+		if (count($models_to_process) > 0) {
111
+			$job_parameters->set_status(JobParameters::status_continue);
112
+		} else {
113
+			$job_parameters->set_status(JobParameters::status_complete);
114
+		}
115
+		return new JobStepResponse(
116
+			$job_parameters,
117
+			sprintf(
118
+				esc_html__('Updated the offset for all datetime fields on the %s model.', 'event_espresso'),
119
+				$model_to_process
120
+			)
121
+		);
122
+	}
123
+
124
+	/**
125
+	 * Performs any clean-up logic when we know the job is completed
126
+	 *
127
+	 * @param JobParameters $job_parameters
128
+	 * @return JobStepResponse
129
+	 * @throws BatchRequestException
130
+	 */
131
+	public function cleanup_job(JobParameters $job_parameters)
132
+	{
133
+		//delete important saved options.
134
+		delete_option(self::MODELS_TO_PROCESS_OPTION_KEY);
135
+		delete_option(self::COUNT_OF_MODELS_PROCESSED);
136
+		delete_option(self::OPTION_KEY_OFFSET_RANGE_START_DATE);
137
+		delete_option(self::OPTION_KEY_OFFSET_RANGE_END_DATE);
138
+		return new JobStepResponse($job_parameters, esc_html__(
139
+			'Offset has been applied to all affected fields.',
140
+			'event_espresso'
141
+		));
142
+	}
143
+
144
+
145
+	/**
146
+	 * Contains the logic for processing a model and applying the datetime offset to affected fields on that model.
147
+	 * @param string $model_class_name
148
+	 * @throws EE_Error
149
+	 */
150
+	protected function processModel($model_class_name)
151
+	{
152
+		global $wpdb;
153
+		/** @var EEM_Base $model */
154
+		$model = $model_class_name::instance();
155
+		$original_offset = self::getOffset();
156
+		$start_date_range = self::getStartDateRange();
157
+		$end_date_range = self::getEndDateRange();
158
+		$sql_date_function = $original_offset > 0 ? 'DATE_ADD' : 'DATE_SUB';
159
+		$offset = abs($original_offset) * 60;
160
+		$date_ranges = array();
161
+		//since some affected models might have two tables, we have to get our tables and set up a query for each table.
162
+		foreach ($model->get_tables() as $table) {
163
+			$query = 'UPDATE ' . $table->get_table_name();
164
+			$fields_affected = array();
165
+			$inner_query = array();
166
+			foreach ($model->_get_fields_for_table($table->get_table_alias()) as $model_field) {
167
+				if ($model_field instanceof EE_Datetime_Field) {
168
+					$inner_query[$model_field->get_table_column()] = $model_field->get_table_column() . ' = '
169
+									 . $sql_date_function . '('
170
+									 . $model_field->get_table_column()
171
+									 . ", INTERVAL {$offset} MINUTE)";
172
+					$fields_affected[] = $model_field;
173
+				}
174
+			}
175
+			if (! $fields_affected) {
176
+				continue;
177
+			}
178
+			//do we do one query per column/field or one query for all fields on the model? It all depends on whether
179
+			//there is a date range applied or not.
180
+			if ($start_date_range instanceof DbSafeDateTime || $end_date_range instanceof DbSafeDateTime) {
181
+				$result = $this->doQueryForEachField($query, $inner_query, $start_date_range, $end_date_range);
182
+			} else {
183
+				$result = $this->doQueryForAllFields($query, $inner_query);
184
+			}
185
+
186
+			//record appropriate logs for the query
187
+			switch (true) {
188
+				case $result === false:
189
+					//record error.
190
+					$error_message = $wpdb->last_error;
191
+					//handle the edgecases where last_error might be empty.
192
+					if (! $error_message) {
193
+						$error_message = esc_html__('Unknown mysql error occured.', 'event_espresso');
194
+					}
195
+					$this->recordChangeLog($model, $original_offset, $table, $fields_affected, $error_message);
196
+					break;
197
+				case is_array($result) && ! empty($result):
198
+					foreach ($result as $field_name => $error_message) {
199
+						$this->recordChangeLog($model, $original_offset, $table, array($field_name), $error_message);
200
+					}
201
+					break;
202
+				default:
203
+					$this->recordChangeLog($model, $original_offset, $table, $fields_affected);
204
+			}
205
+		}
206
+	}
207
+
208
+
209
+	/**
210
+	 * Does the query on each $inner_query individually.
211
+	 *
212
+	 * @param string              $query
213
+	 * @param array               $inner_query
214
+	 * @param DbSafeDateTime|null $start_date_range
215
+	 * @param DbSafeDateTime|null $end_date_range
216
+	 * @return array  An array of any errors encountered and the fields they were for.
217
+	 */
218
+	private function doQueryForEachField($query, array $inner_query, $start_date_range, $end_date_range)
219
+	{
220
+		global $wpdb;
221
+		$errors = array();
222
+		foreach ($inner_query as $field_name => $field_query) {
223
+			$query_to_run = $query;
224
+			$where_conditions = array();
225
+			$query_to_run .= ' SET ' . $field_query;
226
+			if ($start_date_range instanceof DbSafeDateTime) {
227
+				$start_date = $start_date_range->format(EE_Datetime_Field::mysql_timestamp_format);
228
+				$where_conditions[] = "{$field_name} > '{$start_date}'";
229
+			}
230
+			if ($end_date_range instanceof DbSafeDateTime) {
231
+				$end_date = $end_date_range->format(EE_Datetime_Field::mysql_timestamp_format);
232
+				$where_conditions[] = "{$field_name} < '{$end_date}'";
233
+			}
234
+			if ($where_conditions) {
235
+				$query_to_run .= ' WHERE ' . implode(' AND ', $where_conditions);
236
+			}
237
+			$result = $wpdb->query($query_to_run);
238
+			if ($result === false) {
239
+				//record error.
240
+				$error_message = $wpdb->last_error;
241
+				//handle the edgecases where last_error might be empty.
242
+				if (! $error_message) {
243
+					$error_message = esc_html__('Unknown mysql error occured.', 'event_espresso');
244
+				}
245
+				$errors[$field_name] = $error_message;
246
+			}
247
+		}
248
+		return $errors;
249
+	}
250
+
251
+
252
+	/**
253
+	 * Performs the query for all fields within the inner_query
254
+	 *
255
+	 * @param string $query
256
+	 * @param array  $inner_query
257
+	 * @return false|int
258
+	 */
259
+	private function doQueryForAllFields($query, array $inner_query)
260
+	{
261
+		global $wpdb;
262
+		$query .= ' SET ' . implode(',', $inner_query);
263
+		return $wpdb->query($query);
264
+	}
265
+
266
+
267
+	/**
268
+	 * Records a changelog entry using the given information.
269
+	 *
270
+	 * @param EEM_Base              $model
271
+	 * @param float                 $offset
272
+	 * @param EE_Table_Base         $table
273
+	 * @param EE_Model_Field_Base[] $model_fields_affected
274
+	 * @param string                $error_message   If present then there was an error so let's record that instead.
275
+	 * @throws EE_Error
276
+	 */
277
+	private function recordChangeLog(
278
+		EEM_Base $model,
279
+		$offset,
280
+		EE_Table_Base $table,
281
+		$model_fields_affected,
282
+		$error_message = ''
283
+	) {
284
+		//setup $fields list.
285
+		$fields = array();
286
+		/** @var EE_Datetime_Field $model_field */
287
+		foreach ($model_fields_affected as $model_field) {
288
+			if (! $model_field instanceof EE_Datetime_Field) {
289
+				continue;
290
+			}
291
+			$fields[] = $model_field->get_name();
292
+		}
293
+		//setup the message for the changelog entry.
294
+		$message = $error_message
295
+			? sprintf(
296
+				esc_html__(
297
+					'The %1$s table for the %2$s model did not have the offset of %3$f applied to its fields (%4$s), because of the following error:%5$s',
298
+					'event_espresso'
299
+				),
300
+				$table->get_table_name(),
301
+				$model->get_this_model_name(),
302
+				$offset,
303
+				implode(',', $fields),
304
+				$error_message
305
+			)
306
+			: sprintf(
307
+				esc_html__(
308
+					'The %1$s table for the %2$s model has had the offset of %3$f applied to its following fields: %4$s',
309
+					'event_espresso'
310
+				),
311
+				$table->get_table_name(),
312
+				$model->get_this_model_name(),
313
+				$offset,
314
+				implode(',', $fields)
315
+			);
316
+		//write to the log
317
+		$changelog = EE_Change_Log::new_instance(array(
318
+			'LOG_type' => $error_message
319
+				? self::DATETIME_OFFSET_FIX_CHANGELOG_ERROR_TYPE
320
+				: self::DATETIME_OFFSET_FIX_CHANGELOG_TYPE,
321
+			'LOG_message' => $message
322
+		));
323
+		$changelog->save();
324
+	}
325
+
326
+
327
+	/**
328
+	 * Returns an array of models that have datetime fields.
329
+	 * This array is added to a short lived transient cache to keep having to build this list to a minimum.
330
+	 *
331
+	 * @return array an array of model class names.
332
+	 * @throws EE_Error
333
+	 * @throws InvalidDataTypeException
334
+	 * @throws InvalidInterfaceException
335
+	 * @throws InvalidArgumentException
336
+	 */
337
+	private function getModelsWithDatetimeFields()
338
+	{
339
+		$this->getModelsToProcess();
340
+		if (! empty($this->models_with_datetime_fields)) {
341
+			return $this->models_with_datetime_fields;
342
+		}
343
+
344
+		$all_non_abstract_models = EE_Registry::instance()->non_abstract_db_models;
345
+		foreach ($all_non_abstract_models as $non_abstract_model) {
346
+			//get model instance
347
+			/** @var EEM_Base $non_abstract_model */
348
+			$non_abstract_model = $non_abstract_model::instance();
349
+			if ($non_abstract_model->get_a_field_of_type('EE_Datetime_Field') instanceof EE_Datetime_Field) {
350
+				$this->models_with_datetime_fields[] = get_class($non_abstract_model);
351
+			}
352
+		}
353
+		$this->setModelsToProcess($this->models_with_datetime_fields);
354
+		return $this->models_with_datetime_fields;
355
+	}
356
+
357
+
358
+	/**
359
+	 * This simply records the models that have been processed with our tracking option.
360
+	 * @param array $models_to_set  array of model class names.
361
+	 */
362
+	private function setModelsToProcess($models_to_set)
363
+	{
364
+		update_option(self::MODELS_TO_PROCESS_OPTION_KEY, $models_to_set);
365
+	}
366
+
367
+
368
+	/**
369
+	 * Used to keep track of how many models have been processed for the batch
370
+	 * @param $count
371
+	 */
372
+	private function updateCountOfModelsProcessed($count = 1)
373
+	{
374
+		$count = $this->getCountOfModelsProcessed() + (int) $count;
375
+		update_option(self::COUNT_OF_MODELS_PROCESSED, $count);
376
+	}
377
+
378
+
379
+	/**
380
+	 * Retrieve the tracked number of models processed between requests.
381
+	 * @return int
382
+	 */
383
+	private function getCountOfModelsProcessed()
384
+	{
385
+		return (int) get_option(self::COUNT_OF_MODELS_PROCESSED, 0);
386
+	}
387
+
388
+
389
+	/**
390
+	 * Returns the models that are left to process.
391
+	 * @return array  an array of model class names.
392
+	 */
393
+	private function getModelsToProcess()
394
+	{
395
+		if (empty($this->models_with_datetime_fields)) {
396
+			$this->models_with_datetime_fields = get_option(self::MODELS_TO_PROCESS_OPTION_KEY, array());
397
+		}
398
+		return $this->models_with_datetime_fields;
399
+	}
400
+
401
+
402
+	/**
403
+	 * Used to record the offset that will be applied to dates and times for EE_Datetime_Field columns.
404
+	 * @param float $offset
405
+	 */
406
+	public static function updateOffset($offset)
407
+	{
408
+		update_option(self::OFFSET_TO_APPLY_OPTION_KEY, $offset);
409
+	}
410
+
411
+
412
+	/**
413
+	 * Used to retrieve the saved offset that will be applied to dates and times for EE_Datetime_Field columns.
414
+	 *
415
+	 * @return float
416
+	 */
417
+	public static function getOffset()
418
+	{
419
+		return (float) get_option(self::OFFSET_TO_APPLY_OPTION_KEY, 0);
420
+	}
421
+
422
+
423
+	/**
424
+	 * Used to set the saved offset range start date.
425
+	 * @param DbSafeDateTime|null $start_date
426
+	 */
427
+	public static function updateStartDateRange(DbSafeDateTime $start_date = null)
428
+	{
429
+		$date_to_save = $start_date instanceof DbSafeDateTime
430
+			? $start_date->format('U')
431
+			: '';
432
+		update_option(self::OPTION_KEY_OFFSET_RANGE_START_DATE, $date_to_save);
433
+	}
434
+
435
+
436
+	/**
437
+	 * Used to get the saved offset range start date.
438
+	 * @return DbSafeDateTime|null
439
+	 */
440
+	public static function getStartDateRange()
441
+	{
442
+		$start_date = get_option(self::OPTION_KEY_OFFSET_RANGE_START_DATE, null);
443
+		try {
444
+			$datetime = DateTime::createFromFormat('U', $start_date, new DateTimeZone('UTC'));
445
+			$start_date = $datetime instanceof DateTime
446
+				? DbSafeDateTime::createFromDateTime($datetime)
447
+				: null;
448
+
449
+		} catch (Exception $e) {
450
+			$start_date = null;
451
+		}
452
+		return $start_date;
453
+	}
454
+
455
+
456
+
457
+	/**
458
+	 * Used to set the saved offset range end date.
459
+	 * @param DbSafeDateTime|null $end_date
460
+	 */
461
+	public static function updateEndDateRange(DbSafeDateTime $end_date = null)
462
+	{
463
+		$date_to_save = $end_date instanceof DbSafeDateTime
464
+			? $end_date->format('U')
465
+			: '';
466
+		update_option(self::OPTION_KEY_OFFSET_RANGE_END_DATE, $date_to_save);
467
+	}
468
+
469
+
470
+	/**
471
+	 * Used to get the saved offset range end date.
472
+	 * @return DbSafeDateTime|null
473
+	 */
474
+	public static function getEndDateRange()
475
+	{
476
+		$end_date = get_option(self::OPTION_KEY_OFFSET_RANGE_END_DATE, null);
477
+		try {
478
+			$datetime = DateTime::createFromFormat('U', $end_date, new DateTimeZone('UTC'));
479
+			$end_date = $datetime instanceof Datetime
480
+				? DbSafeDateTime::createFromDateTime($datetime)
481
+				: null;
482
+		} catch (Exception $e) {
483
+			$end_date = null;
484
+		}
485
+		return $end_date;
486
+	}
487 487
 }
Please login to merge, or discard this patch.
core/EE_Dependency_Map.core.php 1 patch
Indentation   +800 added lines, -800 removed lines patch added patch discarded remove patch
@@ -5,7 +5,7 @@  discard block
 block discarded – undo
5 5
 use EventEspresso\core\services\loaders\LoaderInterface;
6 6
 
7 7
 if (! defined('EVENT_ESPRESSO_VERSION')) {
8
-    exit('No direct script access allowed');
8
+	exit('No direct script access allowed');
9 9
 }
10 10
 
11 11
 
@@ -22,805 +22,805 @@  discard block
 block discarded – undo
22 22
 class EE_Dependency_Map
23 23
 {
24 24
 
25
-    /**
26
-     * This means that the requested class dependency is not present in the dependency map
27
-     */
28
-    const not_registered = 0;
29
-
30
-    /**
31
-     * This instructs class loaders to ALWAYS return a newly instantiated object for the requested class.
32
-     */
33
-    const load_new_object = 1;
34
-
35
-    /**
36
-     * This instructs class loaders to return a previously instantiated and cached object for the requested class.
37
-     * IF a previously instantiated object does not exist, a new one will be created and added to the cache.
38
-     */
39
-    const load_from_cache = 2;
40
-
41
-    /**
42
-     * When registering a dependency,
43
-     * this indicates to keep any existing dependencies that already exist,
44
-     * and simply discard any new dependencies declared in the incoming data
45
-     */
46
-    const KEEP_EXISTING_DEPENDENCIES = 0;
47
-
48
-    /**
49
-     * When registering a dependency,
50
-     * this indicates to overwrite any existing dependencies that already exist using the incoming data
51
-     */
52
-    const OVERWRITE_DEPENDENCIES = 1;
53
-
54
-
55
-
56
-    /**
57
-     * @type EE_Dependency_Map $_instance
58
-     */
59
-    protected static $_instance;
60
-
61
-    /**
62
-     * @type EE_Request $request
63
-     */
64
-    protected $_request;
65
-
66
-    /**
67
-     * @type EE_Response $response
68
-     */
69
-    protected $_response;
70
-
71
-    /**
72
-     * @type LoaderInterface $loader
73
-     */
74
-    protected $loader;
75
-
76
-    /**
77
-     * @type array $_dependency_map
78
-     */
79
-    protected $_dependency_map = array();
80
-
81
-    /**
82
-     * @type array $_class_loaders
83
-     */
84
-    protected $_class_loaders = array();
85
-
86
-    /**
87
-     * @type array $_aliases
88
-     */
89
-    protected $_aliases = array();
90
-
91
-
92
-
93
-    /**
94
-     * EE_Dependency_Map constructor.
95
-     *
96
-     * @param EE_Request  $request
97
-     * @param EE_Response $response
98
-     */
99
-    protected function __construct(EE_Request $request, EE_Response $response)
100
-    {
101
-        $this->_request = $request;
102
-        $this->_response = $response;
103
-        add_action('EE_Load_Espresso_Core__handle_request__initialize_core_loading', array($this, 'initialize'));
104
-        do_action('EE_Dependency_Map____construct');
105
-    }
106
-
107
-
108
-
109
-    /**
110
-     * @throws InvalidDataTypeException
111
-     * @throws InvalidInterfaceException
112
-     * @throws InvalidArgumentException
113
-     */
114
-    public function initialize()
115
-    {
116
-        $this->_register_core_dependencies();
117
-        $this->_register_core_class_loaders();
118
-        $this->_register_core_aliases();
119
-    }
120
-
121
-
122
-
123
-    /**
124
-     * @singleton method used to instantiate class object
125
-     * @access    public
126
-     * @param EE_Request  $request
127
-     * @param EE_Response $response
128
-     * @return EE_Dependency_Map
129
-     */
130
-    public static function instance(EE_Request $request = null, EE_Response $response = null)
131
-    {
132
-        // check if class object is instantiated, and instantiated properly
133
-        if (! self::$_instance instanceof EE_Dependency_Map) {
134
-            self::$_instance = new EE_Dependency_Map($request, $response);
135
-        }
136
-        return self::$_instance;
137
-    }
138
-
139
-
140
-
141
-    /**
142
-     * @param LoaderInterface $loader
143
-     */
144
-    public function setLoader(LoaderInterface $loader)
145
-    {
146
-        $this->loader = $loader;
147
-    }
148
-
149
-
150
-
151
-    /**
152
-     * @param string $class
153
-     * @param array  $dependencies
154
-     * @param int    $overwrite
155
-     * @return bool
156
-     */
157
-    public static function register_dependencies(
158
-        $class,
159
-        array $dependencies,
160
-        $overwrite = EE_Dependency_Map::KEEP_EXISTING_DEPENDENCIES
161
-    ) {
162
-        return self::$_instance->registerDependencies($class, $dependencies, $overwrite);
163
-    }
164
-
165
-
166
-
167
-    /**
168
-     * Assigns an array of class names and corresponding load sources (new or cached)
169
-     * to the class specified by the first parameter.
170
-     * IMPORTANT !!!
171
-     * The order of elements in the incoming $dependencies array MUST match
172
-     * the order of the constructor parameters for the class in question.
173
-     * This is especially important when overriding any existing dependencies that are registered.
174
-     * the third parameter controls whether any duplicate dependencies are overwritten or not.
175
-     *
176
-     * @param string $class
177
-     * @param array  $dependencies
178
-     * @param int    $overwrite
179
-     * @return bool
180
-     */
181
-    public function registerDependencies(
182
-        $class,
183
-        array $dependencies,
184
-        $overwrite = EE_Dependency_Map::KEEP_EXISTING_DEPENDENCIES
185
-    ) {
186
-        $class = trim($class, '\\');
187
-        $registered = false;
188
-        if (empty(self::$_instance->_dependency_map[ $class ])) {
189
-            self::$_instance->_dependency_map[ $class ] = array();
190
-        }
191
-        // we need to make sure that any aliases used when registering a dependency
192
-        // get resolved to the correct class name
193
-        foreach ((array)$dependencies as $dependency => $load_source) {
194
-            $alias = self::$_instance->get_alias($dependency);
195
-            if (
196
-                $overwrite === EE_Dependency_Map::OVERWRITE_DEPENDENCIES
197
-                || ! isset(self::$_instance->_dependency_map[ $class ][ $alias ])
198
-            ) {
199
-                unset($dependencies[$dependency]);
200
-                $dependencies[$alias] = $load_source;
201
-                $registered = true;
202
-            }
203
-        }
204
-        // now add our two lists of dependencies together.
205
-        // using Union (+=) favours the arrays in precedence from left to right,
206
-        // so $dependencies is NOT overwritten because it is listed first
207
-        // ie: with A = B + C, entries in B take precedence over duplicate entries in C
208
-        // Union is way faster than array_merge() but should be used with caution...
209
-        // especially with numerically indexed arrays
210
-        $dependencies += self::$_instance->_dependency_map[ $class ];
211
-        // now we need to ensure that the resulting dependencies
212
-        // array only has the entries that are required for the class
213
-        // so first count how many dependencies were originally registered for the class
214
-        $dependency_count = count(self::$_instance->_dependency_map[ $class ]);
215
-        // if that count is non-zero (meaning dependencies were already registered)
216
-        self::$_instance->_dependency_map[ $class ] = $dependency_count
217
-            // then truncate the  final array to match that count
218
-            ? array_slice($dependencies, 0, $dependency_count)
219
-            // otherwise just take the incoming array because nothing previously existed
220
-            : $dependencies;
221
-        return $registered;
222
-    }
223
-
224
-
225
-
226
-    /**
227
-     * @param string $class_name
228
-     * @param string $loader
229
-     * @return bool
230
-     * @throws DomainException
231
-     */
232
-    public static function register_class_loader($class_name, $loader = 'load_core')
233
-    {
234
-        if (! $loader instanceof Closure && strpos($class_name, '\\') !== false) {
235
-            throw new DomainException(
236
-                esc_html__('Don\'t use class loaders for FQCNs.', 'event_espresso')
237
-            );
238
-        }
239
-        // check that loader is callable or method starts with "load_" and exists in EE_Registry
240
-        if (
241
-            ! is_callable($loader)
242
-            && (
243
-                strpos($loader, 'load_') !== 0
244
-                || ! method_exists('EE_Registry', $loader)
245
-            )
246
-        ) {
247
-            throw new DomainException(
248
-                sprintf(
249
-                    esc_html__(
250
-                        '"%1$s" is not a valid loader method on EE_Registry.',
251
-                        'event_espresso'
252
-                    ),
253
-                    $loader
254
-                )
255
-            );
256
-        }
257
-        $class_name = self::$_instance->get_alias($class_name);
258
-        if (! isset(self::$_instance->_class_loaders[$class_name])) {
259
-            self::$_instance->_class_loaders[$class_name] = $loader;
260
-            return true;
261
-        }
262
-        return false;
263
-    }
264
-
265
-
266
-
267
-    /**
268
-     * @return array
269
-     */
270
-    public function dependency_map()
271
-    {
272
-        return $this->_dependency_map;
273
-    }
274
-
275
-
276
-
277
-    /**
278
-     * returns TRUE if dependency map contains a listing for the provided class name
279
-     *
280
-     * @param string $class_name
281
-     * @return boolean
282
-     */
283
-    public function has($class_name = '')
284
-    {
285
-        // all legacy models have the same dependencies
286
-        if (strpos($class_name, 'EEM_') === 0) {
287
-            $class_name = 'LEGACY_MODELS';
288
-        }
289
-        return isset($this->_dependency_map[$class_name]) ? true : false;
290
-    }
291
-
292
-
293
-
294
-    /**
295
-     * returns TRUE if dependency map contains a listing for the provided class name AND dependency
296
-     *
297
-     * @param string $class_name
298
-     * @param string $dependency
299
-     * @return bool
300
-     */
301
-    public function has_dependency_for_class($class_name = '', $dependency = '')
302
-    {
303
-        // all legacy models have the same dependencies
304
-        if (strpos($class_name, 'EEM_') === 0) {
305
-            $class_name = 'LEGACY_MODELS';
306
-        }
307
-        $dependency = $this->get_alias($dependency);
308
-        return isset($this->_dependency_map[$class_name], $this->_dependency_map[$class_name][$dependency])
309
-            ? true
310
-            : false;
311
-    }
312
-
313
-
314
-
315
-    /**
316
-     * returns loading strategy for whether a previously cached dependency should be loaded or a new instance returned
317
-     *
318
-     * @param string $class_name
319
-     * @param string $dependency
320
-     * @return int
321
-     */
322
-    public function loading_strategy_for_class_dependency($class_name = '', $dependency = '')
323
-    {
324
-        // all legacy models have the same dependencies
325
-        if (strpos($class_name, 'EEM_') === 0) {
326
-            $class_name = 'LEGACY_MODELS';
327
-        }
328
-        $dependency = $this->get_alias($dependency);
329
-        return $this->has_dependency_for_class($class_name, $dependency)
330
-            ? $this->_dependency_map[$class_name][$dependency]
331
-            : EE_Dependency_Map::not_registered;
332
-    }
333
-
334
-
335
-
336
-    /**
337
-     * @param string $class_name
338
-     * @return string | Closure
339
-     */
340
-    public function class_loader($class_name)
341
-    {
342
-        // all legacy models use load_model()
343
-        if(strpos($class_name, 'EEM_') === 0){
344
-            return 'load_model';
345
-        }
346
-        $class_name = $this->get_alias($class_name);
347
-        return isset($this->_class_loaders[$class_name]) ? $this->_class_loaders[$class_name] : '';
348
-    }
349
-
350
-
351
-
352
-    /**
353
-     * @return array
354
-     */
355
-    public function class_loaders()
356
-    {
357
-        return $this->_class_loaders;
358
-    }
359
-
360
-
361
-
362
-    /**
363
-     * adds an alias for a classname
364
-     *
365
-     * @param string $class_name the class name that should be used (concrete class to replace interface)
366
-     * @param string $alias      the class name that would be type hinted for (abstract parent or interface)
367
-     * @param string $for_class  the class that has the dependency (is type hinting for the interface)
368
-     */
369
-    public function add_alias($class_name, $alias, $for_class = '')
370
-    {
371
-        if ($for_class !== '') {
372
-            if (! isset($this->_aliases[$for_class])) {
373
-                $this->_aliases[$for_class] = array();
374
-            }
375
-            $this->_aliases[$for_class][$class_name] = $alias;
376
-        }
377
-        $this->_aliases[$class_name] = $alias;
378
-    }
379
-
380
-
381
-
382
-    /**
383
-     * returns TRUE if the provided class name has an alias
384
-     *
385
-     * @param string $class_name
386
-     * @param string $for_class
387
-     * @return bool
388
-     */
389
-    public function has_alias($class_name = '', $for_class = '')
390
-    {
391
-        return isset($this->_aliases[$for_class], $this->_aliases[$for_class][$class_name])
392
-               || (
393
-                   isset($this->_aliases[$class_name])
394
-                   && ! is_array($this->_aliases[$class_name])
395
-               );
396
-    }
397
-
398
-
399
-
400
-    /**
401
-     * returns alias for class name if one exists, otherwise returns the original classname
402
-     * functions recursively, so that multiple aliases can be used to drill down to a classname
403
-     *  for example:
404
-     *      if the following two entries were added to the _aliases array:
405
-     *          array(
406
-     *              'interface_alias'           => 'some\namespace\interface'
407
-     *              'some\namespace\interface'  => 'some\namespace\classname'
408
-     *          )
409
-     *      then one could use EE_Registry::instance()->create( 'interface_alias' )
410
-     *      to load an instance of 'some\namespace\classname'
411
-     *
412
-     * @param string $class_name
413
-     * @param string $for_class
414
-     * @return string
415
-     */
416
-    public function get_alias($class_name = '', $for_class = '')
417
-    {
418
-        if (! $this->has_alias($class_name, $for_class)) {
419
-            return $class_name;
420
-        }
421
-        if ($for_class !== '' && isset($this->_aliases[ $for_class ][ $class_name ])) {
422
-            return $this->get_alias($this->_aliases[$for_class][$class_name], $for_class);
423
-        }
424
-        return $this->get_alias($this->_aliases[$class_name]);
425
-    }
426
-
427
-
428
-
429
-    /**
430
-     * Registers the core dependencies and whether a previously instantiated object should be loaded from the cache,
431
-     * if one exists, or whether a new object should be generated every time the requested class is loaded.
432
-     * This is done by using the following class constants:
433
-     *        EE_Dependency_Map::load_from_cache - loads previously instantiated object
434
-     *        EE_Dependency_Map::load_new_object - generates a new object every time
435
-     */
436
-    protected function _register_core_dependencies()
437
-    {
438
-        $this->_dependency_map = array(
439
-            'EE_Request_Handler'                                                                                          => array(
440
-                'EE_Request' => EE_Dependency_Map::load_from_cache,
441
-            ),
442
-            'EE_System'                                                                                                   => array(
443
-                'EE_Registry'                                => EE_Dependency_Map::load_from_cache,
444
-                'EventEspresso\core\services\loaders\Loader' => EE_Dependency_Map::load_from_cache,
445
-                'EE_Capabilities'                            => EE_Dependency_Map::load_from_cache,
446
-                'EE_Request'                                 => EE_Dependency_Map::load_from_cache,
447
-                'EE_Maintenance_Mode'                        => EE_Dependency_Map::load_from_cache,
448
-            ),
449
-            'EE_Session'                                                                                                  => array(
450
-                'EventEspresso\core\services\cache\TransientCacheStorage' => EE_Dependency_Map::load_from_cache,
451
-                'EE_Encryption'                                           => EE_Dependency_Map::load_from_cache,
452
-            ),
453
-            'EE_Cart'                                                                                                     => array(
454
-                'EE_Session' => EE_Dependency_Map::load_from_cache,
455
-            ),
456
-            'EE_Front_Controller'                                                                                         => array(
457
-                'EE_Registry'              => EE_Dependency_Map::load_from_cache,
458
-                'EE_Request_Handler'       => EE_Dependency_Map::load_from_cache,
459
-                'EE_Module_Request_Router' => EE_Dependency_Map::load_from_cache,
460
-            ),
461
-            'EE_Messenger_Collection_Loader'                                                                              => array(
462
-                'EE_Messenger_Collection' => EE_Dependency_Map::load_new_object,
463
-            ),
464
-            'EE_Message_Type_Collection_Loader'                                                                           => array(
465
-                'EE_Message_Type_Collection' => EE_Dependency_Map::load_new_object,
466
-            ),
467
-            'EE_Message_Resource_Manager'                                                                                 => array(
468
-                'EE_Messenger_Collection_Loader'    => EE_Dependency_Map::load_new_object,
469
-                'EE_Message_Type_Collection_Loader' => EE_Dependency_Map::load_new_object,
470
-                'EEM_Message_Template_Group'        => EE_Dependency_Map::load_from_cache,
471
-            ),
472
-            'EE_Message_Factory'                                                                                          => array(
473
-                'EE_Message_Resource_Manager' => EE_Dependency_Map::load_from_cache,
474
-            ),
475
-            'EE_messages'                                                                                                 => array(
476
-                'EE_Message_Resource_Manager' => EE_Dependency_Map::load_from_cache,
477
-            ),
478
-            'EE_Messages_Generator'                                                                                       => array(
479
-                'EE_Messages_Queue'                    => EE_Dependency_Map::load_new_object,
480
-                'EE_Messages_Data_Handler_Collection'  => EE_Dependency_Map::load_new_object,
481
-                'EE_Message_Template_Group_Collection' => EE_Dependency_Map::load_new_object,
482
-                'EEH_Parse_Shortcodes'                 => EE_Dependency_Map::load_from_cache,
483
-            ),
484
-            'EE_Messages_Processor'                                                                                       => array(
485
-                'EE_Message_Resource_Manager' => EE_Dependency_Map::load_from_cache,
486
-            ),
487
-            'EE_Messages_Queue'                                                                                           => array(
488
-                'EE_Message_Repository' => EE_Dependency_Map::load_new_object,
489
-            ),
490
-            'EE_Messages_Template_Defaults'                                                                               => array(
491
-                'EEM_Message_Template_Group' => EE_Dependency_Map::load_from_cache,
492
-                'EEM_Message_Template'       => EE_Dependency_Map::load_from_cache,
493
-            ),
494
-            'EE_Message_To_Generate_From_Request'                                                                         => array(
495
-                'EE_Message_Resource_Manager' => EE_Dependency_Map::load_from_cache,
496
-                'EE_Request_Handler'          => EE_Dependency_Map::load_from_cache,
497
-            ),
498
-            'EventEspresso\core\services\commands\CommandBus'                                                             => array(
499
-                'EventEspresso\core\services\commands\CommandHandlerManager' => EE_Dependency_Map::load_from_cache,
500
-            ),
501
-            'EventEspresso\services\commands\CommandHandler'                                                              => array(
502
-                'EE_Registry'         => EE_Dependency_Map::load_from_cache,
503
-                'CommandBusInterface' => EE_Dependency_Map::load_from_cache,
504
-            ),
505
-            'EventEspresso\core\services\commands\CommandHandlerManager'                                                  => array(
506
-                'EventEspresso\core\services\loaders\Loader' => EE_Dependency_Map::load_from_cache,
507
-            ),
508
-            'EventEspresso\core\services\commands\CompositeCommandHandler'                                                => array(
509
-                'EventEspresso\core\services\commands\CommandBus'     => EE_Dependency_Map::load_from_cache,
510
-                'EventEspresso\core\services\commands\CommandFactory' => EE_Dependency_Map::load_from_cache,
511
-            ),
512
-            'EventEspresso\core\services\commands\CommandFactory'                                                         => array(
513
-                'EventEspresso\core\services\loaders\Loader' => EE_Dependency_Map::load_from_cache,
514
-            ),
515
-            'EventEspresso\core\services\commands\middleware\CapChecker'                                                  => array(
516
-                'EventEspresso\core\domain\services\capabilities\CapabilitiesChecker' => EE_Dependency_Map::load_from_cache,
517
-            ),
518
-            'EventEspresso\core\domain\services\capabilities\CapabilitiesChecker'                                         => array(
519
-                'EE_Capabilities' => EE_Dependency_Map::load_from_cache,
520
-            ),
521
-            'EventEspresso\core\domain\services\capabilities\RegistrationsCapChecker'                                     => array(
522
-                'EE_Capabilities' => EE_Dependency_Map::load_from_cache,
523
-            ),
524
-            'EventEspresso\core\services\commands\registration\CreateRegistrationCommandHandler'                          => array(
525
-                'EventEspresso\core\domain\services\registration\CreateRegistrationService' => EE_Dependency_Map::load_from_cache,
526
-            ),
527
-            'EventEspresso\core\services\commands\registration\CopyRegistrationDetailsCommandHandler'                     => array(
528
-                'EventEspresso\core\domain\services\registration\CopyRegistrationService' => EE_Dependency_Map::load_from_cache,
529
-            ),
530
-            'EventEspresso\core\services\commands\registration\CopyRegistrationPaymentsCommandHandler'                    => array(
531
-                'EventEspresso\core\domain\services\registration\CopyRegistrationService' => EE_Dependency_Map::load_from_cache,
532
-            ),
533
-            'EventEspresso\core\services\commands\registration\CancelRegistrationAndTicketLineItemCommandHandler'         => array(
534
-                'EventEspresso\core\domain\services\registration\CancelTicketLineItemService' => EE_Dependency_Map::load_from_cache,
535
-            ),
536
-            'EventEspresso\core\services\commands\registration\UpdateRegistrationAndTransactionAfterChangeCommandHandler' => array(
537
-                'EventEspresso\core\domain\services\registration\UpdateRegistrationService' => EE_Dependency_Map::load_from_cache,
538
-            ),
539
-            'EventEspresso\core\services\commands\ticket\CreateTicketLineItemCommandHandler'                              => array(
540
-                'EventEspresso\core\domain\services\ticket\CreateTicketLineItemService' => EE_Dependency_Map::load_from_cache,
541
-            ),
542
-            'EventEspresso\core\services\commands\ticket\CancelTicketLineItemCommandHandler'                              => array(
543
-                'EventEspresso\core\domain\services\ticket\CancelTicketLineItemService' => EE_Dependency_Map::load_from_cache,
544
-            ),
545
-            'EventEspresso\core\domain\services\registration\CancelRegistrationService'                                   => array(
546
-                'EventEspresso\core\domain\services\ticket\CancelTicketLineItemService' => EE_Dependency_Map::load_from_cache,
547
-            ),
548
-            'EventEspresso\core\services\commands\attendee\CreateAttendeeCommandHandler'                                  => array(
549
-                'EEM_Attendee' => EE_Dependency_Map::load_from_cache,
550
-            ),
551
-            'EventEspresso\core\services\database\TableManager'                                                           => array(
552
-                'EventEspresso\core\services\database\TableAnalysis' => EE_Dependency_Map::load_from_cache,
553
-            ),
554
-            'EE_Data_Migration_Class_Base'                                                                                => array(
555
-                'EventEspresso\core\services\database\TableAnalysis' => EE_Dependency_Map::load_from_cache,
556
-                'EventEspresso\core\services\database\TableManager'  => EE_Dependency_Map::load_from_cache,
557
-            ),
558
-            'EE_DMS_Core_4_1_0'                                                                                           => array(
559
-                'EventEspresso\core\services\database\TableAnalysis' => EE_Dependency_Map::load_from_cache,
560
-                'EventEspresso\core\services\database\TableManager'  => EE_Dependency_Map::load_from_cache,
561
-            ),
562
-            'EE_DMS_Core_4_2_0'                                                                                           => array(
563
-                'EventEspresso\core\services\database\TableAnalysis' => EE_Dependency_Map::load_from_cache,
564
-                'EventEspresso\core\services\database\TableManager'  => EE_Dependency_Map::load_from_cache,
565
-            ),
566
-            'EE_DMS_Core_4_3_0'                                                                                           => array(
567
-                'EventEspresso\core\services\database\TableAnalysis' => EE_Dependency_Map::load_from_cache,
568
-                'EventEspresso\core\services\database\TableManager'  => EE_Dependency_Map::load_from_cache,
569
-            ),
570
-            'EE_DMS_Core_4_4_0'                                                                                           => array(
571
-                'EventEspresso\core\services\database\TableAnalysis' => EE_Dependency_Map::load_from_cache,
572
-                'EventEspresso\core\services\database\TableManager'  => EE_Dependency_Map::load_from_cache,
573
-            ),
574
-            'EE_DMS_Core_4_5_0'                                                                                           => array(
575
-                'EventEspresso\core\services\database\TableAnalysis' => EE_Dependency_Map::load_from_cache,
576
-                'EventEspresso\core\services\database\TableManager'  => EE_Dependency_Map::load_from_cache,
577
-            ),
578
-            'EE_DMS_Core_4_6_0'                                                                                           => array(
579
-                'EventEspresso\core\services\database\TableAnalysis' => EE_Dependency_Map::load_from_cache,
580
-                'EventEspresso\core\services\database\TableManager'  => EE_Dependency_Map::load_from_cache,
581
-            ),
582
-            'EE_DMS_Core_4_7_0'                                                                                           => array(
583
-                'EventEspresso\core\services\database\TableAnalysis' => EE_Dependency_Map::load_from_cache,
584
-                'EventEspresso\core\services\database\TableManager'  => EE_Dependency_Map::load_from_cache,
585
-            ),
586
-            'EE_DMS_Core_4_8_0'                                                                                           => array(
587
-                'EventEspresso\core\services\database\TableAnalysis' => EE_Dependency_Map::load_from_cache,
588
-                'EventEspresso\core\services\database\TableManager'  => EE_Dependency_Map::load_from_cache,
589
-            ),
590
-            'EE_DMS_Core_4_9_0'                                                                                           => array(
591
-                'EventEspresso\core\services\database\TableAnalysis' => EE_Dependency_Map::load_from_cache,
592
-                'EventEspresso\core\services\database\TableManager'  => EE_Dependency_Map::load_from_cache,
593
-            ),
594
-            'EventEspresso\core\services\assets\Registry'                                                                 => array(
595
-                'EE_Template_Config' => EE_Dependency_Map::load_from_cache,
596
-                'EE_Currency_Config' => EE_Dependency_Map::load_from_cache,
597
-            ),
598
-            'EventEspresso\core\domain\entities\shortcodes\EspressoCancelled'                                             => array(
599
-                'EventEspresso\core\services\cache\PostRelatedCacheManager' => EE_Dependency_Map::load_from_cache,
600
-            ),
601
-            'EventEspresso\core\domain\entities\shortcodes\EspressoCheckout'                                              => array(
602
-                'EventEspresso\core\services\cache\PostRelatedCacheManager' => EE_Dependency_Map::load_from_cache,
603
-            ),
604
-            'EventEspresso\core\domain\entities\shortcodes\EspressoEventAttendees'                                        => array(
605
-                'EventEspresso\core\services\cache\PostRelatedCacheManager' => EE_Dependency_Map::load_from_cache,
606
-            ),
607
-            'EventEspresso\core\domain\entities\shortcodes\EspressoEvents'                                                => array(
608
-                'EventEspresso\core\services\cache\PostRelatedCacheManager' => EE_Dependency_Map::load_from_cache,
609
-            ),
610
-            'EventEspresso\core\domain\entities\shortcodes\EspressoThankYou'                                              => array(
611
-                'EventEspresso\core\services\cache\PostRelatedCacheManager' => EE_Dependency_Map::load_from_cache,
612
-            ),
613
-            'EventEspresso\core\domain\entities\shortcodes\EspressoTicketSelector'                                        => array(
614
-                'EventEspresso\core\services\cache\PostRelatedCacheManager' => EE_Dependency_Map::load_from_cache,
615
-            ),
616
-            'EventEspresso\core\domain\entities\shortcodes\EspressoTxnPage'                                               => array(
617
-                'EventEspresso\core\services\cache\PostRelatedCacheManager' => EE_Dependency_Map::load_from_cache,
618
-            ),
619
-            'EventEspresso\core\services\cache\BasicCacheManager'                        => array(
620
-                'EventEspresso\core\services\cache\TransientCacheStorage' => EE_Dependency_Map::load_from_cache,
621
-            ),
622
-            'EventEspresso\core\services\cache\PostRelatedCacheManager'                  => array(
623
-                'EventEspresso\core\services\cache\TransientCacheStorage' => EE_Dependency_Map::load_from_cache,
624
-            ),
625
-            'EventEspresso\core\domain\services\validation\email\EmailValidationService' => array(
626
-                'EE_Registration_Config'                                  => EE_Dependency_Map::load_from_cache,
627
-                'EventEspresso\core\services\loaders\Loader'              => EE_Dependency_Map::load_from_cache,
628
-            ),
629
-            'EventEspresso\core\domain\values\EmailAddress'                              => array(
630
-                null,
631
-                'EventEspresso\core\domain\services\validation\email\EmailValidationService' => EE_Dependency_Map::load_from_cache,
632
-            ),
633
-            'EventEspresso\core\services\orm\ModelFieldFactory' => array(
634
-                'EventEspresso\core\services\loaders\Loader'              => EE_Dependency_Map::load_from_cache,
635
-            ),
636
-            'LEGACY_MODELS'                                                   => array(
637
-                null,
638
-                'EventEspresso\core\services\database\ModelFieldFactory' => EE_Dependency_Map::load_from_cache,
639
-            ),
640
-            'EE_Module_Request_Router' => array(
641
-                'EE_Request' => EE_Dependency_Map::load_from_cache,
642
-            ),
643
-            'EE_Registration_Processor' => array(
644
-                'EE_Request' => EE_Dependency_Map::load_from_cache,
645
-            ),
646
-            'EventEspresso\core\services\notifications\PersistentAdminNoticeManager' => array(
647
-                null,
648
-                'EventEspresso\core\domain\services\capabilities\CapabilitiesChecker' => EE_Dependency_Map::load_from_cache,
649
-                'EE_Request' => EE_Dependency_Map::load_from_cache,
650
-            ),
651
-        );
652
-    }
653
-
654
-
655
-
656
-    /**
657
-     * Registers how core classes are loaded.
658
-     * This can either be done by simply providing the name of one of the EE_Registry loader methods such as:
659
-     *        'EE_Request_Handler' => 'load_core'
660
-     *        'EE_Messages_Queue'  => 'load_lib'
661
-     *        'EEH_Debug_Tools'    => 'load_helper'
662
-     * or, if greater control is required, by providing a custom closure. For example:
663
-     *        'Some_Class' => function () {
664
-     *            return new Some_Class();
665
-     *        },
666
-     * This is required for instantiating dependencies
667
-     * where an interface has been type hinted in a class constructor. For example:
668
-     *        'Required_Interface' => function () {
669
-     *            return new A_Class_That_Implements_Required_Interface();
670
-     *        },
671
-     *
672
-     * @throws InvalidInterfaceException
673
-     * @throws InvalidDataTypeException
674
-     * @throws InvalidArgumentException
675
-     */
676
-    protected function _register_core_class_loaders()
677
-    {
678
-        //for PHP5.3 compat, we need to register any properties called here in a variable because `$this` cannot
679
-        //be used in a closure.
680
-        $request = &$this->_request;
681
-        $response = &$this->_response;
682
-        // $loader = &$this->loader;
683
-        $this->_class_loaders = array(
684
-            //load_core
685
-            'EE_Capabilities'                      => 'load_core',
686
-            'EE_Encryption'                        => 'load_core',
687
-            'EE_Front_Controller'                  => 'load_core',
688
-            'EE_Module_Request_Router'             => 'load_core',
689
-            'EE_Registry'                          => 'load_core',
690
-            'EE_Request'                           => function () use (&$request) {
691
-                return $request;
692
-            },
693
-            'EE_Response'                          => function () use (&$response) {
694
-                return $response;
695
-            },
696
-            'EE_Request_Handler'                   => 'load_core',
697
-            'EE_Session'                           => 'load_core',
698
-            'EE_Cron_Tasks'                        => 'load_core',
699
-            'EE_System'                            => 'load_core',
700
-            'EE_Maintenance_Mode'                  => 'load_core',
701
-            'EE_Register_CPTs'                     => 'load_core',
702
-            'EE_Admin'                             => 'load_core',
703
-            //load_lib
704
-            'EE_Message_Resource_Manager'          => 'load_lib',
705
-            'EE_Message_Type_Collection'           => 'load_lib',
706
-            'EE_Message_Type_Collection_Loader'    => 'load_lib',
707
-            'EE_Messenger_Collection'              => 'load_lib',
708
-            'EE_Messenger_Collection_Loader'       => 'load_lib',
709
-            'EE_Messages_Processor'                => 'load_lib',
710
-            'EE_Message_Repository'                => 'load_lib',
711
-            'EE_Messages_Queue'                    => 'load_lib',
712
-            'EE_Messages_Data_Handler_Collection'  => 'load_lib',
713
-            'EE_Message_Template_Group_Collection' => 'load_lib',
714
-            'EE_Payment_Method_Manager'            => 'load_lib',
715
-            'EE_Messages_Generator'                => function () {
716
-                return EE_Registry::instance()->load_lib(
717
-                    'Messages_Generator',
718
-                    array(),
719
-                    false,
720
-                    false
721
-                );
722
-            },
723
-            'EE_Messages_Template_Defaults'        => function ($arguments = array()) {
724
-                return EE_Registry::instance()->load_lib(
725
-                    'Messages_Template_Defaults',
726
-                    $arguments,
727
-                    false,
728
-                    false
729
-                );
730
-            },
731
-            //load_model
732
-            // 'EEM_Attendee'                         => 'load_model',
733
-            // 'EEM_Message_Template_Group'           => 'load_model',
734
-            // 'EEM_Message_Template'                 => 'load_model',
735
-            //load_helper
736
-            'EEH_Parse_Shortcodes'                 => function () {
737
-                if (EE_Registry::instance()->load_helper('Parse_Shortcodes')) {
738
-                    return new EEH_Parse_Shortcodes();
739
-                }
740
-                return null;
741
-            },
742
-            'EE_Template_Config'                   => function () {
743
-                return EE_Config::instance()->template_settings;
744
-            },
745
-            'EE_Currency_Config'                   => function () {
746
-                return EE_Config::instance()->currency;
747
-            },
748
-            'EE_Registration_Config'                   => function () {
749
-                return EE_Config::instance()->registration;
750
-            },
751
-            'EventEspresso\core\services\loaders\Loader' => function () {
752
-                return LoaderFactory::getLoader();
753
-            },
754
-        );
755
-    }
756
-
757
-
758
-
759
-    /**
760
-     * can be used for supplying alternate names for classes,
761
-     * or for connecting interface names to instantiable classes
762
-     */
763
-    protected function _register_core_aliases()
764
-    {
765
-        $this->_aliases = array(
766
-            'CommandBusInterface'                                                          => 'EventEspresso\core\services\commands\CommandBusInterface',
767
-            'EventEspresso\core\services\commands\CommandBusInterface'                     => 'EventEspresso\core\services\commands\CommandBus',
768
-            'CommandHandlerManagerInterface'                                               => 'EventEspresso\core\services\commands\CommandHandlerManagerInterface',
769
-            'EventEspresso\core\services\commands\CommandHandlerManagerInterface'          => 'EventEspresso\core\services\commands\CommandHandlerManager',
770
-            'CapChecker'                                                                   => 'EventEspresso\core\services\commands\middleware\CapChecker',
771
-            'AddActionHook'                                                                => 'EventEspresso\core\services\commands\middleware\AddActionHook',
772
-            'CapabilitiesChecker'                                                          => 'EventEspresso\core\domain\services\capabilities\CapabilitiesChecker',
773
-            'CapabilitiesCheckerInterface'                                                 => 'EventEspresso\core\domain\services\capabilities\CapabilitiesCheckerInterface',
774
-            'EventEspresso\core\domain\services\capabilities\CapabilitiesCheckerInterface' => 'EventEspresso\core\domain\services\capabilities\CapabilitiesChecker',
775
-            'CreateRegistrationService'                                                    => 'EventEspresso\core\domain\services\registration\CreateRegistrationService',
776
-            'CreateRegCodeCommandHandler'                                                  => 'EventEspresso\core\services\commands\registration\CreateRegCodeCommand',
777
-            'CreateRegUrlLinkCommandHandler'                                               => 'EventEspresso\core\services\commands\registration\CreateRegUrlLinkCommand',
778
-            'CreateRegistrationCommandHandler'                                             => 'EventEspresso\core\services\commands\registration\CreateRegistrationCommand',
779
-            'CopyRegistrationDetailsCommandHandler'                                        => 'EventEspresso\core\services\commands\registration\CopyRegistrationDetailsCommand',
780
-            'CopyRegistrationPaymentsCommandHandler'                                       => 'EventEspresso\core\services\commands\registration\CopyRegistrationPaymentsCommand',
781
-            'CancelRegistrationAndTicketLineItemCommandHandler'                            => 'EventEspresso\core\services\commands\registration\CancelRegistrationAndTicketLineItemCommandHandler',
782
-            'UpdateRegistrationAndTransactionAfterChangeCommandHandler'                    => 'EventEspresso\core\services\commands\registration\UpdateRegistrationAndTransactionAfterChangeCommandHandler',
783
-            'CreateTicketLineItemCommandHandler'                                           => 'EventEspresso\core\services\commands\ticket\CreateTicketLineItemCommand',
784
-            'CreateTransactionCommandHandler'                                     => 'EventEspresso\core\services\commands\transaction\CreateTransactionCommandHandler',
785
-            'CreateAttendeeCommandHandler'                                        => 'EventEspresso\core\services\commands\attendee\CreateAttendeeCommandHandler',
786
-            'TableManager'                                                                 => 'EventEspresso\core\services\database\TableManager',
787
-            'TableAnalysis'                                                                => 'EventEspresso\core\services\database\TableAnalysis',
788
-            'EspressoShortcode'                                                            => 'EventEspresso\core\services\shortcodes\EspressoShortcode',
789
-            'ShortcodeInterface'                                                           => 'EventEspresso\core\services\shortcodes\ShortcodeInterface',
790
-            'EventEspresso\core\services\shortcodes\ShortcodeInterface'                    => 'EventEspresso\core\services\shortcodes\EspressoShortcode',
791
-            'EventEspresso\core\services\cache\CacheStorageInterface'                      => 'EventEspresso\core\services\cache\TransientCacheStorage',
792
-            'LoaderInterface'                                                              => 'EventEspresso\core\services\loaders\LoaderInterface',
793
-            'EventEspresso\core\services\loaders\LoaderInterface'                          => 'EventEspresso\core\services\loaders\Loader',
794
-            'CommandFactoryInterface'                                                     => 'EventEspresso\core\services\commands\CommandFactoryInterface',
795
-            'EventEspresso\core\services\commands\CommandFactoryInterface'                => 'EventEspresso\core\services\commands\CommandFactory',
796
-            'EventEspresso\core\domain\services\session\SessionIdentifierInterface'       => 'EE_Session',
797
-            'EmailValidatorInterface'                                                     => 'EventEspresso\core\domain\services\validation\email\EmailValidatorInterface',
798
-            'EventEspresso\core\domain\services\validation\email\EmailValidatorInterface' => 'EventEspresso\core\domain\services\validation\email\EmailValidationService',
799
-            'NoticeConverterInterface'                                            => 'EventEspresso\core\services\notices\NoticeConverterInterface',
800
-            'EventEspresso\core\services\notices\NoticeConverterInterface'        => 'EventEspresso\core\services\notices\ConvertNoticesToEeErrors',
801
-            'NoticesContainerInterface'                                            => 'EventEspresso\core\services\notices\NoticesContainerInterface',
802
-            'EventEspresso\core\services\notices\NoticesContainerInterface'        => 'EventEspresso\core\services\notices\NoticesContainer',
803
-        );
804
-        if (! (defined('DOING_AJAX') && DOING_AJAX) && is_admin()) {
805
-            $this->_aliases['EventEspresso\core\services\notices\NoticeConverterInterface'] = 'EventEspresso\core\services\notices\ConvertNoticesToAdminNotices';
806
-        }
807
-    }
808
-
809
-
810
-
811
-    /**
812
-     * This is used to reset the internal map and class_loaders to their original default state at the beginning of the
813
-     * request Primarily used by unit tests.
814
-     *
815
-     * @throws InvalidDataTypeException
816
-     * @throws InvalidInterfaceException
817
-     * @throws InvalidArgumentException
818
-     */
819
-    public function reset()
820
-    {
821
-        $this->_register_core_class_loaders();
822
-        $this->_register_core_dependencies();
823
-    }
25
+	/**
26
+	 * This means that the requested class dependency is not present in the dependency map
27
+	 */
28
+	const not_registered = 0;
29
+
30
+	/**
31
+	 * This instructs class loaders to ALWAYS return a newly instantiated object for the requested class.
32
+	 */
33
+	const load_new_object = 1;
34
+
35
+	/**
36
+	 * This instructs class loaders to return a previously instantiated and cached object for the requested class.
37
+	 * IF a previously instantiated object does not exist, a new one will be created and added to the cache.
38
+	 */
39
+	const load_from_cache = 2;
40
+
41
+	/**
42
+	 * When registering a dependency,
43
+	 * this indicates to keep any existing dependencies that already exist,
44
+	 * and simply discard any new dependencies declared in the incoming data
45
+	 */
46
+	const KEEP_EXISTING_DEPENDENCIES = 0;
47
+
48
+	/**
49
+	 * When registering a dependency,
50
+	 * this indicates to overwrite any existing dependencies that already exist using the incoming data
51
+	 */
52
+	const OVERWRITE_DEPENDENCIES = 1;
53
+
54
+
55
+
56
+	/**
57
+	 * @type EE_Dependency_Map $_instance
58
+	 */
59
+	protected static $_instance;
60
+
61
+	/**
62
+	 * @type EE_Request $request
63
+	 */
64
+	protected $_request;
65
+
66
+	/**
67
+	 * @type EE_Response $response
68
+	 */
69
+	protected $_response;
70
+
71
+	/**
72
+	 * @type LoaderInterface $loader
73
+	 */
74
+	protected $loader;
75
+
76
+	/**
77
+	 * @type array $_dependency_map
78
+	 */
79
+	protected $_dependency_map = array();
80
+
81
+	/**
82
+	 * @type array $_class_loaders
83
+	 */
84
+	protected $_class_loaders = array();
85
+
86
+	/**
87
+	 * @type array $_aliases
88
+	 */
89
+	protected $_aliases = array();
90
+
91
+
92
+
93
+	/**
94
+	 * EE_Dependency_Map constructor.
95
+	 *
96
+	 * @param EE_Request  $request
97
+	 * @param EE_Response $response
98
+	 */
99
+	protected function __construct(EE_Request $request, EE_Response $response)
100
+	{
101
+		$this->_request = $request;
102
+		$this->_response = $response;
103
+		add_action('EE_Load_Espresso_Core__handle_request__initialize_core_loading', array($this, 'initialize'));
104
+		do_action('EE_Dependency_Map____construct');
105
+	}
106
+
107
+
108
+
109
+	/**
110
+	 * @throws InvalidDataTypeException
111
+	 * @throws InvalidInterfaceException
112
+	 * @throws InvalidArgumentException
113
+	 */
114
+	public function initialize()
115
+	{
116
+		$this->_register_core_dependencies();
117
+		$this->_register_core_class_loaders();
118
+		$this->_register_core_aliases();
119
+	}
120
+
121
+
122
+
123
+	/**
124
+	 * @singleton method used to instantiate class object
125
+	 * @access    public
126
+	 * @param EE_Request  $request
127
+	 * @param EE_Response $response
128
+	 * @return EE_Dependency_Map
129
+	 */
130
+	public static function instance(EE_Request $request = null, EE_Response $response = null)
131
+	{
132
+		// check if class object is instantiated, and instantiated properly
133
+		if (! self::$_instance instanceof EE_Dependency_Map) {
134
+			self::$_instance = new EE_Dependency_Map($request, $response);
135
+		}
136
+		return self::$_instance;
137
+	}
138
+
139
+
140
+
141
+	/**
142
+	 * @param LoaderInterface $loader
143
+	 */
144
+	public function setLoader(LoaderInterface $loader)
145
+	{
146
+		$this->loader = $loader;
147
+	}
148
+
149
+
150
+
151
+	/**
152
+	 * @param string $class
153
+	 * @param array  $dependencies
154
+	 * @param int    $overwrite
155
+	 * @return bool
156
+	 */
157
+	public static function register_dependencies(
158
+		$class,
159
+		array $dependencies,
160
+		$overwrite = EE_Dependency_Map::KEEP_EXISTING_DEPENDENCIES
161
+	) {
162
+		return self::$_instance->registerDependencies($class, $dependencies, $overwrite);
163
+	}
164
+
165
+
166
+
167
+	/**
168
+	 * Assigns an array of class names and corresponding load sources (new or cached)
169
+	 * to the class specified by the first parameter.
170
+	 * IMPORTANT !!!
171
+	 * The order of elements in the incoming $dependencies array MUST match
172
+	 * the order of the constructor parameters for the class in question.
173
+	 * This is especially important when overriding any existing dependencies that are registered.
174
+	 * the third parameter controls whether any duplicate dependencies are overwritten or not.
175
+	 *
176
+	 * @param string $class
177
+	 * @param array  $dependencies
178
+	 * @param int    $overwrite
179
+	 * @return bool
180
+	 */
181
+	public function registerDependencies(
182
+		$class,
183
+		array $dependencies,
184
+		$overwrite = EE_Dependency_Map::KEEP_EXISTING_DEPENDENCIES
185
+	) {
186
+		$class = trim($class, '\\');
187
+		$registered = false;
188
+		if (empty(self::$_instance->_dependency_map[ $class ])) {
189
+			self::$_instance->_dependency_map[ $class ] = array();
190
+		}
191
+		// we need to make sure that any aliases used when registering a dependency
192
+		// get resolved to the correct class name
193
+		foreach ((array)$dependencies as $dependency => $load_source) {
194
+			$alias = self::$_instance->get_alias($dependency);
195
+			if (
196
+				$overwrite === EE_Dependency_Map::OVERWRITE_DEPENDENCIES
197
+				|| ! isset(self::$_instance->_dependency_map[ $class ][ $alias ])
198
+			) {
199
+				unset($dependencies[$dependency]);
200
+				$dependencies[$alias] = $load_source;
201
+				$registered = true;
202
+			}
203
+		}
204
+		// now add our two lists of dependencies together.
205
+		// using Union (+=) favours the arrays in precedence from left to right,
206
+		// so $dependencies is NOT overwritten because it is listed first
207
+		// ie: with A = B + C, entries in B take precedence over duplicate entries in C
208
+		// Union is way faster than array_merge() but should be used with caution...
209
+		// especially with numerically indexed arrays
210
+		$dependencies += self::$_instance->_dependency_map[ $class ];
211
+		// now we need to ensure that the resulting dependencies
212
+		// array only has the entries that are required for the class
213
+		// so first count how many dependencies were originally registered for the class
214
+		$dependency_count = count(self::$_instance->_dependency_map[ $class ]);
215
+		// if that count is non-zero (meaning dependencies were already registered)
216
+		self::$_instance->_dependency_map[ $class ] = $dependency_count
217
+			// then truncate the  final array to match that count
218
+			? array_slice($dependencies, 0, $dependency_count)
219
+			// otherwise just take the incoming array because nothing previously existed
220
+			: $dependencies;
221
+		return $registered;
222
+	}
223
+
224
+
225
+
226
+	/**
227
+	 * @param string $class_name
228
+	 * @param string $loader
229
+	 * @return bool
230
+	 * @throws DomainException
231
+	 */
232
+	public static function register_class_loader($class_name, $loader = 'load_core')
233
+	{
234
+		if (! $loader instanceof Closure && strpos($class_name, '\\') !== false) {
235
+			throw new DomainException(
236
+				esc_html__('Don\'t use class loaders for FQCNs.', 'event_espresso')
237
+			);
238
+		}
239
+		// check that loader is callable or method starts with "load_" and exists in EE_Registry
240
+		if (
241
+			! is_callable($loader)
242
+			&& (
243
+				strpos($loader, 'load_') !== 0
244
+				|| ! method_exists('EE_Registry', $loader)
245
+			)
246
+		) {
247
+			throw new DomainException(
248
+				sprintf(
249
+					esc_html__(
250
+						'"%1$s" is not a valid loader method on EE_Registry.',
251
+						'event_espresso'
252
+					),
253
+					$loader
254
+				)
255
+			);
256
+		}
257
+		$class_name = self::$_instance->get_alias($class_name);
258
+		if (! isset(self::$_instance->_class_loaders[$class_name])) {
259
+			self::$_instance->_class_loaders[$class_name] = $loader;
260
+			return true;
261
+		}
262
+		return false;
263
+	}
264
+
265
+
266
+
267
+	/**
268
+	 * @return array
269
+	 */
270
+	public function dependency_map()
271
+	{
272
+		return $this->_dependency_map;
273
+	}
274
+
275
+
276
+
277
+	/**
278
+	 * returns TRUE if dependency map contains a listing for the provided class name
279
+	 *
280
+	 * @param string $class_name
281
+	 * @return boolean
282
+	 */
283
+	public function has($class_name = '')
284
+	{
285
+		// all legacy models have the same dependencies
286
+		if (strpos($class_name, 'EEM_') === 0) {
287
+			$class_name = 'LEGACY_MODELS';
288
+		}
289
+		return isset($this->_dependency_map[$class_name]) ? true : false;
290
+	}
291
+
292
+
293
+
294
+	/**
295
+	 * returns TRUE if dependency map contains a listing for the provided class name AND dependency
296
+	 *
297
+	 * @param string $class_name
298
+	 * @param string $dependency
299
+	 * @return bool
300
+	 */
301
+	public function has_dependency_for_class($class_name = '', $dependency = '')
302
+	{
303
+		// all legacy models have the same dependencies
304
+		if (strpos($class_name, 'EEM_') === 0) {
305
+			$class_name = 'LEGACY_MODELS';
306
+		}
307
+		$dependency = $this->get_alias($dependency);
308
+		return isset($this->_dependency_map[$class_name], $this->_dependency_map[$class_name][$dependency])
309
+			? true
310
+			: false;
311
+	}
312
+
313
+
314
+
315
+	/**
316
+	 * returns loading strategy for whether a previously cached dependency should be loaded or a new instance returned
317
+	 *
318
+	 * @param string $class_name
319
+	 * @param string $dependency
320
+	 * @return int
321
+	 */
322
+	public function loading_strategy_for_class_dependency($class_name = '', $dependency = '')
323
+	{
324
+		// all legacy models have the same dependencies
325
+		if (strpos($class_name, 'EEM_') === 0) {
326
+			$class_name = 'LEGACY_MODELS';
327
+		}
328
+		$dependency = $this->get_alias($dependency);
329
+		return $this->has_dependency_for_class($class_name, $dependency)
330
+			? $this->_dependency_map[$class_name][$dependency]
331
+			: EE_Dependency_Map::not_registered;
332
+	}
333
+
334
+
335
+
336
+	/**
337
+	 * @param string $class_name
338
+	 * @return string | Closure
339
+	 */
340
+	public function class_loader($class_name)
341
+	{
342
+		// all legacy models use load_model()
343
+		if(strpos($class_name, 'EEM_') === 0){
344
+			return 'load_model';
345
+		}
346
+		$class_name = $this->get_alias($class_name);
347
+		return isset($this->_class_loaders[$class_name]) ? $this->_class_loaders[$class_name] : '';
348
+	}
349
+
350
+
351
+
352
+	/**
353
+	 * @return array
354
+	 */
355
+	public function class_loaders()
356
+	{
357
+		return $this->_class_loaders;
358
+	}
359
+
360
+
361
+
362
+	/**
363
+	 * adds an alias for a classname
364
+	 *
365
+	 * @param string $class_name the class name that should be used (concrete class to replace interface)
366
+	 * @param string $alias      the class name that would be type hinted for (abstract parent or interface)
367
+	 * @param string $for_class  the class that has the dependency (is type hinting for the interface)
368
+	 */
369
+	public function add_alias($class_name, $alias, $for_class = '')
370
+	{
371
+		if ($for_class !== '') {
372
+			if (! isset($this->_aliases[$for_class])) {
373
+				$this->_aliases[$for_class] = array();
374
+			}
375
+			$this->_aliases[$for_class][$class_name] = $alias;
376
+		}
377
+		$this->_aliases[$class_name] = $alias;
378
+	}
379
+
380
+
381
+
382
+	/**
383
+	 * returns TRUE if the provided class name has an alias
384
+	 *
385
+	 * @param string $class_name
386
+	 * @param string $for_class
387
+	 * @return bool
388
+	 */
389
+	public function has_alias($class_name = '', $for_class = '')
390
+	{
391
+		return isset($this->_aliases[$for_class], $this->_aliases[$for_class][$class_name])
392
+			   || (
393
+				   isset($this->_aliases[$class_name])
394
+				   && ! is_array($this->_aliases[$class_name])
395
+			   );
396
+	}
397
+
398
+
399
+
400
+	/**
401
+	 * returns alias for class name if one exists, otherwise returns the original classname
402
+	 * functions recursively, so that multiple aliases can be used to drill down to a classname
403
+	 *  for example:
404
+	 *      if the following two entries were added to the _aliases array:
405
+	 *          array(
406
+	 *              'interface_alias'           => 'some\namespace\interface'
407
+	 *              'some\namespace\interface'  => 'some\namespace\classname'
408
+	 *          )
409
+	 *      then one could use EE_Registry::instance()->create( 'interface_alias' )
410
+	 *      to load an instance of 'some\namespace\classname'
411
+	 *
412
+	 * @param string $class_name
413
+	 * @param string $for_class
414
+	 * @return string
415
+	 */
416
+	public function get_alias($class_name = '', $for_class = '')
417
+	{
418
+		if (! $this->has_alias($class_name, $for_class)) {
419
+			return $class_name;
420
+		}
421
+		if ($for_class !== '' && isset($this->_aliases[ $for_class ][ $class_name ])) {
422
+			return $this->get_alias($this->_aliases[$for_class][$class_name], $for_class);
423
+		}
424
+		return $this->get_alias($this->_aliases[$class_name]);
425
+	}
426
+
427
+
428
+
429
+	/**
430
+	 * Registers the core dependencies and whether a previously instantiated object should be loaded from the cache,
431
+	 * if one exists, or whether a new object should be generated every time the requested class is loaded.
432
+	 * This is done by using the following class constants:
433
+	 *        EE_Dependency_Map::load_from_cache - loads previously instantiated object
434
+	 *        EE_Dependency_Map::load_new_object - generates a new object every time
435
+	 */
436
+	protected function _register_core_dependencies()
437
+	{
438
+		$this->_dependency_map = array(
439
+			'EE_Request_Handler'                                                                                          => array(
440
+				'EE_Request' => EE_Dependency_Map::load_from_cache,
441
+			),
442
+			'EE_System'                                                                                                   => array(
443
+				'EE_Registry'                                => EE_Dependency_Map::load_from_cache,
444
+				'EventEspresso\core\services\loaders\Loader' => EE_Dependency_Map::load_from_cache,
445
+				'EE_Capabilities'                            => EE_Dependency_Map::load_from_cache,
446
+				'EE_Request'                                 => EE_Dependency_Map::load_from_cache,
447
+				'EE_Maintenance_Mode'                        => EE_Dependency_Map::load_from_cache,
448
+			),
449
+			'EE_Session'                                                                                                  => array(
450
+				'EventEspresso\core\services\cache\TransientCacheStorage' => EE_Dependency_Map::load_from_cache,
451
+				'EE_Encryption'                                           => EE_Dependency_Map::load_from_cache,
452
+			),
453
+			'EE_Cart'                                                                                                     => array(
454
+				'EE_Session' => EE_Dependency_Map::load_from_cache,
455
+			),
456
+			'EE_Front_Controller'                                                                                         => array(
457
+				'EE_Registry'              => EE_Dependency_Map::load_from_cache,
458
+				'EE_Request_Handler'       => EE_Dependency_Map::load_from_cache,
459
+				'EE_Module_Request_Router' => EE_Dependency_Map::load_from_cache,
460
+			),
461
+			'EE_Messenger_Collection_Loader'                                                                              => array(
462
+				'EE_Messenger_Collection' => EE_Dependency_Map::load_new_object,
463
+			),
464
+			'EE_Message_Type_Collection_Loader'                                                                           => array(
465
+				'EE_Message_Type_Collection' => EE_Dependency_Map::load_new_object,
466
+			),
467
+			'EE_Message_Resource_Manager'                                                                                 => array(
468
+				'EE_Messenger_Collection_Loader'    => EE_Dependency_Map::load_new_object,
469
+				'EE_Message_Type_Collection_Loader' => EE_Dependency_Map::load_new_object,
470
+				'EEM_Message_Template_Group'        => EE_Dependency_Map::load_from_cache,
471
+			),
472
+			'EE_Message_Factory'                                                                                          => array(
473
+				'EE_Message_Resource_Manager' => EE_Dependency_Map::load_from_cache,
474
+			),
475
+			'EE_messages'                                                                                                 => array(
476
+				'EE_Message_Resource_Manager' => EE_Dependency_Map::load_from_cache,
477
+			),
478
+			'EE_Messages_Generator'                                                                                       => array(
479
+				'EE_Messages_Queue'                    => EE_Dependency_Map::load_new_object,
480
+				'EE_Messages_Data_Handler_Collection'  => EE_Dependency_Map::load_new_object,
481
+				'EE_Message_Template_Group_Collection' => EE_Dependency_Map::load_new_object,
482
+				'EEH_Parse_Shortcodes'                 => EE_Dependency_Map::load_from_cache,
483
+			),
484
+			'EE_Messages_Processor'                                                                                       => array(
485
+				'EE_Message_Resource_Manager' => EE_Dependency_Map::load_from_cache,
486
+			),
487
+			'EE_Messages_Queue'                                                                                           => array(
488
+				'EE_Message_Repository' => EE_Dependency_Map::load_new_object,
489
+			),
490
+			'EE_Messages_Template_Defaults'                                                                               => array(
491
+				'EEM_Message_Template_Group' => EE_Dependency_Map::load_from_cache,
492
+				'EEM_Message_Template'       => EE_Dependency_Map::load_from_cache,
493
+			),
494
+			'EE_Message_To_Generate_From_Request'                                                                         => array(
495
+				'EE_Message_Resource_Manager' => EE_Dependency_Map::load_from_cache,
496
+				'EE_Request_Handler'          => EE_Dependency_Map::load_from_cache,
497
+			),
498
+			'EventEspresso\core\services\commands\CommandBus'                                                             => array(
499
+				'EventEspresso\core\services\commands\CommandHandlerManager' => EE_Dependency_Map::load_from_cache,
500
+			),
501
+			'EventEspresso\services\commands\CommandHandler'                                                              => array(
502
+				'EE_Registry'         => EE_Dependency_Map::load_from_cache,
503
+				'CommandBusInterface' => EE_Dependency_Map::load_from_cache,
504
+			),
505
+			'EventEspresso\core\services\commands\CommandHandlerManager'                                                  => array(
506
+				'EventEspresso\core\services\loaders\Loader' => EE_Dependency_Map::load_from_cache,
507
+			),
508
+			'EventEspresso\core\services\commands\CompositeCommandHandler'                                                => array(
509
+				'EventEspresso\core\services\commands\CommandBus'     => EE_Dependency_Map::load_from_cache,
510
+				'EventEspresso\core\services\commands\CommandFactory' => EE_Dependency_Map::load_from_cache,
511
+			),
512
+			'EventEspresso\core\services\commands\CommandFactory'                                                         => array(
513
+				'EventEspresso\core\services\loaders\Loader' => EE_Dependency_Map::load_from_cache,
514
+			),
515
+			'EventEspresso\core\services\commands\middleware\CapChecker'                                                  => array(
516
+				'EventEspresso\core\domain\services\capabilities\CapabilitiesChecker' => EE_Dependency_Map::load_from_cache,
517
+			),
518
+			'EventEspresso\core\domain\services\capabilities\CapabilitiesChecker'                                         => array(
519
+				'EE_Capabilities' => EE_Dependency_Map::load_from_cache,
520
+			),
521
+			'EventEspresso\core\domain\services\capabilities\RegistrationsCapChecker'                                     => array(
522
+				'EE_Capabilities' => EE_Dependency_Map::load_from_cache,
523
+			),
524
+			'EventEspresso\core\services\commands\registration\CreateRegistrationCommandHandler'                          => array(
525
+				'EventEspresso\core\domain\services\registration\CreateRegistrationService' => EE_Dependency_Map::load_from_cache,
526
+			),
527
+			'EventEspresso\core\services\commands\registration\CopyRegistrationDetailsCommandHandler'                     => array(
528
+				'EventEspresso\core\domain\services\registration\CopyRegistrationService' => EE_Dependency_Map::load_from_cache,
529
+			),
530
+			'EventEspresso\core\services\commands\registration\CopyRegistrationPaymentsCommandHandler'                    => array(
531
+				'EventEspresso\core\domain\services\registration\CopyRegistrationService' => EE_Dependency_Map::load_from_cache,
532
+			),
533
+			'EventEspresso\core\services\commands\registration\CancelRegistrationAndTicketLineItemCommandHandler'         => array(
534
+				'EventEspresso\core\domain\services\registration\CancelTicketLineItemService' => EE_Dependency_Map::load_from_cache,
535
+			),
536
+			'EventEspresso\core\services\commands\registration\UpdateRegistrationAndTransactionAfterChangeCommandHandler' => array(
537
+				'EventEspresso\core\domain\services\registration\UpdateRegistrationService' => EE_Dependency_Map::load_from_cache,
538
+			),
539
+			'EventEspresso\core\services\commands\ticket\CreateTicketLineItemCommandHandler'                              => array(
540
+				'EventEspresso\core\domain\services\ticket\CreateTicketLineItemService' => EE_Dependency_Map::load_from_cache,
541
+			),
542
+			'EventEspresso\core\services\commands\ticket\CancelTicketLineItemCommandHandler'                              => array(
543
+				'EventEspresso\core\domain\services\ticket\CancelTicketLineItemService' => EE_Dependency_Map::load_from_cache,
544
+			),
545
+			'EventEspresso\core\domain\services\registration\CancelRegistrationService'                                   => array(
546
+				'EventEspresso\core\domain\services\ticket\CancelTicketLineItemService' => EE_Dependency_Map::load_from_cache,
547
+			),
548
+			'EventEspresso\core\services\commands\attendee\CreateAttendeeCommandHandler'                                  => array(
549
+				'EEM_Attendee' => EE_Dependency_Map::load_from_cache,
550
+			),
551
+			'EventEspresso\core\services\database\TableManager'                                                           => array(
552
+				'EventEspresso\core\services\database\TableAnalysis' => EE_Dependency_Map::load_from_cache,
553
+			),
554
+			'EE_Data_Migration_Class_Base'                                                                                => array(
555
+				'EventEspresso\core\services\database\TableAnalysis' => EE_Dependency_Map::load_from_cache,
556
+				'EventEspresso\core\services\database\TableManager'  => EE_Dependency_Map::load_from_cache,
557
+			),
558
+			'EE_DMS_Core_4_1_0'                                                                                           => array(
559
+				'EventEspresso\core\services\database\TableAnalysis' => EE_Dependency_Map::load_from_cache,
560
+				'EventEspresso\core\services\database\TableManager'  => EE_Dependency_Map::load_from_cache,
561
+			),
562
+			'EE_DMS_Core_4_2_0'                                                                                           => array(
563
+				'EventEspresso\core\services\database\TableAnalysis' => EE_Dependency_Map::load_from_cache,
564
+				'EventEspresso\core\services\database\TableManager'  => EE_Dependency_Map::load_from_cache,
565
+			),
566
+			'EE_DMS_Core_4_3_0'                                                                                           => array(
567
+				'EventEspresso\core\services\database\TableAnalysis' => EE_Dependency_Map::load_from_cache,
568
+				'EventEspresso\core\services\database\TableManager'  => EE_Dependency_Map::load_from_cache,
569
+			),
570
+			'EE_DMS_Core_4_4_0'                                                                                           => array(
571
+				'EventEspresso\core\services\database\TableAnalysis' => EE_Dependency_Map::load_from_cache,
572
+				'EventEspresso\core\services\database\TableManager'  => EE_Dependency_Map::load_from_cache,
573
+			),
574
+			'EE_DMS_Core_4_5_0'                                                                                           => array(
575
+				'EventEspresso\core\services\database\TableAnalysis' => EE_Dependency_Map::load_from_cache,
576
+				'EventEspresso\core\services\database\TableManager'  => EE_Dependency_Map::load_from_cache,
577
+			),
578
+			'EE_DMS_Core_4_6_0'                                                                                           => array(
579
+				'EventEspresso\core\services\database\TableAnalysis' => EE_Dependency_Map::load_from_cache,
580
+				'EventEspresso\core\services\database\TableManager'  => EE_Dependency_Map::load_from_cache,
581
+			),
582
+			'EE_DMS_Core_4_7_0'                                                                                           => array(
583
+				'EventEspresso\core\services\database\TableAnalysis' => EE_Dependency_Map::load_from_cache,
584
+				'EventEspresso\core\services\database\TableManager'  => EE_Dependency_Map::load_from_cache,
585
+			),
586
+			'EE_DMS_Core_4_8_0'                                                                                           => array(
587
+				'EventEspresso\core\services\database\TableAnalysis' => EE_Dependency_Map::load_from_cache,
588
+				'EventEspresso\core\services\database\TableManager'  => EE_Dependency_Map::load_from_cache,
589
+			),
590
+			'EE_DMS_Core_4_9_0'                                                                                           => array(
591
+				'EventEspresso\core\services\database\TableAnalysis' => EE_Dependency_Map::load_from_cache,
592
+				'EventEspresso\core\services\database\TableManager'  => EE_Dependency_Map::load_from_cache,
593
+			),
594
+			'EventEspresso\core\services\assets\Registry'                                                                 => array(
595
+				'EE_Template_Config' => EE_Dependency_Map::load_from_cache,
596
+				'EE_Currency_Config' => EE_Dependency_Map::load_from_cache,
597
+			),
598
+			'EventEspresso\core\domain\entities\shortcodes\EspressoCancelled'                                             => array(
599
+				'EventEspresso\core\services\cache\PostRelatedCacheManager' => EE_Dependency_Map::load_from_cache,
600
+			),
601
+			'EventEspresso\core\domain\entities\shortcodes\EspressoCheckout'                                              => array(
602
+				'EventEspresso\core\services\cache\PostRelatedCacheManager' => EE_Dependency_Map::load_from_cache,
603
+			),
604
+			'EventEspresso\core\domain\entities\shortcodes\EspressoEventAttendees'                                        => array(
605
+				'EventEspresso\core\services\cache\PostRelatedCacheManager' => EE_Dependency_Map::load_from_cache,
606
+			),
607
+			'EventEspresso\core\domain\entities\shortcodes\EspressoEvents'                                                => array(
608
+				'EventEspresso\core\services\cache\PostRelatedCacheManager' => EE_Dependency_Map::load_from_cache,
609
+			),
610
+			'EventEspresso\core\domain\entities\shortcodes\EspressoThankYou'                                              => array(
611
+				'EventEspresso\core\services\cache\PostRelatedCacheManager' => EE_Dependency_Map::load_from_cache,
612
+			),
613
+			'EventEspresso\core\domain\entities\shortcodes\EspressoTicketSelector'                                        => array(
614
+				'EventEspresso\core\services\cache\PostRelatedCacheManager' => EE_Dependency_Map::load_from_cache,
615
+			),
616
+			'EventEspresso\core\domain\entities\shortcodes\EspressoTxnPage'                                               => array(
617
+				'EventEspresso\core\services\cache\PostRelatedCacheManager' => EE_Dependency_Map::load_from_cache,
618
+			),
619
+			'EventEspresso\core\services\cache\BasicCacheManager'                        => array(
620
+				'EventEspresso\core\services\cache\TransientCacheStorage' => EE_Dependency_Map::load_from_cache,
621
+			),
622
+			'EventEspresso\core\services\cache\PostRelatedCacheManager'                  => array(
623
+				'EventEspresso\core\services\cache\TransientCacheStorage' => EE_Dependency_Map::load_from_cache,
624
+			),
625
+			'EventEspresso\core\domain\services\validation\email\EmailValidationService' => array(
626
+				'EE_Registration_Config'                                  => EE_Dependency_Map::load_from_cache,
627
+				'EventEspresso\core\services\loaders\Loader'              => EE_Dependency_Map::load_from_cache,
628
+			),
629
+			'EventEspresso\core\domain\values\EmailAddress'                              => array(
630
+				null,
631
+				'EventEspresso\core\domain\services\validation\email\EmailValidationService' => EE_Dependency_Map::load_from_cache,
632
+			),
633
+			'EventEspresso\core\services\orm\ModelFieldFactory' => array(
634
+				'EventEspresso\core\services\loaders\Loader'              => EE_Dependency_Map::load_from_cache,
635
+			),
636
+			'LEGACY_MODELS'                                                   => array(
637
+				null,
638
+				'EventEspresso\core\services\database\ModelFieldFactory' => EE_Dependency_Map::load_from_cache,
639
+			),
640
+			'EE_Module_Request_Router' => array(
641
+				'EE_Request' => EE_Dependency_Map::load_from_cache,
642
+			),
643
+			'EE_Registration_Processor' => array(
644
+				'EE_Request' => EE_Dependency_Map::load_from_cache,
645
+			),
646
+			'EventEspresso\core\services\notifications\PersistentAdminNoticeManager' => array(
647
+				null,
648
+				'EventEspresso\core\domain\services\capabilities\CapabilitiesChecker' => EE_Dependency_Map::load_from_cache,
649
+				'EE_Request' => EE_Dependency_Map::load_from_cache,
650
+			),
651
+		);
652
+	}
653
+
654
+
655
+
656
+	/**
657
+	 * Registers how core classes are loaded.
658
+	 * This can either be done by simply providing the name of one of the EE_Registry loader methods such as:
659
+	 *        'EE_Request_Handler' => 'load_core'
660
+	 *        'EE_Messages_Queue'  => 'load_lib'
661
+	 *        'EEH_Debug_Tools'    => 'load_helper'
662
+	 * or, if greater control is required, by providing a custom closure. For example:
663
+	 *        'Some_Class' => function () {
664
+	 *            return new Some_Class();
665
+	 *        },
666
+	 * This is required for instantiating dependencies
667
+	 * where an interface has been type hinted in a class constructor. For example:
668
+	 *        'Required_Interface' => function () {
669
+	 *            return new A_Class_That_Implements_Required_Interface();
670
+	 *        },
671
+	 *
672
+	 * @throws InvalidInterfaceException
673
+	 * @throws InvalidDataTypeException
674
+	 * @throws InvalidArgumentException
675
+	 */
676
+	protected function _register_core_class_loaders()
677
+	{
678
+		//for PHP5.3 compat, we need to register any properties called here in a variable because `$this` cannot
679
+		//be used in a closure.
680
+		$request = &$this->_request;
681
+		$response = &$this->_response;
682
+		// $loader = &$this->loader;
683
+		$this->_class_loaders = array(
684
+			//load_core
685
+			'EE_Capabilities'                      => 'load_core',
686
+			'EE_Encryption'                        => 'load_core',
687
+			'EE_Front_Controller'                  => 'load_core',
688
+			'EE_Module_Request_Router'             => 'load_core',
689
+			'EE_Registry'                          => 'load_core',
690
+			'EE_Request'                           => function () use (&$request) {
691
+				return $request;
692
+			},
693
+			'EE_Response'                          => function () use (&$response) {
694
+				return $response;
695
+			},
696
+			'EE_Request_Handler'                   => 'load_core',
697
+			'EE_Session'                           => 'load_core',
698
+			'EE_Cron_Tasks'                        => 'load_core',
699
+			'EE_System'                            => 'load_core',
700
+			'EE_Maintenance_Mode'                  => 'load_core',
701
+			'EE_Register_CPTs'                     => 'load_core',
702
+			'EE_Admin'                             => 'load_core',
703
+			//load_lib
704
+			'EE_Message_Resource_Manager'          => 'load_lib',
705
+			'EE_Message_Type_Collection'           => 'load_lib',
706
+			'EE_Message_Type_Collection_Loader'    => 'load_lib',
707
+			'EE_Messenger_Collection'              => 'load_lib',
708
+			'EE_Messenger_Collection_Loader'       => 'load_lib',
709
+			'EE_Messages_Processor'                => 'load_lib',
710
+			'EE_Message_Repository'                => 'load_lib',
711
+			'EE_Messages_Queue'                    => 'load_lib',
712
+			'EE_Messages_Data_Handler_Collection'  => 'load_lib',
713
+			'EE_Message_Template_Group_Collection' => 'load_lib',
714
+			'EE_Payment_Method_Manager'            => 'load_lib',
715
+			'EE_Messages_Generator'                => function () {
716
+				return EE_Registry::instance()->load_lib(
717
+					'Messages_Generator',
718
+					array(),
719
+					false,
720
+					false
721
+				);
722
+			},
723
+			'EE_Messages_Template_Defaults'        => function ($arguments = array()) {
724
+				return EE_Registry::instance()->load_lib(
725
+					'Messages_Template_Defaults',
726
+					$arguments,
727
+					false,
728
+					false
729
+				);
730
+			},
731
+			//load_model
732
+			// 'EEM_Attendee'                         => 'load_model',
733
+			// 'EEM_Message_Template_Group'           => 'load_model',
734
+			// 'EEM_Message_Template'                 => 'load_model',
735
+			//load_helper
736
+			'EEH_Parse_Shortcodes'                 => function () {
737
+				if (EE_Registry::instance()->load_helper('Parse_Shortcodes')) {
738
+					return new EEH_Parse_Shortcodes();
739
+				}
740
+				return null;
741
+			},
742
+			'EE_Template_Config'                   => function () {
743
+				return EE_Config::instance()->template_settings;
744
+			},
745
+			'EE_Currency_Config'                   => function () {
746
+				return EE_Config::instance()->currency;
747
+			},
748
+			'EE_Registration_Config'                   => function () {
749
+				return EE_Config::instance()->registration;
750
+			},
751
+			'EventEspresso\core\services\loaders\Loader' => function () {
752
+				return LoaderFactory::getLoader();
753
+			},
754
+		);
755
+	}
756
+
757
+
758
+
759
+	/**
760
+	 * can be used for supplying alternate names for classes,
761
+	 * or for connecting interface names to instantiable classes
762
+	 */
763
+	protected function _register_core_aliases()
764
+	{
765
+		$this->_aliases = array(
766
+			'CommandBusInterface'                                                          => 'EventEspresso\core\services\commands\CommandBusInterface',
767
+			'EventEspresso\core\services\commands\CommandBusInterface'                     => 'EventEspresso\core\services\commands\CommandBus',
768
+			'CommandHandlerManagerInterface'                                               => 'EventEspresso\core\services\commands\CommandHandlerManagerInterface',
769
+			'EventEspresso\core\services\commands\CommandHandlerManagerInterface'          => 'EventEspresso\core\services\commands\CommandHandlerManager',
770
+			'CapChecker'                                                                   => 'EventEspresso\core\services\commands\middleware\CapChecker',
771
+			'AddActionHook'                                                                => 'EventEspresso\core\services\commands\middleware\AddActionHook',
772
+			'CapabilitiesChecker'                                                          => 'EventEspresso\core\domain\services\capabilities\CapabilitiesChecker',
773
+			'CapabilitiesCheckerInterface'                                                 => 'EventEspresso\core\domain\services\capabilities\CapabilitiesCheckerInterface',
774
+			'EventEspresso\core\domain\services\capabilities\CapabilitiesCheckerInterface' => 'EventEspresso\core\domain\services\capabilities\CapabilitiesChecker',
775
+			'CreateRegistrationService'                                                    => 'EventEspresso\core\domain\services\registration\CreateRegistrationService',
776
+			'CreateRegCodeCommandHandler'                                                  => 'EventEspresso\core\services\commands\registration\CreateRegCodeCommand',
777
+			'CreateRegUrlLinkCommandHandler'                                               => 'EventEspresso\core\services\commands\registration\CreateRegUrlLinkCommand',
778
+			'CreateRegistrationCommandHandler'                                             => 'EventEspresso\core\services\commands\registration\CreateRegistrationCommand',
779
+			'CopyRegistrationDetailsCommandHandler'                                        => 'EventEspresso\core\services\commands\registration\CopyRegistrationDetailsCommand',
780
+			'CopyRegistrationPaymentsCommandHandler'                                       => 'EventEspresso\core\services\commands\registration\CopyRegistrationPaymentsCommand',
781
+			'CancelRegistrationAndTicketLineItemCommandHandler'                            => 'EventEspresso\core\services\commands\registration\CancelRegistrationAndTicketLineItemCommandHandler',
782
+			'UpdateRegistrationAndTransactionAfterChangeCommandHandler'                    => 'EventEspresso\core\services\commands\registration\UpdateRegistrationAndTransactionAfterChangeCommandHandler',
783
+			'CreateTicketLineItemCommandHandler'                                           => 'EventEspresso\core\services\commands\ticket\CreateTicketLineItemCommand',
784
+			'CreateTransactionCommandHandler'                                     => 'EventEspresso\core\services\commands\transaction\CreateTransactionCommandHandler',
785
+			'CreateAttendeeCommandHandler'                                        => 'EventEspresso\core\services\commands\attendee\CreateAttendeeCommandHandler',
786
+			'TableManager'                                                                 => 'EventEspresso\core\services\database\TableManager',
787
+			'TableAnalysis'                                                                => 'EventEspresso\core\services\database\TableAnalysis',
788
+			'EspressoShortcode'                                                            => 'EventEspresso\core\services\shortcodes\EspressoShortcode',
789
+			'ShortcodeInterface'                                                           => 'EventEspresso\core\services\shortcodes\ShortcodeInterface',
790
+			'EventEspresso\core\services\shortcodes\ShortcodeInterface'                    => 'EventEspresso\core\services\shortcodes\EspressoShortcode',
791
+			'EventEspresso\core\services\cache\CacheStorageInterface'                      => 'EventEspresso\core\services\cache\TransientCacheStorage',
792
+			'LoaderInterface'                                                              => 'EventEspresso\core\services\loaders\LoaderInterface',
793
+			'EventEspresso\core\services\loaders\LoaderInterface'                          => 'EventEspresso\core\services\loaders\Loader',
794
+			'CommandFactoryInterface'                                                     => 'EventEspresso\core\services\commands\CommandFactoryInterface',
795
+			'EventEspresso\core\services\commands\CommandFactoryInterface'                => 'EventEspresso\core\services\commands\CommandFactory',
796
+			'EventEspresso\core\domain\services\session\SessionIdentifierInterface'       => 'EE_Session',
797
+			'EmailValidatorInterface'                                                     => 'EventEspresso\core\domain\services\validation\email\EmailValidatorInterface',
798
+			'EventEspresso\core\domain\services\validation\email\EmailValidatorInterface' => 'EventEspresso\core\domain\services\validation\email\EmailValidationService',
799
+			'NoticeConverterInterface'                                            => 'EventEspresso\core\services\notices\NoticeConverterInterface',
800
+			'EventEspresso\core\services\notices\NoticeConverterInterface'        => 'EventEspresso\core\services\notices\ConvertNoticesToEeErrors',
801
+			'NoticesContainerInterface'                                            => 'EventEspresso\core\services\notices\NoticesContainerInterface',
802
+			'EventEspresso\core\services\notices\NoticesContainerInterface'        => 'EventEspresso\core\services\notices\NoticesContainer',
803
+		);
804
+		if (! (defined('DOING_AJAX') && DOING_AJAX) && is_admin()) {
805
+			$this->_aliases['EventEspresso\core\services\notices\NoticeConverterInterface'] = 'EventEspresso\core\services\notices\ConvertNoticesToAdminNotices';
806
+		}
807
+	}
808
+
809
+
810
+
811
+	/**
812
+	 * This is used to reset the internal map and class_loaders to their original default state at the beginning of the
813
+	 * request Primarily used by unit tests.
814
+	 *
815
+	 * @throws InvalidDataTypeException
816
+	 * @throws InvalidInterfaceException
817
+	 * @throws InvalidArgumentException
818
+	 */
819
+	public function reset()
820
+	{
821
+		$this->_register_core_class_loaders();
822
+		$this->_register_core_dependencies();
823
+	}
824 824
 
825 825
 
826 826
 }
Please login to merge, or discard this patch.
modules/core_rest_api/EED_Core_Rest_Api.module.php 1 patch
Indentation   +1295 added lines, -1295 removed lines patch added patch discarded remove patch
@@ -24,1302 +24,1302 @@
 block discarded – undo
24 24
 class EED_Core_Rest_Api extends \EED_Module
25 25
 {
26 26
 
27
-    const ee_api_namespace           = 'ee/v';
27
+	const ee_api_namespace           = 'ee/v';
28 28
 
29
-    const ee_api_namespace_for_regex = 'ee\/v([^/]*)\/';
30
-
31
-    const saved_routes_option_names  = 'ee_core_routes';
32
-
33
-    /**
34
-     * string used in _links response bodies to make them globally unique.
35
-     *
36
-     * @see http://v2.wp-api.org/extending/linking/
37
-     */
38
-    const ee_api_link_namespace = 'https://api.eventespresso.com/';
39
-
40
-    /**
41
-     * @var CalculatedModelFields
42
-     */
43
-    protected static $_field_calculator;
44
-
45
-
46
-
47
-    /**
48
-     * @return EED_Core_Rest_Api|EED_Module
49
-     */
50
-    public static function instance()
51
-    {
52
-        self::$_field_calculator = new CalculatedModelFields();
53
-        return parent::get_instance(__CLASS__);
54
-    }
55
-
56
-
57
-
58
-    /**
59
-     *    set_hooks - for hooking into EE Core, other modules, etc
60
-     *
61
-     * @access    public
62
-     * @return    void
63
-     */
64
-    public static function set_hooks()
65
-    {
66
-        self::set_hooks_both();
67
-    }
68
-
69
-
70
-
71
-    /**
72
-     *    set_hooks_admin - for hooking into EE Admin Core, other modules, etc
73
-     *
74
-     * @access    public
75
-     * @return    void
76
-     */
77
-    public static function set_hooks_admin()
78
-    {
79
-        self::set_hooks_both();
80
-    }
81
-
82
-
83
-
84
-    public static function set_hooks_both()
85
-    {
86
-        add_action('rest_api_init', array('EED_Core_Rest_Api', 'register_routes'), 10);
87
-        add_action('rest_api_init', array('EED_Core_Rest_Api', 'set_hooks_rest_api'), 5);
88
-        add_filter('rest_route_data', array('EED_Core_Rest_Api', 'hide_old_endpoints'), 10, 2);
89
-        add_filter('rest_index',
90
-            array('EventEspresso\core\libraries\rest_api\controllers\model\Meta', 'filterEeMetadataIntoIndex'));
91
-        EED_Core_Rest_Api::invalidate_cached_route_data_on_version_change();
92
-    }
93
-
94
-
95
-
96
-    /**
97
-     * sets up hooks which only need to be included as part of REST API requests;
98
-     * other requests like to the frontend or admin etc don't need them
99
-     *
100
-     * @throws \EE_Error
101
-     */
102
-    public static function set_hooks_rest_api()
103
-    {
104
-        //set hooks which account for changes made to the API
105
-        EED_Core_Rest_Api::_set_hooks_for_changes();
106
-    }
107
-
108
-
109
-
110
-    /**
111
-     * public wrapper of _set_hooks_for_changes.
112
-     * Loads all the hooks which make requests to old versions of the API
113
-     * appear the same as they always did
114
-     *
115
-     * @throws EE_Error
116
-     */
117
-    public static function set_hooks_for_changes()
118
-    {
119
-        self::_set_hooks_for_changes();
120
-    }
121
-
122
-
123
-
124
-    /**
125
-     * If the user appears to be using WP API basic auth, tell them (via a persistent
126
-     * admin notice and an email) that we're going to remove it soon, so they should
127
-     * replace it with application passwords.
128
-     *
129
-     * @throws InvalidDataTypeException
130
-     */
131
-    public static function maybe_notify_of_basic_auth_removal()
132
-    {
133
-        if (
134
-        apply_filters(
135
-            'FHEE__EED_Core_Rest_Api__maybe_notify_of_basic_auth_removal__override',
136
-            ! isset($_SERVER['PHP_AUTH_USER'])
137
-            && ! isset($_SERVER['HTTP_AUTHORIZATION'])
138
-        )
139
-        ) {
140
-            //sure it's a WP API request, but they aren't using basic auth, so don't bother them
141
-            return;
142
-        }
143
-        //ok they're using the WP API with Basic Auth
144
-        new PersistentAdminNotice(
145
-            'using_basic_auth',
146
-            sprintf(
147
-                __(
148
-                    'We noticed you\'re using the WP API, which is used by the Event Espresso 4 mobile apps. Because of security and compatibility concerns, we will soon be removing our default authentication mechanism, WP API Basic Auth, from Event Espresso. It is recommended you instead install the %1$sWP Application Passwords plugin%2$s and use it with the EE4 Mobile apps. See %3$sour mobile app documentation%2$s for more information. %4$sIf you have installed the WP API Basic Auth plugin separately, or are not using the Event Espresso 4 mobile apps, you can disregard this message.%4$sThe Event Espresso Team',
149
-                    'event_espresso'
150
-                ),
151
-                '<a href="https://wordpress.org/plugins/application-passwords/">',
152
-                '</a>',
153
-                '<a href="https://eventespresso.com/wiki/ee4-event-apps/#authentication">',
154
-                '<br/>'
155
-            )
156
-        );
157
-        if ( ! get_option('ee_notified_admin_on_basic_auth_removal', false)) {
158
-            add_option('ee_notified_admin_on_basic_auth_removal', true);
159
-            //piggy back off EE_Error::set_content_type, which sets the content type to HTML
160
-            add_filter('wp_mail_content_type', array('EE_Error', 'set_content_type'));
161
-            //and send the message to the site admin too
162
-            wp_mail(get_option('admin_email'),
163
-                __('Notice of Removal of WP API Basic Auth From Event Espresso 4', 'event_espresso'), $message);
164
-            remove_filter('wp_mail_content_type', array('EE_Error', 'set_content_type'));
165
-        }
166
-    }
167
-
168
-
169
-
170
-    /**
171
-     * Loads all the hooks which make requests to old versions of the API
172
-     * appear the same as they always did
173
-     *
174
-     * @throws EE_Error
175
-     */
176
-    protected static function _set_hooks_for_changes()
177
-    {
178
-        $folder_contents = EEH_File::get_contents_of_folders(array(EE_LIBRARIES . 'rest_api' . DS . 'changes'), false);
179
-        foreach ($folder_contents as $classname_in_namespace => $filepath) {
180
-            //ignore the base parent class
181
-            //and legacy named classes
182
-            if ($classname_in_namespace === 'ChangesInBase'
183
-                || strpos($classname_in_namespace, 'Changes_In_') === 0
184
-            ) {
185
-                continue;
186
-            }
187
-            $full_classname = 'EventEspresso\core\libraries\rest_api\changes\\' . $classname_in_namespace;
188
-            if (class_exists($full_classname)) {
189
-                $instance_of_class = new $full_classname;
190
-                if ($instance_of_class instanceof ChangesInBase) {
191
-                    $instance_of_class->setHooks();
192
-                }
193
-            }
194
-        }
195
-    }
196
-
197
-
198
-
199
-    /**
200
-     * Filters the WP routes to add our EE-related ones. This takes a bit of time
201
-     * so we actually prefer to only do it when an EE plugin is activated or upgraded
202
-     *
203
-     * @throws \EE_Error
204
-     */
205
-    public static function register_routes()
206
-    {
207
-        foreach (EED_Core_Rest_Api::get_ee_route_data() as $namespace => $relative_routes) {
208
-            foreach ($relative_routes as $relative_route => $data_for_multiple_endpoints) {
209
-                /**
210
-                 * @var array $data_for_multiple_endpoints numerically indexed array
211
-                 *                                         but can also contain route options like {
212
-                 * @type array    $schema                      {
213
-                 * @type callable $schema_callback
214
-                 * @type array    $callback_args               arguments that will be passed to the callback, after the
215
-                 * WP_REST_Request of course
216
-                 * }
217
-                 * }
218
-                 */
219
-                //when registering routes, register all the endpoints' data at the same time
220
-                $multiple_endpoint_args = array();
221
-                foreach ($data_for_multiple_endpoints as $endpoint_key => $data_for_single_endpoint) {
222
-                    /**
223
-                     * @var array     $data_for_single_endpoint {
224
-                     * @type callable $callback
225
-                     * @type string methods
226
-                     * @type array args
227
-                     * @type array _links
228
-                     * @type array    $callback_args            arguments that will be passed to the callback, after the
229
-                     * WP_REST_Request of course
230
-                     * }
231
-                     */
232
-                    //skip route options
233
-                    if (! is_numeric($endpoint_key)) {
234
-                        continue;
235
-                    }
236
-                    if (! isset($data_for_single_endpoint['callback'], $data_for_single_endpoint['methods'])) {
237
-                        throw new EE_Error(
238
-                            esc_html__(
239
-                                // @codingStandardsIgnoreStart
240
-                                'Endpoint configuration data needs to have entries "callback" (callable) and "methods" (comma-separated list of accepts HTTP methods).',
241
-                                // @codingStandardsIgnoreEnd
242
-                                'event_espresso')
243
-                        );
244
-                    }
245
-                    $callback = $data_for_single_endpoint['callback'];
246
-                    $single_endpoint_args = array(
247
-                        'methods' => $data_for_single_endpoint['methods'],
248
-                        'args'    => isset($data_for_single_endpoint['args']) ? $data_for_single_endpoint['args']
249
-                            : array(),
250
-                    );
251
-                    if (isset($data_for_single_endpoint['_links'])) {
252
-                        $single_endpoint_args['_links'] = $data_for_single_endpoint['_links'];
253
-                    }
254
-                    if (isset($data_for_single_endpoint['callback_args'])) {
255
-                        $callback_args = $data_for_single_endpoint['callback_args'];
256
-                        $single_endpoint_args['callback'] = function (\WP_REST_Request $request) use (
257
-                            $callback,
258
-                            $callback_args
259
-                        ) {
260
-                            array_unshift($callback_args, $request);
261
-                            return call_user_func_array(
262
-                                $callback,
263
-                                $callback_args
264
-                            );
265
-                        };
266
-                    } else {
267
-                        $single_endpoint_args['callback'] = $data_for_single_endpoint['callback'];
268
-                    }
269
-                    $multiple_endpoint_args[] = $single_endpoint_args;
270
-                }
271
-                if (isset($data_for_multiple_endpoints['schema'])) {
272
-                    $schema_route_data = $data_for_multiple_endpoints['schema'];
273
-                    $schema_callback = $schema_route_data['schema_callback'];
274
-                    $callback_args = $schema_route_data['callback_args'];
275
-                    $multiple_endpoint_args['schema'] = function () use ($schema_callback, $callback_args) {
276
-                        return call_user_func_array(
277
-                            $schema_callback,
278
-                            $callback_args
279
-                        );
280
-                    };
281
-                }
282
-                register_rest_route(
283
-                    $namespace,
284
-                    $relative_route,
285
-                    $multiple_endpoint_args
286
-                );
287
-            }
288
-        }
289
-    }
290
-
291
-
292
-
293
-    /**
294
-     * Checks if there was a version change or something that merits invalidating the cached
295
-     * route data. If so, invalidates the cached route data so that it gets refreshed
296
-     * next time the WP API is used
297
-     */
298
-    public static function invalidate_cached_route_data_on_version_change()
299
-    {
300
-        if (EE_System::instance()->detect_req_type() !== EE_System::req_type_normal) {
301
-            EED_Core_Rest_Api::invalidate_cached_route_data();
302
-        }
303
-        foreach (EE_Registry::instance()->addons as $addon) {
304
-            if ($addon instanceof EE_Addon && $addon->detect_req_type() !== EE_System::req_type_normal) {
305
-                EED_Core_Rest_Api::invalidate_cached_route_data();
306
-            }
307
-        }
308
-    }
309
-
310
-
311
-
312
-    /**
313
-     * Removes the cached route data so it will get refreshed next time the WP API is used
314
-     */
315
-    public static function invalidate_cached_route_data()
316
-    {
317
-        //delete the saved EE REST API routes
318
-        foreach (EED_Core_Rest_Api::versions_served() as $version => $hidden) {
319
-            delete_option(EED_Core_Rest_Api::saved_routes_option_names . $version);
320
-        }
321
-    }
322
-
323
-
324
-
325
-    /**
326
-     * Gets the EE route data
327
-     *
328
-     * @return array top-level key is the namespace, next-level key is the route and its value is array{
329
-     * @throws \EE_Error
330
-     * @type string|array $callback
331
-     * @type string       $methods
332
-     * @type boolean      $hidden_endpoint
333
-     * }
334
-     */
335
-    public static function get_ee_route_data()
336
-    {
337
-        $ee_routes = array();
338
-        foreach (self::versions_served() as $version => $hidden_endpoints) {
339
-            $ee_routes[self::ee_api_namespace . $version] = self::_get_ee_route_data_for_version(
340
-                $version,
341
-                $hidden_endpoints
342
-            );
343
-        }
344
-        return $ee_routes;
345
-    }
346
-
347
-
348
-
349
-    /**
350
-     * Gets the EE route data from the wp options if it exists already,
351
-     * otherwise re-generates it and saves it to the option
352
-     *
353
-     * @param string  $version
354
-     * @param boolean $hidden_endpoints
355
-     * @return array
356
-     * @throws \EE_Error
357
-     */
358
-    protected static function _get_ee_route_data_for_version($version, $hidden_endpoints = false)
359
-    {
360
-        $ee_routes = get_option(self::saved_routes_option_names . $version, null);
361
-        if (! $ee_routes || (defined('EE_REST_API_DEBUG_MODE') && EE_REST_API_DEBUG_MODE)) {
362
-            $ee_routes = self::_save_ee_route_data_for_version($version, $hidden_endpoints);
363
-        }
364
-        return $ee_routes;
365
-    }
366
-
367
-
368
-
369
-    /**
370
-     * Saves the EE REST API route data to a wp option and returns it
371
-     *
372
-     * @param string  $version
373
-     * @param boolean $hidden_endpoints
374
-     * @return mixed|null
375
-     * @throws \EE_Error
376
-     */
377
-    protected static function _save_ee_route_data_for_version($version, $hidden_endpoints = false)
378
-    {
379
-        $instance = self::instance();
380
-        $routes = apply_filters(
381
-            'EED_Core_Rest_Api__save_ee_route_data_for_version__routes',
382
-            array_replace_recursive(
383
-                $instance->_get_config_route_data_for_version($version, $hidden_endpoints),
384
-                $instance->_get_meta_route_data_for_version($version, $hidden_endpoints),
385
-                $instance->_get_model_route_data_for_version($version, $hidden_endpoints),
386
-                $instance->_get_rpc_route_data_for_version($version, $hidden_endpoints)
387
-            )
388
-        );
389
-        $option_name = self::saved_routes_option_names . $version;
390
-        if (get_option($option_name)) {
391
-            update_option($option_name, $routes, true);
392
-        } else {
393
-            add_option($option_name, $routes, null, 'no');
394
-        }
395
-        return $routes;
396
-    }
397
-
398
-
399
-
400
-    /**
401
-     * Calculates all the EE routes and saves it to a WordPress option so we don't
402
-     * need to calculate it on every request
403
-     *
404
-     * @deprecated since version 4.9.1
405
-     * @return void
406
-     */
407
-    public static function save_ee_routes()
408
-    {
409
-        if (EE_Maintenance_Mode::instance()->models_can_query()) {
410
-            $instance = self::instance();
411
-            $routes = apply_filters(
412
-                'EED_Core_Rest_Api__save_ee_routes__routes',
413
-                array_replace_recursive(
414
-                    $instance->_register_config_routes(),
415
-                    $instance->_register_meta_routes(),
416
-                    $instance->_register_model_routes(),
417
-                    $instance->_register_rpc_routes()
418
-                )
419
-            );
420
-            update_option(self::saved_routes_option_names, $routes, true);
421
-        }
422
-    }
423
-
424
-
425
-
426
-    /**
427
-     * Gets all the route information relating to EE models
428
-     *
429
-     * @return array @see get_ee_route_data
430
-     * @deprecated since version 4.9.1
431
-     */
432
-    protected function _register_model_routes()
433
-    {
434
-        $model_routes = array();
435
-        foreach (self::versions_served() as $version => $hidden_endpoint) {
436
-            $model_routes[EED_Core_Rest_Api::ee_api_namespace
437
-                          . $version] = $this->_get_config_route_data_for_version($version, $hidden_endpoint);
438
-        }
439
-        return $model_routes;
440
-    }
441
-
442
-
443
-
444
-    /**
445
-     * Decides whether or not to add write endpoints for this model.
446
-     *
447
-     * Currently, this defaults to exclude all global tables and models
448
-     * which would allow inserting WP core data (we don't want to duplicate
449
-     * what WP API does, as it's unnecessary, extra work, and potentially extra bugs)
450
-     * @param EEM_Base $model
451
-     * @return bool
452
-     */
453
-    public static function should_have_write_endpoints(EEM_Base $model)
454
-    {
455
-        if ($model->is_wp_core_model()){
456
-            return false;
457
-        }
458
-        foreach($model->get_tables() as $table){
459
-            if( $table->is_global()){
460
-                return false;
461
-            }
462
-        }
463
-        return true;
464
-    }
465
-
466
-
467
-
468
-    /**
469
-     * Gets the names of all models which should have plural routes (eg `ee/v4.8.36/events`)
470
-     * in this versioned namespace of EE4
471
-     * @param $version
472
-     * @return array keys are model names (eg 'Event') and values ar either classnames (eg 'EEM_Event')
473
-     */
474
-    public static function model_names_with_plural_routes($version){
475
-        $model_version_info = new ModelVersionInfo($version);
476
-        $models_to_register = $model_version_info->modelsForRequestedVersion();
477
-        //let's not bother having endpoints for extra metas
478
-        unset(
479
-            $models_to_register['Extra_Meta'],
480
-            $models_to_register['Extra_Join'],
481
-            $models_to_register['Post_Meta']
482
-        );
483
-        return apply_filters(
484
-            'FHEE__EED_Core_REST_API___register_model_routes',
485
-            $models_to_register
486
-        );
487
-    }
488
-
489
-
490
-
491
-    /**
492
-     * Gets the route data for EE models in the specified version
493
-     *
494
-     * @param string  $version
495
-     * @param boolean $hidden_endpoint
496
-     * @return array
497
-     * @throws EE_Error
498
-     */
499
-    protected function _get_model_route_data_for_version($version, $hidden_endpoint = false)
500
-    {
501
-        $model_routes = array();
502
-        $model_version_info = new ModelVersionInfo($version);
503
-        foreach (EED_Core_Rest_Api::model_names_with_plural_routes($version) as $model_name => $model_classname) {
504
-            $model = \EE_Registry::instance()->load_model($model_name);
505
-            //if this isn't a valid model then let's skip iterate to the next item in the loop.
506
-            if (! $model instanceof EEM_Base) {
507
-                continue;
508
-            }
509
-            //yes we could just register one route for ALL models, but then they wouldn't show up in the index
510
-            $plural_model_route = EED_Core_Rest_Api::get_collection_route($model);
511
-            $singular_model_route = EED_Core_Rest_Api::get_entity_route($model, '(?P<id>[^\/]+)');
512
-            $model_routes[$plural_model_route] = array(
513
-                array(
514
-                    'callback'        => array(
515
-                        'EventEspresso\core\libraries\rest_api\controllers\model\Read',
516
-                        'handleRequestGetAll',
517
-                    ),
518
-                    'callback_args'   => array($version, $model_name),
519
-                    'methods'         => WP_REST_Server::READABLE,
520
-                    'hidden_endpoint' => $hidden_endpoint,
521
-                    'args'            => $this->_get_read_query_params($model, $version),
522
-                    '_links'          => array(
523
-                        'self' => rest_url(EED_Core_Rest_Api::ee_api_namespace . $version . $singular_model_route),
524
-                    ),
525
-                ),
526
-                'schema' => array(
527
-                    'schema_callback' => array(
528
-                        'EventEspresso\core\libraries\rest_api\controllers\model\Read',
529
-                        'handleSchemaRequest',
530
-                    ),
531
-                    'callback_args'   => array($version, $model_name),
532
-                ),
533
-            );
534
-            $model_routes[$singular_model_route] = array(
535
-                array(
536
-                    'callback'        => array(
537
-                        'EventEspresso\core\libraries\rest_api\controllers\model\Read',
538
-                        'handleRequestGetOne',
539
-                    ),
540
-                    'callback_args'   => array($version, $model_name),
541
-                    'methods'         => WP_REST_Server::READABLE,
542
-                    'hidden_endpoint' => $hidden_endpoint,
543
-                    'args'            => $this->_get_response_selection_query_params($model, $version),
544
-                ),
545
-            );
546
-            if( apply_filters(
547
-                'FHEE__EED_Core_Rest_Api___get_model_route_data_for_version__add_write_endpoints',
548
-                EED_Core_Rest_Api::should_have_write_endpoints($model),
549
-                $model
550
-            )){
551
-                $model_routes[$plural_model_route][] = array(
552
-                    'callback'        => array(
553
-                        'EventEspresso\core\libraries\rest_api\controllers\model\Write',
554
-                        'handleRequestInsert',
555
-                    ),
556
-                    'callback_args'   => array($version, $model_name),
557
-                    'methods'         => WP_REST_Server::CREATABLE,
558
-                    'hidden_endpoint' => $hidden_endpoint,
559
-                    'args'            => $this->_get_write_params($model_name, $model_version_info, true),
560
-                );
561
-                $model_routes[$singular_model_route] = array_merge(
562
-                    $model_routes[$singular_model_route],
563
-                    array(
564
-                        array(
565
-                            'callback'        => array(
566
-                                'EventEspresso\core\libraries\rest_api\controllers\model\Write',
567
-                                'handleRequestUpdate',
568
-                            ),
569
-                            'callback_args'   => array($version, $model_name),
570
-                            'methods'         => WP_REST_Server::EDITABLE,
571
-                            'hidden_endpoint' => $hidden_endpoint,
572
-                            'args'            => $this->_get_write_params($model_name, $model_version_info),
573
-                        ),
574
-                        array(
575
-                            'callback'        => array(
576
-                                'EventEspresso\core\libraries\rest_api\controllers\model\Write',
577
-                                'handleRequestDelete',
578
-                            ),
579
-                            'callback_args'   => array($version, $model_name),
580
-                            'methods'         => WP_REST_Server::DELETABLE,
581
-                            'hidden_endpoint' => $hidden_endpoint,
582
-                            'args'            => $this->_get_delete_query_params($model, $version),
583
-                        )
584
-                    )
585
-                );
586
-            }
587
-            foreach ($model->relation_settings() as $relation_name => $relation_obj) {
588
-
589
-                $related_route = EED_Core_Rest_Api::get_relation_route_via(
590
-                    $model,
591
-                    '(?P<id>[^\/]+)',
592
-                    $relation_obj
593
-                );
594
-                $endpoints = array(
595
-                    array(
596
-                        'callback'        => array(
597
-                            'EventEspresso\core\libraries\rest_api\controllers\model\Read',
598
-                            'handleRequestGetRelated',
599
-                        ),
600
-                        'callback_args'   => array($version, $model_name, $relation_name),
601
-                        'methods'         => WP_REST_Server::READABLE,
602
-                        'hidden_endpoint' => $hidden_endpoint,
603
-                        'args'            => $this->_get_read_query_params($relation_obj->get_other_model(), $version),
604
-                    ),
605
-                );
606
-                $model_routes[$related_route] = $endpoints;
607
-            }
608
-        }
609
-        return $model_routes;
610
-    }
611
-
612
-
613
-
614
-    /**
615
-     * Gets the relative URI to a model's REST API plural route, after the EE4 versioned namespace,
616
-     * excluding the preceding slash.
617
-     * Eg you pass get_plural_route_to('Event') = 'events'
618
-     *
619
-     * @param EEM_Base $model
620
-     * @return string
621
-     */
622
-    public static function get_collection_route(EEM_Base $model)
623
-    {
624
-        return EEH_Inflector::pluralize_and_lower($model->get_this_model_name());
625
-    }
626
-
627
-
628
-
629
-    /**
630
-     * Gets the relative URI to a model's REST API singular route, after the EE4 versioned namespace,
631
-     * excluding the preceding slash.
632
-     * Eg you pass get_plural_route_to('Event', 12) = 'events/12'
633
-     *
634
-     * @param EEM_Base $model eg Event or Venue
635
-     * @param string $id
636
-     * @return string
637
-     */
638
-    public static function get_entity_route($model, $id)
639
-    {
640
-        return EED_Core_Rest_Api::get_collection_route($model). '/' . $id;
641
-    }
642
-
643
-
644
-    /**
645
-     * Gets the relative URI to a model's REST API singular route, after the EE4 versioned namespace,
646
-     * excluding the preceding slash.
647
-     * Eg you pass get_plural_route_to('Event', 12) = 'events/12'
648
-     *
649
-     * @param EEM_Base                 $model eg Event or Venue
650
-     * @param string                 $id
651
-     * @param EE_Model_Relation_Base $relation_obj
652
-     * @return string
653
-     */
654
-    public static function get_relation_route_via(EEM_Base $model, $id, EE_Model_Relation_Base $relation_obj)
655
-    {
656
-        $related_model_name_endpoint_part = ModelRead::getRelatedEntityName(
657
-            $relation_obj->get_other_model()->get_this_model_name(),
658
-            $relation_obj
659
-        );
660
-        return EED_Core_Rest_Api::get_entity_route($model, $id) . '/' . $related_model_name_endpoint_part;
661
-    }
662
-
663
-
664
-
665
-    /**
666
-     * Adds onto the $relative_route the EE4 REST API versioned namespace.
667
-     * Eg if given '4.8.36' and 'events', will return 'ee/v4.8.36/events'
668
-     * @param string $relative_route
669
-     * @param string $version
670
-     * @return string
671
-     */
672
-    public static function get_versioned_route_to($relative_route, $version = '4.8.36'){
673
-        return '/' . EED_Core_Rest_Api::ee_api_namespace . $version . '/' . $relative_route;
674
-    }
675
-
676
-
677
-
678
-    /**
679
-     * Adds all the RPC-style routes (remote procedure call-like routes, ie
680
-     * routes that don't conform to the traditional REST CRUD-style).
681
-     *
682
-     * @deprecated since 4.9.1
683
-     */
684
-    protected function _register_rpc_routes()
685
-    {
686
-        $routes = array();
687
-        foreach (self::versions_served() as $version => $hidden_endpoint) {
688
-            $routes[self::ee_api_namespace . $version] = $this->_get_rpc_route_data_for_version(
689
-                $version,
690
-                $hidden_endpoint
691
-            );
692
-        }
693
-        return $routes;
694
-    }
695
-
696
-
697
-
698
-    /**
699
-     * @param string  $version
700
-     * @param boolean $hidden_endpoint
701
-     * @return array
702
-     */
703
-    protected function _get_rpc_route_data_for_version($version, $hidden_endpoint = false)
704
-    {
705
-        $this_versions_routes = array();
706
-        //checkin endpoint
707
-        $this_versions_routes['registrations/(?P<REG_ID>\d+)/toggle_checkin_for_datetime/(?P<DTT_ID>\d+)'] = array(
708
-            array(
709
-                'callback'        => array(
710
-                    'EventEspresso\core\libraries\rest_api\controllers\rpc\Checkin',
711
-                    'handleRequestToggleCheckin',
712
-                ),
713
-                'methods'         => WP_REST_Server::CREATABLE,
714
-                'hidden_endpoint' => $hidden_endpoint,
715
-                'args'            => array(
716
-                    'force' => array(
717
-                        'required'    => false,
718
-                        'default'     => false,
719
-                        'description' => __(
720
-                            // @codingStandardsIgnoreStart
721
-                            'Whether to force toggle checkin, or to verify the registration status and allowed ticket uses',
722
-                            // @codingStandardsIgnoreEnd
723
-                            'event_espresso'
724
-                        ),
725
-                    ),
726
-                ),
727
-                'callback_args'   => array($version),
728
-            ),
729
-        );
730
-        return apply_filters(
731
-            'FHEE__EED_Core_Rest_Api___register_rpc_routes__this_versions_routes',
732
-            $this_versions_routes,
733
-            $version,
734
-            $hidden_endpoint
735
-        );
736
-    }
737
-
738
-
739
-
740
-    /**
741
-     * Gets the query params that can be used when request one or many
742
-     *
743
-     * @param EEM_Base $model
744
-     * @param string   $version
745
-     * @return array
746
-     */
747
-    protected function _get_response_selection_query_params(\EEM_Base $model, $version)
748
-    {
749
-        return apply_filters(
750
-            'FHEE__EED_Core_Rest_Api___get_response_selection_query_params',
751
-            array(
752
-                'include'   => array(
753
-                    'required' => false,
754
-                    'default'  => '*',
755
-                    'type'     => 'string',
756
-                ),
757
-                'calculate' => array(
758
-                    'required'          => false,
759
-                    'default'           => '',
760
-                    'enum'              => self::$_field_calculator->retrieveCalculatedFieldsForModel($model),
761
-                    'type'              => 'string',
762
-                    //because we accept a CSV'd list of the enumerated strings, WP core validation and sanitization
763
-                    //freaks out. We'll just validate this argument while handling the request
764
-                    'validate_callback' => null,
765
-                    'sanitize_callback' => null,
766
-                ),
767
-            ),
768
-            $model,
769
-            $version
770
-        );
771
-    }
772
-
773
-
774
-
775
-    /**
776
-     * Gets the parameters acceptable for delete requests
777
-     *
778
-     * @param \EEM_Base $model
779
-     * @param string    $version
780
-     * @return array
781
-     */
782
-    protected function _get_delete_query_params(\EEM_Base $model, $version)
783
-    {
784
-        $params_for_delete = array(
785
-            'allow_blocking' => array(
786
-                'required' => false,
787
-                'default'  => true,
788
-                'type'     => 'boolean',
789
-            ),
790
-        );
791
-        $params_for_delete['force'] = array(
792
-            'required' => false,
793
-            'default'  => false,
794
-            'type'     => 'boolean',
795
-        );
796
-        return apply_filters(
797
-            'FHEE__EED_Core_Rest_Api___get_delete_query_params',
798
-            $params_for_delete,
799
-            $model,
800
-            $version
801
-        );
802
-    }
803
-
804
-
805
-
806
-    /**
807
-     * Gets info about reading query params that are acceptable
808
-     *
809
-     * @param \EEM_Base $model eg 'Event' or 'Venue'
810
-     * @param  string   $version
811
-     * @return array    describing the args acceptable when querying this model
812
-     * @throws EE_Error
813
-     */
814
-    protected function _get_read_query_params(\EEM_Base $model, $version)
815
-    {
816
-        $default_orderby = array();
817
-        foreach ($model->get_combined_primary_key_fields() as $key_field) {
818
-            $default_orderby[$key_field->get_name()] = 'ASC';
819
-        }
820
-        return array_merge(
821
-            $this->_get_response_selection_query_params($model, $version),
822
-            array(
823
-                'where'    => array(
824
-                    'required' => false,
825
-                    'default'  => array(),
826
-                    'type'     => 'object',
827
-                    //because we accept an almost infinite list of possible where conditions, WP
828
-                    // core validation and sanitization freaks out. We'll just validate this argument
829
-                    // while handling the request
830
-                    'validate_callback' => null,
831
-                    'sanitize_callback' => null,
832
-                ),
833
-                'limit'    => array(
834
-                    'required' => false,
835
-                    'default'  => EED_Core_Rest_Api::get_default_query_limit(),
836
-                    'type'     => array(
837
-                        'array',
838
-                        'string',
839
-                        'integer',
840
-                    ),
841
-                    //because we accept a variety of types, WP core validation and sanitization
842
-                    //freaks out. We'll just validate this argument while handling the request
843
-                    'validate_callback' => null,
844
-                    'sanitize_callback' => null,
845
-                ),
846
-                'order_by' => array(
847
-                    'required' => false,
848
-                    'default'  => $default_orderby,
849
-                    'type'     => array(
850
-                        'object',
851
-                        'string',
852
-                    ),//because we accept a variety of types, WP core validation and sanitization
853
-                    //freaks out. We'll just validate this argument while handling the request
854
-                    'validate_callback' => null,
855
-                    'sanitize_callback' => null,
856
-                ),
857
-                'group_by' => array(
858
-                    'required' => false,
859
-                    'default'  => null,
860
-                    'type'     => array(
861
-                        'object',
862
-                        'string',
863
-                    ),
864
-                    //because we accept  an almost infinite list of possible groupings,
865
-                    // WP core validation and sanitization
866
-                    //freaks out. We'll just validate this argument while handling the request
867
-                    'validate_callback' => null,
868
-                    'sanitize_callback' => null,
869
-                ),
870
-                'having'   => array(
871
-                    'required' => false,
872
-                    'default'  => null,
873
-                    'type'     => 'object',
874
-                    //because we accept an almost infinite list of possible where conditions, WP
875
-                    // core validation and sanitization freaks out. We'll just validate this argument
876
-                    // while handling the request
877
-                    'validate_callback' => null,
878
-                    'sanitize_callback' => null,
879
-                ),
880
-                'caps'     => array(
881
-                    'required' => false,
882
-                    'default'  => EEM_Base::caps_read,
883
-                    'type'     => 'string',
884
-                    'enum'     => array(
885
-                        EEM_Base::caps_read,
886
-                        EEM_Base::caps_read_admin,
887
-                        EEM_Base::caps_edit,
888
-                        EEM_Base::caps_delete
889
-                    )
890
-                ),
891
-            )
892
-        );
893
-    }
894
-
895
-
896
-
897
-    /**
898
-     * Gets parameter information for a model regarding writing data
899
-     *
900
-     * @param string           $model_name
901
-     * @param ModelVersionInfo $model_version_info
902
-     * @param boolean          $create                                       whether this is for request to create (in which case we need
903
-     *                                                                       all required params) or just to update (in which case we don't need those on every request)
904
-     * @return array
905
-     */
906
-    protected function _get_write_params(
907
-        $model_name,
908
-        ModelVersionInfo $model_version_info,
909
-        $create = false
910
-    ) {
911
-        $model = EE_Registry::instance()->load_model($model_name);
912
-        $fields = $model_version_info->fieldsOnModelInThisVersion($model);
913
-        $args_info = array();
914
-        foreach ($fields as $field_name => $field_obj) {
915
-            if ($field_obj->is_auto_increment()) {
916
-                //totally ignore auto increment IDs
917
-                continue;
918
-            }
919
-            $arg_info = $field_obj->getSchema();
920
-            $required = $create && ! $field_obj->is_nullable() && $field_obj->get_default_value() === null;
921
-            $arg_info['required'] = $required;
922
-            //remove the read-only flag. If it were read-only we wouldn't list it as an argument while writing, right?
923
-            unset($arg_info['readonly']);
924
-            $schema_properties = $field_obj->getSchemaProperties();
925
-            if (
926
-                isset($schema_properties['raw'])
927
-                && $field_obj->getSchemaType() === 'object'
928
-            ) {
929
-                //if there's a "raw" form of this argument, use those properties instead
930
-                $arg_info = array_replace(
931
-                    $arg_info,
932
-                    $schema_properties['raw']
933
-                );
934
-            }
935
-            $arg_info['default'] = ModelDataTranslator::prepareFieldValueForJson(
936
-                $field_obj,
937
-                $field_obj->get_default_value(),
938
-                $model_version_info->requestedVersion()
939
-            );
940
-            //we do our own validation and sanitization within the controller
941
-            $arg_info['sanitize_callback'] =
942
-                array(
943
-                    'EED_Core_Rest_Api',
944
-                    'default_sanitize_callback',
945
-                );
946
-            $args_info[$field_name] = $arg_info;
947
-            if ($field_obj instanceof EE_Datetime_Field) {
948
-                $gmt_arg_info = $arg_info;
949
-                $gmt_arg_info['description'] = sprintf(
950
-                    esc_html__(
951
-                        '%1$s - the value for this field in UTC. Ignored if %2$s is provided.',
952
-                        'event_espresso'
953
-                    ),
954
-                    $field_obj->get_nicename(),
955
-                    $field_name
956
-                );
957
-                $args_info[$field_name . '_gmt'] = $gmt_arg_info;
958
-            }
959
-        }
960
-        return $args_info;
961
-    }
962
-
963
-
964
-
965
-    /**
966
-     * Replacement for WP API's 'rest_parse_request_arg'.
967
-     * If the value is blank but not required, don't bother validating it.
968
-     * Also, it uses our email validation instead of WP API's default.
969
-     *
970
-     * @param                 $value
971
-     * @param WP_REST_Request $request
972
-     * @param                 $param
973
-     * @return bool|true|WP_Error
974
-     * @throws InvalidArgumentException
975
-     * @throws InvalidInterfaceException
976
-     * @throws InvalidDataTypeException
977
-     */
978
-    public static function default_sanitize_callback( $value, WP_REST_Request $request, $param)
979
-    {
980
-        $attributes = $request->get_attributes();
981
-        if (! isset($attributes['args'][$param])
982
-            || ! is_array($attributes['args'][$param])) {
983
-            $validation_result = true;
984
-        } else {
985
-            $args = $attributes['args'][$param];
986
-            if ((
987
-                    $value === ''
988
-                    || $value === null
989
-                )
990
-                && (! isset($args['required'])
991
-                    || $args['required'] === false
992
-                )
993
-            ) {
994
-                //not required and not provided? that's cool
995
-                $validation_result = true;
996
-            } elseif (isset($args['format'])
997
-                && $args['format'] === 'email'
998
-            ) {
999
-                $validation_result = true;
1000
-                if (! self::_validate_email($value)) {
1001
-                    $validation_result = new WP_Error(
1002
-                        'rest_invalid_param',
1003
-                        esc_html__(
1004
-                            'The email address is not valid or does not exist.',
1005
-                            'event_espresso'
1006
-                        )
1007
-                    );
1008
-                }
1009
-            } else {
1010
-                $validation_result = rest_validate_value_from_schema($value, $args, $param);
1011
-            }
1012
-        }
1013
-        if (is_wp_error($validation_result)) {
1014
-            return $validation_result;
1015
-        }
1016
-        return rest_sanitize_request_arg($value, $request, $param);
1017
-    }
1018
-
1019
-
1020
-
1021
-    /**
1022
-     * Returns whether or not this email address is valid. Copied from EE_Email_Validation_Strategy::_validate_email()
1023
-     *
1024
-     * @param $email
1025
-     * @return bool
1026
-     * @throws InvalidArgumentException
1027
-     * @throws InvalidInterfaceException
1028
-     * @throws InvalidDataTypeException
1029
-     */
1030
-    protected static function _validate_email($email){
1031
-        try {
1032
-            EmailAddressFactory::create($email);
1033
-            return true;
1034
-        } catch (EmailValidationException $e) {
1035
-            return false;
1036
-        }
1037
-    }
1038
-
1039
-
1040
-
1041
-    /**
1042
-     * Gets routes for the config
1043
-     *
1044
-     * @return array @see _register_model_routes
1045
-     * @deprecated since version 4.9.1
1046
-     */
1047
-    protected function _register_config_routes()
1048
-    {
1049
-        $config_routes = array();
1050
-        foreach (self::versions_served() as $version => $hidden_endpoint) {
1051
-            $config_routes[self::ee_api_namespace . $version] = $this->_get_config_route_data_for_version(
1052
-                $version,
1053
-                $hidden_endpoint
1054
-            );
1055
-        }
1056
-        return $config_routes;
1057
-    }
1058
-
1059
-
1060
-
1061
-    /**
1062
-     * Gets routes for the config for the specified version
1063
-     *
1064
-     * @param string  $version
1065
-     * @param boolean $hidden_endpoint
1066
-     * @return array
1067
-     */
1068
-    protected function _get_config_route_data_for_version($version, $hidden_endpoint)
1069
-    {
1070
-        return array(
1071
-            'config'    => array(
1072
-                array(
1073
-                    'callback'        => array(
1074
-                        'EventEspresso\core\libraries\rest_api\controllers\config\Read',
1075
-                        'handleRequest',
1076
-                    ),
1077
-                    'methods'         => WP_REST_Server::READABLE,
1078
-                    'hidden_endpoint' => $hidden_endpoint,
1079
-                    'callback_args'   => array($version),
1080
-                ),
1081
-            ),
1082
-            'site_info' => array(
1083
-                array(
1084
-                    'callback'        => array(
1085
-                        'EventEspresso\core\libraries\rest_api\controllers\config\Read',
1086
-                        'handleRequestSiteInfo',
1087
-                    ),
1088
-                    'methods'         => WP_REST_Server::READABLE,
1089
-                    'hidden_endpoint' => $hidden_endpoint,
1090
-                    'callback_args'   => array($version),
1091
-                ),
1092
-            ),
1093
-        );
1094
-    }
1095
-
1096
-
1097
-
1098
-    /**
1099
-     * Gets the meta info routes
1100
-     *
1101
-     * @return array @see _register_model_routes
1102
-     * @deprecated since version 4.9.1
1103
-     */
1104
-    protected function _register_meta_routes()
1105
-    {
1106
-        $meta_routes = array();
1107
-        foreach (self::versions_served() as $version => $hidden_endpoint) {
1108
-            $meta_routes[self::ee_api_namespace . $version] = $this->_get_meta_route_data_for_version(
1109
-                $version,
1110
-                $hidden_endpoint
1111
-            );
1112
-        }
1113
-        return $meta_routes;
1114
-    }
1115
-
1116
-
1117
-
1118
-    /**
1119
-     * @param string  $version
1120
-     * @param boolean $hidden_endpoint
1121
-     * @return array
1122
-     */
1123
-    protected function _get_meta_route_data_for_version($version, $hidden_endpoint = false)
1124
-    {
1125
-        return array(
1126
-            'resources' => array(
1127
-                array(
1128
-                    'callback'        => array(
1129
-                        'EventEspresso\core\libraries\rest_api\controllers\model\Meta',
1130
-                        'handleRequestModelsMeta',
1131
-                    ),
1132
-                    'methods'         => WP_REST_Server::READABLE,
1133
-                    'hidden_endpoint' => $hidden_endpoint,
1134
-                    'callback_args'   => array($version),
1135
-                ),
1136
-            ),
1137
-        );
1138
-    }
1139
-
1140
-
1141
-
1142
-    /**
1143
-     * Tries to hide old 4.6 endpoints from the
1144
-     *
1145
-     * @param array $route_data
1146
-     * @return array
1147
-     * @throws \EE_Error
1148
-     */
1149
-    public static function hide_old_endpoints($route_data)
1150
-    {
1151
-        //allow API clients to override which endpoints get hidden, in case
1152
-        //they want to discover particular endpoints
1153
-        //also, we don't have access to the request so we have to just grab it from the superglobal
1154
-        $force_show_ee_namespace = ltrim(
1155
-            EEH_Array::is_set($_REQUEST, 'force_show_ee_namespace', ''),
1156
-            '/'
1157
-        );
1158
-        foreach (EED_Core_Rest_Api::get_ee_route_data() as $namespace => $relative_urls) {
1159
-            foreach ($relative_urls as $resource_name => $endpoints) {
1160
-                foreach ($endpoints as $key => $endpoint) {
1161
-                    //skip schema and other route options
1162
-                    if (! is_numeric($key)) {
1163
-                        continue;
1164
-                    }
1165
-                    //by default, hide "hidden_endpoint"s, unless the request indicates
1166
-                    //to $force_show_ee_namespace, in which case only show that one
1167
-                    //namespace's endpoints (and hide all others)
1168
-                    if (
1169
-                        ($force_show_ee_namespace !== '' && $force_show_ee_namespace !== $namespace)
1170
-                        || ($endpoint['hidden_endpoint'] && $force_show_ee_namespace === '')
1171
-                    ) {
1172
-                        $full_route = '/' . ltrim($namespace, '/');
1173
-                        $full_route .= '/' . ltrim($resource_name, '/');
1174
-                        unset($route_data[$full_route]);
1175
-                    }
1176
-                }
1177
-            }
1178
-        }
1179
-        return $route_data;
1180
-    }
1181
-
1182
-
1183
-
1184
-    /**
1185
-     * Returns an array describing which versions of core support serving requests for.
1186
-     * Keys are core versions' major and minor version, and values are the
1187
-     * LOWEST requested version they can serve. Eg, 4.7 can serve requests for 4.6-like
1188
-     * data by just removing a few models and fields from the responses. However, 4.15 might remove
1189
-     * the answers table entirely, in which case it would be very difficult for
1190
-     * it to serve 4.6-style responses.
1191
-     * Versions of core that are missing from this array are unknowns.
1192
-     * previous ver
1193
-     *
1194
-     * @return array
1195
-     */
1196
-    public static function version_compatibilities()
1197
-    {
1198
-        return apply_filters(
1199
-            'FHEE__EED_Core_REST_API__version_compatibilities',
1200
-            array(
1201
-                '4.8.29' => '4.8.29',
1202
-                '4.8.33' => '4.8.29',
1203
-                '4.8.34' => '4.8.29',
1204
-                '4.8.36' => '4.8.29',
1205
-            )
1206
-        );
1207
-    }
1208
-
1209
-
1210
-
1211
-    /**
1212
-     * Gets the latest API version served. Eg if there
1213
-     * are two versions served of the API, 4.8.29 and 4.8.32, and
1214
-     * we are on core version 4.8.34, it will return the string "4.8.32"
1215
-     *
1216
-     * @return string
1217
-     */
1218
-    public static function latest_rest_api_version()
1219
-    {
1220
-        $versions_served = \EED_Core_Rest_Api::versions_served();
1221
-        $versions_served_keys = array_keys($versions_served);
1222
-        return end($versions_served_keys);
1223
-    }
1224
-
1225
-
1226
-
1227
-    /**
1228
-     * Using EED_Core_Rest_Api::version_compatibilities(), determines what version of
1229
-     * EE the API can serve requests for. Eg, if we are on 4.15 of core, and
1230
-     * we can serve requests from 4.12 or later, this will return array( '4.12', '4.13', '4.14', '4.15' ).
1231
-     * We also indicate whether or not this version should be put in the index or not
1232
-     *
1233
-     * @return array keys are API version numbers (just major and minor numbers), and values
1234
-     * are whether or not they should be hidden
1235
-     */
1236
-    public static function versions_served()
1237
-    {
1238
-        $versions_served = array();
1239
-        $possibly_served_versions = EED_Core_Rest_Api::version_compatibilities();
1240
-        $lowest_compatible_version = end($possibly_served_versions);
1241
-        reset($possibly_served_versions);
1242
-        $versions_served_historically = array_keys($possibly_served_versions);
1243
-        $latest_version = end($versions_served_historically);
1244
-        reset($versions_served_historically);
1245
-        //for each version of core we have ever served:
1246
-        foreach ($versions_served_historically as $key_versioned_endpoint) {
1247
-            //if it's not above the current core version, and it's compatible with the current version of core
1248
-            if ($key_versioned_endpoint === $latest_version) {
1249
-                //don't hide the latest version in the index
1250
-                $versions_served[$key_versioned_endpoint] = false;
1251
-            } elseif (
1252
-                $key_versioned_endpoint >= $lowest_compatible_version
1253
-                && $key_versioned_endpoint < EED_Core_Rest_Api::core_version()
1254
-            ) {
1255
-                //include, but hide, previous versions which are still supported
1256
-                $versions_served[$key_versioned_endpoint] = true;
1257
-            } elseif (apply_filters(
1258
-                'FHEE__EED_Core_Rest_Api__versions_served__include_incompatible_versions',
1259
-                false,
1260
-                $possibly_served_versions
1261
-            )) {
1262
-                //if a version is no longer supported, don't include it in index or list of versions served
1263
-                $versions_served[$key_versioned_endpoint] = true;
1264
-            }
1265
-        }
1266
-        return $versions_served;
1267
-    }
1268
-
1269
-
1270
-
1271
-    /**
1272
-     * Gets the major and minor version of EE core's version string
1273
-     *
1274
-     * @return string
1275
-     */
1276
-    public static function core_version()
1277
-    {
1278
-        return apply_filters(
1279
-            'FHEE__EED_Core_REST_API__core_version',
1280
-            implode(
1281
-                '.',
1282
-                array_slice(
1283
-                    explode(
1284
-                        '.',
1285
-                        espresso_version()
1286
-                    ),
1287
-                0,
1288
-                3
1289
-                )
1290
-            )
1291
-        );
1292
-    }
1293
-
1294
-
1295
-
1296
-    /**
1297
-     * Gets the default limit that should be used when querying for resources
1298
-     *
1299
-     * @return int
1300
-     */
1301
-    public static function get_default_query_limit()
1302
-    {
1303
-        //we actually don't use a const because we want folks to always use
1304
-        //this method, not the const directly
1305
-        return apply_filters(
1306
-            'FHEE__EED_Core_Rest_Api__get_default_query_limit',
1307
-            50
1308
-        );
1309
-    }
1310
-
1311
-
1312
-
1313
-    /**
1314
-     *    run - initial module setup
1315
-     *
1316
-     * @access    public
1317
-     * @param  WP $WP
1318
-     * @return    void
1319
-     */
1320
-    public function run($WP)
1321
-    {
1322
-    }
29
+	const ee_api_namespace_for_regex = 'ee\/v([^/]*)\/';
30
+
31
+	const saved_routes_option_names  = 'ee_core_routes';
32
+
33
+	/**
34
+	 * string used in _links response bodies to make them globally unique.
35
+	 *
36
+	 * @see http://v2.wp-api.org/extending/linking/
37
+	 */
38
+	const ee_api_link_namespace = 'https://api.eventespresso.com/';
39
+
40
+	/**
41
+	 * @var CalculatedModelFields
42
+	 */
43
+	protected static $_field_calculator;
44
+
45
+
46
+
47
+	/**
48
+	 * @return EED_Core_Rest_Api|EED_Module
49
+	 */
50
+	public static function instance()
51
+	{
52
+		self::$_field_calculator = new CalculatedModelFields();
53
+		return parent::get_instance(__CLASS__);
54
+	}
55
+
56
+
57
+
58
+	/**
59
+	 *    set_hooks - for hooking into EE Core, other modules, etc
60
+	 *
61
+	 * @access    public
62
+	 * @return    void
63
+	 */
64
+	public static function set_hooks()
65
+	{
66
+		self::set_hooks_both();
67
+	}
68
+
69
+
70
+
71
+	/**
72
+	 *    set_hooks_admin - for hooking into EE Admin Core, other modules, etc
73
+	 *
74
+	 * @access    public
75
+	 * @return    void
76
+	 */
77
+	public static function set_hooks_admin()
78
+	{
79
+		self::set_hooks_both();
80
+	}
81
+
82
+
83
+
84
+	public static function set_hooks_both()
85
+	{
86
+		add_action('rest_api_init', array('EED_Core_Rest_Api', 'register_routes'), 10);
87
+		add_action('rest_api_init', array('EED_Core_Rest_Api', 'set_hooks_rest_api'), 5);
88
+		add_filter('rest_route_data', array('EED_Core_Rest_Api', 'hide_old_endpoints'), 10, 2);
89
+		add_filter('rest_index',
90
+			array('EventEspresso\core\libraries\rest_api\controllers\model\Meta', 'filterEeMetadataIntoIndex'));
91
+		EED_Core_Rest_Api::invalidate_cached_route_data_on_version_change();
92
+	}
93
+
94
+
95
+
96
+	/**
97
+	 * sets up hooks which only need to be included as part of REST API requests;
98
+	 * other requests like to the frontend or admin etc don't need them
99
+	 *
100
+	 * @throws \EE_Error
101
+	 */
102
+	public static function set_hooks_rest_api()
103
+	{
104
+		//set hooks which account for changes made to the API
105
+		EED_Core_Rest_Api::_set_hooks_for_changes();
106
+	}
107
+
108
+
109
+
110
+	/**
111
+	 * public wrapper of _set_hooks_for_changes.
112
+	 * Loads all the hooks which make requests to old versions of the API
113
+	 * appear the same as they always did
114
+	 *
115
+	 * @throws EE_Error
116
+	 */
117
+	public static function set_hooks_for_changes()
118
+	{
119
+		self::_set_hooks_for_changes();
120
+	}
121
+
122
+
123
+
124
+	/**
125
+	 * If the user appears to be using WP API basic auth, tell them (via a persistent
126
+	 * admin notice and an email) that we're going to remove it soon, so they should
127
+	 * replace it with application passwords.
128
+	 *
129
+	 * @throws InvalidDataTypeException
130
+	 */
131
+	public static function maybe_notify_of_basic_auth_removal()
132
+	{
133
+		if (
134
+		apply_filters(
135
+			'FHEE__EED_Core_Rest_Api__maybe_notify_of_basic_auth_removal__override',
136
+			! isset($_SERVER['PHP_AUTH_USER'])
137
+			&& ! isset($_SERVER['HTTP_AUTHORIZATION'])
138
+		)
139
+		) {
140
+			//sure it's a WP API request, but they aren't using basic auth, so don't bother them
141
+			return;
142
+		}
143
+		//ok they're using the WP API with Basic Auth
144
+		new PersistentAdminNotice(
145
+			'using_basic_auth',
146
+			sprintf(
147
+				__(
148
+					'We noticed you\'re using the WP API, which is used by the Event Espresso 4 mobile apps. Because of security and compatibility concerns, we will soon be removing our default authentication mechanism, WP API Basic Auth, from Event Espresso. It is recommended you instead install the %1$sWP Application Passwords plugin%2$s and use it with the EE4 Mobile apps. See %3$sour mobile app documentation%2$s for more information. %4$sIf you have installed the WP API Basic Auth plugin separately, or are not using the Event Espresso 4 mobile apps, you can disregard this message.%4$sThe Event Espresso Team',
149
+					'event_espresso'
150
+				),
151
+				'<a href="https://wordpress.org/plugins/application-passwords/">',
152
+				'</a>',
153
+				'<a href="https://eventespresso.com/wiki/ee4-event-apps/#authentication">',
154
+				'<br/>'
155
+			)
156
+		);
157
+		if ( ! get_option('ee_notified_admin_on_basic_auth_removal', false)) {
158
+			add_option('ee_notified_admin_on_basic_auth_removal', true);
159
+			//piggy back off EE_Error::set_content_type, which sets the content type to HTML
160
+			add_filter('wp_mail_content_type', array('EE_Error', 'set_content_type'));
161
+			//and send the message to the site admin too
162
+			wp_mail(get_option('admin_email'),
163
+				__('Notice of Removal of WP API Basic Auth From Event Espresso 4', 'event_espresso'), $message);
164
+			remove_filter('wp_mail_content_type', array('EE_Error', 'set_content_type'));
165
+		}
166
+	}
167
+
168
+
169
+
170
+	/**
171
+	 * Loads all the hooks which make requests to old versions of the API
172
+	 * appear the same as they always did
173
+	 *
174
+	 * @throws EE_Error
175
+	 */
176
+	protected static function _set_hooks_for_changes()
177
+	{
178
+		$folder_contents = EEH_File::get_contents_of_folders(array(EE_LIBRARIES . 'rest_api' . DS . 'changes'), false);
179
+		foreach ($folder_contents as $classname_in_namespace => $filepath) {
180
+			//ignore the base parent class
181
+			//and legacy named classes
182
+			if ($classname_in_namespace === 'ChangesInBase'
183
+				|| strpos($classname_in_namespace, 'Changes_In_') === 0
184
+			) {
185
+				continue;
186
+			}
187
+			$full_classname = 'EventEspresso\core\libraries\rest_api\changes\\' . $classname_in_namespace;
188
+			if (class_exists($full_classname)) {
189
+				$instance_of_class = new $full_classname;
190
+				if ($instance_of_class instanceof ChangesInBase) {
191
+					$instance_of_class->setHooks();
192
+				}
193
+			}
194
+		}
195
+	}
196
+
197
+
198
+
199
+	/**
200
+	 * Filters the WP routes to add our EE-related ones. This takes a bit of time
201
+	 * so we actually prefer to only do it when an EE plugin is activated or upgraded
202
+	 *
203
+	 * @throws \EE_Error
204
+	 */
205
+	public static function register_routes()
206
+	{
207
+		foreach (EED_Core_Rest_Api::get_ee_route_data() as $namespace => $relative_routes) {
208
+			foreach ($relative_routes as $relative_route => $data_for_multiple_endpoints) {
209
+				/**
210
+				 * @var array $data_for_multiple_endpoints numerically indexed array
211
+				 *                                         but can also contain route options like {
212
+				 * @type array    $schema                      {
213
+				 * @type callable $schema_callback
214
+				 * @type array    $callback_args               arguments that will be passed to the callback, after the
215
+				 * WP_REST_Request of course
216
+				 * }
217
+				 * }
218
+				 */
219
+				//when registering routes, register all the endpoints' data at the same time
220
+				$multiple_endpoint_args = array();
221
+				foreach ($data_for_multiple_endpoints as $endpoint_key => $data_for_single_endpoint) {
222
+					/**
223
+					 * @var array     $data_for_single_endpoint {
224
+					 * @type callable $callback
225
+					 * @type string methods
226
+					 * @type array args
227
+					 * @type array _links
228
+					 * @type array    $callback_args            arguments that will be passed to the callback, after the
229
+					 * WP_REST_Request of course
230
+					 * }
231
+					 */
232
+					//skip route options
233
+					if (! is_numeric($endpoint_key)) {
234
+						continue;
235
+					}
236
+					if (! isset($data_for_single_endpoint['callback'], $data_for_single_endpoint['methods'])) {
237
+						throw new EE_Error(
238
+							esc_html__(
239
+								// @codingStandardsIgnoreStart
240
+								'Endpoint configuration data needs to have entries "callback" (callable) and "methods" (comma-separated list of accepts HTTP methods).',
241
+								// @codingStandardsIgnoreEnd
242
+								'event_espresso')
243
+						);
244
+					}
245
+					$callback = $data_for_single_endpoint['callback'];
246
+					$single_endpoint_args = array(
247
+						'methods' => $data_for_single_endpoint['methods'],
248
+						'args'    => isset($data_for_single_endpoint['args']) ? $data_for_single_endpoint['args']
249
+							: array(),
250
+					);
251
+					if (isset($data_for_single_endpoint['_links'])) {
252
+						$single_endpoint_args['_links'] = $data_for_single_endpoint['_links'];
253
+					}
254
+					if (isset($data_for_single_endpoint['callback_args'])) {
255
+						$callback_args = $data_for_single_endpoint['callback_args'];
256
+						$single_endpoint_args['callback'] = function (\WP_REST_Request $request) use (
257
+							$callback,
258
+							$callback_args
259
+						) {
260
+							array_unshift($callback_args, $request);
261
+							return call_user_func_array(
262
+								$callback,
263
+								$callback_args
264
+							);
265
+						};
266
+					} else {
267
+						$single_endpoint_args['callback'] = $data_for_single_endpoint['callback'];
268
+					}
269
+					$multiple_endpoint_args[] = $single_endpoint_args;
270
+				}
271
+				if (isset($data_for_multiple_endpoints['schema'])) {
272
+					$schema_route_data = $data_for_multiple_endpoints['schema'];
273
+					$schema_callback = $schema_route_data['schema_callback'];
274
+					$callback_args = $schema_route_data['callback_args'];
275
+					$multiple_endpoint_args['schema'] = function () use ($schema_callback, $callback_args) {
276
+						return call_user_func_array(
277
+							$schema_callback,
278
+							$callback_args
279
+						);
280
+					};
281
+				}
282
+				register_rest_route(
283
+					$namespace,
284
+					$relative_route,
285
+					$multiple_endpoint_args
286
+				);
287
+			}
288
+		}
289
+	}
290
+
291
+
292
+
293
+	/**
294
+	 * Checks if there was a version change or something that merits invalidating the cached
295
+	 * route data. If so, invalidates the cached route data so that it gets refreshed
296
+	 * next time the WP API is used
297
+	 */
298
+	public static function invalidate_cached_route_data_on_version_change()
299
+	{
300
+		if (EE_System::instance()->detect_req_type() !== EE_System::req_type_normal) {
301
+			EED_Core_Rest_Api::invalidate_cached_route_data();
302
+		}
303
+		foreach (EE_Registry::instance()->addons as $addon) {
304
+			if ($addon instanceof EE_Addon && $addon->detect_req_type() !== EE_System::req_type_normal) {
305
+				EED_Core_Rest_Api::invalidate_cached_route_data();
306
+			}
307
+		}
308
+	}
309
+
310
+
311
+
312
+	/**
313
+	 * Removes the cached route data so it will get refreshed next time the WP API is used
314
+	 */
315
+	public static function invalidate_cached_route_data()
316
+	{
317
+		//delete the saved EE REST API routes
318
+		foreach (EED_Core_Rest_Api::versions_served() as $version => $hidden) {
319
+			delete_option(EED_Core_Rest_Api::saved_routes_option_names . $version);
320
+		}
321
+	}
322
+
323
+
324
+
325
+	/**
326
+	 * Gets the EE route data
327
+	 *
328
+	 * @return array top-level key is the namespace, next-level key is the route and its value is array{
329
+	 * @throws \EE_Error
330
+	 * @type string|array $callback
331
+	 * @type string       $methods
332
+	 * @type boolean      $hidden_endpoint
333
+	 * }
334
+	 */
335
+	public static function get_ee_route_data()
336
+	{
337
+		$ee_routes = array();
338
+		foreach (self::versions_served() as $version => $hidden_endpoints) {
339
+			$ee_routes[self::ee_api_namespace . $version] = self::_get_ee_route_data_for_version(
340
+				$version,
341
+				$hidden_endpoints
342
+			);
343
+		}
344
+		return $ee_routes;
345
+	}
346
+
347
+
348
+
349
+	/**
350
+	 * Gets the EE route data from the wp options if it exists already,
351
+	 * otherwise re-generates it and saves it to the option
352
+	 *
353
+	 * @param string  $version
354
+	 * @param boolean $hidden_endpoints
355
+	 * @return array
356
+	 * @throws \EE_Error
357
+	 */
358
+	protected static function _get_ee_route_data_for_version($version, $hidden_endpoints = false)
359
+	{
360
+		$ee_routes = get_option(self::saved_routes_option_names . $version, null);
361
+		if (! $ee_routes || (defined('EE_REST_API_DEBUG_MODE') && EE_REST_API_DEBUG_MODE)) {
362
+			$ee_routes = self::_save_ee_route_data_for_version($version, $hidden_endpoints);
363
+		}
364
+		return $ee_routes;
365
+	}
366
+
367
+
368
+
369
+	/**
370
+	 * Saves the EE REST API route data to a wp option and returns it
371
+	 *
372
+	 * @param string  $version
373
+	 * @param boolean $hidden_endpoints
374
+	 * @return mixed|null
375
+	 * @throws \EE_Error
376
+	 */
377
+	protected static function _save_ee_route_data_for_version($version, $hidden_endpoints = false)
378
+	{
379
+		$instance = self::instance();
380
+		$routes = apply_filters(
381
+			'EED_Core_Rest_Api__save_ee_route_data_for_version__routes',
382
+			array_replace_recursive(
383
+				$instance->_get_config_route_data_for_version($version, $hidden_endpoints),
384
+				$instance->_get_meta_route_data_for_version($version, $hidden_endpoints),
385
+				$instance->_get_model_route_data_for_version($version, $hidden_endpoints),
386
+				$instance->_get_rpc_route_data_for_version($version, $hidden_endpoints)
387
+			)
388
+		);
389
+		$option_name = self::saved_routes_option_names . $version;
390
+		if (get_option($option_name)) {
391
+			update_option($option_name, $routes, true);
392
+		} else {
393
+			add_option($option_name, $routes, null, 'no');
394
+		}
395
+		return $routes;
396
+	}
397
+
398
+
399
+
400
+	/**
401
+	 * Calculates all the EE routes and saves it to a WordPress option so we don't
402
+	 * need to calculate it on every request
403
+	 *
404
+	 * @deprecated since version 4.9.1
405
+	 * @return void
406
+	 */
407
+	public static function save_ee_routes()
408
+	{
409
+		if (EE_Maintenance_Mode::instance()->models_can_query()) {
410
+			$instance = self::instance();
411
+			$routes = apply_filters(
412
+				'EED_Core_Rest_Api__save_ee_routes__routes',
413
+				array_replace_recursive(
414
+					$instance->_register_config_routes(),
415
+					$instance->_register_meta_routes(),
416
+					$instance->_register_model_routes(),
417
+					$instance->_register_rpc_routes()
418
+				)
419
+			);
420
+			update_option(self::saved_routes_option_names, $routes, true);
421
+		}
422
+	}
423
+
424
+
425
+
426
+	/**
427
+	 * Gets all the route information relating to EE models
428
+	 *
429
+	 * @return array @see get_ee_route_data
430
+	 * @deprecated since version 4.9.1
431
+	 */
432
+	protected function _register_model_routes()
433
+	{
434
+		$model_routes = array();
435
+		foreach (self::versions_served() as $version => $hidden_endpoint) {
436
+			$model_routes[EED_Core_Rest_Api::ee_api_namespace
437
+						  . $version] = $this->_get_config_route_data_for_version($version, $hidden_endpoint);
438
+		}
439
+		return $model_routes;
440
+	}
441
+
442
+
443
+
444
+	/**
445
+	 * Decides whether or not to add write endpoints for this model.
446
+	 *
447
+	 * Currently, this defaults to exclude all global tables and models
448
+	 * which would allow inserting WP core data (we don't want to duplicate
449
+	 * what WP API does, as it's unnecessary, extra work, and potentially extra bugs)
450
+	 * @param EEM_Base $model
451
+	 * @return bool
452
+	 */
453
+	public static function should_have_write_endpoints(EEM_Base $model)
454
+	{
455
+		if ($model->is_wp_core_model()){
456
+			return false;
457
+		}
458
+		foreach($model->get_tables() as $table){
459
+			if( $table->is_global()){
460
+				return false;
461
+			}
462
+		}
463
+		return true;
464
+	}
465
+
466
+
467
+
468
+	/**
469
+	 * Gets the names of all models which should have plural routes (eg `ee/v4.8.36/events`)
470
+	 * in this versioned namespace of EE4
471
+	 * @param $version
472
+	 * @return array keys are model names (eg 'Event') and values ar either classnames (eg 'EEM_Event')
473
+	 */
474
+	public static function model_names_with_plural_routes($version){
475
+		$model_version_info = new ModelVersionInfo($version);
476
+		$models_to_register = $model_version_info->modelsForRequestedVersion();
477
+		//let's not bother having endpoints for extra metas
478
+		unset(
479
+			$models_to_register['Extra_Meta'],
480
+			$models_to_register['Extra_Join'],
481
+			$models_to_register['Post_Meta']
482
+		);
483
+		return apply_filters(
484
+			'FHEE__EED_Core_REST_API___register_model_routes',
485
+			$models_to_register
486
+		);
487
+	}
488
+
489
+
490
+
491
+	/**
492
+	 * Gets the route data for EE models in the specified version
493
+	 *
494
+	 * @param string  $version
495
+	 * @param boolean $hidden_endpoint
496
+	 * @return array
497
+	 * @throws EE_Error
498
+	 */
499
+	protected function _get_model_route_data_for_version($version, $hidden_endpoint = false)
500
+	{
501
+		$model_routes = array();
502
+		$model_version_info = new ModelVersionInfo($version);
503
+		foreach (EED_Core_Rest_Api::model_names_with_plural_routes($version) as $model_name => $model_classname) {
504
+			$model = \EE_Registry::instance()->load_model($model_name);
505
+			//if this isn't a valid model then let's skip iterate to the next item in the loop.
506
+			if (! $model instanceof EEM_Base) {
507
+				continue;
508
+			}
509
+			//yes we could just register one route for ALL models, but then they wouldn't show up in the index
510
+			$plural_model_route = EED_Core_Rest_Api::get_collection_route($model);
511
+			$singular_model_route = EED_Core_Rest_Api::get_entity_route($model, '(?P<id>[^\/]+)');
512
+			$model_routes[$plural_model_route] = array(
513
+				array(
514
+					'callback'        => array(
515
+						'EventEspresso\core\libraries\rest_api\controllers\model\Read',
516
+						'handleRequestGetAll',
517
+					),
518
+					'callback_args'   => array($version, $model_name),
519
+					'methods'         => WP_REST_Server::READABLE,
520
+					'hidden_endpoint' => $hidden_endpoint,
521
+					'args'            => $this->_get_read_query_params($model, $version),
522
+					'_links'          => array(
523
+						'self' => rest_url(EED_Core_Rest_Api::ee_api_namespace . $version . $singular_model_route),
524
+					),
525
+				),
526
+				'schema' => array(
527
+					'schema_callback' => array(
528
+						'EventEspresso\core\libraries\rest_api\controllers\model\Read',
529
+						'handleSchemaRequest',
530
+					),
531
+					'callback_args'   => array($version, $model_name),
532
+				),
533
+			);
534
+			$model_routes[$singular_model_route] = array(
535
+				array(
536
+					'callback'        => array(
537
+						'EventEspresso\core\libraries\rest_api\controllers\model\Read',
538
+						'handleRequestGetOne',
539
+					),
540
+					'callback_args'   => array($version, $model_name),
541
+					'methods'         => WP_REST_Server::READABLE,
542
+					'hidden_endpoint' => $hidden_endpoint,
543
+					'args'            => $this->_get_response_selection_query_params($model, $version),
544
+				),
545
+			);
546
+			if( apply_filters(
547
+				'FHEE__EED_Core_Rest_Api___get_model_route_data_for_version__add_write_endpoints',
548
+				EED_Core_Rest_Api::should_have_write_endpoints($model),
549
+				$model
550
+			)){
551
+				$model_routes[$plural_model_route][] = array(
552
+					'callback'        => array(
553
+						'EventEspresso\core\libraries\rest_api\controllers\model\Write',
554
+						'handleRequestInsert',
555
+					),
556
+					'callback_args'   => array($version, $model_name),
557
+					'methods'         => WP_REST_Server::CREATABLE,
558
+					'hidden_endpoint' => $hidden_endpoint,
559
+					'args'            => $this->_get_write_params($model_name, $model_version_info, true),
560
+				);
561
+				$model_routes[$singular_model_route] = array_merge(
562
+					$model_routes[$singular_model_route],
563
+					array(
564
+						array(
565
+							'callback'        => array(
566
+								'EventEspresso\core\libraries\rest_api\controllers\model\Write',
567
+								'handleRequestUpdate',
568
+							),
569
+							'callback_args'   => array($version, $model_name),
570
+							'methods'         => WP_REST_Server::EDITABLE,
571
+							'hidden_endpoint' => $hidden_endpoint,
572
+							'args'            => $this->_get_write_params($model_name, $model_version_info),
573
+						),
574
+						array(
575
+							'callback'        => array(
576
+								'EventEspresso\core\libraries\rest_api\controllers\model\Write',
577
+								'handleRequestDelete',
578
+							),
579
+							'callback_args'   => array($version, $model_name),
580
+							'methods'         => WP_REST_Server::DELETABLE,
581
+							'hidden_endpoint' => $hidden_endpoint,
582
+							'args'            => $this->_get_delete_query_params($model, $version),
583
+						)
584
+					)
585
+				);
586
+			}
587
+			foreach ($model->relation_settings() as $relation_name => $relation_obj) {
588
+
589
+				$related_route = EED_Core_Rest_Api::get_relation_route_via(
590
+					$model,
591
+					'(?P<id>[^\/]+)',
592
+					$relation_obj
593
+				);
594
+				$endpoints = array(
595
+					array(
596
+						'callback'        => array(
597
+							'EventEspresso\core\libraries\rest_api\controllers\model\Read',
598
+							'handleRequestGetRelated',
599
+						),
600
+						'callback_args'   => array($version, $model_name, $relation_name),
601
+						'methods'         => WP_REST_Server::READABLE,
602
+						'hidden_endpoint' => $hidden_endpoint,
603
+						'args'            => $this->_get_read_query_params($relation_obj->get_other_model(), $version),
604
+					),
605
+				);
606
+				$model_routes[$related_route] = $endpoints;
607
+			}
608
+		}
609
+		return $model_routes;
610
+	}
611
+
612
+
613
+
614
+	/**
615
+	 * Gets the relative URI to a model's REST API plural route, after the EE4 versioned namespace,
616
+	 * excluding the preceding slash.
617
+	 * Eg you pass get_plural_route_to('Event') = 'events'
618
+	 *
619
+	 * @param EEM_Base $model
620
+	 * @return string
621
+	 */
622
+	public static function get_collection_route(EEM_Base $model)
623
+	{
624
+		return EEH_Inflector::pluralize_and_lower($model->get_this_model_name());
625
+	}
626
+
627
+
628
+
629
+	/**
630
+	 * Gets the relative URI to a model's REST API singular route, after the EE4 versioned namespace,
631
+	 * excluding the preceding slash.
632
+	 * Eg you pass get_plural_route_to('Event', 12) = 'events/12'
633
+	 *
634
+	 * @param EEM_Base $model eg Event or Venue
635
+	 * @param string $id
636
+	 * @return string
637
+	 */
638
+	public static function get_entity_route($model, $id)
639
+	{
640
+		return EED_Core_Rest_Api::get_collection_route($model). '/' . $id;
641
+	}
642
+
643
+
644
+	/**
645
+	 * Gets the relative URI to a model's REST API singular route, after the EE4 versioned namespace,
646
+	 * excluding the preceding slash.
647
+	 * Eg you pass get_plural_route_to('Event', 12) = 'events/12'
648
+	 *
649
+	 * @param EEM_Base                 $model eg Event or Venue
650
+	 * @param string                 $id
651
+	 * @param EE_Model_Relation_Base $relation_obj
652
+	 * @return string
653
+	 */
654
+	public static function get_relation_route_via(EEM_Base $model, $id, EE_Model_Relation_Base $relation_obj)
655
+	{
656
+		$related_model_name_endpoint_part = ModelRead::getRelatedEntityName(
657
+			$relation_obj->get_other_model()->get_this_model_name(),
658
+			$relation_obj
659
+		);
660
+		return EED_Core_Rest_Api::get_entity_route($model, $id) . '/' . $related_model_name_endpoint_part;
661
+	}
662
+
663
+
664
+
665
+	/**
666
+	 * Adds onto the $relative_route the EE4 REST API versioned namespace.
667
+	 * Eg if given '4.8.36' and 'events', will return 'ee/v4.8.36/events'
668
+	 * @param string $relative_route
669
+	 * @param string $version
670
+	 * @return string
671
+	 */
672
+	public static function get_versioned_route_to($relative_route, $version = '4.8.36'){
673
+		return '/' . EED_Core_Rest_Api::ee_api_namespace . $version . '/' . $relative_route;
674
+	}
675
+
676
+
677
+
678
+	/**
679
+	 * Adds all the RPC-style routes (remote procedure call-like routes, ie
680
+	 * routes that don't conform to the traditional REST CRUD-style).
681
+	 *
682
+	 * @deprecated since 4.9.1
683
+	 */
684
+	protected function _register_rpc_routes()
685
+	{
686
+		$routes = array();
687
+		foreach (self::versions_served() as $version => $hidden_endpoint) {
688
+			$routes[self::ee_api_namespace . $version] = $this->_get_rpc_route_data_for_version(
689
+				$version,
690
+				$hidden_endpoint
691
+			);
692
+		}
693
+		return $routes;
694
+	}
695
+
696
+
697
+
698
+	/**
699
+	 * @param string  $version
700
+	 * @param boolean $hidden_endpoint
701
+	 * @return array
702
+	 */
703
+	protected function _get_rpc_route_data_for_version($version, $hidden_endpoint = false)
704
+	{
705
+		$this_versions_routes = array();
706
+		//checkin endpoint
707
+		$this_versions_routes['registrations/(?P<REG_ID>\d+)/toggle_checkin_for_datetime/(?P<DTT_ID>\d+)'] = array(
708
+			array(
709
+				'callback'        => array(
710
+					'EventEspresso\core\libraries\rest_api\controllers\rpc\Checkin',
711
+					'handleRequestToggleCheckin',
712
+				),
713
+				'methods'         => WP_REST_Server::CREATABLE,
714
+				'hidden_endpoint' => $hidden_endpoint,
715
+				'args'            => array(
716
+					'force' => array(
717
+						'required'    => false,
718
+						'default'     => false,
719
+						'description' => __(
720
+							// @codingStandardsIgnoreStart
721
+							'Whether to force toggle checkin, or to verify the registration status and allowed ticket uses',
722
+							// @codingStandardsIgnoreEnd
723
+							'event_espresso'
724
+						),
725
+					),
726
+				),
727
+				'callback_args'   => array($version),
728
+			),
729
+		);
730
+		return apply_filters(
731
+			'FHEE__EED_Core_Rest_Api___register_rpc_routes__this_versions_routes',
732
+			$this_versions_routes,
733
+			$version,
734
+			$hidden_endpoint
735
+		);
736
+	}
737
+
738
+
739
+
740
+	/**
741
+	 * Gets the query params that can be used when request one or many
742
+	 *
743
+	 * @param EEM_Base $model
744
+	 * @param string   $version
745
+	 * @return array
746
+	 */
747
+	protected function _get_response_selection_query_params(\EEM_Base $model, $version)
748
+	{
749
+		return apply_filters(
750
+			'FHEE__EED_Core_Rest_Api___get_response_selection_query_params',
751
+			array(
752
+				'include'   => array(
753
+					'required' => false,
754
+					'default'  => '*',
755
+					'type'     => 'string',
756
+				),
757
+				'calculate' => array(
758
+					'required'          => false,
759
+					'default'           => '',
760
+					'enum'              => self::$_field_calculator->retrieveCalculatedFieldsForModel($model),
761
+					'type'              => 'string',
762
+					//because we accept a CSV'd list of the enumerated strings, WP core validation and sanitization
763
+					//freaks out. We'll just validate this argument while handling the request
764
+					'validate_callback' => null,
765
+					'sanitize_callback' => null,
766
+				),
767
+			),
768
+			$model,
769
+			$version
770
+		);
771
+	}
772
+
773
+
774
+
775
+	/**
776
+	 * Gets the parameters acceptable for delete requests
777
+	 *
778
+	 * @param \EEM_Base $model
779
+	 * @param string    $version
780
+	 * @return array
781
+	 */
782
+	protected function _get_delete_query_params(\EEM_Base $model, $version)
783
+	{
784
+		$params_for_delete = array(
785
+			'allow_blocking' => array(
786
+				'required' => false,
787
+				'default'  => true,
788
+				'type'     => 'boolean',
789
+			),
790
+		);
791
+		$params_for_delete['force'] = array(
792
+			'required' => false,
793
+			'default'  => false,
794
+			'type'     => 'boolean',
795
+		);
796
+		return apply_filters(
797
+			'FHEE__EED_Core_Rest_Api___get_delete_query_params',
798
+			$params_for_delete,
799
+			$model,
800
+			$version
801
+		);
802
+	}
803
+
804
+
805
+
806
+	/**
807
+	 * Gets info about reading query params that are acceptable
808
+	 *
809
+	 * @param \EEM_Base $model eg 'Event' or 'Venue'
810
+	 * @param  string   $version
811
+	 * @return array    describing the args acceptable when querying this model
812
+	 * @throws EE_Error
813
+	 */
814
+	protected function _get_read_query_params(\EEM_Base $model, $version)
815
+	{
816
+		$default_orderby = array();
817
+		foreach ($model->get_combined_primary_key_fields() as $key_field) {
818
+			$default_orderby[$key_field->get_name()] = 'ASC';
819
+		}
820
+		return array_merge(
821
+			$this->_get_response_selection_query_params($model, $version),
822
+			array(
823
+				'where'    => array(
824
+					'required' => false,
825
+					'default'  => array(),
826
+					'type'     => 'object',
827
+					//because we accept an almost infinite list of possible where conditions, WP
828
+					// core validation and sanitization freaks out. We'll just validate this argument
829
+					// while handling the request
830
+					'validate_callback' => null,
831
+					'sanitize_callback' => null,
832
+				),
833
+				'limit'    => array(
834
+					'required' => false,
835
+					'default'  => EED_Core_Rest_Api::get_default_query_limit(),
836
+					'type'     => array(
837
+						'array',
838
+						'string',
839
+						'integer',
840
+					),
841
+					//because we accept a variety of types, WP core validation and sanitization
842
+					//freaks out. We'll just validate this argument while handling the request
843
+					'validate_callback' => null,
844
+					'sanitize_callback' => null,
845
+				),
846
+				'order_by' => array(
847
+					'required' => false,
848
+					'default'  => $default_orderby,
849
+					'type'     => array(
850
+						'object',
851
+						'string',
852
+					),//because we accept a variety of types, WP core validation and sanitization
853
+					//freaks out. We'll just validate this argument while handling the request
854
+					'validate_callback' => null,
855
+					'sanitize_callback' => null,
856
+				),
857
+				'group_by' => array(
858
+					'required' => false,
859
+					'default'  => null,
860
+					'type'     => array(
861
+						'object',
862
+						'string',
863
+					),
864
+					//because we accept  an almost infinite list of possible groupings,
865
+					// WP core validation and sanitization
866
+					//freaks out. We'll just validate this argument while handling the request
867
+					'validate_callback' => null,
868
+					'sanitize_callback' => null,
869
+				),
870
+				'having'   => array(
871
+					'required' => false,
872
+					'default'  => null,
873
+					'type'     => 'object',
874
+					//because we accept an almost infinite list of possible where conditions, WP
875
+					// core validation and sanitization freaks out. We'll just validate this argument
876
+					// while handling the request
877
+					'validate_callback' => null,
878
+					'sanitize_callback' => null,
879
+				),
880
+				'caps'     => array(
881
+					'required' => false,
882
+					'default'  => EEM_Base::caps_read,
883
+					'type'     => 'string',
884
+					'enum'     => array(
885
+						EEM_Base::caps_read,
886
+						EEM_Base::caps_read_admin,
887
+						EEM_Base::caps_edit,
888
+						EEM_Base::caps_delete
889
+					)
890
+				),
891
+			)
892
+		);
893
+	}
894
+
895
+
896
+
897
+	/**
898
+	 * Gets parameter information for a model regarding writing data
899
+	 *
900
+	 * @param string           $model_name
901
+	 * @param ModelVersionInfo $model_version_info
902
+	 * @param boolean          $create                                       whether this is for request to create (in which case we need
903
+	 *                                                                       all required params) or just to update (in which case we don't need those on every request)
904
+	 * @return array
905
+	 */
906
+	protected function _get_write_params(
907
+		$model_name,
908
+		ModelVersionInfo $model_version_info,
909
+		$create = false
910
+	) {
911
+		$model = EE_Registry::instance()->load_model($model_name);
912
+		$fields = $model_version_info->fieldsOnModelInThisVersion($model);
913
+		$args_info = array();
914
+		foreach ($fields as $field_name => $field_obj) {
915
+			if ($field_obj->is_auto_increment()) {
916
+				//totally ignore auto increment IDs
917
+				continue;
918
+			}
919
+			$arg_info = $field_obj->getSchema();
920
+			$required = $create && ! $field_obj->is_nullable() && $field_obj->get_default_value() === null;
921
+			$arg_info['required'] = $required;
922
+			//remove the read-only flag. If it were read-only we wouldn't list it as an argument while writing, right?
923
+			unset($arg_info['readonly']);
924
+			$schema_properties = $field_obj->getSchemaProperties();
925
+			if (
926
+				isset($schema_properties['raw'])
927
+				&& $field_obj->getSchemaType() === 'object'
928
+			) {
929
+				//if there's a "raw" form of this argument, use those properties instead
930
+				$arg_info = array_replace(
931
+					$arg_info,
932
+					$schema_properties['raw']
933
+				);
934
+			}
935
+			$arg_info['default'] = ModelDataTranslator::prepareFieldValueForJson(
936
+				$field_obj,
937
+				$field_obj->get_default_value(),
938
+				$model_version_info->requestedVersion()
939
+			);
940
+			//we do our own validation and sanitization within the controller
941
+			$arg_info['sanitize_callback'] =
942
+				array(
943
+					'EED_Core_Rest_Api',
944
+					'default_sanitize_callback',
945
+				);
946
+			$args_info[$field_name] = $arg_info;
947
+			if ($field_obj instanceof EE_Datetime_Field) {
948
+				$gmt_arg_info = $arg_info;
949
+				$gmt_arg_info['description'] = sprintf(
950
+					esc_html__(
951
+						'%1$s - the value for this field in UTC. Ignored if %2$s is provided.',
952
+						'event_espresso'
953
+					),
954
+					$field_obj->get_nicename(),
955
+					$field_name
956
+				);
957
+				$args_info[$field_name . '_gmt'] = $gmt_arg_info;
958
+			}
959
+		}
960
+		return $args_info;
961
+	}
962
+
963
+
964
+
965
+	/**
966
+	 * Replacement for WP API's 'rest_parse_request_arg'.
967
+	 * If the value is blank but not required, don't bother validating it.
968
+	 * Also, it uses our email validation instead of WP API's default.
969
+	 *
970
+	 * @param                 $value
971
+	 * @param WP_REST_Request $request
972
+	 * @param                 $param
973
+	 * @return bool|true|WP_Error
974
+	 * @throws InvalidArgumentException
975
+	 * @throws InvalidInterfaceException
976
+	 * @throws InvalidDataTypeException
977
+	 */
978
+	public static function default_sanitize_callback( $value, WP_REST_Request $request, $param)
979
+	{
980
+		$attributes = $request->get_attributes();
981
+		if (! isset($attributes['args'][$param])
982
+			|| ! is_array($attributes['args'][$param])) {
983
+			$validation_result = true;
984
+		} else {
985
+			$args = $attributes['args'][$param];
986
+			if ((
987
+					$value === ''
988
+					|| $value === null
989
+				)
990
+				&& (! isset($args['required'])
991
+					|| $args['required'] === false
992
+				)
993
+			) {
994
+				//not required and not provided? that's cool
995
+				$validation_result = true;
996
+			} elseif (isset($args['format'])
997
+				&& $args['format'] === 'email'
998
+			) {
999
+				$validation_result = true;
1000
+				if (! self::_validate_email($value)) {
1001
+					$validation_result = new WP_Error(
1002
+						'rest_invalid_param',
1003
+						esc_html__(
1004
+							'The email address is not valid or does not exist.',
1005
+							'event_espresso'
1006
+						)
1007
+					);
1008
+				}
1009
+			} else {
1010
+				$validation_result = rest_validate_value_from_schema($value, $args, $param);
1011
+			}
1012
+		}
1013
+		if (is_wp_error($validation_result)) {
1014
+			return $validation_result;
1015
+		}
1016
+		return rest_sanitize_request_arg($value, $request, $param);
1017
+	}
1018
+
1019
+
1020
+
1021
+	/**
1022
+	 * Returns whether or not this email address is valid. Copied from EE_Email_Validation_Strategy::_validate_email()
1023
+	 *
1024
+	 * @param $email
1025
+	 * @return bool
1026
+	 * @throws InvalidArgumentException
1027
+	 * @throws InvalidInterfaceException
1028
+	 * @throws InvalidDataTypeException
1029
+	 */
1030
+	protected static function _validate_email($email){
1031
+		try {
1032
+			EmailAddressFactory::create($email);
1033
+			return true;
1034
+		} catch (EmailValidationException $e) {
1035
+			return false;
1036
+		}
1037
+	}
1038
+
1039
+
1040
+
1041
+	/**
1042
+	 * Gets routes for the config
1043
+	 *
1044
+	 * @return array @see _register_model_routes
1045
+	 * @deprecated since version 4.9.1
1046
+	 */
1047
+	protected function _register_config_routes()
1048
+	{
1049
+		$config_routes = array();
1050
+		foreach (self::versions_served() as $version => $hidden_endpoint) {
1051
+			$config_routes[self::ee_api_namespace . $version] = $this->_get_config_route_data_for_version(
1052
+				$version,
1053
+				$hidden_endpoint
1054
+			);
1055
+		}
1056
+		return $config_routes;
1057
+	}
1058
+
1059
+
1060
+
1061
+	/**
1062
+	 * Gets routes for the config for the specified version
1063
+	 *
1064
+	 * @param string  $version
1065
+	 * @param boolean $hidden_endpoint
1066
+	 * @return array
1067
+	 */
1068
+	protected function _get_config_route_data_for_version($version, $hidden_endpoint)
1069
+	{
1070
+		return array(
1071
+			'config'    => array(
1072
+				array(
1073
+					'callback'        => array(
1074
+						'EventEspresso\core\libraries\rest_api\controllers\config\Read',
1075
+						'handleRequest',
1076
+					),
1077
+					'methods'         => WP_REST_Server::READABLE,
1078
+					'hidden_endpoint' => $hidden_endpoint,
1079
+					'callback_args'   => array($version),
1080
+				),
1081
+			),
1082
+			'site_info' => array(
1083
+				array(
1084
+					'callback'        => array(
1085
+						'EventEspresso\core\libraries\rest_api\controllers\config\Read',
1086
+						'handleRequestSiteInfo',
1087
+					),
1088
+					'methods'         => WP_REST_Server::READABLE,
1089
+					'hidden_endpoint' => $hidden_endpoint,
1090
+					'callback_args'   => array($version),
1091
+				),
1092
+			),
1093
+		);
1094
+	}
1095
+
1096
+
1097
+
1098
+	/**
1099
+	 * Gets the meta info routes
1100
+	 *
1101
+	 * @return array @see _register_model_routes
1102
+	 * @deprecated since version 4.9.1
1103
+	 */
1104
+	protected function _register_meta_routes()
1105
+	{
1106
+		$meta_routes = array();
1107
+		foreach (self::versions_served() as $version => $hidden_endpoint) {
1108
+			$meta_routes[self::ee_api_namespace . $version] = $this->_get_meta_route_data_for_version(
1109
+				$version,
1110
+				$hidden_endpoint
1111
+			);
1112
+		}
1113
+		return $meta_routes;
1114
+	}
1115
+
1116
+
1117
+
1118
+	/**
1119
+	 * @param string  $version
1120
+	 * @param boolean $hidden_endpoint
1121
+	 * @return array
1122
+	 */
1123
+	protected function _get_meta_route_data_for_version($version, $hidden_endpoint = false)
1124
+	{
1125
+		return array(
1126
+			'resources' => array(
1127
+				array(
1128
+					'callback'        => array(
1129
+						'EventEspresso\core\libraries\rest_api\controllers\model\Meta',
1130
+						'handleRequestModelsMeta',
1131
+					),
1132
+					'methods'         => WP_REST_Server::READABLE,
1133
+					'hidden_endpoint' => $hidden_endpoint,
1134
+					'callback_args'   => array($version),
1135
+				),
1136
+			),
1137
+		);
1138
+	}
1139
+
1140
+
1141
+
1142
+	/**
1143
+	 * Tries to hide old 4.6 endpoints from the
1144
+	 *
1145
+	 * @param array $route_data
1146
+	 * @return array
1147
+	 * @throws \EE_Error
1148
+	 */
1149
+	public static function hide_old_endpoints($route_data)
1150
+	{
1151
+		//allow API clients to override which endpoints get hidden, in case
1152
+		//they want to discover particular endpoints
1153
+		//also, we don't have access to the request so we have to just grab it from the superglobal
1154
+		$force_show_ee_namespace = ltrim(
1155
+			EEH_Array::is_set($_REQUEST, 'force_show_ee_namespace', ''),
1156
+			'/'
1157
+		);
1158
+		foreach (EED_Core_Rest_Api::get_ee_route_data() as $namespace => $relative_urls) {
1159
+			foreach ($relative_urls as $resource_name => $endpoints) {
1160
+				foreach ($endpoints as $key => $endpoint) {
1161
+					//skip schema and other route options
1162
+					if (! is_numeric($key)) {
1163
+						continue;
1164
+					}
1165
+					//by default, hide "hidden_endpoint"s, unless the request indicates
1166
+					//to $force_show_ee_namespace, in which case only show that one
1167
+					//namespace's endpoints (and hide all others)
1168
+					if (
1169
+						($force_show_ee_namespace !== '' && $force_show_ee_namespace !== $namespace)
1170
+						|| ($endpoint['hidden_endpoint'] && $force_show_ee_namespace === '')
1171
+					) {
1172
+						$full_route = '/' . ltrim($namespace, '/');
1173
+						$full_route .= '/' . ltrim($resource_name, '/');
1174
+						unset($route_data[$full_route]);
1175
+					}
1176
+				}
1177
+			}
1178
+		}
1179
+		return $route_data;
1180
+	}
1181
+
1182
+
1183
+
1184
+	/**
1185
+	 * Returns an array describing which versions of core support serving requests for.
1186
+	 * Keys are core versions' major and minor version, and values are the
1187
+	 * LOWEST requested version they can serve. Eg, 4.7 can serve requests for 4.6-like
1188
+	 * data by just removing a few models and fields from the responses. However, 4.15 might remove
1189
+	 * the answers table entirely, in which case it would be very difficult for
1190
+	 * it to serve 4.6-style responses.
1191
+	 * Versions of core that are missing from this array are unknowns.
1192
+	 * previous ver
1193
+	 *
1194
+	 * @return array
1195
+	 */
1196
+	public static function version_compatibilities()
1197
+	{
1198
+		return apply_filters(
1199
+			'FHEE__EED_Core_REST_API__version_compatibilities',
1200
+			array(
1201
+				'4.8.29' => '4.8.29',
1202
+				'4.8.33' => '4.8.29',
1203
+				'4.8.34' => '4.8.29',
1204
+				'4.8.36' => '4.8.29',
1205
+			)
1206
+		);
1207
+	}
1208
+
1209
+
1210
+
1211
+	/**
1212
+	 * Gets the latest API version served. Eg if there
1213
+	 * are two versions served of the API, 4.8.29 and 4.8.32, and
1214
+	 * we are on core version 4.8.34, it will return the string "4.8.32"
1215
+	 *
1216
+	 * @return string
1217
+	 */
1218
+	public static function latest_rest_api_version()
1219
+	{
1220
+		$versions_served = \EED_Core_Rest_Api::versions_served();
1221
+		$versions_served_keys = array_keys($versions_served);
1222
+		return end($versions_served_keys);
1223
+	}
1224
+
1225
+
1226
+
1227
+	/**
1228
+	 * Using EED_Core_Rest_Api::version_compatibilities(), determines what version of
1229
+	 * EE the API can serve requests for. Eg, if we are on 4.15 of core, and
1230
+	 * we can serve requests from 4.12 or later, this will return array( '4.12', '4.13', '4.14', '4.15' ).
1231
+	 * We also indicate whether or not this version should be put in the index or not
1232
+	 *
1233
+	 * @return array keys are API version numbers (just major and minor numbers), and values
1234
+	 * are whether or not they should be hidden
1235
+	 */
1236
+	public static function versions_served()
1237
+	{
1238
+		$versions_served = array();
1239
+		$possibly_served_versions = EED_Core_Rest_Api::version_compatibilities();
1240
+		$lowest_compatible_version = end($possibly_served_versions);
1241
+		reset($possibly_served_versions);
1242
+		$versions_served_historically = array_keys($possibly_served_versions);
1243
+		$latest_version = end($versions_served_historically);
1244
+		reset($versions_served_historically);
1245
+		//for each version of core we have ever served:
1246
+		foreach ($versions_served_historically as $key_versioned_endpoint) {
1247
+			//if it's not above the current core version, and it's compatible with the current version of core
1248
+			if ($key_versioned_endpoint === $latest_version) {
1249
+				//don't hide the latest version in the index
1250
+				$versions_served[$key_versioned_endpoint] = false;
1251
+			} elseif (
1252
+				$key_versioned_endpoint >= $lowest_compatible_version
1253
+				&& $key_versioned_endpoint < EED_Core_Rest_Api::core_version()
1254
+			) {
1255
+				//include, but hide, previous versions which are still supported
1256
+				$versions_served[$key_versioned_endpoint] = true;
1257
+			} elseif (apply_filters(
1258
+				'FHEE__EED_Core_Rest_Api__versions_served__include_incompatible_versions',
1259
+				false,
1260
+				$possibly_served_versions
1261
+			)) {
1262
+				//if a version is no longer supported, don't include it in index or list of versions served
1263
+				$versions_served[$key_versioned_endpoint] = true;
1264
+			}
1265
+		}
1266
+		return $versions_served;
1267
+	}
1268
+
1269
+
1270
+
1271
+	/**
1272
+	 * Gets the major and minor version of EE core's version string
1273
+	 *
1274
+	 * @return string
1275
+	 */
1276
+	public static function core_version()
1277
+	{
1278
+		return apply_filters(
1279
+			'FHEE__EED_Core_REST_API__core_version',
1280
+			implode(
1281
+				'.',
1282
+				array_slice(
1283
+					explode(
1284
+						'.',
1285
+						espresso_version()
1286
+					),
1287
+				0,
1288
+				3
1289
+				)
1290
+			)
1291
+		);
1292
+	}
1293
+
1294
+
1295
+
1296
+	/**
1297
+	 * Gets the default limit that should be used when querying for resources
1298
+	 *
1299
+	 * @return int
1300
+	 */
1301
+	public static function get_default_query_limit()
1302
+	{
1303
+		//we actually don't use a const because we want folks to always use
1304
+		//this method, not the const directly
1305
+		return apply_filters(
1306
+			'FHEE__EED_Core_Rest_Api__get_default_query_limit',
1307
+			50
1308
+		);
1309
+	}
1310
+
1311
+
1312
+
1313
+	/**
1314
+	 *    run - initial module setup
1315
+	 *
1316
+	 * @access    public
1317
+	 * @param  WP $WP
1318
+	 * @return    void
1319
+	 */
1320
+	public function run($WP)
1321
+	{
1322
+	}
1323 1323
 }
1324 1324
 
1325 1325
 // End of file EED_Core_Rest_Api.module.php
Please login to merge, or discard this patch.
payment_methods/Paypal_Express/EE_PMT_Paypal_Express.pm.php 2 patches
Indentation   +140 added lines, -140 removed lines patch added patch discarded remove patch
@@ -1,5 +1,5 @@  discard block
 block discarded – undo
1 1
 <?php if (! defined('EVENT_ESPRESSO_VERSION')) {
2
-    exit('NO direct script access allowed');
2
+	exit('NO direct script access allowed');
3 3
 }
4 4
 
5 5
 
@@ -17,153 +17,153 @@  discard block
 block discarded – undo
17 17
 class EE_PMT_Paypal_Express extends EE_PMT_Base
18 18
 {
19 19
 
20
-    /**
21
-     * EE_PMT_Paypal_Express constructor.
22
-     */
23
-    public function __construct($pm_instance = null)
24
-    {
25
-        require_once($this->file_folder() . 'EEG_Paypal_Express.gateway.php');
26
-        $this->_gateway = new EEG_Paypal_Express();
20
+	/**
21
+	 * EE_PMT_Paypal_Express constructor.
22
+	 */
23
+	public function __construct($pm_instance = null)
24
+	{
25
+		require_once($this->file_folder() . 'EEG_Paypal_Express.gateway.php');
26
+		$this->_gateway = new EEG_Paypal_Express();
27 27
 
28
-        $this->_pretty_name = esc_html__('PayPal Express', 'event_espresso');
29
-        $this->_template_path = $this->file_folder() . 'templates' . DS;
30
-        $this->_default_description = esc_html__(
31
-            // @codingStandardsIgnoreStart
32
-            'After clicking \'Finalize Registration\', you will be forwarded to PayPal website to Login and make your payment.',
33
-            // @codingStandardsIgnoreEnd
34
-            'event_espresso'
35
-        );
36
-        $this->_default_button_url = $this->file_url() . 'lib' . DS . 'paypal-express-checkout-logo.png';
28
+		$this->_pretty_name = esc_html__('PayPal Express', 'event_espresso');
29
+		$this->_template_path = $this->file_folder() . 'templates' . DS;
30
+		$this->_default_description = esc_html__(
31
+			// @codingStandardsIgnoreStart
32
+			'After clicking \'Finalize Registration\', you will be forwarded to PayPal website to Login and make your payment.',
33
+			// @codingStandardsIgnoreEnd
34
+			'event_espresso'
35
+		);
36
+		$this->_default_button_url = $this->file_url() . 'lib' . DS . 'paypal-express-checkout-logo.png';
37 37
 
38
-        parent::__construct($pm_instance);
39
-    }
38
+		parent::__construct($pm_instance);
39
+	}
40 40
 
41 41
 
42
-    /**
43
-     * Adds the help tab.
44
-     *
45
-     * @see EE_PMT_Base::help_tabs_config()
46
-     * @return array
47
-     */
48
-    public function help_tabs_config()
49
-    {
50
-        return array(
51
-            $this->get_help_tab_name() => array(
52
-                'title'    => esc_html__('PayPal Express Settings', 'event_espresso'),
53
-                'filename' => 'payment_methods_overview_paypal_express'
54
-            )
55
-        );
56
-    }
42
+	/**
43
+	 * Adds the help tab.
44
+	 *
45
+	 * @see EE_PMT_Base::help_tabs_config()
46
+	 * @return array
47
+	 */
48
+	public function help_tabs_config()
49
+	{
50
+		return array(
51
+			$this->get_help_tab_name() => array(
52
+				'title'    => esc_html__('PayPal Express Settings', 'event_espresso'),
53
+				'filename' => 'payment_methods_overview_paypal_express'
54
+			)
55
+		);
56
+	}
57 57
 
58 58
 
59
-    /**
60
-     * Gets the form for all the settings related to this payment method type.
61
-     *
62
-     * @return EE_Payment_Method_Form
63
-     */
64
-    public function generate_new_settings_form()
65
-    {
66
-        EE_Registry::instance()->load_helper('Template');
67
-        $form = new EE_Payment_Method_Form(
68
-            array(
69
-                'extra_meta_inputs' => array(
70
-                    'api_username' => new EE_Text_Input(
71
-                        array(
72
-                            'html_label_text' => sprintf(
73
-                                esc_html__('API Username %s', 'event_espresso'),
74
-                                $this->get_help_tab_link()
75
-                            ),
76
-                            'required'        => true,
77
-                        )
78
-                    ),
79
-                    'api_password' => new EE_Text_Input(
80
-                        array(
81
-                            'html_label_text' => sprintf(
82
-                                esc_html__('API Password %s', 'event_espresso'),
83
-                                $this->get_help_tab_link()
84
-                            ),
85
-                            'required'        => true,
86
-                        )
87
-                    ),
88
-                    'api_signature' => new EE_Text_Input(
89
-                        array(
90
-                            'html_label_text' => sprintf(
91
-                                esc_html__('API Signature %s', 'event_espresso'),
92
-                                $this->get_help_tab_link()
93
-                            ),
94
-                            'required'        => true,
95
-                        )
96
-                    ),
97
-                    'request_shipping_addr' => new EE_Yes_No_Input(
98
-                        array(
99
-                            'html_label_text' => sprintf(
100
-                                esc_html__('Request Shipping Address %s', 'event_espresso'),
101
-                                $this->get_help_tab_link()
102
-                            ),
103
-                            'html_help_text'  => esc_html__(
104
-                                // @codingStandardsIgnoreStart
105
-                                'If set to "Yes", then a shipping address will be requested on the PayPal checkout page.',
106
-                                // @codingStandardsIgnoreEnd
107
-                                'event_espresso'
108
-                            ),
109
-                            'required'        => true,
110
-                            'default'         => false,
111
-                        )
112
-                    ),
113
-                    'image_url' => new EE_Admin_File_Uploader_Input(
114
-                        array(
115
-                            'html_label_text' => sprintf(
116
-                                esc_html__('Image URL %s', 'event_espresso'),
117
-                                $this->get_help_tab_link()
118
-                            ),
119
-                            'html_help_text'  => esc_html__(
120
-                                'Used for your business/personal logo on the PayPal page',
121
-                                'event_espresso'
122
-                            ),
123
-                            'required'        => false,
124
-                        )
125
-                    ),
126
-                )
127
-            )
128
-        );
129
-        return $form;
130
-    }
59
+	/**
60
+	 * Gets the form for all the settings related to this payment method type.
61
+	 *
62
+	 * @return EE_Payment_Method_Form
63
+	 */
64
+	public function generate_new_settings_form()
65
+	{
66
+		EE_Registry::instance()->load_helper('Template');
67
+		$form = new EE_Payment_Method_Form(
68
+			array(
69
+				'extra_meta_inputs' => array(
70
+					'api_username' => new EE_Text_Input(
71
+						array(
72
+							'html_label_text' => sprintf(
73
+								esc_html__('API Username %s', 'event_espresso'),
74
+								$this->get_help_tab_link()
75
+							),
76
+							'required'        => true,
77
+						)
78
+					),
79
+					'api_password' => new EE_Text_Input(
80
+						array(
81
+							'html_label_text' => sprintf(
82
+								esc_html__('API Password %s', 'event_espresso'),
83
+								$this->get_help_tab_link()
84
+							),
85
+							'required'        => true,
86
+						)
87
+					),
88
+					'api_signature' => new EE_Text_Input(
89
+						array(
90
+							'html_label_text' => sprintf(
91
+								esc_html__('API Signature %s', 'event_espresso'),
92
+								$this->get_help_tab_link()
93
+							),
94
+							'required'        => true,
95
+						)
96
+					),
97
+					'request_shipping_addr' => new EE_Yes_No_Input(
98
+						array(
99
+							'html_label_text' => sprintf(
100
+								esc_html__('Request Shipping Address %s', 'event_espresso'),
101
+								$this->get_help_tab_link()
102
+							),
103
+							'html_help_text'  => esc_html__(
104
+								// @codingStandardsIgnoreStart
105
+								'If set to "Yes", then a shipping address will be requested on the PayPal checkout page.',
106
+								// @codingStandardsIgnoreEnd
107
+								'event_espresso'
108
+							),
109
+							'required'        => true,
110
+							'default'         => false,
111
+						)
112
+					),
113
+					'image_url' => new EE_Admin_File_Uploader_Input(
114
+						array(
115
+							'html_label_text' => sprintf(
116
+								esc_html__('Image URL %s', 'event_espresso'),
117
+								$this->get_help_tab_link()
118
+							),
119
+							'html_help_text'  => esc_html__(
120
+								'Used for your business/personal logo on the PayPal page',
121
+								'event_espresso'
122
+							),
123
+							'required'        => false,
124
+						)
125
+					),
126
+				)
127
+			)
128
+		);
129
+		return $form;
130
+	}
131 131
 
132 132
 
133
-    /**
134
-     * Creates a billing form for this payment method type.
135
-     *
136
-     * @param \EE_Transaction $transaction
137
-     * @return \EE_Billing_Info_Form
138
-     */
139
-    public function generate_new_billing_form(EE_Transaction $transaction = null)
140
-    {
141
-        if ($this->_pm_instance->debug_mode()) {
142
-            $form = new EE_Billing_Info_Form(
143
-                $this->_pm_instance,
144
-                array(
145
-                    'name' => 'paypal_express_Info_Form',
146
-                    'subsections' => array(
147
-                        'paypal_express_debug_info' => new EE_Form_Section_Proper(
148
-                            array(
149
-                                'layout_strategy' => new EE_Template_Layout(
150
-                                    array(
151
-                                        'layout_template_file' => $this->_template_path
152
-                                                                    . 'paypal_express_debug_info.template.php',
153
-                                        'template_args'        => array(
154
-                                            'debug_mode' => $this->_pm_instance->debug_mode()
155
-                                        )
156
-                                    )
157
-                                )
158
-                            )
159
-                        )
160
-                    )
161
-                )
162
-            );
163
-            return $form;
164
-        }
133
+	/**
134
+	 * Creates a billing form for this payment method type.
135
+	 *
136
+	 * @param \EE_Transaction $transaction
137
+	 * @return \EE_Billing_Info_Form
138
+	 */
139
+	public function generate_new_billing_form(EE_Transaction $transaction = null)
140
+	{
141
+		if ($this->_pm_instance->debug_mode()) {
142
+			$form = new EE_Billing_Info_Form(
143
+				$this->_pm_instance,
144
+				array(
145
+					'name' => 'paypal_express_Info_Form',
146
+					'subsections' => array(
147
+						'paypal_express_debug_info' => new EE_Form_Section_Proper(
148
+							array(
149
+								'layout_strategy' => new EE_Template_Layout(
150
+									array(
151
+										'layout_template_file' => $this->_template_path
152
+																	. 'paypal_express_debug_info.template.php',
153
+										'template_args'        => array(
154
+											'debug_mode' => $this->_pm_instance->debug_mode()
155
+										)
156
+									)
157
+								)
158
+							)
159
+						)
160
+					)
161
+				)
162
+			);
163
+			return $form;
164
+		}
165 165
 
166
-        return false;
167
-    }
166
+		return false;
167
+	}
168 168
 }
169 169
 // End of file EE_PMT_Paypal_Express.pm.php
170 170
\ No newline at end of file
Please login to merge, or discard this patch.
Spacing   +4 added lines, -4 removed lines patch added patch discarded remove patch
@@ -1,4 +1,4 @@  discard block
 block discarded – undo
1
-<?php if (! defined('EVENT_ESPRESSO_VERSION')) {
1
+<?php if ( ! defined('EVENT_ESPRESSO_VERSION')) {
2 2
     exit('NO direct script access allowed');
3 3
 }
4 4
 
@@ -22,18 +22,18 @@  discard block
 block discarded – undo
22 22
      */
23 23
     public function __construct($pm_instance = null)
24 24
     {
25
-        require_once($this->file_folder() . 'EEG_Paypal_Express.gateway.php');
25
+        require_once($this->file_folder().'EEG_Paypal_Express.gateway.php');
26 26
         $this->_gateway = new EEG_Paypal_Express();
27 27
 
28 28
         $this->_pretty_name = esc_html__('PayPal Express', 'event_espresso');
29
-        $this->_template_path = $this->file_folder() . 'templates' . DS;
29
+        $this->_template_path = $this->file_folder().'templates'.DS;
30 30
         $this->_default_description = esc_html__(
31 31
             // @codingStandardsIgnoreStart
32 32
             'After clicking \'Finalize Registration\', you will be forwarded to PayPal website to Login and make your payment.',
33 33
             // @codingStandardsIgnoreEnd
34 34
             'event_espresso'
35 35
         );
36
-        $this->_default_button_url = $this->file_url() . 'lib' . DS . 'paypal-express-checkout-logo.png';
36
+        $this->_default_button_url = $this->file_url().'lib'.DS.'paypal-express-checkout-logo.png';
37 37
 
38 38
         parent::__construct($pm_instance);
39 39
     }
Please login to merge, or discard this patch.
payment_methods/Paypal_Express/EEG_Paypal_Express.gateway.php 2 patches
Indentation   +669 added lines, -669 removed lines patch added patch discarded remove patch
@@ -1,5 +1,5 @@  discard block
 block discarded – undo
1 1
 <?php if (! defined('EVENT_ESPRESSO_VERSION')) {
2
-    exit('NO direct script access allowed');
2
+	exit('NO direct script access allowed');
3 3
 }
4 4
 
5 5
 
@@ -16,678 +16,678 @@  discard block
 block discarded – undo
16 16
  */
17 17
 //Quickfix to address https://events.codebasehq.com/projects/event-espresso/tickets/11089 ASAP
18 18
 if (! function_exists('mb_strcut')) {
19
-    /**
20
-     * Very simple mimic of mb_substr (which WP ensures exists in wp-includes/compat.php). Still has all the problems of mb_substr
21
-     * (namely, that we might send too many characters to PayPal; however in this case they just issue a warning but nothing breaks)
22
-     * @param $string
23
-     * @param $start
24
-     * @param $length
25
-     * @return bool|string
26
-     */
27
-    function mb_strcut($string, $start, $length = null)
28
-    {
29
-        return mb_substr($string, $start, $length);
30
-    }
19
+	/**
20
+	 * Very simple mimic of mb_substr (which WP ensures exists in wp-includes/compat.php). Still has all the problems of mb_substr
21
+	 * (namely, that we might send too many characters to PayPal; however in this case they just issue a warning but nothing breaks)
22
+	 * @param $string
23
+	 * @param $start
24
+	 * @param $length
25
+	 * @return bool|string
26
+	 */
27
+	function mb_strcut($string, $start, $length = null)
28
+	{
29
+		return mb_substr($string, $start, $length);
30
+	}
31 31
 }
32 32
 class EEG_Paypal_Express extends EE_Offsite_Gateway
33 33
 {
34 34
 
35
-    /**
36
-     * Merchant API Username.
37
-     *
38
-     * @var string
39
-     */
40
-    protected $_api_username;
41
-
42
-    /**
43
-     * Merchant API Password.
44
-     *
45
-     * @var string
46
-     */
47
-    protected $_api_password;
48
-
49
-    /**
50
-     * API Signature.
51
-     *
52
-     * @var string
53
-     */
54
-    protected $_api_signature;
55
-
56
-    /**
57
-     * Request Shipping address on PP checkout page.
58
-     *
59
-     * @var string
60
-     */
61
-    protected $_request_shipping_addr;
62
-
63
-    /**
64
-     * Business/personal logo.
65
-     *
66
-     * @var string
67
-     */
68
-    protected $_image_url;
69
-
70
-    /**
71
-     * gateway URL variable
72
-     *
73
-     * @var string
74
-     */
75
-    protected $_base_gateway_url = '';
76
-
77
-
78
-
79
-    /**
80
-     * EEG_Paypal_Express constructor.
81
-     */
82
-    public function __construct()
83
-    {
84
-        $this->_currencies_supported = array(
85
-            'USD',
86
-            'AUD',
87
-            'BRL',
88
-            'CAD',
89
-            'CZK',
90
-            'DKK',
91
-            'EUR',
92
-            'HKD',
93
-            'HUF',
94
-            'ILS',
95
-            'JPY',
96
-            'MYR',
97
-            'MXN',
98
-            'NOK',
99
-            'NZD',
100
-            'PHP',
101
-            'PLN',
102
-            'GBP',
103
-            'RUB',
104
-            'SGD',
105
-            'SEK',
106
-            'CHF',
107
-            'TWD',
108
-            'THB',
109
-            'TRY',
110
-        );
111
-        parent::__construct();
112
-    }
113
-
114
-
115
-
116
-    /**
117
-     * Sets the gateway URL variable based on whether debug mode is enabled or not.
118
-     *
119
-     * @param array $settings_array
120
-     */
121
-    public function set_settings($settings_array)
122
-    {
123
-        parent::set_settings($settings_array);
124
-        // Redirect URL.
125
-        $this->_base_gateway_url = $this->_debug_mode
126
-            ? 'https://api-3t.sandbox.paypal.com/nvp'
127
-            : 'https://api-3t.paypal.com/nvp';
128
-    }
129
-
130
-
131
-
132
-    /**
133
-     * @param EEI_Payment $payment
134
-     * @param array       $billing_info
135
-     * @param string      $return_url
136
-     * @param string      $notify_url
137
-     * @param string      $cancel_url
138
-     * @return \EE_Payment|\EEI_Payment
139
-     * @throws \EE_Error
140
-     */
141
-    public function set_redirection_info(
142
-        $payment,
143
-        $billing_info = array(),
144
-        $return_url = null,
145
-        $notify_url = null,
146
-        $cancel_url = null
147
-    ) {
148
-        if (! $payment instanceof EEI_Payment) {
149
-            $payment->set_gateway_response(
150
-                esc_html__(
151
-                    'Error. No associated payment was found.',
152
-                    'event_espresso'
153
-                )
154
-            );
155
-            $payment->set_status($this->_pay_model->failed_status());
156
-            return $payment;
157
-        }
158
-        $transaction = $payment->transaction();
159
-        if (! $transaction instanceof EEI_Transaction) {
160
-            $payment->set_gateway_response(
161
-                esc_html__(
162
-                    'Could not process this payment because it has no associated transaction.',
163
-                    'event_espresso'
164
-                )
165
-            );
166
-            $payment->set_status($this->_pay_model->failed_status());
167
-            return $payment;
168
-        }
169
-        $order_description = mb_strcut($this->_format_order_description($payment), 0, 127);
170
-        $primary_registration = $transaction->primary_registration();
171
-        $primary_attendee = $primary_registration instanceof EE_Registration
172
-            ? $primary_registration->attendee()
173
-            : false;
174
-        $locale = explode('-', get_bloginfo('language'));
175
-        // Gather request parameters.
176
-        $token_request_dtls = array(
177
-            'METHOD'                         => 'SetExpressCheckout',
178
-            'PAYMENTREQUEST_0_AMT'           => $payment->amount(),
179
-            'PAYMENTREQUEST_0_CURRENCYCODE'  => $payment->currency_code(),
180
-            'PAYMENTREQUEST_0_DESC'          => $order_description,
181
-            'RETURNURL'                      => $return_url,
182
-            'CANCELURL'                      => $cancel_url,
183
-            'PAYMENTREQUEST_0_PAYMENTACTION' => 'Sale',
184
-            // Buyer does not need to create a PayPal account to check out.
185
-            // This is referred to as PayPal Account Optional.
186
-            'SOLUTIONTYPE'                   => 'Sole',
187
-            //EE will blow up if you change this
188
-            'BUTTONSOURCE'                   => 'EventEspresso_SP',
189
-            // Locale of the pages displayed by PayPal during Express Checkout.
190
-            'LOCALECODE'                     => $locale[1]
191
-        );
192
-        // Show itemized list.
193
-        $itemized_list = $this->itemize_list($payment, $transaction);
194
-        $token_request_dtls = array_merge($token_request_dtls, $itemized_list);
195
-        // Automatically filling out shipping and contact information.
196
-        if ($this->_request_shipping_addr && $primary_attendee instanceof EEI_Attendee) {
197
-            // If you do not pass the shipping address, PayPal obtains it from the buyer's account profile.
198
-            $token_request_dtls['NOSHIPPING'] = '2';
199
-            $token_request_dtls['PAYMENTREQUEST_0_SHIPTOSTREET'] = $primary_attendee->address();
200
-            $token_request_dtls['PAYMENTREQUEST_0_SHIPTOSTREET2'] = $primary_attendee->address2();
201
-            $token_request_dtls['PAYMENTREQUEST_0_SHIPTOCITY'] = $primary_attendee->city();
202
-            $token_request_dtls['PAYMENTREQUEST_0_SHIPTOSTATE'] = $primary_attendee->state_abbrev();
203
-            $token_request_dtls['PAYMENTREQUEST_0_SHIPTOCOUNTRYCODE'] = $primary_attendee->country_ID();
204
-            $token_request_dtls['PAYMENTREQUEST_0_SHIPTOZIP'] = $primary_attendee->zip();
205
-            $token_request_dtls['PAYMENTREQUEST_0_EMAIL'] = $primary_attendee->email();
206
-            $token_request_dtls['PAYMENTREQUEST_0_SHIPTOPHONENUM'] = $primary_attendee->phone();
207
-        } elseif (! $this->_request_shipping_addr) {
208
-            // Do not request shipping details on the PP Checkout page.
209
-            $token_request_dtls['NOSHIPPING'] = '1';
210
-            $token_request_dtls['REQCONFIRMSHIPPING'] = '0';
211
-        }
212
-        // Used a business/personal logo on the PayPal page.
213
-        if (! empty($this->_image_url)) {
214
-            $token_request_dtls['LOGOIMG'] = $this->_image_url;
215
-        }
216
-        $token_request_dtls = apply_filters(
217
-            'FHEE__EEG_Paypal_Express__set_redirection_info__arguments',
218
-            $token_request_dtls,
219
-            $this
220
-        );
221
-        // Request PayPal token.
222
-        $token_request_response = $this->_ppExpress_request($token_request_dtls, 'Payment Token', $payment);
223
-        $token_rstatus = $this->_ppExpress_check_response($token_request_response);
224
-        $response_args = (isset($token_rstatus['args']) && is_array($token_rstatus['args']))
225
-            ? $token_rstatus['args']
226
-            : array();
227
-        if ($token_rstatus['status']) {
228
-            // We got the Token so we may continue with the payment and redirect the client.
229
-            $payment->set_details($response_args);
230
-            $gateway_url = $this->_debug_mode ? 'https://www.sandbox.paypal.com' : 'https://www.paypal.com';
231
-            $payment->set_redirect_url(
232
-                $gateway_url
233
-                . '/checkoutnow?useraction=commit&cmd=_express-checkout&token='
234
-                . $response_args['TOKEN']
235
-            );
236
-        } else {
237
-            if (isset($response_args['L_ERRORCODE'])) {
238
-                $payment->set_gateway_response($response_args['L_ERRORCODE'] . '; ' . $response_args['L_SHORTMESSAGE']);
239
-            } else {
240
-                $payment->set_gateway_response(
241
-                    esc_html__(
242
-                        'Error occurred while trying to setup the Express Checkout.',
243
-                        'event_espresso'
244
-                    )
245
-                );
246
-            }
247
-            $payment->set_details($response_args);
248
-            $payment->set_status($this->_pay_model->failed_status());
249
-        }
250
-        return $payment;
251
-    }
252
-
253
-
254
-
255
-    /**
256
-     * @param array           $update_info {
257
-     * @type string           $gateway_txn_id
258
-     * @type string status an EEMI_Payment status
259
-     *                                     }
260
-     * @param EEI_Transaction $transaction
261
-     * @return EEI_Payment
262
-     */
263
-    public function handle_payment_update($update_info, $transaction)
264
-    {
265
-        $payment = $transaction instanceof EEI_Transaction ? $transaction->last_payment() : null;
266
-        if ($payment instanceof EEI_Payment) {
267
-            $this->log(array('Return from Authorization' => $update_info), $payment);
268
-            $transaction = $payment->transaction();
269
-            if (! $transaction instanceof EEI_Transaction) {
270
-                $payment->set_gateway_response(
271
-                    esc_html__(
272
-                        'Could not process this payment because it has no associated transaction.',
273
-                        'event_espresso'
274
-                    )
275
-                );
276
-                $payment->set_status($this->_pay_model->failed_status());
277
-                return $payment;
278
-            }
279
-            $primary_registrant = $transaction->primary_registration();
280
-            $payment_details = $payment->details();
281
-            // Check if we still have the token.
282
-            if (! isset($payment_details['TOKEN']) || empty($payment_details['TOKEN'])) {
283
-                $payment->set_status($this->_pay_model->failed_status());
284
-                return $payment;
285
-            }
286
-            $cdetails_request_dtls = array(
287
-                'METHOD' => 'GetExpressCheckoutDetails',
288
-                'TOKEN'  => $payment_details['TOKEN'],
289
-            );
290
-            // Request Customer Details.
291
-            $cdetails_request_response = $this->_ppExpress_request(
292
-                $cdetails_request_dtls,
293
-                'Customer Details',
294
-                $payment
295
-            );
296
-            $cdetails_rstatus = $this->_ppExpress_check_response($cdetails_request_response);
297
-            $cdata_response_args = (isset($cdetails_rstatus['args']) && is_array($cdetails_rstatus['args']))
298
-                ? $cdetails_rstatus['args']
299
-                : array();
300
-            if ($cdetails_rstatus['status']) {
301
-                // We got the PayerID so now we can Complete the transaction.
302
-                $docheckout_request_dtls = array(
303
-                    'METHOD'                         => 'DoExpressCheckoutPayment',
304
-                    'PAYERID'                        => $cdata_response_args['PAYERID'],
305
-                    'TOKEN'                          => $payment_details['TOKEN'],
306
-                    'PAYMENTREQUEST_0_PAYMENTACTION' => 'Sale',
307
-                    'PAYMENTREQUEST_0_AMT'           => $payment->amount(),
308
-                    'PAYMENTREQUEST_0_CURRENCYCODE'  => $payment->currency_code(),
309
-                    //EE will blow up if you change this
310
-                    'BUTTONSOURCE'                   => 'EventEspresso_SP',
311
-                );
312
-                 // Include itemized list.
313
-                $itemized_list = $this->itemize_list(
314
-                    $payment,
315
-                    $transaction,
316
-                    $cdata_response_args
317
-                );
318
-                $docheckout_request_dtls = array_merge($docheckout_request_dtls, $itemized_list);
319
-                // Payment Checkout/Capture.
320
-                $docheckout_request_response = $this->_ppExpress_request(
321
-                    $docheckout_request_dtls,
322
-                    'Do Payment',
323
-                    $payment
324
-                );
325
-                $docheckout_rstatus = $this->_ppExpress_check_response($docheckout_request_response);
326
-                $docheckout_response_args = (isset($docheckout_rstatus['args']) && is_array($docheckout_rstatus['args']))
327
-                    ? $docheckout_rstatus['args']
328
-                    : array();
329
-                if ($docheckout_rstatus['status']) {
330
-                    // All is well, payment approved.
331
-                    $primary_registration_code = $primary_registrant instanceof EE_Registration ?
332
-                        $primary_registrant->reg_code()
333
-                        : '';
334
-                    $payment->set_extra_accntng($primary_registration_code);
335
-                    $payment->set_amount(isset($docheckout_response_args['PAYMENTINFO_0_AMT'])
336
-                        ? (float)$docheckout_response_args['PAYMENTINFO_0_AMT']
337
-                        : 0);
338
-                    $payment->set_txn_id_chq_nmbr(isset($docheckout_response_args['PAYMENTINFO_0_TRANSACTIONID'])
339
-                        ? $docheckout_response_args['PAYMENTINFO_0_TRANSACTIONID']
340
-                        : null);
341
-                    $payment->set_details($cdata_response_args);
342
-                    $payment->set_gateway_response(isset($docheckout_response_args['PAYMENTINFO_0_ACK'])
343
-                        ? $docheckout_response_args['PAYMENTINFO_0_ACK']
344
-                        : '');
345
-                    $payment->set_status($this->_pay_model->approved_status());
346
-                } else {
347
-                    if (isset($docheckout_response_args['L_ERRORCODE'])) {
348
-                        $payment->set_gateway_response(
349
-                            $docheckout_response_args['L_ERRORCODE']
350
-                            . '; '
351
-                            . $docheckout_response_args['L_SHORTMESSAGE']
352
-                        );
353
-                    } else {
354
-                        $payment->set_gateway_response(
355
-                            esc_html__(
356
-                                'Error occurred while trying to Capture the funds.',
357
-                                'event_espresso'
358
-                            )
359
-                        );
360
-                    }
361
-                    $payment->set_details($docheckout_response_args);
362
-                    $payment->set_status($this->_pay_model->declined_status());
363
-                }
364
-            } else {
365
-                if (isset($cdata_response_args['L_ERRORCODE'])) {
366
-                    $payment->set_gateway_response(
367
-                        $cdata_response_args['L_ERRORCODE']
368
-                        . '; '
369
-                        . $cdata_response_args['L_SHORTMESSAGE']
370
-                    );
371
-                } else {
372
-                    $payment->set_gateway_response(
373
-                        esc_html__(
374
-                            'Error occurred while trying to get payment Details from PayPal.',
375
-                            'event_espresso'
376
-                        )
377
-                    );
378
-                }
379
-                $payment->set_details($cdata_response_args);
380
-                $payment->set_status($this->_pay_model->failed_status());
381
-            }
382
-        } else {
383
-            $payment->set_gateway_response(
384
-                esc_html__(
385
-                    'Error occurred while trying to process the payment.',
386
-                    'event_espresso'
387
-                )
388
-            );
389
-            $payment->set_status($this->_pay_model->failed_status());
390
-        }
391
-        return $payment;
392
-    }
393
-
394
-
395
-
396
-    /**
397
-     *  Make a list of items that are in the giver transaction.
398
-     *
399
-     * @param EEI_Payment     $payment
400
-     * @param EEI_Transaction $transaction
401
-     * @param array           $request_response_args Data from a previous communication with PP.
402
-     * @return array
403
-     */
404
-    public function itemize_list(EEI_Payment $payment, EEI_Transaction $transaction, $request_response_args = array())
405
-    {
406
-        $itemized_list = array();
407
-        // If we have data from a previous communication with PP (on this transaction) we may use that for our list...
408
-        if (
409
-            ! empty($request_response_args)
410
-            && array_key_exists('L_PAYMENTREQUEST_0_AMT0', $request_response_args)
411
-            && array_key_exists('PAYMENTREQUEST_0_ITEMAMT', $request_response_args)
412
-        ) {
413
-            foreach ($request_response_args as $arg_key => $arg_val) {
414
-                if (
415
-                    strpos($arg_key, 'PAYMENTREQUEST_') !== false
416
-                    && strpos($arg_key, 'NOTIFYURL') === false
417
-                ) {
418
-                    $itemized_list[$arg_key] = $arg_val;
419
-                }
420
-            }
421
-            // If we got only a few Items then something is not right.
422
-            if (count($itemized_list) > 2) {
423
-                return $itemized_list;
424
-            } else {
425
-                if (WP_DEBUG) {
426
-                    throw new EE_Error(
427
-                        sprintf(
428
-                            esc_html__(
429
-                                // @codingStandardsIgnoreStart
430
-                                'Unable to continue with the checkout because a proper purchase list could not be generated. The purchased list we could have sent was %1$s',
431
-                                // @codingStandardsIgnoreEnd
432
-                                'event_espresso'
433
-                            ),
434
-                            wp_json_encode($itemized_list)
435
-                        )
436
-                    );
437
-                }
438
-                // Reset the list and log an error, maybe allow to try and generate a new list (below).
439
-                $itemized_list = array();
440
-                $this->log(
441
-                    array(
442
-                        esc_html__(
443
-                            'Could not generate a proper item list with:',
444
-                            'event_espresso'
445
-                        ) => $request_response_args
446
-                    ),
447
-                    $payment
448
-                );
449
-            }
450
-        }
451
-        // ...otherwise we generate a new list for this transaction.
452
-        if ($this->_money->compare_floats($payment->amount(), $transaction->total(), '==')) {
453
-            $item_num = 0;
454
-            $itemized_sum = 0;
455
-            $total_line_items = $transaction->total_line_item();
456
-            // Go through each item in the list.
457
-            foreach ($total_line_items->get_items() as $line_item) {
458
-                if ($line_item instanceof EE_Line_Item) {
459
-                    // PayPal doesn't like line items with 0.00 amount, so we may skip those.
460
-                    if (EEH_Money::compare_floats($line_item->total(), '0.00', '==')) {
461
-                        continue;
462
-                    }
463
-                    $unit_price = $line_item->unit_price();
464
-                    $line_item_quantity = $line_item->quantity();
465
-                    // This is a discount.
466
-                    if ($line_item->is_percent()) {
467
-                        $unit_price = $line_item->total();
468
-                        $line_item_quantity = 1;
469
-                    }
470
-                    // Item Name.
471
-                    $itemized_list['L_PAYMENTREQUEST_0_NAME' . $item_num] = mb_strcut(
472
-                        $this->_format_line_item_name($line_item, $payment),
473
-                        0,
474
-                        127
475
-                    );
476
-                    // Item description.
477
-                    $itemized_list['L_PAYMENTREQUEST_0_DESC' . $item_num] = mb_strcut(
478
-                        $this->_format_line_item_desc($line_item, $payment),
479
-                        0,
480
-                        127
481
-                    );
482
-                    // Cost of individual item.
483
-                    $itemized_list['L_PAYMENTREQUEST_0_AMT' . $item_num] = $this->format_currency($unit_price);
484
-                    // Item Number.
485
-                    $itemized_list['L_PAYMENTREQUEST_0_NUMBER' . $item_num] = $item_num + 1;
486
-                    // Item quantity.
487
-                    $itemized_list['L_PAYMENTREQUEST_0_QTY' . $item_num] = $line_item_quantity;
488
-                    // Digital item is sold.
489
-                    $itemized_list['L_PAYMENTREQUEST_0_ITEMCATEGORY' . $item_num] = 'Physical';
490
-                    $itemized_sum += $line_item->total();
491
-                    ++$item_num;
492
-                }
493
-            }
494
-            // Item's sales S/H and tax amount.
495
-            $itemized_list['PAYMENTREQUEST_0_ITEMAMT'] = $total_line_items->get_items_total();
496
-            $itemized_list['PAYMENTREQUEST_0_TAXAMT'] = $total_line_items->get_total_tax();
497
-            $itemized_list['PAYMENTREQUEST_0_SHIPPINGAMT'] = '0';
498
-            $itemized_list['PAYMENTREQUEST_0_HANDLINGAMT'] = '0';
499
-            $itemized_sum_diff_from_txn_total = round(
500
-                $transaction->total() - $itemized_sum - $total_line_items->get_total_tax(),
501
-                2
502
-            );
503
-            // If we were not able to recognize some item like promotion, surcharge or cancellation,
504
-            // add the difference as an extra line item.
505
-            if ($this->_money->compare_floats($itemized_sum_diff_from_txn_total, 0, '!=')) {
506
-                // Item Name.
507
-                $itemized_list['L_PAYMENTREQUEST_0_NAME' . $item_num] = mb_strcut(
508
-                    esc_html__(
509
-                        'Other (promotion/surcharge/cancellation)',
510
-                        'event_espresso'
511
-                    ),
512
-                    0,
513
-                    127
514
-                );
515
-                // Item description.
516
-                $itemized_list['L_PAYMENTREQUEST_0_DESC' . $item_num] = '';
517
-                // Cost of individual item.
518
-                $itemized_list['L_PAYMENTREQUEST_0_AMT' . $item_num] = $this->format_currency(
519
-                    $itemized_sum_diff_from_txn_total
520
-                );
521
-                // Item Number.
522
-                $itemized_list['L_PAYMENTREQUEST_0_NUMBER' . $item_num] = $item_num + 1;
523
-                // Item quantity.
524
-                $itemized_list['L_PAYMENTREQUEST_0_QTY' . $item_num] = 1;
525
-                // Digital item is sold.
526
-                $itemized_list['L_PAYMENTREQUEST_0_ITEMCATEGORY' . $item_num] = 'Physical';
527
-                $item_num++;
528
-            }
529
-        } else {
530
-            // Just one Item.
531
-            // Item Name.
532
-            $itemized_list['L_PAYMENTREQUEST_0_NAME0'] = mb_strcut(
533
-                $this->_format_partial_payment_line_item_name($payment),
534
-                0,
535
-                127
536
-            );
537
-            // Item description.
538
-            $itemized_list['L_PAYMENTREQUEST_0_DESC0'] = mb_strcut(
539
-                $this->_format_partial_payment_line_item_desc($payment),
540
-                0,
541
-                127
542
-            );
543
-            // Cost of individual item.
544
-            $itemized_list['L_PAYMENTREQUEST_0_AMT0'] = $this->format_currency($payment->amount());
545
-            // Item Number.
546
-            $itemized_list['L_PAYMENTREQUEST_0_NUMBER0'] = 1;
547
-            // Item quantity.
548
-            $itemized_list['L_PAYMENTREQUEST_0_QTY0'] = 1;
549
-            // Digital item is sold.
550
-            $itemized_list['L_PAYMENTREQUEST_0_ITEMCATEGORY0'] = 'Physical';
551
-            // Item's sales S/H and tax amount.
552
-            $itemized_list['PAYMENTREQUEST_0_ITEMAMT'] = $this->format_currency($payment->amount());
553
-            $itemized_list['PAYMENTREQUEST_0_TAXAMT'] = '0';
554
-            $itemized_list['PAYMENTREQUEST_0_SHIPPINGAMT'] = '0';
555
-            $itemized_list['PAYMENTREQUEST_0_HANDLINGAMT'] = '0';
556
-        }
557
-        return $itemized_list;
558
-    }
559
-
560
-
561
-
562
-    /**
563
-     *  Make the Express checkout request.
564
-     *
565
-     * @param array       $request_params
566
-     * @param string      $request_text
567
-     * @param EEI_Payment $payment
568
-     * @return mixed
569
-     */
570
-    public function _ppExpress_request($request_params, $request_text, $payment)
571
-    {
572
-        $request_dtls = array(
573
-            'VERSION'   => '204.0',
574
-            'USER'      => urlencode($this->_api_username),
575
-            'PWD'       => urlencode($this->_api_password),
576
-            'SIGNATURE' => urlencode($this->_api_signature),
577
-        );
578
-        $dtls = array_merge($request_dtls, $request_params);
579
-        $this->_log_clean_request($dtls, $payment, $request_text . ' Request');
580
-        // Request Customer Details.
581
-        $request_response = wp_remote_post(
582
-            $this->_base_gateway_url,
583
-            array(
584
-                'method'      => 'POST',
585
-                'timeout'     => 45,
586
-                'httpversion' => '1.1',
587
-                'cookies'     => array(),
588
-                'headers'     => array(),
589
-                'body'        => http_build_query($dtls),
590
-            )
591
-        );
592
-        // Log the response.
593
-        $this->log(array($request_text . ' Response' => $request_response), $payment);
594
-        return $request_response;
595
-    }
596
-
597
-
598
-
599
-    /**
600
-     *  Check the response status.
601
-     *
602
-     * @param mixed $request_response
603
-     * @return array
604
-     */
605
-    public function _ppExpress_check_response($request_response)
606
-    {
607
-        if (is_wp_error($request_response) || empty($request_response['body'])) {
608
-            // If we got here then there was an error in this request.
609
-            return array('status' => false, 'args' => $request_response);
610
-        }
611
-        $response_args = array();
612
-        parse_str(urldecode($request_response['body']), $response_args);
613
-        if (! isset($response_args['ACK'])) {
614
-            return array('status' => false, 'args' => $request_response);
615
-        }
616
-        if (
617
-            (
618
-                isset($response_args['PAYERID'])
619
-                || isset($response_args['TOKEN'])
620
-                || isset($response_args['PAYMENTINFO_0_TRANSACTIONID'])
621
-                || (isset($response_args['PAYMENTSTATUS']) && $response_args['PAYMENTSTATUS'] === 'Completed')
622
-            )
623
-            && in_array($response_args['ACK'], array('Success', 'SuccessWithWarning'), true)
624
-        ) {
625
-            // Response status OK, return response parameters for further processing.
626
-            return array('status' => true, 'args' => $response_args);
627
-        }
628
-        $errors = $this->_get_errors($response_args);
629
-        return array('status' => false, 'args' => $errors);
630
-    }
631
-
632
-
633
-
634
-    /**
635
-     *  Log a "Cleared" request.
636
-     *
637
-     * @param array       $request
638
-     * @param EEI_Payment $payment
639
-     * @param string      $info
640
-     * @return void
641
-     */
642
-    private function _log_clean_request($request, $payment, $info)
643
-    {
644
-        $cleaned_request_data = $request;
645
-        unset($cleaned_request_data['PWD'], $cleaned_request_data['USER'], $cleaned_request_data['SIGNATURE']);
646
-        $this->log(array($info => $cleaned_request_data), $payment);
647
-    }
648
-
649
-
650
-
651
-    /**
652
-     *  Get error from the response data.
653
-     *
654
-     * @param array $data_array
655
-     * @return array
656
-     */
657
-    private function _get_errors($data_array)
658
-    {
659
-        $errors = array();
660
-        $n = 0;
661
-        while (isset($data_array["L_ERRORCODE{$n}"])) {
662
-            $l_error_code = isset($data_array["L_ERRORCODE{$n}"])
663
-                ? $data_array["L_ERRORCODE{$n}"]
664
-                : '';
665
-            $l_severity_code = isset($data_array["L_SEVERITYCODE{$n}"])
666
-                ? $data_array["L_SEVERITYCODE{$n}"]
667
-                : '';
668
-            $l_short_message = isset($data_array["L_SHORTMESSAGE{$n}"])
669
-                ? $data_array["L_SHORTMESSAGE{$n}"]
670
-                : '';
671
-            $l_long_message = isset($data_array["L_LONGMESSAGE{$n}"])
672
-                ? $data_array["L_LONGMESSAGE{$n}"]
673
-                : '';
674
-            if ($n === 0) {
675
-                $errors = array(
676
-                    'L_ERRORCODE'    => $l_error_code,
677
-                    'L_SHORTMESSAGE' => $l_short_message,
678
-                    'L_LONGMESSAGE'  => $l_long_message,
679
-                    'L_SEVERITYCODE' => $l_severity_code,
680
-                );
681
-            } else {
682
-                $errors['L_ERRORCODE'] .= ', ' . $l_error_code;
683
-                $errors['L_SHORTMESSAGE'] .= ', ' . $l_short_message;
684
-                $errors['L_LONGMESSAGE'] .= ', ' . $l_long_message;
685
-                $errors['L_SEVERITYCODE'] .= ', ' . $l_severity_code;
686
-            }
687
-            $n++;
688
-        }
689
-        return $errors;
690
-    }
35
+	/**
36
+	 * Merchant API Username.
37
+	 *
38
+	 * @var string
39
+	 */
40
+	protected $_api_username;
41
+
42
+	/**
43
+	 * Merchant API Password.
44
+	 *
45
+	 * @var string
46
+	 */
47
+	protected $_api_password;
48
+
49
+	/**
50
+	 * API Signature.
51
+	 *
52
+	 * @var string
53
+	 */
54
+	protected $_api_signature;
55
+
56
+	/**
57
+	 * Request Shipping address on PP checkout page.
58
+	 *
59
+	 * @var string
60
+	 */
61
+	protected $_request_shipping_addr;
62
+
63
+	/**
64
+	 * Business/personal logo.
65
+	 *
66
+	 * @var string
67
+	 */
68
+	protected $_image_url;
69
+
70
+	/**
71
+	 * gateway URL variable
72
+	 *
73
+	 * @var string
74
+	 */
75
+	protected $_base_gateway_url = '';
76
+
77
+
78
+
79
+	/**
80
+	 * EEG_Paypal_Express constructor.
81
+	 */
82
+	public function __construct()
83
+	{
84
+		$this->_currencies_supported = array(
85
+			'USD',
86
+			'AUD',
87
+			'BRL',
88
+			'CAD',
89
+			'CZK',
90
+			'DKK',
91
+			'EUR',
92
+			'HKD',
93
+			'HUF',
94
+			'ILS',
95
+			'JPY',
96
+			'MYR',
97
+			'MXN',
98
+			'NOK',
99
+			'NZD',
100
+			'PHP',
101
+			'PLN',
102
+			'GBP',
103
+			'RUB',
104
+			'SGD',
105
+			'SEK',
106
+			'CHF',
107
+			'TWD',
108
+			'THB',
109
+			'TRY',
110
+		);
111
+		parent::__construct();
112
+	}
113
+
114
+
115
+
116
+	/**
117
+	 * Sets the gateway URL variable based on whether debug mode is enabled or not.
118
+	 *
119
+	 * @param array $settings_array
120
+	 */
121
+	public function set_settings($settings_array)
122
+	{
123
+		parent::set_settings($settings_array);
124
+		// Redirect URL.
125
+		$this->_base_gateway_url = $this->_debug_mode
126
+			? 'https://api-3t.sandbox.paypal.com/nvp'
127
+			: 'https://api-3t.paypal.com/nvp';
128
+	}
129
+
130
+
131
+
132
+	/**
133
+	 * @param EEI_Payment $payment
134
+	 * @param array       $billing_info
135
+	 * @param string      $return_url
136
+	 * @param string      $notify_url
137
+	 * @param string      $cancel_url
138
+	 * @return \EE_Payment|\EEI_Payment
139
+	 * @throws \EE_Error
140
+	 */
141
+	public function set_redirection_info(
142
+		$payment,
143
+		$billing_info = array(),
144
+		$return_url = null,
145
+		$notify_url = null,
146
+		$cancel_url = null
147
+	) {
148
+		if (! $payment instanceof EEI_Payment) {
149
+			$payment->set_gateway_response(
150
+				esc_html__(
151
+					'Error. No associated payment was found.',
152
+					'event_espresso'
153
+				)
154
+			);
155
+			$payment->set_status($this->_pay_model->failed_status());
156
+			return $payment;
157
+		}
158
+		$transaction = $payment->transaction();
159
+		if (! $transaction instanceof EEI_Transaction) {
160
+			$payment->set_gateway_response(
161
+				esc_html__(
162
+					'Could not process this payment because it has no associated transaction.',
163
+					'event_espresso'
164
+				)
165
+			);
166
+			$payment->set_status($this->_pay_model->failed_status());
167
+			return $payment;
168
+		}
169
+		$order_description = mb_strcut($this->_format_order_description($payment), 0, 127);
170
+		$primary_registration = $transaction->primary_registration();
171
+		$primary_attendee = $primary_registration instanceof EE_Registration
172
+			? $primary_registration->attendee()
173
+			: false;
174
+		$locale = explode('-', get_bloginfo('language'));
175
+		// Gather request parameters.
176
+		$token_request_dtls = array(
177
+			'METHOD'                         => 'SetExpressCheckout',
178
+			'PAYMENTREQUEST_0_AMT'           => $payment->amount(),
179
+			'PAYMENTREQUEST_0_CURRENCYCODE'  => $payment->currency_code(),
180
+			'PAYMENTREQUEST_0_DESC'          => $order_description,
181
+			'RETURNURL'                      => $return_url,
182
+			'CANCELURL'                      => $cancel_url,
183
+			'PAYMENTREQUEST_0_PAYMENTACTION' => 'Sale',
184
+			// Buyer does not need to create a PayPal account to check out.
185
+			// This is referred to as PayPal Account Optional.
186
+			'SOLUTIONTYPE'                   => 'Sole',
187
+			//EE will blow up if you change this
188
+			'BUTTONSOURCE'                   => 'EventEspresso_SP',
189
+			// Locale of the pages displayed by PayPal during Express Checkout.
190
+			'LOCALECODE'                     => $locale[1]
191
+		);
192
+		// Show itemized list.
193
+		$itemized_list = $this->itemize_list($payment, $transaction);
194
+		$token_request_dtls = array_merge($token_request_dtls, $itemized_list);
195
+		// Automatically filling out shipping and contact information.
196
+		if ($this->_request_shipping_addr && $primary_attendee instanceof EEI_Attendee) {
197
+			// If you do not pass the shipping address, PayPal obtains it from the buyer's account profile.
198
+			$token_request_dtls['NOSHIPPING'] = '2';
199
+			$token_request_dtls['PAYMENTREQUEST_0_SHIPTOSTREET'] = $primary_attendee->address();
200
+			$token_request_dtls['PAYMENTREQUEST_0_SHIPTOSTREET2'] = $primary_attendee->address2();
201
+			$token_request_dtls['PAYMENTREQUEST_0_SHIPTOCITY'] = $primary_attendee->city();
202
+			$token_request_dtls['PAYMENTREQUEST_0_SHIPTOSTATE'] = $primary_attendee->state_abbrev();
203
+			$token_request_dtls['PAYMENTREQUEST_0_SHIPTOCOUNTRYCODE'] = $primary_attendee->country_ID();
204
+			$token_request_dtls['PAYMENTREQUEST_0_SHIPTOZIP'] = $primary_attendee->zip();
205
+			$token_request_dtls['PAYMENTREQUEST_0_EMAIL'] = $primary_attendee->email();
206
+			$token_request_dtls['PAYMENTREQUEST_0_SHIPTOPHONENUM'] = $primary_attendee->phone();
207
+		} elseif (! $this->_request_shipping_addr) {
208
+			// Do not request shipping details on the PP Checkout page.
209
+			$token_request_dtls['NOSHIPPING'] = '1';
210
+			$token_request_dtls['REQCONFIRMSHIPPING'] = '0';
211
+		}
212
+		// Used a business/personal logo on the PayPal page.
213
+		if (! empty($this->_image_url)) {
214
+			$token_request_dtls['LOGOIMG'] = $this->_image_url;
215
+		}
216
+		$token_request_dtls = apply_filters(
217
+			'FHEE__EEG_Paypal_Express__set_redirection_info__arguments',
218
+			$token_request_dtls,
219
+			$this
220
+		);
221
+		// Request PayPal token.
222
+		$token_request_response = $this->_ppExpress_request($token_request_dtls, 'Payment Token', $payment);
223
+		$token_rstatus = $this->_ppExpress_check_response($token_request_response);
224
+		$response_args = (isset($token_rstatus['args']) && is_array($token_rstatus['args']))
225
+			? $token_rstatus['args']
226
+			: array();
227
+		if ($token_rstatus['status']) {
228
+			// We got the Token so we may continue with the payment and redirect the client.
229
+			$payment->set_details($response_args);
230
+			$gateway_url = $this->_debug_mode ? 'https://www.sandbox.paypal.com' : 'https://www.paypal.com';
231
+			$payment->set_redirect_url(
232
+				$gateway_url
233
+				. '/checkoutnow?useraction=commit&cmd=_express-checkout&token='
234
+				. $response_args['TOKEN']
235
+			);
236
+		} else {
237
+			if (isset($response_args['L_ERRORCODE'])) {
238
+				$payment->set_gateway_response($response_args['L_ERRORCODE'] . '; ' . $response_args['L_SHORTMESSAGE']);
239
+			} else {
240
+				$payment->set_gateway_response(
241
+					esc_html__(
242
+						'Error occurred while trying to setup the Express Checkout.',
243
+						'event_espresso'
244
+					)
245
+				);
246
+			}
247
+			$payment->set_details($response_args);
248
+			$payment->set_status($this->_pay_model->failed_status());
249
+		}
250
+		return $payment;
251
+	}
252
+
253
+
254
+
255
+	/**
256
+	 * @param array           $update_info {
257
+	 * @type string           $gateway_txn_id
258
+	 * @type string status an EEMI_Payment status
259
+	 *                                     }
260
+	 * @param EEI_Transaction $transaction
261
+	 * @return EEI_Payment
262
+	 */
263
+	public function handle_payment_update($update_info, $transaction)
264
+	{
265
+		$payment = $transaction instanceof EEI_Transaction ? $transaction->last_payment() : null;
266
+		if ($payment instanceof EEI_Payment) {
267
+			$this->log(array('Return from Authorization' => $update_info), $payment);
268
+			$transaction = $payment->transaction();
269
+			if (! $transaction instanceof EEI_Transaction) {
270
+				$payment->set_gateway_response(
271
+					esc_html__(
272
+						'Could not process this payment because it has no associated transaction.',
273
+						'event_espresso'
274
+					)
275
+				);
276
+				$payment->set_status($this->_pay_model->failed_status());
277
+				return $payment;
278
+			}
279
+			$primary_registrant = $transaction->primary_registration();
280
+			$payment_details = $payment->details();
281
+			// Check if we still have the token.
282
+			if (! isset($payment_details['TOKEN']) || empty($payment_details['TOKEN'])) {
283
+				$payment->set_status($this->_pay_model->failed_status());
284
+				return $payment;
285
+			}
286
+			$cdetails_request_dtls = array(
287
+				'METHOD' => 'GetExpressCheckoutDetails',
288
+				'TOKEN'  => $payment_details['TOKEN'],
289
+			);
290
+			// Request Customer Details.
291
+			$cdetails_request_response = $this->_ppExpress_request(
292
+				$cdetails_request_dtls,
293
+				'Customer Details',
294
+				$payment
295
+			);
296
+			$cdetails_rstatus = $this->_ppExpress_check_response($cdetails_request_response);
297
+			$cdata_response_args = (isset($cdetails_rstatus['args']) && is_array($cdetails_rstatus['args']))
298
+				? $cdetails_rstatus['args']
299
+				: array();
300
+			if ($cdetails_rstatus['status']) {
301
+				// We got the PayerID so now we can Complete the transaction.
302
+				$docheckout_request_dtls = array(
303
+					'METHOD'                         => 'DoExpressCheckoutPayment',
304
+					'PAYERID'                        => $cdata_response_args['PAYERID'],
305
+					'TOKEN'                          => $payment_details['TOKEN'],
306
+					'PAYMENTREQUEST_0_PAYMENTACTION' => 'Sale',
307
+					'PAYMENTREQUEST_0_AMT'           => $payment->amount(),
308
+					'PAYMENTREQUEST_0_CURRENCYCODE'  => $payment->currency_code(),
309
+					//EE will blow up if you change this
310
+					'BUTTONSOURCE'                   => 'EventEspresso_SP',
311
+				);
312
+				 // Include itemized list.
313
+				$itemized_list = $this->itemize_list(
314
+					$payment,
315
+					$transaction,
316
+					$cdata_response_args
317
+				);
318
+				$docheckout_request_dtls = array_merge($docheckout_request_dtls, $itemized_list);
319
+				// Payment Checkout/Capture.
320
+				$docheckout_request_response = $this->_ppExpress_request(
321
+					$docheckout_request_dtls,
322
+					'Do Payment',
323
+					$payment
324
+				);
325
+				$docheckout_rstatus = $this->_ppExpress_check_response($docheckout_request_response);
326
+				$docheckout_response_args = (isset($docheckout_rstatus['args']) && is_array($docheckout_rstatus['args']))
327
+					? $docheckout_rstatus['args']
328
+					: array();
329
+				if ($docheckout_rstatus['status']) {
330
+					// All is well, payment approved.
331
+					$primary_registration_code = $primary_registrant instanceof EE_Registration ?
332
+						$primary_registrant->reg_code()
333
+						: '';
334
+					$payment->set_extra_accntng($primary_registration_code);
335
+					$payment->set_amount(isset($docheckout_response_args['PAYMENTINFO_0_AMT'])
336
+						? (float)$docheckout_response_args['PAYMENTINFO_0_AMT']
337
+						: 0);
338
+					$payment->set_txn_id_chq_nmbr(isset($docheckout_response_args['PAYMENTINFO_0_TRANSACTIONID'])
339
+						? $docheckout_response_args['PAYMENTINFO_0_TRANSACTIONID']
340
+						: null);
341
+					$payment->set_details($cdata_response_args);
342
+					$payment->set_gateway_response(isset($docheckout_response_args['PAYMENTINFO_0_ACK'])
343
+						? $docheckout_response_args['PAYMENTINFO_0_ACK']
344
+						: '');
345
+					$payment->set_status($this->_pay_model->approved_status());
346
+				} else {
347
+					if (isset($docheckout_response_args['L_ERRORCODE'])) {
348
+						$payment->set_gateway_response(
349
+							$docheckout_response_args['L_ERRORCODE']
350
+							. '; '
351
+							. $docheckout_response_args['L_SHORTMESSAGE']
352
+						);
353
+					} else {
354
+						$payment->set_gateway_response(
355
+							esc_html__(
356
+								'Error occurred while trying to Capture the funds.',
357
+								'event_espresso'
358
+							)
359
+						);
360
+					}
361
+					$payment->set_details($docheckout_response_args);
362
+					$payment->set_status($this->_pay_model->declined_status());
363
+				}
364
+			} else {
365
+				if (isset($cdata_response_args['L_ERRORCODE'])) {
366
+					$payment->set_gateway_response(
367
+						$cdata_response_args['L_ERRORCODE']
368
+						. '; '
369
+						. $cdata_response_args['L_SHORTMESSAGE']
370
+					);
371
+				} else {
372
+					$payment->set_gateway_response(
373
+						esc_html__(
374
+							'Error occurred while trying to get payment Details from PayPal.',
375
+							'event_espresso'
376
+						)
377
+					);
378
+				}
379
+				$payment->set_details($cdata_response_args);
380
+				$payment->set_status($this->_pay_model->failed_status());
381
+			}
382
+		} else {
383
+			$payment->set_gateway_response(
384
+				esc_html__(
385
+					'Error occurred while trying to process the payment.',
386
+					'event_espresso'
387
+				)
388
+			);
389
+			$payment->set_status($this->_pay_model->failed_status());
390
+		}
391
+		return $payment;
392
+	}
393
+
394
+
395
+
396
+	/**
397
+	 *  Make a list of items that are in the giver transaction.
398
+	 *
399
+	 * @param EEI_Payment     $payment
400
+	 * @param EEI_Transaction $transaction
401
+	 * @param array           $request_response_args Data from a previous communication with PP.
402
+	 * @return array
403
+	 */
404
+	public function itemize_list(EEI_Payment $payment, EEI_Transaction $transaction, $request_response_args = array())
405
+	{
406
+		$itemized_list = array();
407
+		// If we have data from a previous communication with PP (on this transaction) we may use that for our list...
408
+		if (
409
+			! empty($request_response_args)
410
+			&& array_key_exists('L_PAYMENTREQUEST_0_AMT0', $request_response_args)
411
+			&& array_key_exists('PAYMENTREQUEST_0_ITEMAMT', $request_response_args)
412
+		) {
413
+			foreach ($request_response_args as $arg_key => $arg_val) {
414
+				if (
415
+					strpos($arg_key, 'PAYMENTREQUEST_') !== false
416
+					&& strpos($arg_key, 'NOTIFYURL') === false
417
+				) {
418
+					$itemized_list[$arg_key] = $arg_val;
419
+				}
420
+			}
421
+			// If we got only a few Items then something is not right.
422
+			if (count($itemized_list) > 2) {
423
+				return $itemized_list;
424
+			} else {
425
+				if (WP_DEBUG) {
426
+					throw new EE_Error(
427
+						sprintf(
428
+							esc_html__(
429
+								// @codingStandardsIgnoreStart
430
+								'Unable to continue with the checkout because a proper purchase list could not be generated. The purchased list we could have sent was %1$s',
431
+								// @codingStandardsIgnoreEnd
432
+								'event_espresso'
433
+							),
434
+							wp_json_encode($itemized_list)
435
+						)
436
+					);
437
+				}
438
+				// Reset the list and log an error, maybe allow to try and generate a new list (below).
439
+				$itemized_list = array();
440
+				$this->log(
441
+					array(
442
+						esc_html__(
443
+							'Could not generate a proper item list with:',
444
+							'event_espresso'
445
+						) => $request_response_args
446
+					),
447
+					$payment
448
+				);
449
+			}
450
+		}
451
+		// ...otherwise we generate a new list for this transaction.
452
+		if ($this->_money->compare_floats($payment->amount(), $transaction->total(), '==')) {
453
+			$item_num = 0;
454
+			$itemized_sum = 0;
455
+			$total_line_items = $transaction->total_line_item();
456
+			// Go through each item in the list.
457
+			foreach ($total_line_items->get_items() as $line_item) {
458
+				if ($line_item instanceof EE_Line_Item) {
459
+					// PayPal doesn't like line items with 0.00 amount, so we may skip those.
460
+					if (EEH_Money::compare_floats($line_item->total(), '0.00', '==')) {
461
+						continue;
462
+					}
463
+					$unit_price = $line_item->unit_price();
464
+					$line_item_quantity = $line_item->quantity();
465
+					// This is a discount.
466
+					if ($line_item->is_percent()) {
467
+						$unit_price = $line_item->total();
468
+						$line_item_quantity = 1;
469
+					}
470
+					// Item Name.
471
+					$itemized_list['L_PAYMENTREQUEST_0_NAME' . $item_num] = mb_strcut(
472
+						$this->_format_line_item_name($line_item, $payment),
473
+						0,
474
+						127
475
+					);
476
+					// Item description.
477
+					$itemized_list['L_PAYMENTREQUEST_0_DESC' . $item_num] = mb_strcut(
478
+						$this->_format_line_item_desc($line_item, $payment),
479
+						0,
480
+						127
481
+					);
482
+					// Cost of individual item.
483
+					$itemized_list['L_PAYMENTREQUEST_0_AMT' . $item_num] = $this->format_currency($unit_price);
484
+					// Item Number.
485
+					$itemized_list['L_PAYMENTREQUEST_0_NUMBER' . $item_num] = $item_num + 1;
486
+					// Item quantity.
487
+					$itemized_list['L_PAYMENTREQUEST_0_QTY' . $item_num] = $line_item_quantity;
488
+					// Digital item is sold.
489
+					$itemized_list['L_PAYMENTREQUEST_0_ITEMCATEGORY' . $item_num] = 'Physical';
490
+					$itemized_sum += $line_item->total();
491
+					++$item_num;
492
+				}
493
+			}
494
+			// Item's sales S/H and tax amount.
495
+			$itemized_list['PAYMENTREQUEST_0_ITEMAMT'] = $total_line_items->get_items_total();
496
+			$itemized_list['PAYMENTREQUEST_0_TAXAMT'] = $total_line_items->get_total_tax();
497
+			$itemized_list['PAYMENTREQUEST_0_SHIPPINGAMT'] = '0';
498
+			$itemized_list['PAYMENTREQUEST_0_HANDLINGAMT'] = '0';
499
+			$itemized_sum_diff_from_txn_total = round(
500
+				$transaction->total() - $itemized_sum - $total_line_items->get_total_tax(),
501
+				2
502
+			);
503
+			// If we were not able to recognize some item like promotion, surcharge or cancellation,
504
+			// add the difference as an extra line item.
505
+			if ($this->_money->compare_floats($itemized_sum_diff_from_txn_total, 0, '!=')) {
506
+				// Item Name.
507
+				$itemized_list['L_PAYMENTREQUEST_0_NAME' . $item_num] = mb_strcut(
508
+					esc_html__(
509
+						'Other (promotion/surcharge/cancellation)',
510
+						'event_espresso'
511
+					),
512
+					0,
513
+					127
514
+				);
515
+				// Item description.
516
+				$itemized_list['L_PAYMENTREQUEST_0_DESC' . $item_num] = '';
517
+				// Cost of individual item.
518
+				$itemized_list['L_PAYMENTREQUEST_0_AMT' . $item_num] = $this->format_currency(
519
+					$itemized_sum_diff_from_txn_total
520
+				);
521
+				// Item Number.
522
+				$itemized_list['L_PAYMENTREQUEST_0_NUMBER' . $item_num] = $item_num + 1;
523
+				// Item quantity.
524
+				$itemized_list['L_PAYMENTREQUEST_0_QTY' . $item_num] = 1;
525
+				// Digital item is sold.
526
+				$itemized_list['L_PAYMENTREQUEST_0_ITEMCATEGORY' . $item_num] = 'Physical';
527
+				$item_num++;
528
+			}
529
+		} else {
530
+			// Just one Item.
531
+			// Item Name.
532
+			$itemized_list['L_PAYMENTREQUEST_0_NAME0'] = mb_strcut(
533
+				$this->_format_partial_payment_line_item_name($payment),
534
+				0,
535
+				127
536
+			);
537
+			// Item description.
538
+			$itemized_list['L_PAYMENTREQUEST_0_DESC0'] = mb_strcut(
539
+				$this->_format_partial_payment_line_item_desc($payment),
540
+				0,
541
+				127
542
+			);
543
+			// Cost of individual item.
544
+			$itemized_list['L_PAYMENTREQUEST_0_AMT0'] = $this->format_currency($payment->amount());
545
+			// Item Number.
546
+			$itemized_list['L_PAYMENTREQUEST_0_NUMBER0'] = 1;
547
+			// Item quantity.
548
+			$itemized_list['L_PAYMENTREQUEST_0_QTY0'] = 1;
549
+			// Digital item is sold.
550
+			$itemized_list['L_PAYMENTREQUEST_0_ITEMCATEGORY0'] = 'Physical';
551
+			// Item's sales S/H and tax amount.
552
+			$itemized_list['PAYMENTREQUEST_0_ITEMAMT'] = $this->format_currency($payment->amount());
553
+			$itemized_list['PAYMENTREQUEST_0_TAXAMT'] = '0';
554
+			$itemized_list['PAYMENTREQUEST_0_SHIPPINGAMT'] = '0';
555
+			$itemized_list['PAYMENTREQUEST_0_HANDLINGAMT'] = '0';
556
+		}
557
+		return $itemized_list;
558
+	}
559
+
560
+
561
+
562
+	/**
563
+	 *  Make the Express checkout request.
564
+	 *
565
+	 * @param array       $request_params
566
+	 * @param string      $request_text
567
+	 * @param EEI_Payment $payment
568
+	 * @return mixed
569
+	 */
570
+	public function _ppExpress_request($request_params, $request_text, $payment)
571
+	{
572
+		$request_dtls = array(
573
+			'VERSION'   => '204.0',
574
+			'USER'      => urlencode($this->_api_username),
575
+			'PWD'       => urlencode($this->_api_password),
576
+			'SIGNATURE' => urlencode($this->_api_signature),
577
+		);
578
+		$dtls = array_merge($request_dtls, $request_params);
579
+		$this->_log_clean_request($dtls, $payment, $request_text . ' Request');
580
+		// Request Customer Details.
581
+		$request_response = wp_remote_post(
582
+			$this->_base_gateway_url,
583
+			array(
584
+				'method'      => 'POST',
585
+				'timeout'     => 45,
586
+				'httpversion' => '1.1',
587
+				'cookies'     => array(),
588
+				'headers'     => array(),
589
+				'body'        => http_build_query($dtls),
590
+			)
591
+		);
592
+		// Log the response.
593
+		$this->log(array($request_text . ' Response' => $request_response), $payment);
594
+		return $request_response;
595
+	}
596
+
597
+
598
+
599
+	/**
600
+	 *  Check the response status.
601
+	 *
602
+	 * @param mixed $request_response
603
+	 * @return array
604
+	 */
605
+	public function _ppExpress_check_response($request_response)
606
+	{
607
+		if (is_wp_error($request_response) || empty($request_response['body'])) {
608
+			// If we got here then there was an error in this request.
609
+			return array('status' => false, 'args' => $request_response);
610
+		}
611
+		$response_args = array();
612
+		parse_str(urldecode($request_response['body']), $response_args);
613
+		if (! isset($response_args['ACK'])) {
614
+			return array('status' => false, 'args' => $request_response);
615
+		}
616
+		if (
617
+			(
618
+				isset($response_args['PAYERID'])
619
+				|| isset($response_args['TOKEN'])
620
+				|| isset($response_args['PAYMENTINFO_0_TRANSACTIONID'])
621
+				|| (isset($response_args['PAYMENTSTATUS']) && $response_args['PAYMENTSTATUS'] === 'Completed')
622
+			)
623
+			&& in_array($response_args['ACK'], array('Success', 'SuccessWithWarning'), true)
624
+		) {
625
+			// Response status OK, return response parameters for further processing.
626
+			return array('status' => true, 'args' => $response_args);
627
+		}
628
+		$errors = $this->_get_errors($response_args);
629
+		return array('status' => false, 'args' => $errors);
630
+	}
631
+
632
+
633
+
634
+	/**
635
+	 *  Log a "Cleared" request.
636
+	 *
637
+	 * @param array       $request
638
+	 * @param EEI_Payment $payment
639
+	 * @param string      $info
640
+	 * @return void
641
+	 */
642
+	private function _log_clean_request($request, $payment, $info)
643
+	{
644
+		$cleaned_request_data = $request;
645
+		unset($cleaned_request_data['PWD'], $cleaned_request_data['USER'], $cleaned_request_data['SIGNATURE']);
646
+		$this->log(array($info => $cleaned_request_data), $payment);
647
+	}
648
+
649
+
650
+
651
+	/**
652
+	 *  Get error from the response data.
653
+	 *
654
+	 * @param array $data_array
655
+	 * @return array
656
+	 */
657
+	private function _get_errors($data_array)
658
+	{
659
+		$errors = array();
660
+		$n = 0;
661
+		while (isset($data_array["L_ERRORCODE{$n}"])) {
662
+			$l_error_code = isset($data_array["L_ERRORCODE{$n}"])
663
+				? $data_array["L_ERRORCODE{$n}"]
664
+				: '';
665
+			$l_severity_code = isset($data_array["L_SEVERITYCODE{$n}"])
666
+				? $data_array["L_SEVERITYCODE{$n}"]
667
+				: '';
668
+			$l_short_message = isset($data_array["L_SHORTMESSAGE{$n}"])
669
+				? $data_array["L_SHORTMESSAGE{$n}"]
670
+				: '';
671
+			$l_long_message = isset($data_array["L_LONGMESSAGE{$n}"])
672
+				? $data_array["L_LONGMESSAGE{$n}"]
673
+				: '';
674
+			if ($n === 0) {
675
+				$errors = array(
676
+					'L_ERRORCODE'    => $l_error_code,
677
+					'L_SHORTMESSAGE' => $l_short_message,
678
+					'L_LONGMESSAGE'  => $l_long_message,
679
+					'L_SEVERITYCODE' => $l_severity_code,
680
+				);
681
+			} else {
682
+				$errors['L_ERRORCODE'] .= ', ' . $l_error_code;
683
+				$errors['L_SHORTMESSAGE'] .= ', ' . $l_short_message;
684
+				$errors['L_LONGMESSAGE'] .= ', ' . $l_long_message;
685
+				$errors['L_SEVERITYCODE'] .= ', ' . $l_severity_code;
686
+			}
687
+			$n++;
688
+		}
689
+		return $errors;
690
+	}
691 691
 
692 692
 }
693 693
 // End of file EEG_Paypal_Express.gateway.php
Please login to merge, or discard this patch.
Spacing   +29 added lines, -29 removed lines patch added patch discarded remove patch
@@ -1,4 +1,4 @@  discard block
 block discarded – undo
1
-<?php if (! defined('EVENT_ESPRESSO_VERSION')) {
1
+<?php if ( ! defined('EVENT_ESPRESSO_VERSION')) {
2 2
     exit('NO direct script access allowed');
3 3
 }
4 4
 
@@ -15,7 +15,7 @@  discard block
 block discarded – undo
15 15
  * ----------------------------------------------
16 16
  */
17 17
 //Quickfix to address https://events.codebasehq.com/projects/event-espresso/tickets/11089 ASAP
18
-if (! function_exists('mb_strcut')) {
18
+if ( ! function_exists('mb_strcut')) {
19 19
     /**
20 20
      * Very simple mimic of mb_substr (which WP ensures exists in wp-includes/compat.php). Still has all the problems of mb_substr
21 21
      * (namely, that we might send too many characters to PayPal; however in this case they just issue a warning but nothing breaks)
@@ -145,7 +145,7 @@  discard block
 block discarded – undo
145 145
         $notify_url = null,
146 146
         $cancel_url = null
147 147
     ) {
148
-        if (! $payment instanceof EEI_Payment) {
148
+        if ( ! $payment instanceof EEI_Payment) {
149 149
             $payment->set_gateway_response(
150 150
                 esc_html__(
151 151
                     'Error. No associated payment was found.',
@@ -156,7 +156,7 @@  discard block
 block discarded – undo
156 156
             return $payment;
157 157
         }
158 158
         $transaction = $payment->transaction();
159
-        if (! $transaction instanceof EEI_Transaction) {
159
+        if ( ! $transaction instanceof EEI_Transaction) {
160 160
             $payment->set_gateway_response(
161 161
                 esc_html__(
162 162
                     'Could not process this payment because it has no associated transaction.',
@@ -204,13 +204,13 @@  discard block
 block discarded – undo
204 204
             $token_request_dtls['PAYMENTREQUEST_0_SHIPTOZIP'] = $primary_attendee->zip();
205 205
             $token_request_dtls['PAYMENTREQUEST_0_EMAIL'] = $primary_attendee->email();
206 206
             $token_request_dtls['PAYMENTREQUEST_0_SHIPTOPHONENUM'] = $primary_attendee->phone();
207
-        } elseif (! $this->_request_shipping_addr) {
207
+        } elseif ( ! $this->_request_shipping_addr) {
208 208
             // Do not request shipping details on the PP Checkout page.
209 209
             $token_request_dtls['NOSHIPPING'] = '1';
210 210
             $token_request_dtls['REQCONFIRMSHIPPING'] = '0';
211 211
         }
212 212
         // Used a business/personal logo on the PayPal page.
213
-        if (! empty($this->_image_url)) {
213
+        if ( ! empty($this->_image_url)) {
214 214
             $token_request_dtls['LOGOIMG'] = $this->_image_url;
215 215
         }
216 216
         $token_request_dtls = apply_filters(
@@ -235,7 +235,7 @@  discard block
 block discarded – undo
235 235
             );
236 236
         } else {
237 237
             if (isset($response_args['L_ERRORCODE'])) {
238
-                $payment->set_gateway_response($response_args['L_ERRORCODE'] . '; ' . $response_args['L_SHORTMESSAGE']);
238
+                $payment->set_gateway_response($response_args['L_ERRORCODE'].'; '.$response_args['L_SHORTMESSAGE']);
239 239
             } else {
240 240
                 $payment->set_gateway_response(
241 241
                     esc_html__(
@@ -266,7 +266,7 @@  discard block
 block discarded – undo
266 266
         if ($payment instanceof EEI_Payment) {
267 267
             $this->log(array('Return from Authorization' => $update_info), $payment);
268 268
             $transaction = $payment->transaction();
269
-            if (! $transaction instanceof EEI_Transaction) {
269
+            if ( ! $transaction instanceof EEI_Transaction) {
270 270
                 $payment->set_gateway_response(
271 271
                     esc_html__(
272 272
                         'Could not process this payment because it has no associated transaction.',
@@ -279,7 +279,7 @@  discard block
 block discarded – undo
279 279
             $primary_registrant = $transaction->primary_registration();
280 280
             $payment_details = $payment->details();
281 281
             // Check if we still have the token.
282
-            if (! isset($payment_details['TOKEN']) || empty($payment_details['TOKEN'])) {
282
+            if ( ! isset($payment_details['TOKEN']) || empty($payment_details['TOKEN'])) {
283 283
                 $payment->set_status($this->_pay_model->failed_status());
284 284
                 return $payment;
285 285
             }
@@ -333,7 +333,7 @@  discard block
 block discarded – undo
333 333
                         : '';
334 334
                     $payment->set_extra_accntng($primary_registration_code);
335 335
                     $payment->set_amount(isset($docheckout_response_args['PAYMENTINFO_0_AMT'])
336
-                        ? (float)$docheckout_response_args['PAYMENTINFO_0_AMT']
336
+                        ? (float) $docheckout_response_args['PAYMENTINFO_0_AMT']
337 337
                         : 0);
338 338
                     $payment->set_txn_id_chq_nmbr(isset($docheckout_response_args['PAYMENTINFO_0_TRANSACTIONID'])
339 339
                         ? $docheckout_response_args['PAYMENTINFO_0_TRANSACTIONID']
@@ -468,25 +468,25 @@  discard block
 block discarded – undo
468 468
                         $line_item_quantity = 1;
469 469
                     }
470 470
                     // Item Name.
471
-                    $itemized_list['L_PAYMENTREQUEST_0_NAME' . $item_num] = mb_strcut(
471
+                    $itemized_list['L_PAYMENTREQUEST_0_NAME'.$item_num] = mb_strcut(
472 472
                         $this->_format_line_item_name($line_item, $payment),
473 473
                         0,
474 474
                         127
475 475
                     );
476 476
                     // Item description.
477
-                    $itemized_list['L_PAYMENTREQUEST_0_DESC' . $item_num] = mb_strcut(
477
+                    $itemized_list['L_PAYMENTREQUEST_0_DESC'.$item_num] = mb_strcut(
478 478
                         $this->_format_line_item_desc($line_item, $payment),
479 479
                         0,
480 480
                         127
481 481
                     );
482 482
                     // Cost of individual item.
483
-                    $itemized_list['L_PAYMENTREQUEST_0_AMT' . $item_num] = $this->format_currency($unit_price);
483
+                    $itemized_list['L_PAYMENTREQUEST_0_AMT'.$item_num] = $this->format_currency($unit_price);
484 484
                     // Item Number.
485
-                    $itemized_list['L_PAYMENTREQUEST_0_NUMBER' . $item_num] = $item_num + 1;
485
+                    $itemized_list['L_PAYMENTREQUEST_0_NUMBER'.$item_num] = $item_num + 1;
486 486
                     // Item quantity.
487
-                    $itemized_list['L_PAYMENTREQUEST_0_QTY' . $item_num] = $line_item_quantity;
487
+                    $itemized_list['L_PAYMENTREQUEST_0_QTY'.$item_num] = $line_item_quantity;
488 488
                     // Digital item is sold.
489
-                    $itemized_list['L_PAYMENTREQUEST_0_ITEMCATEGORY' . $item_num] = 'Physical';
489
+                    $itemized_list['L_PAYMENTREQUEST_0_ITEMCATEGORY'.$item_num] = 'Physical';
490 490
                     $itemized_sum += $line_item->total();
491 491
                     ++$item_num;
492 492
                 }
@@ -504,7 +504,7 @@  discard block
 block discarded – undo
504 504
             // add the difference as an extra line item.
505 505
             if ($this->_money->compare_floats($itemized_sum_diff_from_txn_total, 0, '!=')) {
506 506
                 // Item Name.
507
-                $itemized_list['L_PAYMENTREQUEST_0_NAME' . $item_num] = mb_strcut(
507
+                $itemized_list['L_PAYMENTREQUEST_0_NAME'.$item_num] = mb_strcut(
508 508
                     esc_html__(
509 509
                         'Other (promotion/surcharge/cancellation)',
510 510
                         'event_espresso'
@@ -513,17 +513,17 @@  discard block
 block discarded – undo
513 513
                     127
514 514
                 );
515 515
                 // Item description.
516
-                $itemized_list['L_PAYMENTREQUEST_0_DESC' . $item_num] = '';
516
+                $itemized_list['L_PAYMENTREQUEST_0_DESC'.$item_num] = '';
517 517
                 // Cost of individual item.
518
-                $itemized_list['L_PAYMENTREQUEST_0_AMT' . $item_num] = $this->format_currency(
518
+                $itemized_list['L_PAYMENTREQUEST_0_AMT'.$item_num] = $this->format_currency(
519 519
                     $itemized_sum_diff_from_txn_total
520 520
                 );
521 521
                 // Item Number.
522
-                $itemized_list['L_PAYMENTREQUEST_0_NUMBER' . $item_num] = $item_num + 1;
522
+                $itemized_list['L_PAYMENTREQUEST_0_NUMBER'.$item_num] = $item_num + 1;
523 523
                 // Item quantity.
524
-                $itemized_list['L_PAYMENTREQUEST_0_QTY' . $item_num] = 1;
524
+                $itemized_list['L_PAYMENTREQUEST_0_QTY'.$item_num] = 1;
525 525
                 // Digital item is sold.
526
-                $itemized_list['L_PAYMENTREQUEST_0_ITEMCATEGORY' . $item_num] = 'Physical';
526
+                $itemized_list['L_PAYMENTREQUEST_0_ITEMCATEGORY'.$item_num] = 'Physical';
527 527
                 $item_num++;
528 528
             }
529 529
         } else {
@@ -576,7 +576,7 @@  discard block
 block discarded – undo
576 576
             'SIGNATURE' => urlencode($this->_api_signature),
577 577
         );
578 578
         $dtls = array_merge($request_dtls, $request_params);
579
-        $this->_log_clean_request($dtls, $payment, $request_text . ' Request');
579
+        $this->_log_clean_request($dtls, $payment, $request_text.' Request');
580 580
         // Request Customer Details.
581 581
         $request_response = wp_remote_post(
582 582
             $this->_base_gateway_url,
@@ -590,7 +590,7 @@  discard block
 block discarded – undo
590 590
             )
591 591
         );
592 592
         // Log the response.
593
-        $this->log(array($request_text . ' Response' => $request_response), $payment);
593
+        $this->log(array($request_text.' Response' => $request_response), $payment);
594 594
         return $request_response;
595 595
     }
596 596
 
@@ -610,7 +610,7 @@  discard block
 block discarded – undo
610 610
         }
611 611
         $response_args = array();
612 612
         parse_str(urldecode($request_response['body']), $response_args);
613
-        if (! isset($response_args['ACK'])) {
613
+        if ( ! isset($response_args['ACK'])) {
614 614
             return array('status' => false, 'args' => $request_response);
615 615
         }
616 616
         if (
@@ -679,10 +679,10 @@  discard block
 block discarded – undo
679 679
                     'L_SEVERITYCODE' => $l_severity_code,
680 680
                 );
681 681
             } else {
682
-                $errors['L_ERRORCODE'] .= ', ' . $l_error_code;
683
-                $errors['L_SHORTMESSAGE'] .= ', ' . $l_short_message;
684
-                $errors['L_LONGMESSAGE'] .= ', ' . $l_long_message;
685
-                $errors['L_SEVERITYCODE'] .= ', ' . $l_severity_code;
682
+                $errors['L_ERRORCODE'] .= ', '.$l_error_code;
683
+                $errors['L_SHORTMESSAGE'] .= ', '.$l_short_message;
684
+                $errors['L_LONGMESSAGE'] .= ', '.$l_long_message;
685
+                $errors['L_SEVERITYCODE'] .= ', '.$l_severity_code;
686 686
             }
687 687
             $n++;
688 688
         }
Please login to merge, or discard this patch.
core/domain/services/event/EventSpacesCalculator.php 2 patches
Indentation   +660 added lines, -660 removed lines patch added patch discarded remove patch
@@ -31,666 +31,666 @@
 block discarded – undo
31 31
 class EventSpacesCalculator
32 32
 {
33 33
 
34
-    /**
35
-     * @var EE_Event $event
36
-     */
37
-    private $event;
38
-
39
-    /**
40
-     * @var array $datetime_query_params
41
-     */
42
-    private $datetime_query_params;
43
-
44
-    /**
45
-     * @var EE_Ticket[] $active_tickets
46
-     */
47
-    private $active_tickets = array();
48
-
49
-    /**
50
-     * @var EE_Datetime[] $datetimes
51
-     */
52
-    private $datetimes = array();
53
-
54
-    /**
55
-     * Array of Ticket IDs grouped by Datetime
56
-     *
57
-     * @var array $datetimes
58
-     */
59
-    private $datetime_tickets = array();
60
-
61
-    /**
62
-     * Max spaces for each Datetime (reg limit - previous sold)
63
-     *
64
-     * @var array $datetime_spaces
65
-     */
66
-    private $datetime_spaces = array();
67
-
68
-    /**
69
-     * Array of Datetime IDs grouped by Ticket
70
-     *
71
-     * @var array[] $ticket_datetimes
72
-     */
73
-    private $ticket_datetimes = array();
74
-
75
-    /**
76
-     * maximum ticket quantities for each ticket (adjusted for reg limit)
77
-     *
78
-     * @var array $ticket_quantities
79
-     */
80
-    private $ticket_quantities = array();
81
-
82
-    /**
83
-     * total quantity of sold and reserved for each ticket
84
-     *
85
-     * @var array $tickets_sold
86
-     */
87
-    private $tickets_sold = array();
88
-
89
-    /**
90
-     * total spaces available across all datetimes
91
-     *
92
-     * @var array $total_spaces
93
-     */
94
-    private $total_spaces = array();
95
-
96
-    /**
97
-     * @var boolean $debug
98
-     */
99
-    private $debug = false;
100
-
101
-    /**
102
-     * @var null|int $spaces_remaining
103
-     */
104
-    private $spaces_remaining;
105
-
106
-    /**
107
-     * @var null|int $total_spaces_available
108
-     */
109
-    private $total_spaces_available;
110
-
111
-
112
-
113
-    /**
114
-     * EventSpacesCalculator constructor.
115
-     *
116
-     * @param EE_Event $event
117
-     * @param array    $datetime_query_params
118
-     * @throws EE_Error
119
-     */
120
-    public function __construct(EE_Event $event, array $datetime_query_params = array())
121
-    {
122
-        $this->event                 = $event;
123
-        $this->datetime_query_params = $datetime_query_params + array('order_by' => array('DTT_reg_limit' => 'ASC'));
124
-        $this->setHooks();
125
-    }
126
-
127
-
128
-
129
-    /**
130
-     * @return void
131
-     */
132
-    private function setHooks()
133
-    {
134
-        add_action( 'AHEE__EE_Ticket__increase_sold', array($this, 'clearResults'));
135
-        add_action( 'AHEE__EE_Ticket__decrease_sold', array($this, 'clearResults'));
136
-        add_action( 'AHEE__EE_Datetime__increase_sold', array($this, 'clearResults'));
137
-        add_action( 'AHEE__EE_Datetime__decrease_sold', array($this, 'clearResults'));
138
-        add_action( 'AHEE__EE_Ticket__increase_reserved', array($this, 'clearResults'));
139
-        add_action( 'AHEE__EE_Ticket__decrease_reserved', array($this, 'clearResults'));
140
-        add_action( 'AHEE__EE_Datetime__increase_reserved', array($this, 'clearResults'));
141
-        add_action( 'AHEE__EE_Datetime__decrease_reserved', array($this, 'clearResults'));
142
-    }
143
-
144
-
145
-
146
-    /**
147
-     * @return void
148
-     */
149
-    public function clearResults()
150
-    {
151
-        $this->spaces_remaining = null;
152
-        $this->total_spaces_available = null;
153
-    }
154
-
155
-
156
-
157
-    /**
158
-     * @return EE_Ticket[]
159
-     * @throws EE_Error
160
-     * @throws InvalidDataTypeException
161
-     * @throws InvalidInterfaceException
162
-     * @throws InvalidArgumentException
163
-     */
164
-    public function getActiveTickets()
165
-    {
166
-        if (empty($this->active_tickets)) {
167
-            $this->active_tickets = $this->event->tickets(
168
-                array(
169
-                    array(
170
-                        'TKT_end_date' => array('>=', EEM_Ticket::instance()->current_time_for_query('TKT_end_date')),
171
-                        'TKT_deleted'  => false,
172
-                    ),
173
-                    'order_by' => array('TKT_qty' => 'ASC'),
174
-                )
175
-            );
176
-        }
177
-        return $this->active_tickets;
178
-    }
179
-
180
-
181
-
182
-    /**
183
-     * @param EE_Ticket[] $active_tickets
184
-     * @throws EE_Error
185
-     * @throws DomainException
186
-     * @throws UnexpectedEntityException
187
-     */
188
-    public function setActiveTickets(array $active_tickets = array())
189
-    {
190
-        if ( ! empty($active_tickets)) {
191
-            foreach ($active_tickets as $active_ticket) {
192
-                $this->validateTicket($active_ticket);
193
-            }
194
-            // sort incoming array by ticket quantity (asc)
195
-            usort(
196
-                $active_tickets,
197
-                function (EE_Ticket $a, EE_Ticket $b) {
198
-                    if ($a->qty() === $b->qty()) {
199
-                        return 0;
200
-                    }
201
-                    return ($a->qty() < $b->qty())
202
-                        ? -1
203
-                        : 1;
204
-                }
205
-            );
206
-        }
207
-        $this->active_tickets = $active_tickets;
208
-    }
209
-
210
-
211
-
212
-    /**
213
-     * @param $ticket
214
-     * @throws DomainException
215
-     * @throws EE_Error
216
-     * @throws UnexpectedEntityException
217
-     */
218
-    private function validateTicket($ticket)
219
-    {
220
-        if ( ! $ticket instanceof EE_Ticket) {
221
-            throw new DomainException(
222
-                esc_html__(
223
-                    'Invalid Ticket. Only EE_Ticket objects can be used to calculate event space availability.',
224
-                    'event_espresso'
225
-                )
226
-            );
227
-        }
228
-        if ($ticket->get_event_ID() !== $this->event->ID()) {
229
-            throw new DomainException(
230
-                sprintf(
231
-                    esc_html__(
232
-                        'An EE_Ticket for Event %1$d was supplied while calculating event space availability for Event %2$d.',
233
-                        'event_espresso'
234
-                    ),
235
-                    $ticket->get_event_ID(),
236
-                    $this->event->ID()
237
-                )
238
-            );
239
-        }
240
-    }
241
-
242
-
243
-
244
-    /**
245
-     * @return EE_Datetime[]
246
-     */
247
-    public function getDatetimes()
248
-    {
249
-        return $this->datetimes;
250
-    }
251
-
252
-
253
-
254
-    /**
255
-     * @param EE_Datetime $datetime
256
-     * @throws EE_Error
257
-     * @throws DomainException
258
-     */
259
-    public function setDatetime(EE_Datetime $datetime)
260
-    {
261
-        if ($datetime->event()->ID() !== $this->event->ID()) {
262
-            throw new DomainException(
263
-                sprintf(
264
-                    esc_html__(
265
-                        'An EE_Datetime for Event %1$d was supplied while calculating event space availability for Event %2$d.',
266
-                        'event_espresso'
267
-                    ),
268
-                    $datetime->event()->ID(),
269
-                    $this->event->ID()
270
-                )
271
-            );
272
-        }
273
-        $this->datetimes[ $datetime->ID() ] = $datetime;
274
-    }
275
-
276
-
277
-
278
-    /**
279
-     * calculate spaces remaining based on "saleable" tickets
280
-     *
281
-     * @return float|int
282
-     * @throws EE_Error
283
-     * @throws DomainException
284
-     * @throws UnexpectedEntityException
285
-     * @throws InvalidDataTypeException
286
-     * @throws InvalidInterfaceException
287
-     * @throws InvalidArgumentException
288
-     */
289
-    public function spacesRemaining()
290
-    {
291
-        if ($this->spaces_remaining === null) {
292
-            $this->initialize();
293
-            $this->spaces_remaining = $this->calculate();
294
-        }
295
-        return $this->spaces_remaining;
296
-    }
297
-
298
-
299
-
300
-    /**
301
-     * calculates total available spaces for an event with no regard for sold tickets
302
-     *
303
-     * @return int|float
304
-     * @throws EE_Error
305
-     * @throws DomainException
306
-     * @throws UnexpectedEntityException
307
-     * @throws InvalidDataTypeException
308
-     * @throws InvalidInterfaceException
309
-     * @throws InvalidArgumentException
310
-     */
311
-    public function totalSpacesAvailable()
312
-    {
313
-        if($this->total_spaces_available === null) {
314
-            $this->initialize();
315
-            $this->total_spaces_available = $this->calculate(false);
316
-        }
317
-        return $this->total_spaces_available;
318
-    }
319
-
320
-
321
-
322
-    /**
323
-     * Loops through the active tickets for the event
324
-     * and builds a series of data arrays that will be used for calculating
325
-     * the total maximum available spaces, as well as the spaces remaining.
326
-     * Because ticket quantities affect datetime spaces and vice versa,
327
-     * we need to be constantly updating these data arrays as things change,
328
-     * which is the entire reason for their existence.
329
-     *
330
-     * @throws EE_Error
331
-     * @throws DomainException
332
-     * @throws UnexpectedEntityException
333
-     * @throws InvalidDataTypeException
334
-     * @throws InvalidInterfaceException
335
-     * @throws InvalidArgumentException
336
-     */
337
-    private function initialize()
338
-    {
339
-        if ($this->debug) {
340
-            \EEH_Debug_Tools::printr(__FUNCTION__, __CLASS__, __FILE__, __LINE__, 2);
341
-        }
342
-        $this->datetime_tickets  = array();
343
-        $this->datetime_spaces   = array();
344
-        $this->ticket_datetimes  = array();
345
-        $this->ticket_quantities = array();
346
-        $this->tickets_sold      = array();
347
-        $this->total_spaces      = array();
348
-        $active_tickets          = $this->getActiveTickets();
349
-        if ( ! empty($active_tickets)) {
350
-            foreach ($active_tickets as $ticket) {
351
-                $this->validateTicket($ticket);
352
-                // we need to index our data arrays using strings for the purpose of sorting,
353
-                // but we also need them to be unique, so  we'll just prepend a letter T to the ID
354
-                $ticket_identifier = "T{$ticket->ID()}";
355
-                // to start, we'll just consider the raw qty to be the maximum availability for this ticket
356
-                $max_tickets = $ticket->qty();
357
-                // but we'll adjust that after looping over each datetime for the ticket and checking reg limits
358
-                $ticket_datetimes = $ticket->datetimes($this->datetime_query_params);
359
-                foreach ($ticket_datetimes as $datetime) {
360
-                    // save all datetimes
361
-                    $this->setDatetime($datetime);
362
-                    $datetime_identifier = "D{$datetime->ID()}";
363
-                    $reg_limit           = $datetime->reg_limit();
364
-                    // ticket quantity can not exceed datetime reg limit
365
-                    $max_tickets = min($max_tickets, $reg_limit);
366
-                    // as described earlier, because we need to be able to constantly adjust numbers for things,
367
-                    // we are going to move all of our data into the following arrays:
368
-                    // datetime spaces initially represents the reg limit for each datetime,
369
-                    // but this will get adjusted as tickets are accounted for
370
-                    $this->datetime_spaces[ $datetime_identifier ] = $reg_limit;
371
-                    // just an array of ticket IDs grouped by datetime
372
-                    $this->datetime_tickets[ $datetime_identifier ][] = $ticket_identifier;
373
-                    // and an array of datetime IDs grouped by ticket
374
-                    $this->ticket_datetimes[ $ticket_identifier ][] = $datetime_identifier;
375
-                }
376
-                // total quantity of sold and reserved for each ticket
377
-                $this->tickets_sold[ $ticket_identifier ] = $ticket->sold() + $ticket->reserved();
378
-                // and the maximum ticket quantities for each ticket (adjusted for reg limit)
379
-                $this->ticket_quantities[ $ticket_identifier ] = $max_tickets;
380
-            }
381
-        }
382
-        // sort datetime spaces by reg limit, but maintain our string indexes
383
-        asort($this->datetime_spaces, SORT_NUMERIC);
384
-        // datetime tickets need to be sorted in the SAME order as the above array...
385
-        // so we'll just use array_merge() to take the structure of datetime_spaces
386
-        // but overwrite all of the data with that from datetime_tickets
387
-        $this->datetime_tickets = array_merge(
388
-            $this->datetime_spaces,
389
-            $this->datetime_tickets
390
-        );
391
-        if ($this->debug) {
392
-            \EEH_Debug_Tools::printr($this->datetime_spaces, 'datetime_spaces', __FILE__, __LINE__);
393
-            \EEH_Debug_Tools::printr($this->datetime_tickets, 'datetime_tickets', __FILE__, __LINE__);
394
-            \EEH_Debug_Tools::printr($this->ticket_quantities, 'ticket_quantities', __FILE__, __LINE__);
395
-        }
396
-    }
397
-
398
-
399
-
400
-    /**
401
-     * performs calculations on initialized data
402
-     *
403
-     * @param bool $consider_sold
404
-     * @return int|float
405
-     */
406
-    private function calculate($consider_sold = true)
407
-    {
408
-        if ($this->debug) {
409
-            \EEH_Debug_Tools::printr(__FUNCTION__, __CLASS__, __FILE__, __LINE__, 2);
410
-        }
411
-        if ($consider_sold) {
412
-            // subtract amounts sold from all ticket quantities and datetime spaces
413
-            $this->adjustTicketQuantitiesDueToSales();
414
-        }
415
-        foreach ($this->datetime_tickets as $datetime_identifier => $tickets) {
416
-            $this->trackAvailableSpacesForDatetimes($datetime_identifier, $tickets);
417
-        }
418
-        // total spaces available is just the sum of the spaces available for each datetime
419
-        $spaces_remaining = array_sum($this->total_spaces);
420
-        if ($this->debug) {
421
-            \EEH_Debug_Tools::printr($this->total_spaces, '$this->total_spaces', __FILE__, __LINE__);
422
-            \EEH_Debug_Tools::printr($this->tickets_sold, '$this->tickets_sold', __FILE__, __LINE__);
423
-            \EEH_Debug_Tools::printr($spaces_remaining, '$spaces_remaining', __FILE__, __LINE__);
424
-        }
425
-        return $spaces_remaining;
426
-    }
427
-
428
-
429
-    /**
430
-     * subtracts amount of  tickets sold from ticket quantities and datetime spaces
431
-     */
432
-    private function adjustTicketQuantitiesDueToSales()
433
-    {
434
-        if ($this->debug) {
435
-            \EEH_Debug_Tools::printr(__FUNCTION__, __CLASS__, __FILE__, __LINE__, 2);
436
-        }
437
-        foreach ($this->tickets_sold as $ticket_identifier => $tickets_sold) {
438
-            if (isset($this->ticket_quantities[ $ticket_identifier ])){
439
-                $this->ticket_quantities[ $ticket_identifier ] -= $tickets_sold;
440
-                if ($this->debug) {
441
-                    \EEH_Debug_Tools::printr("{$tickets_sold} sales for ticket {$ticket_identifier} ", 'subtracting', __FILE__, __LINE__);
442
-                }
443
-            }
444
-            if (
445
-                isset($this->ticket_datetimes[ $ticket_identifier ])
446
-                && is_array($this->ticket_datetimes[ $ticket_identifier ])
447
-            ){
448
-                foreach ($this->ticket_datetimes[ $ticket_identifier ] as $ticket_datetime) {
449
-                    if (isset($this->ticket_quantities[ $ticket_identifier ])) {
450
-                        $this->datetime_spaces[ $ticket_datetime ] -= $tickets_sold;
451
-                        if ($this->debug) {
452
-                            \EEH_Debug_Tools::printr("{$tickets_sold} sales for datetime {$ticket_datetime} ",
453
-                                'subtracting', __FILE__, __LINE__);
454
-                        }
455
-                    }
456
-                }
457
-            }
458
-        }
459
-    }
460
-
461
-
462
-
463
-    /**
464
-     * @param string $datetime_identifier
465
-     * @param array  $tickets
466
-     */
467
-    private function trackAvailableSpacesForDatetimes($datetime_identifier, array $tickets)
468
-    {
469
-        // make sure a reg limit is set for the datetime
470
-        $reg_limit = isset($this->datetime_spaces[ $datetime_identifier ])
471
-            ? $this->datetime_spaces[ $datetime_identifier ]
472
-            : 0;
473
-        // and bail if it is not
474
-        if ( ! $reg_limit) {
475
-            if ($this->debug) {
476
-                \EEH_Debug_Tools::printr('AT CAPACITY', " . {$datetime_identifier}", __FILE__, __LINE__);
477
-            }
478
-            return;
479
-        }
480
-        if ($this->debug) {
481
-            \EEH_Debug_Tools::printr($datetime_identifier, '* $datetime_identifier', __FILE__, __LINE__, 1);
482
-            \EEH_Debug_Tools::printr("{$reg_limit}", 'REG LIMIT', __FILE__, __LINE__);
483
-        }
484
-        // number of allocated spaces always starts at zero
485
-        $spaces_allocated                           = 0;
486
-        $this->total_spaces[ $datetime_identifier ] = 0;
487
-        foreach ($tickets as $ticket_identifier) {
488
-            $spaces_allocated = $this->calculateAvailableSpacesForTicket(
489
-                $datetime_identifier,
490
-                $reg_limit,
491
-                $ticket_identifier,
492
-                $spaces_allocated
493
-            );
494
-        }
495
-        // spaces can't be negative
496
-        $spaces_allocated = max($spaces_allocated, 0);
497
-        if ($spaces_allocated) {
498
-            // track any non-zero values
499
-            $this->total_spaces[ $datetime_identifier ] += $spaces_allocated;
500
-            if ($this->debug) {
501
-                \EEH_Debug_Tools::printr((string)$spaces_allocated, ' . $spaces_allocated: ', __FILE__, __LINE__);
502
-            }
503
-        } else {
504
-            if ($this->debug) {
505
-                \EEH_Debug_Tools::printr(' ', ' . NO TICKETS AVAILABLE FOR DATETIME', __FILE__, __LINE__);
506
-            }
507
-        }
508
-        if ($this->debug) {
509
-            \EEH_Debug_Tools::printr($this->total_spaces[ $datetime_identifier ], '$total_spaces', __FILE__,
510
-                __LINE__);
511
-            \EEH_Debug_Tools::printr($this->ticket_quantities, '$ticket_quantities', __FILE__, __LINE__);
512
-            \EEH_Debug_Tools::printr($this->datetime_spaces, 'datetime_spaces', __FILE__, __LINE__);
513
-        }
514
-    }
515
-
516
-
517
-
518
-    /**
519
-     * @param string $datetime_identifier
520
-     * @param int    $reg_limit
521
-     * @param string $ticket_identifier
522
-     * @param int    $spaces_allocated
523
-     * @return int
524
-     */
525
-    private function calculateAvailableSpacesForTicket(
526
-        $datetime_identifier,
527
-        $reg_limit,
528
-        $ticket_identifier,
529
-        $spaces_allocated
530
-    ) {
531
-        // make sure ticket quantity is set
532
-        $ticket_quantity = isset($this->ticket_quantities[ $ticket_identifier ])
533
-            ? $this->ticket_quantities[ $ticket_identifier ]
534
-            : 0;
535
-        if ($this->debug) {
536
-            \EEH_Debug_Tools::printr("{$spaces_allocated}", '$spaces_allocated', __FILE__, __LINE__);
537
-            \EEH_Debug_Tools::printr("{$ticket_quantity}", "ticket $ticket_identifier quantity: ",
538
-                __FILE__, __LINE__, 2);
539
-        }
540
-        if ($ticket_quantity) {
541
-            if ($this->debug) {
542
-                \EEH_Debug_Tools::printr(
543
-                    ($spaces_allocated <= $reg_limit)
544
-                        ? 'true'
545
-                        : 'false',
546
-                    ' . spaces_allocated <= reg_limit = ',
547
-                    __FILE__, __LINE__
548
-                );
549
-            }
550
-            // if the datetime is NOT at full capacity yet
551
-            if ($spaces_allocated <= $reg_limit) {
552
-                // then the maximum ticket quantity we can allocate is the lowest value of either:
553
-                //  the number of remaining spaces for the datetime, which is the limit - spaces already taken
554
-                //  or the maximum ticket quantity
555
-                $ticket_quantity = min($reg_limit - $spaces_allocated, $ticket_quantity);
556
-                // adjust the available quantity in our tracking array
557
-                $this->ticket_quantities[ $ticket_identifier ] -= $ticket_quantity;
558
-                // and increment spaces allocated for this datetime
559
-                $spaces_allocated += $ticket_quantity;
560
-                $at_capacity = $spaces_allocated >= $reg_limit;
561
-                if ($this->debug) {
562
-                    \EEH_Debug_Tools::printr("{$ticket_quantity} {$ticket_identifier} tickets", ' > > allocate ',
563
-                        __FILE__, __LINE__,   3);
564
-                    if ($at_capacity) {
565
-                        \EEH_Debug_Tools::printr('AT CAPACITY', " . {$datetime_identifier}", __FILE__, __LINE__, 3);
566
-                    }
567
-                }
568
-                // now adjust all other datetimes that allow access to this ticket
569
-                $this->adjustDatetimes(
570
-                    $datetime_identifier,
571
-                    $ticket_identifier,
572
-                    $ticket_quantity,
573
-                    $at_capacity
574
-                );
575
-            }
576
-        }
577
-        return $spaces_allocated;
578
-    }
579
-
580
-
581
-
582
-    /**
583
-     * subtracts ticket amounts from all datetime reg limits
584
-     * that allow access to the ticket specified,
585
-     * because that ticket could be used
586
-     * to attend any of the datetimes it has access to
587
-     *
588
-     * @param string $datetime_identifier
589
-     * @param string $ticket_identifier
590
-     * @param bool   $at_capacity
591
-     * @param int    $ticket_quantity
592
-     */
593
-    private function adjustDatetimes(
594
-        $datetime_identifier,
595
-        $ticket_identifier,
596
-        $ticket_quantity,
597
-        $at_capacity
598
-    ) {
599
-        /** @var array $datetime_tickets */
600
-        foreach ($this->datetime_tickets as $datetime_ID => $datetime_tickets) {
601
-            if ($datetime_ID !== $datetime_identifier || ! is_array($datetime_tickets)) {
602
-                continue;
603
-            }
604
-            $adjusted = $this->adjustDatetimeSpaces(
605
-                $datetime_ID,
606
-                $ticket_identifier,
607
-                $ticket_quantity
608
-            );
609
-            // skip to next ticket if nothing changed
610
-            if (! ($adjusted || $at_capacity)) {
611
-                continue;
612
-            }
613
-            // then all of it's tickets are now unavailable
614
-            foreach ($datetime_tickets as $datetime_ticket) {
615
-                if (
616
-                    ($ticket_identifier === $datetime_ticket || $at_capacity)
617
-                    && isset($this->ticket_quantities[ $datetime_ticket ])
618
-                    && $this->ticket_quantities[ $datetime_ticket ] > 0
619
-                ) {
620
-                    if ($this->debug) {
621
-                        \EEH_Debug_Tools::printr($datetime_ticket, ' . . . adjust ticket quantities for', __FILE__,
622
-                            __LINE__);
623
-                    }
624
-                    // if this datetime is at full capacity, set any tracked available quantities to zero
625
-                    // otherwise just subtract the ticket quantity
626
-                    $new_quantity = $at_capacity
627
-                        ? 0
628
-                        : $this->ticket_quantities[ $datetime_ticket ] - $ticket_quantity;
629
-                    // don't let ticket quantity go below zero
630
-                    $this->ticket_quantities[ $datetime_ticket ] = max($new_quantity, 0);
631
-                    if ($this->debug) {
632
-                        \EEH_Debug_Tools::printr(
633
-                            $at_capacity
634
-                                ? "0 because Datetime {$datetime_identifier} is at capacity"
635
-                                : "{$this->ticket_quantities[ $datetime_ticket ]}",
636
-                            " . . . . {$datetime_ticket} quantity set to ",
637
-                            __FILE__, __LINE__
638
-                        );
639
-                    }
640
-                }
641
-                // but we also need to adjust spaces for any other datetimes this ticket has access to
642
-                if ($datetime_ticket === $ticket_identifier) {
643
-                    if (isset($this->ticket_datetimes[ $datetime_ticket ])
644
-                        && is_array($this->ticket_datetimes[ $datetime_ticket ])
645
-                    ) {
646
-                        if ($this->debug) {
647
-                            \EEH_Debug_Tools::printr($datetime_ticket, ' . . adjust other Datetimes for', __FILE__,
648
-                                __LINE__);
649
-                        }
650
-                        foreach ($this->ticket_datetimes[ $datetime_ticket ] as $datetime) {
651
-                            // don't adjust the current datetime twice
652
-                            if ($datetime !== $datetime_identifier) {
653
-                                $this->adjustDatetimeSpaces(
654
-                                    $datetime,
655
-                                    $datetime_ticket,
656
-                                    $ticket_quantity
657
-                                );
658
-                            }
659
-                        }
660
-                    }
661
-                }
662
-            }
663
-        }
664
-    }
665
-
666
-    private function adjustDatetimeSpaces($datetime_identifier, $ticket_identifier, $ticket_quantity = 0)
667
-    {
668
-        // does datetime have spaces available?
669
-        // and does the supplied ticket have access to this datetime ?
670
-        if (
671
-            $this->datetime_spaces[ $datetime_identifier ] > 0
672
-            && isset($this->datetime_spaces[ $datetime_identifier ], $this->datetime_tickets[ $datetime_identifier ])
673
-            && in_array($ticket_identifier, $this->datetime_tickets[ $datetime_identifier ], true)
674
-            ) {
675
-            if ($this->debug) {
676
-                \EEH_Debug_Tools::printr($datetime_identifier, ' . . adjust Datetime Spaces for', __FILE__, __LINE__);
677
-                \EEH_Debug_Tools::printr("{$this->datetime_spaces[ $datetime_identifier ]}", " . . current  {$datetime_identifier} spaces available", __FILE__, __LINE__);
678
-            }
679
-            // then decrement the available spaces for the datetime
680
-            $this->datetime_spaces[ $datetime_identifier ] -= $ticket_quantity;
681
-            // but don't let quantities go below zero
682
-            $this->datetime_spaces[ $datetime_identifier ] = max(
683
-                $this->datetime_spaces[ $datetime_identifier ],
684
-                0
685
-            );
686
-            if ($this->debug) {
687
-                \EEH_Debug_Tools::printr("{$ticket_quantity}",
688
-                    " . . . {$datetime_identifier} capacity reduced by", __FILE__, __LINE__);
689
-            }
690
-            return true;
691
-        }
692
-        return false;
693
-    }
34
+	/**
35
+	 * @var EE_Event $event
36
+	 */
37
+	private $event;
38
+
39
+	/**
40
+	 * @var array $datetime_query_params
41
+	 */
42
+	private $datetime_query_params;
43
+
44
+	/**
45
+	 * @var EE_Ticket[] $active_tickets
46
+	 */
47
+	private $active_tickets = array();
48
+
49
+	/**
50
+	 * @var EE_Datetime[] $datetimes
51
+	 */
52
+	private $datetimes = array();
53
+
54
+	/**
55
+	 * Array of Ticket IDs grouped by Datetime
56
+	 *
57
+	 * @var array $datetimes
58
+	 */
59
+	private $datetime_tickets = array();
60
+
61
+	/**
62
+	 * Max spaces for each Datetime (reg limit - previous sold)
63
+	 *
64
+	 * @var array $datetime_spaces
65
+	 */
66
+	private $datetime_spaces = array();
67
+
68
+	/**
69
+	 * Array of Datetime IDs grouped by Ticket
70
+	 *
71
+	 * @var array[] $ticket_datetimes
72
+	 */
73
+	private $ticket_datetimes = array();
74
+
75
+	/**
76
+	 * maximum ticket quantities for each ticket (adjusted for reg limit)
77
+	 *
78
+	 * @var array $ticket_quantities
79
+	 */
80
+	private $ticket_quantities = array();
81
+
82
+	/**
83
+	 * total quantity of sold and reserved for each ticket
84
+	 *
85
+	 * @var array $tickets_sold
86
+	 */
87
+	private $tickets_sold = array();
88
+
89
+	/**
90
+	 * total spaces available across all datetimes
91
+	 *
92
+	 * @var array $total_spaces
93
+	 */
94
+	private $total_spaces = array();
95
+
96
+	/**
97
+	 * @var boolean $debug
98
+	 */
99
+	private $debug = false;
100
+
101
+	/**
102
+	 * @var null|int $spaces_remaining
103
+	 */
104
+	private $spaces_remaining;
105
+
106
+	/**
107
+	 * @var null|int $total_spaces_available
108
+	 */
109
+	private $total_spaces_available;
110
+
111
+
112
+
113
+	/**
114
+	 * EventSpacesCalculator constructor.
115
+	 *
116
+	 * @param EE_Event $event
117
+	 * @param array    $datetime_query_params
118
+	 * @throws EE_Error
119
+	 */
120
+	public function __construct(EE_Event $event, array $datetime_query_params = array())
121
+	{
122
+		$this->event                 = $event;
123
+		$this->datetime_query_params = $datetime_query_params + array('order_by' => array('DTT_reg_limit' => 'ASC'));
124
+		$this->setHooks();
125
+	}
126
+
127
+
128
+
129
+	/**
130
+	 * @return void
131
+	 */
132
+	private function setHooks()
133
+	{
134
+		add_action( 'AHEE__EE_Ticket__increase_sold', array($this, 'clearResults'));
135
+		add_action( 'AHEE__EE_Ticket__decrease_sold', array($this, 'clearResults'));
136
+		add_action( 'AHEE__EE_Datetime__increase_sold', array($this, 'clearResults'));
137
+		add_action( 'AHEE__EE_Datetime__decrease_sold', array($this, 'clearResults'));
138
+		add_action( 'AHEE__EE_Ticket__increase_reserved', array($this, 'clearResults'));
139
+		add_action( 'AHEE__EE_Ticket__decrease_reserved', array($this, 'clearResults'));
140
+		add_action( 'AHEE__EE_Datetime__increase_reserved', array($this, 'clearResults'));
141
+		add_action( 'AHEE__EE_Datetime__decrease_reserved', array($this, 'clearResults'));
142
+	}
143
+
144
+
145
+
146
+	/**
147
+	 * @return void
148
+	 */
149
+	public function clearResults()
150
+	{
151
+		$this->spaces_remaining = null;
152
+		$this->total_spaces_available = null;
153
+	}
154
+
155
+
156
+
157
+	/**
158
+	 * @return EE_Ticket[]
159
+	 * @throws EE_Error
160
+	 * @throws InvalidDataTypeException
161
+	 * @throws InvalidInterfaceException
162
+	 * @throws InvalidArgumentException
163
+	 */
164
+	public function getActiveTickets()
165
+	{
166
+		if (empty($this->active_tickets)) {
167
+			$this->active_tickets = $this->event->tickets(
168
+				array(
169
+					array(
170
+						'TKT_end_date' => array('>=', EEM_Ticket::instance()->current_time_for_query('TKT_end_date')),
171
+						'TKT_deleted'  => false,
172
+					),
173
+					'order_by' => array('TKT_qty' => 'ASC'),
174
+				)
175
+			);
176
+		}
177
+		return $this->active_tickets;
178
+	}
179
+
180
+
181
+
182
+	/**
183
+	 * @param EE_Ticket[] $active_tickets
184
+	 * @throws EE_Error
185
+	 * @throws DomainException
186
+	 * @throws UnexpectedEntityException
187
+	 */
188
+	public function setActiveTickets(array $active_tickets = array())
189
+	{
190
+		if ( ! empty($active_tickets)) {
191
+			foreach ($active_tickets as $active_ticket) {
192
+				$this->validateTicket($active_ticket);
193
+			}
194
+			// sort incoming array by ticket quantity (asc)
195
+			usort(
196
+				$active_tickets,
197
+				function (EE_Ticket $a, EE_Ticket $b) {
198
+					if ($a->qty() === $b->qty()) {
199
+						return 0;
200
+					}
201
+					return ($a->qty() < $b->qty())
202
+						? -1
203
+						: 1;
204
+				}
205
+			);
206
+		}
207
+		$this->active_tickets = $active_tickets;
208
+	}
209
+
210
+
211
+
212
+	/**
213
+	 * @param $ticket
214
+	 * @throws DomainException
215
+	 * @throws EE_Error
216
+	 * @throws UnexpectedEntityException
217
+	 */
218
+	private function validateTicket($ticket)
219
+	{
220
+		if ( ! $ticket instanceof EE_Ticket) {
221
+			throw new DomainException(
222
+				esc_html__(
223
+					'Invalid Ticket. Only EE_Ticket objects can be used to calculate event space availability.',
224
+					'event_espresso'
225
+				)
226
+			);
227
+		}
228
+		if ($ticket->get_event_ID() !== $this->event->ID()) {
229
+			throw new DomainException(
230
+				sprintf(
231
+					esc_html__(
232
+						'An EE_Ticket for Event %1$d was supplied while calculating event space availability for Event %2$d.',
233
+						'event_espresso'
234
+					),
235
+					$ticket->get_event_ID(),
236
+					$this->event->ID()
237
+				)
238
+			);
239
+		}
240
+	}
241
+
242
+
243
+
244
+	/**
245
+	 * @return EE_Datetime[]
246
+	 */
247
+	public function getDatetimes()
248
+	{
249
+		return $this->datetimes;
250
+	}
251
+
252
+
253
+
254
+	/**
255
+	 * @param EE_Datetime $datetime
256
+	 * @throws EE_Error
257
+	 * @throws DomainException
258
+	 */
259
+	public function setDatetime(EE_Datetime $datetime)
260
+	{
261
+		if ($datetime->event()->ID() !== $this->event->ID()) {
262
+			throw new DomainException(
263
+				sprintf(
264
+					esc_html__(
265
+						'An EE_Datetime for Event %1$d was supplied while calculating event space availability for Event %2$d.',
266
+						'event_espresso'
267
+					),
268
+					$datetime->event()->ID(),
269
+					$this->event->ID()
270
+				)
271
+			);
272
+		}
273
+		$this->datetimes[ $datetime->ID() ] = $datetime;
274
+	}
275
+
276
+
277
+
278
+	/**
279
+	 * calculate spaces remaining based on "saleable" tickets
280
+	 *
281
+	 * @return float|int
282
+	 * @throws EE_Error
283
+	 * @throws DomainException
284
+	 * @throws UnexpectedEntityException
285
+	 * @throws InvalidDataTypeException
286
+	 * @throws InvalidInterfaceException
287
+	 * @throws InvalidArgumentException
288
+	 */
289
+	public function spacesRemaining()
290
+	{
291
+		if ($this->spaces_remaining === null) {
292
+			$this->initialize();
293
+			$this->spaces_remaining = $this->calculate();
294
+		}
295
+		return $this->spaces_remaining;
296
+	}
297
+
298
+
299
+
300
+	/**
301
+	 * calculates total available spaces for an event with no regard for sold tickets
302
+	 *
303
+	 * @return int|float
304
+	 * @throws EE_Error
305
+	 * @throws DomainException
306
+	 * @throws UnexpectedEntityException
307
+	 * @throws InvalidDataTypeException
308
+	 * @throws InvalidInterfaceException
309
+	 * @throws InvalidArgumentException
310
+	 */
311
+	public function totalSpacesAvailable()
312
+	{
313
+		if($this->total_spaces_available === null) {
314
+			$this->initialize();
315
+			$this->total_spaces_available = $this->calculate(false);
316
+		}
317
+		return $this->total_spaces_available;
318
+	}
319
+
320
+
321
+
322
+	/**
323
+	 * Loops through the active tickets for the event
324
+	 * and builds a series of data arrays that will be used for calculating
325
+	 * the total maximum available spaces, as well as the spaces remaining.
326
+	 * Because ticket quantities affect datetime spaces and vice versa,
327
+	 * we need to be constantly updating these data arrays as things change,
328
+	 * which is the entire reason for their existence.
329
+	 *
330
+	 * @throws EE_Error
331
+	 * @throws DomainException
332
+	 * @throws UnexpectedEntityException
333
+	 * @throws InvalidDataTypeException
334
+	 * @throws InvalidInterfaceException
335
+	 * @throws InvalidArgumentException
336
+	 */
337
+	private function initialize()
338
+	{
339
+		if ($this->debug) {
340
+			\EEH_Debug_Tools::printr(__FUNCTION__, __CLASS__, __FILE__, __LINE__, 2);
341
+		}
342
+		$this->datetime_tickets  = array();
343
+		$this->datetime_spaces   = array();
344
+		$this->ticket_datetimes  = array();
345
+		$this->ticket_quantities = array();
346
+		$this->tickets_sold      = array();
347
+		$this->total_spaces      = array();
348
+		$active_tickets          = $this->getActiveTickets();
349
+		if ( ! empty($active_tickets)) {
350
+			foreach ($active_tickets as $ticket) {
351
+				$this->validateTicket($ticket);
352
+				// we need to index our data arrays using strings for the purpose of sorting,
353
+				// but we also need them to be unique, so  we'll just prepend a letter T to the ID
354
+				$ticket_identifier = "T{$ticket->ID()}";
355
+				// to start, we'll just consider the raw qty to be the maximum availability for this ticket
356
+				$max_tickets = $ticket->qty();
357
+				// but we'll adjust that after looping over each datetime for the ticket and checking reg limits
358
+				$ticket_datetimes = $ticket->datetimes($this->datetime_query_params);
359
+				foreach ($ticket_datetimes as $datetime) {
360
+					// save all datetimes
361
+					$this->setDatetime($datetime);
362
+					$datetime_identifier = "D{$datetime->ID()}";
363
+					$reg_limit           = $datetime->reg_limit();
364
+					// ticket quantity can not exceed datetime reg limit
365
+					$max_tickets = min($max_tickets, $reg_limit);
366
+					// as described earlier, because we need to be able to constantly adjust numbers for things,
367
+					// we are going to move all of our data into the following arrays:
368
+					// datetime spaces initially represents the reg limit for each datetime,
369
+					// but this will get adjusted as tickets are accounted for
370
+					$this->datetime_spaces[ $datetime_identifier ] = $reg_limit;
371
+					// just an array of ticket IDs grouped by datetime
372
+					$this->datetime_tickets[ $datetime_identifier ][] = $ticket_identifier;
373
+					// and an array of datetime IDs grouped by ticket
374
+					$this->ticket_datetimes[ $ticket_identifier ][] = $datetime_identifier;
375
+				}
376
+				// total quantity of sold and reserved for each ticket
377
+				$this->tickets_sold[ $ticket_identifier ] = $ticket->sold() + $ticket->reserved();
378
+				// and the maximum ticket quantities for each ticket (adjusted for reg limit)
379
+				$this->ticket_quantities[ $ticket_identifier ] = $max_tickets;
380
+			}
381
+		}
382
+		// sort datetime spaces by reg limit, but maintain our string indexes
383
+		asort($this->datetime_spaces, SORT_NUMERIC);
384
+		// datetime tickets need to be sorted in the SAME order as the above array...
385
+		// so we'll just use array_merge() to take the structure of datetime_spaces
386
+		// but overwrite all of the data with that from datetime_tickets
387
+		$this->datetime_tickets = array_merge(
388
+			$this->datetime_spaces,
389
+			$this->datetime_tickets
390
+		);
391
+		if ($this->debug) {
392
+			\EEH_Debug_Tools::printr($this->datetime_spaces, 'datetime_spaces', __FILE__, __LINE__);
393
+			\EEH_Debug_Tools::printr($this->datetime_tickets, 'datetime_tickets', __FILE__, __LINE__);
394
+			\EEH_Debug_Tools::printr($this->ticket_quantities, 'ticket_quantities', __FILE__, __LINE__);
395
+		}
396
+	}
397
+
398
+
399
+
400
+	/**
401
+	 * performs calculations on initialized data
402
+	 *
403
+	 * @param bool $consider_sold
404
+	 * @return int|float
405
+	 */
406
+	private function calculate($consider_sold = true)
407
+	{
408
+		if ($this->debug) {
409
+			\EEH_Debug_Tools::printr(__FUNCTION__, __CLASS__, __FILE__, __LINE__, 2);
410
+		}
411
+		if ($consider_sold) {
412
+			// subtract amounts sold from all ticket quantities and datetime spaces
413
+			$this->adjustTicketQuantitiesDueToSales();
414
+		}
415
+		foreach ($this->datetime_tickets as $datetime_identifier => $tickets) {
416
+			$this->trackAvailableSpacesForDatetimes($datetime_identifier, $tickets);
417
+		}
418
+		// total spaces available is just the sum of the spaces available for each datetime
419
+		$spaces_remaining = array_sum($this->total_spaces);
420
+		if ($this->debug) {
421
+			\EEH_Debug_Tools::printr($this->total_spaces, '$this->total_spaces', __FILE__, __LINE__);
422
+			\EEH_Debug_Tools::printr($this->tickets_sold, '$this->tickets_sold', __FILE__, __LINE__);
423
+			\EEH_Debug_Tools::printr($spaces_remaining, '$spaces_remaining', __FILE__, __LINE__);
424
+		}
425
+		return $spaces_remaining;
426
+	}
427
+
428
+
429
+	/**
430
+	 * subtracts amount of  tickets sold from ticket quantities and datetime spaces
431
+	 */
432
+	private function adjustTicketQuantitiesDueToSales()
433
+	{
434
+		if ($this->debug) {
435
+			\EEH_Debug_Tools::printr(__FUNCTION__, __CLASS__, __FILE__, __LINE__, 2);
436
+		}
437
+		foreach ($this->tickets_sold as $ticket_identifier => $tickets_sold) {
438
+			if (isset($this->ticket_quantities[ $ticket_identifier ])){
439
+				$this->ticket_quantities[ $ticket_identifier ] -= $tickets_sold;
440
+				if ($this->debug) {
441
+					\EEH_Debug_Tools::printr("{$tickets_sold} sales for ticket {$ticket_identifier} ", 'subtracting', __FILE__, __LINE__);
442
+				}
443
+			}
444
+			if (
445
+				isset($this->ticket_datetimes[ $ticket_identifier ])
446
+				&& is_array($this->ticket_datetimes[ $ticket_identifier ])
447
+			){
448
+				foreach ($this->ticket_datetimes[ $ticket_identifier ] as $ticket_datetime) {
449
+					if (isset($this->ticket_quantities[ $ticket_identifier ])) {
450
+						$this->datetime_spaces[ $ticket_datetime ] -= $tickets_sold;
451
+						if ($this->debug) {
452
+							\EEH_Debug_Tools::printr("{$tickets_sold} sales for datetime {$ticket_datetime} ",
453
+								'subtracting', __FILE__, __LINE__);
454
+						}
455
+					}
456
+				}
457
+			}
458
+		}
459
+	}
460
+
461
+
462
+
463
+	/**
464
+	 * @param string $datetime_identifier
465
+	 * @param array  $tickets
466
+	 */
467
+	private function trackAvailableSpacesForDatetimes($datetime_identifier, array $tickets)
468
+	{
469
+		// make sure a reg limit is set for the datetime
470
+		$reg_limit = isset($this->datetime_spaces[ $datetime_identifier ])
471
+			? $this->datetime_spaces[ $datetime_identifier ]
472
+			: 0;
473
+		// and bail if it is not
474
+		if ( ! $reg_limit) {
475
+			if ($this->debug) {
476
+				\EEH_Debug_Tools::printr('AT CAPACITY', " . {$datetime_identifier}", __FILE__, __LINE__);
477
+			}
478
+			return;
479
+		}
480
+		if ($this->debug) {
481
+			\EEH_Debug_Tools::printr($datetime_identifier, '* $datetime_identifier', __FILE__, __LINE__, 1);
482
+			\EEH_Debug_Tools::printr("{$reg_limit}", 'REG LIMIT', __FILE__, __LINE__);
483
+		}
484
+		// number of allocated spaces always starts at zero
485
+		$spaces_allocated                           = 0;
486
+		$this->total_spaces[ $datetime_identifier ] = 0;
487
+		foreach ($tickets as $ticket_identifier) {
488
+			$spaces_allocated = $this->calculateAvailableSpacesForTicket(
489
+				$datetime_identifier,
490
+				$reg_limit,
491
+				$ticket_identifier,
492
+				$spaces_allocated
493
+			);
494
+		}
495
+		// spaces can't be negative
496
+		$spaces_allocated = max($spaces_allocated, 0);
497
+		if ($spaces_allocated) {
498
+			// track any non-zero values
499
+			$this->total_spaces[ $datetime_identifier ] += $spaces_allocated;
500
+			if ($this->debug) {
501
+				\EEH_Debug_Tools::printr((string)$spaces_allocated, ' . $spaces_allocated: ', __FILE__, __LINE__);
502
+			}
503
+		} else {
504
+			if ($this->debug) {
505
+				\EEH_Debug_Tools::printr(' ', ' . NO TICKETS AVAILABLE FOR DATETIME', __FILE__, __LINE__);
506
+			}
507
+		}
508
+		if ($this->debug) {
509
+			\EEH_Debug_Tools::printr($this->total_spaces[ $datetime_identifier ], '$total_spaces', __FILE__,
510
+				__LINE__);
511
+			\EEH_Debug_Tools::printr($this->ticket_quantities, '$ticket_quantities', __FILE__, __LINE__);
512
+			\EEH_Debug_Tools::printr($this->datetime_spaces, 'datetime_spaces', __FILE__, __LINE__);
513
+		}
514
+	}
515
+
516
+
517
+
518
+	/**
519
+	 * @param string $datetime_identifier
520
+	 * @param int    $reg_limit
521
+	 * @param string $ticket_identifier
522
+	 * @param int    $spaces_allocated
523
+	 * @return int
524
+	 */
525
+	private function calculateAvailableSpacesForTicket(
526
+		$datetime_identifier,
527
+		$reg_limit,
528
+		$ticket_identifier,
529
+		$spaces_allocated
530
+	) {
531
+		// make sure ticket quantity is set
532
+		$ticket_quantity = isset($this->ticket_quantities[ $ticket_identifier ])
533
+			? $this->ticket_quantities[ $ticket_identifier ]
534
+			: 0;
535
+		if ($this->debug) {
536
+			\EEH_Debug_Tools::printr("{$spaces_allocated}", '$spaces_allocated', __FILE__, __LINE__);
537
+			\EEH_Debug_Tools::printr("{$ticket_quantity}", "ticket $ticket_identifier quantity: ",
538
+				__FILE__, __LINE__, 2);
539
+		}
540
+		if ($ticket_quantity) {
541
+			if ($this->debug) {
542
+				\EEH_Debug_Tools::printr(
543
+					($spaces_allocated <= $reg_limit)
544
+						? 'true'
545
+						: 'false',
546
+					' . spaces_allocated <= reg_limit = ',
547
+					__FILE__, __LINE__
548
+				);
549
+			}
550
+			// if the datetime is NOT at full capacity yet
551
+			if ($spaces_allocated <= $reg_limit) {
552
+				// then the maximum ticket quantity we can allocate is the lowest value of either:
553
+				//  the number of remaining spaces for the datetime, which is the limit - spaces already taken
554
+				//  or the maximum ticket quantity
555
+				$ticket_quantity = min($reg_limit - $spaces_allocated, $ticket_quantity);
556
+				// adjust the available quantity in our tracking array
557
+				$this->ticket_quantities[ $ticket_identifier ] -= $ticket_quantity;
558
+				// and increment spaces allocated for this datetime
559
+				$spaces_allocated += $ticket_quantity;
560
+				$at_capacity = $spaces_allocated >= $reg_limit;
561
+				if ($this->debug) {
562
+					\EEH_Debug_Tools::printr("{$ticket_quantity} {$ticket_identifier} tickets", ' > > allocate ',
563
+						__FILE__, __LINE__,   3);
564
+					if ($at_capacity) {
565
+						\EEH_Debug_Tools::printr('AT CAPACITY', " . {$datetime_identifier}", __FILE__, __LINE__, 3);
566
+					}
567
+				}
568
+				// now adjust all other datetimes that allow access to this ticket
569
+				$this->adjustDatetimes(
570
+					$datetime_identifier,
571
+					$ticket_identifier,
572
+					$ticket_quantity,
573
+					$at_capacity
574
+				);
575
+			}
576
+		}
577
+		return $spaces_allocated;
578
+	}
579
+
580
+
581
+
582
+	/**
583
+	 * subtracts ticket amounts from all datetime reg limits
584
+	 * that allow access to the ticket specified,
585
+	 * because that ticket could be used
586
+	 * to attend any of the datetimes it has access to
587
+	 *
588
+	 * @param string $datetime_identifier
589
+	 * @param string $ticket_identifier
590
+	 * @param bool   $at_capacity
591
+	 * @param int    $ticket_quantity
592
+	 */
593
+	private function adjustDatetimes(
594
+		$datetime_identifier,
595
+		$ticket_identifier,
596
+		$ticket_quantity,
597
+		$at_capacity
598
+	) {
599
+		/** @var array $datetime_tickets */
600
+		foreach ($this->datetime_tickets as $datetime_ID => $datetime_tickets) {
601
+			if ($datetime_ID !== $datetime_identifier || ! is_array($datetime_tickets)) {
602
+				continue;
603
+			}
604
+			$adjusted = $this->adjustDatetimeSpaces(
605
+				$datetime_ID,
606
+				$ticket_identifier,
607
+				$ticket_quantity
608
+			);
609
+			// skip to next ticket if nothing changed
610
+			if (! ($adjusted || $at_capacity)) {
611
+				continue;
612
+			}
613
+			// then all of it's tickets are now unavailable
614
+			foreach ($datetime_tickets as $datetime_ticket) {
615
+				if (
616
+					($ticket_identifier === $datetime_ticket || $at_capacity)
617
+					&& isset($this->ticket_quantities[ $datetime_ticket ])
618
+					&& $this->ticket_quantities[ $datetime_ticket ] > 0
619
+				) {
620
+					if ($this->debug) {
621
+						\EEH_Debug_Tools::printr($datetime_ticket, ' . . . adjust ticket quantities for', __FILE__,
622
+							__LINE__);
623
+					}
624
+					// if this datetime is at full capacity, set any tracked available quantities to zero
625
+					// otherwise just subtract the ticket quantity
626
+					$new_quantity = $at_capacity
627
+						? 0
628
+						: $this->ticket_quantities[ $datetime_ticket ] - $ticket_quantity;
629
+					// don't let ticket quantity go below zero
630
+					$this->ticket_quantities[ $datetime_ticket ] = max($new_quantity, 0);
631
+					if ($this->debug) {
632
+						\EEH_Debug_Tools::printr(
633
+							$at_capacity
634
+								? "0 because Datetime {$datetime_identifier} is at capacity"
635
+								: "{$this->ticket_quantities[ $datetime_ticket ]}",
636
+							" . . . . {$datetime_ticket} quantity set to ",
637
+							__FILE__, __LINE__
638
+						);
639
+					}
640
+				}
641
+				// but we also need to adjust spaces for any other datetimes this ticket has access to
642
+				if ($datetime_ticket === $ticket_identifier) {
643
+					if (isset($this->ticket_datetimes[ $datetime_ticket ])
644
+						&& is_array($this->ticket_datetimes[ $datetime_ticket ])
645
+					) {
646
+						if ($this->debug) {
647
+							\EEH_Debug_Tools::printr($datetime_ticket, ' . . adjust other Datetimes for', __FILE__,
648
+								__LINE__);
649
+						}
650
+						foreach ($this->ticket_datetimes[ $datetime_ticket ] as $datetime) {
651
+							// don't adjust the current datetime twice
652
+							if ($datetime !== $datetime_identifier) {
653
+								$this->adjustDatetimeSpaces(
654
+									$datetime,
655
+									$datetime_ticket,
656
+									$ticket_quantity
657
+								);
658
+							}
659
+						}
660
+					}
661
+				}
662
+			}
663
+		}
664
+	}
665
+
666
+	private function adjustDatetimeSpaces($datetime_identifier, $ticket_identifier, $ticket_quantity = 0)
667
+	{
668
+		// does datetime have spaces available?
669
+		// and does the supplied ticket have access to this datetime ?
670
+		if (
671
+			$this->datetime_spaces[ $datetime_identifier ] > 0
672
+			&& isset($this->datetime_spaces[ $datetime_identifier ], $this->datetime_tickets[ $datetime_identifier ])
673
+			&& in_array($ticket_identifier, $this->datetime_tickets[ $datetime_identifier ], true)
674
+			) {
675
+			if ($this->debug) {
676
+				\EEH_Debug_Tools::printr($datetime_identifier, ' . . adjust Datetime Spaces for', __FILE__, __LINE__);
677
+				\EEH_Debug_Tools::printr("{$this->datetime_spaces[ $datetime_identifier ]}", " . . current  {$datetime_identifier} spaces available", __FILE__, __LINE__);
678
+			}
679
+			// then decrement the available spaces for the datetime
680
+			$this->datetime_spaces[ $datetime_identifier ] -= $ticket_quantity;
681
+			// but don't let quantities go below zero
682
+			$this->datetime_spaces[ $datetime_identifier ] = max(
683
+				$this->datetime_spaces[ $datetime_identifier ],
684
+				0
685
+			);
686
+			if ($this->debug) {
687
+				\EEH_Debug_Tools::printr("{$ticket_quantity}",
688
+					" . . . {$datetime_identifier} capacity reduced by", __FILE__, __LINE__);
689
+			}
690
+			return true;
691
+		}
692
+		return false;
693
+	}
694 694
 
695 695
 }
696 696
 // Location: EventSpacesCalculator.php
Please login to merge, or discard this patch.
Spacing   +50 added lines, -50 removed lines patch added patch discarded remove patch
@@ -131,14 +131,14 @@  discard block
 block discarded – undo
131 131
      */
132 132
     private function setHooks()
133 133
     {
134
-        add_action( 'AHEE__EE_Ticket__increase_sold', array($this, 'clearResults'));
135
-        add_action( 'AHEE__EE_Ticket__decrease_sold', array($this, 'clearResults'));
136
-        add_action( 'AHEE__EE_Datetime__increase_sold', array($this, 'clearResults'));
137
-        add_action( 'AHEE__EE_Datetime__decrease_sold', array($this, 'clearResults'));
138
-        add_action( 'AHEE__EE_Ticket__increase_reserved', array($this, 'clearResults'));
139
-        add_action( 'AHEE__EE_Ticket__decrease_reserved', array($this, 'clearResults'));
140
-        add_action( 'AHEE__EE_Datetime__increase_reserved', array($this, 'clearResults'));
141
-        add_action( 'AHEE__EE_Datetime__decrease_reserved', array($this, 'clearResults'));
134
+        add_action('AHEE__EE_Ticket__increase_sold', array($this, 'clearResults'));
135
+        add_action('AHEE__EE_Ticket__decrease_sold', array($this, 'clearResults'));
136
+        add_action('AHEE__EE_Datetime__increase_sold', array($this, 'clearResults'));
137
+        add_action('AHEE__EE_Datetime__decrease_sold', array($this, 'clearResults'));
138
+        add_action('AHEE__EE_Ticket__increase_reserved', array($this, 'clearResults'));
139
+        add_action('AHEE__EE_Ticket__decrease_reserved', array($this, 'clearResults'));
140
+        add_action('AHEE__EE_Datetime__increase_reserved', array($this, 'clearResults'));
141
+        add_action('AHEE__EE_Datetime__decrease_reserved', array($this, 'clearResults'));
142 142
     }
143 143
 
144 144
 
@@ -194,7 +194,7 @@  discard block
 block discarded – undo
194 194
             // sort incoming array by ticket quantity (asc)
195 195
             usort(
196 196
                 $active_tickets,
197
-                function (EE_Ticket $a, EE_Ticket $b) {
197
+                function(EE_Ticket $a, EE_Ticket $b) {
198 198
                     if ($a->qty() === $b->qty()) {
199 199
                         return 0;
200 200
                     }
@@ -270,7 +270,7 @@  discard block
 block discarded – undo
270 270
                 )
271 271
             );
272 272
         }
273
-        $this->datetimes[ $datetime->ID() ] = $datetime;
273
+        $this->datetimes[$datetime->ID()] = $datetime;
274 274
     }
275 275
 
276 276
 
@@ -310,7 +310,7 @@  discard block
 block discarded – undo
310 310
      */
311 311
     public function totalSpacesAvailable()
312 312
     {
313
-        if($this->total_spaces_available === null) {
313
+        if ($this->total_spaces_available === null) {
314 314
             $this->initialize();
315 315
             $this->total_spaces_available = $this->calculate(false);
316 316
         }
@@ -367,16 +367,16 @@  discard block
 block discarded – undo
367 367
                     // we are going to move all of our data into the following arrays:
368 368
                     // datetime spaces initially represents the reg limit for each datetime,
369 369
                     // but this will get adjusted as tickets are accounted for
370
-                    $this->datetime_spaces[ $datetime_identifier ] = $reg_limit;
370
+                    $this->datetime_spaces[$datetime_identifier] = $reg_limit;
371 371
                     // just an array of ticket IDs grouped by datetime
372
-                    $this->datetime_tickets[ $datetime_identifier ][] = $ticket_identifier;
372
+                    $this->datetime_tickets[$datetime_identifier][] = $ticket_identifier;
373 373
                     // and an array of datetime IDs grouped by ticket
374
-                    $this->ticket_datetimes[ $ticket_identifier ][] = $datetime_identifier;
374
+                    $this->ticket_datetimes[$ticket_identifier][] = $datetime_identifier;
375 375
                 }
376 376
                 // total quantity of sold and reserved for each ticket
377
-                $this->tickets_sold[ $ticket_identifier ] = $ticket->sold() + $ticket->reserved();
377
+                $this->tickets_sold[$ticket_identifier] = $ticket->sold() + $ticket->reserved();
378 378
                 // and the maximum ticket quantities for each ticket (adjusted for reg limit)
379
-                $this->ticket_quantities[ $ticket_identifier ] = $max_tickets;
379
+                $this->ticket_quantities[$ticket_identifier] = $max_tickets;
380 380
             }
381 381
         }
382 382
         // sort datetime spaces by reg limit, but maintain our string indexes
@@ -435,19 +435,19 @@  discard block
 block discarded – undo
435 435
             \EEH_Debug_Tools::printr(__FUNCTION__, __CLASS__, __FILE__, __LINE__, 2);
436 436
         }
437 437
         foreach ($this->tickets_sold as $ticket_identifier => $tickets_sold) {
438
-            if (isset($this->ticket_quantities[ $ticket_identifier ])){
439
-                $this->ticket_quantities[ $ticket_identifier ] -= $tickets_sold;
438
+            if (isset($this->ticket_quantities[$ticket_identifier])) {
439
+                $this->ticket_quantities[$ticket_identifier] -= $tickets_sold;
440 440
                 if ($this->debug) {
441 441
                     \EEH_Debug_Tools::printr("{$tickets_sold} sales for ticket {$ticket_identifier} ", 'subtracting', __FILE__, __LINE__);
442 442
                 }
443 443
             }
444 444
             if (
445
-                isset($this->ticket_datetimes[ $ticket_identifier ])
446
-                && is_array($this->ticket_datetimes[ $ticket_identifier ])
447
-            ){
448
-                foreach ($this->ticket_datetimes[ $ticket_identifier ] as $ticket_datetime) {
449
-                    if (isset($this->ticket_quantities[ $ticket_identifier ])) {
450
-                        $this->datetime_spaces[ $ticket_datetime ] -= $tickets_sold;
445
+                isset($this->ticket_datetimes[$ticket_identifier])
446
+                && is_array($this->ticket_datetimes[$ticket_identifier])
447
+            ) {
448
+                foreach ($this->ticket_datetimes[$ticket_identifier] as $ticket_datetime) {
449
+                    if (isset($this->ticket_quantities[$ticket_identifier])) {
450
+                        $this->datetime_spaces[$ticket_datetime] -= $tickets_sold;
451 451
                         if ($this->debug) {
452 452
                             \EEH_Debug_Tools::printr("{$tickets_sold} sales for datetime {$ticket_datetime} ",
453 453
                                 'subtracting', __FILE__, __LINE__);
@@ -467,8 +467,8 @@  discard block
 block discarded – undo
467 467
     private function trackAvailableSpacesForDatetimes($datetime_identifier, array $tickets)
468 468
     {
469 469
         // make sure a reg limit is set for the datetime
470
-        $reg_limit = isset($this->datetime_spaces[ $datetime_identifier ])
471
-            ? $this->datetime_spaces[ $datetime_identifier ]
470
+        $reg_limit = isset($this->datetime_spaces[$datetime_identifier])
471
+            ? $this->datetime_spaces[$datetime_identifier]
472 472
             : 0;
473 473
         // and bail if it is not
474 474
         if ( ! $reg_limit) {
@@ -483,7 +483,7 @@  discard block
 block discarded – undo
483 483
         }
484 484
         // number of allocated spaces always starts at zero
485 485
         $spaces_allocated                           = 0;
486
-        $this->total_spaces[ $datetime_identifier ] = 0;
486
+        $this->total_spaces[$datetime_identifier] = 0;
487 487
         foreach ($tickets as $ticket_identifier) {
488 488
             $spaces_allocated = $this->calculateAvailableSpacesForTicket(
489 489
                 $datetime_identifier,
@@ -496,9 +496,9 @@  discard block
 block discarded – undo
496 496
         $spaces_allocated = max($spaces_allocated, 0);
497 497
         if ($spaces_allocated) {
498 498
             // track any non-zero values
499
-            $this->total_spaces[ $datetime_identifier ] += $spaces_allocated;
499
+            $this->total_spaces[$datetime_identifier] += $spaces_allocated;
500 500
             if ($this->debug) {
501
-                \EEH_Debug_Tools::printr((string)$spaces_allocated, ' . $spaces_allocated: ', __FILE__, __LINE__);
501
+                \EEH_Debug_Tools::printr((string) $spaces_allocated, ' . $spaces_allocated: ', __FILE__, __LINE__);
502 502
             }
503 503
         } else {
504 504
             if ($this->debug) {
@@ -506,7 +506,7 @@  discard block
 block discarded – undo
506 506
             }
507 507
         }
508 508
         if ($this->debug) {
509
-            \EEH_Debug_Tools::printr($this->total_spaces[ $datetime_identifier ], '$total_spaces', __FILE__,
509
+            \EEH_Debug_Tools::printr($this->total_spaces[$datetime_identifier], '$total_spaces', __FILE__,
510 510
                 __LINE__);
511 511
             \EEH_Debug_Tools::printr($this->ticket_quantities, '$ticket_quantities', __FILE__, __LINE__);
512 512
             \EEH_Debug_Tools::printr($this->datetime_spaces, 'datetime_spaces', __FILE__, __LINE__);
@@ -529,8 +529,8 @@  discard block
 block discarded – undo
529 529
         $spaces_allocated
530 530
     ) {
531 531
         // make sure ticket quantity is set
532
-        $ticket_quantity = isset($this->ticket_quantities[ $ticket_identifier ])
533
-            ? $this->ticket_quantities[ $ticket_identifier ]
532
+        $ticket_quantity = isset($this->ticket_quantities[$ticket_identifier])
533
+            ? $this->ticket_quantities[$ticket_identifier]
534 534
             : 0;
535 535
         if ($this->debug) {
536 536
             \EEH_Debug_Tools::printr("{$spaces_allocated}", '$spaces_allocated', __FILE__, __LINE__);
@@ -554,13 +554,13 @@  discard block
 block discarded – undo
554 554
                 //  or the maximum ticket quantity
555 555
                 $ticket_quantity = min($reg_limit - $spaces_allocated, $ticket_quantity);
556 556
                 // adjust the available quantity in our tracking array
557
-                $this->ticket_quantities[ $ticket_identifier ] -= $ticket_quantity;
557
+                $this->ticket_quantities[$ticket_identifier] -= $ticket_quantity;
558 558
                 // and increment spaces allocated for this datetime
559 559
                 $spaces_allocated += $ticket_quantity;
560 560
                 $at_capacity = $spaces_allocated >= $reg_limit;
561 561
                 if ($this->debug) {
562 562
                     \EEH_Debug_Tools::printr("{$ticket_quantity} {$ticket_identifier} tickets", ' > > allocate ',
563
-                        __FILE__, __LINE__,   3);
563
+                        __FILE__, __LINE__, 3);
564 564
                     if ($at_capacity) {
565 565
                         \EEH_Debug_Tools::printr('AT CAPACITY', " . {$datetime_identifier}", __FILE__, __LINE__, 3);
566 566
                     }
@@ -607,15 +607,15 @@  discard block
 block discarded – undo
607 607
                 $ticket_quantity
608 608
             );
609 609
             // skip to next ticket if nothing changed
610
-            if (! ($adjusted || $at_capacity)) {
610
+            if ( ! ($adjusted || $at_capacity)) {
611 611
                 continue;
612 612
             }
613 613
             // then all of it's tickets are now unavailable
614 614
             foreach ($datetime_tickets as $datetime_ticket) {
615 615
                 if (
616 616
                     ($ticket_identifier === $datetime_ticket || $at_capacity)
617
-                    && isset($this->ticket_quantities[ $datetime_ticket ])
618
-                    && $this->ticket_quantities[ $datetime_ticket ] > 0
617
+                    && isset($this->ticket_quantities[$datetime_ticket])
618
+                    && $this->ticket_quantities[$datetime_ticket] > 0
619 619
                 ) {
620 620
                     if ($this->debug) {
621 621
                         \EEH_Debug_Tools::printr($datetime_ticket, ' . . . adjust ticket quantities for', __FILE__,
@@ -625,14 +625,14 @@  discard block
 block discarded – undo
625 625
                     // otherwise just subtract the ticket quantity
626 626
                     $new_quantity = $at_capacity
627 627
                         ? 0
628
-                        : $this->ticket_quantities[ $datetime_ticket ] - $ticket_quantity;
628
+                        : $this->ticket_quantities[$datetime_ticket] - $ticket_quantity;
629 629
                     // don't let ticket quantity go below zero
630
-                    $this->ticket_quantities[ $datetime_ticket ] = max($new_quantity, 0);
630
+                    $this->ticket_quantities[$datetime_ticket] = max($new_quantity, 0);
631 631
                     if ($this->debug) {
632 632
                         \EEH_Debug_Tools::printr(
633 633
                             $at_capacity
634 634
                                 ? "0 because Datetime {$datetime_identifier} is at capacity"
635
-                                : "{$this->ticket_quantities[ $datetime_ticket ]}",
635
+                                : "{$this->ticket_quantities[$datetime_ticket]}",
636 636
                             " . . . . {$datetime_ticket} quantity set to ",
637 637
                             __FILE__, __LINE__
638 638
                         );
@@ -640,14 +640,14 @@  discard block
 block discarded – undo
640 640
                 }
641 641
                 // but we also need to adjust spaces for any other datetimes this ticket has access to
642 642
                 if ($datetime_ticket === $ticket_identifier) {
643
-                    if (isset($this->ticket_datetimes[ $datetime_ticket ])
644
-                        && is_array($this->ticket_datetimes[ $datetime_ticket ])
643
+                    if (isset($this->ticket_datetimes[$datetime_ticket])
644
+                        && is_array($this->ticket_datetimes[$datetime_ticket])
645 645
                     ) {
646 646
                         if ($this->debug) {
647 647
                             \EEH_Debug_Tools::printr($datetime_ticket, ' . . adjust other Datetimes for', __FILE__,
648 648
                                 __LINE__);
649 649
                         }
650
-                        foreach ($this->ticket_datetimes[ $datetime_ticket ] as $datetime) {
650
+                        foreach ($this->ticket_datetimes[$datetime_ticket] as $datetime) {
651 651
                             // don't adjust the current datetime twice
652 652
                             if ($datetime !== $datetime_identifier) {
653 653
                                 $this->adjustDatetimeSpaces(
@@ -668,19 +668,19 @@  discard block
 block discarded – undo
668 668
         // does datetime have spaces available?
669 669
         // and does the supplied ticket have access to this datetime ?
670 670
         if (
671
-            $this->datetime_spaces[ $datetime_identifier ] > 0
672
-            && isset($this->datetime_spaces[ $datetime_identifier ], $this->datetime_tickets[ $datetime_identifier ])
673
-            && in_array($ticket_identifier, $this->datetime_tickets[ $datetime_identifier ], true)
671
+            $this->datetime_spaces[$datetime_identifier] > 0
672
+            && isset($this->datetime_spaces[$datetime_identifier], $this->datetime_tickets[$datetime_identifier])
673
+            && in_array($ticket_identifier, $this->datetime_tickets[$datetime_identifier], true)
674 674
             ) {
675 675
             if ($this->debug) {
676 676
                 \EEH_Debug_Tools::printr($datetime_identifier, ' . . adjust Datetime Spaces for', __FILE__, __LINE__);
677
-                \EEH_Debug_Tools::printr("{$this->datetime_spaces[ $datetime_identifier ]}", " . . current  {$datetime_identifier} spaces available", __FILE__, __LINE__);
677
+                \EEH_Debug_Tools::printr("{$this->datetime_spaces[$datetime_identifier]}", " . . current  {$datetime_identifier} spaces available", __FILE__, __LINE__);
678 678
             }
679 679
             // then decrement the available spaces for the datetime
680
-            $this->datetime_spaces[ $datetime_identifier ] -= $ticket_quantity;
680
+            $this->datetime_spaces[$datetime_identifier] -= $ticket_quantity;
681 681
             // but don't let quantities go below zero
682
-            $this->datetime_spaces[ $datetime_identifier ] = max(
683
-                $this->datetime_spaces[ $datetime_identifier ],
682
+            $this->datetime_spaces[$datetime_identifier] = max(
683
+                $this->datetime_spaces[$datetime_identifier],
684 684
                 0
685 685
             );
686 686
             if ($this->debug) {
Please login to merge, or discard this patch.