Completed
Branch master (16095c)
by
unknown
09:17 queued 04:49
created
core/admin/EE_Admin_Page_CPT.core.php 2 patches
Indentation   +1421 added lines, -1421 removed lines patch added patch discarded remove patch
@@ -31,447 +31,447 @@  discard block
 block discarded – undo
31 31
  */
32 32
 abstract class EE_Admin_Page_CPT extends EE_Admin_Page
33 33
 {
34
-    /**
35
-     * This gets set in _setup_cpt
36
-     * It will contain the object for the custom post type.
37
-     *
38
-     * @var EE_CPT_Base
39
-     */
40
-    protected $_cpt_object;
41
-
42
-
43
-    /**
44
-     * This property allows cpt classes to define multiple routes as cpt routes.
45
-     * //in this array we define what the custom post type for this route is.
46
-     * array(
47
-     * 'route_name' => 'custom_post_type_slug'
48
-     * )
49
-     *
50
-     * @var array
51
-     */
52
-    protected $_cpt_routes = [];
53
-
54
-
55
-    /**
56
-     * This simply defines what the corresponding routes WP will be redirected to after completing a post save/update.
57
-     * in this format:
58
-     * array(
59
-     * 'post_type_slug' => 'edit_route'
60
-     * )
61
-     *
62
-     * @var array
63
-     */
64
-    protected $_cpt_edit_routes = [];
65
-
66
-
67
-    /**
68
-     * If child classes set the name of their main model via the $_cpt_obj_models property, EE_Admin_Page_CPT will
69
-     * attempt to retrieve the related object model for the edit pages and assign it to _cpt_page_object. the
70
-     * _cpt_model_names property should be in the following format: array(
71
-     * 'route_defined_by_action_param' => 'Model_Name')
72
-     *
73
-     * @var array $_cpt_model_names
74
-     */
75
-    protected $_cpt_model_names = [];
76
-
77
-
78
-    /**
79
-     * @var EE_CPT_Base
80
-     */
81
-    protected $_cpt_model_obj = null;
82
-
83
-
84
-    /**
85
-     * This will hold an array of autosave containers that will be used to obtain input values and hook into the WP
86
-     * autosave so we can save our inputs on the save_post hook!  Children classes should add to this array by using
87
-     * the _register_autosave_containers() method so that we don't override any other containers already registered.
88
-     * Registration of containers should be done before load_page_dependencies() is run.
89
-     *
90
-     * @var array()
91
-     */
92
-    protected $_autosave_containers = [];
93
-
94
-    protected $_autosave_fields     = [];
95
-
96
-    /**
97
-     * Array mapping from admin actions to their equivalent wp core pages for custom post types. So when a user visits
98
-     * a page for an action, it will appear as if they were visiting the wp core page for that custom post type
99
-     *
100
-     * @var array
101
-     */
102
-    protected $_pagenow_map;
103
-
104
-
105
-    /**
106
-     * This is hooked into the WordPress do_action('save_post') hook and runs after the custom post type has been
107
-     * saved.  Child classes are required to declare this method.  Typically you would use this to save any additional
108
-     * data. Keep in mind also that "save_post" runs on EVERY post update to the database. ALSO very important.  When a
109
-     * post transitions from scheduled to published, the save_post action is fired but you will NOT have any _POST data
110
-     * containing any extra info you may have from other meta saves.  So MAKE sure that you handle this accordingly.
111
-     *
112
-     * @abstract
113
-     * @param string  $post_id The ID of the cpt that was saved (so you can link relationally)
114
-     * @param WP_Post $post    The post object of the cpt that was saved.
115
-     * @return void
116
-     */
117
-    abstract protected function _insert_update_cpt_item($post_id, $post);
118
-
119
-
120
-    /**
121
-     * This is hooked into the WordPress do_action('trashed_post') hook and runs after a cpt has been trashed.
122
-     *
123
-     * @abstract
124
-     * @param string $post_id The ID of the cpt that was trashed
125
-     * @return void
126
-     */
127
-    abstract public function trash_cpt_item($post_id);
128
-
129
-
130
-    /**
131
-     * This is hooked into the WordPress do_action('untrashed_post') hook and runs after a cpt has been untrashed
132
-     *
133
-     * @param string $post_id theID of the cpt that was untrashed
134
-     * @return void
135
-     */
136
-    abstract public function restore_cpt_item($post_id);
137
-
138
-
139
-    /**
140
-     * This is hooked into the WordPress do_action('delete_cpt_item') hook and runs after a cpt has been fully deleted
141
-     * from the db
142
-     *
143
-     * @param string $post_id the ID of the cpt that was deleted
144
-     * @return void
145
-     */
146
-    abstract public function delete_cpt_item($post_id);
147
-
148
-
149
-    /**
150
-     * @return LoaderInterface
151
-     * @throws InvalidArgumentException
152
-     * @throws InvalidDataTypeException
153
-     * @throws InvalidInterfaceException
154
-     */
155
-    protected function getLoader(): LoaderInterface
156
-    {
157
-        if (! $this->loader instanceof LoaderInterface) {
158
-            $this->loader = LoaderFactory::getLoader();
159
-        }
160
-        return $this->loader;
161
-    }
162
-
163
-
164
-    /**
165
-     * Just utilizing the method EE_Admin exposes for doing things before page setup.
166
-     *
167
-     * @return void
168
-     */
169
-    protected function _before_page_setup()
170
-    {
171
-        $this->raw_req_action = $this->request->getRequestParam('action');
172
-        $this->raw_req_page   = $this->request->getRequestParam('page');
173
-        $this->_cpt_routes    = array_merge(
174
-            [
175
-                'create_new' => $this->page_slug,
176
-                'edit'       => $this->page_slug,
177
-                'trash'      => $this->page_slug,
178
-            ],
179
-            $this->_cpt_routes
180
-        );
181
-        $cpt_route_action     = $this->_cpt_routes[ $this->raw_req_action ] ?? null;
182
-        // let's see if the current route has a value for cpt_object_slug. if it does, we use that instead of the page
183
-        $page              = $this->raw_req_page ?: $this->page_slug;
184
-        $page              = $cpt_route_action ?: $page;
185
-        $this->_cpt_object = get_post_type_object($page);
186
-        // tweak pagenow for page loading.
187
-        if (! $this->_pagenow_map) {
188
-            $this->_pagenow_map = [
189
-                'create_new' => 'post-new.php',
190
-                'edit'       => 'post.php',
191
-                'trash'      => 'post.php',
192
-            ];
193
-        }
194
-        add_action('current_screen', [$this, 'modify_pagenow']);
195
-        // TODO the below will need to be reworked to account for the cpt routes that are NOT based off of page but action param.
196
-        // get current page from autosave
197
-        $current_page        = $this->request->getRequestParam('ee_autosave_data[ee-cpt-hidden-inputs][current_page]');
198
-        $this->_current_page = $this->request->getRequestParam('current_page', $current_page);
199
-    }
200
-
201
-
202
-    /**
203
-     * Simply ensure that we simulate the correct post route for cpt screens
204
-     *
205
-     * @param WP_Screen|null $current_screen
206
-     * @return void
207
-     */
208
-    public function modify_pagenow(?WP_Screen $current_screen)
209
-    {
210
-        // possibly reset pagenow.
211
-        if (
212
-            $this->page_slug === $this->raw_req_page
213
-            && isset($this->_pagenow_map[ $this->raw_req_action ])
214
-        ) {
215
-            global $pagenow, $hook_suffix;
216
-            $pagenow     = $this->_pagenow_map[ $this->raw_req_action ];
217
-            $hook_suffix = $pagenow;
218
-        }
219
-    }
220
-
221
-
222
-    /**
223
-     * This method is used to register additional autosave containers to the _autosave_containers property.
224
-     *
225
-     * @param array $ids  an array of ids for containers that hold form inputs we want autosave to pickup.  Typically
226
-     *                    you would send along the id of a metabox container.
227
-     * @return void
228
-     * @todo We should automate this at some point by creating a wrapper for add_post_metabox and in our wrapper we
229
-     *                    automatically register the id for the post metabox as a container.
230
-     */
231
-    protected function _register_autosave_containers($ids)
232
-    {
233
-        $this->_autosave_containers = array_merge($this->_autosave_fields, (array) $ids);
234
-    }
235
-
236
-
237
-    /**
238
-     * Something nifty.  We're going to loop through all the registered metaboxes and if the CALLBACK is an instance of
239
-     * EE_Admin_Page OR EE_Admin_Hooks, then we'll add the id to our _autosave_containers array.
240
-     */
241
-    protected function _set_autosave_containers()
242
-    {
243
-        global $wp_meta_boxes;
244
-        $containers = [];
245
-        if (empty($wp_meta_boxes)) {
246
-            return;
247
-        }
248
-        $current_metaboxes = $wp_meta_boxes[ $this->page_slug ] ?? [];
249
-        foreach ($current_metaboxes as $box_context) {
250
-            foreach ($box_context as $box_details) {
251
-                foreach ($box_details as $box) {
252
-                    if (
253
-                        is_array($box) && is_array($box['callback'])
254
-                        && (
255
-                            $box['callback'][0] instanceof EE_Admin_Page
256
-                            || $box['callback'][0] instanceof EE_Admin_Hooks
257
-                        )
258
-                    ) {
259
-                        $containers[] = $box['id'];
260
-                    }
261
-                }
262
-            }
263
-        }
264
-        $this->_autosave_containers = array_merge($this->_autosave_containers, $containers);
265
-        // add hidden inputs container
266
-        $this->_autosave_containers[] = 'ee-cpt-hidden-inputs';
267
-    }
268
-
269
-
270
-    protected function _load_autosave_scripts_styles()
271
-    {
272
-        /*wp_register_script('cpt-autosave', EE_ADMIN_URL . 'assets/ee-cpt-autosave.js', array('ee-serialize-full-array', 'event_editor_js'), EVENT_ESPRESSO_VERSION, TRUE );
34
+	/**
35
+	 * This gets set in _setup_cpt
36
+	 * It will contain the object for the custom post type.
37
+	 *
38
+	 * @var EE_CPT_Base
39
+	 */
40
+	protected $_cpt_object;
41
+
42
+
43
+	/**
44
+	 * This property allows cpt classes to define multiple routes as cpt routes.
45
+	 * //in this array we define what the custom post type for this route is.
46
+	 * array(
47
+	 * 'route_name' => 'custom_post_type_slug'
48
+	 * )
49
+	 *
50
+	 * @var array
51
+	 */
52
+	protected $_cpt_routes = [];
53
+
54
+
55
+	/**
56
+	 * This simply defines what the corresponding routes WP will be redirected to after completing a post save/update.
57
+	 * in this format:
58
+	 * array(
59
+	 * 'post_type_slug' => 'edit_route'
60
+	 * )
61
+	 *
62
+	 * @var array
63
+	 */
64
+	protected $_cpt_edit_routes = [];
65
+
66
+
67
+	/**
68
+	 * If child classes set the name of their main model via the $_cpt_obj_models property, EE_Admin_Page_CPT will
69
+	 * attempt to retrieve the related object model for the edit pages and assign it to _cpt_page_object. the
70
+	 * _cpt_model_names property should be in the following format: array(
71
+	 * 'route_defined_by_action_param' => 'Model_Name')
72
+	 *
73
+	 * @var array $_cpt_model_names
74
+	 */
75
+	protected $_cpt_model_names = [];
76
+
77
+
78
+	/**
79
+	 * @var EE_CPT_Base
80
+	 */
81
+	protected $_cpt_model_obj = null;
82
+
83
+
84
+	/**
85
+	 * This will hold an array of autosave containers that will be used to obtain input values and hook into the WP
86
+	 * autosave so we can save our inputs on the save_post hook!  Children classes should add to this array by using
87
+	 * the _register_autosave_containers() method so that we don't override any other containers already registered.
88
+	 * Registration of containers should be done before load_page_dependencies() is run.
89
+	 *
90
+	 * @var array()
91
+	 */
92
+	protected $_autosave_containers = [];
93
+
94
+	protected $_autosave_fields     = [];
95
+
96
+	/**
97
+	 * Array mapping from admin actions to their equivalent wp core pages for custom post types. So when a user visits
98
+	 * a page for an action, it will appear as if they were visiting the wp core page for that custom post type
99
+	 *
100
+	 * @var array
101
+	 */
102
+	protected $_pagenow_map;
103
+
104
+
105
+	/**
106
+	 * This is hooked into the WordPress do_action('save_post') hook and runs after the custom post type has been
107
+	 * saved.  Child classes are required to declare this method.  Typically you would use this to save any additional
108
+	 * data. Keep in mind also that "save_post" runs on EVERY post update to the database. ALSO very important.  When a
109
+	 * post transitions from scheduled to published, the save_post action is fired but you will NOT have any _POST data
110
+	 * containing any extra info you may have from other meta saves.  So MAKE sure that you handle this accordingly.
111
+	 *
112
+	 * @abstract
113
+	 * @param string  $post_id The ID of the cpt that was saved (so you can link relationally)
114
+	 * @param WP_Post $post    The post object of the cpt that was saved.
115
+	 * @return void
116
+	 */
117
+	abstract protected function _insert_update_cpt_item($post_id, $post);
118
+
119
+
120
+	/**
121
+	 * This is hooked into the WordPress do_action('trashed_post') hook and runs after a cpt has been trashed.
122
+	 *
123
+	 * @abstract
124
+	 * @param string $post_id The ID of the cpt that was trashed
125
+	 * @return void
126
+	 */
127
+	abstract public function trash_cpt_item($post_id);
128
+
129
+
130
+	/**
131
+	 * This is hooked into the WordPress do_action('untrashed_post') hook and runs after a cpt has been untrashed
132
+	 *
133
+	 * @param string $post_id theID of the cpt that was untrashed
134
+	 * @return void
135
+	 */
136
+	abstract public function restore_cpt_item($post_id);
137
+
138
+
139
+	/**
140
+	 * This is hooked into the WordPress do_action('delete_cpt_item') hook and runs after a cpt has been fully deleted
141
+	 * from the db
142
+	 *
143
+	 * @param string $post_id the ID of the cpt that was deleted
144
+	 * @return void
145
+	 */
146
+	abstract public function delete_cpt_item($post_id);
147
+
148
+
149
+	/**
150
+	 * @return LoaderInterface
151
+	 * @throws InvalidArgumentException
152
+	 * @throws InvalidDataTypeException
153
+	 * @throws InvalidInterfaceException
154
+	 */
155
+	protected function getLoader(): LoaderInterface
156
+	{
157
+		if (! $this->loader instanceof LoaderInterface) {
158
+			$this->loader = LoaderFactory::getLoader();
159
+		}
160
+		return $this->loader;
161
+	}
162
+
163
+
164
+	/**
165
+	 * Just utilizing the method EE_Admin exposes for doing things before page setup.
166
+	 *
167
+	 * @return void
168
+	 */
169
+	protected function _before_page_setup()
170
+	{
171
+		$this->raw_req_action = $this->request->getRequestParam('action');
172
+		$this->raw_req_page   = $this->request->getRequestParam('page');
173
+		$this->_cpt_routes    = array_merge(
174
+			[
175
+				'create_new' => $this->page_slug,
176
+				'edit'       => $this->page_slug,
177
+				'trash'      => $this->page_slug,
178
+			],
179
+			$this->_cpt_routes
180
+		);
181
+		$cpt_route_action     = $this->_cpt_routes[ $this->raw_req_action ] ?? null;
182
+		// let's see if the current route has a value for cpt_object_slug. if it does, we use that instead of the page
183
+		$page              = $this->raw_req_page ?: $this->page_slug;
184
+		$page              = $cpt_route_action ?: $page;
185
+		$this->_cpt_object = get_post_type_object($page);
186
+		// tweak pagenow for page loading.
187
+		if (! $this->_pagenow_map) {
188
+			$this->_pagenow_map = [
189
+				'create_new' => 'post-new.php',
190
+				'edit'       => 'post.php',
191
+				'trash'      => 'post.php',
192
+			];
193
+		}
194
+		add_action('current_screen', [$this, 'modify_pagenow']);
195
+		// TODO the below will need to be reworked to account for the cpt routes that are NOT based off of page but action param.
196
+		// get current page from autosave
197
+		$current_page        = $this->request->getRequestParam('ee_autosave_data[ee-cpt-hidden-inputs][current_page]');
198
+		$this->_current_page = $this->request->getRequestParam('current_page', $current_page);
199
+	}
200
+
201
+
202
+	/**
203
+	 * Simply ensure that we simulate the correct post route for cpt screens
204
+	 *
205
+	 * @param WP_Screen|null $current_screen
206
+	 * @return void
207
+	 */
208
+	public function modify_pagenow(?WP_Screen $current_screen)
209
+	{
210
+		// possibly reset pagenow.
211
+		if (
212
+			$this->page_slug === $this->raw_req_page
213
+			&& isset($this->_pagenow_map[ $this->raw_req_action ])
214
+		) {
215
+			global $pagenow, $hook_suffix;
216
+			$pagenow     = $this->_pagenow_map[ $this->raw_req_action ];
217
+			$hook_suffix = $pagenow;
218
+		}
219
+	}
220
+
221
+
222
+	/**
223
+	 * This method is used to register additional autosave containers to the _autosave_containers property.
224
+	 *
225
+	 * @param array $ids  an array of ids for containers that hold form inputs we want autosave to pickup.  Typically
226
+	 *                    you would send along the id of a metabox container.
227
+	 * @return void
228
+	 * @todo We should automate this at some point by creating a wrapper for add_post_metabox and in our wrapper we
229
+	 *                    automatically register the id for the post metabox as a container.
230
+	 */
231
+	protected function _register_autosave_containers($ids)
232
+	{
233
+		$this->_autosave_containers = array_merge($this->_autosave_fields, (array) $ids);
234
+	}
235
+
236
+
237
+	/**
238
+	 * Something nifty.  We're going to loop through all the registered metaboxes and if the CALLBACK is an instance of
239
+	 * EE_Admin_Page OR EE_Admin_Hooks, then we'll add the id to our _autosave_containers array.
240
+	 */
241
+	protected function _set_autosave_containers()
242
+	{
243
+		global $wp_meta_boxes;
244
+		$containers = [];
245
+		if (empty($wp_meta_boxes)) {
246
+			return;
247
+		}
248
+		$current_metaboxes = $wp_meta_boxes[ $this->page_slug ] ?? [];
249
+		foreach ($current_metaboxes as $box_context) {
250
+			foreach ($box_context as $box_details) {
251
+				foreach ($box_details as $box) {
252
+					if (
253
+						is_array($box) && is_array($box['callback'])
254
+						&& (
255
+							$box['callback'][0] instanceof EE_Admin_Page
256
+							|| $box['callback'][0] instanceof EE_Admin_Hooks
257
+						)
258
+					) {
259
+						$containers[] = $box['id'];
260
+					}
261
+				}
262
+			}
263
+		}
264
+		$this->_autosave_containers = array_merge($this->_autosave_containers, $containers);
265
+		// add hidden inputs container
266
+		$this->_autosave_containers[] = 'ee-cpt-hidden-inputs';
267
+	}
268
+
269
+
270
+	protected function _load_autosave_scripts_styles()
271
+	{
272
+		/*wp_register_script('cpt-autosave', EE_ADMIN_URL . 'assets/ee-cpt-autosave.js', array('ee-serialize-full-array', 'event_editor_js'), EVENT_ESPRESSO_VERSION, TRUE );
273 273
         wp_enqueue_script('cpt-autosave');/**/ // todo re-enable when we start doing autosave again in 4.2
274 274
 
275
-        // filter _autosave_containers
276
-        $containers = apply_filters(
277
-            'FHEE__EE_Admin_Page_CPT___load_autosave_scripts_styles__containers',
278
-            $this->_autosave_containers,
279
-            $this
280
-        );
281
-        $containers = apply_filters(
282
-            'FHEE__EE_Admin_Page_CPT__' . get_class($this) . '___load_autosave_scripts_styles__containers',
283
-            $containers,
284
-            $this
285
-        );
286
-
287
-        wp_localize_script(
288
-            'event_editor_js',
289
-            'EE_AUTOSAVE_IDS',
290
-            $containers
291
-        ); // todo once we enable autosaves, this needs to be switched to localize with "cpt-autosave"
292
-
293
-        $unsaved_data_msg = [
294
-            'eventmsg'     => sprintf(
295
-                wp_strip_all_tags(
296
-                    __(
297
-                        "The changes you made to this %s will be lost if you navigate away from this page.",
298
-                        'event_espresso'
299
-                    )
300
-                ),
301
-                $this->_cpt_object->labels->singular_name
302
-            ),
303
-            'inputChanged' => 0,
304
-        ];
305
-        wp_localize_script('event_editor_js', 'UNSAVED_DATA_MSG', $unsaved_data_msg);
306
-    }
307
-
308
-
309
-    /**
310
-     * overloading the EE_Admin_Page parent load_page_dependencies so we can get the cpt stuff added in appropriately
311
-     *
312
-     * @return void
313
-     * @throws EE_Error
314
-     * @throws ReflectionException
315
-     */
316
-    protected function _load_page_dependencies()
317
-    {
318
-        // we only add stuff if this is a cpt_route!
319
-        if (! $this->_cpt_route) {
320
-            parent::_load_page_dependencies();
321
-            return;
322
-        }
323
-        // now let's do some automatic filters into the wp_system
324
-        // and we'll check to make sure the CHILD class
325
-        // automatically has the required methods in place.
326
-        // the following filters are for setting all the redirects
327
-        // on DEFAULT WP custom post type actions
328
-        // let's add a hidden input to the post-edit form
329
-        // so we know when we have to trigger our custom redirects!
330
-        // Otherwise the redirects will happen on ALL post saves which wouldn't be good of course!
331
-        add_action('edit_form_after_title', [$this, 'cpt_post_form_hidden_input']);
332
-        // inject our Admin page nav tabs...
333
-        // let's make sure the nav tabs are set if they aren't already
334
-        // if ( empty( $this->_nav_tabs ) ) $this->_set_nav_tabs();
335
-        add_action('edit_form_top', [$this, 'inject_nav_tabs']);
336
-        // modify the post_updated messages array
337
-        add_action('post_updated_messages', [$this, 'post_update_messages'], 10);
338
-        // This basically allows us to change the title of the "publish" metabox area
339
-        // on CPT pages by setting a 'publishbox' value in the $_labels property array in the child class.
340
-        $screen = $this->_cpt_routes[ $this->_req_action ];
341
-        if (! empty($this->_labels['publishbox'])) {
342
-            $this->addMetaBox(
343
-                'submitdiv',
344
-                $this->getPublishBoxTitle(),
345
-                'post_submit_meta_box',
346
-                $screen,
347
-                'side',
348
-                'core'
349
-            );
350
-        }
351
-        // let's add page_templates metabox if this cpt added support for it.
352
-        if ($this->_supports_page_templates($this->_cpt_object->name)) {
353
-            $this->addMetaBox(
354
-                'page_templates',
355
-                esc_html__('Page Template', 'event_espresso'),
356
-                [$this, 'page_template_meta_box'],
357
-                $screen,
358
-                'side'
359
-            );
360
-        }
361
-        // add preview button
362
-        add_filter('get_sample_permalink_html', [PreviewButton::class, 'addButton'], 5, 2);
363
-        // add shortlink button to cpt edit screens.
364
-        //  We can do this as a universal thing BECAUSE, cpts use the same format for shortlinks as posts!
365
-        add_filter('get_sample_permalink_html', [EventShortlinkButton::class, 'addButton'], 10, 2);
366
-        // this is a filter that allows the addition of extra html after the permalink field on the wp post edit-form
367
-        add_filter('get_sample_permalink_html', [TicketSelectorShortcodeButton::class, 'addButton'], 12, 4);
368
-        // insert our own post_stati dropdown
369
-        add_action('post_submitbox_misc_actions', [$this, 'custom_post_stati_dropdown'], 10);
370
-        // This allows adding additional information to the publish post submitbox on the wp post edit form
371
-        if (method_exists($this, 'extra_misc_actions_publish_box')) {
372
-            add_action('post_submitbox_misc_actions', [$this, 'extra_misc_actions_publish_box'], 10);
373
-        }
374
-        // This allows for adding additional stuff after the title field on the wp post edit form.
375
-        // This is also before the wp_editor for post description field.
376
-        if (method_exists($this, 'edit_form_after_title')) {
377
-            add_action('edit_form_after_title', [$this, 'edit_form_after_title'], 10);
378
-        }
379
-        /**
380
-         * Filtering WP's esc_url to capture urls pointing to core wp routes so they point to our route.
381
-         */
382
-        add_filter('clean_url', [$this, 'switch_core_wp_urls_with_ours'], 10);
383
-        parent::_load_page_dependencies();
384
-        // notice we are ALSO going to load the pagenow hook set for this route
385
-        // (see _before_page_setup for the reset of the pagenow global ).
386
-        // This is for any plugins that are doing things properly
387
-        // and hooking into the load page hook for core wp cpt routes.
388
-        global $pagenow;
389
-        add_action('load-' . $pagenow, [$this, 'modify_current_screen'], 20);
390
-        do_action('load-' . $pagenow);
391
-        add_action('admin_enqueue_scripts', [$this, 'setup_autosave_hooks'], 30);
392
-        // we route REALLY early.
393
-        try {
394
-            $this->_route_admin_request();
395
-        } catch (EE_Error $e) {
396
-            $e->get_error();
397
-        }
398
-    }
399
-
400
-
401
-    /**
402
-     * Since we don't want users going to default core wp routes, this will check any wp urls run through the
403
-     * esc_url() method and if we see a url matching a pattern for our routes, we'll modify it to point to OUR
404
-     * route instead.
405
-     *
406
-     * @param string $good_protocol_url The escaped url.
407
-     * @return string possibly a new url for our route.
408
-     */
409
-    public function switch_core_wp_urls_with_ours(string $good_protocol_url): string
410
-    {
411
-        $routes_to_match = [
412
-            0 => [
413
-                'edit.php?post_type=espresso_attendees',
414
-                'admin.php?page=espresso_registrations&action=contact_list',
415
-            ],
416
-            1 => [
417
-                'edit.php?post_type=' . $this->_cpt_object->name,
418
-                'admin.php?page=' . $this->_cpt_object->name,
419
-            ],
420
-        ];
421
-        foreach ($routes_to_match as $route_matches) {
422
-            if (strpos($good_protocol_url, $route_matches[0]) !== false) {
423
-                return str_replace($route_matches[0], $route_matches[1], $good_protocol_url);
424
-            }
425
-        }
426
-        return $good_protocol_url;
427
-    }
428
-
429
-
430
-    /**
431
-     * Determine whether the current cpt supports page templates or not.
432
-     *
433
-     * @param string $cpt_name The cpt slug we're checking on.
434
-     * @return bool True supported, false not.
435
-     * @throws InvalidArgumentException
436
-     * @throws InvalidDataTypeException
437
-     * @throws InvalidInterfaceException
438
-     * @since %VER%
439
-     */
440
-    private function _supports_page_templates(string $cpt_name): bool
441
-    {
442
-        /** @var EventEspresso\core\domain\entities\custom_post_types\CustomPostTypeDefinitions $custom_post_types */
443
-        $custom_post_types = $this->loader->getShared(
444
-            'EventEspresso\core\domain\entities\custom_post_types\CustomPostTypeDefinitions'
445
-        );
446
-        $cpt_args          = $custom_post_types->getDefinitions();
447
-        $cpt_args          = isset($cpt_args[ $cpt_name ]) ? $cpt_args[ $cpt_name ]['args'] : [];
448
-        $cpt_has_support   = ! empty($cpt_args['page_templates']);
449
-
450
-        $post_templates = wp_get_theme()->get_post_templates();
451
-        // if there are $post_templates for this cpt, then we return false for this method because
452
-        // that means we aren't going to load our page template manager and leave that up to the native
453
-        // cpt template manager.
454
-        return ! isset($post_templates[ $cpt_name ]) ? $cpt_has_support : false;
455
-    }
456
-
457
-
458
-    /**
459
-     * Callback for the page_templates metabox selector.
460
-     *
461
-     * @return void
462
-     * @since %VER%
463
-     */
464
-    public function page_template_meta_box()
465
-    {
466
-        global $post;
467
-        $template = '';
468
-
469
-        $page_template_count = count(get_page_templates());
470
-        if ($page_template_count) {
471
-            $page_template = get_post_meta($post->ID, '_wp_page_template', true);
472
-            $template      = ! empty($page_template) ? $page_template : '';
473
-        }
474
-        ?>
275
+		// filter _autosave_containers
276
+		$containers = apply_filters(
277
+			'FHEE__EE_Admin_Page_CPT___load_autosave_scripts_styles__containers',
278
+			$this->_autosave_containers,
279
+			$this
280
+		);
281
+		$containers = apply_filters(
282
+			'FHEE__EE_Admin_Page_CPT__' . get_class($this) . '___load_autosave_scripts_styles__containers',
283
+			$containers,
284
+			$this
285
+		);
286
+
287
+		wp_localize_script(
288
+			'event_editor_js',
289
+			'EE_AUTOSAVE_IDS',
290
+			$containers
291
+		); // todo once we enable autosaves, this needs to be switched to localize with "cpt-autosave"
292
+
293
+		$unsaved_data_msg = [
294
+			'eventmsg'     => sprintf(
295
+				wp_strip_all_tags(
296
+					__(
297
+						"The changes you made to this %s will be lost if you navigate away from this page.",
298
+						'event_espresso'
299
+					)
300
+				),
301
+				$this->_cpt_object->labels->singular_name
302
+			),
303
+			'inputChanged' => 0,
304
+		];
305
+		wp_localize_script('event_editor_js', 'UNSAVED_DATA_MSG', $unsaved_data_msg);
306
+	}
307
+
308
+
309
+	/**
310
+	 * overloading the EE_Admin_Page parent load_page_dependencies so we can get the cpt stuff added in appropriately
311
+	 *
312
+	 * @return void
313
+	 * @throws EE_Error
314
+	 * @throws ReflectionException
315
+	 */
316
+	protected function _load_page_dependencies()
317
+	{
318
+		// we only add stuff if this is a cpt_route!
319
+		if (! $this->_cpt_route) {
320
+			parent::_load_page_dependencies();
321
+			return;
322
+		}
323
+		// now let's do some automatic filters into the wp_system
324
+		// and we'll check to make sure the CHILD class
325
+		// automatically has the required methods in place.
326
+		// the following filters are for setting all the redirects
327
+		// on DEFAULT WP custom post type actions
328
+		// let's add a hidden input to the post-edit form
329
+		// so we know when we have to trigger our custom redirects!
330
+		// Otherwise the redirects will happen on ALL post saves which wouldn't be good of course!
331
+		add_action('edit_form_after_title', [$this, 'cpt_post_form_hidden_input']);
332
+		// inject our Admin page nav tabs...
333
+		// let's make sure the nav tabs are set if they aren't already
334
+		// if ( empty( $this->_nav_tabs ) ) $this->_set_nav_tabs();
335
+		add_action('edit_form_top', [$this, 'inject_nav_tabs']);
336
+		// modify the post_updated messages array
337
+		add_action('post_updated_messages', [$this, 'post_update_messages'], 10);
338
+		// This basically allows us to change the title of the "publish" metabox area
339
+		// on CPT pages by setting a 'publishbox' value in the $_labels property array in the child class.
340
+		$screen = $this->_cpt_routes[ $this->_req_action ];
341
+		if (! empty($this->_labels['publishbox'])) {
342
+			$this->addMetaBox(
343
+				'submitdiv',
344
+				$this->getPublishBoxTitle(),
345
+				'post_submit_meta_box',
346
+				$screen,
347
+				'side',
348
+				'core'
349
+			);
350
+		}
351
+		// let's add page_templates metabox if this cpt added support for it.
352
+		if ($this->_supports_page_templates($this->_cpt_object->name)) {
353
+			$this->addMetaBox(
354
+				'page_templates',
355
+				esc_html__('Page Template', 'event_espresso'),
356
+				[$this, 'page_template_meta_box'],
357
+				$screen,
358
+				'side'
359
+			);
360
+		}
361
+		// add preview button
362
+		add_filter('get_sample_permalink_html', [PreviewButton::class, 'addButton'], 5, 2);
363
+		// add shortlink button to cpt edit screens.
364
+		//  We can do this as a universal thing BECAUSE, cpts use the same format for shortlinks as posts!
365
+		add_filter('get_sample_permalink_html', [EventShortlinkButton::class, 'addButton'], 10, 2);
366
+		// this is a filter that allows the addition of extra html after the permalink field on the wp post edit-form
367
+		add_filter('get_sample_permalink_html', [TicketSelectorShortcodeButton::class, 'addButton'], 12, 4);
368
+		// insert our own post_stati dropdown
369
+		add_action('post_submitbox_misc_actions', [$this, 'custom_post_stati_dropdown'], 10);
370
+		// This allows adding additional information to the publish post submitbox on the wp post edit form
371
+		if (method_exists($this, 'extra_misc_actions_publish_box')) {
372
+			add_action('post_submitbox_misc_actions', [$this, 'extra_misc_actions_publish_box'], 10);
373
+		}
374
+		// This allows for adding additional stuff after the title field on the wp post edit form.
375
+		// This is also before the wp_editor for post description field.
376
+		if (method_exists($this, 'edit_form_after_title')) {
377
+			add_action('edit_form_after_title', [$this, 'edit_form_after_title'], 10);
378
+		}
379
+		/**
380
+		 * Filtering WP's esc_url to capture urls pointing to core wp routes so they point to our route.
381
+		 */
382
+		add_filter('clean_url', [$this, 'switch_core_wp_urls_with_ours'], 10);
383
+		parent::_load_page_dependencies();
384
+		// notice we are ALSO going to load the pagenow hook set for this route
385
+		// (see _before_page_setup for the reset of the pagenow global ).
386
+		// This is for any plugins that are doing things properly
387
+		// and hooking into the load page hook for core wp cpt routes.
388
+		global $pagenow;
389
+		add_action('load-' . $pagenow, [$this, 'modify_current_screen'], 20);
390
+		do_action('load-' . $pagenow);
391
+		add_action('admin_enqueue_scripts', [$this, 'setup_autosave_hooks'], 30);
392
+		// we route REALLY early.
393
+		try {
394
+			$this->_route_admin_request();
395
+		} catch (EE_Error $e) {
396
+			$e->get_error();
397
+		}
398
+	}
399
+
400
+
401
+	/**
402
+	 * Since we don't want users going to default core wp routes, this will check any wp urls run through the
403
+	 * esc_url() method and if we see a url matching a pattern for our routes, we'll modify it to point to OUR
404
+	 * route instead.
405
+	 *
406
+	 * @param string $good_protocol_url The escaped url.
407
+	 * @return string possibly a new url for our route.
408
+	 */
409
+	public function switch_core_wp_urls_with_ours(string $good_protocol_url): string
410
+	{
411
+		$routes_to_match = [
412
+			0 => [
413
+				'edit.php?post_type=espresso_attendees',
414
+				'admin.php?page=espresso_registrations&action=contact_list',
415
+			],
416
+			1 => [
417
+				'edit.php?post_type=' . $this->_cpt_object->name,
418
+				'admin.php?page=' . $this->_cpt_object->name,
419
+			],
420
+		];
421
+		foreach ($routes_to_match as $route_matches) {
422
+			if (strpos($good_protocol_url, $route_matches[0]) !== false) {
423
+				return str_replace($route_matches[0], $route_matches[1], $good_protocol_url);
424
+			}
425
+		}
426
+		return $good_protocol_url;
427
+	}
428
+
429
+
430
+	/**
431
+	 * Determine whether the current cpt supports page templates or not.
432
+	 *
433
+	 * @param string $cpt_name The cpt slug we're checking on.
434
+	 * @return bool True supported, false not.
435
+	 * @throws InvalidArgumentException
436
+	 * @throws InvalidDataTypeException
437
+	 * @throws InvalidInterfaceException
438
+	 * @since %VER%
439
+	 */
440
+	private function _supports_page_templates(string $cpt_name): bool
441
+	{
442
+		/** @var EventEspresso\core\domain\entities\custom_post_types\CustomPostTypeDefinitions $custom_post_types */
443
+		$custom_post_types = $this->loader->getShared(
444
+			'EventEspresso\core\domain\entities\custom_post_types\CustomPostTypeDefinitions'
445
+		);
446
+		$cpt_args          = $custom_post_types->getDefinitions();
447
+		$cpt_args          = isset($cpt_args[ $cpt_name ]) ? $cpt_args[ $cpt_name ]['args'] : [];
448
+		$cpt_has_support   = ! empty($cpt_args['page_templates']);
449
+
450
+		$post_templates = wp_get_theme()->get_post_templates();
451
+		// if there are $post_templates for this cpt, then we return false for this method because
452
+		// that means we aren't going to load our page template manager and leave that up to the native
453
+		// cpt template manager.
454
+		return ! isset($post_templates[ $cpt_name ]) ? $cpt_has_support : false;
455
+	}
456
+
457
+
458
+	/**
459
+	 * Callback for the page_templates metabox selector.
460
+	 *
461
+	 * @return void
462
+	 * @since %VER%
463
+	 */
464
+	public function page_template_meta_box()
465
+	{
466
+		global $post;
467
+		$template = '';
468
+
469
+		$page_template_count = count(get_page_templates());
470
+		if ($page_template_count) {
471
+			$page_template = get_post_meta($post->ID, '_wp_page_template', true);
472
+			$template      = ! empty($page_template) ? $page_template : '';
473
+		}
474
+		?>
475 475
         <p><strong><?php esc_html_e('Template', 'event_espresso') ?></strong></p>
476 476
         <label class="screen-reader-text" for="page_template">
477 477
             <?php esc_html_e('Page Template', 'event_espresso') ?>
@@ -481,457 +481,457 @@  discard block
 block discarded – undo
481 481
             <?php page_template_dropdown($template); ?>
482 482
         </select>
483 483
         <?php
484
-    }
485
-
486
-
487
-    /**
488
-     * if this post is a draft or scheduled post then we provide a preview button for user to click
489
-     * Method is called from parent and is hooked into the wp 'get_sample_permalink_html' filter.
490
-     *
491
-     * @param string $return    the current html
492
-     * @param int    $id        the post id for the page
493
-     * @return string            The new html string for the permalink area
494
-     * @deprecated 5.0.0.p
495
-     * @see PreviewButton::addButton()
496
-     */
497
-    public function preview_button_html(string $return, int $id): string
498
-    {
499
-        return PreviewButton::addButton($return, $id);
500
-    }
501
-
502
-
503
-    /**
504
-     * add our custom post status dropdown on the wp post page for this cpt
505
-     *
506
-     * @return void
507
-     */
508
-    public function custom_post_stati_dropdown()
509
-    {
510
-        $statuses         = $this->_cpt_model_obj->get_custom_post_statuses();
511
-        $cur_status_label = array_key_exists($this->_cpt_model_obj->status(), $statuses)
512
-            ? $statuses[ $this->_cpt_model_obj->status() ]
513
-            : '';
514
-        $template_args    = [
515
-            'cur_status'            => $this->_cpt_model_obj->status(),
516
-            'statuses'              => $statuses,
517
-            'cur_status_label'      => $cur_status_label,
518
-            'localized_status_save' => sprintf(esc_html__('Save %s', 'event_espresso'), $cur_status_label),
519
-        ];
520
-        // we'll add a trash post status (WP doesn't add one for some reason)
521
-        if ($this->_cpt_model_obj->status() === 'trash') {
522
-            $template_args['cur_status_label'] = esc_html__('Trashed', 'event_espresso');
523
-            $statuses['trash']                 = esc_html__('Trashed', 'event_espresso');
524
-            $template_args['statuses']         = $statuses;
525
-        }
526
-
527
-        $template = EE_ADMIN_TEMPLATE . 'status_dropdown.template.php';
528
-        EEH_Template::display_template($template, $template_args);
529
-    }
530
-
531
-
532
-    public function setup_autosave_hooks()
533
-    {
534
-        $this->_set_autosave_containers();
535
-        $this->_load_autosave_scripts_styles();
536
-    }
537
-
538
-
539
-    /**
540
-     * This is run on all WordPress autosaves AFTER the autosave is complete and sends along a post object (available
541
-     * in request data) containing: post_ID of the saved post autosavenonce for the saved post We'll do the check
542
-     * for the nonce in here, but then this method looks for two things:
543
-     * 1. Execute a method (if exists) matching 'ee_autosave_' and appended with the given route. OR
544
-     * 2. do_actions() for global or class specific actions that have been registered (for plugins/addons not in an
545
-     * EE_Admin_Page class. PLEASE NOTE: Data will be returned using the _return_json() object and so the
546
-     * $_template_args property should be used to hold the $data array.  We're expecting the following things set in
547
-     * template args.
548
-     *    1. $template_args['error'] = IF there is an error you can add the message in here.
549
-     *    2. $template_args['data']['items'] = an array of items that are setup in key index pairs of 'where_values_go'
550
-     *    => 'values_to_add'.  In other words, for the datetime metabox we'll have something like
551
-     *    $this->_template_args['data']['items'] = array(
552
-     *        'event-datetime-ids' => '1,2,3';
553
-     *    );
554
-     *    Keep in mind the following things:
555
-     *    - "where" index is for the input with the id as that string.
556
-     *    - "what" index is what will be used for the value of that input.
557
-     *
558
-     * @return void
559
-     * @throws EE_Error
560
-     */
561
-    public function do_extra_autosave_stuff()
562
-    {
563
-        // next let's check for the autosave nonce (we'll use _verify_nonce )
564
-        $nonce = $this->request->getRequestParam('autosavenonce');
565
-        $this->_verify_nonce($nonce, 'autosave');
566
-        // make sure we define doing autosave (cause WP isn't triggering this we want to make sure we define it)
567
-        if (! defined('DOING_AUTOSAVE')) {
568
-            define('DOING_AUTOSAVE', true);
569
-        }
570
-        // if we made it here then the nonce checked out.  Let's run our methods and actions
571
-        $autosave = "_ee_autosave_$this->_current_view";
572
-        if (method_exists($this, $autosave)) {
573
-            $this->$autosave();
574
-        } else {
575
-            $this->_template_args['success'] = true;
576
-        }
577
-        do_action('AHEE__EE_Admin_Page_CPT__do_extra_autosave_stuff__global_after', $this);
578
-        do_action('AHEE__EE_Admin_Page_CPT__do_extra_autosave_stuff__after_' . get_class($this), $this);
579
-        // now let's return json
580
-        $this->_return_json();
581
-    }
582
-
583
-
584
-    /**
585
-     * This takes care of setting up default routes and pages that utilize the core WP admin pages.
586
-     * Child classes can override the defaults (in cases for adding metaboxes etc.)
587
-     * but take care that you include the defaults here otherwise your core WP admin pages for the cpt won't work!
588
-     *
589
-     * @return void
590
-     * @throws EE_Error
591
-     * @throws ReflectionException
592
-     */
593
-    protected function _extend_page_config_for_cpt()
594
-    {
595
-        // before doing anything we need to make sure this runs ONLY when the loaded page matches the set page_slug
596
-        if ($this->raw_req_page !== $this->page_slug) {
597
-            return;
598
-        }
599
-        // set page routes and page config but ONLY if we're not viewing a custom setup cpt route as defined in _cpt_routes
600
-        if (! empty($this->_cpt_object)) {
601
-            $this->_page_routes = array_merge(
602
-                [
603
-                    'create_new' => [$this, '_create_new_cpt_item'],
604
-                    'edit'       => [$this, '_edit_cpt_item'],
605
-                ],
606
-                $this->_page_routes
607
-            );
608
-            $this->_page_config = array_merge(
609
-                [
610
-                    'create_new' => [
611
-                        'nav'           => [
612
-                            'label' => $this->_cpt_object->labels->add_new_item,
613
-                            'order' => 5,
614
-                        ],
615
-                        'require_nonce' => false,
616
-                    ],
617
-                    'edit'       => [
618
-                        'nav'           => [
619
-                            'label'      => $this->_cpt_object->labels->edit_item,
620
-                            'order'      => 5,
621
-                            'persistent' => false,
622
-                            'url'        => '',
623
-                        ],
624
-                        'require_nonce' => false,
625
-                    ],
626
-                ],
627
-                $this->_page_config
628
-            );
629
-        }
630
-        // load the next section only if this is a matching cpt route as set in the cpt routes array.
631
-        if (! isset($this->_cpt_routes[ $this->_req_action ])) {
632
-            return;
633
-        }
634
-        $this->_cpt_route = true;
635
-        // $this->_cpt_route = isset($this->_cpt_routes[ $this->_req_action ]);
636
-        // add_action('FHEE__EE_Admin_Page___load_page_dependencies__after_load', array( $this, 'modify_current_screen') );
637
-        if (empty($this->_cpt_object)) {
638
-            $msg = sprintf(
639
-                esc_html__(
640
-                    'This page has been set as being related to a registered custom post type, however, the custom post type object could not be retrieved. There are two possible reasons for this:  1. The "%s" does not match a registered post type. or 2. The custom post type is not registered for the "%s" action as indexed in the "$_cpt_routes" property on this class (%s).',
641
-                    'event_espresso'
642
-                ),
643
-                $this->page_slug,
644
-                $this->_req_action,
645
-                get_class($this)
646
-            );
647
-            throw new EE_Error($msg);
648
-        }
649
-        $this->_set_model_object($this->request->getRequestParam('post', 0, DataType::INT));
650
-    }
651
-
652
-
653
-    /**
654
-     * Sets the _cpt_model_object property using what has been set for the _cpt_model_name and a given id.
655
-     *
656
-     * @param int    $id       The id to retrieve the model object for. If empty we set a default object.
657
-     * @param bool   $ignore_route_check
658
-     * @param string $req_type whether the current route is for inserting, updating, or deleting the CPT
659
-     * @throws EE_Error
660
-     * @throws InvalidArgumentException
661
-     * @throws InvalidDataTypeException
662
-     * @throws InvalidInterfaceException
663
-     * @throws ReflectionException
664
-     */
665
-    protected function _set_model_object(int $id = 0, bool $ignore_route_check = false, string $req_type = '')
666
-    {
667
-        $model = null;
668
-        if (
669
-            empty($this->_cpt_model_names)
670
-            || (
671
-                ! $ignore_route_check
672
-                && ! isset($this->_cpt_routes[ $this->_req_action ])
673
-            )
674
-            || (
675
-                $this->_cpt_model_obj instanceof EE_CPT_Base
676
-                && $this->_cpt_model_obj->ID() === $id
677
-            )
678
-        ) {
679
-            // get out cuz we either don't have a model name OR the object has already been set and it has the same id as what has been sent.
680
-            return;
681
-        }
682
-        // if ignore_route_check is true, then get the model name via CustomPostTypeDefinitions
683
-        if ($ignore_route_check) {
684
-            $post_type = get_post_type($id);
685
-            /** @var EventEspresso\core\domain\entities\custom_post_types\CustomPostTypeDefinitions $custom_post_types */
686
-            $custom_post_types = $this->loader->getShared(
687
-                'EventEspresso\core\domain\entities\custom_post_types\CustomPostTypeDefinitions'
688
-            );
689
-            $model_names       = $custom_post_types->getCustomPostTypeModelNames($post_type);
690
-            if (isset($model_names[ $post_type ])) {
691
-                $model = EE_Registry::instance()->load_model($model_names[ $post_type ]);
692
-            }
693
-        } else {
694
-            $model = EE_Registry::instance()->load_model($this->_cpt_model_names[ $this->_req_action ]);
695
-        }
696
-        if ($model instanceof EEM_Base) {
697
-            $this->_cpt_model_obj = ! empty($id) ? $model->get_one_by_ID($id) : $model->create_default_object();
698
-        }
699
-        do_action(
700
-            'AHEE__EE_Admin_Page_CPT__set_model_object__after_set_object',
701
-            $this->_cpt_model_obj,
702
-            $req_type
703
-        );
704
-    }
705
-
706
-
707
-    /**
708
-     * admin_init_global
709
-     * This runs all the code that we want executed within the WP admin_init hook.
710
-     * This method executes for ALL EE Admin pages.
711
-     *
712
-     * @return void
713
-     */
714
-    public function admin_init_global()
715
-    {
716
-        $post_ID = $this->request->getRequestParam('post', 0, DataType::INT);
717
-        // its possible this is a new save so let's catch that instead
718
-        $post_ID        = $this->request->getRequestParam('post_ID', $post_ID, DataType::INT);
719
-        $post           = get_post($post_ID);
720
-        $post_type      = $post instanceof WP_Post ? $post->post_type : false;
721
-        $current_route  = $this->request->getRequestParam('current_route', 'shouldneverwork');
722
-        $route_to_check = $post_type && isset($this->_cpt_routes[ $current_route ])
723
-            ? $this->_cpt_routes[ $current_route ]
724
-            : '';
725
-        add_filter('get_delete_post_link', [$this, 'modify_delete_post_link'], 10, 2);
726
-        add_filter('get_edit_post_link', [$this, 'modify_edit_post_link'], 10, 2);
727
-        if ($post_type === $route_to_check) {
728
-            add_filter('redirect_post_location', [$this, 'cpt_post_location_redirect'], 10, 2);
729
-        }
730
-        // now let's filter redirect if we're on a revision page and the revision is for an event CPT.
731
-        $revision = $this->request->getRequestParam('revision');
732
-        if (! empty($revision)) {
733
-            $action = $this->request->getRequestParam('action');
734
-            // doing a restore?
735
-            if (! empty($action) && $action === 'restore') {
736
-                // get post for revision
737
-                $rev_post   = get_post($revision);
738
-                $rev_parent = get_post($rev_post->post_parent);
739
-                // only do our redirect filter AND our restore revision action if the post_type for the parent is one of our cpts.
740
-                if ($rev_parent && $rev_parent->post_type === $this->page_slug) {
741
-                    add_filter('wp_redirect', [$this, 'revision_redirect'], 10);
742
-                    // restores of revisions
743
-                    add_action('wp_restore_post_revision', [$this, 'restore_revision'], 10, 2);
744
-                }
745
-            }
746
-        }
747
-        // NOTE we ONLY want to run these hooks if we're on the right class for the given post type.  Otherwise we could see some really freaky things happen!
748
-        if ($post_type && $post_type === $route_to_check) {
749
-            // $post_id, $post
750
-            add_action('save_post', [$this, 'insert_update'], 10, 3);
751
-            // $post_id
752
-            add_action('trashed_post', [$this, 'before_trash_cpt_item'], 10);
753
-            add_action('trashed_post', [$this, 'dont_permanently_delete_ee_cpts'], 10);
754
-            add_action('untrashed_post', [$this, 'before_restore_cpt_item'], 10);
755
-            add_action('after_delete_post', [$this, 'before_delete_cpt_item'], 10);
756
-        }
757
-    }
758
-
759
-
760
-    /**
761
-     * Callback for the WordPress trashed_post hook.
762
-     * Execute some basic checks before calling the trash_cpt_item declared in the child class.
763
-     *
764
-     * @param int $post_id
765
-     * @throws EE_Error
766
-     * @throws ReflectionException
767
-     */
768
-    public function before_trash_cpt_item(int $post_id)
769
-    {
770
-        $this->_set_model_object($post_id, true, 'trash');
771
-        // if our cpt object isn't existent then get out immediately.
772
-        if (! $this->_cpt_model_obj instanceof EE_CPT_Base || $this->_cpt_model_obj->ID() !== $post_id) {
773
-            return;
774
-        }
775
-        $this->trash_cpt_item($post_id);
776
-    }
777
-
778
-
779
-    /**
780
-     * Callback for the WordPress untrashed_post hook.
781
-     * Execute some basic checks before calling the restore_cpt_method in the child class.
782
-     *
783
-     * @param $post_id
784
-     * @throws EE_Error
785
-     * @throws ReflectionException
786
-     */
787
-    public function before_restore_cpt_item($post_id)
788
-    {
789
-        $this->_set_model_object($post_id, true, 'restore');
790
-        // if our cpt object isn't existent then get out immediately.
791
-        if (! $this->_cpt_model_obj instanceof EE_CPT_Base || $this->_cpt_model_obj->ID() !== $post_id) {
792
-            return;
793
-        }
794
-        $this->restore_cpt_item($post_id);
795
-    }
796
-
797
-
798
-    /**
799
-     * Callback for the WordPress after_delete_post hook.
800
-     * Execute some basic checks before calling the delete_cpt_item method in the child class.
801
-     *
802
-     * @param $post_id
803
-     * @throws EE_Error
804
-     * @throws ReflectionException
805
-     */
806
-    public function before_delete_cpt_item($post_id)
807
-    {
808
-        $this->_set_model_object($post_id, true, 'delete');
809
-        // if our cpt object isn't existent then get out immediately.
810
-        if (! $this->_cpt_model_obj instanceof EE_CPT_Base || $this->_cpt_model_obj->ID() !== $post_id) {
811
-            return;
812
-        }
813
-        $this->delete_cpt_item($post_id);
814
-    }
815
-
816
-
817
-    /**
818
-     * This simply verifies if the cpt_model_object is instantiated for the given page and throws an error message
819
-     * accordingly.
820
-     *
821
-     * @return void
822
-     * @throws EE_Error
823
-     * @throws ReflectionException
824
-     */
825
-    public function verify_cpt_object()
826
-    {
827
-        $label = ! empty($this->_cpt_object) ? $this->_cpt_object->labels->singular_name : $this->page_label;
828
-        // verify event object
829
-        if (! $this->_cpt_model_obj instanceof EE_CPT_Base) {
830
-            throw new EE_Error(
831
-                sprintf(
832
-                    esc_html__(
833
-                        'Something has gone wrong with the page load because we are unable to set up the object for the %1$s.  This usually happens when the given id for the page route is NOT for the correct custom post type for this page',
834
-                        'event_espresso'
835
-                    ),
836
-                    $label
837
-                )
838
-            );
839
-        }
840
-        // if auto-draft then throw an error
841
-        if ($this->_cpt_model_obj->get('status') === 'auto-draft') {
842
-            EE_Error::overwrite_errors();
843
-            EE_Error::add_error(
844
-                sprintf(
845
-                    esc_html__(
846
-                        'This %1$s was saved without a title, description, or excerpt which means that none of the extra details you added were saved properly.  All autodrafts will show up in the "draft" view of your event list table.  You can delete them from there. Please click the "Add %1$s" button to refresh and restart.',
847
-                        'event_espresso'
848
-                    ),
849
-                    $label
850
-                ),
851
-                __FILE__,
852
-                __FUNCTION__,
853
-                __LINE__
854
-            );
855
-        }
856
-    }
857
-
858
-
859
-    /**
860
-     * admin_footer_scripts_global
861
-     * Anything triggered by the 'admin_print_footer_scripts' WP hook should be put in here. This particular method
862
-     * will apply on ALL EE_Admin pages.
863
-     *
864
-     * @return void
865
-     */
866
-    public function admin_footer_scripts_global()
867
-    {
868
-        $this->_add_admin_page_ajax_loading_img();
869
-        $this->_add_admin_page_overlay();
870
-    }
871
-
872
-
873
-    /**
874
-     * add in any global scripts for cpt routes
875
-     *
876
-     * @return void
877
-     */
878
-    public function load_global_scripts_styles()
879
-    {
880
-        parent::load_global_scripts_styles();
881
-        if ($this->_cpt_model_obj instanceof EE_CPT_Base) {
882
-            // setup custom post status object for localize script but only if we've got a cpt object
883
-            $statuses = $this->_cpt_model_obj->get_custom_post_statuses();
884
-            if (! empty($statuses)) {
885
-                // get ALL statuses!
886
-                $statuses = $this->_cpt_model_obj->get_all_post_statuses();
887
-                // setup object
888
-                $ee_cpt_statuses = [];
889
-                foreach ($statuses as $status => $label) {
890
-                    $ee_cpt_statuses[ $status ] = [
891
-                        'label'      => $label,
892
-                        'save_label' => sprintf(
893
-                            wp_strip_all_tags(__('Save as %s', 'event_espresso')),
894
-                            $label
895
-                        ),
896
-                    ];
897
-                }
898
-                wp_localize_script('ee_admin_js', 'eeCPTstatuses', $ee_cpt_statuses);
899
-            }
900
-        }
901
-    }
902
-
903
-
904
-    /**
905
-     * This is a wrapper for the insert/update routes for cpt items so we can add things that are common to ALL
906
-     * insert/updates
907
-     *
908
-     * @param int     $post_id ID of post being updated
909
-     * @param WP_Post $post    Post object from WP
910
-     * @param bool    $update  Whether this is an update or a new save.
911
-     * @return void
912
-     * @throws EE_Error
913
-     * @throws ReflectionException
914
-     */
915
-    public function insert_update(int $post_id, WP_Post $post, bool $update)
916
-    {
917
-        // make sure that if this is a revision OR trash action that we don't do any updates!
918
-        $action = $this->request->getRequestParam('action');
919
-        if ($action === 'restore' || $action === 'trash') {
920
-            return;
921
-        }
922
-        $this->_set_model_object($post_id, true, 'insert_update');
923
-        // if our cpt object is not instantiated and its NOT the same post_id as what is triggering this callback, then exit.
924
-        if (
925
-            $update
926
-            && (
927
-                ! $this->_cpt_model_obj instanceof EE_CPT_Base
928
-                || $this->_cpt_model_obj->ID() !== $post_id
929
-            )
930
-        ) {
931
-            return;
932
-        }
933
-        // check for autosave and update our req_data property accordingly.
934
-        /*if ( defined('DOING_AUTOSAVE') && DOING_AUTOSAVE && isset( $this->_req_data['ee_autosave_data'] ) ) {
484
+	}
485
+
486
+
487
+	/**
488
+	 * if this post is a draft or scheduled post then we provide a preview button for user to click
489
+	 * Method is called from parent and is hooked into the wp 'get_sample_permalink_html' filter.
490
+	 *
491
+	 * @param string $return    the current html
492
+	 * @param int    $id        the post id for the page
493
+	 * @return string            The new html string for the permalink area
494
+	 * @deprecated 5.0.0.p
495
+	 * @see PreviewButton::addButton()
496
+	 */
497
+	public function preview_button_html(string $return, int $id): string
498
+	{
499
+		return PreviewButton::addButton($return, $id);
500
+	}
501
+
502
+
503
+	/**
504
+	 * add our custom post status dropdown on the wp post page for this cpt
505
+	 *
506
+	 * @return void
507
+	 */
508
+	public function custom_post_stati_dropdown()
509
+	{
510
+		$statuses         = $this->_cpt_model_obj->get_custom_post_statuses();
511
+		$cur_status_label = array_key_exists($this->_cpt_model_obj->status(), $statuses)
512
+			? $statuses[ $this->_cpt_model_obj->status() ]
513
+			: '';
514
+		$template_args    = [
515
+			'cur_status'            => $this->_cpt_model_obj->status(),
516
+			'statuses'              => $statuses,
517
+			'cur_status_label'      => $cur_status_label,
518
+			'localized_status_save' => sprintf(esc_html__('Save %s', 'event_espresso'), $cur_status_label),
519
+		];
520
+		// we'll add a trash post status (WP doesn't add one for some reason)
521
+		if ($this->_cpt_model_obj->status() === 'trash') {
522
+			$template_args['cur_status_label'] = esc_html__('Trashed', 'event_espresso');
523
+			$statuses['trash']                 = esc_html__('Trashed', 'event_espresso');
524
+			$template_args['statuses']         = $statuses;
525
+		}
526
+
527
+		$template = EE_ADMIN_TEMPLATE . 'status_dropdown.template.php';
528
+		EEH_Template::display_template($template, $template_args);
529
+	}
530
+
531
+
532
+	public function setup_autosave_hooks()
533
+	{
534
+		$this->_set_autosave_containers();
535
+		$this->_load_autosave_scripts_styles();
536
+	}
537
+
538
+
539
+	/**
540
+	 * This is run on all WordPress autosaves AFTER the autosave is complete and sends along a post object (available
541
+	 * in request data) containing: post_ID of the saved post autosavenonce for the saved post We'll do the check
542
+	 * for the nonce in here, but then this method looks for two things:
543
+	 * 1. Execute a method (if exists) matching 'ee_autosave_' and appended with the given route. OR
544
+	 * 2. do_actions() for global or class specific actions that have been registered (for plugins/addons not in an
545
+	 * EE_Admin_Page class. PLEASE NOTE: Data will be returned using the _return_json() object and so the
546
+	 * $_template_args property should be used to hold the $data array.  We're expecting the following things set in
547
+	 * template args.
548
+	 *    1. $template_args['error'] = IF there is an error you can add the message in here.
549
+	 *    2. $template_args['data']['items'] = an array of items that are setup in key index pairs of 'where_values_go'
550
+	 *    => 'values_to_add'.  In other words, for the datetime metabox we'll have something like
551
+	 *    $this->_template_args['data']['items'] = array(
552
+	 *        'event-datetime-ids' => '1,2,3';
553
+	 *    );
554
+	 *    Keep in mind the following things:
555
+	 *    - "where" index is for the input with the id as that string.
556
+	 *    - "what" index is what will be used for the value of that input.
557
+	 *
558
+	 * @return void
559
+	 * @throws EE_Error
560
+	 */
561
+	public function do_extra_autosave_stuff()
562
+	{
563
+		// next let's check for the autosave nonce (we'll use _verify_nonce )
564
+		$nonce = $this->request->getRequestParam('autosavenonce');
565
+		$this->_verify_nonce($nonce, 'autosave');
566
+		// make sure we define doing autosave (cause WP isn't triggering this we want to make sure we define it)
567
+		if (! defined('DOING_AUTOSAVE')) {
568
+			define('DOING_AUTOSAVE', true);
569
+		}
570
+		// if we made it here then the nonce checked out.  Let's run our methods and actions
571
+		$autosave = "_ee_autosave_$this->_current_view";
572
+		if (method_exists($this, $autosave)) {
573
+			$this->$autosave();
574
+		} else {
575
+			$this->_template_args['success'] = true;
576
+		}
577
+		do_action('AHEE__EE_Admin_Page_CPT__do_extra_autosave_stuff__global_after', $this);
578
+		do_action('AHEE__EE_Admin_Page_CPT__do_extra_autosave_stuff__after_' . get_class($this), $this);
579
+		// now let's return json
580
+		$this->_return_json();
581
+	}
582
+
583
+
584
+	/**
585
+	 * This takes care of setting up default routes and pages that utilize the core WP admin pages.
586
+	 * Child classes can override the defaults (in cases for adding metaboxes etc.)
587
+	 * but take care that you include the defaults here otherwise your core WP admin pages for the cpt won't work!
588
+	 *
589
+	 * @return void
590
+	 * @throws EE_Error
591
+	 * @throws ReflectionException
592
+	 */
593
+	protected function _extend_page_config_for_cpt()
594
+	{
595
+		// before doing anything we need to make sure this runs ONLY when the loaded page matches the set page_slug
596
+		if ($this->raw_req_page !== $this->page_slug) {
597
+			return;
598
+		}
599
+		// set page routes and page config but ONLY if we're not viewing a custom setup cpt route as defined in _cpt_routes
600
+		if (! empty($this->_cpt_object)) {
601
+			$this->_page_routes = array_merge(
602
+				[
603
+					'create_new' => [$this, '_create_new_cpt_item'],
604
+					'edit'       => [$this, '_edit_cpt_item'],
605
+				],
606
+				$this->_page_routes
607
+			);
608
+			$this->_page_config = array_merge(
609
+				[
610
+					'create_new' => [
611
+						'nav'           => [
612
+							'label' => $this->_cpt_object->labels->add_new_item,
613
+							'order' => 5,
614
+						],
615
+						'require_nonce' => false,
616
+					],
617
+					'edit'       => [
618
+						'nav'           => [
619
+							'label'      => $this->_cpt_object->labels->edit_item,
620
+							'order'      => 5,
621
+							'persistent' => false,
622
+							'url'        => '',
623
+						],
624
+						'require_nonce' => false,
625
+					],
626
+				],
627
+				$this->_page_config
628
+			);
629
+		}
630
+		// load the next section only if this is a matching cpt route as set in the cpt routes array.
631
+		if (! isset($this->_cpt_routes[ $this->_req_action ])) {
632
+			return;
633
+		}
634
+		$this->_cpt_route = true;
635
+		// $this->_cpt_route = isset($this->_cpt_routes[ $this->_req_action ]);
636
+		// add_action('FHEE__EE_Admin_Page___load_page_dependencies__after_load', array( $this, 'modify_current_screen') );
637
+		if (empty($this->_cpt_object)) {
638
+			$msg = sprintf(
639
+				esc_html__(
640
+					'This page has been set as being related to a registered custom post type, however, the custom post type object could not be retrieved. There are two possible reasons for this:  1. The "%s" does not match a registered post type. or 2. The custom post type is not registered for the "%s" action as indexed in the "$_cpt_routes" property on this class (%s).',
641
+					'event_espresso'
642
+				),
643
+				$this->page_slug,
644
+				$this->_req_action,
645
+				get_class($this)
646
+			);
647
+			throw new EE_Error($msg);
648
+		}
649
+		$this->_set_model_object($this->request->getRequestParam('post', 0, DataType::INT));
650
+	}
651
+
652
+
653
+	/**
654
+	 * Sets the _cpt_model_object property using what has been set for the _cpt_model_name and a given id.
655
+	 *
656
+	 * @param int    $id       The id to retrieve the model object for. If empty we set a default object.
657
+	 * @param bool   $ignore_route_check
658
+	 * @param string $req_type whether the current route is for inserting, updating, or deleting the CPT
659
+	 * @throws EE_Error
660
+	 * @throws InvalidArgumentException
661
+	 * @throws InvalidDataTypeException
662
+	 * @throws InvalidInterfaceException
663
+	 * @throws ReflectionException
664
+	 */
665
+	protected function _set_model_object(int $id = 0, bool $ignore_route_check = false, string $req_type = '')
666
+	{
667
+		$model = null;
668
+		if (
669
+			empty($this->_cpt_model_names)
670
+			|| (
671
+				! $ignore_route_check
672
+				&& ! isset($this->_cpt_routes[ $this->_req_action ])
673
+			)
674
+			|| (
675
+				$this->_cpt_model_obj instanceof EE_CPT_Base
676
+				&& $this->_cpt_model_obj->ID() === $id
677
+			)
678
+		) {
679
+			// get out cuz we either don't have a model name OR the object has already been set and it has the same id as what has been sent.
680
+			return;
681
+		}
682
+		// if ignore_route_check is true, then get the model name via CustomPostTypeDefinitions
683
+		if ($ignore_route_check) {
684
+			$post_type = get_post_type($id);
685
+			/** @var EventEspresso\core\domain\entities\custom_post_types\CustomPostTypeDefinitions $custom_post_types */
686
+			$custom_post_types = $this->loader->getShared(
687
+				'EventEspresso\core\domain\entities\custom_post_types\CustomPostTypeDefinitions'
688
+			);
689
+			$model_names       = $custom_post_types->getCustomPostTypeModelNames($post_type);
690
+			if (isset($model_names[ $post_type ])) {
691
+				$model = EE_Registry::instance()->load_model($model_names[ $post_type ]);
692
+			}
693
+		} else {
694
+			$model = EE_Registry::instance()->load_model($this->_cpt_model_names[ $this->_req_action ]);
695
+		}
696
+		if ($model instanceof EEM_Base) {
697
+			$this->_cpt_model_obj = ! empty($id) ? $model->get_one_by_ID($id) : $model->create_default_object();
698
+		}
699
+		do_action(
700
+			'AHEE__EE_Admin_Page_CPT__set_model_object__after_set_object',
701
+			$this->_cpt_model_obj,
702
+			$req_type
703
+		);
704
+	}
705
+
706
+
707
+	/**
708
+	 * admin_init_global
709
+	 * This runs all the code that we want executed within the WP admin_init hook.
710
+	 * This method executes for ALL EE Admin pages.
711
+	 *
712
+	 * @return void
713
+	 */
714
+	public function admin_init_global()
715
+	{
716
+		$post_ID = $this->request->getRequestParam('post', 0, DataType::INT);
717
+		// its possible this is a new save so let's catch that instead
718
+		$post_ID        = $this->request->getRequestParam('post_ID', $post_ID, DataType::INT);
719
+		$post           = get_post($post_ID);
720
+		$post_type      = $post instanceof WP_Post ? $post->post_type : false;
721
+		$current_route  = $this->request->getRequestParam('current_route', 'shouldneverwork');
722
+		$route_to_check = $post_type && isset($this->_cpt_routes[ $current_route ])
723
+			? $this->_cpt_routes[ $current_route ]
724
+			: '';
725
+		add_filter('get_delete_post_link', [$this, 'modify_delete_post_link'], 10, 2);
726
+		add_filter('get_edit_post_link', [$this, 'modify_edit_post_link'], 10, 2);
727
+		if ($post_type === $route_to_check) {
728
+			add_filter('redirect_post_location', [$this, 'cpt_post_location_redirect'], 10, 2);
729
+		}
730
+		// now let's filter redirect if we're on a revision page and the revision is for an event CPT.
731
+		$revision = $this->request->getRequestParam('revision');
732
+		if (! empty($revision)) {
733
+			$action = $this->request->getRequestParam('action');
734
+			// doing a restore?
735
+			if (! empty($action) && $action === 'restore') {
736
+				// get post for revision
737
+				$rev_post   = get_post($revision);
738
+				$rev_parent = get_post($rev_post->post_parent);
739
+				// only do our redirect filter AND our restore revision action if the post_type for the parent is one of our cpts.
740
+				if ($rev_parent && $rev_parent->post_type === $this->page_slug) {
741
+					add_filter('wp_redirect', [$this, 'revision_redirect'], 10);
742
+					// restores of revisions
743
+					add_action('wp_restore_post_revision', [$this, 'restore_revision'], 10, 2);
744
+				}
745
+			}
746
+		}
747
+		// NOTE we ONLY want to run these hooks if we're on the right class for the given post type.  Otherwise we could see some really freaky things happen!
748
+		if ($post_type && $post_type === $route_to_check) {
749
+			// $post_id, $post
750
+			add_action('save_post', [$this, 'insert_update'], 10, 3);
751
+			// $post_id
752
+			add_action('trashed_post', [$this, 'before_trash_cpt_item'], 10);
753
+			add_action('trashed_post', [$this, 'dont_permanently_delete_ee_cpts'], 10);
754
+			add_action('untrashed_post', [$this, 'before_restore_cpt_item'], 10);
755
+			add_action('after_delete_post', [$this, 'before_delete_cpt_item'], 10);
756
+		}
757
+	}
758
+
759
+
760
+	/**
761
+	 * Callback for the WordPress trashed_post hook.
762
+	 * Execute some basic checks before calling the trash_cpt_item declared in the child class.
763
+	 *
764
+	 * @param int $post_id
765
+	 * @throws EE_Error
766
+	 * @throws ReflectionException
767
+	 */
768
+	public function before_trash_cpt_item(int $post_id)
769
+	{
770
+		$this->_set_model_object($post_id, true, 'trash');
771
+		// if our cpt object isn't existent then get out immediately.
772
+		if (! $this->_cpt_model_obj instanceof EE_CPT_Base || $this->_cpt_model_obj->ID() !== $post_id) {
773
+			return;
774
+		}
775
+		$this->trash_cpt_item($post_id);
776
+	}
777
+
778
+
779
+	/**
780
+	 * Callback for the WordPress untrashed_post hook.
781
+	 * Execute some basic checks before calling the restore_cpt_method in the child class.
782
+	 *
783
+	 * @param $post_id
784
+	 * @throws EE_Error
785
+	 * @throws ReflectionException
786
+	 */
787
+	public function before_restore_cpt_item($post_id)
788
+	{
789
+		$this->_set_model_object($post_id, true, 'restore');
790
+		// if our cpt object isn't existent then get out immediately.
791
+		if (! $this->_cpt_model_obj instanceof EE_CPT_Base || $this->_cpt_model_obj->ID() !== $post_id) {
792
+			return;
793
+		}
794
+		$this->restore_cpt_item($post_id);
795
+	}
796
+
797
+
798
+	/**
799
+	 * Callback for the WordPress after_delete_post hook.
800
+	 * Execute some basic checks before calling the delete_cpt_item method in the child class.
801
+	 *
802
+	 * @param $post_id
803
+	 * @throws EE_Error
804
+	 * @throws ReflectionException
805
+	 */
806
+	public function before_delete_cpt_item($post_id)
807
+	{
808
+		$this->_set_model_object($post_id, true, 'delete');
809
+		// if our cpt object isn't existent then get out immediately.
810
+		if (! $this->_cpt_model_obj instanceof EE_CPT_Base || $this->_cpt_model_obj->ID() !== $post_id) {
811
+			return;
812
+		}
813
+		$this->delete_cpt_item($post_id);
814
+	}
815
+
816
+
817
+	/**
818
+	 * This simply verifies if the cpt_model_object is instantiated for the given page and throws an error message
819
+	 * accordingly.
820
+	 *
821
+	 * @return void
822
+	 * @throws EE_Error
823
+	 * @throws ReflectionException
824
+	 */
825
+	public function verify_cpt_object()
826
+	{
827
+		$label = ! empty($this->_cpt_object) ? $this->_cpt_object->labels->singular_name : $this->page_label;
828
+		// verify event object
829
+		if (! $this->_cpt_model_obj instanceof EE_CPT_Base) {
830
+			throw new EE_Error(
831
+				sprintf(
832
+					esc_html__(
833
+						'Something has gone wrong with the page load because we are unable to set up the object for the %1$s.  This usually happens when the given id for the page route is NOT for the correct custom post type for this page',
834
+						'event_espresso'
835
+					),
836
+					$label
837
+				)
838
+			);
839
+		}
840
+		// if auto-draft then throw an error
841
+		if ($this->_cpt_model_obj->get('status') === 'auto-draft') {
842
+			EE_Error::overwrite_errors();
843
+			EE_Error::add_error(
844
+				sprintf(
845
+					esc_html__(
846
+						'This %1$s was saved without a title, description, or excerpt which means that none of the extra details you added were saved properly.  All autodrafts will show up in the "draft" view of your event list table.  You can delete them from there. Please click the "Add %1$s" button to refresh and restart.',
847
+						'event_espresso'
848
+					),
849
+					$label
850
+				),
851
+				__FILE__,
852
+				__FUNCTION__,
853
+				__LINE__
854
+			);
855
+		}
856
+	}
857
+
858
+
859
+	/**
860
+	 * admin_footer_scripts_global
861
+	 * Anything triggered by the 'admin_print_footer_scripts' WP hook should be put in here. This particular method
862
+	 * will apply on ALL EE_Admin pages.
863
+	 *
864
+	 * @return void
865
+	 */
866
+	public function admin_footer_scripts_global()
867
+	{
868
+		$this->_add_admin_page_ajax_loading_img();
869
+		$this->_add_admin_page_overlay();
870
+	}
871
+
872
+
873
+	/**
874
+	 * add in any global scripts for cpt routes
875
+	 *
876
+	 * @return void
877
+	 */
878
+	public function load_global_scripts_styles()
879
+	{
880
+		parent::load_global_scripts_styles();
881
+		if ($this->_cpt_model_obj instanceof EE_CPT_Base) {
882
+			// setup custom post status object for localize script but only if we've got a cpt object
883
+			$statuses = $this->_cpt_model_obj->get_custom_post_statuses();
884
+			if (! empty($statuses)) {
885
+				// get ALL statuses!
886
+				$statuses = $this->_cpt_model_obj->get_all_post_statuses();
887
+				// setup object
888
+				$ee_cpt_statuses = [];
889
+				foreach ($statuses as $status => $label) {
890
+					$ee_cpt_statuses[ $status ] = [
891
+						'label'      => $label,
892
+						'save_label' => sprintf(
893
+							wp_strip_all_tags(__('Save as %s', 'event_espresso')),
894
+							$label
895
+						),
896
+					];
897
+				}
898
+				wp_localize_script('ee_admin_js', 'eeCPTstatuses', $ee_cpt_statuses);
899
+			}
900
+		}
901
+	}
902
+
903
+
904
+	/**
905
+	 * This is a wrapper for the insert/update routes for cpt items so we can add things that are common to ALL
906
+	 * insert/updates
907
+	 *
908
+	 * @param int     $post_id ID of post being updated
909
+	 * @param WP_Post $post    Post object from WP
910
+	 * @param bool    $update  Whether this is an update or a new save.
911
+	 * @return void
912
+	 * @throws EE_Error
913
+	 * @throws ReflectionException
914
+	 */
915
+	public function insert_update(int $post_id, WP_Post $post, bool $update)
916
+	{
917
+		// make sure that if this is a revision OR trash action that we don't do any updates!
918
+		$action = $this->request->getRequestParam('action');
919
+		if ($action === 'restore' || $action === 'trash') {
920
+			return;
921
+		}
922
+		$this->_set_model_object($post_id, true, 'insert_update');
923
+		// if our cpt object is not instantiated and its NOT the same post_id as what is triggering this callback, then exit.
924
+		if (
925
+			$update
926
+			&& (
927
+				! $this->_cpt_model_obj instanceof EE_CPT_Base
928
+				|| $this->_cpt_model_obj->ID() !== $post_id
929
+			)
930
+		) {
931
+			return;
932
+		}
933
+		// check for autosave and update our req_data property accordingly.
934
+		/*if ( defined('DOING_AUTOSAVE') && DOING_AUTOSAVE && isset( $this->_req_data['ee_autosave_data'] ) ) {
935 935
             foreach( (array) $this->_req_data['ee_autosave_data'] as $id => $values ) {
936 936
 
937 937
                 foreach ( (array) $values as $key => $value ) {
@@ -941,548 +941,548 @@  discard block
 block discarded – undo
941 941
 
942 942
         }/**/ // TODO reactivate after autosave is implemented in 4.2
943 943
 
944
-        // take care of updating any selected page_template IF this cpt supports it.
945
-
946
-        $page_template = $this->request->getRequestParam('page_template');
947
-        if ($this->_supports_page_templates($post->post_type) && ! empty($page_template)) {
948
-            // wp version aware.
949
-            if (RecommendedVersions::compareWordPressVersion('4.7')) {
950
-                $page_templates = wp_get_theme()->get_page_templates();
951
-            } else {
952
-                $post->page_template = $page_template;
953
-                $page_templates      = wp_get_theme()->get_page_templates($post);
954
-            }
955
-            if ($page_template !== 'default' && ! isset($page_templates[ $page_template ])) {
956
-                EE_Error::add_error(
957
-                    esc_html__('Invalid Page Template.', 'event_espresso'),
958
-                    __FILE__,
959
-                    __FUNCTION__,
960
-                    __LINE__
961
-                );
962
-            } else {
963
-                update_post_meta($post_id, '_wp_page_template', $page_template);
964
-            }
965
-        }
966
-        if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
967
-            return;
968
-        } //TODO we'll remove this after reimplementing autosave in 4.2
969
-        $this->_insert_update_cpt_item($post_id, $post);
970
-    }
971
-
972
-
973
-    /**
974
-     * This hooks into the wp_trash_post() function and removes the `_wp_trash_meta_status` and `_wp_trash_meta_time`
975
-     * post meta IF the trashed post is one of our CPT's - note this method should only be called with our cpt routes
976
-     * so we don't have to check for our CPT.
977
-     *
978
-     * @param int $post_id ID of the post
979
-     * @return void
980
-     */
981
-    public function dont_permanently_delete_ee_cpts(int $post_id)
982
-    {
983
-        // only do this if we're actually processing one of our CPTs
984
-        // if our cpt object isn't existent then get out immediately.
985
-        if (! $this->_cpt_model_obj instanceof EE_CPT_Base) {
986
-            return;
987
-        }
988
-        delete_post_meta($post_id, '_wp_trash_meta_status');
989
-        delete_post_meta($post_id, '_wp_trash_meta_time');
990
-        // our cpts may have comments so let's take care of that too
991
-        delete_post_meta($post_id, '_wp_trash_meta_comments_status');
992
-    }
993
-
994
-
995
-    /**
996
-     * This is a wrapper for the restore_cpt_revision route for cpt items so we can make sure that when a revision is
997
-     * triggered that we restore related items.  In order to work cpt classes MUST have a restore_cpt_revision method
998
-     * in them. We also have our OWN action in here so addons can hook into the restore process easily.
999
-     *
1000
-     * @param int $post_id     ID of cpt item
1001
-     * @param int $revision_id ID of revision being restored
1002
-     * @return void
1003
-     */
1004
-    public function restore_revision(int $post_id, int $revision_id)
1005
-    {
1006
-        $this->_restore_cpt_item($post_id, $revision_id);
1007
-        // global action
1008
-        do_action('AHEE_EE_Admin_Page_CPT__restore_revision', $post_id, $revision_id);
1009
-        // class specific action so you can limit hooking into a specific page.
1010
-        do_action('AHEE_EE_Admin_Page_CPT_' . get_class($this) . '__restore_revision', $post_id, $revision_id);
1011
-    }
1012
-
1013
-
1014
-    /**
1015
-     * @param int $post_id     ID of cpt item
1016
-     * @param int $revision_id ID of revision for item
1017
-     * @return void
1018
-     * @see restore_revision() for details
1019
-     */
1020
-    abstract protected function _restore_cpt_item(int $post_id, int $revision_id);
1021
-
1022
-
1023
-    /**
1024
-     * Execution of this method is added to the end of the load_page_dependencies method in the parent
1025
-     * so that we can fix a bug where default core metaboxes were not being called in the sidebar.
1026
-     * To fix we have to reset the current_screen using the page_slug
1027
-     * (which is identical - or should be - to our registered_post_type id.)
1028
-     * Also, since the core WP file loads the admin_header.php for WP
1029
-     * (and there are a bunch of other things edit-form-advanced.php loads that need to happen really early)
1030
-     * we need to load it NOW, hence our _route_admin_request in here. (Otherwise screen options won't be set).
1031
-     *
1032
-     * @return void
1033
-     * @throws EE_Error
1034
-     * @throws ReflectionException
1035
-     */
1036
-    public function modify_current_screen()
1037
-    {
1038
-        // ONLY do this if the current page_route IS a cpt route
1039
-        if (! $this->_cpt_route) {
1040
-            return;
1041
-        }
1042
-        // routing things REALLY early b/c this is a cpt admin page
1043
-        set_current_screen($this->_cpt_routes[ $this->_req_action ]);
1044
-        $this->_current_screen       = get_current_screen();
1045
-        $this->_current_screen->base = 'event-espresso';
1046
-        $this->_add_help_tabs(); // we make sure we add any help tabs back in!
1047
-        /*try {
944
+		// take care of updating any selected page_template IF this cpt supports it.
945
+
946
+		$page_template = $this->request->getRequestParam('page_template');
947
+		if ($this->_supports_page_templates($post->post_type) && ! empty($page_template)) {
948
+			// wp version aware.
949
+			if (RecommendedVersions::compareWordPressVersion('4.7')) {
950
+				$page_templates = wp_get_theme()->get_page_templates();
951
+			} else {
952
+				$post->page_template = $page_template;
953
+				$page_templates      = wp_get_theme()->get_page_templates($post);
954
+			}
955
+			if ($page_template !== 'default' && ! isset($page_templates[ $page_template ])) {
956
+				EE_Error::add_error(
957
+					esc_html__('Invalid Page Template.', 'event_espresso'),
958
+					__FILE__,
959
+					__FUNCTION__,
960
+					__LINE__
961
+				);
962
+			} else {
963
+				update_post_meta($post_id, '_wp_page_template', $page_template);
964
+			}
965
+		}
966
+		if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
967
+			return;
968
+		} //TODO we'll remove this after reimplementing autosave in 4.2
969
+		$this->_insert_update_cpt_item($post_id, $post);
970
+	}
971
+
972
+
973
+	/**
974
+	 * This hooks into the wp_trash_post() function and removes the `_wp_trash_meta_status` and `_wp_trash_meta_time`
975
+	 * post meta IF the trashed post is one of our CPT's - note this method should only be called with our cpt routes
976
+	 * so we don't have to check for our CPT.
977
+	 *
978
+	 * @param int $post_id ID of the post
979
+	 * @return void
980
+	 */
981
+	public function dont_permanently_delete_ee_cpts(int $post_id)
982
+	{
983
+		// only do this if we're actually processing one of our CPTs
984
+		// if our cpt object isn't existent then get out immediately.
985
+		if (! $this->_cpt_model_obj instanceof EE_CPT_Base) {
986
+			return;
987
+		}
988
+		delete_post_meta($post_id, '_wp_trash_meta_status');
989
+		delete_post_meta($post_id, '_wp_trash_meta_time');
990
+		// our cpts may have comments so let's take care of that too
991
+		delete_post_meta($post_id, '_wp_trash_meta_comments_status');
992
+	}
993
+
994
+
995
+	/**
996
+	 * This is a wrapper for the restore_cpt_revision route for cpt items so we can make sure that when a revision is
997
+	 * triggered that we restore related items.  In order to work cpt classes MUST have a restore_cpt_revision method
998
+	 * in them. We also have our OWN action in here so addons can hook into the restore process easily.
999
+	 *
1000
+	 * @param int $post_id     ID of cpt item
1001
+	 * @param int $revision_id ID of revision being restored
1002
+	 * @return void
1003
+	 */
1004
+	public function restore_revision(int $post_id, int $revision_id)
1005
+	{
1006
+		$this->_restore_cpt_item($post_id, $revision_id);
1007
+		// global action
1008
+		do_action('AHEE_EE_Admin_Page_CPT__restore_revision', $post_id, $revision_id);
1009
+		// class specific action so you can limit hooking into a specific page.
1010
+		do_action('AHEE_EE_Admin_Page_CPT_' . get_class($this) . '__restore_revision', $post_id, $revision_id);
1011
+	}
1012
+
1013
+
1014
+	/**
1015
+	 * @param int $post_id     ID of cpt item
1016
+	 * @param int $revision_id ID of revision for item
1017
+	 * @return void
1018
+	 * @see restore_revision() for details
1019
+	 */
1020
+	abstract protected function _restore_cpt_item(int $post_id, int $revision_id);
1021
+
1022
+
1023
+	/**
1024
+	 * Execution of this method is added to the end of the load_page_dependencies method in the parent
1025
+	 * so that we can fix a bug where default core metaboxes were not being called in the sidebar.
1026
+	 * To fix we have to reset the current_screen using the page_slug
1027
+	 * (which is identical - or should be - to our registered_post_type id.)
1028
+	 * Also, since the core WP file loads the admin_header.php for WP
1029
+	 * (and there are a bunch of other things edit-form-advanced.php loads that need to happen really early)
1030
+	 * we need to load it NOW, hence our _route_admin_request in here. (Otherwise screen options won't be set).
1031
+	 *
1032
+	 * @return void
1033
+	 * @throws EE_Error
1034
+	 * @throws ReflectionException
1035
+	 */
1036
+	public function modify_current_screen()
1037
+	{
1038
+		// ONLY do this if the current page_route IS a cpt route
1039
+		if (! $this->_cpt_route) {
1040
+			return;
1041
+		}
1042
+		// routing things REALLY early b/c this is a cpt admin page
1043
+		set_current_screen($this->_cpt_routes[ $this->_req_action ]);
1044
+		$this->_current_screen       = get_current_screen();
1045
+		$this->_current_screen->base = 'event-espresso';
1046
+		$this->_add_help_tabs(); // we make sure we add any help tabs back in!
1047
+		/*try {
1048 1048
             $this->_route_admin_request();
1049 1049
         } catch ( EE_Error $e ) {
1050 1050
             $e->get_error();
1051 1051
         }/**/
1052
-    }
1053
-
1054
-
1055
-    /**
1056
-     * This allows child classes to modify the default editor title that appears when people add a new or edit an
1057
-     * existing CPT item.     * This uses the _labels property set by the child class via _define_page_props. Just make
1058
-     * sure you have a key in _labels property that equals 'editor_title' and the value can be whatever you want the
1059
-     * default to be.
1060
-     *
1061
-     * @param string|null $title The new title (or existing if there is no editor_title defined)
1062
-     * @return string|null
1063
-     */
1064
-    public function add_custom_editor_default_title(?string $title): ?string
1065
-    {
1066
-        return $this->_labels['editor_title'][ $this->_cpt_routes[ $this->_req_action ] ] ?? $title;
1067
-    }
1068
-
1069
-
1070
-    /**
1071
-     * hooks into the wp_get_shortlink button and makes sure that the shortlink gets generated
1072
-     *
1073
-     * @param string $shortlink   The already generated shortlink
1074
-     * @param int    $id          Post ID for this item
1075
-     * @return string
1076
-     * @deprecated 5.0.0.p
1077
-     * @see EventShortlinkButton::addButton()
1078
-     */
1079
-    public function add_shortlink_button_to_editor(string $shortlink, int $id): string
1080
-    {
1081
-        return EventShortlinkButton::addButton($shortlink, $id);
1082
-    }
1083
-
1084
-
1085
-    /**
1086
-     * overriding the parent route_admin_request method so we DON'T run the route twice on cpt core page loads (it's
1087
-     * already run in modify_current_screen())
1088
-     *
1089
-     * @return void
1090
-     * @throws EE_Error
1091
-     * @throws ReflectionException
1092
-     */
1093
-    public function route_admin_request()
1094
-    {
1095
-        if ($this->_cpt_route) {
1096
-            return;
1097
-        }
1098
-        try {
1099
-            $this->_route_admin_request();
1100
-        } catch (EE_Error $e) {
1101
-            $e->get_error();
1102
-        }
1103
-    }
1104
-
1105
-
1106
-    /**
1107
-     * Add a hidden form input to cpt core pages so that we know to do redirects to our routes on saves
1108
-     *
1109
-     * @return void
1110
-     */
1111
-    public function cpt_post_form_hidden_input()
1112
-    {
1113
-        // we're also going to add the route value and the current page so we can direct autosave parsing correctly
1114
-        echo '
1052
+	}
1053
+
1054
+
1055
+	/**
1056
+	 * This allows child classes to modify the default editor title that appears when people add a new or edit an
1057
+	 * existing CPT item.     * This uses the _labels property set by the child class via _define_page_props. Just make
1058
+	 * sure you have a key in _labels property that equals 'editor_title' and the value can be whatever you want the
1059
+	 * default to be.
1060
+	 *
1061
+	 * @param string|null $title The new title (or existing if there is no editor_title defined)
1062
+	 * @return string|null
1063
+	 */
1064
+	public function add_custom_editor_default_title(?string $title): ?string
1065
+	{
1066
+		return $this->_labels['editor_title'][ $this->_cpt_routes[ $this->_req_action ] ] ?? $title;
1067
+	}
1068
+
1069
+
1070
+	/**
1071
+	 * hooks into the wp_get_shortlink button and makes sure that the shortlink gets generated
1072
+	 *
1073
+	 * @param string $shortlink   The already generated shortlink
1074
+	 * @param int    $id          Post ID for this item
1075
+	 * @return string
1076
+	 * @deprecated 5.0.0.p
1077
+	 * @see EventShortlinkButton::addButton()
1078
+	 */
1079
+	public function add_shortlink_button_to_editor(string $shortlink, int $id): string
1080
+	{
1081
+		return EventShortlinkButton::addButton($shortlink, $id);
1082
+	}
1083
+
1084
+
1085
+	/**
1086
+	 * overriding the parent route_admin_request method so we DON'T run the route twice on cpt core page loads (it's
1087
+	 * already run in modify_current_screen())
1088
+	 *
1089
+	 * @return void
1090
+	 * @throws EE_Error
1091
+	 * @throws ReflectionException
1092
+	 */
1093
+	public function route_admin_request()
1094
+	{
1095
+		if ($this->_cpt_route) {
1096
+			return;
1097
+		}
1098
+		try {
1099
+			$this->_route_admin_request();
1100
+		} catch (EE_Error $e) {
1101
+			$e->get_error();
1102
+		}
1103
+	}
1104
+
1105
+
1106
+	/**
1107
+	 * Add a hidden form input to cpt core pages so that we know to do redirects to our routes on saves
1108
+	 *
1109
+	 * @return void
1110
+	 */
1111
+	public function cpt_post_form_hidden_input()
1112
+	{
1113
+		// we're also going to add the route value and the current page so we can direct autosave parsing correctly
1114
+		echo '
1115 1115
         <input type="hidden" name="ee_cpt_item_redirect_url" value="' . esc_url_raw($this->_admin_base_url) . '"/>
1116 1116
         <div id="ee-cpt-hidden-inputs">
1117 1117
             <input type="hidden" id="current_route" name="current_route" value="' . esc_attr($this->_current_view) . '"/>
1118 1118
             <input type="hidden" id="current_page" name="current_page" value="' . esc_attr($this->page_slug) . '"/>
1119 1119
         </div>';
1120
-    }
1121
-
1122
-
1123
-    /**
1124
-     * This allows us to redirect the location of revision restores when they happen so it goes to our CPT routes.
1125
-     *
1126
-     * @param string $location Original location url
1127
-     * @return string           new (or original) url to redirect to.
1128
-     * @throws EE_Error
1129
-     */
1130
-    public function revision_redirect(string $location): string
1131
-    {
1132
-        // get revision
1133
-        $revision = $this->request->getRequestParam('revision');
1134
-        // can't do anything without revision so let's get out if not present
1135
-        if (empty($revision)) {
1136
-            return $location;
1137
-        }
1138
-        // get rev_post_data
1139
-        $rev        = get_post($revision);
1140
-        $admin_url  = $this->_admin_base_url;
1141
-        $query_args = [
1142
-            'action'   => 'edit',
1143
-            'post'     => $rev->post_parent,
1144
-            'revision' => $revision,
1145
-            'message'  => 5,
1146
-        ];
1147
-        $this->_process_notices($query_args, true);
1148
-        return EE_Admin_Page_CPT::add_query_args_and_nonce($query_args, $admin_url);
1149
-    }
1150
-
1151
-
1152
-    /**
1153
-     * Modify the edit post link generated by wp core function so that EE CPTs get setup differently.
1154
-     *
1155
-     * @param string $link    the original generated link
1156
-     * @param int    $id      post id
1157
-     * @return string          the link
1158
-     */
1159
-    public function modify_edit_post_link(string $link, int $id): string
1160
-    {
1161
-        $post = get_post($id);
1162
-        $action = $this->request->getRequestParam('action');
1163
-        if (
1164
-            empty($action)
1165
-            || ! isset($this->_cpt_routes[ $action ])
1166
-            || $post->post_type !== $this->_cpt_routes[ $action ]
1167
-        ) {
1168
-            return $link;
1169
-        }
1170
-        $query_args = [
1171
-            'action' => $this->_cpt_edit_routes[ $post->post_type ] ?? 'edit',
1172
-            'post'   => $id,
1173
-        ];
1174
-        return EE_Admin_Page_CPT::add_query_args_and_nonce($query_args, $this->_admin_base_url);
1175
-    }
1176
-
1177
-
1178
-    /**
1179
-     * Modify the trash link on our cpt edit pages so it has the required query var for triggering redirect properly on
1180
-     * our routes.
1181
-     *
1182
-     * @param string $delete_link  original delete link
1183
-     * @param int    $post_id      id of cpt object
1184
-     * @return string new delete link
1185
-     * @throws EE_Error
1186
-     * @throws ReflectionException
1187
-     */
1188
-    public function modify_delete_post_link(string $delete_link, int $post_id): string
1189
-    {
1190
-        $post = get_post($post_id);
1191
-        $action = $this->request->getRequestParam('action');
1192
-        if (
1193
-            ! $post instanceof WP_Post
1194
-            || empty($action)
1195
-            || ! isset($this->_cpt_routes[ $action ])
1196
-            || $post->post_type !== $this->_cpt_routes[ $action ]
1197
-        ) {
1198
-            return $delete_link;
1199
-        }
1200
-        $this->_set_model_object($post->ID, true);
1201
-
1202
-        // returns something like `trash_event` or `trash_attendee` or `trash_venue`
1203
-        $action = 'trash_' . str_replace('ee_', '', strtolower(get_class($this->_cpt_model_obj)));
1204
-
1205
-        return EE_Admin_Page::add_query_args_and_nonce(
1206
-            [
1207
-                'page'   => $this->request->getRequestParam('page'),
1208
-                'action' => $action,
1209
-                $this->_cpt_model_obj->get_model()->get_primary_key_field()->get_name() => $post->ID,
1210
-            ],
1211
-            admin_url()
1212
-        );
1213
-    }
1214
-
1215
-
1216
-    /**
1217
-     * This is the callback for the 'redirect_post_location' filter in wp-admin/post.php
1218
-     * so that we can hijack the default redirect locations for wp custom post types
1219
-     * that WE'RE using and send back to OUR routes.  This should only be hooked in on the right route.
1220
-     *
1221
-     * @param string $location This is the incoming currently set redirect location
1222
-     * @param string $post_id  This is the 'ID' value of the wp_posts table
1223
-     * @return string           the new location to redirect to
1224
-     * @throws EE_Error
1225
-     */
1226
-    public function cpt_post_location_redirect(string $location, string $post_id): string
1227
-    {
1228
-        // we DO have a match so let's setup the url
1229
-        // we have to get the post to determine our route
1230
-        $post       = get_post($post_id);
1231
-        $edit_route = $this->_cpt_edit_routes[ $post->post_type ];
1232
-        // shared query_args
1233
-        $query_args = ['action' => $edit_route, 'post' => $post_id];
1234
-
1235
-        $save = $this->request->getRequestParam('save');
1236
-        $publish = $this->request->getRequestParam('publish');
1237
-        $add_meta = $this->request->getRequestParam('addmeta');
1238
-        $delete_meta = $this->request->getRequestParam('deletemeta');
1239
-        if ($save || $publish) {
1240
-            $status = get_post_status($post_id);
1241
-            if ($publish) {
1242
-                switch ($status) {
1243
-                    case 'pending':
1244
-                        $message = 8;
1245
-                        break;
1246
-                    case 'future':
1247
-                        $message = 9;
1248
-                        break;
1249
-                    default:
1250
-                        $message = 6;
1251
-                }
1252
-            } else {
1253
-                $message = 'draft' === $status ? 10 : 1;
1254
-            }
1255
-        } elseif ($add_meta) {
1256
-            $message = 2;
1257
-        } elseif ($delete_meta) {
1258
-            $message = 3;
1259
-        } elseif ($this->request->getRequestParam('action') === 'post-quickpress-save-cont') {
1260
-            $message = 7;
1261
-        } else {
1262
-            $message = 4;
1263
-        }
1264
-        // change the message if the post type is not viewable on the frontend
1265
-        $this->_cpt_object = get_post_type_object($post->post_type);
1266
-
1267
-        $query_args['message'] = $message === 1 && ! $this->_cpt_object->publicly_queryable ? 4 : $message;
1268
-        $this->_process_notices($query_args, true);
1269
-        return EE_Admin_Page_CPT::add_query_args_and_nonce($query_args, $this->_admin_base_url);
1270
-    }
1271
-
1272
-
1273
-    /**
1274
-     * This method is called to inject nav tabs on core WP cpt pages
1275
-     *
1276
-     * @return void
1277
-     * @throws EE_Error
1278
-     */
1279
-    public function inject_nav_tabs()
1280
-    {
1281
-        echo wp_kses($this->_get_main_nav_tabs(), AllowedTags::getWithFormTags());
1282
-    }
1283
-
1284
-
1285
-    /**
1286
-     * This just sets up the post update messages when an update form is loaded
1287
-     *
1288
-     * @param array $messages the original messages array
1289
-     * @return array           the new messages array
1290
-     */
1291
-    public function post_update_messages(array $messages): array
1292
-    {
1293
-        global $post;
1294
-        $id       = $this->request->getRequestParam('post');
1295
-        $id       = empty($id) && is_object($post) ? $post->ID : null;
1296
-        $revision = $this->request->getRequestParam('revision', 0, 'int');
1297
-
1298
-        $messages[ $post->post_type ] = [
1299
-            0  => '', // Unused. Messages start at index 1.
1300
-            1  => sprintf(
1301
-                esc_html__('%1$s updated. %2$sView %1$s%3$s', 'event_espresso'),
1302
-                $this->_cpt_object->labels->singular_name,
1303
-                '<a href="' . esc_url(get_permalink($id)) . '">',
1304
-                '</a>'
1305
-            ),
1306
-            2  => esc_html__('Custom field updated', 'event_espresso'),
1307
-            3  => esc_html__('Custom field deleted.', 'event_espresso'),
1308
-            4  => sprintf(esc_html__('%1$s updated.', 'event_espresso'), $this->_cpt_object->labels->singular_name),
1309
-            5  => $revision
1310
-                ? sprintf(
1311
-                    esc_html__('%s restored to revision from %s', 'event_espresso'),
1312
-                    $this->_cpt_object->labels->singular_name,
1313
-                    wp_post_revision_title($revision, false)
1314
-                )
1315
-                : false,
1316
-            6  => sprintf(
1317
-                esc_html__('%1$s published. %2$sView %1$s%3$s', 'event_espresso'),
1318
-                $this->_cpt_object->labels->singular_name,
1319
-                '<a href="' . esc_url(get_permalink($id)) . '">',
1320
-                '</a>'
1321
-            ),
1322
-            7  => sprintf(esc_html__('%1$s saved.', 'event_espresso'), $this->_cpt_object->labels->singular_name),
1323
-            8  => sprintf(
1324
-                esc_html__('%1$s submitted. %2$sPreview %1$s%3$s', 'event_espresso'),
1325
-                $this->_cpt_object->labels->singular_name,
1326
-                '<a target="_blank" href="' . esc_url(add_query_arg('preview', 'true', get_permalink($id))) . '">',
1327
-                '</a>'
1328
-            ),
1329
-            9  => sprintf(
1330
-                esc_html__('%1$s scheduled for: %2$s. %3$s">Preview %1$s%3$s', 'event_espresso'),
1331
-                $this->_cpt_object->labels->singular_name,
1332
-                '<strong>' . date_i18n('M j, Y @ G:i', strtotime($post->post_date)) . '</strong>',
1333
-                '<a target="_blank" href="' . esc_url(get_permalink($id)),
1334
-                '</a>'
1335
-            ),
1336
-            10 => sprintf(
1337
-                esc_html__('%1$s draft updated. %2$s">Preview page%3$s', 'event_espresso'),
1338
-                $this->_cpt_object->labels->singular_name,
1339
-                '<a target="_blank" href="' . esc_url(add_query_arg('preview', 'true', get_permalink($id))),
1340
-                '</a>'
1341
-            ),
1342
-        ];
1343
-        return $messages;
1344
-    }
1345
-
1346
-
1347
-    /**
1348
-     * default method for the 'create_new' route for cpt admin pages.
1349
-     * For reference what to include in here, see wp-admin/post-new.php
1350
-     *
1351
-     * @return void
1352
-     */
1353
-    protected function _create_new_cpt_item()
1354
-    {
1355
-        // gather template vars for WP_ADMIN_PATH . 'edit-form-advanced.php'
1356
-        global $post, $title, $post_type, $post_type_object;
1357
-        $post_type        = $this->_cpt_routes[ $this->_req_action ];
1358
-        $post_type_object = $this->_cpt_object;
1359
-        $title            = $post_type_object->labels->add_new_item;
1360
-        $post             = $post = get_default_post_to_edit($this->_cpt_routes[ $this->_req_action ], true);
1361
-        add_action('admin_print_styles', [$this, 'add_new_admin_page_global']);
1362
-        // modify the default editor title field with default title.
1363
-        add_filter('enter_title_here', [$this, 'add_custom_editor_default_title']);
1364
-        $this->loadEditorTemplate();
1365
-    }
1366
-
1367
-
1368
-    /**
1369
-     * Enqueues auto-save and loads the editor template
1370
-     *
1371
-     * @param bool $creating
1372
-     */
1373
-    private function loadEditorTemplate(bool $creating = true)
1374
-    {
1375
-        if ($this->admin_config && ! $this->admin_config->useAdvancedEditor()) {
1376
-            add_filter('admin_body_class', function($classes)
1377
-            {
1378
-                $classes .= ' espresso-legacy-editor';
1379
-                return $classes;
1380
-            });
1381
-        }
1382
-
1383
-        global $post, $title, $is_IE, $post_type, $post_type_object;
1384
-        // these vars are used by the template
1385
-        $editing = true;
1386
-        $post_ID = $post->ID;
1387
-        if (apply_filters('FHEE__EE_Admin_Page_CPT___create_new_cpt_item__replace_editor', false, $post) === false) {
1388
-            // only enqueue autosave when creating event (necessary to get permalink/url generated)
1389
-            // otherwise EE doesn't support autosave fully, so to prevent user confusion we disable it in edit context.
1390
-            $action = $this->request->getRequestParam('action');
1391
-            if ($creating) {
1392
-                wp_enqueue_script('autosave');
1393
-            } elseif (
1394
-                isset($this->_cpt_routes[ $action ])
1395
-                && ! isset($this->_labels['hide_add_button_on_cpt_route'][ $action ])
1396
-            ) {
1397
-                $create_new_action = apply_filters(
1398
-                    'FHEE__EE_Admin_Page_CPT___edit_cpt_item__create_new_action',
1399
-                    'create_new',
1400
-                    $this
1401
-                );
1402
-                $post_new_file     = EE_Admin_Page::add_query_args_and_nonce(
1403
-                    [
1404
-                        'action' => $create_new_action,
1405
-                        'page'   => $this->page_slug,
1406
-                    ],
1407
-                    'admin.php'
1408
-                );
1409
-            }
1410
-            include_once WP_ADMIN_PATH . 'edit-form-advanced.php';
1411
-        }
1412
-    }
1413
-
1414
-
1415
-    public function add_new_admin_page_global()
1416
-    {
1417
-        $admin_page = $this->request->getRequestParam('post', 0, DataType::INT) !== 0
1418
-            ? 'post-php'
1419
-            : 'post-new-php';
1420
-        ?>
1120
+	}
1121
+
1122
+
1123
+	/**
1124
+	 * This allows us to redirect the location of revision restores when they happen so it goes to our CPT routes.
1125
+	 *
1126
+	 * @param string $location Original location url
1127
+	 * @return string           new (or original) url to redirect to.
1128
+	 * @throws EE_Error
1129
+	 */
1130
+	public function revision_redirect(string $location): string
1131
+	{
1132
+		// get revision
1133
+		$revision = $this->request->getRequestParam('revision');
1134
+		// can't do anything without revision so let's get out if not present
1135
+		if (empty($revision)) {
1136
+			return $location;
1137
+		}
1138
+		// get rev_post_data
1139
+		$rev        = get_post($revision);
1140
+		$admin_url  = $this->_admin_base_url;
1141
+		$query_args = [
1142
+			'action'   => 'edit',
1143
+			'post'     => $rev->post_parent,
1144
+			'revision' => $revision,
1145
+			'message'  => 5,
1146
+		];
1147
+		$this->_process_notices($query_args, true);
1148
+		return EE_Admin_Page_CPT::add_query_args_and_nonce($query_args, $admin_url);
1149
+	}
1150
+
1151
+
1152
+	/**
1153
+	 * Modify the edit post link generated by wp core function so that EE CPTs get setup differently.
1154
+	 *
1155
+	 * @param string $link    the original generated link
1156
+	 * @param int    $id      post id
1157
+	 * @return string          the link
1158
+	 */
1159
+	public function modify_edit_post_link(string $link, int $id): string
1160
+	{
1161
+		$post = get_post($id);
1162
+		$action = $this->request->getRequestParam('action');
1163
+		if (
1164
+			empty($action)
1165
+			|| ! isset($this->_cpt_routes[ $action ])
1166
+			|| $post->post_type !== $this->_cpt_routes[ $action ]
1167
+		) {
1168
+			return $link;
1169
+		}
1170
+		$query_args = [
1171
+			'action' => $this->_cpt_edit_routes[ $post->post_type ] ?? 'edit',
1172
+			'post'   => $id,
1173
+		];
1174
+		return EE_Admin_Page_CPT::add_query_args_and_nonce($query_args, $this->_admin_base_url);
1175
+	}
1176
+
1177
+
1178
+	/**
1179
+	 * Modify the trash link on our cpt edit pages so it has the required query var for triggering redirect properly on
1180
+	 * our routes.
1181
+	 *
1182
+	 * @param string $delete_link  original delete link
1183
+	 * @param int    $post_id      id of cpt object
1184
+	 * @return string new delete link
1185
+	 * @throws EE_Error
1186
+	 * @throws ReflectionException
1187
+	 */
1188
+	public function modify_delete_post_link(string $delete_link, int $post_id): string
1189
+	{
1190
+		$post = get_post($post_id);
1191
+		$action = $this->request->getRequestParam('action');
1192
+		if (
1193
+			! $post instanceof WP_Post
1194
+			|| empty($action)
1195
+			|| ! isset($this->_cpt_routes[ $action ])
1196
+			|| $post->post_type !== $this->_cpt_routes[ $action ]
1197
+		) {
1198
+			return $delete_link;
1199
+		}
1200
+		$this->_set_model_object($post->ID, true);
1201
+
1202
+		// returns something like `trash_event` or `trash_attendee` or `trash_venue`
1203
+		$action = 'trash_' . str_replace('ee_', '', strtolower(get_class($this->_cpt_model_obj)));
1204
+
1205
+		return EE_Admin_Page::add_query_args_and_nonce(
1206
+			[
1207
+				'page'   => $this->request->getRequestParam('page'),
1208
+				'action' => $action,
1209
+				$this->_cpt_model_obj->get_model()->get_primary_key_field()->get_name() => $post->ID,
1210
+			],
1211
+			admin_url()
1212
+		);
1213
+	}
1214
+
1215
+
1216
+	/**
1217
+	 * This is the callback for the 'redirect_post_location' filter in wp-admin/post.php
1218
+	 * so that we can hijack the default redirect locations for wp custom post types
1219
+	 * that WE'RE using and send back to OUR routes.  This should only be hooked in on the right route.
1220
+	 *
1221
+	 * @param string $location This is the incoming currently set redirect location
1222
+	 * @param string $post_id  This is the 'ID' value of the wp_posts table
1223
+	 * @return string           the new location to redirect to
1224
+	 * @throws EE_Error
1225
+	 */
1226
+	public function cpt_post_location_redirect(string $location, string $post_id): string
1227
+	{
1228
+		// we DO have a match so let's setup the url
1229
+		// we have to get the post to determine our route
1230
+		$post       = get_post($post_id);
1231
+		$edit_route = $this->_cpt_edit_routes[ $post->post_type ];
1232
+		// shared query_args
1233
+		$query_args = ['action' => $edit_route, 'post' => $post_id];
1234
+
1235
+		$save = $this->request->getRequestParam('save');
1236
+		$publish = $this->request->getRequestParam('publish');
1237
+		$add_meta = $this->request->getRequestParam('addmeta');
1238
+		$delete_meta = $this->request->getRequestParam('deletemeta');
1239
+		if ($save || $publish) {
1240
+			$status = get_post_status($post_id);
1241
+			if ($publish) {
1242
+				switch ($status) {
1243
+					case 'pending':
1244
+						$message = 8;
1245
+						break;
1246
+					case 'future':
1247
+						$message = 9;
1248
+						break;
1249
+					default:
1250
+						$message = 6;
1251
+				}
1252
+			} else {
1253
+				$message = 'draft' === $status ? 10 : 1;
1254
+			}
1255
+		} elseif ($add_meta) {
1256
+			$message = 2;
1257
+		} elseif ($delete_meta) {
1258
+			$message = 3;
1259
+		} elseif ($this->request->getRequestParam('action') === 'post-quickpress-save-cont') {
1260
+			$message = 7;
1261
+		} else {
1262
+			$message = 4;
1263
+		}
1264
+		// change the message if the post type is not viewable on the frontend
1265
+		$this->_cpt_object = get_post_type_object($post->post_type);
1266
+
1267
+		$query_args['message'] = $message === 1 && ! $this->_cpt_object->publicly_queryable ? 4 : $message;
1268
+		$this->_process_notices($query_args, true);
1269
+		return EE_Admin_Page_CPT::add_query_args_and_nonce($query_args, $this->_admin_base_url);
1270
+	}
1271
+
1272
+
1273
+	/**
1274
+	 * This method is called to inject nav tabs on core WP cpt pages
1275
+	 *
1276
+	 * @return void
1277
+	 * @throws EE_Error
1278
+	 */
1279
+	public function inject_nav_tabs()
1280
+	{
1281
+		echo wp_kses($this->_get_main_nav_tabs(), AllowedTags::getWithFormTags());
1282
+	}
1283
+
1284
+
1285
+	/**
1286
+	 * This just sets up the post update messages when an update form is loaded
1287
+	 *
1288
+	 * @param array $messages the original messages array
1289
+	 * @return array           the new messages array
1290
+	 */
1291
+	public function post_update_messages(array $messages): array
1292
+	{
1293
+		global $post;
1294
+		$id       = $this->request->getRequestParam('post');
1295
+		$id       = empty($id) && is_object($post) ? $post->ID : null;
1296
+		$revision = $this->request->getRequestParam('revision', 0, 'int');
1297
+
1298
+		$messages[ $post->post_type ] = [
1299
+			0  => '', // Unused. Messages start at index 1.
1300
+			1  => sprintf(
1301
+				esc_html__('%1$s updated. %2$sView %1$s%3$s', 'event_espresso'),
1302
+				$this->_cpt_object->labels->singular_name,
1303
+				'<a href="' . esc_url(get_permalink($id)) . '">',
1304
+				'</a>'
1305
+			),
1306
+			2  => esc_html__('Custom field updated', 'event_espresso'),
1307
+			3  => esc_html__('Custom field deleted.', 'event_espresso'),
1308
+			4  => sprintf(esc_html__('%1$s updated.', 'event_espresso'), $this->_cpt_object->labels->singular_name),
1309
+			5  => $revision
1310
+				? sprintf(
1311
+					esc_html__('%s restored to revision from %s', 'event_espresso'),
1312
+					$this->_cpt_object->labels->singular_name,
1313
+					wp_post_revision_title($revision, false)
1314
+				)
1315
+				: false,
1316
+			6  => sprintf(
1317
+				esc_html__('%1$s published. %2$sView %1$s%3$s', 'event_espresso'),
1318
+				$this->_cpt_object->labels->singular_name,
1319
+				'<a href="' . esc_url(get_permalink($id)) . '">',
1320
+				'</a>'
1321
+			),
1322
+			7  => sprintf(esc_html__('%1$s saved.', 'event_espresso'), $this->_cpt_object->labels->singular_name),
1323
+			8  => sprintf(
1324
+				esc_html__('%1$s submitted. %2$sPreview %1$s%3$s', 'event_espresso'),
1325
+				$this->_cpt_object->labels->singular_name,
1326
+				'<a target="_blank" href="' . esc_url(add_query_arg('preview', 'true', get_permalink($id))) . '">',
1327
+				'</a>'
1328
+			),
1329
+			9  => sprintf(
1330
+				esc_html__('%1$s scheduled for: %2$s. %3$s">Preview %1$s%3$s', 'event_espresso'),
1331
+				$this->_cpt_object->labels->singular_name,
1332
+				'<strong>' . date_i18n('M j, Y @ G:i', strtotime($post->post_date)) . '</strong>',
1333
+				'<a target="_blank" href="' . esc_url(get_permalink($id)),
1334
+				'</a>'
1335
+			),
1336
+			10 => sprintf(
1337
+				esc_html__('%1$s draft updated. %2$s">Preview page%3$s', 'event_espresso'),
1338
+				$this->_cpt_object->labels->singular_name,
1339
+				'<a target="_blank" href="' . esc_url(add_query_arg('preview', 'true', get_permalink($id))),
1340
+				'</a>'
1341
+			),
1342
+		];
1343
+		return $messages;
1344
+	}
1345
+
1346
+
1347
+	/**
1348
+	 * default method for the 'create_new' route for cpt admin pages.
1349
+	 * For reference what to include in here, see wp-admin/post-new.php
1350
+	 *
1351
+	 * @return void
1352
+	 */
1353
+	protected function _create_new_cpt_item()
1354
+	{
1355
+		// gather template vars for WP_ADMIN_PATH . 'edit-form-advanced.php'
1356
+		global $post, $title, $post_type, $post_type_object;
1357
+		$post_type        = $this->_cpt_routes[ $this->_req_action ];
1358
+		$post_type_object = $this->_cpt_object;
1359
+		$title            = $post_type_object->labels->add_new_item;
1360
+		$post             = $post = get_default_post_to_edit($this->_cpt_routes[ $this->_req_action ], true);
1361
+		add_action('admin_print_styles', [$this, 'add_new_admin_page_global']);
1362
+		// modify the default editor title field with default title.
1363
+		add_filter('enter_title_here', [$this, 'add_custom_editor_default_title']);
1364
+		$this->loadEditorTemplate();
1365
+	}
1366
+
1367
+
1368
+	/**
1369
+	 * Enqueues auto-save and loads the editor template
1370
+	 *
1371
+	 * @param bool $creating
1372
+	 */
1373
+	private function loadEditorTemplate(bool $creating = true)
1374
+	{
1375
+		if ($this->admin_config && ! $this->admin_config->useAdvancedEditor()) {
1376
+			add_filter('admin_body_class', function($classes)
1377
+			{
1378
+				$classes .= ' espresso-legacy-editor';
1379
+				return $classes;
1380
+			});
1381
+		}
1382
+
1383
+		global $post, $title, $is_IE, $post_type, $post_type_object;
1384
+		// these vars are used by the template
1385
+		$editing = true;
1386
+		$post_ID = $post->ID;
1387
+		if (apply_filters('FHEE__EE_Admin_Page_CPT___create_new_cpt_item__replace_editor', false, $post) === false) {
1388
+			// only enqueue autosave when creating event (necessary to get permalink/url generated)
1389
+			// otherwise EE doesn't support autosave fully, so to prevent user confusion we disable it in edit context.
1390
+			$action = $this->request->getRequestParam('action');
1391
+			if ($creating) {
1392
+				wp_enqueue_script('autosave');
1393
+			} elseif (
1394
+				isset($this->_cpt_routes[ $action ])
1395
+				&& ! isset($this->_labels['hide_add_button_on_cpt_route'][ $action ])
1396
+			) {
1397
+				$create_new_action = apply_filters(
1398
+					'FHEE__EE_Admin_Page_CPT___edit_cpt_item__create_new_action',
1399
+					'create_new',
1400
+					$this
1401
+				);
1402
+				$post_new_file     = EE_Admin_Page::add_query_args_and_nonce(
1403
+					[
1404
+						'action' => $create_new_action,
1405
+						'page'   => $this->page_slug,
1406
+					],
1407
+					'admin.php'
1408
+				);
1409
+			}
1410
+			include_once WP_ADMIN_PATH . 'edit-form-advanced.php';
1411
+		}
1412
+	}
1413
+
1414
+
1415
+	public function add_new_admin_page_global()
1416
+	{
1417
+		$admin_page = $this->request->getRequestParam('post', 0, DataType::INT) !== 0
1418
+			? 'post-php'
1419
+			: 'post-new-php';
1420
+		?>
1421 1421
         <script type="text/javascript">
1422 1422
             adminpage = '<?php echo esc_js($admin_page); ?>';
1423 1423
         </script>
1424 1424
         <?php
1425
-    }
1426
-
1427
-
1428
-    /**
1429
-     * default method for the 'edit' route for cpt admin pages
1430
-     * For reference on what to put in here, refer to wp-admin/post.php
1431
-     *
1432
-     * @return void
1433
-     */
1434
-    protected function _edit_cpt_item()
1435
-    {
1436
-        global $post, $post_type, $post_type_object, $title;
1437
-        $post_id = $this->request->getRequestParam('post', 0, DataType::INT);
1438
-        $post    = $post_id ? get_post($post_id, OBJECT, 'edit') : null;
1439
-        if (empty($post)) {
1440
-            wp_die(
1441
-                esc_html__(
1442
-                    "You attempted to edit an item that doesn't exist. Perhaps it was deleted?",
1443
-                    'event_espresso'
1444
-                )
1445
-            );
1446
-        }
1447
-
1448
-        $post_lock = $this->request->getRequestParam('get-post-lock');
1449
-        if ($post_lock) {
1450
-            wp_set_post_lock($post_id);
1451
-            EEH_URL::safeRedirectAndExit(get_edit_post_link($post_id, 'url'));
1452
-        }
1453
-
1454
-        // template vars for WP_ADMIN_PATH . 'edit-form-advanced.php'
1455
-        $post_type        = $this->_cpt_routes[ $this->_req_action ];
1456
-        $post_type_object = $this->_cpt_object;
1457
-        $title = $this->_labels['editor_title'][ $this->_cpt_routes[ $this->_req_action ] ]
1458
-                 ?? $post_type_object->labels->edit_item;
1459
-
1460
-        if (! wp_check_post_lock($post->ID)) {
1461
-            wp_set_post_lock($post->ID);
1462
-        }
1463
-        add_action('admin_footer', '_admin_notice_post_locked');
1464
-        if (post_type_supports($this->_cpt_routes[ $this->_req_action ], 'comments')) {
1465
-            wp_enqueue_script('admin-comments');
1466
-            enqueue_comment_hotkeys_js();
1467
-        }
1468
-        add_action('admin_print_styles', [$this, 'add_new_admin_page_global']);
1469
-        // modify the default editor title field with default title.
1470
-        add_filter('enter_title_here', [$this, 'add_custom_editor_default_title']);
1471
-        $this->loadEditorTemplate(false);
1472
-    }
1473
-
1474
-
1475
-
1476
-    /**
1477
-     * some getters
1478
-     */
1479
-    /**
1480
-     * This returns the protected _cpt_model_obj property
1481
-     *
1482
-     * @return EE_CPT_Base
1483
-     */
1484
-    public function get_cpt_model_obj()
1485
-    {
1486
-        return $this->_cpt_model_obj;
1487
-    }
1425
+	}
1426
+
1427
+
1428
+	/**
1429
+	 * default method for the 'edit' route for cpt admin pages
1430
+	 * For reference on what to put in here, refer to wp-admin/post.php
1431
+	 *
1432
+	 * @return void
1433
+	 */
1434
+	protected function _edit_cpt_item()
1435
+	{
1436
+		global $post, $post_type, $post_type_object, $title;
1437
+		$post_id = $this->request->getRequestParam('post', 0, DataType::INT);
1438
+		$post    = $post_id ? get_post($post_id, OBJECT, 'edit') : null;
1439
+		if (empty($post)) {
1440
+			wp_die(
1441
+				esc_html__(
1442
+					"You attempted to edit an item that doesn't exist. Perhaps it was deleted?",
1443
+					'event_espresso'
1444
+				)
1445
+			);
1446
+		}
1447
+
1448
+		$post_lock = $this->request->getRequestParam('get-post-lock');
1449
+		if ($post_lock) {
1450
+			wp_set_post_lock($post_id);
1451
+			EEH_URL::safeRedirectAndExit(get_edit_post_link($post_id, 'url'));
1452
+		}
1453
+
1454
+		// template vars for WP_ADMIN_PATH . 'edit-form-advanced.php'
1455
+		$post_type        = $this->_cpt_routes[ $this->_req_action ];
1456
+		$post_type_object = $this->_cpt_object;
1457
+		$title = $this->_labels['editor_title'][ $this->_cpt_routes[ $this->_req_action ] ]
1458
+				 ?? $post_type_object->labels->edit_item;
1459
+
1460
+		if (! wp_check_post_lock($post->ID)) {
1461
+			wp_set_post_lock($post->ID);
1462
+		}
1463
+		add_action('admin_footer', '_admin_notice_post_locked');
1464
+		if (post_type_supports($this->_cpt_routes[ $this->_req_action ], 'comments')) {
1465
+			wp_enqueue_script('admin-comments');
1466
+			enqueue_comment_hotkeys_js();
1467
+		}
1468
+		add_action('admin_print_styles', [$this, 'add_new_admin_page_global']);
1469
+		// modify the default editor title field with default title.
1470
+		add_filter('enter_title_here', [$this, 'add_custom_editor_default_title']);
1471
+		$this->loadEditorTemplate(false);
1472
+	}
1473
+
1474
+
1475
+
1476
+	/**
1477
+	 * some getters
1478
+	 */
1479
+	/**
1480
+	 * This returns the protected _cpt_model_obj property
1481
+	 *
1482
+	 * @return EE_CPT_Base
1483
+	 */
1484
+	public function get_cpt_model_obj()
1485
+	{
1486
+		return $this->_cpt_model_obj;
1487
+	}
1488 1488
 }
Please login to merge, or discard this patch.
Spacing   +69 added lines, -69 removed lines patch added patch discarded remove patch
@@ -154,7 +154,7 @@  discard block
 block discarded – undo
154 154
      */
155 155
     protected function getLoader(): LoaderInterface
156 156
     {
157
-        if (! $this->loader instanceof LoaderInterface) {
157
+        if ( ! $this->loader instanceof LoaderInterface) {
158 158
             $this->loader = LoaderFactory::getLoader();
159 159
         }
160 160
         return $this->loader;
@@ -178,13 +178,13 @@  discard block
 block discarded – undo
178 178
             ],
179 179
             $this->_cpt_routes
180 180
         );
181
-        $cpt_route_action     = $this->_cpt_routes[ $this->raw_req_action ] ?? null;
181
+        $cpt_route_action = $this->_cpt_routes[$this->raw_req_action] ?? null;
182 182
         // let's see if the current route has a value for cpt_object_slug. if it does, we use that instead of the page
183 183
         $page              = $this->raw_req_page ?: $this->page_slug;
184 184
         $page              = $cpt_route_action ?: $page;
185 185
         $this->_cpt_object = get_post_type_object($page);
186 186
         // tweak pagenow for page loading.
187
-        if (! $this->_pagenow_map) {
187
+        if ( ! $this->_pagenow_map) {
188 188
             $this->_pagenow_map = [
189 189
                 'create_new' => 'post-new.php',
190 190
                 'edit'       => 'post.php',
@@ -210,10 +210,10 @@  discard block
 block discarded – undo
210 210
         // possibly reset pagenow.
211 211
         if (
212 212
             $this->page_slug === $this->raw_req_page
213
-            && isset($this->_pagenow_map[ $this->raw_req_action ])
213
+            && isset($this->_pagenow_map[$this->raw_req_action])
214 214
         ) {
215 215
             global $pagenow, $hook_suffix;
216
-            $pagenow     = $this->_pagenow_map[ $this->raw_req_action ];
216
+            $pagenow     = $this->_pagenow_map[$this->raw_req_action];
217 217
             $hook_suffix = $pagenow;
218 218
         }
219 219
     }
@@ -245,7 +245,7 @@  discard block
 block discarded – undo
245 245
         if (empty($wp_meta_boxes)) {
246 246
             return;
247 247
         }
248
-        $current_metaboxes = $wp_meta_boxes[ $this->page_slug ] ?? [];
248
+        $current_metaboxes = $wp_meta_boxes[$this->page_slug] ?? [];
249 249
         foreach ($current_metaboxes as $box_context) {
250 250
             foreach ($box_context as $box_details) {
251 251
                 foreach ($box_details as $box) {
@@ -279,7 +279,7 @@  discard block
 block discarded – undo
279 279
             $this
280 280
         );
281 281
         $containers = apply_filters(
282
-            'FHEE__EE_Admin_Page_CPT__' . get_class($this) . '___load_autosave_scripts_styles__containers',
282
+            'FHEE__EE_Admin_Page_CPT__'.get_class($this).'___load_autosave_scripts_styles__containers',
283 283
             $containers,
284 284
             $this
285 285
         );
@@ -316,7 +316,7 @@  discard block
 block discarded – undo
316 316
     protected function _load_page_dependencies()
317 317
     {
318 318
         // we only add stuff if this is a cpt_route!
319
-        if (! $this->_cpt_route) {
319
+        if ( ! $this->_cpt_route) {
320 320
             parent::_load_page_dependencies();
321 321
             return;
322 322
         }
@@ -337,8 +337,8 @@  discard block
 block discarded – undo
337 337
         add_action('post_updated_messages', [$this, 'post_update_messages'], 10);
338 338
         // This basically allows us to change the title of the "publish" metabox area
339 339
         // on CPT pages by setting a 'publishbox' value in the $_labels property array in the child class.
340
-        $screen = $this->_cpt_routes[ $this->_req_action ];
341
-        if (! empty($this->_labels['publishbox'])) {
340
+        $screen = $this->_cpt_routes[$this->_req_action];
341
+        if ( ! empty($this->_labels['publishbox'])) {
342 342
             $this->addMetaBox(
343 343
                 'submitdiv',
344 344
                 $this->getPublishBoxTitle(),
@@ -386,8 +386,8 @@  discard block
 block discarded – undo
386 386
         // This is for any plugins that are doing things properly
387 387
         // and hooking into the load page hook for core wp cpt routes.
388 388
         global $pagenow;
389
-        add_action('load-' . $pagenow, [$this, 'modify_current_screen'], 20);
390
-        do_action('load-' . $pagenow);
389
+        add_action('load-'.$pagenow, [$this, 'modify_current_screen'], 20);
390
+        do_action('load-'.$pagenow);
391 391
         add_action('admin_enqueue_scripts', [$this, 'setup_autosave_hooks'], 30);
392 392
         // we route REALLY early.
393 393
         try {
@@ -414,8 +414,8 @@  discard block
 block discarded – undo
414 414
                 'admin.php?page=espresso_registrations&action=contact_list',
415 415
             ],
416 416
             1 => [
417
-                'edit.php?post_type=' . $this->_cpt_object->name,
418
-                'admin.php?page=' . $this->_cpt_object->name,
417
+                'edit.php?post_type='.$this->_cpt_object->name,
418
+                'admin.php?page='.$this->_cpt_object->name,
419 419
             ],
420 420
         ];
421 421
         foreach ($routes_to_match as $route_matches) {
@@ -444,14 +444,14 @@  discard block
 block discarded – undo
444 444
             'EventEspresso\core\domain\entities\custom_post_types\CustomPostTypeDefinitions'
445 445
         );
446 446
         $cpt_args          = $custom_post_types->getDefinitions();
447
-        $cpt_args          = isset($cpt_args[ $cpt_name ]) ? $cpt_args[ $cpt_name ]['args'] : [];
447
+        $cpt_args          = isset($cpt_args[$cpt_name]) ? $cpt_args[$cpt_name]['args'] : [];
448 448
         $cpt_has_support   = ! empty($cpt_args['page_templates']);
449 449
 
450 450
         $post_templates = wp_get_theme()->get_post_templates();
451 451
         // if there are $post_templates for this cpt, then we return false for this method because
452 452
         // that means we aren't going to load our page template manager and leave that up to the native
453 453
         // cpt template manager.
454
-        return ! isset($post_templates[ $cpt_name ]) ? $cpt_has_support : false;
454
+        return ! isset($post_templates[$cpt_name]) ? $cpt_has_support : false;
455 455
     }
456 456
 
457 457
 
@@ -509,7 +509,7 @@  discard block
 block discarded – undo
509 509
     {
510 510
         $statuses         = $this->_cpt_model_obj->get_custom_post_statuses();
511 511
         $cur_status_label = array_key_exists($this->_cpt_model_obj->status(), $statuses)
512
-            ? $statuses[ $this->_cpt_model_obj->status() ]
512
+            ? $statuses[$this->_cpt_model_obj->status()]
513 513
             : '';
514 514
         $template_args    = [
515 515
             'cur_status'            => $this->_cpt_model_obj->status(),
@@ -524,7 +524,7 @@  discard block
 block discarded – undo
524 524
             $template_args['statuses']         = $statuses;
525 525
         }
526 526
 
527
-        $template = EE_ADMIN_TEMPLATE . 'status_dropdown.template.php';
527
+        $template = EE_ADMIN_TEMPLATE.'status_dropdown.template.php';
528 528
         EEH_Template::display_template($template, $template_args);
529 529
     }
530 530
 
@@ -564,7 +564,7 @@  discard block
 block discarded – undo
564 564
         $nonce = $this->request->getRequestParam('autosavenonce');
565 565
         $this->_verify_nonce($nonce, 'autosave');
566 566
         // make sure we define doing autosave (cause WP isn't triggering this we want to make sure we define it)
567
-        if (! defined('DOING_AUTOSAVE')) {
567
+        if ( ! defined('DOING_AUTOSAVE')) {
568 568
             define('DOING_AUTOSAVE', true);
569 569
         }
570 570
         // if we made it here then the nonce checked out.  Let's run our methods and actions
@@ -575,7 +575,7 @@  discard block
 block discarded – undo
575 575
             $this->_template_args['success'] = true;
576 576
         }
577 577
         do_action('AHEE__EE_Admin_Page_CPT__do_extra_autosave_stuff__global_after', $this);
578
-        do_action('AHEE__EE_Admin_Page_CPT__do_extra_autosave_stuff__after_' . get_class($this), $this);
578
+        do_action('AHEE__EE_Admin_Page_CPT__do_extra_autosave_stuff__after_'.get_class($this), $this);
579 579
         // now let's return json
580 580
         $this->_return_json();
581 581
     }
@@ -597,7 +597,7 @@  discard block
 block discarded – undo
597 597
             return;
598 598
         }
599 599
         // set page routes and page config but ONLY if we're not viewing a custom setup cpt route as defined in _cpt_routes
600
-        if (! empty($this->_cpt_object)) {
600
+        if ( ! empty($this->_cpt_object)) {
601 601
             $this->_page_routes = array_merge(
602 602
                 [
603 603
                     'create_new' => [$this, '_create_new_cpt_item'],
@@ -628,7 +628,7 @@  discard block
 block discarded – undo
628 628
             );
629 629
         }
630 630
         // load the next section only if this is a matching cpt route as set in the cpt routes array.
631
-        if (! isset($this->_cpt_routes[ $this->_req_action ])) {
631
+        if ( ! isset($this->_cpt_routes[$this->_req_action])) {
632 632
             return;
633 633
         }
634 634
         $this->_cpt_route = true;
@@ -669,7 +669,7 @@  discard block
 block discarded – undo
669 669
             empty($this->_cpt_model_names)
670 670
             || (
671 671
                 ! $ignore_route_check
672
-                && ! isset($this->_cpt_routes[ $this->_req_action ])
672
+                && ! isset($this->_cpt_routes[$this->_req_action])
673 673
             )
674 674
             || (
675 675
                 $this->_cpt_model_obj instanceof EE_CPT_Base
@@ -687,11 +687,11 @@  discard block
 block discarded – undo
687 687
                 'EventEspresso\core\domain\entities\custom_post_types\CustomPostTypeDefinitions'
688 688
             );
689 689
             $model_names       = $custom_post_types->getCustomPostTypeModelNames($post_type);
690
-            if (isset($model_names[ $post_type ])) {
691
-                $model = EE_Registry::instance()->load_model($model_names[ $post_type ]);
690
+            if (isset($model_names[$post_type])) {
691
+                $model = EE_Registry::instance()->load_model($model_names[$post_type]);
692 692
             }
693 693
         } else {
694
-            $model = EE_Registry::instance()->load_model($this->_cpt_model_names[ $this->_req_action ]);
694
+            $model = EE_Registry::instance()->load_model($this->_cpt_model_names[$this->_req_action]);
695 695
         }
696 696
         if ($model instanceof EEM_Base) {
697 697
             $this->_cpt_model_obj = ! empty($id) ? $model->get_one_by_ID($id) : $model->create_default_object();
@@ -719,8 +719,8 @@  discard block
 block discarded – undo
719 719
         $post           = get_post($post_ID);
720 720
         $post_type      = $post instanceof WP_Post ? $post->post_type : false;
721 721
         $current_route  = $this->request->getRequestParam('current_route', 'shouldneverwork');
722
-        $route_to_check = $post_type && isset($this->_cpt_routes[ $current_route ])
723
-            ? $this->_cpt_routes[ $current_route ]
722
+        $route_to_check = $post_type && isset($this->_cpt_routes[$current_route])
723
+            ? $this->_cpt_routes[$current_route]
724 724
             : '';
725 725
         add_filter('get_delete_post_link', [$this, 'modify_delete_post_link'], 10, 2);
726 726
         add_filter('get_edit_post_link', [$this, 'modify_edit_post_link'], 10, 2);
@@ -729,10 +729,10 @@  discard block
 block discarded – undo
729 729
         }
730 730
         // now let's filter redirect if we're on a revision page and the revision is for an event CPT.
731 731
         $revision = $this->request->getRequestParam('revision');
732
-        if (! empty($revision)) {
732
+        if ( ! empty($revision)) {
733 733
             $action = $this->request->getRequestParam('action');
734 734
             // doing a restore?
735
-            if (! empty($action) && $action === 'restore') {
735
+            if ( ! empty($action) && $action === 'restore') {
736 736
                 // get post for revision
737 737
                 $rev_post   = get_post($revision);
738 738
                 $rev_parent = get_post($rev_post->post_parent);
@@ -769,7 +769,7 @@  discard block
 block discarded – undo
769 769
     {
770 770
         $this->_set_model_object($post_id, true, 'trash');
771 771
         // if our cpt object isn't existent then get out immediately.
772
-        if (! $this->_cpt_model_obj instanceof EE_CPT_Base || $this->_cpt_model_obj->ID() !== $post_id) {
772
+        if ( ! $this->_cpt_model_obj instanceof EE_CPT_Base || $this->_cpt_model_obj->ID() !== $post_id) {
773 773
             return;
774 774
         }
775 775
         $this->trash_cpt_item($post_id);
@@ -788,7 +788,7 @@  discard block
 block discarded – undo
788 788
     {
789 789
         $this->_set_model_object($post_id, true, 'restore');
790 790
         // if our cpt object isn't existent then get out immediately.
791
-        if (! $this->_cpt_model_obj instanceof EE_CPT_Base || $this->_cpt_model_obj->ID() !== $post_id) {
791
+        if ( ! $this->_cpt_model_obj instanceof EE_CPT_Base || $this->_cpt_model_obj->ID() !== $post_id) {
792 792
             return;
793 793
         }
794 794
         $this->restore_cpt_item($post_id);
@@ -807,7 +807,7 @@  discard block
 block discarded – undo
807 807
     {
808 808
         $this->_set_model_object($post_id, true, 'delete');
809 809
         // if our cpt object isn't existent then get out immediately.
810
-        if (! $this->_cpt_model_obj instanceof EE_CPT_Base || $this->_cpt_model_obj->ID() !== $post_id) {
810
+        if ( ! $this->_cpt_model_obj instanceof EE_CPT_Base || $this->_cpt_model_obj->ID() !== $post_id) {
811 811
             return;
812 812
         }
813 813
         $this->delete_cpt_item($post_id);
@@ -826,7 +826,7 @@  discard block
 block discarded – undo
826 826
     {
827 827
         $label = ! empty($this->_cpt_object) ? $this->_cpt_object->labels->singular_name : $this->page_label;
828 828
         // verify event object
829
-        if (! $this->_cpt_model_obj instanceof EE_CPT_Base) {
829
+        if ( ! $this->_cpt_model_obj instanceof EE_CPT_Base) {
830 830
             throw new EE_Error(
831 831
                 sprintf(
832 832
                     esc_html__(
@@ -881,13 +881,13 @@  discard block
 block discarded – undo
881 881
         if ($this->_cpt_model_obj instanceof EE_CPT_Base) {
882 882
             // setup custom post status object for localize script but only if we've got a cpt object
883 883
             $statuses = $this->_cpt_model_obj->get_custom_post_statuses();
884
-            if (! empty($statuses)) {
884
+            if ( ! empty($statuses)) {
885 885
                 // get ALL statuses!
886 886
                 $statuses = $this->_cpt_model_obj->get_all_post_statuses();
887 887
                 // setup object
888 888
                 $ee_cpt_statuses = [];
889 889
                 foreach ($statuses as $status => $label) {
890
-                    $ee_cpt_statuses[ $status ] = [
890
+                    $ee_cpt_statuses[$status] = [
891 891
                         'label'      => $label,
892 892
                         'save_label' => sprintf(
893 893
                             wp_strip_all_tags(__('Save as %s', 'event_espresso')),
@@ -952,7 +952,7 @@  discard block
 block discarded – undo
952 952
                 $post->page_template = $page_template;
953 953
                 $page_templates      = wp_get_theme()->get_page_templates($post);
954 954
             }
955
-            if ($page_template !== 'default' && ! isset($page_templates[ $page_template ])) {
955
+            if ($page_template !== 'default' && ! isset($page_templates[$page_template])) {
956 956
                 EE_Error::add_error(
957 957
                     esc_html__('Invalid Page Template.', 'event_espresso'),
958 958
                     __FILE__,
@@ -982,7 +982,7 @@  discard block
 block discarded – undo
982 982
     {
983 983
         // only do this if we're actually processing one of our CPTs
984 984
         // if our cpt object isn't existent then get out immediately.
985
-        if (! $this->_cpt_model_obj instanceof EE_CPT_Base) {
985
+        if ( ! $this->_cpt_model_obj instanceof EE_CPT_Base) {
986 986
             return;
987 987
         }
988 988
         delete_post_meta($post_id, '_wp_trash_meta_status');
@@ -1007,7 +1007,7 @@  discard block
 block discarded – undo
1007 1007
         // global action
1008 1008
         do_action('AHEE_EE_Admin_Page_CPT__restore_revision', $post_id, $revision_id);
1009 1009
         // class specific action so you can limit hooking into a specific page.
1010
-        do_action('AHEE_EE_Admin_Page_CPT_' . get_class($this) . '__restore_revision', $post_id, $revision_id);
1010
+        do_action('AHEE_EE_Admin_Page_CPT_'.get_class($this).'__restore_revision', $post_id, $revision_id);
1011 1011
     }
1012 1012
 
1013 1013
 
@@ -1036,11 +1036,11 @@  discard block
 block discarded – undo
1036 1036
     public function modify_current_screen()
1037 1037
     {
1038 1038
         // ONLY do this if the current page_route IS a cpt route
1039
-        if (! $this->_cpt_route) {
1039
+        if ( ! $this->_cpt_route) {
1040 1040
             return;
1041 1041
         }
1042 1042
         // routing things REALLY early b/c this is a cpt admin page
1043
-        set_current_screen($this->_cpt_routes[ $this->_req_action ]);
1043
+        set_current_screen($this->_cpt_routes[$this->_req_action]);
1044 1044
         $this->_current_screen       = get_current_screen();
1045 1045
         $this->_current_screen->base = 'event-espresso';
1046 1046
         $this->_add_help_tabs(); // we make sure we add any help tabs back in!
@@ -1063,7 +1063,7 @@  discard block
 block discarded – undo
1063 1063
      */
1064 1064
     public function add_custom_editor_default_title(?string $title): ?string
1065 1065
     {
1066
-        return $this->_labels['editor_title'][ $this->_cpt_routes[ $this->_req_action ] ] ?? $title;
1066
+        return $this->_labels['editor_title'][$this->_cpt_routes[$this->_req_action]] ?? $title;
1067 1067
     }
1068 1068
 
1069 1069
 
@@ -1112,10 +1112,10 @@  discard block
 block discarded – undo
1112 1112
     {
1113 1113
         // we're also going to add the route value and the current page so we can direct autosave parsing correctly
1114 1114
         echo '
1115
-        <input type="hidden" name="ee_cpt_item_redirect_url" value="' . esc_url_raw($this->_admin_base_url) . '"/>
1115
+        <input type="hidden" name="ee_cpt_item_redirect_url" value="' . esc_url_raw($this->_admin_base_url).'"/>
1116 1116
         <div id="ee-cpt-hidden-inputs">
1117
-            <input type="hidden" id="current_route" name="current_route" value="' . esc_attr($this->_current_view) . '"/>
1118
-            <input type="hidden" id="current_page" name="current_page" value="' . esc_attr($this->page_slug) . '"/>
1117
+            <input type="hidden" id="current_route" name="current_route" value="' . esc_attr($this->_current_view).'"/>
1118
+            <input type="hidden" id="current_page" name="current_page" value="' . esc_attr($this->page_slug).'"/>
1119 1119
         </div>';
1120 1120
     }
1121 1121
 
@@ -1162,13 +1162,13 @@  discard block
 block discarded – undo
1162 1162
         $action = $this->request->getRequestParam('action');
1163 1163
         if (
1164 1164
             empty($action)
1165
-            || ! isset($this->_cpt_routes[ $action ])
1166
-            || $post->post_type !== $this->_cpt_routes[ $action ]
1165
+            || ! isset($this->_cpt_routes[$action])
1166
+            || $post->post_type !== $this->_cpt_routes[$action]
1167 1167
         ) {
1168 1168
             return $link;
1169 1169
         }
1170 1170
         $query_args = [
1171
-            'action' => $this->_cpt_edit_routes[ $post->post_type ] ?? 'edit',
1171
+            'action' => $this->_cpt_edit_routes[$post->post_type] ?? 'edit',
1172 1172
             'post'   => $id,
1173 1173
         ];
1174 1174
         return EE_Admin_Page_CPT::add_query_args_and_nonce($query_args, $this->_admin_base_url);
@@ -1192,15 +1192,15 @@  discard block
 block discarded – undo
1192 1192
         if (
1193 1193
             ! $post instanceof WP_Post
1194 1194
             || empty($action)
1195
-            || ! isset($this->_cpt_routes[ $action ])
1196
-            || $post->post_type !== $this->_cpt_routes[ $action ]
1195
+            || ! isset($this->_cpt_routes[$action])
1196
+            || $post->post_type !== $this->_cpt_routes[$action]
1197 1197
         ) {
1198 1198
             return $delete_link;
1199 1199
         }
1200 1200
         $this->_set_model_object($post->ID, true);
1201 1201
 
1202 1202
         // returns something like `trash_event` or `trash_attendee` or `trash_venue`
1203
-        $action = 'trash_' . str_replace('ee_', '', strtolower(get_class($this->_cpt_model_obj)));
1203
+        $action = 'trash_'.str_replace('ee_', '', strtolower(get_class($this->_cpt_model_obj)));
1204 1204
 
1205 1205
         return EE_Admin_Page::add_query_args_and_nonce(
1206 1206
             [
@@ -1228,7 +1228,7 @@  discard block
 block discarded – undo
1228 1228
         // we DO have a match so let's setup the url
1229 1229
         // we have to get the post to determine our route
1230 1230
         $post       = get_post($post_id);
1231
-        $edit_route = $this->_cpt_edit_routes[ $post->post_type ];
1231
+        $edit_route = $this->_cpt_edit_routes[$post->post_type];
1232 1232
         // shared query_args
1233 1233
         $query_args = ['action' => $edit_route, 'post' => $post_id];
1234 1234
 
@@ -1295,12 +1295,12 @@  discard block
 block discarded – undo
1295 1295
         $id       = empty($id) && is_object($post) ? $post->ID : null;
1296 1296
         $revision = $this->request->getRequestParam('revision', 0, 'int');
1297 1297
 
1298
-        $messages[ $post->post_type ] = [
1298
+        $messages[$post->post_type] = [
1299 1299
             0  => '', // Unused. Messages start at index 1.
1300 1300
             1  => sprintf(
1301 1301
                 esc_html__('%1$s updated. %2$sView %1$s%3$s', 'event_espresso'),
1302 1302
                 $this->_cpt_object->labels->singular_name,
1303
-                '<a href="' . esc_url(get_permalink($id)) . '">',
1303
+                '<a href="'.esc_url(get_permalink($id)).'">',
1304 1304
                 '</a>'
1305 1305
             ),
1306 1306
             2  => esc_html__('Custom field updated', 'event_espresso'),
@@ -1316,27 +1316,27 @@  discard block
 block discarded – undo
1316 1316
             6  => sprintf(
1317 1317
                 esc_html__('%1$s published. %2$sView %1$s%3$s', 'event_espresso'),
1318 1318
                 $this->_cpt_object->labels->singular_name,
1319
-                '<a href="' . esc_url(get_permalink($id)) . '">',
1319
+                '<a href="'.esc_url(get_permalink($id)).'">',
1320 1320
                 '</a>'
1321 1321
             ),
1322 1322
             7  => sprintf(esc_html__('%1$s saved.', 'event_espresso'), $this->_cpt_object->labels->singular_name),
1323 1323
             8  => sprintf(
1324 1324
                 esc_html__('%1$s submitted. %2$sPreview %1$s%3$s', 'event_espresso'),
1325 1325
                 $this->_cpt_object->labels->singular_name,
1326
-                '<a target="_blank" href="' . esc_url(add_query_arg('preview', 'true', get_permalink($id))) . '">',
1326
+                '<a target="_blank" href="'.esc_url(add_query_arg('preview', 'true', get_permalink($id))).'">',
1327 1327
                 '</a>'
1328 1328
             ),
1329 1329
             9  => sprintf(
1330 1330
                 esc_html__('%1$s scheduled for: %2$s. %3$s">Preview %1$s%3$s', 'event_espresso'),
1331 1331
                 $this->_cpt_object->labels->singular_name,
1332
-                '<strong>' . date_i18n('M j, Y @ G:i', strtotime($post->post_date)) . '</strong>',
1333
-                '<a target="_blank" href="' . esc_url(get_permalink($id)),
1332
+                '<strong>'.date_i18n('M j, Y @ G:i', strtotime($post->post_date)).'</strong>',
1333
+                '<a target="_blank" href="'.esc_url(get_permalink($id)),
1334 1334
                 '</a>'
1335 1335
             ),
1336 1336
             10 => sprintf(
1337 1337
                 esc_html__('%1$s draft updated. %2$s">Preview page%3$s', 'event_espresso'),
1338 1338
                 $this->_cpt_object->labels->singular_name,
1339
-                '<a target="_blank" href="' . esc_url(add_query_arg('preview', 'true', get_permalink($id))),
1339
+                '<a target="_blank" href="'.esc_url(add_query_arg('preview', 'true', get_permalink($id))),
1340 1340
                 '</a>'
1341 1341
             ),
1342 1342
         ];
@@ -1354,10 +1354,10 @@  discard block
 block discarded – undo
1354 1354
     {
1355 1355
         // gather template vars for WP_ADMIN_PATH . 'edit-form-advanced.php'
1356 1356
         global $post, $title, $post_type, $post_type_object;
1357
-        $post_type        = $this->_cpt_routes[ $this->_req_action ];
1357
+        $post_type        = $this->_cpt_routes[$this->_req_action];
1358 1358
         $post_type_object = $this->_cpt_object;
1359 1359
         $title            = $post_type_object->labels->add_new_item;
1360
-        $post             = $post = get_default_post_to_edit($this->_cpt_routes[ $this->_req_action ], true);
1360
+        $post             = $post = get_default_post_to_edit($this->_cpt_routes[$this->_req_action], true);
1361 1361
         add_action('admin_print_styles', [$this, 'add_new_admin_page_global']);
1362 1362
         // modify the default editor title field with default title.
1363 1363
         add_filter('enter_title_here', [$this, 'add_custom_editor_default_title']);
@@ -1391,15 +1391,15 @@  discard block
 block discarded – undo
1391 1391
             if ($creating) {
1392 1392
                 wp_enqueue_script('autosave');
1393 1393
             } elseif (
1394
-                isset($this->_cpt_routes[ $action ])
1395
-                && ! isset($this->_labels['hide_add_button_on_cpt_route'][ $action ])
1394
+                isset($this->_cpt_routes[$action])
1395
+                && ! isset($this->_labels['hide_add_button_on_cpt_route'][$action])
1396 1396
             ) {
1397 1397
                 $create_new_action = apply_filters(
1398 1398
                     'FHEE__EE_Admin_Page_CPT___edit_cpt_item__create_new_action',
1399 1399
                     'create_new',
1400 1400
                     $this
1401 1401
                 );
1402
-                $post_new_file     = EE_Admin_Page::add_query_args_and_nonce(
1402
+                $post_new_file = EE_Admin_Page::add_query_args_and_nonce(
1403 1403
                     [
1404 1404
                         'action' => $create_new_action,
1405 1405
                         'page'   => $this->page_slug,
@@ -1407,7 +1407,7 @@  discard block
 block discarded – undo
1407 1407
                     'admin.php'
1408 1408
                 );
1409 1409
             }
1410
-            include_once WP_ADMIN_PATH . 'edit-form-advanced.php';
1410
+            include_once WP_ADMIN_PATH.'edit-form-advanced.php';
1411 1411
         }
1412 1412
     }
1413 1413
 
@@ -1452,16 +1452,16 @@  discard block
 block discarded – undo
1452 1452
         }
1453 1453
 
1454 1454
         // template vars for WP_ADMIN_PATH . 'edit-form-advanced.php'
1455
-        $post_type        = $this->_cpt_routes[ $this->_req_action ];
1455
+        $post_type        = $this->_cpt_routes[$this->_req_action];
1456 1456
         $post_type_object = $this->_cpt_object;
1457
-        $title = $this->_labels['editor_title'][ $this->_cpt_routes[ $this->_req_action ] ]
1457
+        $title = $this->_labels['editor_title'][$this->_cpt_routes[$this->_req_action]]
1458 1458
                  ?? $post_type_object->labels->edit_item;
1459 1459
 
1460
-        if (! wp_check_post_lock($post->ID)) {
1460
+        if ( ! wp_check_post_lock($post->ID)) {
1461 1461
             wp_set_post_lock($post->ID);
1462 1462
         }
1463 1463
         add_action('admin_footer', '_admin_notice_post_locked');
1464
-        if (post_type_supports($this->_cpt_routes[ $this->_req_action ], 'comments')) {
1464
+        if (post_type_supports($this->_cpt_routes[$this->_req_action], 'comments')) {
1465 1465
             wp_enqueue_script('admin-comments');
1466 1466
             enqueue_comment_hotkeys_js();
1467 1467
         }
Please login to merge, or discard this patch.
core/admin/EE_Admin_Page.core.php 2 patches
Indentation   +4221 added lines, -4221 removed lines patch added patch discarded remove patch
@@ -24,4307 +24,4307 @@
 block discarded – undo
24 24
  */
25 25
 abstract class EE_Admin_Page extends EE_Base implements InterminableInterface
26 26
 {
27
-    protected ?EE_Admin_Config     $admin_config       = null;
27
+	protected ?EE_Admin_Config     $admin_config       = null;
28 28
 
29
-    protected ?EE_Admin_Hooks      $_hook_obj          = null;
29
+	protected ?EE_Admin_Hooks      $_hook_obj          = null;
30 30
 
31
-    protected ?EE_Admin_List_Table $_list_table_object = null;
31
+	protected ?EE_Admin_List_Table $_list_table_object = null;
32 32
 
33
-    protected ?EE_Registry         $EE                 = null;
33
+	protected ?EE_Registry         $EE                 = null;
34 34
 
35
-    protected ?FeatureFlags        $feature            = null;
35
+	protected ?FeatureFlags        $feature            = null;
36 36
 
37
-    protected ?LoaderInterface     $loader             = null;
37
+	protected ?LoaderInterface     $loader             = null;
38 38
 
39
-    protected ?RequestInterface    $request            = null;
39
+	protected ?RequestInterface    $request            = null;
40 40
 
41
-    protected ?WP_Screen           $_current_screen    = null;
41
+	protected ?WP_Screen           $_current_screen    = null;
42 42
 
43
-    /**
44
-     * @var array
45
-     * @since 5.0.0.p
46
-     */
47
-    private array $publish_post_meta_box_hidden_fields = [];
43
+	/**
44
+	 * @var array
45
+	 * @since 5.0.0.p
46
+	 */
47
+	private array $publish_post_meta_box_hidden_fields = [];
48 48
 
49
-    /**
50
-     * some default things shared by all child classes
51
-     *
52
-     * @var string[]
53
-     */
54
-    protected array $_default_espresso_metaboxes = [
55
-        '_espresso_news_post_box',
56
-        '_espresso_links_post_box',
57
-        '_espresso_ratings_request',
58
-        '_espresso_sponsors_post_box',
59
-    ];
49
+	/**
50
+	 * some default things shared by all child classes
51
+	 *
52
+	 * @var string[]
53
+	 */
54
+	protected array $_default_espresso_metaboxes = [
55
+		'_espresso_news_post_box',
56
+		'_espresso_links_post_box',
57
+		'_espresso_ratings_request',
58
+		'_espresso_sponsors_post_box',
59
+	];
60 60
 
61
-    /**
62
-     * Used to hold default query args for list table routes to help preserve stickiness of filters for carried out
63
-     * actions.
64
-     *
65
-     * @since 4.6.x
66
-     */
67
-    protected array $_default_route_query_args = [];
61
+	/**
62
+	 * Used to hold default query args for list table routes to help preserve stickiness of filters for carried out
63
+	 * actions.
64
+	 *
65
+	 * @since 4.6.x
66
+	 */
67
+	protected array $_default_route_query_args = [];
68 68
 
69
-    protected array $_labels                   = [];
69
+	protected array $_labels                   = [];
70 70
 
71
-    protected array $_nav_tabs                 = [];
71
+	protected array $_nav_tabs                 = [];
72 72
 
73
-    protected array $_page_config              = [];
73
+	protected array $_page_config              = [];
74 74
 
75
-    /**
76
-     * action => method pairs used for routing incoming requests
77
-     *
78
-     * @var array
79
-     */
80
-    protected array $_page_routes   = [];
75
+	/**
76
+	 * action => method pairs used for routing incoming requests
77
+	 *
78
+	 * @var array
79
+	 */
80
+	protected array $_page_routes   = [];
81 81
 
82
-    protected array $_req_data      = [];
82
+	protected array $_req_data      = [];
83 83
 
84
-    protected array $_route_config  = [];
84
+	protected array $_route_config  = [];
85 85
 
86
-    protected array $_template_args = [];
86
+	protected array $_template_args = [];
87 87
 
88
-    protected array $_views         = [];
88
+	protected array $_views         = [];
89 89
 
90
-    /**
91
-     * yes / no array for admin form fields
92
-     *
93
-     * @var array|array[]
94
-     */
95
-    protected array $_yes_no_values = [];
90
+	/**
91
+	 * yes / no array for admin form fields
92
+	 *
93
+	 * @var array|array[]
94
+	 */
95
+	protected array $_yes_no_values = [];
96 96
 
97
-    protected ?bool  $_is_UI_request = null;
98
-    // this starts at null so we can have no header routes progress through two states.
97
+	protected ?bool  $_is_UI_request = null;
98
+	// this starts at null so we can have no header routes progress through two states.
99 99
 
100
-    protected bool  $_is_caf        = false;                                                                                                                                                                                                                                  // This is just a property that flags whether the given route is a caffeinated route or not.
100
+	protected bool  $_is_caf        = false;                                                                                                                                                                                                                                  // This is just a property that flags whether the given route is a caffeinated route or not.
101 101
 
102
-    protected bool  $_routing       = false;
102
+	protected bool  $_routing       = false;
103 103
 
104
-    /**
105
-     * whether or not initializePage() has run
106
-     *
107
-     * @var bool
108
-     */
109
-    protected bool $initialized = false;
104
+	/**
105
+	 * whether or not initializePage() has run
106
+	 *
107
+	 * @var bool
108
+	 */
109
+	protected bool $initialized = false;
110 110
 
111 111
 
112
-    protected string $_admin_base_path      = '';
112
+	protected string $_admin_base_path      = '';
113 113
 
114
-    protected string $_admin_base_url       = '';
114
+	protected string $_admin_base_url       = '';
115 115
 
116
-    protected string $_admin_page_title     = '';
116
+	protected string $_admin_page_title     = '';
117 117
 
118
-    protected string $_column_template_path = '';
118
+	protected string $_column_template_path = '';
119 119
 
120
-    /**
121
-     * a boolean flag to set whether the current route is a cpt route or not.
122
-     *
123
-     * @var bool
124
-     */
125
-    protected bool $_cpt_route              = false;
120
+	/**
121
+	 * a boolean flag to set whether the current route is a cpt route or not.
122
+	 *
123
+	 * @var bool
124
+	 */
125
+	protected bool $_cpt_route              = false;
126 126
 
127
-    /**
128
-     * set via request page and action args.
129
-     *
130
-     * @var string
131
-     */
132
-    protected string $_current_page          = '';
127
+	/**
128
+	 * set via request page and action args.
129
+	 *
130
+	 * @var string
131
+	 */
132
+	protected string $_current_page          = '';
133 133
 
134
-    protected string $_current_page_view_url = '';
135
-
136
-    protected string $_current_view          = '';
137
-
138
-    protected string $_default_nav_tab_name  = 'overview';
139
-
140
-    /**
141
-     * sanitized request action
142
-     *
143
-     * @var string
144
-     */
145
-    protected string $_req_action = '';
146
-
147
-    /**
148
-     * sanitized request action nonce
149
-     *
150
-     * @var string
151
-     */
152
-    protected string $_req_nonce        = '';
153
-
154
-    protected string $_search_btn_label = '';
155
-
156
-    protected string $_template_path    = '';
157
-
158
-    protected string $_view             = '';
134
+	protected string $_current_page_view_url = '';
135
+
136
+	protected string $_current_view          = '';
137
+
138
+	protected string $_default_nav_tab_name  = 'overview';
139
+
140
+	/**
141
+	 * sanitized request action
142
+	 *
143
+	 * @var string
144
+	 */
145
+	protected string $_req_action = '';
146
+
147
+	/**
148
+	 * sanitized request action nonce
149
+	 *
150
+	 * @var string
151
+	 */
152
+	protected string $_req_nonce        = '';
153
+
154
+	protected string $_search_btn_label = '';
155
+
156
+	protected string $_template_path    = '';
157
+
158
+	protected string $_view             = '';
159 159
 
160
-    /**
161
-     * set early within EE_Admin_Init
162
-     *
163
-     * @var string
164
-     */
165
-    protected string $_wp_page_slug = '';
166
-
167
-    /**
168
-     * if the current class is an admin page extension, like: Extend_Events_Admin_Page,
169
-     * then this would be the parent classname: Events_Admin_Page
170
-     *
171
-     * @var string
172
-     */
173
-    protected string $base_class_name = '';
174
-
175
-    protected string $class_name    = '';
176
-
177
-    /**
178
-     * unprocessed value for the 'action' request param (default '')
179
-     *
180
-     * @var string
181
-     */
182
-    protected string $raw_req_action = '';
183
-
184
-    /**
185
-     * unprocessed value for the 'page' request param (default '')
186
-     *
187
-     * @var string
188
-     */
189
-    protected string $raw_req_page = '';
190
-
191
-    public string    $page_folder  = '';
192
-
193
-    public string    $page_label   = '';
194
-
195
-    public string    $page_slug    = '';
196
-
197
-
198
-    /**
199
-     * the current page route and route config
200
-     *
201
-     * @var array|callable|string|null
202
-     */
203
-    protected $_route = null;
204
-
205
-
206
-
207
-    /**
208
-     * @Constructor
209
-     * @param bool $routing indicate whether we want to just load the object and handle routing or just load the object.
210
-     * @throws InvalidArgumentException
211
-     * @throws InvalidDataTypeException
212
-     * @throws InvalidInterfaceException
213
-     * @throws ReflectionException
214
-     */
215
-    public function __construct($routing = true)
216
-    {
217
-        $this->loader       = LoaderFactory::getLoader();
218
-        $this->admin_config = $this->loader->getShared('EE_Admin_Config');
219
-        $this->feature      = $this->loader->getShared(FeatureFlags::class);
220
-        $this->request      = $this->loader->getShared(RequestInterface::class);
221
-        // routing enabled?
222
-        $this->_routing = $routing;
223
-
224
-        $this->class_name      = get_class($this);
225
-        $this->base_class_name = strpos($this->class_name, 'Extend_') === 0
226
-            ? str_replace('Extend_', '', $this->class_name)
227
-            : '';
228
-
229
-        if (strpos($this->_get_dir(), 'caffeinated') !== false) {
230
-            $this->_is_caf = true;
231
-        }
232
-        $this->_yes_no_values = [
233
-            ['id' => true, 'text' => esc_html__('Yes', 'event_espresso')],
234
-            ['id' => false, 'text' => esc_html__('No', 'event_espresso')],
235
-        ];
236
-        // set the _req_data property.
237
-        $this->_req_data = $this->request->requestParams();
238
-    }
239
-
240
-
241
-    /**
242
-     * @return EE_Admin_Config
243
-     */
244
-    public function adminConfig(): EE_Admin_Config
245
-    {
246
-        return $this->admin_config;
247
-    }
248
-
249
-
250
-    /**
251
-     * @return FeatureFlags
252
-     */
253
-    public function feature(): FeatureFlags
254
-    {
255
-        return $this->feature;
256
-    }
257
-
258
-
259
-    /**
260
-     * This logic used to be in the constructor, but that caused a chicken <--> egg scenario
261
-     * for child classes that needed to set properties prior to these methods getting called,
262
-     * but also needed the parent class to have its construction completed as well.
263
-     * Bottom line is that constructors should ONLY be used for setting initial properties
264
-     * and any complex initialization logic should only run after instantiation is complete.
265
-     *
266
-     * This method gets called immediately after construction from within
267
-     *      EE_Admin_Page_Init::_initialize_admin_page()
268
-     *
269
-     * @throws EE_Error
270
-     * @throws InvalidArgumentException
271
-     * @throws InvalidDataTypeException
272
-     * @throws InvalidInterfaceException
273
-     * @throws ReflectionException
274
-     * @since 5.0.0.p
275
-     */
276
-    public function initializePage()
277
-    {
278
-        if ($this->initialized) {
279
-            return;
280
-        }
281
-        // set initial page props (child method)
282
-        $this->_init_page_props();
283
-        // set global defaults
284
-        $this->_set_defaults();
285
-        // set early because incoming requests could be ajax related and we need to register those hooks.
286
-        $this->_global_ajax_hooks();
287
-        $this->_ajax_hooks();
288
-        // other_page_hooks have to be early too.
289
-        $this->_do_other_page_hooks();
290
-        // set up page dependencies
291
-        $this->_before_page_setup();
292
-        $this->_page_setup();
293
-        $this->initialized = true;
294
-    }
295
-
296
-
297
-    /**
298
-     * _init_page_props
299
-     * Child classes use to set at least the following properties:
300
-     * $page_slug.
301
-     * $page_label.
302
-     *
303
-     * @abstract
304
-     * @return void
305
-     */
306
-    abstract protected function _init_page_props();
307
-
308
-
309
-    /**
310
-     * _ajax_hooks
311
-     * child classes put all their add_action('wp_ajax_{name_of_hook}') hooks in here.
312
-     * Note: within the ajax callback methods.
313
-     *
314
-     * @abstract
315
-     * @return void
316
-     */
317
-    abstract protected function _ajax_hooks();
318
-
319
-
320
-    /**
321
-     * _define_page_props
322
-     * child classes define page properties in here.  Must include at least:
323
-     * $_admin_base_url = base_url for all admin pages
324
-     * $_admin_page_title = default admin_page_title for admin pages
325
-     * $_labels = array of default labels for various automatically generated elements:
326
-     *    array(
327
-     *        'buttons' => array(
328
-     *            'add' => esc_html__('label for add new button'),
329
-     *            'edit' => esc_html__('label for edit button'),
330
-     *            'delete' => esc_html__('label for delete button')
331
-     *            )
332
-     *        )
333
-     *
334
-     * @abstract
335
-     * @return void
336
-     */
337
-    abstract protected function _define_page_props();
338
-
339
-
340
-    /**
341
-     * _set_page_routes
342
-     * child classes use this to define the page routes for all subpages handled by the class.  Page routes are
343
-     * assigned to a action => method pairs in an array and to the $_page_routes property.  Each page route must also
344
-     * have a 'default' route. Here's the format
345
-     * $this->_page_routes = array(
346
-     *        'default' => array(
347
-     *            'func' => '_default_method_handling_route',
348
-     *            'args' => array('array','of','args'),
349
-     *            'noheader' => true, //add this in if this page route is processed before any headers are loaded (i.e.
350
-     *            ajax request, backend processing)
351
-     *            'headers_sent_route'=>'headers_route_reference', //add this if noheader=>true, and you want to load a
352
-     *            headers route after.  The string you enter here should match the defined route reference for a
353
-     *            headers sent route.
354
-     *            'capability' => 'route_capability', //indicate a string for minimum capability required to access
355
-     *            this route.
356
-     *            'obj_id' => 10 // if this route has an object id, then this can include it (used for capability
357
-     *            checks).
358
-     *        ),
359
-     *        'insert_item' => '_method_for_handling_insert_item' //this can be used if all we need to have is a
360
-     *        handling method.
361
-     *        )
362
-     * )
363
-     *
364
-     * @abstract
365
-     * @return void
366
-     */
367
-    abstract protected function _set_page_routes();
368
-
369
-
370
-    /**
371
-     * _set_page_config
372
-     * child classes use this to define the _page_config array for all subpages handled by the class. Each key in the
373
-     * array corresponds to the page_route for the loaded page. Format:
374
-     * $this->_page_config = array(
375
-     *        'default' => array(
376
-     *            'labels' => array(
377
-     *                'buttons' => array(
378
-     *                    'add' => esc_html__('label for adding item'),
379
-     *                    'edit' => esc_html__('label for editing item'),
380
-     *                    'delete' => esc_html__('label for deleting item')
381
-     *                ),
382
-     *                'publishbox' => esc_html__('Localized Title for Publish metabox', 'event_espresso')
383
-     *            ), //optional an array of custom labels for various automatically generated elements to use on the
384
-     *            page. If this isn't present then the defaults will be used as set for the $this->_labels in
385
-     *            _define_page_props() method
386
-     *            'nav' => array(
387
-     *                'label' => esc_html__('Label for Tab', 'event_espresso').
388
-     *                'url' => 'http://someurl', //automatically generated UNLESS you define
389
-     *                'css_class' => 'css-class', //automatically generated UNLESS you define
390
-     *                'order' => 10, //required to indicate tab position.
391
-     *                'persistent' => false //if you want the nav tab to ONLY display when the specific route is
392
-     *                displayed then add this parameter.
393
-     *            'list_table' => 'name_of_list_table' //string for list table class to be loaded for this admin_page.
394
-     *            'metaboxes' => array('metabox1', 'metabox2'), //if present this key indicates we want to load
395
-     *            metaboxes set for eventespresso admin pages.
396
-     *            'has_metaboxes' => true, //this boolean flag can simply be used to indicate if the route will have
397
-     *            metaboxes.  Typically this is used if the 'metaboxes' index is not used because metaboxes are added
398
-     *            later.  We just use this flag to make sure the necessary js gets enqueued on page load.
399
-     *            'has_help_popups' => false //defaults(true) //this boolean flag can simply be used to indicate if the
400
-     *            given route has help popups setup and if it does then we need to make sure thickbox is enqueued.
401
-     *            'columns' => array(4, 2), //this key triggers the setup of a page that uses columns (metaboxes).  The
402
-     *            array indicates the max number of columns (4) and the default number of columns on page load (2).
403
-     *            There is an option in the "screen_options" dropdown that is setup so users can pick what columns they
404
-     *            want to display.
405
-     *            'help_tabs' => array( //this is used for adding help tabs to a page
406
-     *                'tab_id' => array(
407
-     *                    'title' => 'tab_title',
408
-     *                    'filename' => 'name_of_file_containing_content', //this is the primary method for setting
409
-     *                    help tab content.  The fallback if it isn't present is to try a the callback.  Filename
410
-     *                    should match a file in the admin folder's "help_tabs" dir (ie..
411
-     *                    events/help_tabs/name_of_file_containing_content.help_tab.php)
412
-     *                    'callback' => 'callback_method_for_content', //if 'filename' isn't present then system will
413
-     *                    attempt to use the callback which should match the name of a method in the class
414
-     *                    ),
415
-     *                'tab2_id' => array(
416
-     *                    'title' => 'tab2 title',
417
-     *                    'filename' => 'file_name_2'
418
-     *                    'callback' => 'callback_method_for_content',
419
-     *                 ),
420
-     *            'help_sidebar' => 'callback_for_sidebar_content', //this is used for setting up the sidebar in the
421
-     *            help tab area on an admin page. @return void
422
-     *
423
-     * @abstract
424
-     */
425
-    abstract protected function _set_page_config();
426
-
427
-
428
-    /**
429
-     * _add_screen_options
430
-     * Child classes can add any extra wp_screen_options within this method using built-in WP functions/methods for
431
-     * doing so. Note child classes can also define _add_screen_options_($this->_current_view) to limit screen options
432
-     * to a particular view.
433
-     *
434
-     * @link   http://chrismarslender.com/wp-tutorials/wordpress-screen-options-tutorial/
435
-     *         see also WP_Screen object documents...
436
-     * @link   http://codex.wordpress.org/Class_Reference/WP_Screen
437
-     * @abstract
438
-     * @return void
439
-     */
440
-    abstract protected function _add_screen_options();
441
-
442
-
443
-    /**
444
-     * _add_feature_pointers
445
-     * Child classes should use this method for implementing any "feature pointers" (using built-in WP styling js).
446
-     * Note child classes can also define _add_feature_pointers_($this->_current_view) to limit screen options to a
447
-     * particular view. Note: this is just a placeholder for now.  Implementation will come down the road See:
448
-     * WP_Internal_Pointers class in wp-admin/includes/template.php for example (its a final class so can't be
449
-     * extended) also see:
450
-     *
451
-     * @link   http://eamann.com/tech/wordpress-portland/
452
-     * @abstract
453
-     * @return void
454
-     */
455
-    abstract protected function _add_feature_pointers();
456
-
457
-
458
-    /**
459
-     * load_scripts_styles
460
-     * child classes put their wp_enqueue_script and wp_enqueue_style hooks in here for anything they need loaded for
461
-     * their pages/subpages.  Note this is for all pages/subpages of the system.  You can also load only specific
462
-     * scripts/styles per view by putting them in a dynamic function in this format
463
-     * (load_scripts_styles_{$this->_current_view}) which matches your page route (action request arg)
464
-     *
465
-     * @abstract
466
-     * @return void
467
-     */
468
-    abstract public function load_scripts_styles();
469
-
470
-
471
-    /**
472
-     * admin_init
473
-     * Anything that should be set/executed at 'admin_init' WP hook runtime should be put in here.  This will apply to
474
-     * all pages/views loaded by child class.
475
-     *
476
-     * @abstract
477
-     * @return void
478
-     */
479
-    abstract public function admin_init();
480
-
481
-
482
-    /**
483
-     * admin_notices
484
-     * Anything triggered by the 'admin_notices' WP hook should be put in here.  This particular method will apply to
485
-     * all pages/views loaded by child class.
486
-     *
487
-     * @abstract
488
-     * @return void
489
-     */
490
-    abstract public function admin_notices();
491
-
492
-
493
-    /**
494
-     * admin_footer_scripts
495
-     * Anything triggered by the 'admin_print_footer_scripts' WP hook should be put in here. This particular method
496
-     * will apply to all pages/views loaded by child class.
497
-     *
498
-     * @return void
499
-     */
500
-    abstract public function admin_footer_scripts();
501
-
502
-
503
-    /**
504
-     * admin_footer
505
-     * anything triggered by the 'admin_footer' WP action hook should be added to here. This particular method will
506
-     * apply to all pages/views loaded by child class.
507
-     *
508
-     * @return void
509
-     */
510
-    public function admin_footer()
511
-    {
512
-    }
513
-
514
-
515
-    /**
516
-     * _global_ajax_hooks
517
-     * all global add_action('wp_ajax_{name_of_hook}') hooks in here.
518
-     * Note: within the ajax callback methods.
519
-     *
520
-     * @abstract
521
-     * @return void
522
-     */
523
-    protected function _global_ajax_hooks()
524
-    {
525
-        // for lazy loading of metabox content
526
-        add_action('wp_ajax_espresso-ajax-content', [$this, 'ajax_metabox_content'], 10);
527
-
528
-        add_action(
529
-            'wp_ajax_espresso_hide_status_change_notice',
530
-            [$this, 'hideStatusChangeNotice']
531
-        );
532
-        add_action(
533
-            'wp_ajax_nopriv_espresso_hide_status_change_notice',
534
-            [$this, 'hideStatusChangeNotice']
535
-        );
536
-    }
537
-
538
-
539
-    public function ajax_metabox_content()
540
-    {
541
-        $content_id  = $this->request->getRequestParam('contentid', '');
542
-        $content_url = $this->request->getRequestParam('contenturl', '', 'url');
543
-        EE_Admin_Page::cached_rss_display($content_id, $content_url);
544
-        wp_die();
545
-    }
546
-
547
-
548
-    public function hideStatusChangeNotice()
549
-    {
550
-        $response = [];
551
-        try {
552
-            /** @var StatusChangeNotice $status_change_notice */
553
-            $status_change_notice = $this->loader->getShared(
554
-                'EventEspresso\core\domain\services\admin\notices\status_change\StatusChangeNotice'
555
-            );
556
-            $response['success']  = $status_change_notice->dismiss() > -1;
557
-        } catch (Exception $exception) {
558
-            $response['errors'] = $exception->getMessage();
559
-        }
560
-        echo wp_json_encode($response);
561
-        exit();
562
-    }
563
-
564
-
565
-    /**
566
-     * allows extending classes do something specific before the parent constructor runs _page_setup().
567
-     *
568
-     * @return void
569
-     */
570
-    protected function _before_page_setup()
571
-    {
572
-        // default is to do nothing
573
-    }
574
-
575
-
576
-    /**
577
-     * Makes sure any things that need to be loaded early get handled.
578
-     * We also escape early here if the page requested doesn't match the object.
579
-     *
580
-     * @final
581
-     * @return void
582
-     * @throws EE_Error
583
-     * @throws InvalidArgumentException
584
-     * @throws ReflectionException
585
-     * @throws InvalidDataTypeException
586
-     * @throws InvalidInterfaceException
587
-     */
588
-    final protected function _page_setup()
589
-    {
590
-        // requires?
591
-        // admin_init stuff - global - we're setting this REALLY early
592
-        // so if EE_Admin pages have to hook into other WP pages they can.
593
-        // But keep in mind, not everything is available from the EE_Admin Page object at this point.
594
-        add_action('admin_init', [$this, 'admin_init_global'], 5);
595
-        // next verify if we need to load anything...
596
-        $this->_current_page = $this->request->getRequestParam('page', '', 'key');
597
-        $this->_current_page = $this->request->getRequestParam('current_page', $this->_current_page, 'key');
598
-        $this->page_folder   = strtolower(
599
-            str_replace(['_Admin_Page', 'Extend_'], '', $this->class_name)
600
-        );
601
-        global $ee_menu_slugs;
602
-        $ee_menu_slugs = (array) $ee_menu_slugs;
603
-        if (
604
-            ! $this->request->isAjax()
605
-            && (! $this->_current_page || ! isset($ee_menu_slugs[ $this->_current_page ]))
606
-        ) {
607
-            return;
608
-        }
609
-        // because WP List tables have two duplicate select inputs for choosing bulk actions,
610
-        // we need to copy the action from the second to the first
611
-        $action     = $this->request->getRequestParam('action', '-1', 'key');
612
-        $action2    = $this->request->getRequestParam('action2', '-1', 'key');
613
-        $action     = $action !== '-1' ? $action : $action2;
614
-        $req_action = $action !== '-1' ? $action : 'default';
615
-
616
-        // if a specific 'route' has been set, and the action is 'default' OR we are doing_ajax
617
-        // then let's use the route as the action.
618
-        // This covers cases where we're coming in from a list table that isn't on the default route.
619
-        $route             = $this->request->getRequestParam('route');
620
-        $this->_req_action = $route && ($req_action === 'default' || $this->request->isAjax())
621
-            ? $route
622
-            : $req_action;
623
-
624
-        $this->_current_view = $this->_req_action;
625
-        $this->_req_nonce    = $this->_req_action . '_nonce';
626
-        $this->_define_page_props();
627
-        $this->_current_page_view_url = add_query_arg(
628
-            ['page' => $this->_current_page, 'action' => $this->_current_view],
629
-            $this->_admin_base_url
630
-        );
631
-        // set page configs
632
-        $this->_set_page_routes();
633
-        $this->_set_page_config();
634
-        // let's include any referrer data in our default_query_args for this route for "stickiness".
635
-        if ($this->request->requestParamIsSet('wp_referer')) {
636
-            $wp_referer = $this->request->getRequestParam('wp_referer');
637
-            if ($wp_referer) {
638
-                $this->_default_route_query_args['wp_referer'] = $wp_referer;
639
-            }
640
-        }
641
-        // for CPT and other extended functionality.
642
-        // If there is an _extend_page_config_for_cpt
643
-        // then let's run that to modify all the various page configuration arrays.
644
-        if (method_exists($this, '_extend_page_config_for_cpt')) {
645
-            $this->_extend_page_config_for_cpt();
646
-        }
647
-        // filter routes and page_config so addons can add their stuff. Filtering done per class
648
-        $this->_page_routes = apply_filters(
649
-            'FHEE__' . $this->class_name . '__page_setup__page_routes',
650
-            $this->_page_routes,
651
-            $this
652
-        );
653
-        $this->_page_config = apply_filters(
654
-            'FHEE__' . $this->class_name . '__page_setup__page_config',
655
-            $this->_page_config,
656
-            $this
657
-        );
658
-        if ($this->base_class_name !== '') {
659
-            $this->_page_routes = apply_filters(
660
-                'FHEE__' . $this->base_class_name . '__page_setup__page_routes',
661
-                $this->_page_routes,
662
-                $this
663
-            );
664
-            $this->_page_config = apply_filters(
665
-                'FHEE__' . $this->base_class_name . '__page_setup__page_config',
666
-                $this->_page_config,
667
-                $this
668
-            );
669
-        }
670
-        // if AHEE__EE_Admin_Page__route_admin_request_$this->_current_view method is present
671
-        // then we call it hooked into the AHEE__EE_Admin_Page__route_admin_request action
672
-        if (method_exists($this, 'AHEE__EE_Admin_Page__route_admin_request_' . $this->_current_view)) {
673
-            add_action(
674
-                'AHEE__EE_Admin_Page__route_admin_request',
675
-                [$this, 'AHEE__EE_Admin_Page__route_admin_request_' . $this->_current_view],
676
-                10,
677
-                2
678
-            );
679
-        }
680
-        // next route only if routing enabled
681
-        if ($this->_routing && ! $this->request->isAjax()) {
682
-            $this->_verify_routes();
683
-            // next let's just check user_access and kill if no access
684
-            $this->check_user_access();
685
-            if ($this->_is_UI_request) {
686
-                // admin_init stuff - global, all views for this page class, specific view
687
-                add_action('admin_init', [$this, 'admin_init'], 10);
688
-                if (method_exists($this, 'admin_init_' . $this->_current_view)) {
689
-                    add_action('admin_init', [$this, 'admin_init_' . $this->_current_view], 15);
690
-                }
691
-            } else {
692
-                // hijack regular WP loading and route admin request immediately
693
-                @ini_set('memory_limit', apply_filters('admin_memory_limit', WP_MAX_MEMORY_LIMIT));
694
-                $this->route_admin_request();
695
-            }
696
-        }
697
-    }
698
-
699
-
700
-    /**
701
-     * Provides a way for related child admin pages to load stuff on the loaded admin page.
702
-     *
703
-     * @return void
704
-     * @throws EE_Error
705
-     */
706
-    private function _do_other_page_hooks()
707
-    {
708
-        $registered_pages = apply_filters('FHEE_do_other_page_hooks_' . $this->page_slug, []);
709
-        foreach ($registered_pages as $page) {
710
-            // now let's setup the file name and class that should be present
711
-            $classname = str_replace('.class.php', '', $page);
712
-            // autoloaders should take care of loading file
713
-            if (! class_exists($classname)) {
714
-                $error_msg[] = sprintf(
715
-                    esc_html__(
716
-                        'Something went wrong with loading the %s admin hooks page.',
717
-                        'event_espresso'
718
-                    ),
719
-                    $page
720
-                );
721
-                $error_msg[] = $error_msg[0]
722
-                               . "\r\n"
723
-                               . sprintf(
724
-                                   esc_html__(
725
-                                       'There is no class in place for the %1$s admin hooks page.%2$sMake sure you have %3$s defined. If this is a non-EE-core admin page then you also must have an autoloader in place for your class',
726
-                                       'event_espresso'
727
-                                   ),
728
-                                   $page,
729
-                                   '<br />',
730
-                                   '<strong>' . $classname . '</strong>'
731
-                               );
732
-                throw new EE_Error(implode('||', $error_msg));
733
-            }
734
-            // notice we are passing the instance of this class to the hook object.
735
-            $this->loader->getShared($classname, [$this]);
736
-        }
737
-    }
738
-
739
-
740
-    /**
741
-     * @throws ReflectionException
742
-     * @throws EE_Error
743
-     */
744
-    public function load_page_dependencies()
745
-    {
746
-        try {
747
-            $this->_load_page_dependencies();
748
-        } catch (EE_Error $e) {
749
-            $e->get_error();
750
-        }
751
-    }
752
-
753
-
754
-    /**
755
-     * load_page_dependencies
756
-     * loads things specific to this page class when its loaded.  Really helps with efficiency.
757
-     *
758
-     * @return void
759
-     * @throws DomainException
760
-     * @throws EE_Error
761
-     * @throws InvalidArgumentException
762
-     * @throws InvalidDataTypeException
763
-     * @throws InvalidInterfaceException
764
-     * @throws ReflectionException
765
-     */
766
-    protected function _load_page_dependencies()
767
-    {
768
-        // let's set the current_screen and screen options to override what WP set
769
-        $this->_current_screen = get_current_screen();
770
-        // load admin_notices - global, page class, and view specific
771
-        add_action('admin_notices', [$this, 'admin_notices_global'], 5);
772
-        add_action('admin_notices', [$this, 'admin_notices'], 10);
773
-        if (method_exists($this, 'admin_notices_' . $this->_current_view)) {
774
-            add_action('admin_notices', [$this, 'admin_notices_' . $this->_current_view], 15);
775
-        }
776
-        // load network admin_notices - global, page class, and view specific
777
-        add_action('network_admin_notices', [$this, 'network_admin_notices_global'], 5);
778
-        if (method_exists($this, 'network_admin_notices_' . $this->_current_view)) {
779
-            add_action('network_admin_notices', [$this, 'network_admin_notices_' . $this->_current_view]);
780
-        }
781
-        // this will save any per_page screen options if they are present
782
-        $this->_set_per_page_screen_options();
783
-        // setup list table properties
784
-        $this->_set_list_table();
785
-        // child classes can "register" a metabox to be automatically handled via the _page_config array property.
786
-        // However in some cases the metaboxes will need to be added within a route handling callback.
787
-        add_action('add_meta_boxes', [$this, 'addRegisteredMetaBoxes'], 99);
788
-        // hack because promos admin was loading the edited promotion object in the metaboxes callback
789
-        // which should NOT be generated on non-UI requests like POST updates/inserts
790
-        if (
791
-            $this->class_name === 'Promotions_Admin_Page'
792
-            && ($this->_req_action === 'edit' || $this->_req_action === 'create_new')
793
-        ) {
794
-            $this->addRegisteredMetaBoxes();
795
-        }
796
-        $this->_add_screen_columns();
797
-        // add screen options - global, page child class, and view specific
798
-        $this->_add_global_screen_options();
799
-        $this->_add_screen_options();
800
-        $add_screen_options = "_add_screen_options_{$this->_current_view}";
801
-        if (method_exists($this, $add_screen_options)) {
802
-            $this->{$add_screen_options}();
803
-        }
804
-        // add help tab(s) - set via page_config and qtips.
805
-        $this->_add_help_tabs();
806
-        $this->_add_qtips();
807
-        // add feature_pointers - global, page child class, and view specific
808
-        $this->_add_feature_pointers();
809
-        $this->_add_global_feature_pointers();
810
-        $add_feature_pointer = "_add_feature_pointer_{$this->_current_view}";
811
-        if (method_exists($this, $add_feature_pointer)) {
812
-            $this->{$add_feature_pointer}();
813
-        }
814
-        // enqueue scripts/styles - global, page class, and view specific
815
-        add_action('admin_enqueue_scripts', [$this, 'load_global_scripts_styles'], 5);
816
-        add_action('admin_enqueue_scripts', [$this, 'load_scripts_styles'], 10);
817
-        if (method_exists($this, "load_scripts_styles_{$this->_current_view}")) {
818
-            add_action('admin_enqueue_scripts', [$this, "load_scripts_styles_{$this->_current_view}"], 15);
819
-        }
820
-        add_action('admin_enqueue_scripts', [$this, 'admin_footer_scripts_eei18n_js_strings'], 100);
821
-        // admin_print_footer_scripts - global, page child class, and view specific.
822
-        // NOTE, despite the name, whenever possible, scripts should NOT be loaded using this.
823
-        // In most cases that's doing_it_wrong().  But adding hidden container elements etc.
824
-        // is a good use case. Notice the late priority we're giving these
825
-        add_action('admin_print_footer_scripts', [$this, 'admin_footer_scripts_global'], 99);
826
-        add_action('admin_print_footer_scripts', [$this, 'admin_footer_scripts'], 100);
827
-        if (method_exists($this, "admin_footer_scripts_{$this->_current_view}")) {
828
-            add_action('admin_print_footer_scripts', [$this, "admin_footer_scripts_{$this->_current_view}"], 101);
829
-        }
830
-        // admin footer scripts
831
-        add_action('admin_footer', [$this, 'admin_footer_global'], 99);
832
-        add_action('admin_footer', [$this, 'admin_footer'], 100);
833
-        if (method_exists($this, "admin_footer_{$this->_current_view}")) {
834
-            add_action('admin_footer', [$this, "admin_footer_{$this->_current_view}"], 101);
835
-        }
836
-        do_action('FHEE__EE_Admin_Page___load_page_dependencies__after_load', $this->page_slug);
837
-        // targeted hook
838
-        do_action(
839
-            "FHEE__EE_Admin_Page___load_page_dependencies__after_load__{$this->page_slug}__{$this->_req_action}"
840
-        );
841
-    }
842
-
843
-
844
-    /**
845
-     * _set_defaults
846
-     * This sets some global defaults for class properties.
847
-     */
848
-    private function _set_defaults()
849
-    {
850
-        // init template args
851
-        $this->set_template_args(
852
-            [
853
-                'admin_page_header'  => '',
854
-                'admin_page_content' => '',
855
-                'post_body_content'  => '',
856
-                'before_list_table'  => '',
857
-                'after_list_table'   => '',
858
-            ]
859
-        );
860
-    }
861
-
862
-
863
-    /**
864
-     * route_admin_request
865
-     *
866
-     * @return void
867
-     * @throws InvalidArgumentException
868
-     * @throws InvalidInterfaceException
869
-     * @throws InvalidDataTypeException
870
-     * @throws EE_Error
871
-     * @throws ReflectionException
872
-     * @see    _route_admin_request()
873
-     */
874
-    public function route_admin_request()
875
-    {
876
-        try {
877
-            $this->_route_admin_request();
878
-        } catch (EE_Error $e) {
879
-            $e->get_error();
880
-        }
881
-    }
882
-
883
-
884
-    public function set_wp_page_slug($wp_page_slug)
885
-    {
886
-        $this->_wp_page_slug = $wp_page_slug;
887
-        // if in network admin then we need to append "-network" to the page slug. Why? Because that's how WP rolls...
888
-        if (is_network_admin()) {
889
-            $this->_wp_page_slug .= '-network';
890
-        }
891
-    }
892
-
893
-
894
-    /**
895
-     * _verify_routes
896
-     * All this method does is verify the incoming request and make sure that routes exist for it.  We do this early so
897
-     * we know if we need to drop out.
898
-     *
899
-     * @return bool
900
-     * @throws EE_Error
901
-     */
902
-    protected function _verify_routes()
903
-    {
904
-        do_action('AHEE_log', __FILE__, __FUNCTION__, '');
905
-        if (! $this->_current_page && ! $this->request->isAjax()) {
906
-            return false;
907
-        }
908
-        // check that the page_routes array is not empty
909
-        if (empty($this->_page_routes)) {
910
-            // user error msg
911
-            $error_msg = sprintf(
912
-                esc_html__('No page routes have been set for the %s admin page.', 'event_espresso'),
913
-                $this->_admin_page_title
914
-            );
915
-            // developer error msg
916
-            $error_msg .= '||' . $error_msg
917
-                          . esc_html__(
918
-                              ' Make sure the "set_page_routes()" method exists, and is setting the "_page_routes" array properly.',
919
-                              'event_espresso'
920
-                          );
921
-            throw new EE_Error($error_msg);
922
-        }
923
-        // and that the requested page route exists
924
-        if (array_key_exists($this->_req_action, $this->_page_routes)) {
925
-            $this->_route        = $this->_page_routes[ $this->_req_action ];
926
-            $this->_route_config = $this->_page_config[ $this->_req_action ] ?? [];
927
-        } else {
928
-            // user error msg
929
-            $error_msg = sprintf(
930
-                esc_html__(
931
-                    'The requested page route does not exist for the %s admin page.',
932
-                    'event_espresso'
933
-                ),
934
-                $this->_admin_page_title
935
-            );
936
-            // developer error msg
937
-            $error_msg .= '||' . $error_msg
938
-                          . sprintf(
939
-                              esc_html__(
940
-                                  ' Create a key in the "_page_routes" array named "%s" and set its value to the appropriate method.',
941
-                                  'event_espresso'
942
-                              ),
943
-                              $this->_req_action
944
-                          );
945
-            throw new EE_Error($error_msg);
946
-        }
947
-        // and that a default route exists
948
-        if (! array_key_exists('default', $this->_page_routes)) {
949
-            // user error msg
950
-            $error_msg = sprintf(
951
-                esc_html__(
952
-                    'A default page route has not been set for the % admin page.',
953
-                    'event_espresso'
954
-                ),
955
-                $this->_admin_page_title
956
-            );
957
-            // developer error msg
958
-            $error_msg .= '||' . $error_msg
959
-                          . esc_html__(
960
-                              ' Create a key in the "_page_routes" array named "default" and set its value to your default page method.',
961
-                              'event_espresso'
962
-                          );
963
-            throw new EE_Error($error_msg);
964
-        }
965
-
966
-        // first lets' catch if the UI request has EVER been set.
967
-        if ($this->_is_UI_request === null) {
968
-            // lets set if this is a UI request or not.
969
-            $this->_is_UI_request = ! $this->request->getRequestParam('noheader', false, 'bool');
970
-            // wait a minute... we might have a noheader in the route array
971
-            $this->_is_UI_request = ! (
972
-                is_array($this->_route) && isset($this->_route['noheader']) && $this->_route['noheader']
973
-            )
974
-                ? $this->_is_UI_request
975
-                : false;
976
-        }
977
-        $this->_set_current_labels();
978
-        return true;
979
-    }
980
-
981
-
982
-    /**
983
-     * this method simply verifies a given route and makes sure its an actual route available for the loaded page
984
-     *
985
-     * @param string $route the route name we're verifying
986
-     * @return bool we'll throw an exception if this isn't a valid route.
987
-     * @throws EE_Error
988
-     */
989
-    protected function _verify_route($route)
990
-    {
991
-        if (array_key_exists($this->_req_action, $this->_page_routes)) {
992
-            return true;
993
-        }
994
-        // user error msg
995
-        $error_msg = sprintf(
996
-            esc_html__('The given page route does not exist for the %s admin page.', 'event_espresso'),
997
-            $this->_admin_page_title
998
-        );
999
-        // developer error msg
1000
-        $error_msg .= '||' . $error_msg
1001
-                      . sprintf(
1002
-                          esc_html__(
1003
-                              ' Check the route you are using in your method (%s) and make sure it matches a route set in your "_page_routes" array property',
1004
-                              'event_espresso'
1005
-                          ),
1006
-                          $route
1007
-                      );
1008
-        throw new EE_Error($error_msg);
1009
-    }
1010
-
1011
-
1012
-    /**
1013
-     * perform nonce verification
1014
-     * This method has be encapsulated here so that any ajax requests that bypass normal routes can verify their nonces
1015
-     * using this method (and save retyping!)
1016
-     *
1017
-     * @param string $nonce     The nonce sent
1018
-     * @param string $nonce_ref The nonce reference string (name0)
1019
-     * @return void
1020
-     * @throws EE_Error
1021
-     * @throws InvalidArgumentException
1022
-     * @throws InvalidDataTypeException
1023
-     * @throws InvalidInterfaceException
1024
-     */
1025
-    protected function _verify_nonce($nonce, $nonce_ref)
1026
-    {
1027
-        // verify nonce against expected value
1028
-        if (! wp_verify_nonce($nonce, $nonce_ref)) {
1029
-            // these are not the droids you are looking for !!!
1030
-            $msg = sprintf(
1031
-                esc_html__('%sNonce Fail.%s', 'event_espresso'),
1032
-                '<a href="https://www.youtube.com/watch?v=56_S0WeTkzs">',
1033
-                '</a>'
1034
-            );
1035
-            if (WP_DEBUG) {
1036
-                $msg .= "\n  ";
1037
-                $msg .= sprintf(
1038
-                    esc_html__(
1039
-                        'In order to dynamically generate nonces for your actions, use the %s::add_query_args_and_nonce() method. May the Nonce be with you!',
1040
-                        'event_espresso'
1041
-                    ),
1042
-                    __CLASS__
1043
-                );
1044
-            }
1045
-            if (! $this->request->isAjax()) {
1046
-                wp_die($msg);
1047
-            }
1048
-            EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
1049
-            $this->_return_json();
1050
-        }
1051
-    }
1052
-
1053
-
1054
-    /**
1055
-     * _route_admin_request()
1056
-     * Meat and potatoes of the class.  Basically, this dude checks out what's being requested and sees if there are
1057
-     * some doodads to work the magic and handle the flingjangy. Translation:  Checks if the requested action is listed
1058
-     * in the page routes and then will try to load the corresponding method.
1059
-     *
1060
-     * @return void
1061
-     * @throws EE_Error
1062
-     * @throws InvalidArgumentException
1063
-     * @throws InvalidDataTypeException
1064
-     * @throws InvalidInterfaceException
1065
-     * @throws ReflectionException
1066
-     */
1067
-    protected function _route_admin_request()
1068
-    {
1069
-        if (! $this->_is_UI_request) {
1070
-            $this->_verify_routes();
1071
-        }
1072
-        $nonce_check = ! isset($this->_route_config['require_nonce']) || $this->_route_config['require_nonce'];
1073
-        if ($this->_req_action !== 'default' && $nonce_check) {
1074
-            // set nonce from post data
1075
-            $nonce = $this->request->getRequestParam($this->_req_nonce, '');
1076
-            $this->_verify_nonce($nonce, $this->_req_nonce);
1077
-        }
1078
-        // set the nav_tabs array but ONLY if this is  UI_request
1079
-        if ($this->_is_UI_request) {
1080
-            $this->_set_nav_tabs();
1081
-        }
1082
-        // grab callback function
1083
-        $func = is_array($this->_route) && isset($this->_route['func'])
1084
-            ? $this->_route['func']
1085
-            : $this->_route;
1086
-        // check if callback has args
1087
-        $args = is_array($this->_route) && isset($this->_route['args']) ? $this->_route['args'] : [];
1088
-        // action right before calling route
1089
-        // (hook is something like 'AHEE__Registrations_Admin_Page__route_admin_request')
1090
-        if (! did_action('AHEE__EE_Admin_Page__route_admin_request')) {
1091
-            do_action('AHEE__EE_Admin_Page__route_admin_request', $this->_current_view, $this);
1092
-        }
1093
-        // strip _wp_http_referer from the server REQUEST_URI
1094
-        // else it grows in length on every submission due to recursion,
1095
-        // ultimately causing a "Request-URI Too Large" error
1096
-        $this->request->unSetRequestParam('_wp_http_referer');
1097
-        $this->request->unSetServerParam('_wp_http_referer');
1098
-        $cleaner_request_uri = remove_query_arg(
1099
-            '_wp_http_referer',
1100
-            wp_unslash($this->request->getServerParam('REQUEST_URI'))
1101
-        );
1102
-        $this->request->setRequestParam('_wp_http_referer', $cleaner_request_uri, true);
1103
-        $this->request->setServerParam('REQUEST_URI', $cleaner_request_uri, true);
1104
-        $route_callback = [];
1105
-        if (! empty($func)) {
1106
-            if (is_array($func)) {
1107
-                $route_callback = $func;
1108
-            } elseif (is_string($func)) {
1109
-                if (strpos($func, '::') !== false) {
1110
-                    $route_callback = explode('::', $func);
1111
-                } elseif (method_exists($this, $func)) {
1112
-                    $route_callback = [$this, $func];
1113
-                } else {
1114
-                    $route_callback = $func;
1115
-                }
1116
-            }
1117
-            [$class, $method] = $route_callback;
1118
-            // is it neither a class method NOR a standalone function?
1119
-            if (! is_callable($route_callback)) {
1120
-                // user error msg
1121
-                $error_msg = esc_html__(
1122
-                    'An error occurred. The  requested page route could not be found.',
1123
-                    'event_espresso'
1124
-                );
1125
-                // developer error msg
1126
-                $error_msg .= '||';
1127
-                $error_msg .= sprintf(
1128
-                    esc_html__(
1129
-                        'Page route "%s" could not be called. Check that the spelling for method names and actions in the "_page_routes" array are all correct.',
1130
-                        'event_espresso'
1131
-                    ),
1132
-                    $method
1133
-                );
1134
-                throw new DomainException($error_msg);
1135
-            }
1136
-            if ($class !== $this && ! in_array($this, $args)) {
1137
-                // send along this admin page object for access by addons.
1138
-                $args['admin_page'] = $this;
1139
-            }
1140
-
1141
-            try {
1142
-                call_user_func_array($route_callback, $args);
1143
-            } catch (Throwable $throwable) {
1144
-                $arg_keys = array_keys($args);
1145
-                $nice_args = [];
1146
-                foreach ($args as $key => $arg) {
1147
-                    $nice_args[ $key ] = is_object($arg) ? get_class($arg) : $arg_keys[ $key ];
1148
-                }
1149
-                new ExceptionStackTraceDisplay(
1150
-                        new RuntimeException(
1151
-                            sprintf(
1152
-                                esc_html__(
1153
-                                    'Page route "%1$s" with the supplied arguments (%2$s) threw the following exception: %3$s',
1154
-                                    'event_espresso'
1155
-                                ),
1156
-                                $method,
1157
-                                implode(', ', $nice_args),
1158
-                                $throwable->getMessage()
1159
-                            ),
1160
-                            $throwable->getCode(),
1161
-                            $throwable
1162
-                        )
1163
-                );
1164
-            }
1165
-        }
1166
-        // if we've routed and this route has a no headers route AND a sent_headers_route,
1167
-        // then we need to reset the routing properties to the new route.
1168
-        // now if UI request is FALSE and noheader is true AND we have a headers_sent_route in the route array then let's set UI_request to true because the no header route has a second func after headers have been sent.
1169
-        if (
1170
-            $this->_is_UI_request === false
1171
-            && is_array($this->_route)
1172
-            && ! empty($this->_route['headers_sent_route'])
1173
-        ) {
1174
-            $this->_reset_routing_properties($this->_route['headers_sent_route']);
1175
-        }
1176
-    }
1177
-
1178
-
1179
-    /**
1180
-     * This method just allows the resetting of page properties in the case where a no headers
1181
-     * route redirects to a headers route in its route config.
1182
-     *
1183
-     * @param string $new_route New (non header) route to redirect to.
1184
-     * @return   void
1185
-     * @throws ReflectionException
1186
-     * @throws InvalidArgumentException
1187
-     * @throws InvalidInterfaceException
1188
-     * @throws InvalidDataTypeException
1189
-     * @throws EE_Error
1190
-     * @since   4.3.0
1191
-     */
1192
-    protected function _reset_routing_properties($new_route)
1193
-    {
1194
-        $this->_is_UI_request = true;
1195
-        // now we set the current route to whatever the headers_sent_route is set at
1196
-        $this->request->setRequestParam('action', $new_route);
1197
-        // rerun page setup
1198
-        $this->_page_setup();
1199
-    }
1200
-
1201
-
1202
-    /**
1203
-     * _add_query_arg
1204
-     * adds nonce to array of arguments then calls WP add_query_arg function
1205
-     *(internally just uses EEH_URL's function with the same name)
1206
-     *
1207
-     * @param array  $args
1208
-     * @param string $url
1209
-     * @param bool   $sticky                  if true, then the existing Request params will be appended to the
1210
-     *                                        generated url in an associative array indexed by the key 'wp_referer';
1211
-     *                                        Example usage: If the current page is:
1212
-     *                                        http://mydomain.com/wp-admin/admin.php?page=espresso_registrations
1213
-     *                                        &action=default&event_id=20&month_range=March%202015
1214
-     *                                        &_wpnonce=5467821
1215
-     *                                        and you call:
1216
-     *                                        EE_Admin_Page::add_query_args_and_nonce(
1217
-     *                                        array(
1218
-     *                                        'action' => 'resend_something',
1219
-     *                                        'page=>espresso_registrations'
1220
-     *                                        ),
1221
-     *                                        $some_url,
1222
-     *                                        true
1223
-     *                                        );
1224
-     *                                        It will produce a url in this structure:
1225
-     *                                        http://{$some_url}/?page=espresso_registrations&action=resend_something
1226
-     *                                        &wp_referer[action]=default&wp_referer[event_id]=20&wpreferer[
1227
-     *                                        month_range]=March%202015
1228
-     * @param bool   $exclude_nonce           If true, the the nonce will be excluded from the generated nonce.
1229
-     * @param int    $context
1230
-     * @return string
1231
-     */
1232
-    public static function add_query_args_and_nonce(
1233
-        $args = [],
1234
-        $url = '',
1235
-        $sticky = false,
1236
-        $exclude_nonce = false,
1237
-        int $context = EEH_URL::CONTEXT_NONE
1238
-    ) {
1239
-        // if there is a _wp_http_referer include the values from the request but only if sticky = true
1240
-        if ($sticky) {
1241
-            /** @var RequestInterface $request */
1242
-            $request = LoaderFactory::getLoader()->getShared(RequestInterface::class);
1243
-            $request->unSetRequestParams(['_wp_http_referer', 'wp_referer'], true);
1244
-            $request->unSetServerParam('_wp_http_referer', true);
1245
-            foreach ($request->requestParams() as $key => $value) {
1246
-                // do not add nonces
1247
-                if (strpos($key, 'nonce') !== false) {
1248
-                    continue;
1249
-                }
1250
-                $args[ 'wp_referer[' . $key . ']' ] = is_string($value) ? htmlspecialchars($value) : $value;
1251
-            }
1252
-        }
1253
-        return EEH_URL::add_query_args_and_nonce($args, $url, $exclude_nonce, $context);
1254
-    }
1255
-
1256
-
1257
-    /**
1258
-     * This returns a generated link that will load the related help tab.
1259
-     *
1260
-     * @param string $help_tab_id the id for the connected help tab
1261
-     * @param string $icon_style  (optional) include css class for the style you want to use for the help icon.
1262
-     * @param string $help_text   (optional) send help text you want to use for the link if default not to be used
1263
-     * @return string              generated link
1264
-     * @uses EEH_Template::get_help_tab_link()
1265
-     */
1266
-    protected function _get_help_tab_link($help_tab_id, $icon_style = '', $help_text = '')
1267
-    {
1268
-        return EEH_Template::get_help_tab_link(
1269
-            $help_tab_id,
1270
-            $this->page_slug,
1271
-            $this->_req_action,
1272
-            $icon_style,
1273
-            $help_text
1274
-        );
1275
-    }
1276
-
1277
-
1278
-    /**
1279
-     * _add_help_tabs
1280
-     * Note child classes define their help tabs within the page_config array.
1281
-     *
1282
-     * @link   http://codex.wordpress.org/Function_Reference/add_help_tab
1283
-     * @return void
1284
-     * @throws DomainException
1285
-     * @throws EE_Error
1286
-     * @throws ReflectionException
1287
-     */
1288
-    protected function _add_help_tabs()
1289
-    {
1290
-        if (isset($this->_page_config[ $this->_req_action ])) {
1291
-            $config = $this->_page_config[ $this->_req_action ];
1292
-            // let's see if there is a help_sidebar set for the current route and we'll set that up for usage as well.
1293
-            if (is_array($config) && isset($config['help_sidebar'])) {
1294
-                // check that the callback given is valid
1295
-                if (! method_exists($this, $config['help_sidebar'])) {
1296
-                    throw new EE_Error(
1297
-                        sprintf(
1298
-                            esc_html__(
1299
-                                'The _page_config array has a callback set for the "help_sidebar" option.  However the callback given (%s) is not a valid callback.  Doublecheck the spelling and make sure this method exists for the class %s',
1300
-                                'event_espresso'
1301
-                            ),
1302
-                            $config['help_sidebar'],
1303
-                            $this->class_name
1304
-                        )
1305
-                    );
1306
-                }
1307
-                $content = apply_filters(
1308
-                    'FHEE__' . $this->class_name . '__add_help_tabs__help_sidebar',
1309
-                    $this->{$config['help_sidebar']}()
1310
-                );
1311
-                $this->_current_screen->set_help_sidebar($content);
1312
-            }
1313
-            if (! isset($config['help_tabs'])) {
1314
-                return;
1315
-            } //no help tabs for this route
1316
-            foreach ((array) $config['help_tabs'] as $tab_id => $cfg) {
1317
-                // we're here so there ARE help tabs!
1318
-                // make sure we've got what we need
1319
-                if (! isset($cfg['title'])) {
1320
-                    throw new EE_Error(
1321
-                        esc_html__(
1322
-                            'The _page_config array is not set up properly for help tabs.  It is missing a title',
1323
-                            'event_espresso'
1324
-                        )
1325
-                    );
1326
-                }
1327
-                if (! isset($cfg['filename']) && ! isset($cfg['callback']) && ! isset($cfg['content'])) {
1328
-                    throw new EE_Error(
1329
-                        esc_html__(
1330
-                            'The _page_config array is not setup properly for help tabs. It is missing a either a filename reference, or a callback reference or a content reference so there is no way to know the content for the help tab',
1331
-                            'event_espresso'
1332
-                        )
1333
-                    );
1334
-                }
1335
-                // first priority goes to content.
1336
-                if (! empty($cfg['content'])) {
1337
-                    $content = ! empty($cfg['content']) ? $cfg['content'] : null;
1338
-                    // second priority goes to filename
1339
-                } elseif (! empty($cfg['filename'])) {
1340
-                    $file_path = $this->_get_dir() . '/help_tabs/' . $cfg['filename'] . '.help_tab.php';
1341
-                    // it's possible that the file is located on decaf route (and above sets up for caf route, if this is the case then lets check decaf route too)
1342
-                    $file_path = ! is_readable($file_path) ? EE_ADMIN_PAGES
1343
-                                                             . basename($this->_get_dir())
1344
-                                                             . '/help_tabs/'
1345
-                                                             . $cfg['filename']
1346
-                                                             . '.help_tab.php' : $file_path;
1347
-                    // if file is STILL not readable then let's do a EE_Error so its more graceful than a fatal error.
1348
-                    if (! isset($cfg['callback']) && ! is_readable($file_path)) {
1349
-                        EE_Error::add_error(
1350
-                            sprintf(
1351
-                                esc_html__(
1352
-                                    'The filename given for the help tab %s is not a valid file and there is no other configuration for the tab content.  Please check that the string you set for the help tab on this route (%s) is the correct spelling.  The file should be in %s',
1353
-                                    'event_espresso'
1354
-                                ),
1355
-                                $tab_id,
1356
-                                key($config),
1357
-                                $file_path
1358
-                            ),
1359
-                            __FILE__,
1360
-                            __FUNCTION__,
1361
-                            __LINE__
1362
-                        );
1363
-                        return;
1364
-                    }
1365
-                    $template_args['admin_page_obj'] = $this;
1366
-                    $content                         = EEH_Template::display_template(
1367
-                        $file_path,
1368
-                        $template_args,
1369
-                        true
1370
-                    );
1371
-                } else {
1372
-                    $content = '';
1373
-                }
1374
-                // check if callback is valid
1375
-                if (
1376
-                    empty($content)
1377
-                    && (
1378
-                        ! isset($cfg['callback']) || ! method_exists($this, $cfg['callback'])
1379
-                    )
1380
-                ) {
1381
-                    EE_Error::add_error(
1382
-                        sprintf(
1383
-                            esc_html__(
1384
-                                'The callback given for a %s help tab on this page does not content OR a corresponding method for generating the content.  Check the spelling or make sure the method is present.',
1385
-                                'event_espresso'
1386
-                            ),
1387
-                            $cfg['title']
1388
-                        ),
1389
-                        __FILE__,
1390
-                        __FUNCTION__,
1391
-                        __LINE__
1392
-                    );
1393
-                    return;
1394
-                }
1395
-                // setup config array for help tab method
1396
-                $id  = $this->page_slug . '-' . $this->_req_action . '-' . $tab_id;
1397
-                $_ht = [
1398
-                    'id'       => $id,
1399
-                    'title'    => $cfg['title'],
1400
-                    'callback' => isset($cfg['callback']) && empty($content) ? [$this, $cfg['callback']] : null,
1401
-                    'content'  => $content,
1402
-                ];
1403
-                $this->_current_screen->add_help_tab($_ht);
1404
-            }
1405
-        }
1406
-    }
1407
-
1408
-
1409
-    /**
1410
-     * This simply sets up any qtips that have been defined in the page config
1411
-     *
1412
-     * @return void
1413
-     * @throws ReflectionException
1414
-     * @throws EE_Error
1415
-     */
1416
-    protected function _add_qtips()
1417
-    {
1418
-        if (isset($this->_route_config['qtips'])) {
1419
-            $qtips = (array) $this->_route_config['qtips'];
1420
-            // load qtip loader
1421
-            $path = [
1422
-                $this->_get_dir() . '/qtips/',
1423
-                EE_ADMIN_PAGES . basename($this->_get_dir()) . '/qtips/',
1424
-            ];
1425
-            EEH_Qtip_Loader::instance()->register($qtips, $path);
1426
-        }
1427
-    }
1428
-
1429
-
1430
-    /**
1431
-     * _set_nav_tabs
1432
-     * This sets up the nav tabs from the page_routes array.  This method can be overwritten by child classes if you
1433
-     * wish to add additional tabs or modify accordingly.
1434
-     *
1435
-     * @return void
1436
-     * @throws InvalidArgumentException
1437
-     * @throws InvalidInterfaceException
1438
-     * @throws InvalidDataTypeException
1439
-     */
1440
-    protected function _set_nav_tabs()
1441
-    {
1442
-        $i        = 0;
1443
-        $only_tab = count($this->_page_config) < 2;
1444
-        foreach ($this->_page_config as $slug => $config) {
1445
-            if (! is_array($config) || empty($config['nav'])) {
1446
-                continue;
1447
-            }
1448
-            // no nav tab for this config
1449
-            // check for persistent flag
1450
-            if ($slug !== $this->_req_action && isset($config['nav']['persistent']) && ! $config['nav']['persistent']) {
1451
-                // nav tab is only to appear when route requested.
1452
-                continue;
1453
-            }
1454
-            if (! $this->check_user_access($slug, true)) {
1455
-                // no nav tab because current user does not have access.
1456
-                continue;
1457
-            }
1458
-            $css_class = $config['css_class'] ?? '';
1459
-            $css_class .= $only_tab ? ' ee-only-tab' : '';
1460
-            $css_class .= " ee-nav-tab__$slug";
1461
-
1462
-            $this->_nav_tabs[ $slug ] = [
1463
-                'url'       => $config['nav']['url'] ?? EE_Admin_Page::add_query_args_and_nonce(
1464
-                        ['action' => $slug],
1465
-                        $this->_admin_base_url
1466
-                    ),
1467
-                'link_text' => $this->navTabLabel($config['nav'], $slug),
1468
-                'css_class' => $this->_req_action === $slug ? $css_class . ' nav-tab-active' : $css_class,
1469
-                'order'     => $config['nav']['order'] ?? $i,
1470
-            ];
1471
-            $i++;
1472
-        }
1473
-        // if $this->_nav_tabs is empty then lets set the default
1474
-        if (empty($this->_nav_tabs)) {
1475
-            $this->_nav_tabs[ $this->_default_nav_tab_name ] = [
1476
-                'url'       => $this->_admin_base_url,
1477
-                'link_text' => ucwords(str_replace('_', ' ', $this->_default_nav_tab_name)),
1478
-                'css_class' => 'nav-tab-active',
1479
-                'order'     => 10,
1480
-            ];
1481
-        }
1482
-        // now let's sort the tabs according to order
1483
-        usort($this->_nav_tabs, [$this, '_sort_nav_tabs']);
1484
-    }
1485
-
1486
-
1487
-    private function navTabLabel(array $nav_tab, string $slug): string
1488
-    {
1489
-        $label = $nav_tab['label'] ?? ucwords(str_replace('_', ' ', $slug));
1490
-        $icon  = $nav_tab['icon'] ?? null;
1491
-        $icon  = $icon ? '<span class="dashicons ' . $icon . '"></span>' : '';
1492
-        return '
160
+	/**
161
+	 * set early within EE_Admin_Init
162
+	 *
163
+	 * @var string
164
+	 */
165
+	protected string $_wp_page_slug = '';
166
+
167
+	/**
168
+	 * if the current class is an admin page extension, like: Extend_Events_Admin_Page,
169
+	 * then this would be the parent classname: Events_Admin_Page
170
+	 *
171
+	 * @var string
172
+	 */
173
+	protected string $base_class_name = '';
174
+
175
+	protected string $class_name    = '';
176
+
177
+	/**
178
+	 * unprocessed value for the 'action' request param (default '')
179
+	 *
180
+	 * @var string
181
+	 */
182
+	protected string $raw_req_action = '';
183
+
184
+	/**
185
+	 * unprocessed value for the 'page' request param (default '')
186
+	 *
187
+	 * @var string
188
+	 */
189
+	protected string $raw_req_page = '';
190
+
191
+	public string    $page_folder  = '';
192
+
193
+	public string    $page_label   = '';
194
+
195
+	public string    $page_slug    = '';
196
+
197
+
198
+	/**
199
+	 * the current page route and route config
200
+	 *
201
+	 * @var array|callable|string|null
202
+	 */
203
+	protected $_route = null;
204
+
205
+
206
+
207
+	/**
208
+	 * @Constructor
209
+	 * @param bool $routing indicate whether we want to just load the object and handle routing or just load the object.
210
+	 * @throws InvalidArgumentException
211
+	 * @throws InvalidDataTypeException
212
+	 * @throws InvalidInterfaceException
213
+	 * @throws ReflectionException
214
+	 */
215
+	public function __construct($routing = true)
216
+	{
217
+		$this->loader       = LoaderFactory::getLoader();
218
+		$this->admin_config = $this->loader->getShared('EE_Admin_Config');
219
+		$this->feature      = $this->loader->getShared(FeatureFlags::class);
220
+		$this->request      = $this->loader->getShared(RequestInterface::class);
221
+		// routing enabled?
222
+		$this->_routing = $routing;
223
+
224
+		$this->class_name      = get_class($this);
225
+		$this->base_class_name = strpos($this->class_name, 'Extend_') === 0
226
+			? str_replace('Extend_', '', $this->class_name)
227
+			: '';
228
+
229
+		if (strpos($this->_get_dir(), 'caffeinated') !== false) {
230
+			$this->_is_caf = true;
231
+		}
232
+		$this->_yes_no_values = [
233
+			['id' => true, 'text' => esc_html__('Yes', 'event_espresso')],
234
+			['id' => false, 'text' => esc_html__('No', 'event_espresso')],
235
+		];
236
+		// set the _req_data property.
237
+		$this->_req_data = $this->request->requestParams();
238
+	}
239
+
240
+
241
+	/**
242
+	 * @return EE_Admin_Config
243
+	 */
244
+	public function adminConfig(): EE_Admin_Config
245
+	{
246
+		return $this->admin_config;
247
+	}
248
+
249
+
250
+	/**
251
+	 * @return FeatureFlags
252
+	 */
253
+	public function feature(): FeatureFlags
254
+	{
255
+		return $this->feature;
256
+	}
257
+
258
+
259
+	/**
260
+	 * This logic used to be in the constructor, but that caused a chicken <--> egg scenario
261
+	 * for child classes that needed to set properties prior to these methods getting called,
262
+	 * but also needed the parent class to have its construction completed as well.
263
+	 * Bottom line is that constructors should ONLY be used for setting initial properties
264
+	 * and any complex initialization logic should only run after instantiation is complete.
265
+	 *
266
+	 * This method gets called immediately after construction from within
267
+	 *      EE_Admin_Page_Init::_initialize_admin_page()
268
+	 *
269
+	 * @throws EE_Error
270
+	 * @throws InvalidArgumentException
271
+	 * @throws InvalidDataTypeException
272
+	 * @throws InvalidInterfaceException
273
+	 * @throws ReflectionException
274
+	 * @since 5.0.0.p
275
+	 */
276
+	public function initializePage()
277
+	{
278
+		if ($this->initialized) {
279
+			return;
280
+		}
281
+		// set initial page props (child method)
282
+		$this->_init_page_props();
283
+		// set global defaults
284
+		$this->_set_defaults();
285
+		// set early because incoming requests could be ajax related and we need to register those hooks.
286
+		$this->_global_ajax_hooks();
287
+		$this->_ajax_hooks();
288
+		// other_page_hooks have to be early too.
289
+		$this->_do_other_page_hooks();
290
+		// set up page dependencies
291
+		$this->_before_page_setup();
292
+		$this->_page_setup();
293
+		$this->initialized = true;
294
+	}
295
+
296
+
297
+	/**
298
+	 * _init_page_props
299
+	 * Child classes use to set at least the following properties:
300
+	 * $page_slug.
301
+	 * $page_label.
302
+	 *
303
+	 * @abstract
304
+	 * @return void
305
+	 */
306
+	abstract protected function _init_page_props();
307
+
308
+
309
+	/**
310
+	 * _ajax_hooks
311
+	 * child classes put all their add_action('wp_ajax_{name_of_hook}') hooks in here.
312
+	 * Note: within the ajax callback methods.
313
+	 *
314
+	 * @abstract
315
+	 * @return void
316
+	 */
317
+	abstract protected function _ajax_hooks();
318
+
319
+
320
+	/**
321
+	 * _define_page_props
322
+	 * child classes define page properties in here.  Must include at least:
323
+	 * $_admin_base_url = base_url for all admin pages
324
+	 * $_admin_page_title = default admin_page_title for admin pages
325
+	 * $_labels = array of default labels for various automatically generated elements:
326
+	 *    array(
327
+	 *        'buttons' => array(
328
+	 *            'add' => esc_html__('label for add new button'),
329
+	 *            'edit' => esc_html__('label for edit button'),
330
+	 *            'delete' => esc_html__('label for delete button')
331
+	 *            )
332
+	 *        )
333
+	 *
334
+	 * @abstract
335
+	 * @return void
336
+	 */
337
+	abstract protected function _define_page_props();
338
+
339
+
340
+	/**
341
+	 * _set_page_routes
342
+	 * child classes use this to define the page routes for all subpages handled by the class.  Page routes are
343
+	 * assigned to a action => method pairs in an array and to the $_page_routes property.  Each page route must also
344
+	 * have a 'default' route. Here's the format
345
+	 * $this->_page_routes = array(
346
+	 *        'default' => array(
347
+	 *            'func' => '_default_method_handling_route',
348
+	 *            'args' => array('array','of','args'),
349
+	 *            'noheader' => true, //add this in if this page route is processed before any headers are loaded (i.e.
350
+	 *            ajax request, backend processing)
351
+	 *            'headers_sent_route'=>'headers_route_reference', //add this if noheader=>true, and you want to load a
352
+	 *            headers route after.  The string you enter here should match the defined route reference for a
353
+	 *            headers sent route.
354
+	 *            'capability' => 'route_capability', //indicate a string for minimum capability required to access
355
+	 *            this route.
356
+	 *            'obj_id' => 10 // if this route has an object id, then this can include it (used for capability
357
+	 *            checks).
358
+	 *        ),
359
+	 *        'insert_item' => '_method_for_handling_insert_item' //this can be used if all we need to have is a
360
+	 *        handling method.
361
+	 *        )
362
+	 * )
363
+	 *
364
+	 * @abstract
365
+	 * @return void
366
+	 */
367
+	abstract protected function _set_page_routes();
368
+
369
+
370
+	/**
371
+	 * _set_page_config
372
+	 * child classes use this to define the _page_config array for all subpages handled by the class. Each key in the
373
+	 * array corresponds to the page_route for the loaded page. Format:
374
+	 * $this->_page_config = array(
375
+	 *        'default' => array(
376
+	 *            'labels' => array(
377
+	 *                'buttons' => array(
378
+	 *                    'add' => esc_html__('label for adding item'),
379
+	 *                    'edit' => esc_html__('label for editing item'),
380
+	 *                    'delete' => esc_html__('label for deleting item')
381
+	 *                ),
382
+	 *                'publishbox' => esc_html__('Localized Title for Publish metabox', 'event_espresso')
383
+	 *            ), //optional an array of custom labels for various automatically generated elements to use on the
384
+	 *            page. If this isn't present then the defaults will be used as set for the $this->_labels in
385
+	 *            _define_page_props() method
386
+	 *            'nav' => array(
387
+	 *                'label' => esc_html__('Label for Tab', 'event_espresso').
388
+	 *                'url' => 'http://someurl', //automatically generated UNLESS you define
389
+	 *                'css_class' => 'css-class', //automatically generated UNLESS you define
390
+	 *                'order' => 10, //required to indicate tab position.
391
+	 *                'persistent' => false //if you want the nav tab to ONLY display when the specific route is
392
+	 *                displayed then add this parameter.
393
+	 *            'list_table' => 'name_of_list_table' //string for list table class to be loaded for this admin_page.
394
+	 *            'metaboxes' => array('metabox1', 'metabox2'), //if present this key indicates we want to load
395
+	 *            metaboxes set for eventespresso admin pages.
396
+	 *            'has_metaboxes' => true, //this boolean flag can simply be used to indicate if the route will have
397
+	 *            metaboxes.  Typically this is used if the 'metaboxes' index is not used because metaboxes are added
398
+	 *            later.  We just use this flag to make sure the necessary js gets enqueued on page load.
399
+	 *            'has_help_popups' => false //defaults(true) //this boolean flag can simply be used to indicate if the
400
+	 *            given route has help popups setup and if it does then we need to make sure thickbox is enqueued.
401
+	 *            'columns' => array(4, 2), //this key triggers the setup of a page that uses columns (metaboxes).  The
402
+	 *            array indicates the max number of columns (4) and the default number of columns on page load (2).
403
+	 *            There is an option in the "screen_options" dropdown that is setup so users can pick what columns they
404
+	 *            want to display.
405
+	 *            'help_tabs' => array( //this is used for adding help tabs to a page
406
+	 *                'tab_id' => array(
407
+	 *                    'title' => 'tab_title',
408
+	 *                    'filename' => 'name_of_file_containing_content', //this is the primary method for setting
409
+	 *                    help tab content.  The fallback if it isn't present is to try a the callback.  Filename
410
+	 *                    should match a file in the admin folder's "help_tabs" dir (ie..
411
+	 *                    events/help_tabs/name_of_file_containing_content.help_tab.php)
412
+	 *                    'callback' => 'callback_method_for_content', //if 'filename' isn't present then system will
413
+	 *                    attempt to use the callback which should match the name of a method in the class
414
+	 *                    ),
415
+	 *                'tab2_id' => array(
416
+	 *                    'title' => 'tab2 title',
417
+	 *                    'filename' => 'file_name_2'
418
+	 *                    'callback' => 'callback_method_for_content',
419
+	 *                 ),
420
+	 *            'help_sidebar' => 'callback_for_sidebar_content', //this is used for setting up the sidebar in the
421
+	 *            help tab area on an admin page. @return void
422
+	 *
423
+	 * @abstract
424
+	 */
425
+	abstract protected function _set_page_config();
426
+
427
+
428
+	/**
429
+	 * _add_screen_options
430
+	 * Child classes can add any extra wp_screen_options within this method using built-in WP functions/methods for
431
+	 * doing so. Note child classes can also define _add_screen_options_($this->_current_view) to limit screen options
432
+	 * to a particular view.
433
+	 *
434
+	 * @link   http://chrismarslender.com/wp-tutorials/wordpress-screen-options-tutorial/
435
+	 *         see also WP_Screen object documents...
436
+	 * @link   http://codex.wordpress.org/Class_Reference/WP_Screen
437
+	 * @abstract
438
+	 * @return void
439
+	 */
440
+	abstract protected function _add_screen_options();
441
+
442
+
443
+	/**
444
+	 * _add_feature_pointers
445
+	 * Child classes should use this method for implementing any "feature pointers" (using built-in WP styling js).
446
+	 * Note child classes can also define _add_feature_pointers_($this->_current_view) to limit screen options to a
447
+	 * particular view. Note: this is just a placeholder for now.  Implementation will come down the road See:
448
+	 * WP_Internal_Pointers class in wp-admin/includes/template.php for example (its a final class so can't be
449
+	 * extended) also see:
450
+	 *
451
+	 * @link   http://eamann.com/tech/wordpress-portland/
452
+	 * @abstract
453
+	 * @return void
454
+	 */
455
+	abstract protected function _add_feature_pointers();
456
+
457
+
458
+	/**
459
+	 * load_scripts_styles
460
+	 * child classes put their wp_enqueue_script and wp_enqueue_style hooks in here for anything they need loaded for
461
+	 * their pages/subpages.  Note this is for all pages/subpages of the system.  You can also load only specific
462
+	 * scripts/styles per view by putting them in a dynamic function in this format
463
+	 * (load_scripts_styles_{$this->_current_view}) which matches your page route (action request arg)
464
+	 *
465
+	 * @abstract
466
+	 * @return void
467
+	 */
468
+	abstract public function load_scripts_styles();
469
+
470
+
471
+	/**
472
+	 * admin_init
473
+	 * Anything that should be set/executed at 'admin_init' WP hook runtime should be put in here.  This will apply to
474
+	 * all pages/views loaded by child class.
475
+	 *
476
+	 * @abstract
477
+	 * @return void
478
+	 */
479
+	abstract public function admin_init();
480
+
481
+
482
+	/**
483
+	 * admin_notices
484
+	 * Anything triggered by the 'admin_notices' WP hook should be put in here.  This particular method will apply to
485
+	 * all pages/views loaded by child class.
486
+	 *
487
+	 * @abstract
488
+	 * @return void
489
+	 */
490
+	abstract public function admin_notices();
491
+
492
+
493
+	/**
494
+	 * admin_footer_scripts
495
+	 * Anything triggered by the 'admin_print_footer_scripts' WP hook should be put in here. This particular method
496
+	 * will apply to all pages/views loaded by child class.
497
+	 *
498
+	 * @return void
499
+	 */
500
+	abstract public function admin_footer_scripts();
501
+
502
+
503
+	/**
504
+	 * admin_footer
505
+	 * anything triggered by the 'admin_footer' WP action hook should be added to here. This particular method will
506
+	 * apply to all pages/views loaded by child class.
507
+	 *
508
+	 * @return void
509
+	 */
510
+	public function admin_footer()
511
+	{
512
+	}
513
+
514
+
515
+	/**
516
+	 * _global_ajax_hooks
517
+	 * all global add_action('wp_ajax_{name_of_hook}') hooks in here.
518
+	 * Note: within the ajax callback methods.
519
+	 *
520
+	 * @abstract
521
+	 * @return void
522
+	 */
523
+	protected function _global_ajax_hooks()
524
+	{
525
+		// for lazy loading of metabox content
526
+		add_action('wp_ajax_espresso-ajax-content', [$this, 'ajax_metabox_content'], 10);
527
+
528
+		add_action(
529
+			'wp_ajax_espresso_hide_status_change_notice',
530
+			[$this, 'hideStatusChangeNotice']
531
+		);
532
+		add_action(
533
+			'wp_ajax_nopriv_espresso_hide_status_change_notice',
534
+			[$this, 'hideStatusChangeNotice']
535
+		);
536
+	}
537
+
538
+
539
+	public function ajax_metabox_content()
540
+	{
541
+		$content_id  = $this->request->getRequestParam('contentid', '');
542
+		$content_url = $this->request->getRequestParam('contenturl', '', 'url');
543
+		EE_Admin_Page::cached_rss_display($content_id, $content_url);
544
+		wp_die();
545
+	}
546
+
547
+
548
+	public function hideStatusChangeNotice()
549
+	{
550
+		$response = [];
551
+		try {
552
+			/** @var StatusChangeNotice $status_change_notice */
553
+			$status_change_notice = $this->loader->getShared(
554
+				'EventEspresso\core\domain\services\admin\notices\status_change\StatusChangeNotice'
555
+			);
556
+			$response['success']  = $status_change_notice->dismiss() > -1;
557
+		} catch (Exception $exception) {
558
+			$response['errors'] = $exception->getMessage();
559
+		}
560
+		echo wp_json_encode($response);
561
+		exit();
562
+	}
563
+
564
+
565
+	/**
566
+	 * allows extending classes do something specific before the parent constructor runs _page_setup().
567
+	 *
568
+	 * @return void
569
+	 */
570
+	protected function _before_page_setup()
571
+	{
572
+		// default is to do nothing
573
+	}
574
+
575
+
576
+	/**
577
+	 * Makes sure any things that need to be loaded early get handled.
578
+	 * We also escape early here if the page requested doesn't match the object.
579
+	 *
580
+	 * @final
581
+	 * @return void
582
+	 * @throws EE_Error
583
+	 * @throws InvalidArgumentException
584
+	 * @throws ReflectionException
585
+	 * @throws InvalidDataTypeException
586
+	 * @throws InvalidInterfaceException
587
+	 */
588
+	final protected function _page_setup()
589
+	{
590
+		// requires?
591
+		// admin_init stuff - global - we're setting this REALLY early
592
+		// so if EE_Admin pages have to hook into other WP pages they can.
593
+		// But keep in mind, not everything is available from the EE_Admin Page object at this point.
594
+		add_action('admin_init', [$this, 'admin_init_global'], 5);
595
+		// next verify if we need to load anything...
596
+		$this->_current_page = $this->request->getRequestParam('page', '', 'key');
597
+		$this->_current_page = $this->request->getRequestParam('current_page', $this->_current_page, 'key');
598
+		$this->page_folder   = strtolower(
599
+			str_replace(['_Admin_Page', 'Extend_'], '', $this->class_name)
600
+		);
601
+		global $ee_menu_slugs;
602
+		$ee_menu_slugs = (array) $ee_menu_slugs;
603
+		if (
604
+			! $this->request->isAjax()
605
+			&& (! $this->_current_page || ! isset($ee_menu_slugs[ $this->_current_page ]))
606
+		) {
607
+			return;
608
+		}
609
+		// because WP List tables have two duplicate select inputs for choosing bulk actions,
610
+		// we need to copy the action from the second to the first
611
+		$action     = $this->request->getRequestParam('action', '-1', 'key');
612
+		$action2    = $this->request->getRequestParam('action2', '-1', 'key');
613
+		$action     = $action !== '-1' ? $action : $action2;
614
+		$req_action = $action !== '-1' ? $action : 'default';
615
+
616
+		// if a specific 'route' has been set, and the action is 'default' OR we are doing_ajax
617
+		// then let's use the route as the action.
618
+		// This covers cases where we're coming in from a list table that isn't on the default route.
619
+		$route             = $this->request->getRequestParam('route');
620
+		$this->_req_action = $route && ($req_action === 'default' || $this->request->isAjax())
621
+			? $route
622
+			: $req_action;
623
+
624
+		$this->_current_view = $this->_req_action;
625
+		$this->_req_nonce    = $this->_req_action . '_nonce';
626
+		$this->_define_page_props();
627
+		$this->_current_page_view_url = add_query_arg(
628
+			['page' => $this->_current_page, 'action' => $this->_current_view],
629
+			$this->_admin_base_url
630
+		);
631
+		// set page configs
632
+		$this->_set_page_routes();
633
+		$this->_set_page_config();
634
+		// let's include any referrer data in our default_query_args for this route for "stickiness".
635
+		if ($this->request->requestParamIsSet('wp_referer')) {
636
+			$wp_referer = $this->request->getRequestParam('wp_referer');
637
+			if ($wp_referer) {
638
+				$this->_default_route_query_args['wp_referer'] = $wp_referer;
639
+			}
640
+		}
641
+		// for CPT and other extended functionality.
642
+		// If there is an _extend_page_config_for_cpt
643
+		// then let's run that to modify all the various page configuration arrays.
644
+		if (method_exists($this, '_extend_page_config_for_cpt')) {
645
+			$this->_extend_page_config_for_cpt();
646
+		}
647
+		// filter routes and page_config so addons can add their stuff. Filtering done per class
648
+		$this->_page_routes = apply_filters(
649
+			'FHEE__' . $this->class_name . '__page_setup__page_routes',
650
+			$this->_page_routes,
651
+			$this
652
+		);
653
+		$this->_page_config = apply_filters(
654
+			'FHEE__' . $this->class_name . '__page_setup__page_config',
655
+			$this->_page_config,
656
+			$this
657
+		);
658
+		if ($this->base_class_name !== '') {
659
+			$this->_page_routes = apply_filters(
660
+				'FHEE__' . $this->base_class_name . '__page_setup__page_routes',
661
+				$this->_page_routes,
662
+				$this
663
+			);
664
+			$this->_page_config = apply_filters(
665
+				'FHEE__' . $this->base_class_name . '__page_setup__page_config',
666
+				$this->_page_config,
667
+				$this
668
+			);
669
+		}
670
+		// if AHEE__EE_Admin_Page__route_admin_request_$this->_current_view method is present
671
+		// then we call it hooked into the AHEE__EE_Admin_Page__route_admin_request action
672
+		if (method_exists($this, 'AHEE__EE_Admin_Page__route_admin_request_' . $this->_current_view)) {
673
+			add_action(
674
+				'AHEE__EE_Admin_Page__route_admin_request',
675
+				[$this, 'AHEE__EE_Admin_Page__route_admin_request_' . $this->_current_view],
676
+				10,
677
+				2
678
+			);
679
+		}
680
+		// next route only if routing enabled
681
+		if ($this->_routing && ! $this->request->isAjax()) {
682
+			$this->_verify_routes();
683
+			// next let's just check user_access and kill if no access
684
+			$this->check_user_access();
685
+			if ($this->_is_UI_request) {
686
+				// admin_init stuff - global, all views for this page class, specific view
687
+				add_action('admin_init', [$this, 'admin_init'], 10);
688
+				if (method_exists($this, 'admin_init_' . $this->_current_view)) {
689
+					add_action('admin_init', [$this, 'admin_init_' . $this->_current_view], 15);
690
+				}
691
+			} else {
692
+				// hijack regular WP loading and route admin request immediately
693
+				@ini_set('memory_limit', apply_filters('admin_memory_limit', WP_MAX_MEMORY_LIMIT));
694
+				$this->route_admin_request();
695
+			}
696
+		}
697
+	}
698
+
699
+
700
+	/**
701
+	 * Provides a way for related child admin pages to load stuff on the loaded admin page.
702
+	 *
703
+	 * @return void
704
+	 * @throws EE_Error
705
+	 */
706
+	private function _do_other_page_hooks()
707
+	{
708
+		$registered_pages = apply_filters('FHEE_do_other_page_hooks_' . $this->page_slug, []);
709
+		foreach ($registered_pages as $page) {
710
+			// now let's setup the file name and class that should be present
711
+			$classname = str_replace('.class.php', '', $page);
712
+			// autoloaders should take care of loading file
713
+			if (! class_exists($classname)) {
714
+				$error_msg[] = sprintf(
715
+					esc_html__(
716
+						'Something went wrong with loading the %s admin hooks page.',
717
+						'event_espresso'
718
+					),
719
+					$page
720
+				);
721
+				$error_msg[] = $error_msg[0]
722
+							   . "\r\n"
723
+							   . sprintf(
724
+								   esc_html__(
725
+									   'There is no class in place for the %1$s admin hooks page.%2$sMake sure you have %3$s defined. If this is a non-EE-core admin page then you also must have an autoloader in place for your class',
726
+									   'event_espresso'
727
+								   ),
728
+								   $page,
729
+								   '<br />',
730
+								   '<strong>' . $classname . '</strong>'
731
+							   );
732
+				throw new EE_Error(implode('||', $error_msg));
733
+			}
734
+			// notice we are passing the instance of this class to the hook object.
735
+			$this->loader->getShared($classname, [$this]);
736
+		}
737
+	}
738
+
739
+
740
+	/**
741
+	 * @throws ReflectionException
742
+	 * @throws EE_Error
743
+	 */
744
+	public function load_page_dependencies()
745
+	{
746
+		try {
747
+			$this->_load_page_dependencies();
748
+		} catch (EE_Error $e) {
749
+			$e->get_error();
750
+		}
751
+	}
752
+
753
+
754
+	/**
755
+	 * load_page_dependencies
756
+	 * loads things specific to this page class when its loaded.  Really helps with efficiency.
757
+	 *
758
+	 * @return void
759
+	 * @throws DomainException
760
+	 * @throws EE_Error
761
+	 * @throws InvalidArgumentException
762
+	 * @throws InvalidDataTypeException
763
+	 * @throws InvalidInterfaceException
764
+	 * @throws ReflectionException
765
+	 */
766
+	protected function _load_page_dependencies()
767
+	{
768
+		// let's set the current_screen and screen options to override what WP set
769
+		$this->_current_screen = get_current_screen();
770
+		// load admin_notices - global, page class, and view specific
771
+		add_action('admin_notices', [$this, 'admin_notices_global'], 5);
772
+		add_action('admin_notices', [$this, 'admin_notices'], 10);
773
+		if (method_exists($this, 'admin_notices_' . $this->_current_view)) {
774
+			add_action('admin_notices', [$this, 'admin_notices_' . $this->_current_view], 15);
775
+		}
776
+		// load network admin_notices - global, page class, and view specific
777
+		add_action('network_admin_notices', [$this, 'network_admin_notices_global'], 5);
778
+		if (method_exists($this, 'network_admin_notices_' . $this->_current_view)) {
779
+			add_action('network_admin_notices', [$this, 'network_admin_notices_' . $this->_current_view]);
780
+		}
781
+		// this will save any per_page screen options if they are present
782
+		$this->_set_per_page_screen_options();
783
+		// setup list table properties
784
+		$this->_set_list_table();
785
+		// child classes can "register" a metabox to be automatically handled via the _page_config array property.
786
+		// However in some cases the metaboxes will need to be added within a route handling callback.
787
+		add_action('add_meta_boxes', [$this, 'addRegisteredMetaBoxes'], 99);
788
+		// hack because promos admin was loading the edited promotion object in the metaboxes callback
789
+		// which should NOT be generated on non-UI requests like POST updates/inserts
790
+		if (
791
+			$this->class_name === 'Promotions_Admin_Page'
792
+			&& ($this->_req_action === 'edit' || $this->_req_action === 'create_new')
793
+		) {
794
+			$this->addRegisteredMetaBoxes();
795
+		}
796
+		$this->_add_screen_columns();
797
+		// add screen options - global, page child class, and view specific
798
+		$this->_add_global_screen_options();
799
+		$this->_add_screen_options();
800
+		$add_screen_options = "_add_screen_options_{$this->_current_view}";
801
+		if (method_exists($this, $add_screen_options)) {
802
+			$this->{$add_screen_options}();
803
+		}
804
+		// add help tab(s) - set via page_config and qtips.
805
+		$this->_add_help_tabs();
806
+		$this->_add_qtips();
807
+		// add feature_pointers - global, page child class, and view specific
808
+		$this->_add_feature_pointers();
809
+		$this->_add_global_feature_pointers();
810
+		$add_feature_pointer = "_add_feature_pointer_{$this->_current_view}";
811
+		if (method_exists($this, $add_feature_pointer)) {
812
+			$this->{$add_feature_pointer}();
813
+		}
814
+		// enqueue scripts/styles - global, page class, and view specific
815
+		add_action('admin_enqueue_scripts', [$this, 'load_global_scripts_styles'], 5);
816
+		add_action('admin_enqueue_scripts', [$this, 'load_scripts_styles'], 10);
817
+		if (method_exists($this, "load_scripts_styles_{$this->_current_view}")) {
818
+			add_action('admin_enqueue_scripts', [$this, "load_scripts_styles_{$this->_current_view}"], 15);
819
+		}
820
+		add_action('admin_enqueue_scripts', [$this, 'admin_footer_scripts_eei18n_js_strings'], 100);
821
+		// admin_print_footer_scripts - global, page child class, and view specific.
822
+		// NOTE, despite the name, whenever possible, scripts should NOT be loaded using this.
823
+		// In most cases that's doing_it_wrong().  But adding hidden container elements etc.
824
+		// is a good use case. Notice the late priority we're giving these
825
+		add_action('admin_print_footer_scripts', [$this, 'admin_footer_scripts_global'], 99);
826
+		add_action('admin_print_footer_scripts', [$this, 'admin_footer_scripts'], 100);
827
+		if (method_exists($this, "admin_footer_scripts_{$this->_current_view}")) {
828
+			add_action('admin_print_footer_scripts', [$this, "admin_footer_scripts_{$this->_current_view}"], 101);
829
+		}
830
+		// admin footer scripts
831
+		add_action('admin_footer', [$this, 'admin_footer_global'], 99);
832
+		add_action('admin_footer', [$this, 'admin_footer'], 100);
833
+		if (method_exists($this, "admin_footer_{$this->_current_view}")) {
834
+			add_action('admin_footer', [$this, "admin_footer_{$this->_current_view}"], 101);
835
+		}
836
+		do_action('FHEE__EE_Admin_Page___load_page_dependencies__after_load', $this->page_slug);
837
+		// targeted hook
838
+		do_action(
839
+			"FHEE__EE_Admin_Page___load_page_dependencies__after_load__{$this->page_slug}__{$this->_req_action}"
840
+		);
841
+	}
842
+
843
+
844
+	/**
845
+	 * _set_defaults
846
+	 * This sets some global defaults for class properties.
847
+	 */
848
+	private function _set_defaults()
849
+	{
850
+		// init template args
851
+		$this->set_template_args(
852
+			[
853
+				'admin_page_header'  => '',
854
+				'admin_page_content' => '',
855
+				'post_body_content'  => '',
856
+				'before_list_table'  => '',
857
+				'after_list_table'   => '',
858
+			]
859
+		);
860
+	}
861
+
862
+
863
+	/**
864
+	 * route_admin_request
865
+	 *
866
+	 * @return void
867
+	 * @throws InvalidArgumentException
868
+	 * @throws InvalidInterfaceException
869
+	 * @throws InvalidDataTypeException
870
+	 * @throws EE_Error
871
+	 * @throws ReflectionException
872
+	 * @see    _route_admin_request()
873
+	 */
874
+	public function route_admin_request()
875
+	{
876
+		try {
877
+			$this->_route_admin_request();
878
+		} catch (EE_Error $e) {
879
+			$e->get_error();
880
+		}
881
+	}
882
+
883
+
884
+	public function set_wp_page_slug($wp_page_slug)
885
+	{
886
+		$this->_wp_page_slug = $wp_page_slug;
887
+		// if in network admin then we need to append "-network" to the page slug. Why? Because that's how WP rolls...
888
+		if (is_network_admin()) {
889
+			$this->_wp_page_slug .= '-network';
890
+		}
891
+	}
892
+
893
+
894
+	/**
895
+	 * _verify_routes
896
+	 * All this method does is verify the incoming request and make sure that routes exist for it.  We do this early so
897
+	 * we know if we need to drop out.
898
+	 *
899
+	 * @return bool
900
+	 * @throws EE_Error
901
+	 */
902
+	protected function _verify_routes()
903
+	{
904
+		do_action('AHEE_log', __FILE__, __FUNCTION__, '');
905
+		if (! $this->_current_page && ! $this->request->isAjax()) {
906
+			return false;
907
+		}
908
+		// check that the page_routes array is not empty
909
+		if (empty($this->_page_routes)) {
910
+			// user error msg
911
+			$error_msg = sprintf(
912
+				esc_html__('No page routes have been set for the %s admin page.', 'event_espresso'),
913
+				$this->_admin_page_title
914
+			);
915
+			// developer error msg
916
+			$error_msg .= '||' . $error_msg
917
+						  . esc_html__(
918
+							  ' Make sure the "set_page_routes()" method exists, and is setting the "_page_routes" array properly.',
919
+							  'event_espresso'
920
+						  );
921
+			throw new EE_Error($error_msg);
922
+		}
923
+		// and that the requested page route exists
924
+		if (array_key_exists($this->_req_action, $this->_page_routes)) {
925
+			$this->_route        = $this->_page_routes[ $this->_req_action ];
926
+			$this->_route_config = $this->_page_config[ $this->_req_action ] ?? [];
927
+		} else {
928
+			// user error msg
929
+			$error_msg = sprintf(
930
+				esc_html__(
931
+					'The requested page route does not exist for the %s admin page.',
932
+					'event_espresso'
933
+				),
934
+				$this->_admin_page_title
935
+			);
936
+			// developer error msg
937
+			$error_msg .= '||' . $error_msg
938
+						  . sprintf(
939
+							  esc_html__(
940
+								  ' Create a key in the "_page_routes" array named "%s" and set its value to the appropriate method.',
941
+								  'event_espresso'
942
+							  ),
943
+							  $this->_req_action
944
+						  );
945
+			throw new EE_Error($error_msg);
946
+		}
947
+		// and that a default route exists
948
+		if (! array_key_exists('default', $this->_page_routes)) {
949
+			// user error msg
950
+			$error_msg = sprintf(
951
+				esc_html__(
952
+					'A default page route has not been set for the % admin page.',
953
+					'event_espresso'
954
+				),
955
+				$this->_admin_page_title
956
+			);
957
+			// developer error msg
958
+			$error_msg .= '||' . $error_msg
959
+						  . esc_html__(
960
+							  ' Create a key in the "_page_routes" array named "default" and set its value to your default page method.',
961
+							  'event_espresso'
962
+						  );
963
+			throw new EE_Error($error_msg);
964
+		}
965
+
966
+		// first lets' catch if the UI request has EVER been set.
967
+		if ($this->_is_UI_request === null) {
968
+			// lets set if this is a UI request or not.
969
+			$this->_is_UI_request = ! $this->request->getRequestParam('noheader', false, 'bool');
970
+			// wait a minute... we might have a noheader in the route array
971
+			$this->_is_UI_request = ! (
972
+				is_array($this->_route) && isset($this->_route['noheader']) && $this->_route['noheader']
973
+			)
974
+				? $this->_is_UI_request
975
+				: false;
976
+		}
977
+		$this->_set_current_labels();
978
+		return true;
979
+	}
980
+
981
+
982
+	/**
983
+	 * this method simply verifies a given route and makes sure its an actual route available for the loaded page
984
+	 *
985
+	 * @param string $route the route name we're verifying
986
+	 * @return bool we'll throw an exception if this isn't a valid route.
987
+	 * @throws EE_Error
988
+	 */
989
+	protected function _verify_route($route)
990
+	{
991
+		if (array_key_exists($this->_req_action, $this->_page_routes)) {
992
+			return true;
993
+		}
994
+		// user error msg
995
+		$error_msg = sprintf(
996
+			esc_html__('The given page route does not exist for the %s admin page.', 'event_espresso'),
997
+			$this->_admin_page_title
998
+		);
999
+		// developer error msg
1000
+		$error_msg .= '||' . $error_msg
1001
+					  . sprintf(
1002
+						  esc_html__(
1003
+							  ' Check the route you are using in your method (%s) and make sure it matches a route set in your "_page_routes" array property',
1004
+							  'event_espresso'
1005
+						  ),
1006
+						  $route
1007
+					  );
1008
+		throw new EE_Error($error_msg);
1009
+	}
1010
+
1011
+
1012
+	/**
1013
+	 * perform nonce verification
1014
+	 * This method has be encapsulated here so that any ajax requests that bypass normal routes can verify their nonces
1015
+	 * using this method (and save retyping!)
1016
+	 *
1017
+	 * @param string $nonce     The nonce sent
1018
+	 * @param string $nonce_ref The nonce reference string (name0)
1019
+	 * @return void
1020
+	 * @throws EE_Error
1021
+	 * @throws InvalidArgumentException
1022
+	 * @throws InvalidDataTypeException
1023
+	 * @throws InvalidInterfaceException
1024
+	 */
1025
+	protected function _verify_nonce($nonce, $nonce_ref)
1026
+	{
1027
+		// verify nonce against expected value
1028
+		if (! wp_verify_nonce($nonce, $nonce_ref)) {
1029
+			// these are not the droids you are looking for !!!
1030
+			$msg = sprintf(
1031
+				esc_html__('%sNonce Fail.%s', 'event_espresso'),
1032
+				'<a href="https://www.youtube.com/watch?v=56_S0WeTkzs">',
1033
+				'</a>'
1034
+			);
1035
+			if (WP_DEBUG) {
1036
+				$msg .= "\n  ";
1037
+				$msg .= sprintf(
1038
+					esc_html__(
1039
+						'In order to dynamically generate nonces for your actions, use the %s::add_query_args_and_nonce() method. May the Nonce be with you!',
1040
+						'event_espresso'
1041
+					),
1042
+					__CLASS__
1043
+				);
1044
+			}
1045
+			if (! $this->request->isAjax()) {
1046
+				wp_die($msg);
1047
+			}
1048
+			EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
1049
+			$this->_return_json();
1050
+		}
1051
+	}
1052
+
1053
+
1054
+	/**
1055
+	 * _route_admin_request()
1056
+	 * Meat and potatoes of the class.  Basically, this dude checks out what's being requested and sees if there are
1057
+	 * some doodads to work the magic and handle the flingjangy. Translation:  Checks if the requested action is listed
1058
+	 * in the page routes and then will try to load the corresponding method.
1059
+	 *
1060
+	 * @return void
1061
+	 * @throws EE_Error
1062
+	 * @throws InvalidArgumentException
1063
+	 * @throws InvalidDataTypeException
1064
+	 * @throws InvalidInterfaceException
1065
+	 * @throws ReflectionException
1066
+	 */
1067
+	protected function _route_admin_request()
1068
+	{
1069
+		if (! $this->_is_UI_request) {
1070
+			$this->_verify_routes();
1071
+		}
1072
+		$nonce_check = ! isset($this->_route_config['require_nonce']) || $this->_route_config['require_nonce'];
1073
+		if ($this->_req_action !== 'default' && $nonce_check) {
1074
+			// set nonce from post data
1075
+			$nonce = $this->request->getRequestParam($this->_req_nonce, '');
1076
+			$this->_verify_nonce($nonce, $this->_req_nonce);
1077
+		}
1078
+		// set the nav_tabs array but ONLY if this is  UI_request
1079
+		if ($this->_is_UI_request) {
1080
+			$this->_set_nav_tabs();
1081
+		}
1082
+		// grab callback function
1083
+		$func = is_array($this->_route) && isset($this->_route['func'])
1084
+			? $this->_route['func']
1085
+			: $this->_route;
1086
+		// check if callback has args
1087
+		$args = is_array($this->_route) && isset($this->_route['args']) ? $this->_route['args'] : [];
1088
+		// action right before calling route
1089
+		// (hook is something like 'AHEE__Registrations_Admin_Page__route_admin_request')
1090
+		if (! did_action('AHEE__EE_Admin_Page__route_admin_request')) {
1091
+			do_action('AHEE__EE_Admin_Page__route_admin_request', $this->_current_view, $this);
1092
+		}
1093
+		// strip _wp_http_referer from the server REQUEST_URI
1094
+		// else it grows in length on every submission due to recursion,
1095
+		// ultimately causing a "Request-URI Too Large" error
1096
+		$this->request->unSetRequestParam('_wp_http_referer');
1097
+		$this->request->unSetServerParam('_wp_http_referer');
1098
+		$cleaner_request_uri = remove_query_arg(
1099
+			'_wp_http_referer',
1100
+			wp_unslash($this->request->getServerParam('REQUEST_URI'))
1101
+		);
1102
+		$this->request->setRequestParam('_wp_http_referer', $cleaner_request_uri, true);
1103
+		$this->request->setServerParam('REQUEST_URI', $cleaner_request_uri, true);
1104
+		$route_callback = [];
1105
+		if (! empty($func)) {
1106
+			if (is_array($func)) {
1107
+				$route_callback = $func;
1108
+			} elseif (is_string($func)) {
1109
+				if (strpos($func, '::') !== false) {
1110
+					$route_callback = explode('::', $func);
1111
+				} elseif (method_exists($this, $func)) {
1112
+					$route_callback = [$this, $func];
1113
+				} else {
1114
+					$route_callback = $func;
1115
+				}
1116
+			}
1117
+			[$class, $method] = $route_callback;
1118
+			// is it neither a class method NOR a standalone function?
1119
+			if (! is_callable($route_callback)) {
1120
+				// user error msg
1121
+				$error_msg = esc_html__(
1122
+					'An error occurred. The  requested page route could not be found.',
1123
+					'event_espresso'
1124
+				);
1125
+				// developer error msg
1126
+				$error_msg .= '||';
1127
+				$error_msg .= sprintf(
1128
+					esc_html__(
1129
+						'Page route "%s" could not be called. Check that the spelling for method names and actions in the "_page_routes" array are all correct.',
1130
+						'event_espresso'
1131
+					),
1132
+					$method
1133
+				);
1134
+				throw new DomainException($error_msg);
1135
+			}
1136
+			if ($class !== $this && ! in_array($this, $args)) {
1137
+				// send along this admin page object for access by addons.
1138
+				$args['admin_page'] = $this;
1139
+			}
1140
+
1141
+			try {
1142
+				call_user_func_array($route_callback, $args);
1143
+			} catch (Throwable $throwable) {
1144
+				$arg_keys = array_keys($args);
1145
+				$nice_args = [];
1146
+				foreach ($args as $key => $arg) {
1147
+					$nice_args[ $key ] = is_object($arg) ? get_class($arg) : $arg_keys[ $key ];
1148
+				}
1149
+				new ExceptionStackTraceDisplay(
1150
+						new RuntimeException(
1151
+							sprintf(
1152
+								esc_html__(
1153
+									'Page route "%1$s" with the supplied arguments (%2$s) threw the following exception: %3$s',
1154
+									'event_espresso'
1155
+								),
1156
+								$method,
1157
+								implode(', ', $nice_args),
1158
+								$throwable->getMessage()
1159
+							),
1160
+							$throwable->getCode(),
1161
+							$throwable
1162
+						)
1163
+				);
1164
+			}
1165
+		}
1166
+		// if we've routed and this route has a no headers route AND a sent_headers_route,
1167
+		// then we need to reset the routing properties to the new route.
1168
+		// now if UI request is FALSE and noheader is true AND we have a headers_sent_route in the route array then let's set UI_request to true because the no header route has a second func after headers have been sent.
1169
+		if (
1170
+			$this->_is_UI_request === false
1171
+			&& is_array($this->_route)
1172
+			&& ! empty($this->_route['headers_sent_route'])
1173
+		) {
1174
+			$this->_reset_routing_properties($this->_route['headers_sent_route']);
1175
+		}
1176
+	}
1177
+
1178
+
1179
+	/**
1180
+	 * This method just allows the resetting of page properties in the case where a no headers
1181
+	 * route redirects to a headers route in its route config.
1182
+	 *
1183
+	 * @param string $new_route New (non header) route to redirect to.
1184
+	 * @return   void
1185
+	 * @throws ReflectionException
1186
+	 * @throws InvalidArgumentException
1187
+	 * @throws InvalidInterfaceException
1188
+	 * @throws InvalidDataTypeException
1189
+	 * @throws EE_Error
1190
+	 * @since   4.3.0
1191
+	 */
1192
+	protected function _reset_routing_properties($new_route)
1193
+	{
1194
+		$this->_is_UI_request = true;
1195
+		// now we set the current route to whatever the headers_sent_route is set at
1196
+		$this->request->setRequestParam('action', $new_route);
1197
+		// rerun page setup
1198
+		$this->_page_setup();
1199
+	}
1200
+
1201
+
1202
+	/**
1203
+	 * _add_query_arg
1204
+	 * adds nonce to array of arguments then calls WP add_query_arg function
1205
+	 *(internally just uses EEH_URL's function with the same name)
1206
+	 *
1207
+	 * @param array  $args
1208
+	 * @param string $url
1209
+	 * @param bool   $sticky                  if true, then the existing Request params will be appended to the
1210
+	 *                                        generated url in an associative array indexed by the key 'wp_referer';
1211
+	 *                                        Example usage: If the current page is:
1212
+	 *                                        http://mydomain.com/wp-admin/admin.php?page=espresso_registrations
1213
+	 *                                        &action=default&event_id=20&month_range=March%202015
1214
+	 *                                        &_wpnonce=5467821
1215
+	 *                                        and you call:
1216
+	 *                                        EE_Admin_Page::add_query_args_and_nonce(
1217
+	 *                                        array(
1218
+	 *                                        'action' => 'resend_something',
1219
+	 *                                        'page=>espresso_registrations'
1220
+	 *                                        ),
1221
+	 *                                        $some_url,
1222
+	 *                                        true
1223
+	 *                                        );
1224
+	 *                                        It will produce a url in this structure:
1225
+	 *                                        http://{$some_url}/?page=espresso_registrations&action=resend_something
1226
+	 *                                        &wp_referer[action]=default&wp_referer[event_id]=20&wpreferer[
1227
+	 *                                        month_range]=March%202015
1228
+	 * @param bool   $exclude_nonce           If true, the the nonce will be excluded from the generated nonce.
1229
+	 * @param int    $context
1230
+	 * @return string
1231
+	 */
1232
+	public static function add_query_args_and_nonce(
1233
+		$args = [],
1234
+		$url = '',
1235
+		$sticky = false,
1236
+		$exclude_nonce = false,
1237
+		int $context = EEH_URL::CONTEXT_NONE
1238
+	) {
1239
+		// if there is a _wp_http_referer include the values from the request but only if sticky = true
1240
+		if ($sticky) {
1241
+			/** @var RequestInterface $request */
1242
+			$request = LoaderFactory::getLoader()->getShared(RequestInterface::class);
1243
+			$request->unSetRequestParams(['_wp_http_referer', 'wp_referer'], true);
1244
+			$request->unSetServerParam('_wp_http_referer', true);
1245
+			foreach ($request->requestParams() as $key => $value) {
1246
+				// do not add nonces
1247
+				if (strpos($key, 'nonce') !== false) {
1248
+					continue;
1249
+				}
1250
+				$args[ 'wp_referer[' . $key . ']' ] = is_string($value) ? htmlspecialchars($value) : $value;
1251
+			}
1252
+		}
1253
+		return EEH_URL::add_query_args_and_nonce($args, $url, $exclude_nonce, $context);
1254
+	}
1255
+
1256
+
1257
+	/**
1258
+	 * This returns a generated link that will load the related help tab.
1259
+	 *
1260
+	 * @param string $help_tab_id the id for the connected help tab
1261
+	 * @param string $icon_style  (optional) include css class for the style you want to use for the help icon.
1262
+	 * @param string $help_text   (optional) send help text you want to use for the link if default not to be used
1263
+	 * @return string              generated link
1264
+	 * @uses EEH_Template::get_help_tab_link()
1265
+	 */
1266
+	protected function _get_help_tab_link($help_tab_id, $icon_style = '', $help_text = '')
1267
+	{
1268
+		return EEH_Template::get_help_tab_link(
1269
+			$help_tab_id,
1270
+			$this->page_slug,
1271
+			$this->_req_action,
1272
+			$icon_style,
1273
+			$help_text
1274
+		);
1275
+	}
1276
+
1277
+
1278
+	/**
1279
+	 * _add_help_tabs
1280
+	 * Note child classes define their help tabs within the page_config array.
1281
+	 *
1282
+	 * @link   http://codex.wordpress.org/Function_Reference/add_help_tab
1283
+	 * @return void
1284
+	 * @throws DomainException
1285
+	 * @throws EE_Error
1286
+	 * @throws ReflectionException
1287
+	 */
1288
+	protected function _add_help_tabs()
1289
+	{
1290
+		if (isset($this->_page_config[ $this->_req_action ])) {
1291
+			$config = $this->_page_config[ $this->_req_action ];
1292
+			// let's see if there is a help_sidebar set for the current route and we'll set that up for usage as well.
1293
+			if (is_array($config) && isset($config['help_sidebar'])) {
1294
+				// check that the callback given is valid
1295
+				if (! method_exists($this, $config['help_sidebar'])) {
1296
+					throw new EE_Error(
1297
+						sprintf(
1298
+							esc_html__(
1299
+								'The _page_config array has a callback set for the "help_sidebar" option.  However the callback given (%s) is not a valid callback.  Doublecheck the spelling and make sure this method exists for the class %s',
1300
+								'event_espresso'
1301
+							),
1302
+							$config['help_sidebar'],
1303
+							$this->class_name
1304
+						)
1305
+					);
1306
+				}
1307
+				$content = apply_filters(
1308
+					'FHEE__' . $this->class_name . '__add_help_tabs__help_sidebar',
1309
+					$this->{$config['help_sidebar']}()
1310
+				);
1311
+				$this->_current_screen->set_help_sidebar($content);
1312
+			}
1313
+			if (! isset($config['help_tabs'])) {
1314
+				return;
1315
+			} //no help tabs for this route
1316
+			foreach ((array) $config['help_tabs'] as $tab_id => $cfg) {
1317
+				// we're here so there ARE help tabs!
1318
+				// make sure we've got what we need
1319
+				if (! isset($cfg['title'])) {
1320
+					throw new EE_Error(
1321
+						esc_html__(
1322
+							'The _page_config array is not set up properly for help tabs.  It is missing a title',
1323
+							'event_espresso'
1324
+						)
1325
+					);
1326
+				}
1327
+				if (! isset($cfg['filename']) && ! isset($cfg['callback']) && ! isset($cfg['content'])) {
1328
+					throw new EE_Error(
1329
+						esc_html__(
1330
+							'The _page_config array is not setup properly for help tabs. It is missing a either a filename reference, or a callback reference or a content reference so there is no way to know the content for the help tab',
1331
+							'event_espresso'
1332
+						)
1333
+					);
1334
+				}
1335
+				// first priority goes to content.
1336
+				if (! empty($cfg['content'])) {
1337
+					$content = ! empty($cfg['content']) ? $cfg['content'] : null;
1338
+					// second priority goes to filename
1339
+				} elseif (! empty($cfg['filename'])) {
1340
+					$file_path = $this->_get_dir() . '/help_tabs/' . $cfg['filename'] . '.help_tab.php';
1341
+					// it's possible that the file is located on decaf route (and above sets up for caf route, if this is the case then lets check decaf route too)
1342
+					$file_path = ! is_readable($file_path) ? EE_ADMIN_PAGES
1343
+															 . basename($this->_get_dir())
1344
+															 . '/help_tabs/'
1345
+															 . $cfg['filename']
1346
+															 . '.help_tab.php' : $file_path;
1347
+					// if file is STILL not readable then let's do a EE_Error so its more graceful than a fatal error.
1348
+					if (! isset($cfg['callback']) && ! is_readable($file_path)) {
1349
+						EE_Error::add_error(
1350
+							sprintf(
1351
+								esc_html__(
1352
+									'The filename given for the help tab %s is not a valid file and there is no other configuration for the tab content.  Please check that the string you set for the help tab on this route (%s) is the correct spelling.  The file should be in %s',
1353
+									'event_espresso'
1354
+								),
1355
+								$tab_id,
1356
+								key($config),
1357
+								$file_path
1358
+							),
1359
+							__FILE__,
1360
+							__FUNCTION__,
1361
+							__LINE__
1362
+						);
1363
+						return;
1364
+					}
1365
+					$template_args['admin_page_obj'] = $this;
1366
+					$content                         = EEH_Template::display_template(
1367
+						$file_path,
1368
+						$template_args,
1369
+						true
1370
+					);
1371
+				} else {
1372
+					$content = '';
1373
+				}
1374
+				// check if callback is valid
1375
+				if (
1376
+					empty($content)
1377
+					&& (
1378
+						! isset($cfg['callback']) || ! method_exists($this, $cfg['callback'])
1379
+					)
1380
+				) {
1381
+					EE_Error::add_error(
1382
+						sprintf(
1383
+							esc_html__(
1384
+								'The callback given for a %s help tab on this page does not content OR a corresponding method for generating the content.  Check the spelling or make sure the method is present.',
1385
+								'event_espresso'
1386
+							),
1387
+							$cfg['title']
1388
+						),
1389
+						__FILE__,
1390
+						__FUNCTION__,
1391
+						__LINE__
1392
+					);
1393
+					return;
1394
+				}
1395
+				// setup config array for help tab method
1396
+				$id  = $this->page_slug . '-' . $this->_req_action . '-' . $tab_id;
1397
+				$_ht = [
1398
+					'id'       => $id,
1399
+					'title'    => $cfg['title'],
1400
+					'callback' => isset($cfg['callback']) && empty($content) ? [$this, $cfg['callback']] : null,
1401
+					'content'  => $content,
1402
+				];
1403
+				$this->_current_screen->add_help_tab($_ht);
1404
+			}
1405
+		}
1406
+	}
1407
+
1408
+
1409
+	/**
1410
+	 * This simply sets up any qtips that have been defined in the page config
1411
+	 *
1412
+	 * @return void
1413
+	 * @throws ReflectionException
1414
+	 * @throws EE_Error
1415
+	 */
1416
+	protected function _add_qtips()
1417
+	{
1418
+		if (isset($this->_route_config['qtips'])) {
1419
+			$qtips = (array) $this->_route_config['qtips'];
1420
+			// load qtip loader
1421
+			$path = [
1422
+				$this->_get_dir() . '/qtips/',
1423
+				EE_ADMIN_PAGES . basename($this->_get_dir()) . '/qtips/',
1424
+			];
1425
+			EEH_Qtip_Loader::instance()->register($qtips, $path);
1426
+		}
1427
+	}
1428
+
1429
+
1430
+	/**
1431
+	 * _set_nav_tabs
1432
+	 * This sets up the nav tabs from the page_routes array.  This method can be overwritten by child classes if you
1433
+	 * wish to add additional tabs or modify accordingly.
1434
+	 *
1435
+	 * @return void
1436
+	 * @throws InvalidArgumentException
1437
+	 * @throws InvalidInterfaceException
1438
+	 * @throws InvalidDataTypeException
1439
+	 */
1440
+	protected function _set_nav_tabs()
1441
+	{
1442
+		$i        = 0;
1443
+		$only_tab = count($this->_page_config) < 2;
1444
+		foreach ($this->_page_config as $slug => $config) {
1445
+			if (! is_array($config) || empty($config['nav'])) {
1446
+				continue;
1447
+			}
1448
+			// no nav tab for this config
1449
+			// check for persistent flag
1450
+			if ($slug !== $this->_req_action && isset($config['nav']['persistent']) && ! $config['nav']['persistent']) {
1451
+				// nav tab is only to appear when route requested.
1452
+				continue;
1453
+			}
1454
+			if (! $this->check_user_access($slug, true)) {
1455
+				// no nav tab because current user does not have access.
1456
+				continue;
1457
+			}
1458
+			$css_class = $config['css_class'] ?? '';
1459
+			$css_class .= $only_tab ? ' ee-only-tab' : '';
1460
+			$css_class .= " ee-nav-tab__$slug";
1461
+
1462
+			$this->_nav_tabs[ $slug ] = [
1463
+				'url'       => $config['nav']['url'] ?? EE_Admin_Page::add_query_args_and_nonce(
1464
+						['action' => $slug],
1465
+						$this->_admin_base_url
1466
+					),
1467
+				'link_text' => $this->navTabLabel($config['nav'], $slug),
1468
+				'css_class' => $this->_req_action === $slug ? $css_class . ' nav-tab-active' : $css_class,
1469
+				'order'     => $config['nav']['order'] ?? $i,
1470
+			];
1471
+			$i++;
1472
+		}
1473
+		// if $this->_nav_tabs is empty then lets set the default
1474
+		if (empty($this->_nav_tabs)) {
1475
+			$this->_nav_tabs[ $this->_default_nav_tab_name ] = [
1476
+				'url'       => $this->_admin_base_url,
1477
+				'link_text' => ucwords(str_replace('_', ' ', $this->_default_nav_tab_name)),
1478
+				'css_class' => 'nav-tab-active',
1479
+				'order'     => 10,
1480
+			];
1481
+		}
1482
+		// now let's sort the tabs according to order
1483
+		usort($this->_nav_tabs, [$this, '_sort_nav_tabs']);
1484
+	}
1485
+
1486
+
1487
+	private function navTabLabel(array $nav_tab, string $slug): string
1488
+	{
1489
+		$label = $nav_tab['label'] ?? ucwords(str_replace('_', ' ', $slug));
1490
+		$icon  = $nav_tab['icon'] ?? null;
1491
+		$icon  = $icon ? '<span class="dashicons ' . $icon . '"></span>' : '';
1492
+		return '
1493 1493
             <span class="ee-admin-screen-tab__label">
1494 1494
                 ' . $icon . '
1495 1495
                 <span class="ee-nav-label__text">' . $label . '</span>
1496 1496
             </span>';
1497
-    }
1498
-
1499
-
1500
-    /**
1501
-     * _set_current_labels
1502
-     * This method modifies the _labels property with any optional specific labels indicated in the _page_routes
1503
-     * property array
1504
-     *
1505
-     * @return void
1506
-     */
1507
-    private function _set_current_labels()
1508
-    {
1509
-        if (is_array($this->_route_config) && isset($this->_route_config['labels'])) {
1510
-            foreach ($this->_route_config['labels'] as $label => $text) {
1511
-                if (is_array($text)) {
1512
-                    foreach ($text as $sublabel => $subtext) {
1513
-                        $this->_labels[ $label ][ $sublabel ] = $subtext;
1514
-                    }
1515
-                } else {
1516
-                    $this->_labels[ $label ] = $text;
1517
-                }
1518
-            }
1519
-        }
1520
-    }
1521
-
1522
-
1523
-    /**
1524
-     *        verifies user access for this admin page
1525
-     *
1526
-     * @param string $route_to_check if present then the capability for the route matching this string is checked.
1527
-     * @param bool   $verify_only    Default is FALSE which means if user check fails then wp_die().  Otherwise just
1528
-     *                               return false if verify fail.
1529
-     * @return bool
1530
-     * @throws InvalidArgumentException
1531
-     * @throws InvalidDataTypeException
1532
-     * @throws InvalidInterfaceException
1533
-     */
1534
-    public function check_user_access($route_to_check = '', $verify_only = false)
1535
-    {
1536
-        $route_to_check = ! empty($route_to_check) ? $route_to_check : $this->_req_action;
1537
-        $capability     = ! empty($route_to_check)
1538
-                          && isset($this->_page_routes[ $route_to_check ])
1539
-                          && is_array($this->_page_routes[ $route_to_check ])
1540
-                          && ! empty($this->_page_routes[ $route_to_check ]['capability'])
1541
-            ? $this->_page_routes[ $route_to_check ]['capability']
1542
-            : null;
1543
-
1544
-        if (empty($capability) && empty($route_to_check)) {
1545
-            $capability = ! empty($this->_route['capability']) ? $this->_route['capability'] : 'manage_options';
1546
-        } else {
1547
-            $capability = empty($capability) ? 'manage_options' : $capability;
1548
-        }
1549
-
1550
-        $id = is_array($this->_route) && ! empty($this->_route['obj_id']) ? $this->_route['obj_id'] : 0;
1551
-
1552
-        if (
1553
-            ! $this->request->isAjax()
1554
-            && (
1555
-                ! function_exists('is_admin')
1556
-                || ! EE_Registry::instance()->CAP->current_user_can(
1557
-                    $capability,
1558
-                    "{$this->page_slug}_$route_to_check",
1559
-                    $id
1560
-                )
1561
-            )
1562
-        ) {
1563
-            if ($verify_only) {
1564
-                return false;
1565
-            }
1566
-            if (is_user_logged_in()) {
1567
-                wp_die(esc_html__('You do not have access to this route.', 'event_espresso'));
1568
-            }
1569
-            return false;
1570
-        }
1571
-        return true;
1572
-    }
1573
-
1574
-
1575
-    /**
1576
-     * @param string                 $box_id
1577
-     * @param string                 $title
1578
-     * @param callable|string|null   $callback
1579
-     * @param string|array|WP_Screen $screen
1580
-     * @param string                 $context
1581
-     * @param string                 $priority
1582
-     * @param array|null             $callback_args
1583
-     */
1584
-    protected function addMetaBox(
1585
-        string $box_id,
1586
-        string $title,
1587
-        $callback,
1588
-        $screen,
1589
-        string $context = 'normal',
1590
-        string $priority = 'default',
1591
-        ?array $callback_args = null
1592
-    ) {
1593
-        if (! (is_callable($callback) || ! function_exists($callback))) {
1594
-            return;
1595
-        }
1596
-
1597
-        add_meta_box($box_id, $title, $callback, $screen, $context, $priority, $callback_args);
1598
-        add_filter(
1599
-            "postbox_classes_{$this->_wp_page_slug}_{$box_id}",
1600
-            function ($classes) {
1601
-                $classes[] = 'ee-admin-container';
1602
-                return $classes;
1603
-            }
1604
-        );
1605
-    }
1606
-
1607
-
1608
-    /**
1609
-     * admin_init_global
1610
-     * This runs all the code that we want executed within the WP admin_init hook.
1611
-     * This method executes for ALL EE Admin pages.
1612
-     *
1613
-     * @return void
1614
-     */
1615
-    public function admin_init_global()
1616
-    {
1617
-    }
1618
-
1619
-
1620
-    /**
1621
-     * wp_loaded_global
1622
-     * This runs all the code that we want executed within the WP wp_loaded hook.  This method is optional for an
1623
-     * EE_Admin page and will execute on every EE Admin Page load
1624
-     *
1625
-     * @return void
1626
-     */
1627
-    public function wp_loaded()
1628
-    {
1629
-    }
1630
-
1631
-
1632
-    /**
1633
-     * admin_notices
1634
-     * Anything triggered by the 'admin_notices' WP hook should be put in here.  This particular method will apply on
1635
-     * ALL EE_Admin pages.
1636
-     *
1637
-     * @return void
1638
-     */
1639
-    public function admin_notices_global()
1640
-    {
1641
-        $this->_display_no_javascript_warning();
1642
-        $this->_display_espresso_notices();
1643
-    }
1644
-
1645
-
1646
-    public function network_admin_notices_global()
1647
-    {
1648
-        $this->_display_no_javascript_warning();
1649
-        $this->_display_espresso_notices();
1650
-    }
1651
-
1652
-
1653
-    /**
1654
-     * admin_footer_scripts_global
1655
-     * Anything triggered by the 'admin_print_footer_scripts' WP hook should be put in here. This particular method
1656
-     * will apply on ALL EE_Admin pages.
1657
-     *
1658
-     * @return void
1659
-     */
1660
-    public function admin_footer_scripts_global()
1661
-    {
1662
-        $this->_add_admin_page_ajax_loading_img();
1663
-        $this->_add_admin_page_overlay();
1664
-        // if metaboxes are present we need to add the nonce field
1665
-        if (
1666
-            isset($this->_route_config['metaboxes'])
1667
-            || isset($this->_route_config['list_table'])
1668
-            || (isset($this->_route_config['has_metaboxes']) && $this->_route_config['has_metaboxes'])
1669
-        ) {
1670
-            wp_nonce_field('closedpostboxes', 'closedpostboxesnonce', false);
1671
-            wp_nonce_field('meta-box-order', 'meta-box-order-nonce', false);
1672
-        }
1673
-    }
1674
-
1675
-
1676
-    /**
1677
-     * admin_footer_global
1678
-     * Anything triggered by the wp 'admin_footer' wp hook should be put in here.
1679
-     * This particular method will apply on ALL EE_Admin Pages.
1680
-     *
1681
-     * @return void
1682
-     */
1683
-    public function admin_footer_global()
1684
-    {
1685
-        // dialog container for dialog helper
1686
-        echo '
1497
+	}
1498
+
1499
+
1500
+	/**
1501
+	 * _set_current_labels
1502
+	 * This method modifies the _labels property with any optional specific labels indicated in the _page_routes
1503
+	 * property array
1504
+	 *
1505
+	 * @return void
1506
+	 */
1507
+	private function _set_current_labels()
1508
+	{
1509
+		if (is_array($this->_route_config) && isset($this->_route_config['labels'])) {
1510
+			foreach ($this->_route_config['labels'] as $label => $text) {
1511
+				if (is_array($text)) {
1512
+					foreach ($text as $sublabel => $subtext) {
1513
+						$this->_labels[ $label ][ $sublabel ] = $subtext;
1514
+					}
1515
+				} else {
1516
+					$this->_labels[ $label ] = $text;
1517
+				}
1518
+			}
1519
+		}
1520
+	}
1521
+
1522
+
1523
+	/**
1524
+	 *        verifies user access for this admin page
1525
+	 *
1526
+	 * @param string $route_to_check if present then the capability for the route matching this string is checked.
1527
+	 * @param bool   $verify_only    Default is FALSE which means if user check fails then wp_die().  Otherwise just
1528
+	 *                               return false if verify fail.
1529
+	 * @return bool
1530
+	 * @throws InvalidArgumentException
1531
+	 * @throws InvalidDataTypeException
1532
+	 * @throws InvalidInterfaceException
1533
+	 */
1534
+	public function check_user_access($route_to_check = '', $verify_only = false)
1535
+	{
1536
+		$route_to_check = ! empty($route_to_check) ? $route_to_check : $this->_req_action;
1537
+		$capability     = ! empty($route_to_check)
1538
+						  && isset($this->_page_routes[ $route_to_check ])
1539
+						  && is_array($this->_page_routes[ $route_to_check ])
1540
+						  && ! empty($this->_page_routes[ $route_to_check ]['capability'])
1541
+			? $this->_page_routes[ $route_to_check ]['capability']
1542
+			: null;
1543
+
1544
+		if (empty($capability) && empty($route_to_check)) {
1545
+			$capability = ! empty($this->_route['capability']) ? $this->_route['capability'] : 'manage_options';
1546
+		} else {
1547
+			$capability = empty($capability) ? 'manage_options' : $capability;
1548
+		}
1549
+
1550
+		$id = is_array($this->_route) && ! empty($this->_route['obj_id']) ? $this->_route['obj_id'] : 0;
1551
+
1552
+		if (
1553
+			! $this->request->isAjax()
1554
+			&& (
1555
+				! function_exists('is_admin')
1556
+				|| ! EE_Registry::instance()->CAP->current_user_can(
1557
+					$capability,
1558
+					"{$this->page_slug}_$route_to_check",
1559
+					$id
1560
+				)
1561
+			)
1562
+		) {
1563
+			if ($verify_only) {
1564
+				return false;
1565
+			}
1566
+			if (is_user_logged_in()) {
1567
+				wp_die(esc_html__('You do not have access to this route.', 'event_espresso'));
1568
+			}
1569
+			return false;
1570
+		}
1571
+		return true;
1572
+	}
1573
+
1574
+
1575
+	/**
1576
+	 * @param string                 $box_id
1577
+	 * @param string                 $title
1578
+	 * @param callable|string|null   $callback
1579
+	 * @param string|array|WP_Screen $screen
1580
+	 * @param string                 $context
1581
+	 * @param string                 $priority
1582
+	 * @param array|null             $callback_args
1583
+	 */
1584
+	protected function addMetaBox(
1585
+		string $box_id,
1586
+		string $title,
1587
+		$callback,
1588
+		$screen,
1589
+		string $context = 'normal',
1590
+		string $priority = 'default',
1591
+		?array $callback_args = null
1592
+	) {
1593
+		if (! (is_callable($callback) || ! function_exists($callback))) {
1594
+			return;
1595
+		}
1596
+
1597
+		add_meta_box($box_id, $title, $callback, $screen, $context, $priority, $callback_args);
1598
+		add_filter(
1599
+			"postbox_classes_{$this->_wp_page_slug}_{$box_id}",
1600
+			function ($classes) {
1601
+				$classes[] = 'ee-admin-container';
1602
+				return $classes;
1603
+			}
1604
+		);
1605
+	}
1606
+
1607
+
1608
+	/**
1609
+	 * admin_init_global
1610
+	 * This runs all the code that we want executed within the WP admin_init hook.
1611
+	 * This method executes for ALL EE Admin pages.
1612
+	 *
1613
+	 * @return void
1614
+	 */
1615
+	public function admin_init_global()
1616
+	{
1617
+	}
1618
+
1619
+
1620
+	/**
1621
+	 * wp_loaded_global
1622
+	 * This runs all the code that we want executed within the WP wp_loaded hook.  This method is optional for an
1623
+	 * EE_Admin page and will execute on every EE Admin Page load
1624
+	 *
1625
+	 * @return void
1626
+	 */
1627
+	public function wp_loaded()
1628
+	{
1629
+	}
1630
+
1631
+
1632
+	/**
1633
+	 * admin_notices
1634
+	 * Anything triggered by the 'admin_notices' WP hook should be put in here.  This particular method will apply on
1635
+	 * ALL EE_Admin pages.
1636
+	 *
1637
+	 * @return void
1638
+	 */
1639
+	public function admin_notices_global()
1640
+	{
1641
+		$this->_display_no_javascript_warning();
1642
+		$this->_display_espresso_notices();
1643
+	}
1644
+
1645
+
1646
+	public function network_admin_notices_global()
1647
+	{
1648
+		$this->_display_no_javascript_warning();
1649
+		$this->_display_espresso_notices();
1650
+	}
1651
+
1652
+
1653
+	/**
1654
+	 * admin_footer_scripts_global
1655
+	 * Anything triggered by the 'admin_print_footer_scripts' WP hook should be put in here. This particular method
1656
+	 * will apply on ALL EE_Admin pages.
1657
+	 *
1658
+	 * @return void
1659
+	 */
1660
+	public function admin_footer_scripts_global()
1661
+	{
1662
+		$this->_add_admin_page_ajax_loading_img();
1663
+		$this->_add_admin_page_overlay();
1664
+		// if metaboxes are present we need to add the nonce field
1665
+		if (
1666
+			isset($this->_route_config['metaboxes'])
1667
+			|| isset($this->_route_config['list_table'])
1668
+			|| (isset($this->_route_config['has_metaboxes']) && $this->_route_config['has_metaboxes'])
1669
+		) {
1670
+			wp_nonce_field('closedpostboxes', 'closedpostboxesnonce', false);
1671
+			wp_nonce_field('meta-box-order', 'meta-box-order-nonce', false);
1672
+		}
1673
+	}
1674
+
1675
+
1676
+	/**
1677
+	 * admin_footer_global
1678
+	 * Anything triggered by the wp 'admin_footer' wp hook should be put in here.
1679
+	 * This particular method will apply on ALL EE_Admin Pages.
1680
+	 *
1681
+	 * @return void
1682
+	 */
1683
+	public function admin_footer_global()
1684
+	{
1685
+		// dialog container for dialog helper
1686
+		echo '
1687 1687
         <div class="ee-admin-dialog-container auto-hide hidden">
1688 1688
             <div class="ee-notices"></div>
1689 1689
             <div class="ee-admin-dialog-container-inner-content"></div>
1690 1690
         </div>
1691 1691
         ';
1692 1692
 
1693
-        // current set timezone for timezone js
1694
-        echo '<span id="current_timezone" class="hidden">' . esc_html(EEH_DTT_Helper::get_timezone()) . '</span>';
1695
-    }
1696
-
1697
-
1698
-    /**
1699
-     * This function sees if there is a method for help popup content existing for the given route.  If there is then
1700
-     * we'll use the retrieved array to output the content using the template. For child classes: If you want to have
1701
-     * help popups then in your templates or your content you set "triggers" for the content using the
1702
-     * "_set_help_trigger('help_trigger_id')" where "help_trigger_id" is what you will use later in your custom method
1703
-     * for the help popup content on that page. Then in your Child_Admin_Page class you need to define a help popup
1704
-     * method for the content in the format "_help_popup_content_{route_name}()"  So if you are setting help content
1705
-     * for the
1706
-     * 'edit_event' route you should have a method named "_help_popup_content_edit_route". In your defined
1707
-     * "help_popup_content_..." method.  You must prepare and return an array in the following format array(
1708
-     *    'help_trigger_id' => array(
1709
-     *        'title' => esc_html__('localized title for popup', 'event_espresso'),
1710
-     *        'content' => esc_html__('localized content for popup', 'event_espresso')
1711
-     *    )
1712
-     * );
1713
-     * Then the EE_Admin_Parent will take care of making sure that is setup properly on the correct route.
1714
-     *
1715
-     * @param array $help_array
1716
-     * @param bool  $display
1717
-     * @return string content
1718
-     * @throws DomainException
1719
-     * @throws EE_Error
1720
-     */
1721
-    protected function _set_help_popup_content($help_array = [], $display = false)
1722
-    {
1723
-        $content    = '';
1724
-        $help_array = empty($help_array) ? $this->_get_help_content() : $help_array;
1725
-        // loop through the array and setup content
1726
-        foreach ($help_array as $trigger => $help) {
1727
-            // make sure the array is setup properly
1728
-            if (! isset($help['title'], $help['content'])) {
1729
-                throw new EE_Error(
1730
-                    esc_html__(
1731
-                        'Does not look like the popup content array has been setup correctly.  Might want to double check that.  Read the comments for the _get_help_popup_content method found in "EE_Admin_Page" class',
1732
-                        'event_espresso'
1733
-                    )
1734
-                );
1735
-            }
1736
-            // we're good so let's setup the template vars and then assign parsed template content to our content.
1737
-            $template_args = [
1738
-                'help_popup_id'      => $trigger,
1739
-                'help_popup_title'   => $help['title'],
1740
-                'help_popup_content' => $help['content'],
1741
-            ];
1742
-            $content       .= EEH_Template::display_template(
1743
-                EE_ADMIN_TEMPLATE . 'admin_help_popup.template.php',
1744
-                $template_args,
1745
-                true
1746
-            );
1747
-        }
1748
-        if ($display) {
1749
-            echo wp_kses($content, AllowedTags::getWithFormTags());
1750
-            return '';
1751
-        }
1752
-        return $content;
1753
-    }
1754
-
1755
-
1756
-    /**
1757
-     * All this does is retrieve the help content array if set by the EE_Admin_Page child
1758
-     *
1759
-     * @return array properly formatted array for help popup content
1760
-     * @throws EE_Error
1761
-     */
1762
-    private function _get_help_content()
1763
-    {
1764
-        // what is the method we're looking for?
1765
-        $method_name = '_help_popup_content_' . $this->_req_action;
1766
-        // if method doesn't exist let's get out.
1767
-        if (! method_exists($this, $method_name)) {
1768
-            return [];
1769
-        }
1770
-        // k we're good to go let's retrieve the help array
1771
-        $help_array = $this->{$method_name}();
1772
-        // make sure we've got an array!
1773
-        if (! is_array($help_array)) {
1774
-            throw new EE_Error(
1775
-                esc_html__(
1776
-                    'Something went wrong with help popup content generation. Expecting an array and well, this ain\'t no array bub.',
1777
-                    'event_espresso'
1778
-                )
1779
-            );
1780
-        }
1781
-        return $help_array;
1782
-    }
1783
-
1784
-
1785
-    /**
1786
-     * EE Admin Pages can use this to set a properly formatted trigger for a help popup.
1787
-     * By default the trigger html is printed.  Otherwise it can be returned if the $display flag is set "false"
1788
-     * See comments made on the _set_help_content method for understanding other parts to the help popup tool.
1789
-     *
1790
-     * @param string  $trigger_id reference for retrieving the trigger content for the popup
1791
-     * @param boolean $display    if false then we return the trigger string
1792
-     * @param array   $dimensions an array of dimensions for the box (array(h,w))
1793
-     * @return string
1794
-     * @throws DomainException
1795
-     * @throws EE_Error
1796
-     */
1797
-    protected function _set_help_trigger($trigger_id, $display = true, $dimensions = ['400', '640'])
1798
-    {
1799
-        if ($this->request->isAjax()) {
1800
-            return '';
1801
-        }
1802
-        // let's check and see if there is any content set for this popup.  If there isn't then we'll include a default title and content so that developers know something needs to be corrected
1803
-        $help_array   = $this->_get_help_content();
1804
-        $help_content = '';
1805
-        if (empty($help_array) || ! isset($help_array[ $trigger_id ])) {
1806
-            $help_array[ $trigger_id ] = [
1807
-                'title'   => esc_html__('Missing Content', 'event_espresso'),
1808
-                'content' => esc_html__(
1809
-                    'A trigger has been set that doesn\'t have any corresponding content. Make sure you have set the help content. (see the "_set_help_popup_content" method in the EE_Admin_Page for instructions.)',
1810
-                    'event_espresso'
1811
-                ),
1812
-            ];
1813
-            $help_content              = $this->_set_help_popup_content($help_array);
1814
-        }
1815
-        // let's setup the trigger
1816
-        $content = '<a class="ee-dialog" href="?height='
1817
-                   . esc_attr($dimensions[0])
1818
-                   . '&width='
1819
-                   . esc_attr($dimensions[1])
1820
-                   . '&inlineId='
1821
-                   . esc_attr($trigger_id)
1822
-                   . '" target="_blank"><span class="question ee-help-popup-question"></span></a>';
1823
-        $content .= $help_content;
1824
-        if ($display) {
1825
-            echo wp_kses($content, AllowedTags::getWithFormTags());
1826
-            return '';
1827
-        }
1828
-        return $content;
1829
-    }
1830
-
1831
-
1832
-    /**
1833
-     * _add_global_screen_options
1834
-     * Add any extra wp_screen_options within this method using built-in WP functions/methods for doing so.
1835
-     * This particular method will add_screen_options on ALL EE_Admin Pages
1836
-     *
1837
-     * @link   http://chrismarslender.com/wp-tutorials/wordpress-screen-options-tutorial/
1838
-     *         see also WP_Screen object documents...
1839
-     * @link   http://codex.wordpress.org/Class_Reference/WP_Screen
1840
-     * @abstract
1841
-     * @return void
1842
-     */
1843
-    private function _add_global_screen_options()
1844
-    {
1845
-    }
1846
-
1847
-
1848
-    /**
1849
-     * _add_global_feature_pointers
1850
-     * This method is used for implementing any "feature pointers" (using built-in WP styling js).
1851
-     * This particular method will implement feature pointers for ALL EE_Admin pages.
1852
-     * Note: this is just a placeholder for now.  Implementation will come down the road
1853
-     *
1854
-     * @see    WP_Internal_Pointers class in wp-admin/includes/template.php for example (its a final class so can't be
1855
-     *         extended) also see:
1856
-     * @link   http://eamann.com/tech/wordpress-portland/
1857
-     * @abstract
1858
-     * @return void
1859
-     */
1860
-    private function _add_global_feature_pointers()
1861
-    {
1862
-    }
1863
-
1864
-
1865
-    /**
1866
-     * load_global_scripts_styles
1867
-     * The scripts and styles enqueued in here will be loaded on every EE Admin page
1868
-     *
1869
-     * @return void
1870
-     */
1871
-    public function load_global_scripts_styles()
1872
-    {
1873
-        // add debugging styles
1874
-        if (WP_DEBUG) {
1875
-            add_action('admin_head', [$this, 'add_xdebug_style']);
1876
-        }
1877
-        // taking care of metaboxes
1878
-        if (
1879
-            empty($this->_cpt_route)
1880
-            && (isset($this->_route_config['metaboxes']) || isset($this->_route_config['has_metaboxes']))
1881
-        ) {
1882
-            wp_enqueue_script('dashboard');
1883
-        }
1884
-
1885
-        wp_enqueue_script(JqueryAssetManager::JS_HANDLE_JQUERY_UI_TOUCH_PUNCH);
1886
-        wp_enqueue_script(EspressoLegacyAdminAssetManager::JS_HANDLE_EE_ADMIN);
1887
-        // LOCALIZED DATA
1888
-        // localize script for ajax lazy loading
1889
-        wp_localize_script(
1890
-            EspressoLegacyAdminAssetManager::JS_HANDLE_EE_ADMIN,
1891
-            'eeLazyLoadingContainers',
1892
-            apply_filters(
1893
-                'FHEE__EE_Admin_Page_Core__load_global_scripts_styles__loader_containers',
1894
-                ['espresso_news_post_box_content']
1895
-            )
1896
-        );
1897
-        StatusChangeNotice::loadAssets();
1898
-
1899
-        add_filter(
1900
-            'admin_body_class',
1901
-            function ($classes) {
1902
-                if (strpos($classes, 'espresso-admin') === false) {
1903
-                    $classes .= ' espresso-admin';
1904
-                }
1905
-                return $classes;
1906
-            }
1907
-        );
1908
-    }
1909
-
1910
-
1911
-    /**
1912
-     *        admin_footer_scripts_eei18n_js_strings
1913
-     *
1914
-     * @return        void
1915
-     */
1916
-    public function admin_footer_scripts_eei18n_js_strings()
1917
-    {
1918
-        EE_Registry::$i18n_js_strings['ajax_url']       = WP_AJAX_URL;
1919
-        EE_Registry::$i18n_js_strings['confirm_delete'] = wp_strip_all_tags(
1920
-            __(
1921
-                'Are you absolutely sure you want to delete this item?\nThis action will delete ALL DATA associated with this item!!!\nThis can NOT be undone!!!',
1922
-                'event_espresso'
1923
-            )
1924
-        );
1925
-        EE_Registry::$i18n_js_strings['January']        = wp_strip_all_tags(__('January', 'event_espresso'));
1926
-        EE_Registry::$i18n_js_strings['February']       = wp_strip_all_tags(__('February', 'event_espresso'));
1927
-        EE_Registry::$i18n_js_strings['March']          = wp_strip_all_tags(__('March', 'event_espresso'));
1928
-        EE_Registry::$i18n_js_strings['April']          = wp_strip_all_tags(__('April', 'event_espresso'));
1929
-        EE_Registry::$i18n_js_strings['May']            = wp_strip_all_tags(__('May', 'event_espresso'));
1930
-        EE_Registry::$i18n_js_strings['June']           = wp_strip_all_tags(__('June', 'event_espresso'));
1931
-        EE_Registry::$i18n_js_strings['July']           = wp_strip_all_tags(__('July', 'event_espresso'));
1932
-        EE_Registry::$i18n_js_strings['August']         = wp_strip_all_tags(__('August', 'event_espresso'));
1933
-        EE_Registry::$i18n_js_strings['September']      = wp_strip_all_tags(__('September', 'event_espresso'));
1934
-        EE_Registry::$i18n_js_strings['October']        = wp_strip_all_tags(__('October', 'event_espresso'));
1935
-        EE_Registry::$i18n_js_strings['November']       = wp_strip_all_tags(__('November', 'event_espresso'));
1936
-        EE_Registry::$i18n_js_strings['December']       = wp_strip_all_tags(__('December', 'event_espresso'));
1937
-        EE_Registry::$i18n_js_strings['Jan']            = wp_strip_all_tags(__('Jan', 'event_espresso'));
1938
-        EE_Registry::$i18n_js_strings['Feb']            = wp_strip_all_tags(__('Feb', 'event_espresso'));
1939
-        EE_Registry::$i18n_js_strings['Mar']            = wp_strip_all_tags(__('Mar', 'event_espresso'));
1940
-        EE_Registry::$i18n_js_strings['Apr']            = wp_strip_all_tags(__('Apr', 'event_espresso'));
1941
-        EE_Registry::$i18n_js_strings['May']            = wp_strip_all_tags(__('May', 'event_espresso'));
1942
-        EE_Registry::$i18n_js_strings['Jun']            = wp_strip_all_tags(__('Jun', 'event_espresso'));
1943
-        EE_Registry::$i18n_js_strings['Jul']            = wp_strip_all_tags(__('Jul', 'event_espresso'));
1944
-        EE_Registry::$i18n_js_strings['Aug']            = wp_strip_all_tags(__('Aug', 'event_espresso'));
1945
-        EE_Registry::$i18n_js_strings['Sep']            = wp_strip_all_tags(__('Sep', 'event_espresso'));
1946
-        EE_Registry::$i18n_js_strings['Oct']            = wp_strip_all_tags(__('Oct', 'event_espresso'));
1947
-        EE_Registry::$i18n_js_strings['Nov']            = wp_strip_all_tags(__('Nov', 'event_espresso'));
1948
-        EE_Registry::$i18n_js_strings['Dec']            = wp_strip_all_tags(__('Dec', 'event_espresso'));
1949
-        EE_Registry::$i18n_js_strings['Sunday']         = wp_strip_all_tags(__('Sunday', 'event_espresso'));
1950
-        EE_Registry::$i18n_js_strings['Monday']         = wp_strip_all_tags(__('Monday', 'event_espresso'));
1951
-        EE_Registry::$i18n_js_strings['Tuesday']        = wp_strip_all_tags(__('Tuesday', 'event_espresso'));
1952
-        EE_Registry::$i18n_js_strings['Wednesday']      = wp_strip_all_tags(__('Wednesday', 'event_espresso'));
1953
-        EE_Registry::$i18n_js_strings['Thursday']       = wp_strip_all_tags(__('Thursday', 'event_espresso'));
1954
-        EE_Registry::$i18n_js_strings['Friday']         = wp_strip_all_tags(__('Friday', 'event_espresso'));
1955
-        EE_Registry::$i18n_js_strings['Saturday']       = wp_strip_all_tags(__('Saturday', 'event_espresso'));
1956
-        EE_Registry::$i18n_js_strings['Sun']            = wp_strip_all_tags(__('Sun', 'event_espresso'));
1957
-        EE_Registry::$i18n_js_strings['Mon']            = wp_strip_all_tags(__('Mon', 'event_espresso'));
1958
-        EE_Registry::$i18n_js_strings['Tue']            = wp_strip_all_tags(__('Tue', 'event_espresso'));
1959
-        EE_Registry::$i18n_js_strings['Wed']            = wp_strip_all_tags(__('Wed', 'event_espresso'));
1960
-        EE_Registry::$i18n_js_strings['Thu']            = wp_strip_all_tags(__('Thu', 'event_espresso'));
1961
-        EE_Registry::$i18n_js_strings['Fri']            = wp_strip_all_tags(__('Fri', 'event_espresso'));
1962
-        EE_Registry::$i18n_js_strings['Sat']            = wp_strip_all_tags(__('Sat', 'event_espresso'));
1963
-    }
1964
-
1965
-
1966
-    /**
1967
-     *        load enhanced xdebug styles for ppl with failing eyesight
1968
-     *
1969
-     * @return        void
1970
-     */
1971
-    public function add_xdebug_style()
1972
-    {
1973
-        echo '<style>.xdebug-error { font-size:1.5em; }</style>';
1974
-    }
1975
-
1976
-
1977
-    /************************/
1978
-    /** LIST TABLE METHODS **/
1979
-    /************************/
1980
-    /**
1981
-     * this sets up the list table if the current view requires it.
1982
-     *
1983
-     * @return void
1984
-     * @throws EE_Error
1985
-     * @throws InvalidArgumentException
1986
-     * @throws InvalidDataTypeException
1987
-     * @throws InvalidInterfaceException
1988
-     */
1989
-    protected function _set_list_table()
1990
-    {
1991
-        // first is this a list_table view?
1992
-        if (! isset($this->_route_config['list_table'])) {
1993
-            return;
1994
-        } //not a list_table view so get out.
1995
-        // list table functions are per view specific (because some admin pages might have more than one list table!)
1996
-        $list_table_view = '_set_list_table_views_' . $this->_req_action;
1997
-        if (! method_exists($this, $list_table_view) || $this->{$list_table_view}() === false) {
1998
-            // user error msg
1999
-            $error_msg = esc_html__(
2000
-                'An error occurred. The requested list table views could not be found.',
2001
-                'event_espresso'
2002
-            );
2003
-            // developer error msg
2004
-            $error_msg .= '||'
2005
-                          . sprintf(
2006
-                              esc_html__(
2007
-                                  'List table views for "%s" route could not be setup. Check that you have the corresponding method, "%s" set up for defining list_table_views for this route.',
2008
-                                  'event_espresso'
2009
-                              ),
2010
-                              $this->_req_action,
2011
-                              $list_table_view
2012
-                          );
2013
-            throw new EE_Error($error_msg);
2014
-        }
2015
-        // let's provide the ability to filter the views per PAGE AND ROUTE, per PAGE, and globally
2016
-        $this->_views = apply_filters(
2017
-            'FHEE_list_table_views_' . $this->page_slug . '_' . $this->_req_action,
2018
-            $this->_views
2019
-        );
2020
-        $this->_views = apply_filters('FHEE_list_table_views_' . $this->page_slug, $this->_views);
2021
-        $this->_views = apply_filters('FHEE_list_table_views', $this->_views);
2022
-        $this->_set_list_table_view();
2023
-        $this->_set_list_table_object();
2024
-    }
2025
-
2026
-
2027
-    /**
2028
-     * set current view for List Table
2029
-     *
2030
-     * @return void
2031
-     */
2032
-    protected function _set_list_table_view()
2033
-    {
2034
-        $this->_view = isset($this->_views['in_use']) ? 'in_use' : 'all';
2035
-        $status      = $this->request->getRequestParam('status', null, 'key');
2036
-        $this->_view = $status && array_key_exists($status, $this->_views)
2037
-            ? $status
2038
-            : $this->_view;
2039
-    }
2040
-
2041
-
2042
-    /**
2043
-     * _set_list_table_object
2044
-     * WP_List_Table objects need to be loaded fairly early so automatic stuff WP does is taken care of.
2045
-     *
2046
-     * @throws InvalidInterfaceException
2047
-     * @throws InvalidArgumentException
2048
-     * @throws InvalidDataTypeException
2049
-     * @throws EE_Error
2050
-     * @throws InvalidInterfaceException
2051
-     */
2052
-    protected function _set_list_table_object()
2053
-    {
2054
-        if (isset($this->_route_config['list_table'])) {
2055
-            if (! class_exists($this->_route_config['list_table'])) {
2056
-                throw new EE_Error(
2057
-                    sprintf(
2058
-                        esc_html__(
2059
-                            'The %s class defined for the list table does not exist.  Please check the spelling of the class ref in the $_page_config property on %s.',
2060
-                            'event_espresso'
2061
-                        ),
2062
-                        $this->_route_config['list_table'],
2063
-                        $this->class_name
2064
-                    )
2065
-                );
2066
-            }
2067
-            $this->_list_table_object = $this->loader->getShared(
2068
-                $this->_route_config['list_table'],
2069
-                [
2070
-                    $this,
2071
-                    LoaderFactory::getShared(AdminListTableFilters::class)
2072
-                ]
2073
-            );
2074
-        }
2075
-    }
2076
-
2077
-
2078
-    /**
2079
-     * get_list_table_view_RLs - get it? View RL ?? VU-RL???  URL ??
2080
-     *
2081
-     * @param array $extra_query_args                     Optional. An array of extra query args to add to the generated
2082
-     *                                                    urls.  The array should be indexed by the view it is being
2083
-     *                                                    added to.
2084
-     * @return array
2085
-     */
2086
-    public function get_list_table_view_RLs($extra_query_args = [])
2087
-    {
2088
-        do_action('AHEE_log', __FILE__, __FUNCTION__, '');
2089
-        if (empty($this->_views)) {
2090
-            $this->_views = [];
2091
-        }
2092
-        // cycle thru views
2093
-        foreach ($this->_views as $key => $view) {
2094
-            $query_args = [];
2095
-            // check for current view
2096
-            $this->_views[ $key ]['class']               = $this->_view === $view['slug'] ? 'current' : '';
2097
-            $query_args['action']                        = $this->_req_action;
2098
-            $query_args[ $this->_req_action . '_nonce' ] = wp_create_nonce($query_args['action'] . '_nonce');
2099
-            $query_args['status']                        = $view['slug'];
2100
-            // merge any other arguments sent in.
2101
-            if (isset($extra_query_args[ $view['slug'] ])) {
2102
-                foreach ($extra_query_args[ $view['slug'] ] as $extra_query_arg) {
2103
-                    $query_args[] = $extra_query_arg;
2104
-                }
2105
-            }
2106
-            $this->_views[ $key ]['url'] = EE_Admin_Page::add_query_args_and_nonce($query_args, $this->_admin_base_url);
2107
-        }
2108
-        return $this->_views;
2109
-    }
2110
-
2111
-
2112
-    /**
2113
-     * _entries_per_page_dropdown
2114
-     * generates a dropdown box for selecting the number of visible rows in an admin page list table
2115
-     *
2116
-     * @param int $max_entries total number of rows in the table
2117
-     * @return string
2118
-     * @todo   : Note: ideally this should be added to the screen options dropdown as that would be consistent with how
2119
-     *                         WP does it.
2120
-     */
2121
-    protected function _entries_per_page_dropdown($max_entries = 0)
2122
-    {
2123
-        do_action('AHEE_log', __FILE__, __FUNCTION__, '');
2124
-        $values   = [10, 25, 50, 100];
2125
-        $per_page = $this->request->getRequestParam('per_page', 10, 'int');
2126
-        if ($max_entries) {
2127
-            $values[] = $max_entries;
2128
-            sort($values);
2129
-        }
2130
-        $entries_per_page_dropdown = '
1693
+		// current set timezone for timezone js
1694
+		echo '<span id="current_timezone" class="hidden">' . esc_html(EEH_DTT_Helper::get_timezone()) . '</span>';
1695
+	}
1696
+
1697
+
1698
+	/**
1699
+	 * This function sees if there is a method for help popup content existing for the given route.  If there is then
1700
+	 * we'll use the retrieved array to output the content using the template. For child classes: If you want to have
1701
+	 * help popups then in your templates or your content you set "triggers" for the content using the
1702
+	 * "_set_help_trigger('help_trigger_id')" where "help_trigger_id" is what you will use later in your custom method
1703
+	 * for the help popup content on that page. Then in your Child_Admin_Page class you need to define a help popup
1704
+	 * method for the content in the format "_help_popup_content_{route_name}()"  So if you are setting help content
1705
+	 * for the
1706
+	 * 'edit_event' route you should have a method named "_help_popup_content_edit_route". In your defined
1707
+	 * "help_popup_content_..." method.  You must prepare and return an array in the following format array(
1708
+	 *    'help_trigger_id' => array(
1709
+	 *        'title' => esc_html__('localized title for popup', 'event_espresso'),
1710
+	 *        'content' => esc_html__('localized content for popup', 'event_espresso')
1711
+	 *    )
1712
+	 * );
1713
+	 * Then the EE_Admin_Parent will take care of making sure that is setup properly on the correct route.
1714
+	 *
1715
+	 * @param array $help_array
1716
+	 * @param bool  $display
1717
+	 * @return string content
1718
+	 * @throws DomainException
1719
+	 * @throws EE_Error
1720
+	 */
1721
+	protected function _set_help_popup_content($help_array = [], $display = false)
1722
+	{
1723
+		$content    = '';
1724
+		$help_array = empty($help_array) ? $this->_get_help_content() : $help_array;
1725
+		// loop through the array and setup content
1726
+		foreach ($help_array as $trigger => $help) {
1727
+			// make sure the array is setup properly
1728
+			if (! isset($help['title'], $help['content'])) {
1729
+				throw new EE_Error(
1730
+					esc_html__(
1731
+						'Does not look like the popup content array has been setup correctly.  Might want to double check that.  Read the comments for the _get_help_popup_content method found in "EE_Admin_Page" class',
1732
+						'event_espresso'
1733
+					)
1734
+				);
1735
+			}
1736
+			// we're good so let's setup the template vars and then assign parsed template content to our content.
1737
+			$template_args = [
1738
+				'help_popup_id'      => $trigger,
1739
+				'help_popup_title'   => $help['title'],
1740
+				'help_popup_content' => $help['content'],
1741
+			];
1742
+			$content       .= EEH_Template::display_template(
1743
+				EE_ADMIN_TEMPLATE . 'admin_help_popup.template.php',
1744
+				$template_args,
1745
+				true
1746
+			);
1747
+		}
1748
+		if ($display) {
1749
+			echo wp_kses($content, AllowedTags::getWithFormTags());
1750
+			return '';
1751
+		}
1752
+		return $content;
1753
+	}
1754
+
1755
+
1756
+	/**
1757
+	 * All this does is retrieve the help content array if set by the EE_Admin_Page child
1758
+	 *
1759
+	 * @return array properly formatted array for help popup content
1760
+	 * @throws EE_Error
1761
+	 */
1762
+	private function _get_help_content()
1763
+	{
1764
+		// what is the method we're looking for?
1765
+		$method_name = '_help_popup_content_' . $this->_req_action;
1766
+		// if method doesn't exist let's get out.
1767
+		if (! method_exists($this, $method_name)) {
1768
+			return [];
1769
+		}
1770
+		// k we're good to go let's retrieve the help array
1771
+		$help_array = $this->{$method_name}();
1772
+		// make sure we've got an array!
1773
+		if (! is_array($help_array)) {
1774
+			throw new EE_Error(
1775
+				esc_html__(
1776
+					'Something went wrong with help popup content generation. Expecting an array and well, this ain\'t no array bub.',
1777
+					'event_espresso'
1778
+				)
1779
+			);
1780
+		}
1781
+		return $help_array;
1782
+	}
1783
+
1784
+
1785
+	/**
1786
+	 * EE Admin Pages can use this to set a properly formatted trigger for a help popup.
1787
+	 * By default the trigger html is printed.  Otherwise it can be returned if the $display flag is set "false"
1788
+	 * See comments made on the _set_help_content method for understanding other parts to the help popup tool.
1789
+	 *
1790
+	 * @param string  $trigger_id reference for retrieving the trigger content for the popup
1791
+	 * @param boolean $display    if false then we return the trigger string
1792
+	 * @param array   $dimensions an array of dimensions for the box (array(h,w))
1793
+	 * @return string
1794
+	 * @throws DomainException
1795
+	 * @throws EE_Error
1796
+	 */
1797
+	protected function _set_help_trigger($trigger_id, $display = true, $dimensions = ['400', '640'])
1798
+	{
1799
+		if ($this->request->isAjax()) {
1800
+			return '';
1801
+		}
1802
+		// let's check and see if there is any content set for this popup.  If there isn't then we'll include a default title and content so that developers know something needs to be corrected
1803
+		$help_array   = $this->_get_help_content();
1804
+		$help_content = '';
1805
+		if (empty($help_array) || ! isset($help_array[ $trigger_id ])) {
1806
+			$help_array[ $trigger_id ] = [
1807
+				'title'   => esc_html__('Missing Content', 'event_espresso'),
1808
+				'content' => esc_html__(
1809
+					'A trigger has been set that doesn\'t have any corresponding content. Make sure you have set the help content. (see the "_set_help_popup_content" method in the EE_Admin_Page for instructions.)',
1810
+					'event_espresso'
1811
+				),
1812
+			];
1813
+			$help_content              = $this->_set_help_popup_content($help_array);
1814
+		}
1815
+		// let's setup the trigger
1816
+		$content = '<a class="ee-dialog" href="?height='
1817
+				   . esc_attr($dimensions[0])
1818
+				   . '&width='
1819
+				   . esc_attr($dimensions[1])
1820
+				   . '&inlineId='
1821
+				   . esc_attr($trigger_id)
1822
+				   . '" target="_blank"><span class="question ee-help-popup-question"></span></a>';
1823
+		$content .= $help_content;
1824
+		if ($display) {
1825
+			echo wp_kses($content, AllowedTags::getWithFormTags());
1826
+			return '';
1827
+		}
1828
+		return $content;
1829
+	}
1830
+
1831
+
1832
+	/**
1833
+	 * _add_global_screen_options
1834
+	 * Add any extra wp_screen_options within this method using built-in WP functions/methods for doing so.
1835
+	 * This particular method will add_screen_options on ALL EE_Admin Pages
1836
+	 *
1837
+	 * @link   http://chrismarslender.com/wp-tutorials/wordpress-screen-options-tutorial/
1838
+	 *         see also WP_Screen object documents...
1839
+	 * @link   http://codex.wordpress.org/Class_Reference/WP_Screen
1840
+	 * @abstract
1841
+	 * @return void
1842
+	 */
1843
+	private function _add_global_screen_options()
1844
+	{
1845
+	}
1846
+
1847
+
1848
+	/**
1849
+	 * _add_global_feature_pointers
1850
+	 * This method is used for implementing any "feature pointers" (using built-in WP styling js).
1851
+	 * This particular method will implement feature pointers for ALL EE_Admin pages.
1852
+	 * Note: this is just a placeholder for now.  Implementation will come down the road
1853
+	 *
1854
+	 * @see    WP_Internal_Pointers class in wp-admin/includes/template.php for example (its a final class so can't be
1855
+	 *         extended) also see:
1856
+	 * @link   http://eamann.com/tech/wordpress-portland/
1857
+	 * @abstract
1858
+	 * @return void
1859
+	 */
1860
+	private function _add_global_feature_pointers()
1861
+	{
1862
+	}
1863
+
1864
+
1865
+	/**
1866
+	 * load_global_scripts_styles
1867
+	 * The scripts and styles enqueued in here will be loaded on every EE Admin page
1868
+	 *
1869
+	 * @return void
1870
+	 */
1871
+	public function load_global_scripts_styles()
1872
+	{
1873
+		// add debugging styles
1874
+		if (WP_DEBUG) {
1875
+			add_action('admin_head', [$this, 'add_xdebug_style']);
1876
+		}
1877
+		// taking care of metaboxes
1878
+		if (
1879
+			empty($this->_cpt_route)
1880
+			&& (isset($this->_route_config['metaboxes']) || isset($this->_route_config['has_metaboxes']))
1881
+		) {
1882
+			wp_enqueue_script('dashboard');
1883
+		}
1884
+
1885
+		wp_enqueue_script(JqueryAssetManager::JS_HANDLE_JQUERY_UI_TOUCH_PUNCH);
1886
+		wp_enqueue_script(EspressoLegacyAdminAssetManager::JS_HANDLE_EE_ADMIN);
1887
+		// LOCALIZED DATA
1888
+		// localize script for ajax lazy loading
1889
+		wp_localize_script(
1890
+			EspressoLegacyAdminAssetManager::JS_HANDLE_EE_ADMIN,
1891
+			'eeLazyLoadingContainers',
1892
+			apply_filters(
1893
+				'FHEE__EE_Admin_Page_Core__load_global_scripts_styles__loader_containers',
1894
+				['espresso_news_post_box_content']
1895
+			)
1896
+		);
1897
+		StatusChangeNotice::loadAssets();
1898
+
1899
+		add_filter(
1900
+			'admin_body_class',
1901
+			function ($classes) {
1902
+				if (strpos($classes, 'espresso-admin') === false) {
1903
+					$classes .= ' espresso-admin';
1904
+				}
1905
+				return $classes;
1906
+			}
1907
+		);
1908
+	}
1909
+
1910
+
1911
+	/**
1912
+	 *        admin_footer_scripts_eei18n_js_strings
1913
+	 *
1914
+	 * @return        void
1915
+	 */
1916
+	public function admin_footer_scripts_eei18n_js_strings()
1917
+	{
1918
+		EE_Registry::$i18n_js_strings['ajax_url']       = WP_AJAX_URL;
1919
+		EE_Registry::$i18n_js_strings['confirm_delete'] = wp_strip_all_tags(
1920
+			__(
1921
+				'Are you absolutely sure you want to delete this item?\nThis action will delete ALL DATA associated with this item!!!\nThis can NOT be undone!!!',
1922
+				'event_espresso'
1923
+			)
1924
+		);
1925
+		EE_Registry::$i18n_js_strings['January']        = wp_strip_all_tags(__('January', 'event_espresso'));
1926
+		EE_Registry::$i18n_js_strings['February']       = wp_strip_all_tags(__('February', 'event_espresso'));
1927
+		EE_Registry::$i18n_js_strings['March']          = wp_strip_all_tags(__('March', 'event_espresso'));
1928
+		EE_Registry::$i18n_js_strings['April']          = wp_strip_all_tags(__('April', 'event_espresso'));
1929
+		EE_Registry::$i18n_js_strings['May']            = wp_strip_all_tags(__('May', 'event_espresso'));
1930
+		EE_Registry::$i18n_js_strings['June']           = wp_strip_all_tags(__('June', 'event_espresso'));
1931
+		EE_Registry::$i18n_js_strings['July']           = wp_strip_all_tags(__('July', 'event_espresso'));
1932
+		EE_Registry::$i18n_js_strings['August']         = wp_strip_all_tags(__('August', 'event_espresso'));
1933
+		EE_Registry::$i18n_js_strings['September']      = wp_strip_all_tags(__('September', 'event_espresso'));
1934
+		EE_Registry::$i18n_js_strings['October']        = wp_strip_all_tags(__('October', 'event_espresso'));
1935
+		EE_Registry::$i18n_js_strings['November']       = wp_strip_all_tags(__('November', 'event_espresso'));
1936
+		EE_Registry::$i18n_js_strings['December']       = wp_strip_all_tags(__('December', 'event_espresso'));
1937
+		EE_Registry::$i18n_js_strings['Jan']            = wp_strip_all_tags(__('Jan', 'event_espresso'));
1938
+		EE_Registry::$i18n_js_strings['Feb']            = wp_strip_all_tags(__('Feb', 'event_espresso'));
1939
+		EE_Registry::$i18n_js_strings['Mar']            = wp_strip_all_tags(__('Mar', 'event_espresso'));
1940
+		EE_Registry::$i18n_js_strings['Apr']            = wp_strip_all_tags(__('Apr', 'event_espresso'));
1941
+		EE_Registry::$i18n_js_strings['May']            = wp_strip_all_tags(__('May', 'event_espresso'));
1942
+		EE_Registry::$i18n_js_strings['Jun']            = wp_strip_all_tags(__('Jun', 'event_espresso'));
1943
+		EE_Registry::$i18n_js_strings['Jul']            = wp_strip_all_tags(__('Jul', 'event_espresso'));
1944
+		EE_Registry::$i18n_js_strings['Aug']            = wp_strip_all_tags(__('Aug', 'event_espresso'));
1945
+		EE_Registry::$i18n_js_strings['Sep']            = wp_strip_all_tags(__('Sep', 'event_espresso'));
1946
+		EE_Registry::$i18n_js_strings['Oct']            = wp_strip_all_tags(__('Oct', 'event_espresso'));
1947
+		EE_Registry::$i18n_js_strings['Nov']            = wp_strip_all_tags(__('Nov', 'event_espresso'));
1948
+		EE_Registry::$i18n_js_strings['Dec']            = wp_strip_all_tags(__('Dec', 'event_espresso'));
1949
+		EE_Registry::$i18n_js_strings['Sunday']         = wp_strip_all_tags(__('Sunday', 'event_espresso'));
1950
+		EE_Registry::$i18n_js_strings['Monday']         = wp_strip_all_tags(__('Monday', 'event_espresso'));
1951
+		EE_Registry::$i18n_js_strings['Tuesday']        = wp_strip_all_tags(__('Tuesday', 'event_espresso'));
1952
+		EE_Registry::$i18n_js_strings['Wednesday']      = wp_strip_all_tags(__('Wednesday', 'event_espresso'));
1953
+		EE_Registry::$i18n_js_strings['Thursday']       = wp_strip_all_tags(__('Thursday', 'event_espresso'));
1954
+		EE_Registry::$i18n_js_strings['Friday']         = wp_strip_all_tags(__('Friday', 'event_espresso'));
1955
+		EE_Registry::$i18n_js_strings['Saturday']       = wp_strip_all_tags(__('Saturday', 'event_espresso'));
1956
+		EE_Registry::$i18n_js_strings['Sun']            = wp_strip_all_tags(__('Sun', 'event_espresso'));
1957
+		EE_Registry::$i18n_js_strings['Mon']            = wp_strip_all_tags(__('Mon', 'event_espresso'));
1958
+		EE_Registry::$i18n_js_strings['Tue']            = wp_strip_all_tags(__('Tue', 'event_espresso'));
1959
+		EE_Registry::$i18n_js_strings['Wed']            = wp_strip_all_tags(__('Wed', 'event_espresso'));
1960
+		EE_Registry::$i18n_js_strings['Thu']            = wp_strip_all_tags(__('Thu', 'event_espresso'));
1961
+		EE_Registry::$i18n_js_strings['Fri']            = wp_strip_all_tags(__('Fri', 'event_espresso'));
1962
+		EE_Registry::$i18n_js_strings['Sat']            = wp_strip_all_tags(__('Sat', 'event_espresso'));
1963
+	}
1964
+
1965
+
1966
+	/**
1967
+	 *        load enhanced xdebug styles for ppl with failing eyesight
1968
+	 *
1969
+	 * @return        void
1970
+	 */
1971
+	public function add_xdebug_style()
1972
+	{
1973
+		echo '<style>.xdebug-error { font-size:1.5em; }</style>';
1974
+	}
1975
+
1976
+
1977
+	/************************/
1978
+	/** LIST TABLE METHODS **/
1979
+	/************************/
1980
+	/**
1981
+	 * this sets up the list table if the current view requires it.
1982
+	 *
1983
+	 * @return void
1984
+	 * @throws EE_Error
1985
+	 * @throws InvalidArgumentException
1986
+	 * @throws InvalidDataTypeException
1987
+	 * @throws InvalidInterfaceException
1988
+	 */
1989
+	protected function _set_list_table()
1990
+	{
1991
+		// first is this a list_table view?
1992
+		if (! isset($this->_route_config['list_table'])) {
1993
+			return;
1994
+		} //not a list_table view so get out.
1995
+		// list table functions are per view specific (because some admin pages might have more than one list table!)
1996
+		$list_table_view = '_set_list_table_views_' . $this->_req_action;
1997
+		if (! method_exists($this, $list_table_view) || $this->{$list_table_view}() === false) {
1998
+			// user error msg
1999
+			$error_msg = esc_html__(
2000
+				'An error occurred. The requested list table views could not be found.',
2001
+				'event_espresso'
2002
+			);
2003
+			// developer error msg
2004
+			$error_msg .= '||'
2005
+						  . sprintf(
2006
+							  esc_html__(
2007
+								  'List table views for "%s" route could not be setup. Check that you have the corresponding method, "%s" set up for defining list_table_views for this route.',
2008
+								  'event_espresso'
2009
+							  ),
2010
+							  $this->_req_action,
2011
+							  $list_table_view
2012
+						  );
2013
+			throw new EE_Error($error_msg);
2014
+		}
2015
+		// let's provide the ability to filter the views per PAGE AND ROUTE, per PAGE, and globally
2016
+		$this->_views = apply_filters(
2017
+			'FHEE_list_table_views_' . $this->page_slug . '_' . $this->_req_action,
2018
+			$this->_views
2019
+		);
2020
+		$this->_views = apply_filters('FHEE_list_table_views_' . $this->page_slug, $this->_views);
2021
+		$this->_views = apply_filters('FHEE_list_table_views', $this->_views);
2022
+		$this->_set_list_table_view();
2023
+		$this->_set_list_table_object();
2024
+	}
2025
+
2026
+
2027
+	/**
2028
+	 * set current view for List Table
2029
+	 *
2030
+	 * @return void
2031
+	 */
2032
+	protected function _set_list_table_view()
2033
+	{
2034
+		$this->_view = isset($this->_views['in_use']) ? 'in_use' : 'all';
2035
+		$status      = $this->request->getRequestParam('status', null, 'key');
2036
+		$this->_view = $status && array_key_exists($status, $this->_views)
2037
+			? $status
2038
+			: $this->_view;
2039
+	}
2040
+
2041
+
2042
+	/**
2043
+	 * _set_list_table_object
2044
+	 * WP_List_Table objects need to be loaded fairly early so automatic stuff WP does is taken care of.
2045
+	 *
2046
+	 * @throws InvalidInterfaceException
2047
+	 * @throws InvalidArgumentException
2048
+	 * @throws InvalidDataTypeException
2049
+	 * @throws EE_Error
2050
+	 * @throws InvalidInterfaceException
2051
+	 */
2052
+	protected function _set_list_table_object()
2053
+	{
2054
+		if (isset($this->_route_config['list_table'])) {
2055
+			if (! class_exists($this->_route_config['list_table'])) {
2056
+				throw new EE_Error(
2057
+					sprintf(
2058
+						esc_html__(
2059
+							'The %s class defined for the list table does not exist.  Please check the spelling of the class ref in the $_page_config property on %s.',
2060
+							'event_espresso'
2061
+						),
2062
+						$this->_route_config['list_table'],
2063
+						$this->class_name
2064
+					)
2065
+				);
2066
+			}
2067
+			$this->_list_table_object = $this->loader->getShared(
2068
+				$this->_route_config['list_table'],
2069
+				[
2070
+					$this,
2071
+					LoaderFactory::getShared(AdminListTableFilters::class)
2072
+				]
2073
+			);
2074
+		}
2075
+	}
2076
+
2077
+
2078
+	/**
2079
+	 * get_list_table_view_RLs - get it? View RL ?? VU-RL???  URL ??
2080
+	 *
2081
+	 * @param array $extra_query_args                     Optional. An array of extra query args to add to the generated
2082
+	 *                                                    urls.  The array should be indexed by the view it is being
2083
+	 *                                                    added to.
2084
+	 * @return array
2085
+	 */
2086
+	public function get_list_table_view_RLs($extra_query_args = [])
2087
+	{
2088
+		do_action('AHEE_log', __FILE__, __FUNCTION__, '');
2089
+		if (empty($this->_views)) {
2090
+			$this->_views = [];
2091
+		}
2092
+		// cycle thru views
2093
+		foreach ($this->_views as $key => $view) {
2094
+			$query_args = [];
2095
+			// check for current view
2096
+			$this->_views[ $key ]['class']               = $this->_view === $view['slug'] ? 'current' : '';
2097
+			$query_args['action']                        = $this->_req_action;
2098
+			$query_args[ $this->_req_action . '_nonce' ] = wp_create_nonce($query_args['action'] . '_nonce');
2099
+			$query_args['status']                        = $view['slug'];
2100
+			// merge any other arguments sent in.
2101
+			if (isset($extra_query_args[ $view['slug'] ])) {
2102
+				foreach ($extra_query_args[ $view['slug'] ] as $extra_query_arg) {
2103
+					$query_args[] = $extra_query_arg;
2104
+				}
2105
+			}
2106
+			$this->_views[ $key ]['url'] = EE_Admin_Page::add_query_args_and_nonce($query_args, $this->_admin_base_url);
2107
+		}
2108
+		return $this->_views;
2109
+	}
2110
+
2111
+
2112
+	/**
2113
+	 * _entries_per_page_dropdown
2114
+	 * generates a dropdown box for selecting the number of visible rows in an admin page list table
2115
+	 *
2116
+	 * @param int $max_entries total number of rows in the table
2117
+	 * @return string
2118
+	 * @todo   : Note: ideally this should be added to the screen options dropdown as that would be consistent with how
2119
+	 *                         WP does it.
2120
+	 */
2121
+	protected function _entries_per_page_dropdown($max_entries = 0)
2122
+	{
2123
+		do_action('AHEE_log', __FILE__, __FUNCTION__, '');
2124
+		$values   = [10, 25, 50, 100];
2125
+		$per_page = $this->request->getRequestParam('per_page', 10, 'int');
2126
+		if ($max_entries) {
2127
+			$values[] = $max_entries;
2128
+			sort($values);
2129
+		}
2130
+		$entries_per_page_dropdown = '
2131 2131
 			<div id="entries-per-page-dv" class="alignleft actions">
2132 2132
 				<label class="hide-if-no-js">
2133 2133
 					Show
2134 2134
 					<select id="entries-per-page-slct" name="entries-per-page-slct">';
2135
-        foreach ($values as $value) {
2136
-            if ($value < $max_entries) {
2137
-                $selected                  = $value === $per_page ? ' selected="' . $per_page . '"' : '';
2138
-                $entries_per_page_dropdown .= '
2135
+		foreach ($values as $value) {
2136
+			if ($value < $max_entries) {
2137
+				$selected                  = $value === $per_page ? ' selected="' . $per_page . '"' : '';
2138
+				$entries_per_page_dropdown .= '
2139 2139
 						<option value="' . $value . '"' . $selected . '>' . $value . '&nbsp;&nbsp;</option>';
2140
-            }
2141
-        }
2142
-        $selected                  = $max_entries === $per_page ? ' selected="' . $per_page . '"' : '';
2143
-        $entries_per_page_dropdown .= '
2140
+			}
2141
+		}
2142
+		$selected                  = $max_entries === $per_page ? ' selected="' . $per_page . '"' : '';
2143
+		$entries_per_page_dropdown .= '
2144 2144
 						<option value="' . $max_entries . '"' . $selected . '>All&nbsp;&nbsp;</option>';
2145
-        $entries_per_page_dropdown .= '
2145
+		$entries_per_page_dropdown .= '
2146 2146
 					</select>
2147 2147
 					entries
2148 2148
 				</label>
2149 2149
 				<input id="entries-per-page-btn" class="button button--secondary" type="submit" value="Go" >
2150 2150
 			</div>
2151 2151
 		';
2152
-        return $entries_per_page_dropdown;
2153
-    }
2154
-
2155
-
2156
-    /**
2157
-     *        _set_search_attributes
2158
-     *
2159
-     * @return        void
2160
-     */
2161
-    public function _set_search_attributes()
2162
-    {
2163
-        $this->_template_args['search']['btn_label'] = sprintf(
2164
-            esc_html__('Search %s', 'event_espresso'),
2165
-            empty($this->_search_btn_label) ? $this->page_label
2166
-                : $this->_search_btn_label
2167
-        );
2168
-        $this->_template_args['search']['callback']  = 'search_' . $this->page_slug;
2169
-    }
2170
-
2171
-
2172
-
2173
-    /*** END LIST TABLE METHODS **/
2174
-
2175
-    /**
2176
-     * @return void
2177
-     * @throws EE_Error
2178
-     */
2179
-    public function addRegisteredMetaBoxes()
2180
-    {
2181
-        remove_action('add_meta_boxes', [$this, 'addRegisteredMetaBoxes'], 99);
2182
-        $this->_add_registered_meta_boxes();
2183
-    }
2184
-
2185
-
2186
-    /**
2187
-     * _add_registered_metaboxes
2188
-     *  this loads any registered metaboxes via the 'metaboxes' index in the _page_config property array.
2189
-     *
2190
-     * @link   http://codex.wordpress.org/Function_Reference/add_meta_box
2191
-     * @return void
2192
-     * @throws EE_Error
2193
-     */
2194
-    private function _add_registered_meta_boxes()
2195
-    {
2196
-        // we only add meta boxes if the page_route calls for it
2197
-        if (
2198
-            is_array($this->_route_config)
2199
-            && isset($this->_route_config['metaboxes'])
2200
-            && is_array($this->_route_config['metaboxes'])
2201
-        ) {
2202
-            // this simply loops through the callbacks provided
2203
-            // and checks if there is a corresponding callback registered by the child
2204
-            // if there is then we go ahead and process the metabox loader.
2205
-            foreach ($this->_route_config['metaboxes'] as $key => $metabox_callback) {
2206
-                // first check for Closures
2207
-                if ($metabox_callback instanceof Closure) {
2208
-                    $result = $metabox_callback();
2209
-                } elseif (is_callable($metabox_callback)) {
2210
-                    $result = call_user_func($metabox_callback);
2211
-                } elseif (method_exists($this, $metabox_callback)) {
2212
-                    $result = $this->{$metabox_callback}();
2213
-                } else {
2214
-                    $result = false;
2215
-                }
2216
-                if ($result === false) {
2217
-                    // user error msg
2218
-                    $error_msg = esc_html__(
2219
-                        'An error occurred. The  requested metabox could not be found.',
2220
-                        'event_espresso'
2221
-                    );
2222
-                    // developer error msg
2223
-                    $error_msg .= '||'
2224
-                                  . sprintf(
2225
-                                      esc_html__(
2226
-                                          'The metabox with the string "%s" could not be called. Check that the spelling for method names and actions in the "_page_config[\'metaboxes\']" array are all correct.',
2227
-                                          'event_espresso'
2228
-                                      ),
2229
-                                      $metabox_callback
2230
-                                  );
2231
-                    throw new EE_Error($error_msg);
2232
-                }
2233
-                unset($this->_route_config['metaboxes'][ $key ]);
2234
-            }
2235
-        }
2236
-    }
2237
-
2238
-
2239
-    /**
2240
-     * _add_screen_columns
2241
-     * This will check the _page_config array and if there is "columns" key index indicated, we'll set the template as
2242
-     * the dynamic column template and we'll setup the column options for the page.
2243
-     *
2244
-     * @return void
2245
-     */
2246
-    private function _add_screen_columns()
2247
-    {
2248
-        if (
2249
-            is_array($this->_route_config)
2250
-            && isset($this->_route_config['columns'])
2251
-            && is_array($this->_route_config['columns'])
2252
-            && count($this->_route_config['columns']) === 2
2253
-        ) {
2254
-            add_screen_option(
2255
-                'layout_columns',
2256
-                [
2257
-                    'max'     => (int) $this->_route_config['columns'][0],
2258
-                    'default' => (int) $this->_route_config['columns'][1],
2259
-                ]
2260
-            );
2261
-            $this->_template_args['num_columns']                 = $this->_route_config['columns'][0];
2262
-            $screen_id                                           = $this->_current_screen->id;
2263
-            $screen_columns                                      = (int) get_user_option("screen_layout_{$screen_id}");
2264
-            $total_columns                                       = ! empty($screen_columns)
2265
-                ? $screen_columns
2266
-                : $this->_route_config['columns'][1];
2267
-            $this->_template_args['current_screen_widget_class'] = 'columns-' . $total_columns;
2268
-            $this->_template_args['current_page']                = $this->_wp_page_slug;
2269
-            $this->_template_args['screen']                      = $this->_current_screen;
2270
-            $this->_column_template_path                         = EE_ADMIN_TEMPLATE
2271
-                                                                   . 'admin_details_metabox_column_wrapper.template.php';
2272
-            // finally if we don't have has_metaboxes set in the route config
2273
-            // let's make sure it IS set other wise the necessary hidden fields for this won't be loaded.
2274
-            $this->_route_config['has_metaboxes'] = true;
2275
-        }
2276
-    }
2277
-
2278
-
2279
-
2280
-    /** GLOBALLY AVAILABLE METABOXES **/
2281
-
2282
-
2283
-    /**
2284
-     * In this section we put any globally available EE metaboxes for all EE Admin pages.  They are called by simply
2285
-     * referencing the callback in the _page_config array property.  This way you can be very specific about what pages
2286
-     * these get loaded on.
2287
-     */
2288
-    private function _espresso_news_post_box()
2289
-    {
2290
-        $news_box_title = apply_filters(
2291
-            'FHEE__EE_Admin_Page___espresso_news_post_box__news_box_title',
2292
-            esc_html__('New @ Event Espresso', 'event_espresso')
2293
-        );
2294
-        $this->addMetaBox(
2295
-            'espresso_news_post_box',
2296
-            $news_box_title,
2297
-            [
2298
-                $this,
2299
-                'espresso_news_post_box',
2300
-            ],
2301
-            $this->_wp_page_slug,
2302
-            'side',
2303
-            'low'
2304
-        );
2305
-    }
2306
-
2307
-
2308
-    /**
2309
-     * Code for setting up espresso ratings request metabox.
2310
-     */
2311
-    protected function _espresso_ratings_request()
2312
-    {
2313
-        if (! apply_filters('FHEE_show_ratings_request_meta_box', true)) {
2314
-            return;
2315
-        }
2316
-        $ratings_box_title = apply_filters(
2317
-            'FHEE__EE_Admin_Page___espresso_news_post_box__news_box_title',
2318
-            esc_html__('Keep Event Espresso Decaf Free', 'event_espresso')
2319
-        );
2320
-        $this->addMetaBox(
2321
-            'espresso_ratings_request',
2322
-            $ratings_box_title,
2323
-            [
2324
-                $this,
2325
-                'espresso_ratings_request',
2326
-            ],
2327
-            $this->_wp_page_slug,
2328
-            'side'
2329
-        );
2330
-    }
2331
-
2332
-
2333
-    /**
2334
-     * Code for setting up espresso ratings request metabox content.
2335
-     *
2336
-     * @throws DomainException
2337
-     */
2338
-    public function espresso_ratings_request()
2339
-    {
2340
-        EEH_Template::display_template(EE_ADMIN_TEMPLATE . 'espresso_ratings_request_content.template.php');
2341
-    }
2342
-
2343
-
2344
-    public static function cached_rss_display($rss_id, $url)
2345
-    {
2346
-        $loading   = '<p class="widget-loading hide-if-no-js">'
2347
-                     . esc_html__('Loading&#8230;', 'event_espresso')
2348
-                     . '</p><p class="hide-if-js">'
2349
-                     . esc_html__('This widget requires JavaScript.', 'event_espresso')
2350
-                     . '</p>';
2351
-        $pre       = '<div class="espresso-rss-display">' . "\n\t";
2352
-        $pre       .= '<span id="' . esc_attr($rss_id) . '_url" class="hidden">' . esc_url_raw($url) . '</span>';
2353
-        $post      = '</div>' . "\n";
2354
-        $cache_key = 'ee_rss_' . md5($rss_id);
2355
-        $output    = get_transient($cache_key);
2356
-        if ($output !== false) {
2357
-            echo wp_kses($pre . $output . $post, AllowedTags::getWithFormTags());
2358
-            return true;
2359
-        }
2360
-        if (! (defined('DOING_AJAX') && DOING_AJAX)) {
2361
-            echo wp_kses($pre . $loading . $post, AllowedTags::getWithFormTags());
2362
-            return false;
2363
-        }
2364
-        ob_start();
2365
-        wp_widget_rss_output($url, ['show_date' => 0, 'items' => 5]);
2366
-        set_transient($cache_key, ob_get_flush(), 12 * HOUR_IN_SECONDS);
2367
-        return true;
2368
-    }
2369
-
2370
-
2371
-    public function espresso_news_post_box()
2372
-    {
2373
-        ?>
2152
+		return $entries_per_page_dropdown;
2153
+	}
2154
+
2155
+
2156
+	/**
2157
+	 *        _set_search_attributes
2158
+	 *
2159
+	 * @return        void
2160
+	 */
2161
+	public function _set_search_attributes()
2162
+	{
2163
+		$this->_template_args['search']['btn_label'] = sprintf(
2164
+			esc_html__('Search %s', 'event_espresso'),
2165
+			empty($this->_search_btn_label) ? $this->page_label
2166
+				: $this->_search_btn_label
2167
+		);
2168
+		$this->_template_args['search']['callback']  = 'search_' . $this->page_slug;
2169
+	}
2170
+
2171
+
2172
+
2173
+	/*** END LIST TABLE METHODS **/
2174
+
2175
+	/**
2176
+	 * @return void
2177
+	 * @throws EE_Error
2178
+	 */
2179
+	public function addRegisteredMetaBoxes()
2180
+	{
2181
+		remove_action('add_meta_boxes', [$this, 'addRegisteredMetaBoxes'], 99);
2182
+		$this->_add_registered_meta_boxes();
2183
+	}
2184
+
2185
+
2186
+	/**
2187
+	 * _add_registered_metaboxes
2188
+	 *  this loads any registered metaboxes via the 'metaboxes' index in the _page_config property array.
2189
+	 *
2190
+	 * @link   http://codex.wordpress.org/Function_Reference/add_meta_box
2191
+	 * @return void
2192
+	 * @throws EE_Error
2193
+	 */
2194
+	private function _add_registered_meta_boxes()
2195
+	{
2196
+		// we only add meta boxes if the page_route calls for it
2197
+		if (
2198
+			is_array($this->_route_config)
2199
+			&& isset($this->_route_config['metaboxes'])
2200
+			&& is_array($this->_route_config['metaboxes'])
2201
+		) {
2202
+			// this simply loops through the callbacks provided
2203
+			// and checks if there is a corresponding callback registered by the child
2204
+			// if there is then we go ahead and process the metabox loader.
2205
+			foreach ($this->_route_config['metaboxes'] as $key => $metabox_callback) {
2206
+				// first check for Closures
2207
+				if ($metabox_callback instanceof Closure) {
2208
+					$result = $metabox_callback();
2209
+				} elseif (is_callable($metabox_callback)) {
2210
+					$result = call_user_func($metabox_callback);
2211
+				} elseif (method_exists($this, $metabox_callback)) {
2212
+					$result = $this->{$metabox_callback}();
2213
+				} else {
2214
+					$result = false;
2215
+				}
2216
+				if ($result === false) {
2217
+					// user error msg
2218
+					$error_msg = esc_html__(
2219
+						'An error occurred. The  requested metabox could not be found.',
2220
+						'event_espresso'
2221
+					);
2222
+					// developer error msg
2223
+					$error_msg .= '||'
2224
+								  . sprintf(
2225
+									  esc_html__(
2226
+										  'The metabox with the string "%s" could not be called. Check that the spelling for method names and actions in the "_page_config[\'metaboxes\']" array are all correct.',
2227
+										  'event_espresso'
2228
+									  ),
2229
+									  $metabox_callback
2230
+								  );
2231
+					throw new EE_Error($error_msg);
2232
+				}
2233
+				unset($this->_route_config['metaboxes'][ $key ]);
2234
+			}
2235
+		}
2236
+	}
2237
+
2238
+
2239
+	/**
2240
+	 * _add_screen_columns
2241
+	 * This will check the _page_config array and if there is "columns" key index indicated, we'll set the template as
2242
+	 * the dynamic column template and we'll setup the column options for the page.
2243
+	 *
2244
+	 * @return void
2245
+	 */
2246
+	private function _add_screen_columns()
2247
+	{
2248
+		if (
2249
+			is_array($this->_route_config)
2250
+			&& isset($this->_route_config['columns'])
2251
+			&& is_array($this->_route_config['columns'])
2252
+			&& count($this->_route_config['columns']) === 2
2253
+		) {
2254
+			add_screen_option(
2255
+				'layout_columns',
2256
+				[
2257
+					'max'     => (int) $this->_route_config['columns'][0],
2258
+					'default' => (int) $this->_route_config['columns'][1],
2259
+				]
2260
+			);
2261
+			$this->_template_args['num_columns']                 = $this->_route_config['columns'][0];
2262
+			$screen_id                                           = $this->_current_screen->id;
2263
+			$screen_columns                                      = (int) get_user_option("screen_layout_{$screen_id}");
2264
+			$total_columns                                       = ! empty($screen_columns)
2265
+				? $screen_columns
2266
+				: $this->_route_config['columns'][1];
2267
+			$this->_template_args['current_screen_widget_class'] = 'columns-' . $total_columns;
2268
+			$this->_template_args['current_page']                = $this->_wp_page_slug;
2269
+			$this->_template_args['screen']                      = $this->_current_screen;
2270
+			$this->_column_template_path                         = EE_ADMIN_TEMPLATE
2271
+																   . 'admin_details_metabox_column_wrapper.template.php';
2272
+			// finally if we don't have has_metaboxes set in the route config
2273
+			// let's make sure it IS set other wise the necessary hidden fields for this won't be loaded.
2274
+			$this->_route_config['has_metaboxes'] = true;
2275
+		}
2276
+	}
2277
+
2278
+
2279
+
2280
+	/** GLOBALLY AVAILABLE METABOXES **/
2281
+
2282
+
2283
+	/**
2284
+	 * In this section we put any globally available EE metaboxes for all EE Admin pages.  They are called by simply
2285
+	 * referencing the callback in the _page_config array property.  This way you can be very specific about what pages
2286
+	 * these get loaded on.
2287
+	 */
2288
+	private function _espresso_news_post_box()
2289
+	{
2290
+		$news_box_title = apply_filters(
2291
+			'FHEE__EE_Admin_Page___espresso_news_post_box__news_box_title',
2292
+			esc_html__('New @ Event Espresso', 'event_espresso')
2293
+		);
2294
+		$this->addMetaBox(
2295
+			'espresso_news_post_box',
2296
+			$news_box_title,
2297
+			[
2298
+				$this,
2299
+				'espresso_news_post_box',
2300
+			],
2301
+			$this->_wp_page_slug,
2302
+			'side',
2303
+			'low'
2304
+		);
2305
+	}
2306
+
2307
+
2308
+	/**
2309
+	 * Code for setting up espresso ratings request metabox.
2310
+	 */
2311
+	protected function _espresso_ratings_request()
2312
+	{
2313
+		if (! apply_filters('FHEE_show_ratings_request_meta_box', true)) {
2314
+			return;
2315
+		}
2316
+		$ratings_box_title = apply_filters(
2317
+			'FHEE__EE_Admin_Page___espresso_news_post_box__news_box_title',
2318
+			esc_html__('Keep Event Espresso Decaf Free', 'event_espresso')
2319
+		);
2320
+		$this->addMetaBox(
2321
+			'espresso_ratings_request',
2322
+			$ratings_box_title,
2323
+			[
2324
+				$this,
2325
+				'espresso_ratings_request',
2326
+			],
2327
+			$this->_wp_page_slug,
2328
+			'side'
2329
+		);
2330
+	}
2331
+
2332
+
2333
+	/**
2334
+	 * Code for setting up espresso ratings request metabox content.
2335
+	 *
2336
+	 * @throws DomainException
2337
+	 */
2338
+	public function espresso_ratings_request()
2339
+	{
2340
+		EEH_Template::display_template(EE_ADMIN_TEMPLATE . 'espresso_ratings_request_content.template.php');
2341
+	}
2342
+
2343
+
2344
+	public static function cached_rss_display($rss_id, $url)
2345
+	{
2346
+		$loading   = '<p class="widget-loading hide-if-no-js">'
2347
+					 . esc_html__('Loading&#8230;', 'event_espresso')
2348
+					 . '</p><p class="hide-if-js">'
2349
+					 . esc_html__('This widget requires JavaScript.', 'event_espresso')
2350
+					 . '</p>';
2351
+		$pre       = '<div class="espresso-rss-display">' . "\n\t";
2352
+		$pre       .= '<span id="' . esc_attr($rss_id) . '_url" class="hidden">' . esc_url_raw($url) . '</span>';
2353
+		$post      = '</div>' . "\n";
2354
+		$cache_key = 'ee_rss_' . md5($rss_id);
2355
+		$output    = get_transient($cache_key);
2356
+		if ($output !== false) {
2357
+			echo wp_kses($pre . $output . $post, AllowedTags::getWithFormTags());
2358
+			return true;
2359
+		}
2360
+		if (! (defined('DOING_AJAX') && DOING_AJAX)) {
2361
+			echo wp_kses($pre . $loading . $post, AllowedTags::getWithFormTags());
2362
+			return false;
2363
+		}
2364
+		ob_start();
2365
+		wp_widget_rss_output($url, ['show_date' => 0, 'items' => 5]);
2366
+		set_transient($cache_key, ob_get_flush(), 12 * HOUR_IN_SECONDS);
2367
+		return true;
2368
+	}
2369
+
2370
+
2371
+	public function espresso_news_post_box()
2372
+	{
2373
+		?>
2374 2374
         <div class="padding">
2375 2375
             <div id="espresso_news_post_box_content" class="infolinks">
2376 2376
                 <?php
2377
-                // Get RSS Feed(s)
2378
-                EE_Admin_Page::cached_rss_display(
2379
-                    'espresso_news_post_box_content',
2380
-                    esc_url_raw(
2381
-                        apply_filters(
2382
-                            'FHEE__EE_Admin_Page__espresso_news_post_box__feed_url',
2383
-                            'https://eventespresso.com/feed/'
2384
-                        )
2385
-                    )
2386
-                );
2387
-                ?>
2377
+				// Get RSS Feed(s)
2378
+				EE_Admin_Page::cached_rss_display(
2379
+					'espresso_news_post_box_content',
2380
+					esc_url_raw(
2381
+						apply_filters(
2382
+							'FHEE__EE_Admin_Page__espresso_news_post_box__feed_url',
2383
+							'https://eventespresso.com/feed/'
2384
+						)
2385
+					)
2386
+				);
2387
+				?>
2388 2388
             </div>
2389 2389
             <?php do_action('AHEE__EE_Admin_Page__espresso_news_post_box__after_content'); ?>
2390 2390
         </div>
2391 2391
         <?php
2392
-    }
2393
-
2394
-
2395
-    private function _espresso_links_post_box()
2396
-    {
2397
-        // Hiding until we actually have content to put in here...
2398
-        // $this->addMetaBox('espresso_links_post_box', esc_html__('Helpful Plugin Links', 'event_espresso'), array( $this, 'espresso_links_post_box'), $this->_wp_page_slug, 'side');
2399
-    }
2400
-
2401
-
2402
-    public function espresso_links_post_box()
2403
-    {
2404
-        // Hiding until we actually have content to put in here...
2405
-        // EEH_Template::display_template(
2406
-        //     EE_ADMIN_TEMPLATE . 'admin_general_metabox_contents_espresso_links.template.php'
2407
-        // );
2408
-    }
2409
-
2410
-
2411
-    protected function _espresso_sponsors_post_box()
2412
-    {
2413
-        if (apply_filters('FHEE_show_sponsors_meta_box', true)) {
2414
-            $this->addMetaBox(
2415
-                'espresso_sponsors_post_box',
2416
-                esc_html__('Event Espresso Highlights', 'event_espresso'),
2417
-                [$this, 'espresso_sponsors_post_box'],
2418
-                $this->_wp_page_slug,
2419
-                'side'
2420
-            );
2421
-        }
2422
-    }
2423
-
2424
-
2425
-    public function espresso_sponsors_post_box()
2426
-    {
2427
-        EEH_Template::display_template(
2428
-            EE_ADMIN_TEMPLATE . 'admin_general_metabox_contents_espresso_sponsors.template.php'
2429
-        );
2430
-    }
2431
-
2432
-
2433
-    /**
2434
-     * if there is [ 'label' => [ 'publishbox' => 'some title' ]]
2435
-     * present in the _page_config array, then we'll use that for the metabox label.
2436
-     * Otherwise we'll just use publish
2437
-     * (publishbox itself could be an array of labels indexed by routes)
2438
-     *
2439
-     * @return string
2440
-     * @since   5.0.0.p
2441
-     */
2442
-    protected function getPublishBoxTitle(): string
2443
-    {
2444
-        $publish_box_title = esc_html__('Publish', 'event_espresso');
2445
-        if (! empty($this->_labels['publishbox'])) {
2446
-            if (is_array($this->_labels['publishbox'])) {
2447
-                $publish_box_title = $this->_labels['publishbox'][ $this->_req_action ] ?? $publish_box_title;
2448
-            } else {
2449
-                $publish_box_title = $this->_labels['publishbox'];
2450
-            }
2451
-        }
2452
-        return apply_filters(
2453
-            'FHEE__EE_Admin_Page___publish_post_box__box_label',
2454
-            $publish_box_title,
2455
-            $this->_req_action,
2456
-            $this
2457
-        );
2458
-    }
2459
-
2460
-
2461
-    /**
2462
-     * @throws EE_Error
2463
-     */
2464
-    private function _publish_post_box()
2465
-    {
2466
-        $title = $this->getPublishBoxTitle();
2467
-        if (empty($this->_template_args['save_buttons'])) {
2468
-            $this->_set_publish_post_box_vars(
2469
-                sanitize_key($title),
2470
-                "espresso_{$this->page_slug}_editor_overview",
2471
-                '',
2472
-                '',
2473
-                true
2474
-            );
2475
-        } else {
2476
-            $this->addPublishPostMetaBoxHiddenFields(
2477
-                sanitize_key($title),
2478
-                ['type' => 'hidden', 'value' => "espresso_{$this->page_slug}_editor_overview"]
2479
-            );
2480
-        }
2481
-        $this->addMetaBox(
2482
-            "espresso_{$this->page_slug}_editor_overview",
2483
-            $title,
2484
-            [$this, 'editor_overview'],
2485
-            $this->_current_screen->id,
2486
-            'side',
2487
-            'high'
2488
-        );
2489
-    }
2490
-
2491
-
2492
-    public function editor_overview()
2493
-    {
2494
-        /**
2495
-         * @var string $publish_box_extra_content
2496
-         * @var string $publish_hidden_fields
2497
-         * @var string $publish_delete_link
2498
-         * @var string $save_buttons
2499
-         */
2500
-        // if we have extra content set let's add it in if not make sure its empty
2501
-        $this->_template_args['publish_box_extra_content'] = $this->_template_args['publish_box_extra_content'] ?? '';
2502
-        echo EEH_Template::display_template(
2503
-            EE_ADMIN_TEMPLATE . 'admin_details_publish_metabox.template.php',
2504
-            $this->_template_args,
2505
-            true
2506
-        );
2507
-    }
2508
-
2509
-
2510
-    /** end of globally available metaboxes section **/
2511
-
2512
-
2513
-    /**
2514
-     * Sets the _template_args arguments used by the _publish_post_box shortcut
2515
-     * Note: currently there is no validation for this.  However, if you want the delete button, the
2516
-     * save, and save and close buttons to work properly, then you will want to include a
2517
-     * values for the name and id arguments.
2518
-     *
2519
-     * @param string|null $name                     key used for the action ID (i.e. event_id)
2520
-     * @param int|string  $id                       id attached to the item published
2521
-     * @param string|null $delete                   page route callback for the delete action
2522
-     * @param string|null $save_close_redirect_URL  custom URL to redirect to after Save & Close has been completed
2523
-     * @param boolean     $both_btns                whether to display BOTH the "Save & Close" and "Save" buttons
2524
-     *                                              or just the "Save" button
2525
-     * @throws EE_Error
2526
-     * @throws InvalidArgumentException
2527
-     * @throws InvalidDataTypeException
2528
-     * @throws InvalidInterfaceException
2529
-     * @todo  Add in validation for name/id arguments.
2530
-     */
2531
-    protected function _set_publish_post_box_vars(
2532
-        ?string $name = '',
2533
-        $id = 0,
2534
-        ?string $delete = '',
2535
-        ?string $save_close_redirect_URL = '',
2536
-        bool $both_btns = true
2537
-    ) {
2538
-        // if Save & Close, use a custom redirect URL or default to the main page?
2539
-        $save_close_redirect_URL = ! empty($save_close_redirect_URL)
2540
-            ? $save_close_redirect_URL
2541
-            : $this->_admin_base_url;
2542
-        // create the Save & Close and Save buttons
2543
-        $this->_set_save_buttons($both_btns, [], [], $save_close_redirect_URL);
2544
-        // if we have extra content set let's add it in if not make sure its empty
2545
-        $this->_template_args['publish_box_extra_content'] = $this->_template_args['publish_box_extra_content'] ?? '';
2546
-        if ($delete && ! empty($id) && empty($this->_template_args['publish_delete_link'])) {
2547
-            // make sure we have a default if just true is sent.
2548
-            $delete      = ! empty($delete) ? $delete : 'delete';
2549
-            $this->_template_args['publish_delete_link'] = $this->get_action_link_or_button(
2550
-                $delete,
2551
-                $delete,
2552
-                [$name => $id],
2553
-                'submitdelete deletion button button--outline button--caution'
2554
-            );
2555
-        }
2556
-        if (! isset($this->_template_args['publish_delete_link'])) {
2557
-            $this->_template_args['publish_delete_link'] = '';
2558
-        }
2559
-        if (! empty($name) && ! empty($id)) {
2560
-            $this->addPublishPostMetaBoxHiddenFields($name, ['type' => 'hidden', 'value' => $id]);
2561
-        }
2562
-        $hidden_fields = $this->_generate_admin_form_fields($this->publish_post_meta_box_hidden_fields, 'array');
2563
-        // add hidden fields
2564
-        $this->_template_args['publish_hidden_fields'] = $this->_template_args['publish_hidden_fields'] ?? '';
2565
-        foreach ($hidden_fields as $hidden_field) {
2566
-            $this->_template_args['publish_hidden_fields'] .= $hidden_field['field'] ?? '';
2567
-        }
2568
-    }
2569
-
2570
-
2571
-    /**
2572
-     * @param string|null $name
2573
-     * @param int|string  $id
2574
-     * @param string|null $delete
2575
-     * @param string|null $save_close_redirect_URL
2576
-     * @param bool        $both_btns
2577
-     * @throws EE_Error
2578
-     */
2579
-    public function set_publish_post_box_vars(
2580
-        ?string $name = '',
2581
-        $id = 0,
2582
-        ?string $delete = '',
2583
-        ?string $save_close_redirect_URL = '',
2584
-        bool $both_btns = false
2585
-    ) {
2586
-        $this->_set_publish_post_box_vars($name, $id, $delete, $save_close_redirect_URL, $both_btns);
2587
-    }
2588
-
2589
-
2590
-    protected function addPublishPostMetaBoxHiddenFields(string $field_name, array $field_attributes)
2591
-    {
2592
-        $this->publish_post_meta_box_hidden_fields[ $field_name ] = $field_attributes;
2593
-    }
2594
-
2595
-
2596
-    /**
2597
-     * displays an error message to ppl who have javascript disabled
2598
-     *
2599
-     * @return void
2600
-     */
2601
-    private function _display_no_javascript_warning()
2602
-    {
2603
-        ?>
2392
+	}
2393
+
2394
+
2395
+	private function _espresso_links_post_box()
2396
+	{
2397
+		// Hiding until we actually have content to put in here...
2398
+		// $this->addMetaBox('espresso_links_post_box', esc_html__('Helpful Plugin Links', 'event_espresso'), array( $this, 'espresso_links_post_box'), $this->_wp_page_slug, 'side');
2399
+	}
2400
+
2401
+
2402
+	public function espresso_links_post_box()
2403
+	{
2404
+		// Hiding until we actually have content to put in here...
2405
+		// EEH_Template::display_template(
2406
+		//     EE_ADMIN_TEMPLATE . 'admin_general_metabox_contents_espresso_links.template.php'
2407
+		// );
2408
+	}
2409
+
2410
+
2411
+	protected function _espresso_sponsors_post_box()
2412
+	{
2413
+		if (apply_filters('FHEE_show_sponsors_meta_box', true)) {
2414
+			$this->addMetaBox(
2415
+				'espresso_sponsors_post_box',
2416
+				esc_html__('Event Espresso Highlights', 'event_espresso'),
2417
+				[$this, 'espresso_sponsors_post_box'],
2418
+				$this->_wp_page_slug,
2419
+				'side'
2420
+			);
2421
+		}
2422
+	}
2423
+
2424
+
2425
+	public function espresso_sponsors_post_box()
2426
+	{
2427
+		EEH_Template::display_template(
2428
+			EE_ADMIN_TEMPLATE . 'admin_general_metabox_contents_espresso_sponsors.template.php'
2429
+		);
2430
+	}
2431
+
2432
+
2433
+	/**
2434
+	 * if there is [ 'label' => [ 'publishbox' => 'some title' ]]
2435
+	 * present in the _page_config array, then we'll use that for the metabox label.
2436
+	 * Otherwise we'll just use publish
2437
+	 * (publishbox itself could be an array of labels indexed by routes)
2438
+	 *
2439
+	 * @return string
2440
+	 * @since   5.0.0.p
2441
+	 */
2442
+	protected function getPublishBoxTitle(): string
2443
+	{
2444
+		$publish_box_title = esc_html__('Publish', 'event_espresso');
2445
+		if (! empty($this->_labels['publishbox'])) {
2446
+			if (is_array($this->_labels['publishbox'])) {
2447
+				$publish_box_title = $this->_labels['publishbox'][ $this->_req_action ] ?? $publish_box_title;
2448
+			} else {
2449
+				$publish_box_title = $this->_labels['publishbox'];
2450
+			}
2451
+		}
2452
+		return apply_filters(
2453
+			'FHEE__EE_Admin_Page___publish_post_box__box_label',
2454
+			$publish_box_title,
2455
+			$this->_req_action,
2456
+			$this
2457
+		);
2458
+	}
2459
+
2460
+
2461
+	/**
2462
+	 * @throws EE_Error
2463
+	 */
2464
+	private function _publish_post_box()
2465
+	{
2466
+		$title = $this->getPublishBoxTitle();
2467
+		if (empty($this->_template_args['save_buttons'])) {
2468
+			$this->_set_publish_post_box_vars(
2469
+				sanitize_key($title),
2470
+				"espresso_{$this->page_slug}_editor_overview",
2471
+				'',
2472
+				'',
2473
+				true
2474
+			);
2475
+		} else {
2476
+			$this->addPublishPostMetaBoxHiddenFields(
2477
+				sanitize_key($title),
2478
+				['type' => 'hidden', 'value' => "espresso_{$this->page_slug}_editor_overview"]
2479
+			);
2480
+		}
2481
+		$this->addMetaBox(
2482
+			"espresso_{$this->page_slug}_editor_overview",
2483
+			$title,
2484
+			[$this, 'editor_overview'],
2485
+			$this->_current_screen->id,
2486
+			'side',
2487
+			'high'
2488
+		);
2489
+	}
2490
+
2491
+
2492
+	public function editor_overview()
2493
+	{
2494
+		/**
2495
+		 * @var string $publish_box_extra_content
2496
+		 * @var string $publish_hidden_fields
2497
+		 * @var string $publish_delete_link
2498
+		 * @var string $save_buttons
2499
+		 */
2500
+		// if we have extra content set let's add it in if not make sure its empty
2501
+		$this->_template_args['publish_box_extra_content'] = $this->_template_args['publish_box_extra_content'] ?? '';
2502
+		echo EEH_Template::display_template(
2503
+			EE_ADMIN_TEMPLATE . 'admin_details_publish_metabox.template.php',
2504
+			$this->_template_args,
2505
+			true
2506
+		);
2507
+	}
2508
+
2509
+
2510
+	/** end of globally available metaboxes section **/
2511
+
2512
+
2513
+	/**
2514
+	 * Sets the _template_args arguments used by the _publish_post_box shortcut
2515
+	 * Note: currently there is no validation for this.  However, if you want the delete button, the
2516
+	 * save, and save and close buttons to work properly, then you will want to include a
2517
+	 * values for the name and id arguments.
2518
+	 *
2519
+	 * @param string|null $name                     key used for the action ID (i.e. event_id)
2520
+	 * @param int|string  $id                       id attached to the item published
2521
+	 * @param string|null $delete                   page route callback for the delete action
2522
+	 * @param string|null $save_close_redirect_URL  custom URL to redirect to after Save & Close has been completed
2523
+	 * @param boolean     $both_btns                whether to display BOTH the "Save & Close" and "Save" buttons
2524
+	 *                                              or just the "Save" button
2525
+	 * @throws EE_Error
2526
+	 * @throws InvalidArgumentException
2527
+	 * @throws InvalidDataTypeException
2528
+	 * @throws InvalidInterfaceException
2529
+	 * @todo  Add in validation for name/id arguments.
2530
+	 */
2531
+	protected function _set_publish_post_box_vars(
2532
+		?string $name = '',
2533
+		$id = 0,
2534
+		?string $delete = '',
2535
+		?string $save_close_redirect_URL = '',
2536
+		bool $both_btns = true
2537
+	) {
2538
+		// if Save & Close, use a custom redirect URL or default to the main page?
2539
+		$save_close_redirect_URL = ! empty($save_close_redirect_URL)
2540
+			? $save_close_redirect_URL
2541
+			: $this->_admin_base_url;
2542
+		// create the Save & Close and Save buttons
2543
+		$this->_set_save_buttons($both_btns, [], [], $save_close_redirect_URL);
2544
+		// if we have extra content set let's add it in if not make sure its empty
2545
+		$this->_template_args['publish_box_extra_content'] = $this->_template_args['publish_box_extra_content'] ?? '';
2546
+		if ($delete && ! empty($id) && empty($this->_template_args['publish_delete_link'])) {
2547
+			// make sure we have a default if just true is sent.
2548
+			$delete      = ! empty($delete) ? $delete : 'delete';
2549
+			$this->_template_args['publish_delete_link'] = $this->get_action_link_or_button(
2550
+				$delete,
2551
+				$delete,
2552
+				[$name => $id],
2553
+				'submitdelete deletion button button--outline button--caution'
2554
+			);
2555
+		}
2556
+		if (! isset($this->_template_args['publish_delete_link'])) {
2557
+			$this->_template_args['publish_delete_link'] = '';
2558
+		}
2559
+		if (! empty($name) && ! empty($id)) {
2560
+			$this->addPublishPostMetaBoxHiddenFields($name, ['type' => 'hidden', 'value' => $id]);
2561
+		}
2562
+		$hidden_fields = $this->_generate_admin_form_fields($this->publish_post_meta_box_hidden_fields, 'array');
2563
+		// add hidden fields
2564
+		$this->_template_args['publish_hidden_fields'] = $this->_template_args['publish_hidden_fields'] ?? '';
2565
+		foreach ($hidden_fields as $hidden_field) {
2566
+			$this->_template_args['publish_hidden_fields'] .= $hidden_field['field'] ?? '';
2567
+		}
2568
+	}
2569
+
2570
+
2571
+	/**
2572
+	 * @param string|null $name
2573
+	 * @param int|string  $id
2574
+	 * @param string|null $delete
2575
+	 * @param string|null $save_close_redirect_URL
2576
+	 * @param bool        $both_btns
2577
+	 * @throws EE_Error
2578
+	 */
2579
+	public function set_publish_post_box_vars(
2580
+		?string $name = '',
2581
+		$id = 0,
2582
+		?string $delete = '',
2583
+		?string $save_close_redirect_URL = '',
2584
+		bool $both_btns = false
2585
+	) {
2586
+		$this->_set_publish_post_box_vars($name, $id, $delete, $save_close_redirect_URL, $both_btns);
2587
+	}
2588
+
2589
+
2590
+	protected function addPublishPostMetaBoxHiddenFields(string $field_name, array $field_attributes)
2591
+	{
2592
+		$this->publish_post_meta_box_hidden_fields[ $field_name ] = $field_attributes;
2593
+	}
2594
+
2595
+
2596
+	/**
2597
+	 * displays an error message to ppl who have javascript disabled
2598
+	 *
2599
+	 * @return void
2600
+	 */
2601
+	private function _display_no_javascript_warning()
2602
+	{
2603
+		?>
2604 2604
         <noscript>
2605 2605
             <div id="no-js-message" class="error">
2606 2606
                 <p style="font-size:1.3em;">
2607 2607
                     <span style="color:red;"><?php esc_html_e('Warning!', 'event_espresso'); ?></span>
2608 2608
                     <?php esc_html_e(
2609
-                        'Javascript is currently turned off for your browser. Javascript must be enabled in order for all of the features on this page to function properly. Please turn your javascript back on.',
2610
-                        'event_espresso'
2611
-                    ); ?>
2609
+						'Javascript is currently turned off for your browser. Javascript must be enabled in order for all of the features on this page to function properly. Please turn your javascript back on.',
2610
+						'event_espresso'
2611
+					); ?>
2612 2612
                 </p>
2613 2613
             </div>
2614 2614
         </noscript>
2615 2615
         <?php
2616
-    }
2617
-
2618
-
2619
-    /**
2620
-     * displays espresso success and/or error notices
2621
-     *
2622
-     * @return void
2623
-     */
2624
-    protected function _display_espresso_notices()
2625
-    {
2626
-        $notices = $this->_get_transient(true);
2627
-        echo stripslashes($notices);
2628
-    }
2629
-
2630
-
2631
-    /**
2632
-     * spinny things pacify the masses
2633
-     *
2634
-     * @return void
2635
-     */
2636
-    protected function _add_admin_page_ajax_loading_img()
2637
-    {
2638
-        ?>
2616
+	}
2617
+
2618
+
2619
+	/**
2620
+	 * displays espresso success and/or error notices
2621
+	 *
2622
+	 * @return void
2623
+	 */
2624
+	protected function _display_espresso_notices()
2625
+	{
2626
+		$notices = $this->_get_transient(true);
2627
+		echo stripslashes($notices);
2628
+	}
2629
+
2630
+
2631
+	/**
2632
+	 * spinny things pacify the masses
2633
+	 *
2634
+	 * @return void
2635
+	 */
2636
+	protected function _add_admin_page_ajax_loading_img()
2637
+	{
2638
+		?>
2639 2639
         <div id="espresso-ajax-loading" class="ajax-loading-grey">
2640 2640
             <span class="ee-spinner ee-spin"></span><span class="hidden"><?php
2641
-                esc_html_e('loading...', 'event_espresso'); ?></span>
2641
+				esc_html_e('loading...', 'event_espresso'); ?></span>
2642 2642
         </div>
2643 2643
         <?php
2644
-    }
2644
+	}
2645 2645
 
2646 2646
 
2647
-    /**
2648
-     * add admin page overlay for modal boxes
2649
-     *
2650
-     * @return void
2651
-     */
2652
-    protected function _add_admin_page_overlay()
2653
-    {
2654
-        ?>
2647
+	/**
2648
+	 * add admin page overlay for modal boxes
2649
+	 *
2650
+	 * @return void
2651
+	 */
2652
+	protected function _add_admin_page_overlay()
2653
+	{
2654
+		?>
2655 2655
         <div id="espresso-admin-page-overlay-dv" class=""></div>
2656 2656
         <?php
2657
-    }
2658
-
2659
-
2660
-    /**
2661
-     * facade for $this->addMetaBox()
2662
-     *
2663
-     * @param string  $action        where the metabox gets displayed
2664
-     * @param string  $title         Title of Metabox (output in metabox header)
2665
-     * @param string  $callback      If not empty and $create_fun is set to false then we'll use a custom callback
2666
-     *                               instead of the one created in here.
2667
-     * @param array   $callback_args an array of args supplied for the metabox
2668
-     * @param string  $column        what metabox column
2669
-     * @param string  $priority      give this metabox a priority (using accepted priorities for wp meta boxes)
2670
-     * @param boolean $create_func   default is true.  Basically we can say we don't WANT to have the runtime function
2671
-     *                               created but just set our own callback for wp's add_meta_box.
2672
-     * @throws DomainException
2673
-     */
2674
-    public function _add_admin_page_meta_box(
2675
-        $action,
2676
-        $title,
2677
-        $callback,
2678
-        $callback_args,
2679
-        $column = 'normal',
2680
-        $priority = 'high',
2681
-        $create_func = true
2682
-    ) {
2683
-        do_action('AHEE_log', __FILE__, __FUNCTION__, $callback);
2684
-        // if we have empty callback args and we want to automatically create the metabox callback then we need to make sure the callback args are generated.
2685
-        if (empty($callback_args) && $create_func) {
2686
-            $callback_args = [
2687
-                'template_path' => $this->_template_path,
2688
-                'template_args' => $this->_template_args,
2689
-            ];
2690
-        }
2691
-        // if $create_func is true (default) then we automatically create the function for displaying the actual meta box.  If false then we take the $callback reference passed through and use it instead (so callers can define their own callback function/method if they wish)
2692
-        $call_back_func = $create_func
2693
-            ? static function ($post, $metabox) {
2694
-                do_action('AHEE_log', __FILE__, __FUNCTION__, '');
2695
-                echo EEH_Template::display_template(
2696
-                    $metabox['args']['template_path'],
2697
-                    $metabox['args']['template_args'],
2698
-                    true
2699
-                );
2700
-            }
2701
-            : $callback;
2702
-        $this->addMetaBox(
2703
-            str_replace('_', '-', $action) . '-mbox',
2704
-            $title,
2705
-            $call_back_func,
2706
-            $this->_wp_page_slug,
2707
-            $column,
2708
-            $priority,
2709
-            $callback_args
2710
-        );
2711
-    }
2712
-
2713
-
2714
-    /**
2715
-     * generates HTML wrapper for and admin details page that contains metaboxes in columns
2716
-     *
2717
-     * @throws DomainException
2718
-     * @throws EE_Error
2719
-     * @throws InvalidArgumentException
2720
-     * @throws InvalidDataTypeException
2721
-     * @throws InvalidInterfaceException
2722
-     */
2723
-    public function display_admin_page_with_metabox_columns()
2724
-    {
2725
-        $this->_template_args['post_body_content']  = $this->_template_args['admin_page_content'];
2726
-        $this->_template_args['admin_page_content'] = EEH_Template::display_template(
2727
-            $this->_column_template_path,
2728
-            $this->_template_args,
2729
-            true
2730
-        );
2731
-        // the final wrapper
2732
-        $this->admin_page_wrapper();
2733
-    }
2734
-
2735
-
2736
-    /**
2737
-     * generates  HTML wrapper for an admin details page
2738
-     *
2739
-     * @return void
2740
-     * @throws DomainException
2741
-     * @throws EE_Error
2742
-     * @throws InvalidArgumentException
2743
-     * @throws InvalidDataTypeException
2744
-     * @throws InvalidInterfaceException
2745
-     */
2746
-    public function display_admin_page_with_sidebar()
2747
-    {
2748
-        $this->_display_admin_page(true);
2749
-    }
2750
-
2751
-
2752
-    /**
2753
-     * generates  HTML wrapper for an admin details page (except no sidebar)
2754
-     *
2755
-     * @return void
2756
-     * @throws DomainException
2757
-     * @throws EE_Error
2758
-     * @throws InvalidArgumentException
2759
-     * @throws InvalidDataTypeException
2760
-     * @throws InvalidInterfaceException
2761
-     */
2762
-    public function display_admin_page_with_no_sidebar()
2763
-    {
2764
-        $this->_display_admin_page();
2765
-    }
2766
-
2767
-
2768
-    /**
2769
-     * generates HTML wrapper for an EE about admin page (no sidebar)
2770
-     *
2771
-     * @return void
2772
-     * @throws DomainException
2773
-     * @throws EE_Error
2774
-     * @throws InvalidArgumentException
2775
-     * @throws InvalidDataTypeException
2776
-     * @throws InvalidInterfaceException
2777
-     */
2778
-    public function display_about_admin_page()
2779
-    {
2780
-        $this->_display_admin_page(false, true);
2781
-    }
2782
-
2783
-
2784
-    /**
2785
-     * display_admin_page
2786
-     * contains the code for actually displaying an admin page
2787
-     *
2788
-     * @param boolean $sidebar true with sidebar, false without
2789
-     * @param boolean $about   use the about admin wrapper instead of the default.
2790
-     * @return void
2791
-     * @throws DomainException
2792
-     * @throws EE_Error
2793
-     * @throws InvalidArgumentException
2794
-     * @throws InvalidDataTypeException
2795
-     * @throws InvalidInterfaceException
2796
-     */
2797
-    private function _display_admin_page($sidebar = false, $about = false)
2798
-    {
2799
-        do_action('AHEE_log', __FILE__, __FUNCTION__, '');
2800
-        // custom remove metaboxes hook to add or remove any metaboxes to/from Admin pages.
2801
-        do_action('AHEE__EE_Admin_Page___display_admin_page__modify_metaboxes');
2802
-        // set current wp page slug - looks like: event-espresso_page_event_categories
2803
-        // keep in mind "event-espresso" COULD be something else if the top level menu label has been translated.
2804
-
2805
-        $post_body_content = $this->_template_args['before_admin_page_content'] ?? '';
2806
-
2807
-        $this->_template_args['add_page_frame'] = $this->_req_action !== 'system_status'
2808
-                                                  && $this->_req_action !== 'data_reset'
2809
-                                                  && $this->_wp_page_slug !== 'event-espresso_page_espresso_packages'
2810
-                                                  && strpos($post_body_content, 'wp-list-table') === false;
2811
-
2812
-        $this->_template_args['current_page']                 = $this->_wp_page_slug;
2813
-        $this->_template_args['admin_page_wrapper_div_id']    = $this->_cpt_route
2814
-            ? 'poststuff'
2815
-            : 'espresso-default-admin';
2816
-        $this->_template_args['admin_page_wrapper_div_class'] = str_replace(
2817
-                                                                    'event-espresso_page_espresso_',
2818
-                                                                    '',
2819
-                                                                    $this->_wp_page_slug
2820
-                                                                ) . ' ' . $this->_req_action . '-route';
2821
-
2822
-        $template_path = $sidebar
2823
-            ? EE_ADMIN_TEMPLATE . 'admin_details_wrapper.template.php'
2824
-            : EE_ADMIN_TEMPLATE . 'admin_details_wrapper_no_sidebar.template.php';
2825
-        if ($this->request->isAjax()) {
2826
-            $template_path = EE_ADMIN_TEMPLATE . 'admin_details_wrapper_no_sidebar_ajax.template.php';
2827
-        }
2828
-        $template_path = ! empty($this->_column_template_path) ? $this->_column_template_path : $template_path;
2829
-
2830
-        $this->_template_args['post_body_content']         = $this->_template_args['admin_page_content'] ?? '';
2831
-        $this->_template_args['before_admin_page_content'] = $post_body_content;
2832
-        $this->_template_args['after_admin_page_content']  = $this->_template_args['after_admin_page_content'] ?? '';
2833
-        $this->_template_args['admin_page_content']        = EEH_Template::display_template(
2834
-            $template_path,
2835
-            $this->_template_args,
2836
-            true
2837
-        );
2838
-        // the final template wrapper
2839
-        $this->admin_page_wrapper($about);
2840
-    }
2841
-
2842
-
2843
-    /**
2844
-     * This is used to display caf preview pages.
2845
-     *
2846
-     * @param string $utm_campaign_source what is the key used for google analytics link
2847
-     * @param bool   $display_sidebar     whether to use the sidebar template or the full template for the page.  TRUE
2848
-     *                                    = SHOW sidebar, FALSE = no sidebar. Default no sidebar.
2849
-     * @return void
2850
-     * @throws DomainException
2851
-     * @throws EE_Error
2852
-     * @throws InvalidArgumentException
2853
-     * @throws InvalidDataTypeException
2854
-     * @throws InvalidInterfaceException
2855
-     * @since 4.3.2
2856
-     */
2857
-    public function display_admin_caf_preview_page($utm_campaign_source = '', $display_sidebar = true)
2858
-    {
2859
-        // let's generate a default preview action button if there isn't one already present.
2860
-        $this->_labels['buttons']['buy_now']           = esc_html__(
2861
-            'Upgrade to Event Espresso 4 Right Now',
2862
-            'event_espresso'
2863
-        );
2864
-        $buy_now_url                                   = add_query_arg(
2865
-            [
2866
-                'ee_ver'       => 'ee4',
2867
-                'utm_source'   => 'ee4_plugin_admin',
2868
-                'utm_medium'   => 'link',
2869
-                'utm_campaign' => $utm_campaign_source,
2870
-                'utm_content'  => 'buy_now_button',
2871
-            ],
2872
-            'https://eventespresso.com/pricing/'
2873
-        );
2874
-        $this->_template_args['preview_action_button'] = ! isset($this->_template_args['preview_action_button'])
2875
-            ? $this->get_action_link_or_button(
2876
-                '',
2877
-                'buy_now',
2878
-                [],
2879
-                'button button--primary button--big',
2880
-                esc_url_raw($buy_now_url),
2881
-                true
2882
-            )
2883
-            : $this->_template_args['preview_action_button'];
2884
-        $this->_template_args['admin_page_content']    = EEH_Template::display_template(
2885
-            EE_ADMIN_TEMPLATE . 'admin_caf_full_page_preview.template.php',
2886
-            $this->_template_args,
2887
-            true
2888
-        );
2889
-        $this->_display_admin_page($display_sidebar);
2890
-    }
2891
-
2892
-
2893
-    /**
2894
-     * display_admin_list_table_page_with_sidebar
2895
-     * generates HTML wrapper for an admin_page with list_table
2896
-     *
2897
-     * @return void
2898
-     * @throws DomainException
2899
-     * @throws EE_Error
2900
-     * @throws InvalidArgumentException
2901
-     * @throws InvalidDataTypeException
2902
-     * @throws InvalidInterfaceException
2903
-     */
2904
-    public function display_admin_list_table_page_with_sidebar()
2905
-    {
2906
-        $this->_display_admin_list_table_page(true);
2907
-    }
2908
-
2909
-
2910
-    /**
2911
-     * display_admin_list_table_page_with_no_sidebar
2912
-     * generates HTML wrapper for an admin_page with list_table (but with no sidebar)
2913
-     *
2914
-     * @return void
2915
-     * @throws DomainException
2916
-     * @throws EE_Error
2917
-     * @throws InvalidArgumentException
2918
-     * @throws InvalidDataTypeException
2919
-     * @throws InvalidInterfaceException
2920
-     */
2921
-    public function display_admin_list_table_page_with_no_sidebar()
2922
-    {
2923
-        $this->_display_admin_list_table_page();
2924
-    }
2925
-
2926
-
2927
-    /**
2928
-     * generates html wrapper for an admin_list_table page
2929
-     *
2930
-     * @param boolean $sidebar whether to display with sidebar or not.
2931
-     * @return void
2932
-     * @throws DomainException
2933
-     * @throws EE_Error
2934
-     * @throws InvalidArgumentException
2935
-     * @throws InvalidDataTypeException
2936
-     * @throws InvalidInterfaceException
2937
-     */
2938
-    private function _display_admin_list_table_page($sidebar = false)
2939
-    {
2940
-        // setup search attributes
2941
-        $this->_set_search_attributes();
2942
-        $this->_template_args['current_page']     = $this->_wp_page_slug;
2943
-        $template_path                            = EE_ADMIN_TEMPLATE . 'admin_list_wrapper.template.php';
2944
-        $this->_template_args['table_url']        = $this->request->isAjax()
2945
-            ? add_query_arg(['noheader' => 'true', 'route' => $this->_req_action], $this->_admin_base_url)
2946
-            : add_query_arg(['route' => $this->_req_action], $this->_admin_base_url);
2947
-        $this->_template_args['list_table']       = $this->_list_table_object;
2948
-        $this->_template_args['current_route']    = $this->_req_action;
2949
-        $this->_template_args['list_table_class'] = get_class($this->_list_table_object);
2950
-        $ajax_sorting_callback                    = $this->_list_table_object->get_ajax_sorting_callback();
2951
-        if (! empty($ajax_sorting_callback)) {
2952
-            $sortable_list_table_form_fields = wp_nonce_field(
2953
-                $ajax_sorting_callback . '_nonce',
2954
-                $ajax_sorting_callback . '_nonce',
2955
-                false,
2956
-                false
2957
-            );
2958
-            $sortable_list_table_form_fields .= '<input type="hidden" id="ajax_table_sort_page" name="ajax_table_sort_page" value="'
2959
-                                                . $this->page_slug
2960
-                                                . '" />';
2961
-            $sortable_list_table_form_fields .= '<input type="hidden" id="ajax_table_sort_action" name="ajax_table_sort_action" value="'
2962
-                                                . $ajax_sorting_callback
2963
-                                                . '" />';
2964
-        } else {
2965
-            $sortable_list_table_form_fields = '';
2966
-        }
2967
-        $this->_template_args['sortable_list_table_form_fields'] = $sortable_list_table_form_fields;
2968
-
2969
-        $hidden_form_fields = $this->_template_args['list_table_hidden_fields'] ?? '';
2970
-
2971
-        $nonce_ref          = $this->_req_action . '_nonce';
2972
-        $hidden_form_fields .= '
2657
+	}
2658
+
2659
+
2660
+	/**
2661
+	 * facade for $this->addMetaBox()
2662
+	 *
2663
+	 * @param string  $action        where the metabox gets displayed
2664
+	 * @param string  $title         Title of Metabox (output in metabox header)
2665
+	 * @param string  $callback      If not empty and $create_fun is set to false then we'll use a custom callback
2666
+	 *                               instead of the one created in here.
2667
+	 * @param array   $callback_args an array of args supplied for the metabox
2668
+	 * @param string  $column        what metabox column
2669
+	 * @param string  $priority      give this metabox a priority (using accepted priorities for wp meta boxes)
2670
+	 * @param boolean $create_func   default is true.  Basically we can say we don't WANT to have the runtime function
2671
+	 *                               created but just set our own callback for wp's add_meta_box.
2672
+	 * @throws DomainException
2673
+	 */
2674
+	public function _add_admin_page_meta_box(
2675
+		$action,
2676
+		$title,
2677
+		$callback,
2678
+		$callback_args,
2679
+		$column = 'normal',
2680
+		$priority = 'high',
2681
+		$create_func = true
2682
+	) {
2683
+		do_action('AHEE_log', __FILE__, __FUNCTION__, $callback);
2684
+		// if we have empty callback args and we want to automatically create the metabox callback then we need to make sure the callback args are generated.
2685
+		if (empty($callback_args) && $create_func) {
2686
+			$callback_args = [
2687
+				'template_path' => $this->_template_path,
2688
+				'template_args' => $this->_template_args,
2689
+			];
2690
+		}
2691
+		// if $create_func is true (default) then we automatically create the function for displaying the actual meta box.  If false then we take the $callback reference passed through and use it instead (so callers can define their own callback function/method if they wish)
2692
+		$call_back_func = $create_func
2693
+			? static function ($post, $metabox) {
2694
+				do_action('AHEE_log', __FILE__, __FUNCTION__, '');
2695
+				echo EEH_Template::display_template(
2696
+					$metabox['args']['template_path'],
2697
+					$metabox['args']['template_args'],
2698
+					true
2699
+				);
2700
+			}
2701
+			: $callback;
2702
+		$this->addMetaBox(
2703
+			str_replace('_', '-', $action) . '-mbox',
2704
+			$title,
2705
+			$call_back_func,
2706
+			$this->_wp_page_slug,
2707
+			$column,
2708
+			$priority,
2709
+			$callback_args
2710
+		);
2711
+	}
2712
+
2713
+
2714
+	/**
2715
+	 * generates HTML wrapper for and admin details page that contains metaboxes in columns
2716
+	 *
2717
+	 * @throws DomainException
2718
+	 * @throws EE_Error
2719
+	 * @throws InvalidArgumentException
2720
+	 * @throws InvalidDataTypeException
2721
+	 * @throws InvalidInterfaceException
2722
+	 */
2723
+	public function display_admin_page_with_metabox_columns()
2724
+	{
2725
+		$this->_template_args['post_body_content']  = $this->_template_args['admin_page_content'];
2726
+		$this->_template_args['admin_page_content'] = EEH_Template::display_template(
2727
+			$this->_column_template_path,
2728
+			$this->_template_args,
2729
+			true
2730
+		);
2731
+		// the final wrapper
2732
+		$this->admin_page_wrapper();
2733
+	}
2734
+
2735
+
2736
+	/**
2737
+	 * generates  HTML wrapper for an admin details page
2738
+	 *
2739
+	 * @return void
2740
+	 * @throws DomainException
2741
+	 * @throws EE_Error
2742
+	 * @throws InvalidArgumentException
2743
+	 * @throws InvalidDataTypeException
2744
+	 * @throws InvalidInterfaceException
2745
+	 */
2746
+	public function display_admin_page_with_sidebar()
2747
+	{
2748
+		$this->_display_admin_page(true);
2749
+	}
2750
+
2751
+
2752
+	/**
2753
+	 * generates  HTML wrapper for an admin details page (except no sidebar)
2754
+	 *
2755
+	 * @return void
2756
+	 * @throws DomainException
2757
+	 * @throws EE_Error
2758
+	 * @throws InvalidArgumentException
2759
+	 * @throws InvalidDataTypeException
2760
+	 * @throws InvalidInterfaceException
2761
+	 */
2762
+	public function display_admin_page_with_no_sidebar()
2763
+	{
2764
+		$this->_display_admin_page();
2765
+	}
2766
+
2767
+
2768
+	/**
2769
+	 * generates HTML wrapper for an EE about admin page (no sidebar)
2770
+	 *
2771
+	 * @return void
2772
+	 * @throws DomainException
2773
+	 * @throws EE_Error
2774
+	 * @throws InvalidArgumentException
2775
+	 * @throws InvalidDataTypeException
2776
+	 * @throws InvalidInterfaceException
2777
+	 */
2778
+	public function display_about_admin_page()
2779
+	{
2780
+		$this->_display_admin_page(false, true);
2781
+	}
2782
+
2783
+
2784
+	/**
2785
+	 * display_admin_page
2786
+	 * contains the code for actually displaying an admin page
2787
+	 *
2788
+	 * @param boolean $sidebar true with sidebar, false without
2789
+	 * @param boolean $about   use the about admin wrapper instead of the default.
2790
+	 * @return void
2791
+	 * @throws DomainException
2792
+	 * @throws EE_Error
2793
+	 * @throws InvalidArgumentException
2794
+	 * @throws InvalidDataTypeException
2795
+	 * @throws InvalidInterfaceException
2796
+	 */
2797
+	private function _display_admin_page($sidebar = false, $about = false)
2798
+	{
2799
+		do_action('AHEE_log', __FILE__, __FUNCTION__, '');
2800
+		// custom remove metaboxes hook to add or remove any metaboxes to/from Admin pages.
2801
+		do_action('AHEE__EE_Admin_Page___display_admin_page__modify_metaboxes');
2802
+		// set current wp page slug - looks like: event-espresso_page_event_categories
2803
+		// keep in mind "event-espresso" COULD be something else if the top level menu label has been translated.
2804
+
2805
+		$post_body_content = $this->_template_args['before_admin_page_content'] ?? '';
2806
+
2807
+		$this->_template_args['add_page_frame'] = $this->_req_action !== 'system_status'
2808
+												  && $this->_req_action !== 'data_reset'
2809
+												  && $this->_wp_page_slug !== 'event-espresso_page_espresso_packages'
2810
+												  && strpos($post_body_content, 'wp-list-table') === false;
2811
+
2812
+		$this->_template_args['current_page']                 = $this->_wp_page_slug;
2813
+		$this->_template_args['admin_page_wrapper_div_id']    = $this->_cpt_route
2814
+			? 'poststuff'
2815
+			: 'espresso-default-admin';
2816
+		$this->_template_args['admin_page_wrapper_div_class'] = str_replace(
2817
+																	'event-espresso_page_espresso_',
2818
+																	'',
2819
+																	$this->_wp_page_slug
2820
+																) . ' ' . $this->_req_action . '-route';
2821
+
2822
+		$template_path = $sidebar
2823
+			? EE_ADMIN_TEMPLATE . 'admin_details_wrapper.template.php'
2824
+			: EE_ADMIN_TEMPLATE . 'admin_details_wrapper_no_sidebar.template.php';
2825
+		if ($this->request->isAjax()) {
2826
+			$template_path = EE_ADMIN_TEMPLATE . 'admin_details_wrapper_no_sidebar_ajax.template.php';
2827
+		}
2828
+		$template_path = ! empty($this->_column_template_path) ? $this->_column_template_path : $template_path;
2829
+
2830
+		$this->_template_args['post_body_content']         = $this->_template_args['admin_page_content'] ?? '';
2831
+		$this->_template_args['before_admin_page_content'] = $post_body_content;
2832
+		$this->_template_args['after_admin_page_content']  = $this->_template_args['after_admin_page_content'] ?? '';
2833
+		$this->_template_args['admin_page_content']        = EEH_Template::display_template(
2834
+			$template_path,
2835
+			$this->_template_args,
2836
+			true
2837
+		);
2838
+		// the final template wrapper
2839
+		$this->admin_page_wrapper($about);
2840
+	}
2841
+
2842
+
2843
+	/**
2844
+	 * This is used to display caf preview pages.
2845
+	 *
2846
+	 * @param string $utm_campaign_source what is the key used for google analytics link
2847
+	 * @param bool   $display_sidebar     whether to use the sidebar template or the full template for the page.  TRUE
2848
+	 *                                    = SHOW sidebar, FALSE = no sidebar. Default no sidebar.
2849
+	 * @return void
2850
+	 * @throws DomainException
2851
+	 * @throws EE_Error
2852
+	 * @throws InvalidArgumentException
2853
+	 * @throws InvalidDataTypeException
2854
+	 * @throws InvalidInterfaceException
2855
+	 * @since 4.3.2
2856
+	 */
2857
+	public function display_admin_caf_preview_page($utm_campaign_source = '', $display_sidebar = true)
2858
+	{
2859
+		// let's generate a default preview action button if there isn't one already present.
2860
+		$this->_labels['buttons']['buy_now']           = esc_html__(
2861
+			'Upgrade to Event Espresso 4 Right Now',
2862
+			'event_espresso'
2863
+		);
2864
+		$buy_now_url                                   = add_query_arg(
2865
+			[
2866
+				'ee_ver'       => 'ee4',
2867
+				'utm_source'   => 'ee4_plugin_admin',
2868
+				'utm_medium'   => 'link',
2869
+				'utm_campaign' => $utm_campaign_source,
2870
+				'utm_content'  => 'buy_now_button',
2871
+			],
2872
+			'https://eventespresso.com/pricing/'
2873
+		);
2874
+		$this->_template_args['preview_action_button'] = ! isset($this->_template_args['preview_action_button'])
2875
+			? $this->get_action_link_or_button(
2876
+				'',
2877
+				'buy_now',
2878
+				[],
2879
+				'button button--primary button--big',
2880
+				esc_url_raw($buy_now_url),
2881
+				true
2882
+			)
2883
+			: $this->_template_args['preview_action_button'];
2884
+		$this->_template_args['admin_page_content']    = EEH_Template::display_template(
2885
+			EE_ADMIN_TEMPLATE . 'admin_caf_full_page_preview.template.php',
2886
+			$this->_template_args,
2887
+			true
2888
+		);
2889
+		$this->_display_admin_page($display_sidebar);
2890
+	}
2891
+
2892
+
2893
+	/**
2894
+	 * display_admin_list_table_page_with_sidebar
2895
+	 * generates HTML wrapper for an admin_page with list_table
2896
+	 *
2897
+	 * @return void
2898
+	 * @throws DomainException
2899
+	 * @throws EE_Error
2900
+	 * @throws InvalidArgumentException
2901
+	 * @throws InvalidDataTypeException
2902
+	 * @throws InvalidInterfaceException
2903
+	 */
2904
+	public function display_admin_list_table_page_with_sidebar()
2905
+	{
2906
+		$this->_display_admin_list_table_page(true);
2907
+	}
2908
+
2909
+
2910
+	/**
2911
+	 * display_admin_list_table_page_with_no_sidebar
2912
+	 * generates HTML wrapper for an admin_page with list_table (but with no sidebar)
2913
+	 *
2914
+	 * @return void
2915
+	 * @throws DomainException
2916
+	 * @throws EE_Error
2917
+	 * @throws InvalidArgumentException
2918
+	 * @throws InvalidDataTypeException
2919
+	 * @throws InvalidInterfaceException
2920
+	 */
2921
+	public function display_admin_list_table_page_with_no_sidebar()
2922
+	{
2923
+		$this->_display_admin_list_table_page();
2924
+	}
2925
+
2926
+
2927
+	/**
2928
+	 * generates html wrapper for an admin_list_table page
2929
+	 *
2930
+	 * @param boolean $sidebar whether to display with sidebar or not.
2931
+	 * @return void
2932
+	 * @throws DomainException
2933
+	 * @throws EE_Error
2934
+	 * @throws InvalidArgumentException
2935
+	 * @throws InvalidDataTypeException
2936
+	 * @throws InvalidInterfaceException
2937
+	 */
2938
+	private function _display_admin_list_table_page($sidebar = false)
2939
+	{
2940
+		// setup search attributes
2941
+		$this->_set_search_attributes();
2942
+		$this->_template_args['current_page']     = $this->_wp_page_slug;
2943
+		$template_path                            = EE_ADMIN_TEMPLATE . 'admin_list_wrapper.template.php';
2944
+		$this->_template_args['table_url']        = $this->request->isAjax()
2945
+			? add_query_arg(['noheader' => 'true', 'route' => $this->_req_action], $this->_admin_base_url)
2946
+			: add_query_arg(['route' => $this->_req_action], $this->_admin_base_url);
2947
+		$this->_template_args['list_table']       = $this->_list_table_object;
2948
+		$this->_template_args['current_route']    = $this->_req_action;
2949
+		$this->_template_args['list_table_class'] = get_class($this->_list_table_object);
2950
+		$ajax_sorting_callback                    = $this->_list_table_object->get_ajax_sorting_callback();
2951
+		if (! empty($ajax_sorting_callback)) {
2952
+			$sortable_list_table_form_fields = wp_nonce_field(
2953
+				$ajax_sorting_callback . '_nonce',
2954
+				$ajax_sorting_callback . '_nonce',
2955
+				false,
2956
+				false
2957
+			);
2958
+			$sortable_list_table_form_fields .= '<input type="hidden" id="ajax_table_sort_page" name="ajax_table_sort_page" value="'
2959
+												. $this->page_slug
2960
+												. '" />';
2961
+			$sortable_list_table_form_fields .= '<input type="hidden" id="ajax_table_sort_action" name="ajax_table_sort_action" value="'
2962
+												. $ajax_sorting_callback
2963
+												. '" />';
2964
+		} else {
2965
+			$sortable_list_table_form_fields = '';
2966
+		}
2967
+		$this->_template_args['sortable_list_table_form_fields'] = $sortable_list_table_form_fields;
2968
+
2969
+		$hidden_form_fields = $this->_template_args['list_table_hidden_fields'] ?? '';
2970
+
2971
+		$nonce_ref          = $this->_req_action . '_nonce';
2972
+		$hidden_form_fields .= '
2973 2973
             <input type="hidden" name="' . $nonce_ref . '" value="' . wp_create_nonce($nonce_ref) . '">';
2974 2974
 
2975
-        $this->_template_args['list_table_hidden_fields'] = $hidden_form_fields;
2976
-        // display message about search results?
2977
-        $search                                    = $this->request->getRequestParam('s');
2978
-        $this->_template_args['before_list_table'] .= ! empty($search)
2979
-            ? '<p class="ee-search-results">' . sprintf(
2980
-                esc_html__('Displaying search results for the search string: %1$s', 'event_espresso'),
2981
-                trim($search, '%')
2982
-            ) . '</p>'
2983
-            : '';
2984
-        // filter before_list_table template arg
2985
-        $this->_template_args['before_list_table'] = apply_filters(
2986
-            'FHEE__EE_Admin_Page___display_admin_list_table_page__before_list_table__template_arg',
2987
-            $this->_template_args['before_list_table'],
2988
-            $this->page_slug,
2989
-            $this->request->requestParams(),
2990
-            $this->_req_action
2991
-        );
2992
-        // convert to array and filter again
2993
-        // arrays are easier to inject new items in a specific location,
2994
-        // but would not be backwards compatible, so we have to add a new filter
2995
-        $this->_template_args['before_list_table'] = implode(
2996
-            " \n",
2997
-            (array) apply_filters(
2998
-                'FHEE__EE_Admin_Page___display_admin_list_table_page__before_list_table__template_args_array',
2999
-                (array) $this->_template_args['before_list_table'],
3000
-                $this->page_slug,
3001
-                $this->request->requestParams(),
3002
-                $this->_req_action
3003
-            )
3004
-        );
3005
-        // filter after_list_table template arg
3006
-        $this->_template_args['after_list_table'] = apply_filters(
3007
-            'FHEE__EE_Admin_Page___display_admin_list_table_page__after_list_table__template_arg',
3008
-            $this->_template_args['after_list_table'],
3009
-            $this->page_slug,
3010
-            $this->request->requestParams(),
3011
-            $this->_req_action
3012
-        );
3013
-        // convert to array and filter again
3014
-        // arrays are easier to inject new items in a specific location,
3015
-        // but would not be backwards compatible, so we have to add a new filter
3016
-        $this->_template_args['after_list_table']   = implode(
3017
-            " \n",
3018
-            (array) apply_filters(
3019
-                'FHEE__EE_Admin_Page___display_admin_list_table_page__after_list_table__template_args_array',
3020
-                (array) $this->_template_args['after_list_table'],
3021
-                $this->page_slug,
3022
-                $this->request->requestParams(),
3023
-                $this->_req_action
3024
-            )
3025
-        );
3026
-        $this->_template_args['admin_page_content'] = EEH_Template::display_template(
3027
-            $template_path,
3028
-            $this->_template_args,
3029
-            true
3030
-        );
3031
-        // the final template wrapper
3032
-        if ($sidebar) {
3033
-            $this->display_admin_page_with_sidebar();
3034
-        } else {
3035
-            $this->display_admin_page_with_no_sidebar();
3036
-        }
3037
-    }
3038
-
3039
-
3040
-    /**
3041
-     * This just prepares a legend using the given items and the admin_details_legend.template.php file and returns the
3042
-     * html string for the legend.
3043
-     * $items are expected in an array in the following format:
3044
-     * $legend_items = array(
3045
-     *        'item_id' => array(
3046
-     *            'icon' => 'http://url_to_icon_being_described.png',
3047
-     *            'desc' => esc_html__('localized description of item');
3048
-     *        )
3049
-     * );
3050
-     *
3051
-     * @param array $items see above for format of array
3052
-     * @return string html string of legend
3053
-     * @throws DomainException
3054
-     */
3055
-    protected function _display_legend($items)
3056
-    {
3057
-        $this->_template_args['items'] = apply_filters(
3058
-            'FHEE__EE_Admin_Page___display_legend__items',
3059
-            (array) $items,
3060
-            $this
3061
-        );
3062
-        /** @var StatusChangeNotice $status_change_notice */
3063
-        $status_change_notice                         = $this->loader->getShared(
3064
-            'EventEspresso\core\domain\services\admin\notices\status_change\StatusChangeNotice'
3065
-        );
3066
-        $this->_template_args['status_change_notice'] = $status_change_notice->display(
3067
-            '__admin-legend',
3068
-            $this->page_slug
3069
-        );
3070
-        return EEH_Template::display_template(
3071
-            EE_ADMIN_TEMPLATE . 'admin_details_legend.template.php',
3072
-            $this->_template_args,
3073
-            true
3074
-        );
3075
-    }
3076
-
3077
-
3078
-    /**
3079
-     * This is used whenever we're DOING_AJAX to return a formatted json array that our calling javascript can expect
3080
-     * The returned json object is created from an array in the following format:
3081
-     * array(
3082
-     *  'error' => FALSE, //(default FALSE), contains any errors and/or exceptions (exceptions return json early),
3083
-     *  'success' => FALSE, //(default FALSE) - contains any special success message.
3084
-     *  'notices' => '', // - contains any EE_Error formatted notices
3085
-     *  'content' => 'string can be html', //this is a string of formatted content (can be html)
3086
-     *  'data' => array() //this can be any key/value pairs that a method returns for later json parsing by the js.
3087
-     *  We're also going to include the template args with every package (so js can pick out any specific template args
3088
-     *  that might be included in here)
3089
-     * )
3090
-     * The json object is populated by whatever is set in the $_template_args property.
3091
-     *
3092
-     * @param bool  $sticky_notices    Used to indicate whether you want to ensure notices are added to a transient
3093
-     *                                 instead of displayed.
3094
-     * @param array $notices_arguments Use this to pass any additional args on to the _process_notices.
3095
-     * @return void
3096
-     * @throws EE_Error
3097
-     * @throws InvalidArgumentException
3098
-     * @throws InvalidDataTypeException
3099
-     * @throws InvalidInterfaceException
3100
-     */
3101
-    protected function _return_json($sticky_notices = false, $notices_arguments = [])
3102
-    {
3103
-        // make sure any EE_Error notices have been handled.
3104
-        $this->_process_notices($notices_arguments, true, $sticky_notices);
3105
-        $data = isset($this->_template_args['data']) ? $this->_template_args['data'] : [];
3106
-        unset($this->_template_args['data']);
3107
-        $json = [
3108
-            'error'     => isset($this->_template_args['error']) ? $this->_template_args['error'] : false,
3109
-            'success'   => isset($this->_template_args['success']) ? $this->_template_args['success'] : false,
3110
-            'errors'    => isset($this->_template_args['errors']) ? $this->_template_args['errors'] : false,
3111
-            'attention' => isset($this->_template_args['attention']) ? $this->_template_args['attention'] : false,
3112
-            'notices'   => EE_Error::get_notices(),
3113
-            'content'   => isset($this->_template_args['admin_page_content'])
3114
-                ? $this->_template_args['admin_page_content'] : '',
3115
-            'data'      => array_merge($data, ['template_args' => $this->_template_args]),
3116
-            'isEEajax'  => true
3117
-            // special flag so any ajax.Success methods in js can identify this return package as a EEajax package.
3118
-        ];
3119
-        // make sure there are no php errors or headers_sent.  Then we can set correct json header.
3120
-        if (null === error_get_last() || ! headers_sent()) {
3121
-            header('Content-Type: application/json; charset=UTF-8');
3122
-        }
3123
-        echo wp_json_encode($json);
3124
-        exit();
3125
-    }
3126
-
3127
-
3128
-    /**
3129
-     * Simply a wrapper for the protected method so we can call this outside the class (ONLY when doing ajax)
3130
-     *
3131
-     * @return void
3132
-     * @throws EE_Error
3133
-     * @throws InvalidArgumentException
3134
-     * @throws InvalidDataTypeException
3135
-     * @throws InvalidInterfaceException
3136
-     */
3137
-    public function return_json()
3138
-    {
3139
-        if ($this->request->isAjax()) {
3140
-            $this->_return_json();
3141
-        } else {
3142
-            throw new EE_Error(
3143
-                sprintf(
3144
-                    esc_html__('The public %s method can only be called when DOING_AJAX = TRUE', 'event_espresso'),
3145
-                    __FUNCTION__
3146
-                )
3147
-            );
3148
-        }
3149
-    }
3150
-
3151
-
3152
-    /**
3153
-     * This provides a way for child hook classes to send along themselves by reference so methods/properties within
3154
-     * them can be accessed by EE_Admin_child pages. This is assigned to the $_hook_obj property.
3155
-     *
3156
-     * @param EE_Admin_Hooks $hook_obj This will be the object for the EE_Admin_Hooks child
3157
-     */
3158
-    public function set_hook_object(EE_Admin_Hooks $hook_obj)
3159
-    {
3160
-        $this->_hook_obj = $hook_obj;
3161
-    }
3162
-
3163
-
3164
-    /**
3165
-     *        generates  HTML wrapper with Tabbed nav for an admin page
3166
-     *
3167
-     * @param boolean $about whether to use the special about page wrapper or default.
3168
-     * @return void
3169
-     * @throws DomainException
3170
-     * @throws EE_Error
3171
-     * @throws InvalidArgumentException
3172
-     * @throws InvalidDataTypeException
3173
-     * @throws InvalidInterfaceException
3174
-     */
3175
-    public function admin_page_wrapper($about = false)
3176
-    {
3177
-        do_action('AHEE_log', __FILE__, __FUNCTION__, '');
3178
-        $this->_template_args['nav_tabs']         = $this->_get_main_nav_tabs();
3179
-        $this->_template_args['admin_page_title'] = $this->_admin_page_title;
3180
-
3181
-        $this->_template_args['before_admin_page_content'] = apply_filters(
3182
-            "FHEE_before_admin_page_content{$this->_current_page}{$this->_current_view}",
3183
-            $this->_template_args['before_admin_page_content'] ?? ''
3184
-        );
3185
-
3186
-        $this->_template_args['after_admin_page_content'] = apply_filters(
3187
-            "FHEE_after_admin_page_content{$this->_current_page}{$this->_current_view}",
3188
-            $this->_template_args['after_admin_page_content'] ?? ''
3189
-        );
3190
-        $this->_template_args['after_admin_page_content'] .= $this->_set_help_popup_content();
3191
-
3192
-        if ($this->request->isAjax()) {
3193
-            $this->_template_args['admin_page_content'] = EEH_Template::display_template(
3194
-            // $template_path,
3195
-                EE_ADMIN_TEMPLATE . 'admin_wrapper_ajax.template.php',
3196
-                $this->_template_args,
3197
-                true
3198
-            );
3199
-            $this->_return_json();
3200
-        }
3201
-        // load settings page wrapper template
3202
-        $template_path = $about
3203
-            ? EE_ADMIN_TEMPLATE . 'about_admin_wrapper.template.php'
3204
-            : EE_ADMIN_TEMPLATE . 'admin_wrapper.template.php';
3205
-
3206
-        EEH_Template::display_template($template_path, $this->_template_args);
3207
-    }
3208
-
3209
-
3210
-    /**
3211
-     * This returns the admin_nav tabs html using the configuration in the _nav_tabs property
3212
-     *
3213
-     * @return string html
3214
-     * @throws EE_Error
3215
-     */
3216
-    protected function _get_main_nav_tabs()
3217
-    {
3218
-        // let's generate the html using the EEH_Tabbed_Content helper.
3219
-        // We do this here so that it's possible for child classes to add in nav tabs dynamically at the last minute
3220
-        // (rather than setting in the page_routes array)
3221
-        return EEH_Tabbed_Content::display_admin_nav_tabs($this->_nav_tabs, $this->page_slug);
3222
-    }
3223
-
3224
-
3225
-    /**
3226
-     *        sort nav tabs
3227
-     *
3228
-     * @param $a
3229
-     * @param $b
3230
-     * @return int
3231
-     */
3232
-    private function _sort_nav_tabs($a, $b)
3233
-    {
3234
-        if ($a['order'] === $b['order']) {
3235
-            return 0;
3236
-        }
3237
-        return ($a['order'] < $b['order']) ? -1 : 1;
3238
-    }
3239
-
3240
-
3241
-    /**
3242
-     * generates HTML for the forms used on admin pages
3243
-     *
3244
-     * @param array  $input_vars - array of input field details
3245
-     * @param string $generator  indicates which generator to use: options are 'string' or 'array'
3246
-     * @param bool   $id
3247
-     * @return array|string
3248
-     * @uses   EEH_Form_Fields::get_form_fields (/helper/EEH_Form_Fields.helper.php)
3249
-     * @uses   EEH_Form_Fields::get_form_fields_array (/helper/EEH_Form_Fields.helper.php)
3250
-     */
3251
-    protected function _generate_admin_form_fields($input_vars = [], $generator = 'string', $id = false)
3252
-    {
3253
-        return $generator === 'string'
3254
-            ? EEH_Form_Fields::get_form_fields($input_vars, $id)
3255
-            : EEH_Form_Fields::get_form_fields_array($input_vars);
3256
-    }
3257
-
3258
-
3259
-    /**
3260
-     * generates the "Save" and "Save & Close" buttons for edit forms
3261
-     *
3262
-     * @param bool             $both     if true then both buttons will be generated.  If false then just the "Save &
3263
-     *                                   Close" button.
3264
-     * @param array            $text     if included, generator will use the given text for the buttons ( array([0] =>
3265
-     *                                   'Save', [1] => 'save & close')
3266
-     * @param array            $actions  if included allows us to set the actions that each button will carry out (i.e.
3267
-     *                                   via the "name" value in the button).  We can also use this to just dump
3268
-     *                                   default actions by submitting some other value.
3269
-     * @param bool|string|null $referrer if false then we just do the default action on save and close.  Other wise it
3270
-     *                                   will use the $referrer string. IF null, then we don't do ANYTHING on save and
3271
-     *                                   close (normal form handling).
3272
-     */
3273
-    protected function _set_save_buttons($both = true, $text = [], $actions = [], $referrer = null)
3274
-    {
3275
-        // make sure $text and $actions are in an array
3276
-        $text          = (array) $text;
3277
-        $actions       = (array) $actions;
3278
-        $referrer_url  = ! empty($referrer) ? $referrer : $this->request->getServerParam('REQUEST_URI');
3279
-        $button_text   = ! empty($text)
3280
-            ? $text
3281
-            : [
3282
-                esc_html__('Save', 'event_espresso'),
3283
-                esc_html__('Save and Close', 'event_espresso'),
3284
-            ];
3285
-        $default_names = ['save', 'save_and_close'];
3286
-        $buttons       = '';
3287
-        foreach ($button_text as $key => $button) {
3288
-            $ref     = $default_names[ $key ];
3289
-            $name    = ! empty($actions) ? $actions[ $key ] : $ref;
3290
-            $buttons .= '<input type="submit" class="button button--primary ' . $ref . '" '
3291
-                        . 'value="' . $button . '" name="' . $name . '" '
3292
-                        . 'id="' . $this->_current_view . '_' . $ref . '" />';
3293
-            if (! $both) {
3294
-                break;
3295
-            }
3296
-        }
3297
-        // add in a hidden index for the current page (so save and close redirects properly)
3298
-        $buttons                              .= '<input type="hidden" id="save_and_close_referrer" name="save_and_close_referrer" value="'
3299
-                                                 . $referrer_url
3300
-                                                 . '" />';
3301
-        $this->_template_args['save_buttons'] = $buttons;
3302
-    }
3303
-
3304
-
3305
-    /**
3306
-     * Wrapper for the protected function.  Allows plugins/addons to call this to set the form tags.
3307
-     *
3308
-     * @param string $route
3309
-     * @param array  $additional_hidden_fields
3310
-     * @see   $this->_set_add_edit_form_tags() for details on params
3311
-     * @since 4.6.0
3312
-     */
3313
-    public function set_add_edit_form_tags($route = '', $additional_hidden_fields = [])
3314
-    {
3315
-        $this->_set_add_edit_form_tags($route, $additional_hidden_fields);
3316
-    }
3317
-
3318
-
3319
-    /**
3320
-     * set form open and close tags on add/edit pages.
3321
-     *
3322
-     * @param string $route                    the route you want the form to direct to
3323
-     * @param array  $additional_hidden_fields any additional hidden fields required in the form header
3324
-     * @return void
3325
-     */
3326
-    protected function _set_add_edit_form_tags($route = '', $additional_hidden_fields = [])
3327
-    {
3328
-        if (empty($route)) {
3329
-            $user_msg = esc_html__(
3330
-                'An error occurred. No action was set for this page\'s form.',
3331
-                'event_espresso'
3332
-            );
3333
-            $dev_msg  = $user_msg . "\n"
3334
-                        . sprintf(
3335
-                            esc_html__('The $route argument is required for the %s->%s method.', 'event_espresso'),
3336
-                            __FUNCTION__,
3337
-                            __CLASS__
3338
-                        );
3339
-            EE_Error::add_error($user_msg . '||' . $dev_msg, __FILE__, __FUNCTION__, __LINE__);
3340
-        }
3341
-        // open form
3342
-        $action                                            = $this->_admin_base_url;
3343
-        $this->_template_args['before_admin_page_content'] = "
2975
+		$this->_template_args['list_table_hidden_fields'] = $hidden_form_fields;
2976
+		// display message about search results?
2977
+		$search                                    = $this->request->getRequestParam('s');
2978
+		$this->_template_args['before_list_table'] .= ! empty($search)
2979
+			? '<p class="ee-search-results">' . sprintf(
2980
+				esc_html__('Displaying search results for the search string: %1$s', 'event_espresso'),
2981
+				trim($search, '%')
2982
+			) . '</p>'
2983
+			: '';
2984
+		// filter before_list_table template arg
2985
+		$this->_template_args['before_list_table'] = apply_filters(
2986
+			'FHEE__EE_Admin_Page___display_admin_list_table_page__before_list_table__template_arg',
2987
+			$this->_template_args['before_list_table'],
2988
+			$this->page_slug,
2989
+			$this->request->requestParams(),
2990
+			$this->_req_action
2991
+		);
2992
+		// convert to array and filter again
2993
+		// arrays are easier to inject new items in a specific location,
2994
+		// but would not be backwards compatible, so we have to add a new filter
2995
+		$this->_template_args['before_list_table'] = implode(
2996
+			" \n",
2997
+			(array) apply_filters(
2998
+				'FHEE__EE_Admin_Page___display_admin_list_table_page__before_list_table__template_args_array',
2999
+				(array) $this->_template_args['before_list_table'],
3000
+				$this->page_slug,
3001
+				$this->request->requestParams(),
3002
+				$this->_req_action
3003
+			)
3004
+		);
3005
+		// filter after_list_table template arg
3006
+		$this->_template_args['after_list_table'] = apply_filters(
3007
+			'FHEE__EE_Admin_Page___display_admin_list_table_page__after_list_table__template_arg',
3008
+			$this->_template_args['after_list_table'],
3009
+			$this->page_slug,
3010
+			$this->request->requestParams(),
3011
+			$this->_req_action
3012
+		);
3013
+		// convert to array and filter again
3014
+		// arrays are easier to inject new items in a specific location,
3015
+		// but would not be backwards compatible, so we have to add a new filter
3016
+		$this->_template_args['after_list_table']   = implode(
3017
+			" \n",
3018
+			(array) apply_filters(
3019
+				'FHEE__EE_Admin_Page___display_admin_list_table_page__after_list_table__template_args_array',
3020
+				(array) $this->_template_args['after_list_table'],
3021
+				$this->page_slug,
3022
+				$this->request->requestParams(),
3023
+				$this->_req_action
3024
+			)
3025
+		);
3026
+		$this->_template_args['admin_page_content'] = EEH_Template::display_template(
3027
+			$template_path,
3028
+			$this->_template_args,
3029
+			true
3030
+		);
3031
+		// the final template wrapper
3032
+		if ($sidebar) {
3033
+			$this->display_admin_page_with_sidebar();
3034
+		} else {
3035
+			$this->display_admin_page_with_no_sidebar();
3036
+		}
3037
+	}
3038
+
3039
+
3040
+	/**
3041
+	 * This just prepares a legend using the given items and the admin_details_legend.template.php file and returns the
3042
+	 * html string for the legend.
3043
+	 * $items are expected in an array in the following format:
3044
+	 * $legend_items = array(
3045
+	 *        'item_id' => array(
3046
+	 *            'icon' => 'http://url_to_icon_being_described.png',
3047
+	 *            'desc' => esc_html__('localized description of item');
3048
+	 *        )
3049
+	 * );
3050
+	 *
3051
+	 * @param array $items see above for format of array
3052
+	 * @return string html string of legend
3053
+	 * @throws DomainException
3054
+	 */
3055
+	protected function _display_legend($items)
3056
+	{
3057
+		$this->_template_args['items'] = apply_filters(
3058
+			'FHEE__EE_Admin_Page___display_legend__items',
3059
+			(array) $items,
3060
+			$this
3061
+		);
3062
+		/** @var StatusChangeNotice $status_change_notice */
3063
+		$status_change_notice                         = $this->loader->getShared(
3064
+			'EventEspresso\core\domain\services\admin\notices\status_change\StatusChangeNotice'
3065
+		);
3066
+		$this->_template_args['status_change_notice'] = $status_change_notice->display(
3067
+			'__admin-legend',
3068
+			$this->page_slug
3069
+		);
3070
+		return EEH_Template::display_template(
3071
+			EE_ADMIN_TEMPLATE . 'admin_details_legend.template.php',
3072
+			$this->_template_args,
3073
+			true
3074
+		);
3075
+	}
3076
+
3077
+
3078
+	/**
3079
+	 * This is used whenever we're DOING_AJAX to return a formatted json array that our calling javascript can expect
3080
+	 * The returned json object is created from an array in the following format:
3081
+	 * array(
3082
+	 *  'error' => FALSE, //(default FALSE), contains any errors and/or exceptions (exceptions return json early),
3083
+	 *  'success' => FALSE, //(default FALSE) - contains any special success message.
3084
+	 *  'notices' => '', // - contains any EE_Error formatted notices
3085
+	 *  'content' => 'string can be html', //this is a string of formatted content (can be html)
3086
+	 *  'data' => array() //this can be any key/value pairs that a method returns for later json parsing by the js.
3087
+	 *  We're also going to include the template args with every package (so js can pick out any specific template args
3088
+	 *  that might be included in here)
3089
+	 * )
3090
+	 * The json object is populated by whatever is set in the $_template_args property.
3091
+	 *
3092
+	 * @param bool  $sticky_notices    Used to indicate whether you want to ensure notices are added to a transient
3093
+	 *                                 instead of displayed.
3094
+	 * @param array $notices_arguments Use this to pass any additional args on to the _process_notices.
3095
+	 * @return void
3096
+	 * @throws EE_Error
3097
+	 * @throws InvalidArgumentException
3098
+	 * @throws InvalidDataTypeException
3099
+	 * @throws InvalidInterfaceException
3100
+	 */
3101
+	protected function _return_json($sticky_notices = false, $notices_arguments = [])
3102
+	{
3103
+		// make sure any EE_Error notices have been handled.
3104
+		$this->_process_notices($notices_arguments, true, $sticky_notices);
3105
+		$data = isset($this->_template_args['data']) ? $this->_template_args['data'] : [];
3106
+		unset($this->_template_args['data']);
3107
+		$json = [
3108
+			'error'     => isset($this->_template_args['error']) ? $this->_template_args['error'] : false,
3109
+			'success'   => isset($this->_template_args['success']) ? $this->_template_args['success'] : false,
3110
+			'errors'    => isset($this->_template_args['errors']) ? $this->_template_args['errors'] : false,
3111
+			'attention' => isset($this->_template_args['attention']) ? $this->_template_args['attention'] : false,
3112
+			'notices'   => EE_Error::get_notices(),
3113
+			'content'   => isset($this->_template_args['admin_page_content'])
3114
+				? $this->_template_args['admin_page_content'] : '',
3115
+			'data'      => array_merge($data, ['template_args' => $this->_template_args]),
3116
+			'isEEajax'  => true
3117
+			// special flag so any ajax.Success methods in js can identify this return package as a EEajax package.
3118
+		];
3119
+		// make sure there are no php errors or headers_sent.  Then we can set correct json header.
3120
+		if (null === error_get_last() || ! headers_sent()) {
3121
+			header('Content-Type: application/json; charset=UTF-8');
3122
+		}
3123
+		echo wp_json_encode($json);
3124
+		exit();
3125
+	}
3126
+
3127
+
3128
+	/**
3129
+	 * Simply a wrapper for the protected method so we can call this outside the class (ONLY when doing ajax)
3130
+	 *
3131
+	 * @return void
3132
+	 * @throws EE_Error
3133
+	 * @throws InvalidArgumentException
3134
+	 * @throws InvalidDataTypeException
3135
+	 * @throws InvalidInterfaceException
3136
+	 */
3137
+	public function return_json()
3138
+	{
3139
+		if ($this->request->isAjax()) {
3140
+			$this->_return_json();
3141
+		} else {
3142
+			throw new EE_Error(
3143
+				sprintf(
3144
+					esc_html__('The public %s method can only be called when DOING_AJAX = TRUE', 'event_espresso'),
3145
+					__FUNCTION__
3146
+				)
3147
+			);
3148
+		}
3149
+	}
3150
+
3151
+
3152
+	/**
3153
+	 * This provides a way for child hook classes to send along themselves by reference so methods/properties within
3154
+	 * them can be accessed by EE_Admin_child pages. This is assigned to the $_hook_obj property.
3155
+	 *
3156
+	 * @param EE_Admin_Hooks $hook_obj This will be the object for the EE_Admin_Hooks child
3157
+	 */
3158
+	public function set_hook_object(EE_Admin_Hooks $hook_obj)
3159
+	{
3160
+		$this->_hook_obj = $hook_obj;
3161
+	}
3162
+
3163
+
3164
+	/**
3165
+	 *        generates  HTML wrapper with Tabbed nav for an admin page
3166
+	 *
3167
+	 * @param boolean $about whether to use the special about page wrapper or default.
3168
+	 * @return void
3169
+	 * @throws DomainException
3170
+	 * @throws EE_Error
3171
+	 * @throws InvalidArgumentException
3172
+	 * @throws InvalidDataTypeException
3173
+	 * @throws InvalidInterfaceException
3174
+	 */
3175
+	public function admin_page_wrapper($about = false)
3176
+	{
3177
+		do_action('AHEE_log', __FILE__, __FUNCTION__, '');
3178
+		$this->_template_args['nav_tabs']         = $this->_get_main_nav_tabs();
3179
+		$this->_template_args['admin_page_title'] = $this->_admin_page_title;
3180
+
3181
+		$this->_template_args['before_admin_page_content'] = apply_filters(
3182
+			"FHEE_before_admin_page_content{$this->_current_page}{$this->_current_view}",
3183
+			$this->_template_args['before_admin_page_content'] ?? ''
3184
+		);
3185
+
3186
+		$this->_template_args['after_admin_page_content'] = apply_filters(
3187
+			"FHEE_after_admin_page_content{$this->_current_page}{$this->_current_view}",
3188
+			$this->_template_args['after_admin_page_content'] ?? ''
3189
+		);
3190
+		$this->_template_args['after_admin_page_content'] .= $this->_set_help_popup_content();
3191
+
3192
+		if ($this->request->isAjax()) {
3193
+			$this->_template_args['admin_page_content'] = EEH_Template::display_template(
3194
+			// $template_path,
3195
+				EE_ADMIN_TEMPLATE . 'admin_wrapper_ajax.template.php',
3196
+				$this->_template_args,
3197
+				true
3198
+			);
3199
+			$this->_return_json();
3200
+		}
3201
+		// load settings page wrapper template
3202
+		$template_path = $about
3203
+			? EE_ADMIN_TEMPLATE . 'about_admin_wrapper.template.php'
3204
+			: EE_ADMIN_TEMPLATE . 'admin_wrapper.template.php';
3205
+
3206
+		EEH_Template::display_template($template_path, $this->_template_args);
3207
+	}
3208
+
3209
+
3210
+	/**
3211
+	 * This returns the admin_nav tabs html using the configuration in the _nav_tabs property
3212
+	 *
3213
+	 * @return string html
3214
+	 * @throws EE_Error
3215
+	 */
3216
+	protected function _get_main_nav_tabs()
3217
+	{
3218
+		// let's generate the html using the EEH_Tabbed_Content helper.
3219
+		// We do this here so that it's possible for child classes to add in nav tabs dynamically at the last minute
3220
+		// (rather than setting in the page_routes array)
3221
+		return EEH_Tabbed_Content::display_admin_nav_tabs($this->_nav_tabs, $this->page_slug);
3222
+	}
3223
+
3224
+
3225
+	/**
3226
+	 *        sort nav tabs
3227
+	 *
3228
+	 * @param $a
3229
+	 * @param $b
3230
+	 * @return int
3231
+	 */
3232
+	private function _sort_nav_tabs($a, $b)
3233
+	{
3234
+		if ($a['order'] === $b['order']) {
3235
+			return 0;
3236
+		}
3237
+		return ($a['order'] < $b['order']) ? -1 : 1;
3238
+	}
3239
+
3240
+
3241
+	/**
3242
+	 * generates HTML for the forms used on admin pages
3243
+	 *
3244
+	 * @param array  $input_vars - array of input field details
3245
+	 * @param string $generator  indicates which generator to use: options are 'string' or 'array'
3246
+	 * @param bool   $id
3247
+	 * @return array|string
3248
+	 * @uses   EEH_Form_Fields::get_form_fields (/helper/EEH_Form_Fields.helper.php)
3249
+	 * @uses   EEH_Form_Fields::get_form_fields_array (/helper/EEH_Form_Fields.helper.php)
3250
+	 */
3251
+	protected function _generate_admin_form_fields($input_vars = [], $generator = 'string', $id = false)
3252
+	{
3253
+		return $generator === 'string'
3254
+			? EEH_Form_Fields::get_form_fields($input_vars, $id)
3255
+			: EEH_Form_Fields::get_form_fields_array($input_vars);
3256
+	}
3257
+
3258
+
3259
+	/**
3260
+	 * generates the "Save" and "Save & Close" buttons for edit forms
3261
+	 *
3262
+	 * @param bool             $both     if true then both buttons will be generated.  If false then just the "Save &
3263
+	 *                                   Close" button.
3264
+	 * @param array            $text     if included, generator will use the given text for the buttons ( array([0] =>
3265
+	 *                                   'Save', [1] => 'save & close')
3266
+	 * @param array            $actions  if included allows us to set the actions that each button will carry out (i.e.
3267
+	 *                                   via the "name" value in the button).  We can also use this to just dump
3268
+	 *                                   default actions by submitting some other value.
3269
+	 * @param bool|string|null $referrer if false then we just do the default action on save and close.  Other wise it
3270
+	 *                                   will use the $referrer string. IF null, then we don't do ANYTHING on save and
3271
+	 *                                   close (normal form handling).
3272
+	 */
3273
+	protected function _set_save_buttons($both = true, $text = [], $actions = [], $referrer = null)
3274
+	{
3275
+		// make sure $text and $actions are in an array
3276
+		$text          = (array) $text;
3277
+		$actions       = (array) $actions;
3278
+		$referrer_url  = ! empty($referrer) ? $referrer : $this->request->getServerParam('REQUEST_URI');
3279
+		$button_text   = ! empty($text)
3280
+			? $text
3281
+			: [
3282
+				esc_html__('Save', 'event_espresso'),
3283
+				esc_html__('Save and Close', 'event_espresso'),
3284
+			];
3285
+		$default_names = ['save', 'save_and_close'];
3286
+		$buttons       = '';
3287
+		foreach ($button_text as $key => $button) {
3288
+			$ref     = $default_names[ $key ];
3289
+			$name    = ! empty($actions) ? $actions[ $key ] : $ref;
3290
+			$buttons .= '<input type="submit" class="button button--primary ' . $ref . '" '
3291
+						. 'value="' . $button . '" name="' . $name . '" '
3292
+						. 'id="' . $this->_current_view . '_' . $ref . '" />';
3293
+			if (! $both) {
3294
+				break;
3295
+			}
3296
+		}
3297
+		// add in a hidden index for the current page (so save and close redirects properly)
3298
+		$buttons                              .= '<input type="hidden" id="save_and_close_referrer" name="save_and_close_referrer" value="'
3299
+												 . $referrer_url
3300
+												 . '" />';
3301
+		$this->_template_args['save_buttons'] = $buttons;
3302
+	}
3303
+
3304
+
3305
+	/**
3306
+	 * Wrapper for the protected function.  Allows plugins/addons to call this to set the form tags.
3307
+	 *
3308
+	 * @param string $route
3309
+	 * @param array  $additional_hidden_fields
3310
+	 * @see   $this->_set_add_edit_form_tags() for details on params
3311
+	 * @since 4.6.0
3312
+	 */
3313
+	public function set_add_edit_form_tags($route = '', $additional_hidden_fields = [])
3314
+	{
3315
+		$this->_set_add_edit_form_tags($route, $additional_hidden_fields);
3316
+	}
3317
+
3318
+
3319
+	/**
3320
+	 * set form open and close tags on add/edit pages.
3321
+	 *
3322
+	 * @param string $route                    the route you want the form to direct to
3323
+	 * @param array  $additional_hidden_fields any additional hidden fields required in the form header
3324
+	 * @return void
3325
+	 */
3326
+	protected function _set_add_edit_form_tags($route = '', $additional_hidden_fields = [])
3327
+	{
3328
+		if (empty($route)) {
3329
+			$user_msg = esc_html__(
3330
+				'An error occurred. No action was set for this page\'s form.',
3331
+				'event_espresso'
3332
+			);
3333
+			$dev_msg  = $user_msg . "\n"
3334
+						. sprintf(
3335
+							esc_html__('The $route argument is required for the %s->%s method.', 'event_espresso'),
3336
+							__FUNCTION__,
3337
+							__CLASS__
3338
+						);
3339
+			EE_Error::add_error($user_msg . '||' . $dev_msg, __FILE__, __FUNCTION__, __LINE__);
3340
+		}
3341
+		// open form
3342
+		$action                                            = $this->_admin_base_url;
3343
+		$this->_template_args['before_admin_page_content'] = "
3344 3344
             <form name='form' method='post' action='{$action}' id='{$route}_event_form' class='ee-admin-page-form' >
3345 3345
             ";
3346
-        // add nonce
3347
-        $nonce                                             =
3348
-            wp_nonce_field($route . '_nonce', $route . '_nonce', false, false);
3349
-        $this->_template_args['before_admin_page_content'] .= "\n\t" . $nonce;
3350
-        // add REQUIRED form action
3351
-        $hidden_fields = [
3352
-            'action' => ['type' => 'hidden', 'value' => $route],
3353
-        ];
3354
-        // merge arrays
3355
-        $hidden_fields = is_array($additional_hidden_fields)
3356
-            ? array_merge($hidden_fields, $additional_hidden_fields)
3357
-            : $hidden_fields;
3358
-        // generate form fields
3359
-        $form_fields = $this->_generate_admin_form_fields($hidden_fields, 'array');
3360
-        // add fields to form
3361
-        foreach ((array) $form_fields as $form_field) {
3362
-            $this->_template_args['before_admin_page_content'] .= "\n\t" . $form_field['field'];
3363
-        }
3364
-        // close form
3365
-        $this->_template_args['after_admin_page_content'] = '</form>';
3366
-    }
3367
-
3368
-
3369
-    /**
3370
-     * Public Wrapper for _redirect_after_action() method since its
3371
-     * discovered it would be useful for external code to have access.
3372
-     *
3373
-     * @param bool   $success
3374
-     * @param string $what
3375
-     * @param string $action_desc
3376
-     * @param array  $query_args
3377
-     * @param bool   $override_overwrite
3378
-     * @throws EE_Error
3379
-     * @see   EE_Admin_Page::_redirect_after_action() for params.
3380
-     * @since 4.5.0
3381
-     */
3382
-    public function redirect_after_action(
3383
-        $success = false,
3384
-        $what = 'item',
3385
-        $action_desc = 'processed',
3386
-        $query_args = [],
3387
-        $override_overwrite = false
3388
-    ) {
3389
-        $this->_redirect_after_action(
3390
-            $success,
3391
-            $what,
3392
-            $action_desc,
3393
-            $query_args,
3394
-            $override_overwrite
3395
-        );
3396
-    }
3397
-
3398
-
3399
-    /**
3400
-     * Helper method for merging existing request data with the returned redirect url.
3401
-     *
3402
-     * This is typically used for redirects after an action so that if the original view was a filtered view those
3403
-     * filters are still applied.
3404
-     *
3405
-     * @param array $new_route_data
3406
-     * @return array
3407
-     */
3408
-    protected function mergeExistingRequestParamsWithRedirectArgs(array $new_route_data)
3409
-    {
3410
-        foreach ($this->request->requestParams() as $ref => $value) {
3411
-            // unset nonces
3412
-            if (strpos($ref, 'nonce') !== false) {
3413
-                $this->request->unSetRequestParam($ref);
3414
-                continue;
3415
-            }
3416
-            // urlencode values.
3417
-            $value = is_array($value) ? array_map('urlencode', $value) : urlencode($value);
3418
-            $this->request->setRequestParam($ref, $value);
3419
-        }
3420
-        return array_merge($this->request->requestParams(), $new_route_data);
3421
-    }
3422
-
3423
-
3424
-    /**
3425
-     * @param int|float|string $success            - whether success was for two or more records, or just one, or none
3426
-     * @param string           $what               - what the action was performed on
3427
-     * @param string           $action_desc        - what was done ie: updated, deleted, etc
3428
-     * @param array            $query_args         - an array of query_args to be added to the URL to redirect to
3429
-     * @param BOOL             $override_overwrite - by default all EE_Error::success messages are overwritten,
3430
-     *                                             this allows you to override this so that they show.
3431
-     * @return void
3432
-     * @throws EE_Error
3433
-     * @throws InvalidArgumentException
3434
-     * @throws InvalidDataTypeException
3435
-     * @throws InvalidInterfaceException
3436
-     */
3437
-    protected function _redirect_after_action(
3438
-        $success = 0,
3439
-        string $what = 'item',
3440
-        string $action_desc = 'processed',
3441
-        array $query_args = [],
3442
-        bool $override_overwrite = false
3443
-    ) {
3444
-        do_action('AHEE_log', __FILE__, __FUNCTION__, '');
3445
-        $notices = EE_Error::get_notices(false);
3446
-        // overwrite default success messages //BUT ONLY if overwrite not overridden
3447
-        if (! $override_overwrite || ! empty($notices['errors'])) {
3448
-            EE_Error::overwrite_success();
3449
-        }
3450
-        if (! $override_overwrite && ! empty($what) && ! empty($action_desc) && empty($notices['errors'])) {
3451
-            // how many records affected ? more than one record ? or just one ?
3452
-            EE_Error::add_success(
3453
-                sprintf(
3454
-                    esc_html(
3455
-                        _n(
3456
-                            'The "%1$s" has been successfully %2$s.',
3457
-                            'The "%1$s" have been successfully %2$s.',
3458
-                            $success,
3459
-                            'event_espresso'
3460
-                        )
3461
-                    ),
3462
-                    $what,
3463
-                    $action_desc
3464
-                ),
3465
-                __FILE__,
3466
-                __FUNCTION__,
3467
-                __LINE__
3468
-            );
3469
-        }
3470
-        // check that $query_args isn't something crazy
3471
-        if (! is_array($query_args)) {
3472
-            $query_args = [];
3473
-        }
3474
-        /**
3475
-         * Allow injecting actions before the query_args are modified for possible different
3476
-         * redirections on save and close actions
3477
-         *
3478
-         * @param array $query_args       The original query_args array coming into the
3479
-         *                                method.
3480
-         * @since 4.2.0
3481
-         */
3482
-        do_action(
3483
-            "AHEE__{$this->class_name}___redirect_after_action__before_redirect_modification_{$this->_req_action}",
3484
-            $query_args
3485
-        );
3486
-        // set redirect url.
3487
-        // Note if there is a "page" index in the $query_args then we go with vanilla admin.php route,
3488
-        // otherwise we go with whatever is set as the _admin_base_url
3489
-        $redirect_url = isset($query_args['page']) ? admin_url('admin.php') : $this->_admin_base_url;
3490
-        // calculate where we're going (if we have a "save and close" button pushed)
3491
-        if (
3492
-            $this->request->requestParamIsSet('save_and_close')
3493
-            && $this->request->requestParamIsSet('save_and_close_referrer')
3494
-        ) {
3495
-            // even though we have the save_and_close referrer, we need to parse the url for the action in order to generate a nonce
3496
-            $parsed_url = parse_url($this->request->getRequestParam('save_and_close_referrer', '', 'url'));
3497
-            // regenerate query args array from referrer URL
3498
-            parse_str($parsed_url['query'], $query_args);
3499
-            // correct page and action will be in the query args now
3500
-            $redirect_url = admin_url('admin.php');
3501
-        }
3502
-        // merge any default query_args set in _default_route_query_args property
3503
-        if (! empty($this->_default_route_query_args) && ! $this->_is_UI_request) {
3504
-            $args_to_merge = [];
3505
-            foreach ($this->_default_route_query_args as $query_param => $query_value) {
3506
-                // is there a wp_referer array in our _default_route_query_args property?
3507
-                if ($query_param === 'wp_referer') {
3508
-                    $query_value = (array) $query_value;
3509
-                    foreach ($query_value as $reference => $value) {
3510
-                        if (strpos($reference, 'nonce') !== false) {
3511
-                            continue;
3512
-                        }
3513
-                        // finally we will override any arguments in the referer with
3514
-                        // what might be set on the _default_route_query_args array.
3515
-                        if (isset($this->_default_route_query_args[ $reference ])) {
3516
-                            $args_to_merge[ $reference ] = urlencode($this->_default_route_query_args[ $reference ]);
3517
-                        } else {
3518
-                            $args_to_merge[ $reference ] = urlencode($value);
3519
-                        }
3520
-                    }
3521
-                    continue;
3522
-                }
3523
-                $args_to_merge[ $query_param ] = $query_value;
3524
-            }
3525
-            // now let's merge these arguments but override with what was specifically sent in to the
3526
-            // redirect.
3527
-            $query_args = array_merge($args_to_merge, $query_args);
3528
-        }
3529
-        $this->_process_notices($query_args);
3530
-        // generate redirect url
3531
-        // if redirecting to anything other than the main page, add a nonce
3532
-        if (isset($query_args['action'])) {
3533
-            // manually generate wp_nonce and merge that with the query vars
3534
-            // becuz the wp_nonce_url function wrecks havoc on some vars
3535
-            $query_args['_wpnonce'] = wp_create_nonce($query_args['action'] . '_nonce');
3536
-        }
3537
-        // we're adding some hooks and filters in here for processing any things just before redirects
3538
-        // (example: an admin page has done an insert or update and we want to run something after that).
3539
-        do_action('AHEE_redirect_' . $this->class_name . $this->_req_action, $query_args);
3540
-        $redirect_url = apply_filters(
3541
-            'FHEE_redirect_' . $this->class_name . $this->_req_action,
3542
-            EE_Admin_Page::add_query_args_and_nonce($query_args, $redirect_url),
3543
-            $query_args
3544
-        );
3545
-        // check if we're doing ajax.  If we are then lets just return the results and js can handle how it wants.
3546
-        if ($this->request->isAjax()) {
3547
-            $default_data                    = [
3548
-                'close'        => true,
3549
-                'redirect_url' => $redirect_url,
3550
-                'where'        => 'main',
3551
-                'what'         => 'append',
3552
-            ];
3553
-            $this->_template_args['success'] = $success;
3554
-            $this->_template_args['data']    = ! empty($this->_template_args['data']) ? array_merge(
3555
-                $default_data,
3556
-                $this->_template_args['data']
3557
-            ) : $default_data;
3558
-            $this->_return_json();
3559
-        }
3560
-        wp_safe_redirect($redirect_url);
3561
-        exit();
3562
-    }
3563
-
3564
-
3565
-    /**
3566
-     * process any notices before redirecting (or returning ajax request)
3567
-     * This method sets the $this->_template_args['notices'] attribute;
3568
-     *
3569
-     * @param array $query_args         any query args that need to be used for notice transient ('action')
3570
-     * @param bool  $skip_route_verify  This is typically used when we are processing notices REALLY early and
3571
-     *                                  page_routes haven't been defined yet.
3572
-     * @param bool  $sticky_notices     This is used to flag that regardless of whether this is doing_ajax or not, we
3573
-     *                                  still save a transient for the notice.
3574
-     * @return void
3575
-     * @throws EE_Error
3576
-     * @throws InvalidArgumentException
3577
-     * @throws InvalidDataTypeException
3578
-     * @throws InvalidInterfaceException
3579
-     */
3580
-    protected function _process_notices($query_args = [], $skip_route_verify = false, $sticky_notices = true)
3581
-    {
3582
-        // first let's set individual error properties if doing_ajax and the properties aren't already set.
3583
-        if ($this->request->isAjax()) {
3584
-            $notices = EE_Error::get_notices(false);
3585
-            if (empty($this->_template_args['success'])) {
3586
-                $this->_template_args['success'] = isset($notices['success']) ? $notices['success'] : false;
3587
-            }
3588
-            if (empty($this->_template_args['errors'])) {
3589
-                $this->_template_args['errors'] = isset($notices['errors']) ? $notices['errors'] : false;
3590
-            }
3591
-            if (empty($this->_template_args['attention'])) {
3592
-                $this->_template_args['attention'] = isset($notices['attention']) ? $notices['attention'] : false;
3593
-            }
3594
-        }
3595
-        $this->_template_args['notices'] = EE_Error::get_notices();
3596
-        // IF this isn't ajax we need to create a transient for the notices using the route (however, overridden if $sticky_notices == true)
3597
-        if (! $this->request->isAjax() || $sticky_notices) {
3598
-            $route = isset($query_args['action']) ? $query_args['action'] : 'default';
3599
-            $this->_add_transient(
3600
-                $route,
3601
-                $this->_template_args['notices'],
3602
-                true,
3603
-                $skip_route_verify
3604
-            );
3605
-        }
3606
-    }
3607
-
3608
-
3609
-    /**
3610
-     * get_action_link_or_button
3611
-     * returns the button html for adding, editing, or deleting an item (depending on given type)
3612
-     *
3613
-     * @param string $action        use this to indicate which action the url is generated with.
3614
-     * @param string $type          accepted strings must be defined in the $_labels['button'] array(as the key)
3615
-     *                              property.
3616
-     * @param array  $extra_request if the button requires extra params you can include them in $key=>$value pairs.
3617
-     * @param string $class         Use this to give the class for the button. Defaults to 'button--primary'
3618
-     * @param string $base_url      If this is not provided
3619
-     *                              the _admin_base_url will be used as the default for the button base_url.
3620
-     *                              Otherwise this value will be used.
3621
-     * @param bool   $exclude_nonce If true then no nonce will be in the generated button link.
3622
-     * @return string
3623
-     * @throws InvalidArgumentException
3624
-     * @throws InvalidInterfaceException
3625
-     * @throws InvalidDataTypeException
3626
-     * @throws EE_Error
3627
-     */
3628
-    public function get_action_link_or_button(
3629
-        $action,
3630
-        $type = 'add',
3631
-        $extra_request = [],
3632
-        $class = 'button button--primary',
3633
-        $base_url = '',
3634
-        $exclude_nonce = false
3635
-    ) {
3636
-        // first let's validate the action (if $base_url is FALSE otherwise validation will happen further along)
3637
-        if (empty($base_url) && ! isset($this->_page_routes[ $action ])) {
3638
-            throw new EE_Error(
3639
-                sprintf(
3640
-                    esc_html__(
3641
-                        'There is no page route for given action for the button.  This action was given: %s',
3642
-                        'event_espresso'
3643
-                    ),
3644
-                    $action
3645
-                )
3646
-            );
3647
-        }
3648
-        if (! isset($this->_labels['buttons'][ $type ])) {
3649
-            throw new EE_Error(
3650
-                sprintf(
3651
-                    esc_html__(
3652
-                        'There is no label for the given button type (%s). Labels are set in the <code>_page_config</code> property.',
3653
-                        'event_espresso'
3654
-                    ),
3655
-                    $type
3656
-                )
3657
-            );
3658
-        }
3659
-        // finally check user access for this button.
3660
-        $has_access = $this->check_user_access($action, true);
3661
-        if (! $has_access) {
3662
-            return '';
3663
-        }
3664
-        $_base_url  = ! $base_url ? $this->_admin_base_url : $base_url;
3665
-        $query_args = [
3666
-            'action' => $action,
3667
-        ];
3668
-        // merge extra_request args but make sure our original action takes precedence and doesn't get overwritten.
3669
-        if (! empty($extra_request)) {
3670
-            $query_args = array_merge($extra_request, $query_args);
3671
-        }
3672
-        $url = EE_Admin_Page::add_query_args_and_nonce($query_args, $_base_url, false, $exclude_nonce);
3673
-        return EEH_Template::get_button_or_link($url, $this->_labels['buttons'][ $type ], $class);
3674
-    }
3675
-
3676
-
3677
-    /**
3678
-     * _per_page_screen_option
3679
-     * Utility function for adding in a per_page_option in the screen_options_dropdown.
3680
-     *
3681
-     * @return void
3682
-     * @throws InvalidArgumentException
3683
-     * @throws InvalidInterfaceException
3684
-     * @throws InvalidDataTypeException
3685
-     */
3686
-    protected function _per_page_screen_option()
3687
-    {
3688
-        $option = 'per_page';
3689
-        $args   = [
3690
-            'label'   => apply_filters(
3691
-                'FHEE__EE_Admin_Page___per_page_screen_options___label',
3692
-                $this->_admin_page_title,
3693
-                $this
3694
-            ),
3695
-            'default' => (int) apply_filters(
3696
-                'FHEE__EE_Admin_Page___per_page_screen_options__default',
3697
-                20
3698
-            ),
3699
-            'option'  => $this->_current_page . '_' . $this->_current_view . '_per_page',
3700
-        ];
3701
-        // ONLY add the screen option if the user has access to it.
3702
-        if ($this->check_user_access($this->_current_view, true)) {
3703
-            add_screen_option($option, $args);
3704
-        }
3705
-    }
3706
-
3707
-
3708
-    /**
3709
-     * set_per_page_screen_option
3710
-     * All this does is make sure that WordPress saves any per_page screen options (if set) for the current page.
3711
-     * we have to do this rather than running inside the 'set-screen-options' hook because it runs earlier than
3712
-     * admin_menu.
3713
-     *
3714
-     * @return void
3715
-     */
3716
-    private function _set_per_page_screen_options()
3717
-    {
3718
-        if ($this->request->requestParamIsSet('wp_screen_options')) {
3719
-            check_admin_referer('screen-options-nonce', 'screenoptionnonce');
3720
-            if (! $user = wp_get_current_user()) {
3721
-                return;
3722
-            }
3723
-            $option = $this->request->getRequestParam('wp_screen_options[option]', '', 'key');
3724
-            if (! $option) {
3725
-                return;
3726
-            }
3727
-            $value      = $this->request->getRequestParam('wp_screen_options[value]', 0, 'int');
3728
-            $map_option = $option;
3729
-            $option     = str_replace('-', '_', $option);
3730
-            switch ($map_option) {
3731
-                case $this->_current_page . '_' . $this->_current_view . '_per_page':
3732
-                    $max_value = apply_filters(
3733
-                        'FHEE__EE_Admin_Page___set_per_page_screen_options__max_value',
3734
-                        999,
3735
-                        $this->_current_page,
3736
-                        $this->_current_view
3737
-                    );
3738
-                    if ($value < 1) {
3739
-                        return;
3740
-                    }
3741
-                    $value = min($value, $max_value);
3742
-                    break;
3743
-                default:
3744
-                    $value = apply_filters(
3745
-                        'FHEE__EE_Admin_Page___set_per_page_screen_options__value',
3746
-                        false,
3747
-                        $option,
3748
-                        $value
3749
-                    );
3750
-                    if (false === $value) {
3751
-                        return;
3752
-                    }
3753
-                    break;
3754
-            }
3755
-            update_user_meta($user->ID, $option, $value);
3756
-            wp_safe_redirect(remove_query_arg(['pagenum', 'apage', 'paged'], wp_get_referer()));
3757
-            exit;
3758
-        }
3759
-    }
3760
-
3761
-
3762
-    /**
3763
-     * This just allows for setting the $_template_args property if it needs to be set outside the object
3764
-     *
3765
-     * @param array $data array that will be assigned to template args.
3766
-     */
3767
-    public function set_template_args($data)
3768
-    {
3769
-        $this->_template_args = array_merge($this->_template_args, (array) $data);
3770
-    }
3771
-
3772
-
3773
-    /**
3774
-     * This makes available the WP transient system for temporarily moving data between routes
3775
-     *
3776
-     * @param string $route             the route that should receive the transient
3777
-     * @param array  $data              the data that gets sent
3778
-     * @param bool   $notices           If this is for notices then we use this to indicate so, otherwise its just a
3779
-     *                                  normal route transient.
3780
-     * @param bool   $skip_route_verify Used to indicate we want to skip route verification.  This is usually ONLY used
3781
-     *                                  when we are adding a transient before page_routes have been defined.
3782
-     * @return void
3783
-     * @throws EE_Error
3784
-     */
3785
-    protected function _add_transient($route, $data, $notices = false, $skip_route_verify = false)
3786
-    {
3787
-        $user_id = get_current_user_id();
3788
-        if (! $skip_route_verify) {
3789
-            $this->_verify_route($route);
3790
-        }
3791
-        // now let's set the string for what kind of transient we're setting
3792
-        $transient = $notices
3793
-            ? 'ee_rte_n_tx_' . $route . '_' . $user_id
3794
-            : 'rte_tx_' . $route . '_' . $user_id;
3795
-        $data      = $notices ? ['notices' => $data] : $data;
3796
-        // is there already a transient for this route?  If there is then let's ADD to that transient
3797
-        $existing = is_multisite() && is_network_admin()
3798
-            ? get_site_transient($transient)
3799
-            : get_transient($transient);
3800
-        if ($existing) {
3801
-            $data = array_merge((array) $data, (array) $existing);
3802
-        }
3803
-        if (is_multisite() && is_network_admin()) {
3804
-            set_site_transient($transient, $data, 8);
3805
-        } else {
3806
-            set_transient($transient, $data, 8);
3807
-        }
3808
-    }
3809
-
3810
-
3811
-    /**
3812
-     * this retrieves the temporary transient that has been set for moving data between routes.
3813
-     *
3814
-     * @param bool   $notices true we get notices transient. False we just return normal route transient
3815
-     * @param string $route
3816
-     * @return mixed data
3817
-     */
3818
-    protected function _get_transient($notices = false, $route = '')
3819
-    {
3820
-        $user_id   = get_current_user_id();
3821
-        $route     = ! $route ? $this->_req_action : $route;
3822
-        $transient = $notices
3823
-            ? 'ee_rte_n_tx_' . $route . '_' . $user_id
3824
-            : 'rte_tx_' . $route . '_' . $user_id;
3825
-        $data      = is_multisite() && is_network_admin()
3826
-            ? get_site_transient($transient)
3827
-            : get_transient($transient);
3828
-        // delete transient after retrieval (just in case it hasn't expired);
3829
-        if (is_multisite() && is_network_admin()) {
3830
-            delete_site_transient($transient);
3831
-        } else {
3832
-            delete_transient($transient);
3833
-        }
3834
-        return $notices && isset($data['notices']) ? $data['notices'] : $data;
3835
-    }
3836
-
3837
-
3838
-    /**
3839
-     * The purpose of this method is just to run garbage collection on any EE transients that might have expired but
3840
-     * would not be called later. This will be assigned to run on a specific EE Admin page. (place the method in the
3841
-     * default route callback on the EE_Admin page you want it run.)
3842
-     *
3843
-     * @return void
3844
-     */
3845
-    protected function _transient_garbage_collection()
3846
-    {
3847
-        global $wpdb;
3848
-        // retrieve all existing transients
3849
-        $query =
3850
-            "SELECT option_name FROM {$wpdb->options} WHERE option_name LIKE '%rte_tx_%' OR option_name LIKE '%rte_n_tx_%'";
3851
-        if ($results = $wpdb->get_results($query)) {
3852
-            foreach ($results as $result) {
3853
-                $transient = str_replace('_transient_', '', $result->option_name);
3854
-                get_transient($transient);
3855
-                if (is_multisite() && is_network_admin()) {
3856
-                    get_site_transient($transient);
3857
-                }
3858
-            }
3859
-        }
3860
-    }
3861
-
3862
-
3863
-    /**
3864
-     * get_view
3865
-     *
3866
-     * @return string content of _view property
3867
-     */
3868
-    public function get_view()
3869
-    {
3870
-        return $this->_view;
3871
-    }
3872
-
3873
-
3874
-    /**
3875
-     * getter for the protected $_views property
3876
-     *
3877
-     * @return array
3878
-     */
3879
-    public function get_views()
3880
-    {
3881
-        return $this->_views;
3882
-    }
3883
-
3884
-
3885
-    /**
3886
-     * get_current_page
3887
-     *
3888
-     * @return string _current_page property value
3889
-     */
3890
-    public function get_current_page()
3891
-    {
3892
-        return $this->_current_page;
3893
-    }
3894
-
3895
-
3896
-    /**
3897
-     * get_current_view
3898
-     *
3899
-     * @return string _current_view property value
3900
-     */
3901
-    public function get_current_view()
3902
-    {
3903
-        return $this->_current_view;
3904
-    }
3905
-
3906
-
3907
-    /**
3908
-     * get_current_screen
3909
-     *
3910
-     * @return object The current WP_Screen object
3911
-     */
3912
-    public function get_current_screen()
3913
-    {
3914
-        return $this->_current_screen;
3915
-    }
3916
-
3917
-
3918
-    /**
3919
-     * get_current_page_view_url
3920
-     *
3921
-     * @return string This returns the url for the current_page_view.
3922
-     */
3923
-    public function get_current_page_view_url()
3924
-    {
3925
-        return $this->_current_page_view_url;
3926
-    }
3927
-
3928
-
3929
-    /**
3930
-     * just returns the Request
3931
-     *
3932
-     * @return RequestInterface
3933
-     */
3934
-    public function get_request()
3935
-    {
3936
-        return $this->request;
3937
-    }
3938
-
3939
-
3940
-    /**
3941
-     * just returns the _req_data property
3942
-     *
3943
-     * @return array
3944
-     */
3945
-    public function get_request_data()
3946
-    {
3947
-        return $this->request->requestParams();
3948
-    }
3949
-
3950
-
3951
-    /**
3952
-     * returns the _req_data protected property
3953
-     *
3954
-     * @return string
3955
-     */
3956
-    public function get_req_action()
3957
-    {
3958
-        return $this->_req_action;
3959
-    }
3960
-
3961
-
3962
-    /**
3963
-     * @return bool  value of $_is_caf property
3964
-     */
3965
-    public function is_caf()
3966
-    {
3967
-        return $this->_is_caf;
3968
-    }
3969
-
3970
-
3971
-    /**
3972
-     * @return mixed
3973
-     */
3974
-    public function default_espresso_metaboxes()
3975
-    {
3976
-        return $this->_default_espresso_metaboxes;
3977
-    }
3978
-
3979
-
3980
-    /**
3981
-     * @return mixed
3982
-     */
3983
-    public function admin_base_url()
3984
-    {
3985
-        return $this->_admin_base_url;
3986
-    }
3987
-
3988
-
3989
-    /**
3990
-     * @return mixed
3991
-     */
3992
-    public function wp_page_slug()
3993
-    {
3994
-        return $this->_wp_page_slug;
3995
-    }
3996
-
3997
-
3998
-    /**
3999
-     * updates  espresso configuration settings
4000
-     *
4001
-     * @param string                   $tab
4002
-     * @param EE_Config_Base|EE_Config $config
4003
-     * @param string                   $file file where error occurred
4004
-     * @param string                   $func function  where error occurred
4005
-     * @param string                   $line line no where error occurred
4006
-     * @return boolean
4007
-     */
4008
-    protected function _update_espresso_configuration($tab, $config, $file = '', $func = '', $line = '')
4009
-    {
4010
-        // remove any options that are NOT going to be saved with the config settings.
4011
-        if (isset($config->core->ee_ueip_optin)) {
4012
-            // TODO: remove the following two lines and make sure values are migrated from 3.1
4013
-            update_option('ee_ueip_optin', $config->core->ee_ueip_optin);
4014
-            update_option('ee_ueip_has_notified', true);
4015
-        }
4016
-        // and save it (note we're also doing the network save here)
4017
-        $net_saved    = ! is_main_site() || EE_Network_Config::instance()->update_config(false, false);
4018
-        $config_saved = EE_Config::instance()->update_espresso_config(false, false);
4019
-        if ($config_saved && $net_saved) {
4020
-            EE_Error::add_success(sprintf(esc_html__('"%s" have been successfully updated.', 'event_espresso'), $tab));
4021
-            return true;
4022
-        }
4023
-        EE_Error::add_error(
4024
-            sprintf(esc_html__('The "%s" were not updated.', 'event_espresso'), $tab),
4025
-            $file,
4026
-            $func,
4027
-            $line
4028
-        );
4029
-        return false;
4030
-    }
4031
-
4032
-
4033
-    /**
4034
-     * Returns an array to be used for EE_FOrm_Fields.helper.php's select_input as the $values argument.
4035
-     *
4036
-     * @return array
4037
-     */
4038
-    public function get_yes_no_values()
4039
-    {
4040
-        return $this->_yes_no_values;
4041
-    }
4042
-
4043
-
4044
-    /**
4045
-     * @return string
4046
-     * @throws ReflectionException
4047
-     * @since 5.0.0.p
4048
-     */
4049
-    protected function _get_dir()
4050
-    {
4051
-        $reflector = new ReflectionClass($this->class_name);
4052
-        return dirname($reflector->getFileName());
4053
-    }
4054
-
4055
-
4056
-    /**
4057
-     * A helper for getting a "next link".
4058
-     *
4059
-     * @param string $url   The url to link to
4060
-     * @param string $class The class to use.
4061
-     * @return string
4062
-     */
4063
-    protected function _next_link($url, $class = 'dashicons dashicons-arrow-right')
4064
-    {
4065
-        return '<a class="' . $class . '" href="' . $url . '"></a>';
4066
-    }
4067
-
4068
-
4069
-    /**
4070
-     * A helper for getting a "previous link".
4071
-     *
4072
-     * @param string $url   The url to link to
4073
-     * @param string $class The class to use.
4074
-     * @return string
4075
-     */
4076
-    protected function _previous_link($url, $class = 'dashicons dashicons-arrow-left')
4077
-    {
4078
-        return '<a class="' . $class . '" href="' . $url . '"></a>';
4079
-    }
4080
-
4081
-
4082
-
4083
-
4084
-
4085
-
4086
-
4087
-    // below are some messages related methods that should be available across the EE_Admin system.  Note, these methods are NOT page specific
4088
-
4089
-
4090
-    /**
4091
-     * This processes an request to resend a registration and assumes we have a _REG_ID for doing so. So if the caller
4092
-     * knows that the _REG_ID isn't in the req_data array but CAN obtain it, the caller should ADD the _REG_ID to the
4093
-     * _req_data array.
4094
-     *
4095
-     * @return bool success/fail
4096
-     * @throws EE_Error
4097
-     * @throws InvalidArgumentException
4098
-     * @throws ReflectionException
4099
-     * @throws InvalidDataTypeException
4100
-     * @throws InvalidInterfaceException
4101
-     */
4102
-    protected function _process_resend_registration()
4103
-    {
4104
-        $this->_template_args['success'] = EED_Messages::process_resend($this->_req_data);
4105
-        do_action(
4106
-            'AHEE__EE_Admin_Page___process_resend_registration',
4107
-            $this->_template_args['success'],
4108
-            $this->request->requestParams()
4109
-        );
4110
-        return $this->_template_args['success'];
4111
-    }
4112
-
4113
-
4114
-    /**
4115
-     * This automatically processes any payment message notifications when manual payment has been applied.
4116
-     *
4117
-     * @param EE_Payment $payment
4118
-     * @return bool success/fail
4119
-     */
4120
-    protected function _process_payment_notification(EE_Payment $payment)
4121
-    {
4122
-        add_filter('FHEE__EE_Payment_Processor__process_registration_payments__display_notifications', '__return_true');
4123
-        do_action('AHEE__EE_Admin_Page___process_admin_payment_notification', $payment);
4124
-        $this->_template_args['success'] = apply_filters(
4125
-            'FHEE__EE_Admin_Page___process_admin_payment_notification__success',
4126
-            false,
4127
-            $payment
4128
-        );
4129
-        return $this->_template_args['success'];
4130
-    }
4131
-
4132
-
4133
-    /**
4134
-     * @param EEM_Base      $entity_model
4135
-     * @param string        $entity_PK_name name of the primary key field used as a request param, ie: id, ID, etc
4136
-     * @param string        $action         one of the EE_Admin_List_Table::ACTION_* constants: delete, restore, trash
4137
-     * @param string        $delete_column  name of the field that denotes whether entity is trashed
4138
-     * @param callable|null $callback       called after entity is trashed, restored, or deleted
4139
-     * @return int|float
4140
-     * @throws EE_Error
4141
-     */
4142
-    protected function trashRestoreDeleteEntities(
4143
-        EEM_Base $entity_model,
4144
-        $entity_PK_name,
4145
-        $action = EE_Admin_List_Table::ACTION_DELETE,
4146
-        $delete_column = '',
4147
-        callable $callback = null
4148
-    ) {
4149
-        $entity_PK      = $entity_model->get_primary_key_field();
4150
-        $entity_PK_name = $entity_PK_name ?: $entity_PK->get_name();
4151
-        $entity_PK_type = $this->resolveEntityFieldDataType($entity_PK);
4152
-        // grab ID if deleting a single entity
4153
-        if ($this->request->requestParamIsSet($entity_PK_name)) {
4154
-            $ID = $this->request->getRequestParam($entity_PK_name, 0, $entity_PK_type);
4155
-            return $this->trashRestoreDeleteEntity($entity_model, $ID, $action, $delete_column, $callback) ? 1 : 0;
4156
-        }
4157
-        // or grab checkbox array if bulk deleting
4158
-        $checkboxes = $this->request->getRequestParam('checkbox', [], $entity_PK_type, true);
4159
-        if (empty($checkboxes)) {
4160
-            return 0;
4161
-        }
4162
-        $success = 0;
4163
-        $IDs     = array_keys($checkboxes);
4164
-        // cycle thru bulk action checkboxes
4165
-        foreach ($IDs as $ID) {
4166
-            // increment $success
4167
-            if ($this->trashRestoreDeleteEntity($entity_model, $ID, $action, $delete_column, $callback)) {
4168
-                $success++;
4169
-            }
4170
-        }
4171
-        $count = (int) count($checkboxes);
4172
-        // if multiple entities were deleted successfully, then $deleted will be full count of deletions,
4173
-        // otherwise it will be a fraction of ( actual deletions / total entities to be deleted )
4174
-        return $success === $count ? $count : $success / $count;
4175
-    }
4176
-
4177
-
4178
-    /**
4179
-     * @param EE_Primary_Key_Field_Base $entity_PK
4180
-     * @return string
4181
-     * @throws EE_Error
4182
-     * @since   4.10.30.p
4183
-     */
4184
-    private function resolveEntityFieldDataType(EE_Primary_Key_Field_Base $entity_PK)
4185
-    {
4186
-        $entity_PK_type = $entity_PK->getSchemaType();
4187
-        switch ($entity_PK_type) {
4188
-            case 'boolean':
4189
-                return 'bool';
4190
-            case 'integer':
4191
-                return 'int';
4192
-            case 'number':
4193
-                return 'float';
4194
-            case 'string':
4195
-                return 'string';
4196
-        }
4197
-        throw new RuntimeException(
4198
-            sprintf(
4199
-                esc_html__(
4200
-                    '"%1$s" is an invalid schema type for the %2$s primary key.',
4201
-                    'event_espresso'
4202
-                ),
4203
-                $entity_PK_type,
4204
-                $entity_PK->get_name()
4205
-            )
4206
-        );
4207
-    }
4208
-
4209
-
4210
-    /**
4211
-     * @param EEM_Base      $entity_model
4212
-     * @param int|string    $entity_ID
4213
-     * @param string        $action        one of the EE_Admin_List_Table::ACTION_* constants: delete, restore, trash
4214
-     * @param string        $delete_column name of the field that denotes whether entity is trashed
4215
-     * @param callable|null $callback      called after entity is trashed, restored, or deleted
4216
-     * @return bool
4217
-     */
4218
-    protected function trashRestoreDeleteEntity(
4219
-        EEM_Base $entity_model,
4220
-        $entity_ID,
4221
-        string $action,
4222
-        string $delete_column,
4223
-        ?callable $callback = null
4224
-    ): bool {
4225
-        $entity_ID = absint($entity_ID);
4226
-        if (! $entity_ID) {
4227
-            $this->trashRestoreDeleteError($action, $entity_model);
4228
-        }
4229
-        $result = 0;
4230
-        try {
4231
-            $entity = $entity_model->get_one_by_ID($entity_ID);
4232
-            if (! $entity instanceof EE_Base_Class) {
4233
-                throw new DomainException(
4234
-                    sprintf(
4235
-                        esc_html__(
4236
-                            'Missing or invalid %1$s entity with ID of "%2$s" returned from db.',
4237
-                            'event_espresso'
4238
-                        ),
4239
-                        str_replace('EEM_', '', $entity_model->get_this_model_name()),
4240
-                        $entity_ID
4241
-                    )
4242
-                );
4243
-            }
4244
-            switch ($action) {
4245
-                case EE_Admin_List_Table::ACTION_DELETE:
4246
-                    $result = (bool) $entity->delete_permanently();
4247
-                    break;
4248
-                case EE_Admin_List_Table::ACTION_RESTORE:
4249
-                    $result = $entity->delete_or_restore(false);
4250
-                    break;
4251
-                case EE_Admin_List_Table::ACTION_TRASH:
4252
-                    $result = $entity->delete_or_restore();
4253
-                    break;
4254
-            }
4255
-        } catch (Exception $exception) {
4256
-            $this->trashRestoreDeleteError($action, $entity_model, $exception);
4257
-        }
4258
-        if (is_callable($callback)) {
4259
-            call_user_func_array($callback, [$entity_model, $entity_ID, $action, $result, $delete_column]);
4260
-        }
4261
-        return $result;
4262
-    }
4263
-
4264
-
4265
-    /**
4266
-     * @param EEM_Base $entity_model
4267
-     * @param string   $delete_column
4268
-     * @since 4.10.30.p
4269
-     */
4270
-    private function validateDeleteColumn(EEM_Base $entity_model, $delete_column)
4271
-    {
4272
-        if (empty($delete_column)) {
4273
-            throw new DomainException(
4274
-                sprintf(
4275
-                    esc_html__(
4276
-                        'You need to specify the name of the "delete column" on the %2$s model, in order to trash or restore an entity.',
4277
-                        'event_espresso'
4278
-                    ),
4279
-                    $entity_model->get_this_model_name()
4280
-                )
4281
-            );
4282
-        }
4283
-        if (! $entity_model->has_field($delete_column)) {
4284
-            throw new DomainException(
4285
-                sprintf(
4286
-                    esc_html__(
4287
-                        'The %1$s field does not exist on the %2$s model.',
4288
-                        'event_espresso'
4289
-                    ),
4290
-                    $delete_column,
4291
-                    $entity_model->get_this_model_name()
4292
-                )
4293
-            );
4294
-        }
4295
-    }
4296
-
4297
-
4298
-    /**
4299
-     * @param EEM_Base       $entity_model
4300
-     * @param Exception|null $exception
4301
-     * @param string         $action
4302
-     * @since 4.10.30.p
4303
-     */
4304
-    private function trashRestoreDeleteError($action, EEM_Base $entity_model, Exception $exception = null)
4305
-    {
4306
-        if ($exception instanceof Exception) {
4307
-            throw new RuntimeException(
4308
-                sprintf(
4309
-                    esc_html__(
4310
-                        'Could not %1$s the %2$s because the following error occurred: %3$s',
4311
-                        'event_espresso'
4312
-                    ),
4313
-                    $action,
4314
-                    $entity_model->get_this_model_name(),
4315
-                    $exception->getMessage()
4316
-                )
4317
-            );
4318
-        }
4319
-        throw new RuntimeException(
4320
-            sprintf(
4321
-                esc_html__(
4322
-                    'Could not %1$s the %2$s because an invalid ID was received.',
4323
-                    'event_espresso'
4324
-                ),
4325
-                $action,
4326
-                $entity_model->get_this_model_name()
4327
-            )
4328
-        );
4329
-    }
3346
+		// add nonce
3347
+		$nonce                                             =
3348
+			wp_nonce_field($route . '_nonce', $route . '_nonce', false, false);
3349
+		$this->_template_args['before_admin_page_content'] .= "\n\t" . $nonce;
3350
+		// add REQUIRED form action
3351
+		$hidden_fields = [
3352
+			'action' => ['type' => 'hidden', 'value' => $route],
3353
+		];
3354
+		// merge arrays
3355
+		$hidden_fields = is_array($additional_hidden_fields)
3356
+			? array_merge($hidden_fields, $additional_hidden_fields)
3357
+			: $hidden_fields;
3358
+		// generate form fields
3359
+		$form_fields = $this->_generate_admin_form_fields($hidden_fields, 'array');
3360
+		// add fields to form
3361
+		foreach ((array) $form_fields as $form_field) {
3362
+			$this->_template_args['before_admin_page_content'] .= "\n\t" . $form_field['field'];
3363
+		}
3364
+		// close form
3365
+		$this->_template_args['after_admin_page_content'] = '</form>';
3366
+	}
3367
+
3368
+
3369
+	/**
3370
+	 * Public Wrapper for _redirect_after_action() method since its
3371
+	 * discovered it would be useful for external code to have access.
3372
+	 *
3373
+	 * @param bool   $success
3374
+	 * @param string $what
3375
+	 * @param string $action_desc
3376
+	 * @param array  $query_args
3377
+	 * @param bool   $override_overwrite
3378
+	 * @throws EE_Error
3379
+	 * @see   EE_Admin_Page::_redirect_after_action() for params.
3380
+	 * @since 4.5.0
3381
+	 */
3382
+	public function redirect_after_action(
3383
+		$success = false,
3384
+		$what = 'item',
3385
+		$action_desc = 'processed',
3386
+		$query_args = [],
3387
+		$override_overwrite = false
3388
+	) {
3389
+		$this->_redirect_after_action(
3390
+			$success,
3391
+			$what,
3392
+			$action_desc,
3393
+			$query_args,
3394
+			$override_overwrite
3395
+		);
3396
+	}
3397
+
3398
+
3399
+	/**
3400
+	 * Helper method for merging existing request data with the returned redirect url.
3401
+	 *
3402
+	 * This is typically used for redirects after an action so that if the original view was a filtered view those
3403
+	 * filters are still applied.
3404
+	 *
3405
+	 * @param array $new_route_data
3406
+	 * @return array
3407
+	 */
3408
+	protected function mergeExistingRequestParamsWithRedirectArgs(array $new_route_data)
3409
+	{
3410
+		foreach ($this->request->requestParams() as $ref => $value) {
3411
+			// unset nonces
3412
+			if (strpos($ref, 'nonce') !== false) {
3413
+				$this->request->unSetRequestParam($ref);
3414
+				continue;
3415
+			}
3416
+			// urlencode values.
3417
+			$value = is_array($value) ? array_map('urlencode', $value) : urlencode($value);
3418
+			$this->request->setRequestParam($ref, $value);
3419
+		}
3420
+		return array_merge($this->request->requestParams(), $new_route_data);
3421
+	}
3422
+
3423
+
3424
+	/**
3425
+	 * @param int|float|string $success            - whether success was for two or more records, or just one, or none
3426
+	 * @param string           $what               - what the action was performed on
3427
+	 * @param string           $action_desc        - what was done ie: updated, deleted, etc
3428
+	 * @param array            $query_args         - an array of query_args to be added to the URL to redirect to
3429
+	 * @param BOOL             $override_overwrite - by default all EE_Error::success messages are overwritten,
3430
+	 *                                             this allows you to override this so that they show.
3431
+	 * @return void
3432
+	 * @throws EE_Error
3433
+	 * @throws InvalidArgumentException
3434
+	 * @throws InvalidDataTypeException
3435
+	 * @throws InvalidInterfaceException
3436
+	 */
3437
+	protected function _redirect_after_action(
3438
+		$success = 0,
3439
+		string $what = 'item',
3440
+		string $action_desc = 'processed',
3441
+		array $query_args = [],
3442
+		bool $override_overwrite = false
3443
+	) {
3444
+		do_action('AHEE_log', __FILE__, __FUNCTION__, '');
3445
+		$notices = EE_Error::get_notices(false);
3446
+		// overwrite default success messages //BUT ONLY if overwrite not overridden
3447
+		if (! $override_overwrite || ! empty($notices['errors'])) {
3448
+			EE_Error::overwrite_success();
3449
+		}
3450
+		if (! $override_overwrite && ! empty($what) && ! empty($action_desc) && empty($notices['errors'])) {
3451
+			// how many records affected ? more than one record ? or just one ?
3452
+			EE_Error::add_success(
3453
+				sprintf(
3454
+					esc_html(
3455
+						_n(
3456
+							'The "%1$s" has been successfully %2$s.',
3457
+							'The "%1$s" have been successfully %2$s.',
3458
+							$success,
3459
+							'event_espresso'
3460
+						)
3461
+					),
3462
+					$what,
3463
+					$action_desc
3464
+				),
3465
+				__FILE__,
3466
+				__FUNCTION__,
3467
+				__LINE__
3468
+			);
3469
+		}
3470
+		// check that $query_args isn't something crazy
3471
+		if (! is_array($query_args)) {
3472
+			$query_args = [];
3473
+		}
3474
+		/**
3475
+		 * Allow injecting actions before the query_args are modified for possible different
3476
+		 * redirections on save and close actions
3477
+		 *
3478
+		 * @param array $query_args       The original query_args array coming into the
3479
+		 *                                method.
3480
+		 * @since 4.2.0
3481
+		 */
3482
+		do_action(
3483
+			"AHEE__{$this->class_name}___redirect_after_action__before_redirect_modification_{$this->_req_action}",
3484
+			$query_args
3485
+		);
3486
+		// set redirect url.
3487
+		// Note if there is a "page" index in the $query_args then we go with vanilla admin.php route,
3488
+		// otherwise we go with whatever is set as the _admin_base_url
3489
+		$redirect_url = isset($query_args['page']) ? admin_url('admin.php') : $this->_admin_base_url;
3490
+		// calculate where we're going (if we have a "save and close" button pushed)
3491
+		if (
3492
+			$this->request->requestParamIsSet('save_and_close')
3493
+			&& $this->request->requestParamIsSet('save_and_close_referrer')
3494
+		) {
3495
+			// even though we have the save_and_close referrer, we need to parse the url for the action in order to generate a nonce
3496
+			$parsed_url = parse_url($this->request->getRequestParam('save_and_close_referrer', '', 'url'));
3497
+			// regenerate query args array from referrer URL
3498
+			parse_str($parsed_url['query'], $query_args);
3499
+			// correct page and action will be in the query args now
3500
+			$redirect_url = admin_url('admin.php');
3501
+		}
3502
+		// merge any default query_args set in _default_route_query_args property
3503
+		if (! empty($this->_default_route_query_args) && ! $this->_is_UI_request) {
3504
+			$args_to_merge = [];
3505
+			foreach ($this->_default_route_query_args as $query_param => $query_value) {
3506
+				// is there a wp_referer array in our _default_route_query_args property?
3507
+				if ($query_param === 'wp_referer') {
3508
+					$query_value = (array) $query_value;
3509
+					foreach ($query_value as $reference => $value) {
3510
+						if (strpos($reference, 'nonce') !== false) {
3511
+							continue;
3512
+						}
3513
+						// finally we will override any arguments in the referer with
3514
+						// what might be set on the _default_route_query_args array.
3515
+						if (isset($this->_default_route_query_args[ $reference ])) {
3516
+							$args_to_merge[ $reference ] = urlencode($this->_default_route_query_args[ $reference ]);
3517
+						} else {
3518
+							$args_to_merge[ $reference ] = urlencode($value);
3519
+						}
3520
+					}
3521
+					continue;
3522
+				}
3523
+				$args_to_merge[ $query_param ] = $query_value;
3524
+			}
3525
+			// now let's merge these arguments but override with what was specifically sent in to the
3526
+			// redirect.
3527
+			$query_args = array_merge($args_to_merge, $query_args);
3528
+		}
3529
+		$this->_process_notices($query_args);
3530
+		// generate redirect url
3531
+		// if redirecting to anything other than the main page, add a nonce
3532
+		if (isset($query_args['action'])) {
3533
+			// manually generate wp_nonce and merge that with the query vars
3534
+			// becuz the wp_nonce_url function wrecks havoc on some vars
3535
+			$query_args['_wpnonce'] = wp_create_nonce($query_args['action'] . '_nonce');
3536
+		}
3537
+		// we're adding some hooks and filters in here for processing any things just before redirects
3538
+		// (example: an admin page has done an insert or update and we want to run something after that).
3539
+		do_action('AHEE_redirect_' . $this->class_name . $this->_req_action, $query_args);
3540
+		$redirect_url = apply_filters(
3541
+			'FHEE_redirect_' . $this->class_name . $this->_req_action,
3542
+			EE_Admin_Page::add_query_args_and_nonce($query_args, $redirect_url),
3543
+			$query_args
3544
+		);
3545
+		// check if we're doing ajax.  If we are then lets just return the results and js can handle how it wants.
3546
+		if ($this->request->isAjax()) {
3547
+			$default_data                    = [
3548
+				'close'        => true,
3549
+				'redirect_url' => $redirect_url,
3550
+				'where'        => 'main',
3551
+				'what'         => 'append',
3552
+			];
3553
+			$this->_template_args['success'] = $success;
3554
+			$this->_template_args['data']    = ! empty($this->_template_args['data']) ? array_merge(
3555
+				$default_data,
3556
+				$this->_template_args['data']
3557
+			) : $default_data;
3558
+			$this->_return_json();
3559
+		}
3560
+		wp_safe_redirect($redirect_url);
3561
+		exit();
3562
+	}
3563
+
3564
+
3565
+	/**
3566
+	 * process any notices before redirecting (or returning ajax request)
3567
+	 * This method sets the $this->_template_args['notices'] attribute;
3568
+	 *
3569
+	 * @param array $query_args         any query args that need to be used for notice transient ('action')
3570
+	 * @param bool  $skip_route_verify  This is typically used when we are processing notices REALLY early and
3571
+	 *                                  page_routes haven't been defined yet.
3572
+	 * @param bool  $sticky_notices     This is used to flag that regardless of whether this is doing_ajax or not, we
3573
+	 *                                  still save a transient for the notice.
3574
+	 * @return void
3575
+	 * @throws EE_Error
3576
+	 * @throws InvalidArgumentException
3577
+	 * @throws InvalidDataTypeException
3578
+	 * @throws InvalidInterfaceException
3579
+	 */
3580
+	protected function _process_notices($query_args = [], $skip_route_verify = false, $sticky_notices = true)
3581
+	{
3582
+		// first let's set individual error properties if doing_ajax and the properties aren't already set.
3583
+		if ($this->request->isAjax()) {
3584
+			$notices = EE_Error::get_notices(false);
3585
+			if (empty($this->_template_args['success'])) {
3586
+				$this->_template_args['success'] = isset($notices['success']) ? $notices['success'] : false;
3587
+			}
3588
+			if (empty($this->_template_args['errors'])) {
3589
+				$this->_template_args['errors'] = isset($notices['errors']) ? $notices['errors'] : false;
3590
+			}
3591
+			if (empty($this->_template_args['attention'])) {
3592
+				$this->_template_args['attention'] = isset($notices['attention']) ? $notices['attention'] : false;
3593
+			}
3594
+		}
3595
+		$this->_template_args['notices'] = EE_Error::get_notices();
3596
+		// IF this isn't ajax we need to create a transient for the notices using the route (however, overridden if $sticky_notices == true)
3597
+		if (! $this->request->isAjax() || $sticky_notices) {
3598
+			$route = isset($query_args['action']) ? $query_args['action'] : 'default';
3599
+			$this->_add_transient(
3600
+				$route,
3601
+				$this->_template_args['notices'],
3602
+				true,
3603
+				$skip_route_verify
3604
+			);
3605
+		}
3606
+	}
3607
+
3608
+
3609
+	/**
3610
+	 * get_action_link_or_button
3611
+	 * returns the button html for adding, editing, or deleting an item (depending on given type)
3612
+	 *
3613
+	 * @param string $action        use this to indicate which action the url is generated with.
3614
+	 * @param string $type          accepted strings must be defined in the $_labels['button'] array(as the key)
3615
+	 *                              property.
3616
+	 * @param array  $extra_request if the button requires extra params you can include them in $key=>$value pairs.
3617
+	 * @param string $class         Use this to give the class for the button. Defaults to 'button--primary'
3618
+	 * @param string $base_url      If this is not provided
3619
+	 *                              the _admin_base_url will be used as the default for the button base_url.
3620
+	 *                              Otherwise this value will be used.
3621
+	 * @param bool   $exclude_nonce If true then no nonce will be in the generated button link.
3622
+	 * @return string
3623
+	 * @throws InvalidArgumentException
3624
+	 * @throws InvalidInterfaceException
3625
+	 * @throws InvalidDataTypeException
3626
+	 * @throws EE_Error
3627
+	 */
3628
+	public function get_action_link_or_button(
3629
+		$action,
3630
+		$type = 'add',
3631
+		$extra_request = [],
3632
+		$class = 'button button--primary',
3633
+		$base_url = '',
3634
+		$exclude_nonce = false
3635
+	) {
3636
+		// first let's validate the action (if $base_url is FALSE otherwise validation will happen further along)
3637
+		if (empty($base_url) && ! isset($this->_page_routes[ $action ])) {
3638
+			throw new EE_Error(
3639
+				sprintf(
3640
+					esc_html__(
3641
+						'There is no page route for given action for the button.  This action was given: %s',
3642
+						'event_espresso'
3643
+					),
3644
+					$action
3645
+				)
3646
+			);
3647
+		}
3648
+		if (! isset($this->_labels['buttons'][ $type ])) {
3649
+			throw new EE_Error(
3650
+				sprintf(
3651
+					esc_html__(
3652
+						'There is no label for the given button type (%s). Labels are set in the <code>_page_config</code> property.',
3653
+						'event_espresso'
3654
+					),
3655
+					$type
3656
+				)
3657
+			);
3658
+		}
3659
+		// finally check user access for this button.
3660
+		$has_access = $this->check_user_access($action, true);
3661
+		if (! $has_access) {
3662
+			return '';
3663
+		}
3664
+		$_base_url  = ! $base_url ? $this->_admin_base_url : $base_url;
3665
+		$query_args = [
3666
+			'action' => $action,
3667
+		];
3668
+		// merge extra_request args but make sure our original action takes precedence and doesn't get overwritten.
3669
+		if (! empty($extra_request)) {
3670
+			$query_args = array_merge($extra_request, $query_args);
3671
+		}
3672
+		$url = EE_Admin_Page::add_query_args_and_nonce($query_args, $_base_url, false, $exclude_nonce);
3673
+		return EEH_Template::get_button_or_link($url, $this->_labels['buttons'][ $type ], $class);
3674
+	}
3675
+
3676
+
3677
+	/**
3678
+	 * _per_page_screen_option
3679
+	 * Utility function for adding in a per_page_option in the screen_options_dropdown.
3680
+	 *
3681
+	 * @return void
3682
+	 * @throws InvalidArgumentException
3683
+	 * @throws InvalidInterfaceException
3684
+	 * @throws InvalidDataTypeException
3685
+	 */
3686
+	protected function _per_page_screen_option()
3687
+	{
3688
+		$option = 'per_page';
3689
+		$args   = [
3690
+			'label'   => apply_filters(
3691
+				'FHEE__EE_Admin_Page___per_page_screen_options___label',
3692
+				$this->_admin_page_title,
3693
+				$this
3694
+			),
3695
+			'default' => (int) apply_filters(
3696
+				'FHEE__EE_Admin_Page___per_page_screen_options__default',
3697
+				20
3698
+			),
3699
+			'option'  => $this->_current_page . '_' . $this->_current_view . '_per_page',
3700
+		];
3701
+		// ONLY add the screen option if the user has access to it.
3702
+		if ($this->check_user_access($this->_current_view, true)) {
3703
+			add_screen_option($option, $args);
3704
+		}
3705
+	}
3706
+
3707
+
3708
+	/**
3709
+	 * set_per_page_screen_option
3710
+	 * All this does is make sure that WordPress saves any per_page screen options (if set) for the current page.
3711
+	 * we have to do this rather than running inside the 'set-screen-options' hook because it runs earlier than
3712
+	 * admin_menu.
3713
+	 *
3714
+	 * @return void
3715
+	 */
3716
+	private function _set_per_page_screen_options()
3717
+	{
3718
+		if ($this->request->requestParamIsSet('wp_screen_options')) {
3719
+			check_admin_referer('screen-options-nonce', 'screenoptionnonce');
3720
+			if (! $user = wp_get_current_user()) {
3721
+				return;
3722
+			}
3723
+			$option = $this->request->getRequestParam('wp_screen_options[option]', '', 'key');
3724
+			if (! $option) {
3725
+				return;
3726
+			}
3727
+			$value      = $this->request->getRequestParam('wp_screen_options[value]', 0, 'int');
3728
+			$map_option = $option;
3729
+			$option     = str_replace('-', '_', $option);
3730
+			switch ($map_option) {
3731
+				case $this->_current_page . '_' . $this->_current_view . '_per_page':
3732
+					$max_value = apply_filters(
3733
+						'FHEE__EE_Admin_Page___set_per_page_screen_options__max_value',
3734
+						999,
3735
+						$this->_current_page,
3736
+						$this->_current_view
3737
+					);
3738
+					if ($value < 1) {
3739
+						return;
3740
+					}
3741
+					$value = min($value, $max_value);
3742
+					break;
3743
+				default:
3744
+					$value = apply_filters(
3745
+						'FHEE__EE_Admin_Page___set_per_page_screen_options__value',
3746
+						false,
3747
+						$option,
3748
+						$value
3749
+					);
3750
+					if (false === $value) {
3751
+						return;
3752
+					}
3753
+					break;
3754
+			}
3755
+			update_user_meta($user->ID, $option, $value);
3756
+			wp_safe_redirect(remove_query_arg(['pagenum', 'apage', 'paged'], wp_get_referer()));
3757
+			exit;
3758
+		}
3759
+	}
3760
+
3761
+
3762
+	/**
3763
+	 * This just allows for setting the $_template_args property if it needs to be set outside the object
3764
+	 *
3765
+	 * @param array $data array that will be assigned to template args.
3766
+	 */
3767
+	public function set_template_args($data)
3768
+	{
3769
+		$this->_template_args = array_merge($this->_template_args, (array) $data);
3770
+	}
3771
+
3772
+
3773
+	/**
3774
+	 * This makes available the WP transient system for temporarily moving data between routes
3775
+	 *
3776
+	 * @param string $route             the route that should receive the transient
3777
+	 * @param array  $data              the data that gets sent
3778
+	 * @param bool   $notices           If this is for notices then we use this to indicate so, otherwise its just a
3779
+	 *                                  normal route transient.
3780
+	 * @param bool   $skip_route_verify Used to indicate we want to skip route verification.  This is usually ONLY used
3781
+	 *                                  when we are adding a transient before page_routes have been defined.
3782
+	 * @return void
3783
+	 * @throws EE_Error
3784
+	 */
3785
+	protected function _add_transient($route, $data, $notices = false, $skip_route_verify = false)
3786
+	{
3787
+		$user_id = get_current_user_id();
3788
+		if (! $skip_route_verify) {
3789
+			$this->_verify_route($route);
3790
+		}
3791
+		// now let's set the string for what kind of transient we're setting
3792
+		$transient = $notices
3793
+			? 'ee_rte_n_tx_' . $route . '_' . $user_id
3794
+			: 'rte_tx_' . $route . '_' . $user_id;
3795
+		$data      = $notices ? ['notices' => $data] : $data;
3796
+		// is there already a transient for this route?  If there is then let's ADD to that transient
3797
+		$existing = is_multisite() && is_network_admin()
3798
+			? get_site_transient($transient)
3799
+			: get_transient($transient);
3800
+		if ($existing) {
3801
+			$data = array_merge((array) $data, (array) $existing);
3802
+		}
3803
+		if (is_multisite() && is_network_admin()) {
3804
+			set_site_transient($transient, $data, 8);
3805
+		} else {
3806
+			set_transient($transient, $data, 8);
3807
+		}
3808
+	}
3809
+
3810
+
3811
+	/**
3812
+	 * this retrieves the temporary transient that has been set for moving data between routes.
3813
+	 *
3814
+	 * @param bool   $notices true we get notices transient. False we just return normal route transient
3815
+	 * @param string $route
3816
+	 * @return mixed data
3817
+	 */
3818
+	protected function _get_transient($notices = false, $route = '')
3819
+	{
3820
+		$user_id   = get_current_user_id();
3821
+		$route     = ! $route ? $this->_req_action : $route;
3822
+		$transient = $notices
3823
+			? 'ee_rte_n_tx_' . $route . '_' . $user_id
3824
+			: 'rte_tx_' . $route . '_' . $user_id;
3825
+		$data      = is_multisite() && is_network_admin()
3826
+			? get_site_transient($transient)
3827
+			: get_transient($transient);
3828
+		// delete transient after retrieval (just in case it hasn't expired);
3829
+		if (is_multisite() && is_network_admin()) {
3830
+			delete_site_transient($transient);
3831
+		} else {
3832
+			delete_transient($transient);
3833
+		}
3834
+		return $notices && isset($data['notices']) ? $data['notices'] : $data;
3835
+	}
3836
+
3837
+
3838
+	/**
3839
+	 * The purpose of this method is just to run garbage collection on any EE transients that might have expired but
3840
+	 * would not be called later. This will be assigned to run on a specific EE Admin page. (place the method in the
3841
+	 * default route callback on the EE_Admin page you want it run.)
3842
+	 *
3843
+	 * @return void
3844
+	 */
3845
+	protected function _transient_garbage_collection()
3846
+	{
3847
+		global $wpdb;
3848
+		// retrieve all existing transients
3849
+		$query =
3850
+			"SELECT option_name FROM {$wpdb->options} WHERE option_name LIKE '%rte_tx_%' OR option_name LIKE '%rte_n_tx_%'";
3851
+		if ($results = $wpdb->get_results($query)) {
3852
+			foreach ($results as $result) {
3853
+				$transient = str_replace('_transient_', '', $result->option_name);
3854
+				get_transient($transient);
3855
+				if (is_multisite() && is_network_admin()) {
3856
+					get_site_transient($transient);
3857
+				}
3858
+			}
3859
+		}
3860
+	}
3861
+
3862
+
3863
+	/**
3864
+	 * get_view
3865
+	 *
3866
+	 * @return string content of _view property
3867
+	 */
3868
+	public function get_view()
3869
+	{
3870
+		return $this->_view;
3871
+	}
3872
+
3873
+
3874
+	/**
3875
+	 * getter for the protected $_views property
3876
+	 *
3877
+	 * @return array
3878
+	 */
3879
+	public function get_views()
3880
+	{
3881
+		return $this->_views;
3882
+	}
3883
+
3884
+
3885
+	/**
3886
+	 * get_current_page
3887
+	 *
3888
+	 * @return string _current_page property value
3889
+	 */
3890
+	public function get_current_page()
3891
+	{
3892
+		return $this->_current_page;
3893
+	}
3894
+
3895
+
3896
+	/**
3897
+	 * get_current_view
3898
+	 *
3899
+	 * @return string _current_view property value
3900
+	 */
3901
+	public function get_current_view()
3902
+	{
3903
+		return $this->_current_view;
3904
+	}
3905
+
3906
+
3907
+	/**
3908
+	 * get_current_screen
3909
+	 *
3910
+	 * @return object The current WP_Screen object
3911
+	 */
3912
+	public function get_current_screen()
3913
+	{
3914
+		return $this->_current_screen;
3915
+	}
3916
+
3917
+
3918
+	/**
3919
+	 * get_current_page_view_url
3920
+	 *
3921
+	 * @return string This returns the url for the current_page_view.
3922
+	 */
3923
+	public function get_current_page_view_url()
3924
+	{
3925
+		return $this->_current_page_view_url;
3926
+	}
3927
+
3928
+
3929
+	/**
3930
+	 * just returns the Request
3931
+	 *
3932
+	 * @return RequestInterface
3933
+	 */
3934
+	public function get_request()
3935
+	{
3936
+		return $this->request;
3937
+	}
3938
+
3939
+
3940
+	/**
3941
+	 * just returns the _req_data property
3942
+	 *
3943
+	 * @return array
3944
+	 */
3945
+	public function get_request_data()
3946
+	{
3947
+		return $this->request->requestParams();
3948
+	}
3949
+
3950
+
3951
+	/**
3952
+	 * returns the _req_data protected property
3953
+	 *
3954
+	 * @return string
3955
+	 */
3956
+	public function get_req_action()
3957
+	{
3958
+		return $this->_req_action;
3959
+	}
3960
+
3961
+
3962
+	/**
3963
+	 * @return bool  value of $_is_caf property
3964
+	 */
3965
+	public function is_caf()
3966
+	{
3967
+		return $this->_is_caf;
3968
+	}
3969
+
3970
+
3971
+	/**
3972
+	 * @return mixed
3973
+	 */
3974
+	public function default_espresso_metaboxes()
3975
+	{
3976
+		return $this->_default_espresso_metaboxes;
3977
+	}
3978
+
3979
+
3980
+	/**
3981
+	 * @return mixed
3982
+	 */
3983
+	public function admin_base_url()
3984
+	{
3985
+		return $this->_admin_base_url;
3986
+	}
3987
+
3988
+
3989
+	/**
3990
+	 * @return mixed
3991
+	 */
3992
+	public function wp_page_slug()
3993
+	{
3994
+		return $this->_wp_page_slug;
3995
+	}
3996
+
3997
+
3998
+	/**
3999
+	 * updates  espresso configuration settings
4000
+	 *
4001
+	 * @param string                   $tab
4002
+	 * @param EE_Config_Base|EE_Config $config
4003
+	 * @param string                   $file file where error occurred
4004
+	 * @param string                   $func function  where error occurred
4005
+	 * @param string                   $line line no where error occurred
4006
+	 * @return boolean
4007
+	 */
4008
+	protected function _update_espresso_configuration($tab, $config, $file = '', $func = '', $line = '')
4009
+	{
4010
+		// remove any options that are NOT going to be saved with the config settings.
4011
+		if (isset($config->core->ee_ueip_optin)) {
4012
+			// TODO: remove the following two lines and make sure values are migrated from 3.1
4013
+			update_option('ee_ueip_optin', $config->core->ee_ueip_optin);
4014
+			update_option('ee_ueip_has_notified', true);
4015
+		}
4016
+		// and save it (note we're also doing the network save here)
4017
+		$net_saved    = ! is_main_site() || EE_Network_Config::instance()->update_config(false, false);
4018
+		$config_saved = EE_Config::instance()->update_espresso_config(false, false);
4019
+		if ($config_saved && $net_saved) {
4020
+			EE_Error::add_success(sprintf(esc_html__('"%s" have been successfully updated.', 'event_espresso'), $tab));
4021
+			return true;
4022
+		}
4023
+		EE_Error::add_error(
4024
+			sprintf(esc_html__('The "%s" were not updated.', 'event_espresso'), $tab),
4025
+			$file,
4026
+			$func,
4027
+			$line
4028
+		);
4029
+		return false;
4030
+	}
4031
+
4032
+
4033
+	/**
4034
+	 * Returns an array to be used for EE_FOrm_Fields.helper.php's select_input as the $values argument.
4035
+	 *
4036
+	 * @return array
4037
+	 */
4038
+	public function get_yes_no_values()
4039
+	{
4040
+		return $this->_yes_no_values;
4041
+	}
4042
+
4043
+
4044
+	/**
4045
+	 * @return string
4046
+	 * @throws ReflectionException
4047
+	 * @since 5.0.0.p
4048
+	 */
4049
+	protected function _get_dir()
4050
+	{
4051
+		$reflector = new ReflectionClass($this->class_name);
4052
+		return dirname($reflector->getFileName());
4053
+	}
4054
+
4055
+
4056
+	/**
4057
+	 * A helper for getting a "next link".
4058
+	 *
4059
+	 * @param string $url   The url to link to
4060
+	 * @param string $class The class to use.
4061
+	 * @return string
4062
+	 */
4063
+	protected function _next_link($url, $class = 'dashicons dashicons-arrow-right')
4064
+	{
4065
+		return '<a class="' . $class . '" href="' . $url . '"></a>';
4066
+	}
4067
+
4068
+
4069
+	/**
4070
+	 * A helper for getting a "previous link".
4071
+	 *
4072
+	 * @param string $url   The url to link to
4073
+	 * @param string $class The class to use.
4074
+	 * @return string
4075
+	 */
4076
+	protected function _previous_link($url, $class = 'dashicons dashicons-arrow-left')
4077
+	{
4078
+		return '<a class="' . $class . '" href="' . $url . '"></a>';
4079
+	}
4080
+
4081
+
4082
+
4083
+
4084
+
4085
+
4086
+
4087
+	// below are some messages related methods that should be available across the EE_Admin system.  Note, these methods are NOT page specific
4088
+
4089
+
4090
+	/**
4091
+	 * This processes an request to resend a registration and assumes we have a _REG_ID for doing so. So if the caller
4092
+	 * knows that the _REG_ID isn't in the req_data array but CAN obtain it, the caller should ADD the _REG_ID to the
4093
+	 * _req_data array.
4094
+	 *
4095
+	 * @return bool success/fail
4096
+	 * @throws EE_Error
4097
+	 * @throws InvalidArgumentException
4098
+	 * @throws ReflectionException
4099
+	 * @throws InvalidDataTypeException
4100
+	 * @throws InvalidInterfaceException
4101
+	 */
4102
+	protected function _process_resend_registration()
4103
+	{
4104
+		$this->_template_args['success'] = EED_Messages::process_resend($this->_req_data);
4105
+		do_action(
4106
+			'AHEE__EE_Admin_Page___process_resend_registration',
4107
+			$this->_template_args['success'],
4108
+			$this->request->requestParams()
4109
+		);
4110
+		return $this->_template_args['success'];
4111
+	}
4112
+
4113
+
4114
+	/**
4115
+	 * This automatically processes any payment message notifications when manual payment has been applied.
4116
+	 *
4117
+	 * @param EE_Payment $payment
4118
+	 * @return bool success/fail
4119
+	 */
4120
+	protected function _process_payment_notification(EE_Payment $payment)
4121
+	{
4122
+		add_filter('FHEE__EE_Payment_Processor__process_registration_payments__display_notifications', '__return_true');
4123
+		do_action('AHEE__EE_Admin_Page___process_admin_payment_notification', $payment);
4124
+		$this->_template_args['success'] = apply_filters(
4125
+			'FHEE__EE_Admin_Page___process_admin_payment_notification__success',
4126
+			false,
4127
+			$payment
4128
+		);
4129
+		return $this->_template_args['success'];
4130
+	}
4131
+
4132
+
4133
+	/**
4134
+	 * @param EEM_Base      $entity_model
4135
+	 * @param string        $entity_PK_name name of the primary key field used as a request param, ie: id, ID, etc
4136
+	 * @param string        $action         one of the EE_Admin_List_Table::ACTION_* constants: delete, restore, trash
4137
+	 * @param string        $delete_column  name of the field that denotes whether entity is trashed
4138
+	 * @param callable|null $callback       called after entity is trashed, restored, or deleted
4139
+	 * @return int|float
4140
+	 * @throws EE_Error
4141
+	 */
4142
+	protected function trashRestoreDeleteEntities(
4143
+		EEM_Base $entity_model,
4144
+		$entity_PK_name,
4145
+		$action = EE_Admin_List_Table::ACTION_DELETE,
4146
+		$delete_column = '',
4147
+		callable $callback = null
4148
+	) {
4149
+		$entity_PK      = $entity_model->get_primary_key_field();
4150
+		$entity_PK_name = $entity_PK_name ?: $entity_PK->get_name();
4151
+		$entity_PK_type = $this->resolveEntityFieldDataType($entity_PK);
4152
+		// grab ID if deleting a single entity
4153
+		if ($this->request->requestParamIsSet($entity_PK_name)) {
4154
+			$ID = $this->request->getRequestParam($entity_PK_name, 0, $entity_PK_type);
4155
+			return $this->trashRestoreDeleteEntity($entity_model, $ID, $action, $delete_column, $callback) ? 1 : 0;
4156
+		}
4157
+		// or grab checkbox array if bulk deleting
4158
+		$checkboxes = $this->request->getRequestParam('checkbox', [], $entity_PK_type, true);
4159
+		if (empty($checkboxes)) {
4160
+			return 0;
4161
+		}
4162
+		$success = 0;
4163
+		$IDs     = array_keys($checkboxes);
4164
+		// cycle thru bulk action checkboxes
4165
+		foreach ($IDs as $ID) {
4166
+			// increment $success
4167
+			if ($this->trashRestoreDeleteEntity($entity_model, $ID, $action, $delete_column, $callback)) {
4168
+				$success++;
4169
+			}
4170
+		}
4171
+		$count = (int) count($checkboxes);
4172
+		// if multiple entities were deleted successfully, then $deleted will be full count of deletions,
4173
+		// otherwise it will be a fraction of ( actual deletions / total entities to be deleted )
4174
+		return $success === $count ? $count : $success / $count;
4175
+	}
4176
+
4177
+
4178
+	/**
4179
+	 * @param EE_Primary_Key_Field_Base $entity_PK
4180
+	 * @return string
4181
+	 * @throws EE_Error
4182
+	 * @since   4.10.30.p
4183
+	 */
4184
+	private function resolveEntityFieldDataType(EE_Primary_Key_Field_Base $entity_PK)
4185
+	{
4186
+		$entity_PK_type = $entity_PK->getSchemaType();
4187
+		switch ($entity_PK_type) {
4188
+			case 'boolean':
4189
+				return 'bool';
4190
+			case 'integer':
4191
+				return 'int';
4192
+			case 'number':
4193
+				return 'float';
4194
+			case 'string':
4195
+				return 'string';
4196
+		}
4197
+		throw new RuntimeException(
4198
+			sprintf(
4199
+				esc_html__(
4200
+					'"%1$s" is an invalid schema type for the %2$s primary key.',
4201
+					'event_espresso'
4202
+				),
4203
+				$entity_PK_type,
4204
+				$entity_PK->get_name()
4205
+			)
4206
+		);
4207
+	}
4208
+
4209
+
4210
+	/**
4211
+	 * @param EEM_Base      $entity_model
4212
+	 * @param int|string    $entity_ID
4213
+	 * @param string        $action        one of the EE_Admin_List_Table::ACTION_* constants: delete, restore, trash
4214
+	 * @param string        $delete_column name of the field that denotes whether entity is trashed
4215
+	 * @param callable|null $callback      called after entity is trashed, restored, or deleted
4216
+	 * @return bool
4217
+	 */
4218
+	protected function trashRestoreDeleteEntity(
4219
+		EEM_Base $entity_model,
4220
+		$entity_ID,
4221
+		string $action,
4222
+		string $delete_column,
4223
+		?callable $callback = null
4224
+	): bool {
4225
+		$entity_ID = absint($entity_ID);
4226
+		if (! $entity_ID) {
4227
+			$this->trashRestoreDeleteError($action, $entity_model);
4228
+		}
4229
+		$result = 0;
4230
+		try {
4231
+			$entity = $entity_model->get_one_by_ID($entity_ID);
4232
+			if (! $entity instanceof EE_Base_Class) {
4233
+				throw new DomainException(
4234
+					sprintf(
4235
+						esc_html__(
4236
+							'Missing or invalid %1$s entity with ID of "%2$s" returned from db.',
4237
+							'event_espresso'
4238
+						),
4239
+						str_replace('EEM_', '', $entity_model->get_this_model_name()),
4240
+						$entity_ID
4241
+					)
4242
+				);
4243
+			}
4244
+			switch ($action) {
4245
+				case EE_Admin_List_Table::ACTION_DELETE:
4246
+					$result = (bool) $entity->delete_permanently();
4247
+					break;
4248
+				case EE_Admin_List_Table::ACTION_RESTORE:
4249
+					$result = $entity->delete_or_restore(false);
4250
+					break;
4251
+				case EE_Admin_List_Table::ACTION_TRASH:
4252
+					$result = $entity->delete_or_restore();
4253
+					break;
4254
+			}
4255
+		} catch (Exception $exception) {
4256
+			$this->trashRestoreDeleteError($action, $entity_model, $exception);
4257
+		}
4258
+		if (is_callable($callback)) {
4259
+			call_user_func_array($callback, [$entity_model, $entity_ID, $action, $result, $delete_column]);
4260
+		}
4261
+		return $result;
4262
+	}
4263
+
4264
+
4265
+	/**
4266
+	 * @param EEM_Base $entity_model
4267
+	 * @param string   $delete_column
4268
+	 * @since 4.10.30.p
4269
+	 */
4270
+	private function validateDeleteColumn(EEM_Base $entity_model, $delete_column)
4271
+	{
4272
+		if (empty($delete_column)) {
4273
+			throw new DomainException(
4274
+				sprintf(
4275
+					esc_html__(
4276
+						'You need to specify the name of the "delete column" on the %2$s model, in order to trash or restore an entity.',
4277
+						'event_espresso'
4278
+					),
4279
+					$entity_model->get_this_model_name()
4280
+				)
4281
+			);
4282
+		}
4283
+		if (! $entity_model->has_field($delete_column)) {
4284
+			throw new DomainException(
4285
+				sprintf(
4286
+					esc_html__(
4287
+						'The %1$s field does not exist on the %2$s model.',
4288
+						'event_espresso'
4289
+					),
4290
+					$delete_column,
4291
+					$entity_model->get_this_model_name()
4292
+				)
4293
+			);
4294
+		}
4295
+	}
4296
+
4297
+
4298
+	/**
4299
+	 * @param EEM_Base       $entity_model
4300
+	 * @param Exception|null $exception
4301
+	 * @param string         $action
4302
+	 * @since 4.10.30.p
4303
+	 */
4304
+	private function trashRestoreDeleteError($action, EEM_Base $entity_model, Exception $exception = null)
4305
+	{
4306
+		if ($exception instanceof Exception) {
4307
+			throw new RuntimeException(
4308
+				sprintf(
4309
+					esc_html__(
4310
+						'Could not %1$s the %2$s because the following error occurred: %3$s',
4311
+						'event_espresso'
4312
+					),
4313
+					$action,
4314
+					$entity_model->get_this_model_name(),
4315
+					$exception->getMessage()
4316
+				)
4317
+			);
4318
+		}
4319
+		throw new RuntimeException(
4320
+			sprintf(
4321
+				esc_html__(
4322
+					'Could not %1$s the %2$s because an invalid ID was received.',
4323
+					'event_espresso'
4324
+				),
4325
+				$action,
4326
+				$entity_model->get_this_model_name()
4327
+			)
4328
+		);
4329
+	}
4330 4330
 }
Please login to merge, or discard this patch.
Spacing   +180 added lines, -180 removed lines patch added patch discarded remove patch
@@ -97,7 +97,7 @@  discard block
 block discarded – undo
97 97
     protected ?bool  $_is_UI_request = null;
98 98
     // this starts at null so we can have no header routes progress through two states.
99 99
 
100
-    protected bool  $_is_caf        = false;                                                                                                                                                                                                                                  // This is just a property that flags whether the given route is a caffeinated route or not.
100
+    protected bool  $_is_caf        = false; // This is just a property that flags whether the given route is a caffeinated route or not.
101 101
 
102 102
     protected bool  $_routing       = false;
103 103
 
@@ -122,7 +122,7 @@  discard block
 block discarded – undo
122 122
      *
123 123
      * @var bool
124 124
      */
125
-    protected bool $_cpt_route              = false;
125
+    protected bool $_cpt_route = false;
126 126
 
127 127
     /**
128 128
      * set via request page and action args.
@@ -172,7 +172,7 @@  discard block
 block discarded – undo
172 172
      */
173 173
     protected string $base_class_name = '';
174 174
 
175
-    protected string $class_name    = '';
175
+    protected string $class_name = '';
176 176
 
177 177
     /**
178 178
      * unprocessed value for the 'action' request param (default '')
@@ -602,7 +602,7 @@  discard block
 block discarded – undo
602 602
         $ee_menu_slugs = (array) $ee_menu_slugs;
603 603
         if (
604 604
             ! $this->request->isAjax()
605
-            && (! $this->_current_page || ! isset($ee_menu_slugs[ $this->_current_page ]))
605
+            && ( ! $this->_current_page || ! isset($ee_menu_slugs[$this->_current_page]))
606 606
         ) {
607 607
             return;
608 608
         }
@@ -622,7 +622,7 @@  discard block
 block discarded – undo
622 622
             : $req_action;
623 623
 
624 624
         $this->_current_view = $this->_req_action;
625
-        $this->_req_nonce    = $this->_req_action . '_nonce';
625
+        $this->_req_nonce    = $this->_req_action.'_nonce';
626 626
         $this->_define_page_props();
627 627
         $this->_current_page_view_url = add_query_arg(
628 628
             ['page' => $this->_current_page, 'action' => $this->_current_view],
@@ -646,33 +646,33 @@  discard block
 block discarded – undo
646 646
         }
647 647
         // filter routes and page_config so addons can add their stuff. Filtering done per class
648 648
         $this->_page_routes = apply_filters(
649
-            'FHEE__' . $this->class_name . '__page_setup__page_routes',
649
+            'FHEE__'.$this->class_name.'__page_setup__page_routes',
650 650
             $this->_page_routes,
651 651
             $this
652 652
         );
653 653
         $this->_page_config = apply_filters(
654
-            'FHEE__' . $this->class_name . '__page_setup__page_config',
654
+            'FHEE__'.$this->class_name.'__page_setup__page_config',
655 655
             $this->_page_config,
656 656
             $this
657 657
         );
658 658
         if ($this->base_class_name !== '') {
659 659
             $this->_page_routes = apply_filters(
660
-                'FHEE__' . $this->base_class_name . '__page_setup__page_routes',
660
+                'FHEE__'.$this->base_class_name.'__page_setup__page_routes',
661 661
                 $this->_page_routes,
662 662
                 $this
663 663
             );
664 664
             $this->_page_config = apply_filters(
665
-                'FHEE__' . $this->base_class_name . '__page_setup__page_config',
665
+                'FHEE__'.$this->base_class_name.'__page_setup__page_config',
666 666
                 $this->_page_config,
667 667
                 $this
668 668
             );
669 669
         }
670 670
         // if AHEE__EE_Admin_Page__route_admin_request_$this->_current_view method is present
671 671
         // then we call it hooked into the AHEE__EE_Admin_Page__route_admin_request action
672
-        if (method_exists($this, 'AHEE__EE_Admin_Page__route_admin_request_' . $this->_current_view)) {
672
+        if (method_exists($this, 'AHEE__EE_Admin_Page__route_admin_request_'.$this->_current_view)) {
673 673
             add_action(
674 674
                 'AHEE__EE_Admin_Page__route_admin_request',
675
-                [$this, 'AHEE__EE_Admin_Page__route_admin_request_' . $this->_current_view],
675
+                [$this, 'AHEE__EE_Admin_Page__route_admin_request_'.$this->_current_view],
676 676
                 10,
677 677
                 2
678 678
             );
@@ -685,8 +685,8 @@  discard block
 block discarded – undo
685 685
             if ($this->_is_UI_request) {
686 686
                 // admin_init stuff - global, all views for this page class, specific view
687 687
                 add_action('admin_init', [$this, 'admin_init'], 10);
688
-                if (method_exists($this, 'admin_init_' . $this->_current_view)) {
689
-                    add_action('admin_init', [$this, 'admin_init_' . $this->_current_view], 15);
688
+                if (method_exists($this, 'admin_init_'.$this->_current_view)) {
689
+                    add_action('admin_init', [$this, 'admin_init_'.$this->_current_view], 15);
690 690
                 }
691 691
             } else {
692 692
                 // hijack regular WP loading and route admin request immediately
@@ -705,12 +705,12 @@  discard block
 block discarded – undo
705 705
      */
706 706
     private function _do_other_page_hooks()
707 707
     {
708
-        $registered_pages = apply_filters('FHEE_do_other_page_hooks_' . $this->page_slug, []);
708
+        $registered_pages = apply_filters('FHEE_do_other_page_hooks_'.$this->page_slug, []);
709 709
         foreach ($registered_pages as $page) {
710 710
             // now let's setup the file name and class that should be present
711 711
             $classname = str_replace('.class.php', '', $page);
712 712
             // autoloaders should take care of loading file
713
-            if (! class_exists($classname)) {
713
+            if ( ! class_exists($classname)) {
714 714
                 $error_msg[] = sprintf(
715 715
                     esc_html__(
716 716
                         'Something went wrong with loading the %s admin hooks page.',
@@ -727,7 +727,7 @@  discard block
 block discarded – undo
727 727
                                    ),
728 728
                                    $page,
729 729
                                    '<br />',
730
-                                   '<strong>' . $classname . '</strong>'
730
+                                   '<strong>'.$classname.'</strong>'
731 731
                                );
732 732
                 throw new EE_Error(implode('||', $error_msg));
733 733
             }
@@ -770,13 +770,13 @@  discard block
 block discarded – undo
770 770
         // load admin_notices - global, page class, and view specific
771 771
         add_action('admin_notices', [$this, 'admin_notices_global'], 5);
772 772
         add_action('admin_notices', [$this, 'admin_notices'], 10);
773
-        if (method_exists($this, 'admin_notices_' . $this->_current_view)) {
774
-            add_action('admin_notices', [$this, 'admin_notices_' . $this->_current_view], 15);
773
+        if (method_exists($this, 'admin_notices_'.$this->_current_view)) {
774
+            add_action('admin_notices', [$this, 'admin_notices_'.$this->_current_view], 15);
775 775
         }
776 776
         // load network admin_notices - global, page class, and view specific
777 777
         add_action('network_admin_notices', [$this, 'network_admin_notices_global'], 5);
778
-        if (method_exists($this, 'network_admin_notices_' . $this->_current_view)) {
779
-            add_action('network_admin_notices', [$this, 'network_admin_notices_' . $this->_current_view]);
778
+        if (method_exists($this, 'network_admin_notices_'.$this->_current_view)) {
779
+            add_action('network_admin_notices', [$this, 'network_admin_notices_'.$this->_current_view]);
780 780
         }
781 781
         // this will save any per_page screen options if they are present
782 782
         $this->_set_per_page_screen_options();
@@ -902,7 +902,7 @@  discard block
 block discarded – undo
902 902
     protected function _verify_routes()
903 903
     {
904 904
         do_action('AHEE_log', __FILE__, __FUNCTION__, '');
905
-        if (! $this->_current_page && ! $this->request->isAjax()) {
905
+        if ( ! $this->_current_page && ! $this->request->isAjax()) {
906 906
             return false;
907 907
         }
908 908
         // check that the page_routes array is not empty
@@ -913,7 +913,7 @@  discard block
 block discarded – undo
913 913
                 $this->_admin_page_title
914 914
             );
915 915
             // developer error msg
916
-            $error_msg .= '||' . $error_msg
916
+            $error_msg .= '||'.$error_msg
917 917
                           . esc_html__(
918 918
                               ' Make sure the "set_page_routes()" method exists, and is setting the "_page_routes" array properly.',
919 919
                               'event_espresso'
@@ -922,8 +922,8 @@  discard block
 block discarded – undo
922 922
         }
923 923
         // and that the requested page route exists
924 924
         if (array_key_exists($this->_req_action, $this->_page_routes)) {
925
-            $this->_route        = $this->_page_routes[ $this->_req_action ];
926
-            $this->_route_config = $this->_page_config[ $this->_req_action ] ?? [];
925
+            $this->_route        = $this->_page_routes[$this->_req_action];
926
+            $this->_route_config = $this->_page_config[$this->_req_action] ?? [];
927 927
         } else {
928 928
             // user error msg
929 929
             $error_msg = sprintf(
@@ -934,7 +934,7 @@  discard block
 block discarded – undo
934 934
                 $this->_admin_page_title
935 935
             );
936 936
             // developer error msg
937
-            $error_msg .= '||' . $error_msg
937
+            $error_msg .= '||'.$error_msg
938 938
                           . sprintf(
939 939
                               esc_html__(
940 940
                                   ' Create a key in the "_page_routes" array named "%s" and set its value to the appropriate method.',
@@ -945,7 +945,7 @@  discard block
 block discarded – undo
945 945
             throw new EE_Error($error_msg);
946 946
         }
947 947
         // and that a default route exists
948
-        if (! array_key_exists('default', $this->_page_routes)) {
948
+        if ( ! array_key_exists('default', $this->_page_routes)) {
949 949
             // user error msg
950 950
             $error_msg = sprintf(
951 951
                 esc_html__(
@@ -955,7 +955,7 @@  discard block
 block discarded – undo
955 955
                 $this->_admin_page_title
956 956
             );
957 957
             // developer error msg
958
-            $error_msg .= '||' . $error_msg
958
+            $error_msg .= '||'.$error_msg
959 959
                           . esc_html__(
960 960
                               ' Create a key in the "_page_routes" array named "default" and set its value to your default page method.',
961 961
                               'event_espresso'
@@ -997,7 +997,7 @@  discard block
 block discarded – undo
997 997
             $this->_admin_page_title
998 998
         );
999 999
         // developer error msg
1000
-        $error_msg .= '||' . $error_msg
1000
+        $error_msg .= '||'.$error_msg
1001 1001
                       . sprintf(
1002 1002
                           esc_html__(
1003 1003
                               ' Check the route you are using in your method (%s) and make sure it matches a route set in your "_page_routes" array property',
@@ -1025,7 +1025,7 @@  discard block
 block discarded – undo
1025 1025
     protected function _verify_nonce($nonce, $nonce_ref)
1026 1026
     {
1027 1027
         // verify nonce against expected value
1028
-        if (! wp_verify_nonce($nonce, $nonce_ref)) {
1028
+        if ( ! wp_verify_nonce($nonce, $nonce_ref)) {
1029 1029
             // these are not the droids you are looking for !!!
1030 1030
             $msg = sprintf(
1031 1031
                 esc_html__('%sNonce Fail.%s', 'event_espresso'),
@@ -1042,7 +1042,7 @@  discard block
 block discarded – undo
1042 1042
                     __CLASS__
1043 1043
                 );
1044 1044
             }
1045
-            if (! $this->request->isAjax()) {
1045
+            if ( ! $this->request->isAjax()) {
1046 1046
                 wp_die($msg);
1047 1047
             }
1048 1048
             EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
@@ -1066,7 +1066,7 @@  discard block
 block discarded – undo
1066 1066
      */
1067 1067
     protected function _route_admin_request()
1068 1068
     {
1069
-        if (! $this->_is_UI_request) {
1069
+        if ( ! $this->_is_UI_request) {
1070 1070
             $this->_verify_routes();
1071 1071
         }
1072 1072
         $nonce_check = ! isset($this->_route_config['require_nonce']) || $this->_route_config['require_nonce'];
@@ -1087,7 +1087,7 @@  discard block
 block discarded – undo
1087 1087
         $args = is_array($this->_route) && isset($this->_route['args']) ? $this->_route['args'] : [];
1088 1088
         // action right before calling route
1089 1089
         // (hook is something like 'AHEE__Registrations_Admin_Page__route_admin_request')
1090
-        if (! did_action('AHEE__EE_Admin_Page__route_admin_request')) {
1090
+        if ( ! did_action('AHEE__EE_Admin_Page__route_admin_request')) {
1091 1091
             do_action('AHEE__EE_Admin_Page__route_admin_request', $this->_current_view, $this);
1092 1092
         }
1093 1093
         // strip _wp_http_referer from the server REQUEST_URI
@@ -1102,7 +1102,7 @@  discard block
 block discarded – undo
1102 1102
         $this->request->setRequestParam('_wp_http_referer', $cleaner_request_uri, true);
1103 1103
         $this->request->setServerParam('REQUEST_URI', $cleaner_request_uri, true);
1104 1104
         $route_callback = [];
1105
-        if (! empty($func)) {
1105
+        if ( ! empty($func)) {
1106 1106
             if (is_array($func)) {
1107 1107
                 $route_callback = $func;
1108 1108
             } elseif (is_string($func)) {
@@ -1116,7 +1116,7 @@  discard block
 block discarded – undo
1116 1116
             }
1117 1117
             [$class, $method] = $route_callback;
1118 1118
             // is it neither a class method NOR a standalone function?
1119
-            if (! is_callable($route_callback)) {
1119
+            if ( ! is_callable($route_callback)) {
1120 1120
                 // user error msg
1121 1121
                 $error_msg = esc_html__(
1122 1122
                     'An error occurred. The  requested page route could not be found.',
@@ -1144,7 +1144,7 @@  discard block
 block discarded – undo
1144 1144
                 $arg_keys = array_keys($args);
1145 1145
                 $nice_args = [];
1146 1146
                 foreach ($args as $key => $arg) {
1147
-                    $nice_args[ $key ] = is_object($arg) ? get_class($arg) : $arg_keys[ $key ];
1147
+                    $nice_args[$key] = is_object($arg) ? get_class($arg) : $arg_keys[$key];
1148 1148
                 }
1149 1149
                 new ExceptionStackTraceDisplay(
1150 1150
                         new RuntimeException(
@@ -1247,7 +1247,7 @@  discard block
 block discarded – undo
1247 1247
                 if (strpos($key, 'nonce') !== false) {
1248 1248
                     continue;
1249 1249
                 }
1250
-                $args[ 'wp_referer[' . $key . ']' ] = is_string($value) ? htmlspecialchars($value) : $value;
1250
+                $args['wp_referer['.$key.']'] = is_string($value) ? htmlspecialchars($value) : $value;
1251 1251
             }
1252 1252
         }
1253 1253
         return EEH_URL::add_query_args_and_nonce($args, $url, $exclude_nonce, $context);
@@ -1287,12 +1287,12 @@  discard block
 block discarded – undo
1287 1287
      */
1288 1288
     protected function _add_help_tabs()
1289 1289
     {
1290
-        if (isset($this->_page_config[ $this->_req_action ])) {
1291
-            $config = $this->_page_config[ $this->_req_action ];
1290
+        if (isset($this->_page_config[$this->_req_action])) {
1291
+            $config = $this->_page_config[$this->_req_action];
1292 1292
             // let's see if there is a help_sidebar set for the current route and we'll set that up for usage as well.
1293 1293
             if (is_array($config) && isset($config['help_sidebar'])) {
1294 1294
                 // check that the callback given is valid
1295
-                if (! method_exists($this, $config['help_sidebar'])) {
1295
+                if ( ! method_exists($this, $config['help_sidebar'])) {
1296 1296
                     throw new EE_Error(
1297 1297
                         sprintf(
1298 1298
                             esc_html__(
@@ -1305,18 +1305,18 @@  discard block
 block discarded – undo
1305 1305
                     );
1306 1306
                 }
1307 1307
                 $content = apply_filters(
1308
-                    'FHEE__' . $this->class_name . '__add_help_tabs__help_sidebar',
1308
+                    'FHEE__'.$this->class_name.'__add_help_tabs__help_sidebar',
1309 1309
                     $this->{$config['help_sidebar']}()
1310 1310
                 );
1311 1311
                 $this->_current_screen->set_help_sidebar($content);
1312 1312
             }
1313
-            if (! isset($config['help_tabs'])) {
1313
+            if ( ! isset($config['help_tabs'])) {
1314 1314
                 return;
1315 1315
             } //no help tabs for this route
1316 1316
             foreach ((array) $config['help_tabs'] as $tab_id => $cfg) {
1317 1317
                 // we're here so there ARE help tabs!
1318 1318
                 // make sure we've got what we need
1319
-                if (! isset($cfg['title'])) {
1319
+                if ( ! isset($cfg['title'])) {
1320 1320
                     throw new EE_Error(
1321 1321
                         esc_html__(
1322 1322
                             'The _page_config array is not set up properly for help tabs.  It is missing a title',
@@ -1324,7 +1324,7 @@  discard block
 block discarded – undo
1324 1324
                         )
1325 1325
                     );
1326 1326
                 }
1327
-                if (! isset($cfg['filename']) && ! isset($cfg['callback']) && ! isset($cfg['content'])) {
1327
+                if ( ! isset($cfg['filename']) && ! isset($cfg['callback']) && ! isset($cfg['content'])) {
1328 1328
                     throw new EE_Error(
1329 1329
                         esc_html__(
1330 1330
                             'The _page_config array is not setup properly for help tabs. It is missing a either a filename reference, or a callback reference or a content reference so there is no way to know the content for the help tab',
@@ -1333,11 +1333,11 @@  discard block
 block discarded – undo
1333 1333
                     );
1334 1334
                 }
1335 1335
                 // first priority goes to content.
1336
-                if (! empty($cfg['content'])) {
1336
+                if ( ! empty($cfg['content'])) {
1337 1337
                     $content = ! empty($cfg['content']) ? $cfg['content'] : null;
1338 1338
                     // second priority goes to filename
1339
-                } elseif (! empty($cfg['filename'])) {
1340
-                    $file_path = $this->_get_dir() . '/help_tabs/' . $cfg['filename'] . '.help_tab.php';
1339
+                } elseif ( ! empty($cfg['filename'])) {
1340
+                    $file_path = $this->_get_dir().'/help_tabs/'.$cfg['filename'].'.help_tab.php';
1341 1341
                     // it's possible that the file is located on decaf route (and above sets up for caf route, if this is the case then lets check decaf route too)
1342 1342
                     $file_path = ! is_readable($file_path) ? EE_ADMIN_PAGES
1343 1343
                                                              . basename($this->_get_dir())
@@ -1345,7 +1345,7 @@  discard block
 block discarded – undo
1345 1345
                                                              . $cfg['filename']
1346 1346
                                                              . '.help_tab.php' : $file_path;
1347 1347
                     // if file is STILL not readable then let's do a EE_Error so its more graceful than a fatal error.
1348
-                    if (! isset($cfg['callback']) && ! is_readable($file_path)) {
1348
+                    if ( ! isset($cfg['callback']) && ! is_readable($file_path)) {
1349 1349
                         EE_Error::add_error(
1350 1350
                             sprintf(
1351 1351
                                 esc_html__(
@@ -1393,7 +1393,7 @@  discard block
 block discarded – undo
1393 1393
                     return;
1394 1394
                 }
1395 1395
                 // setup config array for help tab method
1396
-                $id  = $this->page_slug . '-' . $this->_req_action . '-' . $tab_id;
1396
+                $id  = $this->page_slug.'-'.$this->_req_action.'-'.$tab_id;
1397 1397
                 $_ht = [
1398 1398
                     'id'       => $id,
1399 1399
                     'title'    => $cfg['title'],
@@ -1419,8 +1419,8 @@  discard block
 block discarded – undo
1419 1419
             $qtips = (array) $this->_route_config['qtips'];
1420 1420
             // load qtip loader
1421 1421
             $path = [
1422
-                $this->_get_dir() . '/qtips/',
1423
-                EE_ADMIN_PAGES . basename($this->_get_dir()) . '/qtips/',
1422
+                $this->_get_dir().'/qtips/',
1423
+                EE_ADMIN_PAGES.basename($this->_get_dir()).'/qtips/',
1424 1424
             ];
1425 1425
             EEH_Qtip_Loader::instance()->register($qtips, $path);
1426 1426
         }
@@ -1442,7 +1442,7 @@  discard block
 block discarded – undo
1442 1442
         $i        = 0;
1443 1443
         $only_tab = count($this->_page_config) < 2;
1444 1444
         foreach ($this->_page_config as $slug => $config) {
1445
-            if (! is_array($config) || empty($config['nav'])) {
1445
+            if ( ! is_array($config) || empty($config['nav'])) {
1446 1446
                 continue;
1447 1447
             }
1448 1448
             // no nav tab for this config
@@ -1451,7 +1451,7 @@  discard block
 block discarded – undo
1451 1451
                 // nav tab is only to appear when route requested.
1452 1452
                 continue;
1453 1453
             }
1454
-            if (! $this->check_user_access($slug, true)) {
1454
+            if ( ! $this->check_user_access($slug, true)) {
1455 1455
                 // no nav tab because current user does not have access.
1456 1456
                 continue;
1457 1457
             }
@@ -1459,20 +1459,20 @@  discard block
 block discarded – undo
1459 1459
             $css_class .= $only_tab ? ' ee-only-tab' : '';
1460 1460
             $css_class .= " ee-nav-tab__$slug";
1461 1461
 
1462
-            $this->_nav_tabs[ $slug ] = [
1462
+            $this->_nav_tabs[$slug] = [
1463 1463
                 'url'       => $config['nav']['url'] ?? EE_Admin_Page::add_query_args_and_nonce(
1464 1464
                         ['action' => $slug],
1465 1465
                         $this->_admin_base_url
1466 1466
                     ),
1467 1467
                 'link_text' => $this->navTabLabel($config['nav'], $slug),
1468
-                'css_class' => $this->_req_action === $slug ? $css_class . ' nav-tab-active' : $css_class,
1468
+                'css_class' => $this->_req_action === $slug ? $css_class.' nav-tab-active' : $css_class,
1469 1469
                 'order'     => $config['nav']['order'] ?? $i,
1470 1470
             ];
1471 1471
             $i++;
1472 1472
         }
1473 1473
         // if $this->_nav_tabs is empty then lets set the default
1474 1474
         if (empty($this->_nav_tabs)) {
1475
-            $this->_nav_tabs[ $this->_default_nav_tab_name ] = [
1475
+            $this->_nav_tabs[$this->_default_nav_tab_name] = [
1476 1476
                 'url'       => $this->_admin_base_url,
1477 1477
                 'link_text' => ucwords(str_replace('_', ' ', $this->_default_nav_tab_name)),
1478 1478
                 'css_class' => 'nav-tab-active',
@@ -1488,11 +1488,11 @@  discard block
 block discarded – undo
1488 1488
     {
1489 1489
         $label = $nav_tab['label'] ?? ucwords(str_replace('_', ' ', $slug));
1490 1490
         $icon  = $nav_tab['icon'] ?? null;
1491
-        $icon  = $icon ? '<span class="dashicons ' . $icon . '"></span>' : '';
1491
+        $icon  = $icon ? '<span class="dashicons '.$icon.'"></span>' : '';
1492 1492
         return '
1493 1493
             <span class="ee-admin-screen-tab__label">
1494
-                ' . $icon . '
1495
-                <span class="ee-nav-label__text">' . $label . '</span>
1494
+                ' . $icon.'
1495
+                <span class="ee-nav-label__text">' . $label.'</span>
1496 1496
             </span>';
1497 1497
     }
1498 1498
 
@@ -1510,10 +1510,10 @@  discard block
 block discarded – undo
1510 1510
             foreach ($this->_route_config['labels'] as $label => $text) {
1511 1511
                 if (is_array($text)) {
1512 1512
                     foreach ($text as $sublabel => $subtext) {
1513
-                        $this->_labels[ $label ][ $sublabel ] = $subtext;
1513
+                        $this->_labels[$label][$sublabel] = $subtext;
1514 1514
                     }
1515 1515
                 } else {
1516
-                    $this->_labels[ $label ] = $text;
1516
+                    $this->_labels[$label] = $text;
1517 1517
                 }
1518 1518
             }
1519 1519
         }
@@ -1535,10 +1535,10 @@  discard block
 block discarded – undo
1535 1535
     {
1536 1536
         $route_to_check = ! empty($route_to_check) ? $route_to_check : $this->_req_action;
1537 1537
         $capability     = ! empty($route_to_check)
1538
-                          && isset($this->_page_routes[ $route_to_check ])
1539
-                          && is_array($this->_page_routes[ $route_to_check ])
1540
-                          && ! empty($this->_page_routes[ $route_to_check ]['capability'])
1541
-            ? $this->_page_routes[ $route_to_check ]['capability']
1538
+                          && isset($this->_page_routes[$route_to_check])
1539
+                          && is_array($this->_page_routes[$route_to_check])
1540
+                          && ! empty($this->_page_routes[$route_to_check]['capability'])
1541
+            ? $this->_page_routes[$route_to_check]['capability']
1542 1542
             : null;
1543 1543
 
1544 1544
         if (empty($capability) && empty($route_to_check)) {
@@ -1590,14 +1590,14 @@  discard block
 block discarded – undo
1590 1590
         string $priority = 'default',
1591 1591
         ?array $callback_args = null
1592 1592
     ) {
1593
-        if (! (is_callable($callback) || ! function_exists($callback))) {
1593
+        if ( ! (is_callable($callback) || ! function_exists($callback))) {
1594 1594
             return;
1595 1595
         }
1596 1596
 
1597 1597
         add_meta_box($box_id, $title, $callback, $screen, $context, $priority, $callback_args);
1598 1598
         add_filter(
1599 1599
             "postbox_classes_{$this->_wp_page_slug}_{$box_id}",
1600
-            function ($classes) {
1600
+            function($classes) {
1601 1601
                 $classes[] = 'ee-admin-container';
1602 1602
                 return $classes;
1603 1603
             }
@@ -1691,7 +1691,7 @@  discard block
 block discarded – undo
1691 1691
         ';
1692 1692
 
1693 1693
         // current set timezone for timezone js
1694
-        echo '<span id="current_timezone" class="hidden">' . esc_html(EEH_DTT_Helper::get_timezone()) . '</span>';
1694
+        echo '<span id="current_timezone" class="hidden">'.esc_html(EEH_DTT_Helper::get_timezone()).'</span>';
1695 1695
     }
1696 1696
 
1697 1697
 
@@ -1725,7 +1725,7 @@  discard block
 block discarded – undo
1725 1725
         // loop through the array and setup content
1726 1726
         foreach ($help_array as $trigger => $help) {
1727 1727
             // make sure the array is setup properly
1728
-            if (! isset($help['title'], $help['content'])) {
1728
+            if ( ! isset($help['title'], $help['content'])) {
1729 1729
                 throw new EE_Error(
1730 1730
                     esc_html__(
1731 1731
                         'Does not look like the popup content array has been setup correctly.  Might want to double check that.  Read the comments for the _get_help_popup_content method found in "EE_Admin_Page" class',
@@ -1739,8 +1739,8 @@  discard block
 block discarded – undo
1739 1739
                 'help_popup_title'   => $help['title'],
1740 1740
                 'help_popup_content' => $help['content'],
1741 1741
             ];
1742
-            $content       .= EEH_Template::display_template(
1743
-                EE_ADMIN_TEMPLATE . 'admin_help_popup.template.php',
1742
+            $content .= EEH_Template::display_template(
1743
+                EE_ADMIN_TEMPLATE.'admin_help_popup.template.php',
1744 1744
                 $template_args,
1745 1745
                 true
1746 1746
             );
@@ -1762,15 +1762,15 @@  discard block
 block discarded – undo
1762 1762
     private function _get_help_content()
1763 1763
     {
1764 1764
         // what is the method we're looking for?
1765
-        $method_name = '_help_popup_content_' . $this->_req_action;
1765
+        $method_name = '_help_popup_content_'.$this->_req_action;
1766 1766
         // if method doesn't exist let's get out.
1767
-        if (! method_exists($this, $method_name)) {
1767
+        if ( ! method_exists($this, $method_name)) {
1768 1768
             return [];
1769 1769
         }
1770 1770
         // k we're good to go let's retrieve the help array
1771 1771
         $help_array = $this->{$method_name}();
1772 1772
         // make sure we've got an array!
1773
-        if (! is_array($help_array)) {
1773
+        if ( ! is_array($help_array)) {
1774 1774
             throw new EE_Error(
1775 1775
                 esc_html__(
1776 1776
                     'Something went wrong with help popup content generation. Expecting an array and well, this ain\'t no array bub.',
@@ -1802,15 +1802,15 @@  discard block
 block discarded – undo
1802 1802
         // let's check and see if there is any content set for this popup.  If there isn't then we'll include a default title and content so that developers know something needs to be corrected
1803 1803
         $help_array   = $this->_get_help_content();
1804 1804
         $help_content = '';
1805
-        if (empty($help_array) || ! isset($help_array[ $trigger_id ])) {
1806
-            $help_array[ $trigger_id ] = [
1805
+        if (empty($help_array) || ! isset($help_array[$trigger_id])) {
1806
+            $help_array[$trigger_id] = [
1807 1807
                 'title'   => esc_html__('Missing Content', 'event_espresso'),
1808 1808
                 'content' => esc_html__(
1809 1809
                     'A trigger has been set that doesn\'t have any corresponding content. Make sure you have set the help content. (see the "_set_help_popup_content" method in the EE_Admin_Page for instructions.)',
1810 1810
                     'event_espresso'
1811 1811
                 ),
1812 1812
             ];
1813
-            $help_content              = $this->_set_help_popup_content($help_array);
1813
+            $help_content = $this->_set_help_popup_content($help_array);
1814 1814
         }
1815 1815
         // let's setup the trigger
1816 1816
         $content = '<a class="ee-dialog" href="?height='
@@ -1898,7 +1898,7 @@  discard block
 block discarded – undo
1898 1898
 
1899 1899
         add_filter(
1900 1900
             'admin_body_class',
1901
-            function ($classes) {
1901
+            function($classes) {
1902 1902
                 if (strpos($classes, 'espresso-admin') === false) {
1903 1903
                     $classes .= ' espresso-admin';
1904 1904
                 }
@@ -1989,12 +1989,12 @@  discard block
 block discarded – undo
1989 1989
     protected function _set_list_table()
1990 1990
     {
1991 1991
         // first is this a list_table view?
1992
-        if (! isset($this->_route_config['list_table'])) {
1992
+        if ( ! isset($this->_route_config['list_table'])) {
1993 1993
             return;
1994 1994
         } //not a list_table view so get out.
1995 1995
         // list table functions are per view specific (because some admin pages might have more than one list table!)
1996
-        $list_table_view = '_set_list_table_views_' . $this->_req_action;
1997
-        if (! method_exists($this, $list_table_view) || $this->{$list_table_view}() === false) {
1996
+        $list_table_view = '_set_list_table_views_'.$this->_req_action;
1997
+        if ( ! method_exists($this, $list_table_view) || $this->{$list_table_view}() === false) {
1998 1998
             // user error msg
1999 1999
             $error_msg = esc_html__(
2000 2000
                 'An error occurred. The requested list table views could not be found.',
@@ -2014,10 +2014,10 @@  discard block
 block discarded – undo
2014 2014
         }
2015 2015
         // let's provide the ability to filter the views per PAGE AND ROUTE, per PAGE, and globally
2016 2016
         $this->_views = apply_filters(
2017
-            'FHEE_list_table_views_' . $this->page_slug . '_' . $this->_req_action,
2017
+            'FHEE_list_table_views_'.$this->page_slug.'_'.$this->_req_action,
2018 2018
             $this->_views
2019 2019
         );
2020
-        $this->_views = apply_filters('FHEE_list_table_views_' . $this->page_slug, $this->_views);
2020
+        $this->_views = apply_filters('FHEE_list_table_views_'.$this->page_slug, $this->_views);
2021 2021
         $this->_views = apply_filters('FHEE_list_table_views', $this->_views);
2022 2022
         $this->_set_list_table_view();
2023 2023
         $this->_set_list_table_object();
@@ -2052,7 +2052,7 @@  discard block
 block discarded – undo
2052 2052
     protected function _set_list_table_object()
2053 2053
     {
2054 2054
         if (isset($this->_route_config['list_table'])) {
2055
-            if (! class_exists($this->_route_config['list_table'])) {
2055
+            if ( ! class_exists($this->_route_config['list_table'])) {
2056 2056
                 throw new EE_Error(
2057 2057
                     sprintf(
2058 2058
                         esc_html__(
@@ -2093,17 +2093,17 @@  discard block
 block discarded – undo
2093 2093
         foreach ($this->_views as $key => $view) {
2094 2094
             $query_args = [];
2095 2095
             // check for current view
2096
-            $this->_views[ $key ]['class']               = $this->_view === $view['slug'] ? 'current' : '';
2096
+            $this->_views[$key]['class']               = $this->_view === $view['slug'] ? 'current' : '';
2097 2097
             $query_args['action']                        = $this->_req_action;
2098
-            $query_args[ $this->_req_action . '_nonce' ] = wp_create_nonce($query_args['action'] . '_nonce');
2098
+            $query_args[$this->_req_action.'_nonce'] = wp_create_nonce($query_args['action'].'_nonce');
2099 2099
             $query_args['status']                        = $view['slug'];
2100 2100
             // merge any other arguments sent in.
2101
-            if (isset($extra_query_args[ $view['slug'] ])) {
2102
-                foreach ($extra_query_args[ $view['slug'] ] as $extra_query_arg) {
2101
+            if (isset($extra_query_args[$view['slug']])) {
2102
+                foreach ($extra_query_args[$view['slug']] as $extra_query_arg) {
2103 2103
                     $query_args[] = $extra_query_arg;
2104 2104
                 }
2105 2105
             }
2106
-            $this->_views[ $key ]['url'] = EE_Admin_Page::add_query_args_and_nonce($query_args, $this->_admin_base_url);
2106
+            $this->_views[$key]['url'] = EE_Admin_Page::add_query_args_and_nonce($query_args, $this->_admin_base_url);
2107 2107
         }
2108 2108
         return $this->_views;
2109 2109
     }
@@ -2134,14 +2134,14 @@  discard block
 block discarded – undo
2134 2134
 					<select id="entries-per-page-slct" name="entries-per-page-slct">';
2135 2135
         foreach ($values as $value) {
2136 2136
             if ($value < $max_entries) {
2137
-                $selected                  = $value === $per_page ? ' selected="' . $per_page . '"' : '';
2137
+                $selected = $value === $per_page ? ' selected="'.$per_page.'"' : '';
2138 2138
                 $entries_per_page_dropdown .= '
2139
-						<option value="' . $value . '"' . $selected . '>' . $value . '&nbsp;&nbsp;</option>';
2139
+						<option value="' . $value.'"'.$selected.'>'.$value.'&nbsp;&nbsp;</option>';
2140 2140
             }
2141 2141
         }
2142
-        $selected                  = $max_entries === $per_page ? ' selected="' . $per_page . '"' : '';
2142
+        $selected = $max_entries === $per_page ? ' selected="'.$per_page.'"' : '';
2143 2143
         $entries_per_page_dropdown .= '
2144
-						<option value="' . $max_entries . '"' . $selected . '>All&nbsp;&nbsp;</option>';
2144
+						<option value="' . $max_entries.'"'.$selected.'>All&nbsp;&nbsp;</option>';
2145 2145
         $entries_per_page_dropdown .= '
2146 2146
 					</select>
2147 2147
 					entries
@@ -2165,7 +2165,7 @@  discard block
 block discarded – undo
2165 2165
             empty($this->_search_btn_label) ? $this->page_label
2166 2166
                 : $this->_search_btn_label
2167 2167
         );
2168
-        $this->_template_args['search']['callback']  = 'search_' . $this->page_slug;
2168
+        $this->_template_args['search']['callback'] = 'search_'.$this->page_slug;
2169 2169
     }
2170 2170
 
2171 2171
 
@@ -2230,7 +2230,7 @@  discard block
 block discarded – undo
2230 2230
                                   );
2231 2231
                     throw new EE_Error($error_msg);
2232 2232
                 }
2233
-                unset($this->_route_config['metaboxes'][ $key ]);
2233
+                unset($this->_route_config['metaboxes'][$key]);
2234 2234
             }
2235 2235
         }
2236 2236
     }
@@ -2264,7 +2264,7 @@  discard block
 block discarded – undo
2264 2264
             $total_columns                                       = ! empty($screen_columns)
2265 2265
                 ? $screen_columns
2266 2266
                 : $this->_route_config['columns'][1];
2267
-            $this->_template_args['current_screen_widget_class'] = 'columns-' . $total_columns;
2267
+            $this->_template_args['current_screen_widget_class'] = 'columns-'.$total_columns;
2268 2268
             $this->_template_args['current_page']                = $this->_wp_page_slug;
2269 2269
             $this->_template_args['screen']                      = $this->_current_screen;
2270 2270
             $this->_column_template_path                         = EE_ADMIN_TEMPLATE
@@ -2310,7 +2310,7 @@  discard block
 block discarded – undo
2310 2310
      */
2311 2311
     protected function _espresso_ratings_request()
2312 2312
     {
2313
-        if (! apply_filters('FHEE_show_ratings_request_meta_box', true)) {
2313
+        if ( ! apply_filters('FHEE_show_ratings_request_meta_box', true)) {
2314 2314
             return;
2315 2315
         }
2316 2316
         $ratings_box_title = apply_filters(
@@ -2337,28 +2337,28 @@  discard block
 block discarded – undo
2337 2337
      */
2338 2338
     public function espresso_ratings_request()
2339 2339
     {
2340
-        EEH_Template::display_template(EE_ADMIN_TEMPLATE . 'espresso_ratings_request_content.template.php');
2340
+        EEH_Template::display_template(EE_ADMIN_TEMPLATE.'espresso_ratings_request_content.template.php');
2341 2341
     }
2342 2342
 
2343 2343
 
2344 2344
     public static function cached_rss_display($rss_id, $url)
2345 2345
     {
2346
-        $loading   = '<p class="widget-loading hide-if-no-js">'
2346
+        $loading = '<p class="widget-loading hide-if-no-js">'
2347 2347
                      . esc_html__('Loading&#8230;', 'event_espresso')
2348 2348
                      . '</p><p class="hide-if-js">'
2349 2349
                      . esc_html__('This widget requires JavaScript.', 'event_espresso')
2350 2350
                      . '</p>';
2351
-        $pre       = '<div class="espresso-rss-display">' . "\n\t";
2352
-        $pre       .= '<span id="' . esc_attr($rss_id) . '_url" class="hidden">' . esc_url_raw($url) . '</span>';
2353
-        $post      = '</div>' . "\n";
2354
-        $cache_key = 'ee_rss_' . md5($rss_id);
2351
+        $pre       = '<div class="espresso-rss-display">'."\n\t";
2352
+        $pre .= '<span id="'.esc_attr($rss_id).'_url" class="hidden">'.esc_url_raw($url).'</span>';
2353
+        $post      = '</div>'."\n";
2354
+        $cache_key = 'ee_rss_'.md5($rss_id);
2355 2355
         $output    = get_transient($cache_key);
2356 2356
         if ($output !== false) {
2357
-            echo wp_kses($pre . $output . $post, AllowedTags::getWithFormTags());
2357
+            echo wp_kses($pre.$output.$post, AllowedTags::getWithFormTags());
2358 2358
             return true;
2359 2359
         }
2360
-        if (! (defined('DOING_AJAX') && DOING_AJAX)) {
2361
-            echo wp_kses($pre . $loading . $post, AllowedTags::getWithFormTags());
2360
+        if ( ! (defined('DOING_AJAX') && DOING_AJAX)) {
2361
+            echo wp_kses($pre.$loading.$post, AllowedTags::getWithFormTags());
2362 2362
             return false;
2363 2363
         }
2364 2364
         ob_start();
@@ -2425,7 +2425,7 @@  discard block
 block discarded – undo
2425 2425
     public function espresso_sponsors_post_box()
2426 2426
     {
2427 2427
         EEH_Template::display_template(
2428
-            EE_ADMIN_TEMPLATE . 'admin_general_metabox_contents_espresso_sponsors.template.php'
2428
+            EE_ADMIN_TEMPLATE.'admin_general_metabox_contents_espresso_sponsors.template.php'
2429 2429
         );
2430 2430
     }
2431 2431
 
@@ -2442,9 +2442,9 @@  discard block
 block discarded – undo
2442 2442
     protected function getPublishBoxTitle(): string
2443 2443
     {
2444 2444
         $publish_box_title = esc_html__('Publish', 'event_espresso');
2445
-        if (! empty($this->_labels['publishbox'])) {
2445
+        if ( ! empty($this->_labels['publishbox'])) {
2446 2446
             if (is_array($this->_labels['publishbox'])) {
2447
-                $publish_box_title = $this->_labels['publishbox'][ $this->_req_action ] ?? $publish_box_title;
2447
+                $publish_box_title = $this->_labels['publishbox'][$this->_req_action] ?? $publish_box_title;
2448 2448
             } else {
2449 2449
                 $publish_box_title = $this->_labels['publishbox'];
2450 2450
             }
@@ -2500,7 +2500,7 @@  discard block
 block discarded – undo
2500 2500
         // if we have extra content set let's add it in if not make sure its empty
2501 2501
         $this->_template_args['publish_box_extra_content'] = $this->_template_args['publish_box_extra_content'] ?? '';
2502 2502
         echo EEH_Template::display_template(
2503
-            EE_ADMIN_TEMPLATE . 'admin_details_publish_metabox.template.php',
2503
+            EE_ADMIN_TEMPLATE.'admin_details_publish_metabox.template.php',
2504 2504
             $this->_template_args,
2505 2505
             true
2506 2506
         );
@@ -2545,7 +2545,7 @@  discard block
 block discarded – undo
2545 2545
         $this->_template_args['publish_box_extra_content'] = $this->_template_args['publish_box_extra_content'] ?? '';
2546 2546
         if ($delete && ! empty($id) && empty($this->_template_args['publish_delete_link'])) {
2547 2547
             // make sure we have a default if just true is sent.
2548
-            $delete      = ! empty($delete) ? $delete : 'delete';
2548
+            $delete = ! empty($delete) ? $delete : 'delete';
2549 2549
             $this->_template_args['publish_delete_link'] = $this->get_action_link_or_button(
2550 2550
                 $delete,
2551 2551
                 $delete,
@@ -2553,10 +2553,10 @@  discard block
 block discarded – undo
2553 2553
                 'submitdelete deletion button button--outline button--caution'
2554 2554
             );
2555 2555
         }
2556
-        if (! isset($this->_template_args['publish_delete_link'])) {
2556
+        if ( ! isset($this->_template_args['publish_delete_link'])) {
2557 2557
             $this->_template_args['publish_delete_link'] = '';
2558 2558
         }
2559
-        if (! empty($name) && ! empty($id)) {
2559
+        if ( ! empty($name) && ! empty($id)) {
2560 2560
             $this->addPublishPostMetaBoxHiddenFields($name, ['type' => 'hidden', 'value' => $id]);
2561 2561
         }
2562 2562
         $hidden_fields = $this->_generate_admin_form_fields($this->publish_post_meta_box_hidden_fields, 'array');
@@ -2589,7 +2589,7 @@  discard block
 block discarded – undo
2589 2589
 
2590 2590
     protected function addPublishPostMetaBoxHiddenFields(string $field_name, array $field_attributes)
2591 2591
     {
2592
-        $this->publish_post_meta_box_hidden_fields[ $field_name ] = $field_attributes;
2592
+        $this->publish_post_meta_box_hidden_fields[$field_name] = $field_attributes;
2593 2593
     }
2594 2594
 
2595 2595
 
@@ -2690,7 +2690,7 @@  discard block
 block discarded – undo
2690 2690
         }
2691 2691
         // if $create_func is true (default) then we automatically create the function for displaying the actual meta box.  If false then we take the $callback reference passed through and use it instead (so callers can define their own callback function/method if they wish)
2692 2692
         $call_back_func = $create_func
2693
-            ? static function ($post, $metabox) {
2693
+            ? static function($post, $metabox) {
2694 2694
                 do_action('AHEE_log', __FILE__, __FUNCTION__, '');
2695 2695
                 echo EEH_Template::display_template(
2696 2696
                     $metabox['args']['template_path'],
@@ -2700,7 +2700,7 @@  discard block
 block discarded – undo
2700 2700
             }
2701 2701
             : $callback;
2702 2702
         $this->addMetaBox(
2703
-            str_replace('_', '-', $action) . '-mbox',
2703
+            str_replace('_', '-', $action).'-mbox',
2704 2704
             $title,
2705 2705
             $call_back_func,
2706 2706
             $this->_wp_page_slug,
@@ -2817,13 +2817,13 @@  discard block
 block discarded – undo
2817 2817
                                                                     'event-espresso_page_espresso_',
2818 2818
                                                                     '',
2819 2819
                                                                     $this->_wp_page_slug
2820
-                                                                ) . ' ' . $this->_req_action . '-route';
2820
+                                                                ).' '.$this->_req_action.'-route';
2821 2821
 
2822 2822
         $template_path = $sidebar
2823 2823
             ? EE_ADMIN_TEMPLATE . 'admin_details_wrapper.template.php'
2824
-            : EE_ADMIN_TEMPLATE . 'admin_details_wrapper_no_sidebar.template.php';
2824
+            : EE_ADMIN_TEMPLATE.'admin_details_wrapper_no_sidebar.template.php';
2825 2825
         if ($this->request->isAjax()) {
2826
-            $template_path = EE_ADMIN_TEMPLATE . 'admin_details_wrapper_no_sidebar_ajax.template.php';
2826
+            $template_path = EE_ADMIN_TEMPLATE.'admin_details_wrapper_no_sidebar_ajax.template.php';
2827 2827
         }
2828 2828
         $template_path = ! empty($this->_column_template_path) ? $this->_column_template_path : $template_path;
2829 2829
 
@@ -2857,11 +2857,11 @@  discard block
 block discarded – undo
2857 2857
     public function display_admin_caf_preview_page($utm_campaign_source = '', $display_sidebar = true)
2858 2858
     {
2859 2859
         // let's generate a default preview action button if there isn't one already present.
2860
-        $this->_labels['buttons']['buy_now']           = esc_html__(
2860
+        $this->_labels['buttons']['buy_now'] = esc_html__(
2861 2861
             'Upgrade to Event Espresso 4 Right Now',
2862 2862
             'event_espresso'
2863 2863
         );
2864
-        $buy_now_url                                   = add_query_arg(
2864
+        $buy_now_url = add_query_arg(
2865 2865
             [
2866 2866
                 'ee_ver'       => 'ee4',
2867 2867
                 'utm_source'   => 'ee4_plugin_admin',
@@ -2881,8 +2881,8 @@  discard block
 block discarded – undo
2881 2881
                 true
2882 2882
             )
2883 2883
             : $this->_template_args['preview_action_button'];
2884
-        $this->_template_args['admin_page_content']    = EEH_Template::display_template(
2885
-            EE_ADMIN_TEMPLATE . 'admin_caf_full_page_preview.template.php',
2884
+        $this->_template_args['admin_page_content'] = EEH_Template::display_template(
2885
+            EE_ADMIN_TEMPLATE.'admin_caf_full_page_preview.template.php',
2886 2886
             $this->_template_args,
2887 2887
             true
2888 2888
         );
@@ -2940,7 +2940,7 @@  discard block
 block discarded – undo
2940 2940
         // setup search attributes
2941 2941
         $this->_set_search_attributes();
2942 2942
         $this->_template_args['current_page']     = $this->_wp_page_slug;
2943
-        $template_path                            = EE_ADMIN_TEMPLATE . 'admin_list_wrapper.template.php';
2943
+        $template_path                            = EE_ADMIN_TEMPLATE.'admin_list_wrapper.template.php';
2944 2944
         $this->_template_args['table_url']        = $this->request->isAjax()
2945 2945
             ? add_query_arg(['noheader' => 'true', 'route' => $this->_req_action], $this->_admin_base_url)
2946 2946
             : add_query_arg(['route' => $this->_req_action], $this->_admin_base_url);
@@ -2948,10 +2948,10 @@  discard block
 block discarded – undo
2948 2948
         $this->_template_args['current_route']    = $this->_req_action;
2949 2949
         $this->_template_args['list_table_class'] = get_class($this->_list_table_object);
2950 2950
         $ajax_sorting_callback                    = $this->_list_table_object->get_ajax_sorting_callback();
2951
-        if (! empty($ajax_sorting_callback)) {
2951
+        if ( ! empty($ajax_sorting_callback)) {
2952 2952
             $sortable_list_table_form_fields = wp_nonce_field(
2953
-                $ajax_sorting_callback . '_nonce',
2954
-                $ajax_sorting_callback . '_nonce',
2953
+                $ajax_sorting_callback.'_nonce',
2954
+                $ajax_sorting_callback.'_nonce',
2955 2955
                 false,
2956 2956
                 false
2957 2957
             );
@@ -2968,18 +2968,18 @@  discard block
 block discarded – undo
2968 2968
 
2969 2969
         $hidden_form_fields = $this->_template_args['list_table_hidden_fields'] ?? '';
2970 2970
 
2971
-        $nonce_ref          = $this->_req_action . '_nonce';
2971
+        $nonce_ref          = $this->_req_action.'_nonce';
2972 2972
         $hidden_form_fields .= '
2973
-            <input type="hidden" name="' . $nonce_ref . '" value="' . wp_create_nonce($nonce_ref) . '">';
2973
+            <input type="hidden" name="' . $nonce_ref.'" value="'.wp_create_nonce($nonce_ref).'">';
2974 2974
 
2975 2975
         $this->_template_args['list_table_hidden_fields'] = $hidden_form_fields;
2976 2976
         // display message about search results?
2977
-        $search                                    = $this->request->getRequestParam('s');
2977
+        $search = $this->request->getRequestParam('s');
2978 2978
         $this->_template_args['before_list_table'] .= ! empty($search)
2979
-            ? '<p class="ee-search-results">' . sprintf(
2979
+            ? '<p class="ee-search-results">'.sprintf(
2980 2980
                 esc_html__('Displaying search results for the search string: %1$s', 'event_espresso'),
2981 2981
                 trim($search, '%')
2982
-            ) . '</p>'
2982
+            ).'</p>'
2983 2983
             : '';
2984 2984
         // filter before_list_table template arg
2985 2985
         $this->_template_args['before_list_table'] = apply_filters(
@@ -3013,7 +3013,7 @@  discard block
 block discarded – undo
3013 3013
         // convert to array and filter again
3014 3014
         // arrays are easier to inject new items in a specific location,
3015 3015
         // but would not be backwards compatible, so we have to add a new filter
3016
-        $this->_template_args['after_list_table']   = implode(
3016
+        $this->_template_args['after_list_table'] = implode(
3017 3017
             " \n",
3018 3018
             (array) apply_filters(
3019 3019
                 'FHEE__EE_Admin_Page___display_admin_list_table_page__after_list_table__template_args_array',
@@ -3068,7 +3068,7 @@  discard block
 block discarded – undo
3068 3068
             $this->page_slug
3069 3069
         );
3070 3070
         return EEH_Template::display_template(
3071
-            EE_ADMIN_TEMPLATE . 'admin_details_legend.template.php',
3071
+            EE_ADMIN_TEMPLATE.'admin_details_legend.template.php',
3072 3072
             $this->_template_args,
3073 3073
             true
3074 3074
         );
@@ -3192,7 +3192,7 @@  discard block
 block discarded – undo
3192 3192
         if ($this->request->isAjax()) {
3193 3193
             $this->_template_args['admin_page_content'] = EEH_Template::display_template(
3194 3194
             // $template_path,
3195
-                EE_ADMIN_TEMPLATE . 'admin_wrapper_ajax.template.php',
3195
+                EE_ADMIN_TEMPLATE.'admin_wrapper_ajax.template.php',
3196 3196
                 $this->_template_args,
3197 3197
                 true
3198 3198
             );
@@ -3201,7 +3201,7 @@  discard block
 block discarded – undo
3201 3201
         // load settings page wrapper template
3202 3202
         $template_path = $about
3203 3203
             ? EE_ADMIN_TEMPLATE . 'about_admin_wrapper.template.php'
3204
-            : EE_ADMIN_TEMPLATE . 'admin_wrapper.template.php';
3204
+            : EE_ADMIN_TEMPLATE.'admin_wrapper.template.php';
3205 3205
 
3206 3206
         EEH_Template::display_template($template_path, $this->_template_args);
3207 3207
     }
@@ -3285,17 +3285,17 @@  discard block
 block discarded – undo
3285 3285
         $default_names = ['save', 'save_and_close'];
3286 3286
         $buttons       = '';
3287 3287
         foreach ($button_text as $key => $button) {
3288
-            $ref     = $default_names[ $key ];
3289
-            $name    = ! empty($actions) ? $actions[ $key ] : $ref;
3290
-            $buttons .= '<input type="submit" class="button button--primary ' . $ref . '" '
3291
-                        . 'value="' . $button . '" name="' . $name . '" '
3292
-                        . 'id="' . $this->_current_view . '_' . $ref . '" />';
3293
-            if (! $both) {
3288
+            $ref     = $default_names[$key];
3289
+            $name    = ! empty($actions) ? $actions[$key] : $ref;
3290
+            $buttons .= '<input type="submit" class="button button--primary '.$ref.'" '
3291
+                        . 'value="'.$button.'" name="'.$name.'" '
3292
+                        . 'id="'.$this->_current_view.'_'.$ref.'" />';
3293
+            if ( ! $both) {
3294 3294
                 break;
3295 3295
             }
3296 3296
         }
3297 3297
         // add in a hidden index for the current page (so save and close redirects properly)
3298
-        $buttons                              .= '<input type="hidden" id="save_and_close_referrer" name="save_and_close_referrer" value="'
3298
+        $buttons .= '<input type="hidden" id="save_and_close_referrer" name="save_and_close_referrer" value="'
3299 3299
                                                  . $referrer_url
3300 3300
                                                  . '" />';
3301 3301
         $this->_template_args['save_buttons'] = $buttons;
@@ -3330,13 +3330,13 @@  discard block
 block discarded – undo
3330 3330
                 'An error occurred. No action was set for this page\'s form.',
3331 3331
                 'event_espresso'
3332 3332
             );
3333
-            $dev_msg  = $user_msg . "\n"
3333
+            $dev_msg = $user_msg."\n"
3334 3334
                         . sprintf(
3335 3335
                             esc_html__('The $route argument is required for the %s->%s method.', 'event_espresso'),
3336 3336
                             __FUNCTION__,
3337 3337
                             __CLASS__
3338 3338
                         );
3339
-            EE_Error::add_error($user_msg . '||' . $dev_msg, __FILE__, __FUNCTION__, __LINE__);
3339
+            EE_Error::add_error($user_msg.'||'.$dev_msg, __FILE__, __FUNCTION__, __LINE__);
3340 3340
         }
3341 3341
         // open form
3342 3342
         $action                                            = $this->_admin_base_url;
@@ -3344,9 +3344,9 @@  discard block
 block discarded – undo
3344 3344
             <form name='form' method='post' action='{$action}' id='{$route}_event_form' class='ee-admin-page-form' >
3345 3345
             ";
3346 3346
         // add nonce
3347
-        $nonce                                             =
3348
-            wp_nonce_field($route . '_nonce', $route . '_nonce', false, false);
3349
-        $this->_template_args['before_admin_page_content'] .= "\n\t" . $nonce;
3347
+        $nonce =
3348
+            wp_nonce_field($route.'_nonce', $route.'_nonce', false, false);
3349
+        $this->_template_args['before_admin_page_content'] .= "\n\t".$nonce;
3350 3350
         // add REQUIRED form action
3351 3351
         $hidden_fields = [
3352 3352
             'action' => ['type' => 'hidden', 'value' => $route],
@@ -3359,7 +3359,7 @@  discard block
 block discarded – undo
3359 3359
         $form_fields = $this->_generate_admin_form_fields($hidden_fields, 'array');
3360 3360
         // add fields to form
3361 3361
         foreach ((array) $form_fields as $form_field) {
3362
-            $this->_template_args['before_admin_page_content'] .= "\n\t" . $form_field['field'];
3362
+            $this->_template_args['before_admin_page_content'] .= "\n\t".$form_field['field'];
3363 3363
         }
3364 3364
         // close form
3365 3365
         $this->_template_args['after_admin_page_content'] = '</form>';
@@ -3444,10 +3444,10 @@  discard block
 block discarded – undo
3444 3444
         do_action('AHEE_log', __FILE__, __FUNCTION__, '');
3445 3445
         $notices = EE_Error::get_notices(false);
3446 3446
         // overwrite default success messages //BUT ONLY if overwrite not overridden
3447
-        if (! $override_overwrite || ! empty($notices['errors'])) {
3447
+        if ( ! $override_overwrite || ! empty($notices['errors'])) {
3448 3448
             EE_Error::overwrite_success();
3449 3449
         }
3450
-        if (! $override_overwrite && ! empty($what) && ! empty($action_desc) && empty($notices['errors'])) {
3450
+        if ( ! $override_overwrite && ! empty($what) && ! empty($action_desc) && empty($notices['errors'])) {
3451 3451
             // how many records affected ? more than one record ? or just one ?
3452 3452
             EE_Error::add_success(
3453 3453
                 sprintf(
@@ -3468,7 +3468,7 @@  discard block
 block discarded – undo
3468 3468
             );
3469 3469
         }
3470 3470
         // check that $query_args isn't something crazy
3471
-        if (! is_array($query_args)) {
3471
+        if ( ! is_array($query_args)) {
3472 3472
             $query_args = [];
3473 3473
         }
3474 3474
         /**
@@ -3500,7 +3500,7 @@  discard block
 block discarded – undo
3500 3500
             $redirect_url = admin_url('admin.php');
3501 3501
         }
3502 3502
         // merge any default query_args set in _default_route_query_args property
3503
-        if (! empty($this->_default_route_query_args) && ! $this->_is_UI_request) {
3503
+        if ( ! empty($this->_default_route_query_args) && ! $this->_is_UI_request) {
3504 3504
             $args_to_merge = [];
3505 3505
             foreach ($this->_default_route_query_args as $query_param => $query_value) {
3506 3506
                 // is there a wp_referer array in our _default_route_query_args property?
@@ -3512,15 +3512,15 @@  discard block
 block discarded – undo
3512 3512
                         }
3513 3513
                         // finally we will override any arguments in the referer with
3514 3514
                         // what might be set on the _default_route_query_args array.
3515
-                        if (isset($this->_default_route_query_args[ $reference ])) {
3516
-                            $args_to_merge[ $reference ] = urlencode($this->_default_route_query_args[ $reference ]);
3515
+                        if (isset($this->_default_route_query_args[$reference])) {
3516
+                            $args_to_merge[$reference] = urlencode($this->_default_route_query_args[$reference]);
3517 3517
                         } else {
3518
-                            $args_to_merge[ $reference ] = urlencode($value);
3518
+                            $args_to_merge[$reference] = urlencode($value);
3519 3519
                         }
3520 3520
                     }
3521 3521
                     continue;
3522 3522
                 }
3523
-                $args_to_merge[ $query_param ] = $query_value;
3523
+                $args_to_merge[$query_param] = $query_value;
3524 3524
             }
3525 3525
             // now let's merge these arguments but override with what was specifically sent in to the
3526 3526
             // redirect.
@@ -3532,19 +3532,19 @@  discard block
 block discarded – undo
3532 3532
         if (isset($query_args['action'])) {
3533 3533
             // manually generate wp_nonce and merge that with the query vars
3534 3534
             // becuz the wp_nonce_url function wrecks havoc on some vars
3535
-            $query_args['_wpnonce'] = wp_create_nonce($query_args['action'] . '_nonce');
3535
+            $query_args['_wpnonce'] = wp_create_nonce($query_args['action'].'_nonce');
3536 3536
         }
3537 3537
         // we're adding some hooks and filters in here for processing any things just before redirects
3538 3538
         // (example: an admin page has done an insert or update and we want to run something after that).
3539
-        do_action('AHEE_redirect_' . $this->class_name . $this->_req_action, $query_args);
3539
+        do_action('AHEE_redirect_'.$this->class_name.$this->_req_action, $query_args);
3540 3540
         $redirect_url = apply_filters(
3541
-            'FHEE_redirect_' . $this->class_name . $this->_req_action,
3541
+            'FHEE_redirect_'.$this->class_name.$this->_req_action,
3542 3542
             EE_Admin_Page::add_query_args_and_nonce($query_args, $redirect_url),
3543 3543
             $query_args
3544 3544
         );
3545 3545
         // check if we're doing ajax.  If we are then lets just return the results and js can handle how it wants.
3546 3546
         if ($this->request->isAjax()) {
3547
-            $default_data                    = [
3547
+            $default_data = [
3548 3548
                 'close'        => true,
3549 3549
                 'redirect_url' => $redirect_url,
3550 3550
                 'where'        => 'main',
@@ -3594,7 +3594,7 @@  discard block
 block discarded – undo
3594 3594
         }
3595 3595
         $this->_template_args['notices'] = EE_Error::get_notices();
3596 3596
         // IF this isn't ajax we need to create a transient for the notices using the route (however, overridden if $sticky_notices == true)
3597
-        if (! $this->request->isAjax() || $sticky_notices) {
3597
+        if ( ! $this->request->isAjax() || $sticky_notices) {
3598 3598
             $route = isset($query_args['action']) ? $query_args['action'] : 'default';
3599 3599
             $this->_add_transient(
3600 3600
                 $route,
@@ -3634,7 +3634,7 @@  discard block
 block discarded – undo
3634 3634
         $exclude_nonce = false
3635 3635
     ) {
3636 3636
         // first let's validate the action (if $base_url is FALSE otherwise validation will happen further along)
3637
-        if (empty($base_url) && ! isset($this->_page_routes[ $action ])) {
3637
+        if (empty($base_url) && ! isset($this->_page_routes[$action])) {
3638 3638
             throw new EE_Error(
3639 3639
                 sprintf(
3640 3640
                     esc_html__(
@@ -3645,7 +3645,7 @@  discard block
 block discarded – undo
3645 3645
                 )
3646 3646
             );
3647 3647
         }
3648
-        if (! isset($this->_labels['buttons'][ $type ])) {
3648
+        if ( ! isset($this->_labels['buttons'][$type])) {
3649 3649
             throw new EE_Error(
3650 3650
                 sprintf(
3651 3651
                     esc_html__(
@@ -3658,7 +3658,7 @@  discard block
 block discarded – undo
3658 3658
         }
3659 3659
         // finally check user access for this button.
3660 3660
         $has_access = $this->check_user_access($action, true);
3661
-        if (! $has_access) {
3661
+        if ( ! $has_access) {
3662 3662
             return '';
3663 3663
         }
3664 3664
         $_base_url  = ! $base_url ? $this->_admin_base_url : $base_url;
@@ -3666,11 +3666,11 @@  discard block
 block discarded – undo
3666 3666
             'action' => $action,
3667 3667
         ];
3668 3668
         // merge extra_request args but make sure our original action takes precedence and doesn't get overwritten.
3669
-        if (! empty($extra_request)) {
3669
+        if ( ! empty($extra_request)) {
3670 3670
             $query_args = array_merge($extra_request, $query_args);
3671 3671
         }
3672 3672
         $url = EE_Admin_Page::add_query_args_and_nonce($query_args, $_base_url, false, $exclude_nonce);
3673
-        return EEH_Template::get_button_or_link($url, $this->_labels['buttons'][ $type ], $class);
3673
+        return EEH_Template::get_button_or_link($url, $this->_labels['buttons'][$type], $class);
3674 3674
     }
3675 3675
 
3676 3676
 
@@ -3696,7 +3696,7 @@  discard block
 block discarded – undo
3696 3696
                 'FHEE__EE_Admin_Page___per_page_screen_options__default',
3697 3697
                 20
3698 3698
             ),
3699
-            'option'  => $this->_current_page . '_' . $this->_current_view . '_per_page',
3699
+            'option'  => $this->_current_page.'_'.$this->_current_view.'_per_page',
3700 3700
         ];
3701 3701
         // ONLY add the screen option if the user has access to it.
3702 3702
         if ($this->check_user_access($this->_current_view, true)) {
@@ -3717,18 +3717,18 @@  discard block
 block discarded – undo
3717 3717
     {
3718 3718
         if ($this->request->requestParamIsSet('wp_screen_options')) {
3719 3719
             check_admin_referer('screen-options-nonce', 'screenoptionnonce');
3720
-            if (! $user = wp_get_current_user()) {
3720
+            if ( ! $user = wp_get_current_user()) {
3721 3721
                 return;
3722 3722
             }
3723 3723
             $option = $this->request->getRequestParam('wp_screen_options[option]', '', 'key');
3724
-            if (! $option) {
3724
+            if ( ! $option) {
3725 3725
                 return;
3726 3726
             }
3727 3727
             $value      = $this->request->getRequestParam('wp_screen_options[value]', 0, 'int');
3728 3728
             $map_option = $option;
3729 3729
             $option     = str_replace('-', '_', $option);
3730 3730
             switch ($map_option) {
3731
-                case $this->_current_page . '_' . $this->_current_view . '_per_page':
3731
+                case $this->_current_page.'_'.$this->_current_view.'_per_page':
3732 3732
                     $max_value = apply_filters(
3733 3733
                         'FHEE__EE_Admin_Page___set_per_page_screen_options__max_value',
3734 3734
                         999,
@@ -3785,13 +3785,13 @@  discard block
 block discarded – undo
3785 3785
     protected function _add_transient($route, $data, $notices = false, $skip_route_verify = false)
3786 3786
     {
3787 3787
         $user_id = get_current_user_id();
3788
-        if (! $skip_route_verify) {
3788
+        if ( ! $skip_route_verify) {
3789 3789
             $this->_verify_route($route);
3790 3790
         }
3791 3791
         // now let's set the string for what kind of transient we're setting
3792 3792
         $transient = $notices
3793
-            ? 'ee_rte_n_tx_' . $route . '_' . $user_id
3794
-            : 'rte_tx_' . $route . '_' . $user_id;
3793
+            ? 'ee_rte_n_tx_'.$route.'_'.$user_id
3794
+            : 'rte_tx_'.$route.'_'.$user_id;
3795 3795
         $data      = $notices ? ['notices' => $data] : $data;
3796 3796
         // is there already a transient for this route?  If there is then let's ADD to that transient
3797 3797
         $existing = is_multisite() && is_network_admin()
@@ -3820,8 +3820,8 @@  discard block
 block discarded – undo
3820 3820
         $user_id   = get_current_user_id();
3821 3821
         $route     = ! $route ? $this->_req_action : $route;
3822 3822
         $transient = $notices
3823
-            ? 'ee_rte_n_tx_' . $route . '_' . $user_id
3824
-            : 'rte_tx_' . $route . '_' . $user_id;
3823
+            ? 'ee_rte_n_tx_'.$route.'_'.$user_id
3824
+            : 'rte_tx_'.$route.'_'.$user_id;
3825 3825
         $data      = is_multisite() && is_network_admin()
3826 3826
             ? get_site_transient($transient)
3827 3827
             : get_transient($transient);
@@ -4062,7 +4062,7 @@  discard block
 block discarded – undo
4062 4062
      */
4063 4063
     protected function _next_link($url, $class = 'dashicons dashicons-arrow-right')
4064 4064
     {
4065
-        return '<a class="' . $class . '" href="' . $url . '"></a>';
4065
+        return '<a class="'.$class.'" href="'.$url.'"></a>';
4066 4066
     }
4067 4067
 
4068 4068
 
@@ -4075,7 +4075,7 @@  discard block
 block discarded – undo
4075 4075
      */
4076 4076
     protected function _previous_link($url, $class = 'dashicons dashicons-arrow-left')
4077 4077
     {
4078
-        return '<a class="' . $class . '" href="' . $url . '"></a>';
4078
+        return '<a class="'.$class.'" href="'.$url.'"></a>';
4079 4079
     }
4080 4080
 
4081 4081
 
@@ -4223,13 +4223,13 @@  discard block
 block discarded – undo
4223 4223
         ?callable $callback = null
4224 4224
     ): bool {
4225 4225
         $entity_ID = absint($entity_ID);
4226
-        if (! $entity_ID) {
4226
+        if ( ! $entity_ID) {
4227 4227
             $this->trashRestoreDeleteError($action, $entity_model);
4228 4228
         }
4229 4229
         $result = 0;
4230 4230
         try {
4231 4231
             $entity = $entity_model->get_one_by_ID($entity_ID);
4232
-            if (! $entity instanceof EE_Base_Class) {
4232
+            if ( ! $entity instanceof EE_Base_Class) {
4233 4233
                 throw new DomainException(
4234 4234
                     sprintf(
4235 4235
                         esc_html__(
@@ -4280,7 +4280,7 @@  discard block
 block discarded – undo
4280 4280
                 )
4281 4281
             );
4282 4282
         }
4283
-        if (! $entity_model->has_field($delete_column)) {
4283
+        if ( ! $entity_model->has_field($delete_column)) {
4284 4284
             throw new DomainException(
4285 4285
                 sprintf(
4286 4286
                     esc_html__(
Please login to merge, or discard this patch.
core/EE_Addon.core.php 2 patches
Indentation   +859 added lines, -859 removed lines patch added patch discarded remove patch
@@ -18,870 +18,870 @@
 block discarded – undo
18 18
  */
19 19
 abstract class EE_Addon extends EE_Configurable implements RequiresDependencyMapInterface, RequiresDomainInterface
20 20
 {
21
-    /**
22
-     * prefix to be added onto an addon's plugin slug to make a wp option name
23
-     * which will be used to store the plugin's activation history
24
-     */
25
-    const ee_addon_version_history_option_prefix = 'ee_version_history_';
26
-
27
-    /**
28
-     * @var $_version
29
-     * @type string
30
-     */
31
-    protected $_version = '';
32
-
33
-    /**
34
-     * @var $_min_core_version
35
-     * @type string
36
-     */
37
-    protected $_min_core_version = '';
38
-
39
-    /**
40
-     * derived from plugin 'main_file_path using plugin_basename()
41
-     *
42
-     * @type string $_plugin_basename
43
-     */
44
-    protected $_plugin_basename = '';
45
-
46
-    /**
47
-     * A non-internationalized name to identify this addon for use in URLs, etc
48
-     *
49
-     * @type string $_plugin_slug
50
-     */
51
-    protected $_plugin_slug = '';
52
-
53
-    /**
54
-     * A non-internationalized name to identify this addon. Eg 'Calendar','MailChimp',etc/
55
-     *
56
-     * @type string _addon_name
57
-     */
58
-    protected $_addon_name = '';
59
-
60
-    /**
61
-     * one of the EE_System::req_type_* constants
62
-     *
63
-     * @type int $_req_type
64
-     */
65
-    protected $_req_type;
66
-
67
-    /**
68
-     * page slug to be used when generating the "Settings" link on the WP plugin page
69
-     *
70
-     * @type string $_plugin_action_slug
71
-     */
72
-    protected $_plugin_action_slug = '';
73
-
74
-    /**
75
-     * if not empty, inserts a new table row after this plugin's row on the WP Plugins page
76
-     * that can be used for adding upgrading/marketing info
77
-     *
78
-     * @type array $_plugins_page_row
79
-     */
80
-    protected $_plugins_page_row = [];
81
-
82
-
83
-    /**
84
-     *    filepath to the main file, which can be used for register_activation_hook, register_deactivation_hook, etc.
85
-     *
86
-     * @type string
87
-     */
88
-    protected $_main_plugin_file;
89
-
90
-    /**
91
-     *    This is the slug used to identify this add-on within the plugin update engine.
92
-     *
93
-     * @type string
94
-     */
95
-    protected $pue_slug = '';
96
-
97
-
98
-    /**
99
-     * @var EE_Dependency_Map $dependency_map
100
-     */
101
-    private $dependency_map;
102
-
103
-
104
-    /**
105
-     * @var DomainInterface $domain
106
-     */
107
-    private $domain;
108
-
109
-
110
-    /**
111
-     * @param EE_Dependency_Map|null $dependency_map [optional]
112
-     * @param DomainInterface|null   $domain         [optional]
113
-     */
114
-    public function __construct(EE_Dependency_Map $dependency_map = null, DomainInterface $domain = null)
115
-    {
116
-        if ($dependency_map instanceof EE_Dependency_Map) {
117
-            $this->setDependencyMap($dependency_map);
118
-        }
119
-        if ($domain instanceof DomainInterface) {
120
-            $this->setDomain($domain);
121
-        }
122
-        add_action('AHEE__EE_System__load_controllers__load_admin_controllers', [$this, 'admin_init']);
123
-    }
124
-
125
-
126
-    /**
127
-     * @param EE_Dependency_Map $dependency_map
128
-     */
129
-    public function setDependencyMap($dependency_map)
130
-    {
131
-        $this->dependency_map = $dependency_map;
132
-    }
133
-
134
-
135
-    /**
136
-     * @return EE_Dependency_Map
137
-     */
138
-    public function dependencyMap(): ?EE_Dependency_Map
139
-    {
140
-        return $this->dependency_map;
141
-    }
142
-
143
-
144
-    /**
145
-     * @param DomainInterface $domain
146
-     */
147
-    public function setDomain(DomainInterface $domain)
148
-    {
149
-        $this->domain = $domain;
150
-    }
151
-
152
-
153
-    /**
154
-     * @return DomainInterface
155
-     */
156
-    public function domain(): ?DomainInterface
157
-    {
158
-        return $this->domain;
159
-    }
160
-
161
-
162
-    /**
163
-     * @param string $version
164
-     */
165
-    public function set_version(string $version = '')
166
-    {
167
-        $this->_version = $version;
168
-    }
169
-
170
-
171
-    /**
172
-     * get__version
173
-     *
174
-     * @return string
175
-     */
176
-    public function version(): string
177
-    {
178
-        return $this->_version;
179
-    }
180
-
181
-
182
-    /**
183
-     * @param mixed $min_core_version
184
-     */
185
-    public function set_min_core_version($min_core_version = null)
186
-    {
187
-        $this->_min_core_version = $min_core_version;
188
-    }
189
-
190
-
191
-    /**
192
-     * get__min_core_version
193
-     *
194
-     * @return string
195
-     */
196
-    public function min_core_version(): string
197
-    {
198
-        return $this->_min_core_version;
199
-    }
200
-
201
-
202
-    /**
203
-     * Sets addon_name
204
-     *
205
-     * @param string $addon_name
206
-     */
207
-    public function set_name(string $addon_name)
208
-    {
209
-        $this->_addon_name = $addon_name;
210
-    }
211
-
212
-
213
-    /**
214
-     * Gets addon_name
215
-     *
216
-     * @return string
217
-     */
218
-    public function name()
219
-    {
220
-        return $this->_addon_name;
221
-    }
222
-
223
-
224
-    /**
225
-     * @return string
226
-     */
227
-    public function plugin_basename(): string
228
-    {
229
-
230
-        return $this->_plugin_basename;
231
-    }
232
-
233
-
234
-    /**
235
-     * @param string $plugin_basename
236
-     */
237
-    public function set_plugin_basename(string $plugin_basename)
238
-    {
239
-
240
-        $this->_plugin_basename = $plugin_basename;
241
-    }
242
-
243
-
244
-    /**
245
-     * @return string
246
-     */
247
-    public function plugin_slug(): string
248
-    {
249
-
250
-        return $this->_plugin_slug;
251
-    }
252
-
253
-
254
-    /**
255
-     * @param string $plugin_slug
256
-     */
257
-    public function set_plugin_slug(string $plugin_slug)
258
-    {
259
-
260
-        $this->_plugin_slug = $plugin_slug;
261
-    }
262
-
263
-
264
-    /**
265
-     * @return string
266
-     */
267
-    public function plugin_action_slug(): string
268
-    {
269
-
270
-        return $this->_plugin_action_slug;
271
-    }
272
-
273
-
274
-    /**
275
-     * @param string $plugin_action_slug
276
-     */
277
-    public function set_plugin_action_slug(string $plugin_action_slug)
278
-    {
279
-
280
-        $this->_plugin_action_slug = $plugin_action_slug;
281
-    }
282
-
283
-
284
-    /**
285
-     * @return array
286
-     */
287
-    public function get_plugins_page_row(): array
288
-    {
289
-
290
-        return $this->_plugins_page_row;
291
-    }
292
-
293
-
294
-    /**
295
-     * @param array|string $plugins_page_row
296
-     */
297
-    public function set_plugins_page_row(array $plugins_page_row = [])
298
-    {
299
-        // sigh.... check for example content that I stupidly merged to master and remove it if found
300
-        if (
301
-            ! is_array($plugins_page_row)
302
-            && strpos($plugins_page_row, '<h3>Promotions Addon Upsell Info</h3>') !== false
303
-        ) {
304
-            $plugins_page_row = [];
305
-        }
306
-        $this->_plugins_page_row = (array) $plugins_page_row;
307
-    }
308
-
309
-
310
-    /**
311
-     * Called when EE core detects this addon has been activated for the first time.
312
-     * If the site isn't in maintenance mode, should setup the addon's database
313
-     *
314
-     * @return void
315
-     * @throws EE_Error
316
-     */
317
-    public function new_install()
318
-    {
319
-        $classname = get_class($this);
320
-        do_action("AHEE__{$classname}__new_install");
321
-        do_action('AHEE__EE_Addon__new_install', $this);
322
-        EE_Maintenance_Mode::instance()->set_maintenance_mode_if_db_old();
323
-        add_action(
324
-            'AHEE__EE_System__perform_activations_upgrades_and_migrations',
325
-            [$this, 'initialize_db_if_no_migrations_required']
326
-        );
327
-    }
328
-
329
-
330
-    /**
331
-     * Called when EE core detects this addon has been reactivated. When this happens,
332
-     * it's good to just check that your data is still intact
333
-     *
334
-     * @return void
335
-     * @throws EE_Error
336
-     */
337
-    public function reactivation()
338
-    {
339
-        $classname = get_class($this);
340
-        do_action("AHEE__{$classname}__reactivation");
341
-        do_action('AHEE__EE_Addon__reactivation', $this);
342
-        EE_Maintenance_Mode::instance()->set_maintenance_mode_if_db_old();
343
-        add_action(
344
-            'AHEE__EE_System__perform_activations_upgrades_and_migrations',
345
-            [$this, 'initialize_db_if_no_migrations_required']
346
-        );
347
-    }
348
-
349
-
350
-    /**
351
-     * Called when the registered deactivation hook for this addon fires.
352
-     *
353
-     * @throws EE_Error
354
-     */
355
-    public function deactivation()
356
-    {
357
-        $classname = get_class($this);
358
-        do_action("AHEE__{$classname}__deactivation");
359
-        do_action('AHEE__EE_Addon__deactivation', $this);
360
-        // check if the site no longer needs to be in maintenance mode
361
-        EE_Register_Addon::deregister($this->name());
362
-        EE_Maintenance_Mode::instance()->set_maintenance_mode_if_db_old();
363
-    }
364
-
365
-
366
-    /**
367
-     * Takes care of double-checking that we're not in maintenance mode, and then
368
-     * initializing this addon's necessary initial data. This is called by default on new activations
369
-     * and reactivations.
370
-     *
371
-     * @param bool $verify_schema whether to verify the database's schema for this addon, or just its data.
372
-     *                               This is a resource-intensive job so we prefer to only do it when necessary
373
-     * @return void
374
-     * @throws EE_Error
375
-     * @throws InvalidInterfaceException
376
-     * @throws InvalidDataTypeException
377
-     * @throws InvalidArgumentException
378
-     * @throws ReflectionException
379
-     */
380
-    public function initialize_db_if_no_migrations_required($verify_schema = true)
381
-    {
382
-        if ($verify_schema === '') {
383
-            // wp core bug imo: if no args are passed to `do_action('some_hook_name')` besides the hook's name
384
-            // (ie, no 2nd or 3rd arguments), instead of calling the registered callbacks with no arguments, it
385
-            // calls them with an argument of an empty string (ie ""), which evaluates to false
386
-            // so we need to treat the empty string as if nothing had been passed, and should instead use the default
387
-            $verify_schema = true;
388
-        }
389
-        if (EE_Maintenance_Mode::instance()->level() !== EE_Maintenance_Mode::level_2_complete_maintenance) {
390
-            if ($verify_schema) {
391
-                $this->initialize_db();
392
-            }
393
-            $this->initialize_default_data();
394
-            // @todo: this will probably need to be adjusted in 4.4 as the array changed formats I believe
395
-            EE_Data_Migration_Manager::instance()->update_current_database_state_to(
396
-                [
397
-                    'slug'    => $this->name(),
398
-                    'version' => $this->version(),
399
-                ]
400
-            );
401
-            /* make sure core's data is a-ok
21
+	/**
22
+	 * prefix to be added onto an addon's plugin slug to make a wp option name
23
+	 * which will be used to store the plugin's activation history
24
+	 */
25
+	const ee_addon_version_history_option_prefix = 'ee_version_history_';
26
+
27
+	/**
28
+	 * @var $_version
29
+	 * @type string
30
+	 */
31
+	protected $_version = '';
32
+
33
+	/**
34
+	 * @var $_min_core_version
35
+	 * @type string
36
+	 */
37
+	protected $_min_core_version = '';
38
+
39
+	/**
40
+	 * derived from plugin 'main_file_path using plugin_basename()
41
+	 *
42
+	 * @type string $_plugin_basename
43
+	 */
44
+	protected $_plugin_basename = '';
45
+
46
+	/**
47
+	 * A non-internationalized name to identify this addon for use in URLs, etc
48
+	 *
49
+	 * @type string $_plugin_slug
50
+	 */
51
+	protected $_plugin_slug = '';
52
+
53
+	/**
54
+	 * A non-internationalized name to identify this addon. Eg 'Calendar','MailChimp',etc/
55
+	 *
56
+	 * @type string _addon_name
57
+	 */
58
+	protected $_addon_name = '';
59
+
60
+	/**
61
+	 * one of the EE_System::req_type_* constants
62
+	 *
63
+	 * @type int $_req_type
64
+	 */
65
+	protected $_req_type;
66
+
67
+	/**
68
+	 * page slug to be used when generating the "Settings" link on the WP plugin page
69
+	 *
70
+	 * @type string $_plugin_action_slug
71
+	 */
72
+	protected $_plugin_action_slug = '';
73
+
74
+	/**
75
+	 * if not empty, inserts a new table row after this plugin's row on the WP Plugins page
76
+	 * that can be used for adding upgrading/marketing info
77
+	 *
78
+	 * @type array $_plugins_page_row
79
+	 */
80
+	protected $_plugins_page_row = [];
81
+
82
+
83
+	/**
84
+	 *    filepath to the main file, which can be used for register_activation_hook, register_deactivation_hook, etc.
85
+	 *
86
+	 * @type string
87
+	 */
88
+	protected $_main_plugin_file;
89
+
90
+	/**
91
+	 *    This is the slug used to identify this add-on within the plugin update engine.
92
+	 *
93
+	 * @type string
94
+	 */
95
+	protected $pue_slug = '';
96
+
97
+
98
+	/**
99
+	 * @var EE_Dependency_Map $dependency_map
100
+	 */
101
+	private $dependency_map;
102
+
103
+
104
+	/**
105
+	 * @var DomainInterface $domain
106
+	 */
107
+	private $domain;
108
+
109
+
110
+	/**
111
+	 * @param EE_Dependency_Map|null $dependency_map [optional]
112
+	 * @param DomainInterface|null   $domain         [optional]
113
+	 */
114
+	public function __construct(EE_Dependency_Map $dependency_map = null, DomainInterface $domain = null)
115
+	{
116
+		if ($dependency_map instanceof EE_Dependency_Map) {
117
+			$this->setDependencyMap($dependency_map);
118
+		}
119
+		if ($domain instanceof DomainInterface) {
120
+			$this->setDomain($domain);
121
+		}
122
+		add_action('AHEE__EE_System__load_controllers__load_admin_controllers', [$this, 'admin_init']);
123
+	}
124
+
125
+
126
+	/**
127
+	 * @param EE_Dependency_Map $dependency_map
128
+	 */
129
+	public function setDependencyMap($dependency_map)
130
+	{
131
+		$this->dependency_map = $dependency_map;
132
+	}
133
+
134
+
135
+	/**
136
+	 * @return EE_Dependency_Map
137
+	 */
138
+	public function dependencyMap(): ?EE_Dependency_Map
139
+	{
140
+		return $this->dependency_map;
141
+	}
142
+
143
+
144
+	/**
145
+	 * @param DomainInterface $domain
146
+	 */
147
+	public function setDomain(DomainInterface $domain)
148
+	{
149
+		$this->domain = $domain;
150
+	}
151
+
152
+
153
+	/**
154
+	 * @return DomainInterface
155
+	 */
156
+	public function domain(): ?DomainInterface
157
+	{
158
+		return $this->domain;
159
+	}
160
+
161
+
162
+	/**
163
+	 * @param string $version
164
+	 */
165
+	public function set_version(string $version = '')
166
+	{
167
+		$this->_version = $version;
168
+	}
169
+
170
+
171
+	/**
172
+	 * get__version
173
+	 *
174
+	 * @return string
175
+	 */
176
+	public function version(): string
177
+	{
178
+		return $this->_version;
179
+	}
180
+
181
+
182
+	/**
183
+	 * @param mixed $min_core_version
184
+	 */
185
+	public function set_min_core_version($min_core_version = null)
186
+	{
187
+		$this->_min_core_version = $min_core_version;
188
+	}
189
+
190
+
191
+	/**
192
+	 * get__min_core_version
193
+	 *
194
+	 * @return string
195
+	 */
196
+	public function min_core_version(): string
197
+	{
198
+		return $this->_min_core_version;
199
+	}
200
+
201
+
202
+	/**
203
+	 * Sets addon_name
204
+	 *
205
+	 * @param string $addon_name
206
+	 */
207
+	public function set_name(string $addon_name)
208
+	{
209
+		$this->_addon_name = $addon_name;
210
+	}
211
+
212
+
213
+	/**
214
+	 * Gets addon_name
215
+	 *
216
+	 * @return string
217
+	 */
218
+	public function name()
219
+	{
220
+		return $this->_addon_name;
221
+	}
222
+
223
+
224
+	/**
225
+	 * @return string
226
+	 */
227
+	public function plugin_basename(): string
228
+	{
229
+
230
+		return $this->_plugin_basename;
231
+	}
232
+
233
+
234
+	/**
235
+	 * @param string $plugin_basename
236
+	 */
237
+	public function set_plugin_basename(string $plugin_basename)
238
+	{
239
+
240
+		$this->_plugin_basename = $plugin_basename;
241
+	}
242
+
243
+
244
+	/**
245
+	 * @return string
246
+	 */
247
+	public function plugin_slug(): string
248
+	{
249
+
250
+		return $this->_plugin_slug;
251
+	}
252
+
253
+
254
+	/**
255
+	 * @param string $plugin_slug
256
+	 */
257
+	public function set_plugin_slug(string $plugin_slug)
258
+	{
259
+
260
+		$this->_plugin_slug = $plugin_slug;
261
+	}
262
+
263
+
264
+	/**
265
+	 * @return string
266
+	 */
267
+	public function plugin_action_slug(): string
268
+	{
269
+
270
+		return $this->_plugin_action_slug;
271
+	}
272
+
273
+
274
+	/**
275
+	 * @param string $plugin_action_slug
276
+	 */
277
+	public function set_plugin_action_slug(string $plugin_action_slug)
278
+	{
279
+
280
+		$this->_plugin_action_slug = $plugin_action_slug;
281
+	}
282
+
283
+
284
+	/**
285
+	 * @return array
286
+	 */
287
+	public function get_plugins_page_row(): array
288
+	{
289
+
290
+		return $this->_plugins_page_row;
291
+	}
292
+
293
+
294
+	/**
295
+	 * @param array|string $plugins_page_row
296
+	 */
297
+	public function set_plugins_page_row(array $plugins_page_row = [])
298
+	{
299
+		// sigh.... check for example content that I stupidly merged to master and remove it if found
300
+		if (
301
+			! is_array($plugins_page_row)
302
+			&& strpos($plugins_page_row, '<h3>Promotions Addon Upsell Info</h3>') !== false
303
+		) {
304
+			$plugins_page_row = [];
305
+		}
306
+		$this->_plugins_page_row = (array) $plugins_page_row;
307
+	}
308
+
309
+
310
+	/**
311
+	 * Called when EE core detects this addon has been activated for the first time.
312
+	 * If the site isn't in maintenance mode, should setup the addon's database
313
+	 *
314
+	 * @return void
315
+	 * @throws EE_Error
316
+	 */
317
+	public function new_install()
318
+	{
319
+		$classname = get_class($this);
320
+		do_action("AHEE__{$classname}__new_install");
321
+		do_action('AHEE__EE_Addon__new_install', $this);
322
+		EE_Maintenance_Mode::instance()->set_maintenance_mode_if_db_old();
323
+		add_action(
324
+			'AHEE__EE_System__perform_activations_upgrades_and_migrations',
325
+			[$this, 'initialize_db_if_no_migrations_required']
326
+		);
327
+	}
328
+
329
+
330
+	/**
331
+	 * Called when EE core detects this addon has been reactivated. When this happens,
332
+	 * it's good to just check that your data is still intact
333
+	 *
334
+	 * @return void
335
+	 * @throws EE_Error
336
+	 */
337
+	public function reactivation()
338
+	{
339
+		$classname = get_class($this);
340
+		do_action("AHEE__{$classname}__reactivation");
341
+		do_action('AHEE__EE_Addon__reactivation', $this);
342
+		EE_Maintenance_Mode::instance()->set_maintenance_mode_if_db_old();
343
+		add_action(
344
+			'AHEE__EE_System__perform_activations_upgrades_and_migrations',
345
+			[$this, 'initialize_db_if_no_migrations_required']
346
+		);
347
+	}
348
+
349
+
350
+	/**
351
+	 * Called when the registered deactivation hook for this addon fires.
352
+	 *
353
+	 * @throws EE_Error
354
+	 */
355
+	public function deactivation()
356
+	{
357
+		$classname = get_class($this);
358
+		do_action("AHEE__{$classname}__deactivation");
359
+		do_action('AHEE__EE_Addon__deactivation', $this);
360
+		// check if the site no longer needs to be in maintenance mode
361
+		EE_Register_Addon::deregister($this->name());
362
+		EE_Maintenance_Mode::instance()->set_maintenance_mode_if_db_old();
363
+	}
364
+
365
+
366
+	/**
367
+	 * Takes care of double-checking that we're not in maintenance mode, and then
368
+	 * initializing this addon's necessary initial data. This is called by default on new activations
369
+	 * and reactivations.
370
+	 *
371
+	 * @param bool $verify_schema whether to verify the database's schema for this addon, or just its data.
372
+	 *                               This is a resource-intensive job so we prefer to only do it when necessary
373
+	 * @return void
374
+	 * @throws EE_Error
375
+	 * @throws InvalidInterfaceException
376
+	 * @throws InvalidDataTypeException
377
+	 * @throws InvalidArgumentException
378
+	 * @throws ReflectionException
379
+	 */
380
+	public function initialize_db_if_no_migrations_required($verify_schema = true)
381
+	{
382
+		if ($verify_schema === '') {
383
+			// wp core bug imo: if no args are passed to `do_action('some_hook_name')` besides the hook's name
384
+			// (ie, no 2nd or 3rd arguments), instead of calling the registered callbacks with no arguments, it
385
+			// calls them with an argument of an empty string (ie ""), which evaluates to false
386
+			// so we need to treat the empty string as if nothing had been passed, and should instead use the default
387
+			$verify_schema = true;
388
+		}
389
+		if (EE_Maintenance_Mode::instance()->level() !== EE_Maintenance_Mode::level_2_complete_maintenance) {
390
+			if ($verify_schema) {
391
+				$this->initialize_db();
392
+			}
393
+			$this->initialize_default_data();
394
+			// @todo: this will probably need to be adjusted in 4.4 as the array changed formats I believe
395
+			EE_Data_Migration_Manager::instance()->update_current_database_state_to(
396
+				[
397
+					'slug'    => $this->name(),
398
+					'version' => $this->version(),
399
+				]
400
+			);
401
+			/* make sure core's data is a-ok
402 402
              * (at the time of writing, we especially want to verify all the caps are present
403 403
              * because payment method type capabilities are added dynamically, and it's
404 404
              * possible this addon added a payment method. But it's also possible
405 405
              * other data needs to be verified)
406 406
              */
407
-            EEH_Activation::initialize_db_content();
408
-            /** @var EventEspresso\core\domain\services\custom_post_types\RewriteRules $rewrite_rules */
409
-            $rewrite_rules = LoaderFactory::getLoader()->getShared(
410
-                'EventEspresso\core\domain\services\custom_post_types\RewriteRules'
411
-            );
412
-            $rewrite_rules->flushRewriteRules();
413
-            // in case there are lots of addons being activated at once, let's force garbage collection
414
-            // to help avoid memory limit errors
415
-            // EEH_Debug_Tools::instance()->measure_memory( 'db content initialized for ' . get_class( $this), true );
416
-            gc_collect_cycles();
417
-        } else {
418
-            // ask the data migration manager to init this addon's data
419
-            // when migrations are finished because we can't do it now
420
-            EE_Data_Migration_Manager::instance()->enqueue_db_initialization_for($this->name());
421
-        }
422
-    }
423
-
424
-
425
-    /**
426
-     * Used to setup this addon's database tables, but not necessarily any default
427
-     * data in them. The default is to actually use the most up-to-date data migration script
428
-     * for this addon, and just use its schema_changes_before_migration() and schema_changes_after_migration()
429
-     * methods to setup the db.
430
-     *
431
-     * @throws EE_Error
432
-     * @throws ReflectionException
433
-     */
434
-    public function initialize_db()
435
-    {
436
-        // find the migration script that sets the database to be compatible with the code
437
-        $current_dms_name = EE_Data_Migration_Manager::instance()->get_most_up_to_date_dms($this->name());
438
-        if ($current_dms_name) {
439
-            $current_data_migration_script = EE_Registry::instance()->load_dms($current_dms_name);
440
-            $current_data_migration_script->set_migrating(false);
441
-            $current_data_migration_script->schema_changes_before_migration();
442
-            $current_data_migration_script->schema_changes_after_migration();
443
-            if ($current_data_migration_script->get_errors()) {
444
-                foreach ($current_data_migration_script->get_errors() as $error) {
445
-                    EE_Error::add_error($error, __FILE__, __FUNCTION__, __LINE__);
446
-                }
447
-            }
448
-        }
449
-        // if not DMS was found that should be ok. This addon just doesn't require any database changes
450
-        EE_Data_Migration_Manager::instance()->update_current_database_state_to(
451
-            [
452
-                'slug'    => $this->name(),
453
-                'version' => $this->version(),
454
-            ]
455
-        );
456
-    }
457
-
458
-
459
-    /**
460
-     * If you want to setup default data for the addon, override this method, and call
461
-     * parent::initialize_default_data() from within it. This is normally called
462
-     * from EE_Addon::initialize_db_if_no_migrations_required(), just after EE_Addon::initialize_db()
463
-     * and should verify default data is present (but this is also called
464
-     * on reactivations and just after migrations, so please verify you actually want
465
-     * to ADD default data, because it may already be present).
466
-     * However, please call this parent (currently it just fires a hook which other
467
-     * addons may be depending on)
468
-     */
469
-    public function initialize_default_data()
470
-    {
471
-        /**
472
-         * Called when an addon is ensuring its default data is set (possibly called
473
-         * on a reactivation, so first check for the absence of other data before setting
474
-         * default data)
475
-         *
476
-         * @param EE_Addon $addon the addon that called this
477
-         */
478
-        do_action('AHEE__EE_Addon__initialize_default_data__begin', $this);
479
-        // override to insert default data. It is safe to use the models here
480
-        // because the site should not be in maintenance mode
481
-    }
482
-
483
-
484
-    /**
485
-     * EE Core detected that this addon has been upgraded. We should check if there
486
-     * are any new migration scripts, and if so put the site into maintenance mode until
487
-     * they're ran
488
-     *
489
-     * @return void
490
-     * @throws EE_Error
491
-     */
492
-    public function upgrade()
493
-    {
494
-        $classname = get_class($this);
495
-        do_action("AHEE__{$classname}__upgrade");
496
-        do_action('AHEE__EE_Addon__upgrade', $this);
497
-        EE_Maintenance_Mode::instance()->set_maintenance_mode_if_db_old();
498
-        // also it's possible there is new default data that needs to be added
499
-        add_action(
500
-            'AHEE__EE_System__perform_activations_upgrades_and_migrations',
501
-            [$this, 'initialize_db_if_no_migrations_required']
502
-        );
503
-    }
504
-
505
-
506
-    /**
507
-     * If Core detects this addon has been downgraded, you may want to invoke some special logic here.
508
-     */
509
-    public function downgrade()
510
-    {
511
-        $classname = get_class($this);
512
-        do_action("AHEE__{$classname}__downgrade");
513
-        do_action('AHEE__EE_Addon__downgrade', $this);
514
-        // it's possible there's old default data that needs to be double-checked
515
-        add_action(
516
-            'AHEE__EE_System__perform_activations_upgrades_and_migrations',
517
-            [$this, 'initialize_db_if_no_migrations_required']
518
-        );
519
-    }
520
-
521
-
522
-    /**
523
-     * set_db_update_option_name
524
-     * Until we do something better, we'll just check for migration scripts upon
525
-     * plugin activation only. In the future, we'll want to do it on plugin updates too
526
-     *
527
-     * @return bool
528
-     */
529
-    public function set_db_update_option_name(): bool
530
-    {
531
-        EE_Error::doing_it_wrong(
532
-            __FUNCTION__,
533
-            esc_html__(
534
-                'EE_Addon::set_db_update_option_name was renamed to EE_Addon::set_activation_indicator_option',
535
-                'event_espresso'
536
-            ),
537
-            '4.3.0.alpha.016'
538
-        );
539
-        // let's just handle this on the next request, ok? right now we're just not really ready
540
-        return $this->set_activation_indicator_option();
541
-    }
542
-
543
-
544
-    /**
545
-     * Returns the name of the activation indicator option
546
-     * (an option which is set temporarily to indicate that this addon was just activated)
547
-     *
548
-     * @return string
549
-     * @deprecated since version 4.3.0.alpha.016
550
-     */
551
-    public function get_db_update_option_name(): string
552
-    {
553
-        EE_Error::doing_it_wrong(
554
-            __FUNCTION__,
555
-            esc_html__(
556
-                'EE_Addon::get_db_update_option was renamed to EE_Addon::get_activation_indicator_option_name',
557
-                'event_espresso'
558
-            ),
559
-            '4.3.0.alpha.016'
560
-        );
561
-        return $this->get_activation_indicator_option_name();
562
-    }
563
-
564
-
565
-    /**
566
-     * When the addon is activated, this should be called to set a wordpress option that
567
-     * indicates it was activated. This is especially useful for detecting reactivations.
568
-     *
569
-     * @return bool
570
-     */
571
-    public function set_activation_indicator_option(): bool
572
-    {
573
-        // let's just handle this on the next request, ok? right now we're just not really ready
574
-        return update_option($this->get_activation_indicator_option_name(), true);
575
-    }
576
-
577
-
578
-    /**
579
-     * Gets the name of the wp option which is used to temporarily indicate that this addon was activated
580
-     *
581
-     * @return string
582
-     */
583
-    public function get_activation_indicator_option_name(): string
584
-    {
585
-        return 'ee_activation_' . $this->name();
586
-    }
587
-
588
-
589
-    /**
590
-     * Used by EE_System to set the request type of this addon. Should not be used by addon developers
591
-     *
592
-     * @param int $req_type
593
-     */
594
-    public function set_req_type(int $req_type)
595
-    {
596
-        $this->_req_type = $req_type;
597
-    }
598
-
599
-
600
-    /**
601
-     * Returns the request type of this addon (ie, EE_System::req_type_normal, EE_System::req_type_new_activation,
602
-     * EE_System::req_type_reactivation, EE_System::req_type_upgrade, or EE_System::req_type_downgrade). This is set by
603
-     * EE_System when it is checking for new install or upgrades of addons
604
-     */
605
-    public function detect_req_type($redetect = false): int
606
-    {
607
-        if ($this->_req_type === null || $redetect) {
608
-            $this->detect_activation_or_upgrade();
609
-        }
610
-        return $this->_req_type;
611
-    }
612
-
613
-
614
-    /**
615
-     * Detects the request type for this addon (whether it was just activated, upgrades, a normal request, etc.)
616
-     * Should only be called once per request
617
-     *
618
-     * @return void
619
-     * @throws EE_Error
620
-     */
621
-    public function detect_activation_or_upgrade()
622
-    {
623
-        $activation_history_for_addon = $this->get_activation_history();
624
-        $request_type                 = EE_System::detect_req_type_given_activation_history(
625
-            $activation_history_for_addon,
626
-            $this->get_activation_indicator_option_name(),
627
-            $this->version()
628
-        );
629
-        $this->set_req_type($request_type);
630
-        $classname = get_class($this);
631
-        switch ($request_type) {
632
-            case EE_System::req_type_new_activation:
633
-                do_action("AHEE__{$classname}__detect_activations_or_upgrades__new_activation");
634
-                do_action('AHEE__EE_Addon__detect_activations_or_upgrades__new_activation', $this);
635
-                $this->new_install();
636
-                $this->update_list_of_installed_versions($activation_history_for_addon);
637
-                break;
638
-            case EE_System::req_type_reactivation:
639
-                do_action("AHEE__{$classname}__detect_activations_or_upgrades__reactivation");
640
-                do_action('AHEE__EE_Addon__detect_activations_or_upgrades__reactivation', $this);
641
-                $this->reactivation();
642
-                $this->update_list_of_installed_versions($activation_history_for_addon);
643
-                break;
644
-            case EE_System::req_type_upgrade:
645
-                do_action("AHEE__{$classname}__detect_activations_or_upgrades__upgrade");
646
-                do_action('AHEE__EE_Addon__detect_activations_or_upgrades__upgrade', $this);
647
-                $this->upgrade();
648
-                $this->update_list_of_installed_versions($activation_history_for_addon);
649
-                break;
650
-            case EE_System::req_type_downgrade:
651
-                do_action("AHEE__{$classname}__detect_activations_or_upgrades__downgrade");
652
-                do_action('AHEE__EE_Addon__detect_activations_or_upgrades__downgrade', $this);
653
-                $this->downgrade();
654
-                $this->update_list_of_installed_versions($activation_history_for_addon);
655
-                break;
656
-            case EE_System::req_type_normal:
657
-            default:
658
-                break;
659
-        }
660
-
661
-        do_action("AHEE__{$classname}__detect_if_activation_or_upgrade__complete");
662
-    }
663
-
664
-
665
-    /**
666
-     * Updates the version history for this addon
667
-     *
668
-     * @param array  $version_history
669
-     * @param string $current_version_to_add
670
-     * @return bool success
671
-     */
672
-    public function update_list_of_installed_versions($version_history = null, $current_version_to_add = null): bool
673
-    {
674
-        if (! $version_history) {
675
-            $version_history = $this->get_activation_history();
676
-        }
677
-        if ($current_version_to_add === null) {
678
-            $current_version_to_add = $this->version();
679
-        }
680
-        $version_history[ $current_version_to_add ][] = date('Y-m-d H:i:s', time());
681
-        return update_option($this->get_activation_history_option_name(), $version_history);
682
-    }
683
-
684
-
685
-    /**
686
-     * Gets the name of the wp option that stores the activation history
687
-     * of this addon
688
-     *
689
-     * @return string
690
-     */
691
-    public function get_activation_history_option_name(): string
692
-    {
693
-        return self::ee_addon_version_history_option_prefix . $this->name();
694
-    }
695
-
696
-
697
-    /**
698
-     * Gets the wp option which stores the activation history for this addon
699
-     *
700
-     * @return array
701
-     */
702
-    public function get_activation_history(): array
703
-    {
704
-        return get_option($this->get_activation_history_option_name(), []);
705
-    }
706
-
707
-
708
-    /**
709
-     * @param string $config_section
710
-     */
711
-    public function set_config_section($config_section = '')
712
-    {
713
-        $this->_config_section = ! empty($config_section) ? $config_section : 'addons';
714
-    }
715
-
716
-
717
-    /**
718
-     * Sets the filepath to the main plugin file
719
-     *
720
-     * @param string $filepath
721
-     */
722
-    public function set_main_plugin_file(string $filepath)
723
-    {
724
-        $this->_main_plugin_file = $filepath;
725
-    }
726
-
727
-
728
-    /**
729
-     * gets the filepath to teh main file
730
-     *
731
-     * @return string
732
-     */
733
-    public function get_main_plugin_file(): string
734
-    {
735
-        return $this->_main_plugin_file;
736
-    }
737
-
738
-
739
-    /**
740
-     * Gets the filename (no path) of the main file (the main file loaded
741
-     * by WP)
742
-     *
743
-     * @return string
744
-     */
745
-    public function get_main_plugin_file_basename(): string
746
-    {
747
-        return plugin_basename($this->get_main_plugin_file());
748
-    }
749
-
750
-
751
-    /**
752
-     * Gets the folder name which contains the main plugin file
753
-     *
754
-     * @return string
755
-     */
756
-    public function get_main_plugin_file_dirname(): string
757
-    {
758
-        return dirname($this->get_main_plugin_file());
759
-    }
760
-
761
-
762
-    /**
763
-     * sets hooks used in the admin
764
-     *
765
-     * @return void
766
-     */
767
-    public function admin_init()
768
-    {
769
-        // is admin and not in M-Mode ?
770
-        if (is_admin() && ! EE_Maintenance_Mode::instance()->level()) {
771
-            add_filter('plugin_action_links', [$this, 'plugin_action_links'], 10, 2);
772
-            add_filter('after_plugin_row_' . $this->_plugin_basename, [$this, 'after_plugin_row'], 10, 3);
773
-        }
774
-    }
775
-
776
-
777
-    /**
778
-     * plugin_actions
779
-     * Add a settings link to the Plugins page, so people can go straight from the plugin page to the settings page.
780
-     *
781
-     * @param array  $links
782
-     * @param string $file
783
-     * @return array
784
-     */
785
-    public function plugin_action_links(array $links, string $file): array
786
-    {
787
-        if ($file === $this->plugin_basename() && $this->plugin_action_slug() !== '') {
788
-            // before other links
789
-            array_unshift(
790
-                $links,
791
-                '<a href="admin.php?page=' . $this->plugin_action_slug() . '">'
792
-                . esc_html__('Settings', 'event_espresso')
793
-                . '</a>'
794
-            );
795
-        }
796
-        return $links;
797
-    }
798
-
799
-
800
-    /**
801
-     * after_plugin_row
802
-     * Add additional content to the plugins page plugin row
803
-     * Inserts another row
804
-     *
805
-     * @param string $plugin_file
806
-     * @param array  $plugin_data
807
-     * @param string $status
808
-     * @return void
809
-     */
810
-    public function after_plugin_row(string $plugin_file, array $plugin_data, string $status)
811
-    {
812
-        $after_plugin_row = '';
813
-        $plugins_page_row = $this->get_plugins_page_row();
814
-        if (! empty($plugins_page_row) && $plugin_file === $this->plugin_basename()) {
815
-            $class       = $status ? 'active' : 'inactive';
816
-            $link_text   = isset($plugins_page_row['link_text']) ? $plugins_page_row['link_text'] : '';
817
-            $link_url    = isset($plugins_page_row['link_url']) ? $plugins_page_row['link_url'] : '';
818
-            $description = isset($plugins_page_row['description'])
819
-                ? $plugins_page_row['description']
820
-                : '';
821
-            if (! empty($link_text) && ! empty($link_url) && ! empty($description)) {
822
-                $after_plugin_row .= '<tr id="' . sanitize_title($plugin_file) . '-ee-addon" class="' . $class . '">';
823
-                $after_plugin_row .= '<th class="check-column" scope="row"></th>';
824
-                $after_plugin_row .= '<td class="ee-addon-upsell-info-title-td plugin-title column-primary">';
825
-                $after_plugin_row .= '<p class="ee-addon-upsell-info-dv">
407
+			EEH_Activation::initialize_db_content();
408
+			/** @var EventEspresso\core\domain\services\custom_post_types\RewriteRules $rewrite_rules */
409
+			$rewrite_rules = LoaderFactory::getLoader()->getShared(
410
+				'EventEspresso\core\domain\services\custom_post_types\RewriteRules'
411
+			);
412
+			$rewrite_rules->flushRewriteRules();
413
+			// in case there are lots of addons being activated at once, let's force garbage collection
414
+			// to help avoid memory limit errors
415
+			// EEH_Debug_Tools::instance()->measure_memory( 'db content initialized for ' . get_class( $this), true );
416
+			gc_collect_cycles();
417
+		} else {
418
+			// ask the data migration manager to init this addon's data
419
+			// when migrations are finished because we can't do it now
420
+			EE_Data_Migration_Manager::instance()->enqueue_db_initialization_for($this->name());
421
+		}
422
+	}
423
+
424
+
425
+	/**
426
+	 * Used to setup this addon's database tables, but not necessarily any default
427
+	 * data in them. The default is to actually use the most up-to-date data migration script
428
+	 * for this addon, and just use its schema_changes_before_migration() and schema_changes_after_migration()
429
+	 * methods to setup the db.
430
+	 *
431
+	 * @throws EE_Error
432
+	 * @throws ReflectionException
433
+	 */
434
+	public function initialize_db()
435
+	{
436
+		// find the migration script that sets the database to be compatible with the code
437
+		$current_dms_name = EE_Data_Migration_Manager::instance()->get_most_up_to_date_dms($this->name());
438
+		if ($current_dms_name) {
439
+			$current_data_migration_script = EE_Registry::instance()->load_dms($current_dms_name);
440
+			$current_data_migration_script->set_migrating(false);
441
+			$current_data_migration_script->schema_changes_before_migration();
442
+			$current_data_migration_script->schema_changes_after_migration();
443
+			if ($current_data_migration_script->get_errors()) {
444
+				foreach ($current_data_migration_script->get_errors() as $error) {
445
+					EE_Error::add_error($error, __FILE__, __FUNCTION__, __LINE__);
446
+				}
447
+			}
448
+		}
449
+		// if not DMS was found that should be ok. This addon just doesn't require any database changes
450
+		EE_Data_Migration_Manager::instance()->update_current_database_state_to(
451
+			[
452
+				'slug'    => $this->name(),
453
+				'version' => $this->version(),
454
+			]
455
+		);
456
+	}
457
+
458
+
459
+	/**
460
+	 * If you want to setup default data for the addon, override this method, and call
461
+	 * parent::initialize_default_data() from within it. This is normally called
462
+	 * from EE_Addon::initialize_db_if_no_migrations_required(), just after EE_Addon::initialize_db()
463
+	 * and should verify default data is present (but this is also called
464
+	 * on reactivations and just after migrations, so please verify you actually want
465
+	 * to ADD default data, because it may already be present).
466
+	 * However, please call this parent (currently it just fires a hook which other
467
+	 * addons may be depending on)
468
+	 */
469
+	public function initialize_default_data()
470
+	{
471
+		/**
472
+		 * Called when an addon is ensuring its default data is set (possibly called
473
+		 * on a reactivation, so first check for the absence of other data before setting
474
+		 * default data)
475
+		 *
476
+		 * @param EE_Addon $addon the addon that called this
477
+		 */
478
+		do_action('AHEE__EE_Addon__initialize_default_data__begin', $this);
479
+		// override to insert default data. It is safe to use the models here
480
+		// because the site should not be in maintenance mode
481
+	}
482
+
483
+
484
+	/**
485
+	 * EE Core detected that this addon has been upgraded. We should check if there
486
+	 * are any new migration scripts, and if so put the site into maintenance mode until
487
+	 * they're ran
488
+	 *
489
+	 * @return void
490
+	 * @throws EE_Error
491
+	 */
492
+	public function upgrade()
493
+	{
494
+		$classname = get_class($this);
495
+		do_action("AHEE__{$classname}__upgrade");
496
+		do_action('AHEE__EE_Addon__upgrade', $this);
497
+		EE_Maintenance_Mode::instance()->set_maintenance_mode_if_db_old();
498
+		// also it's possible there is new default data that needs to be added
499
+		add_action(
500
+			'AHEE__EE_System__perform_activations_upgrades_and_migrations',
501
+			[$this, 'initialize_db_if_no_migrations_required']
502
+		);
503
+	}
504
+
505
+
506
+	/**
507
+	 * If Core detects this addon has been downgraded, you may want to invoke some special logic here.
508
+	 */
509
+	public function downgrade()
510
+	{
511
+		$classname = get_class($this);
512
+		do_action("AHEE__{$classname}__downgrade");
513
+		do_action('AHEE__EE_Addon__downgrade', $this);
514
+		// it's possible there's old default data that needs to be double-checked
515
+		add_action(
516
+			'AHEE__EE_System__perform_activations_upgrades_and_migrations',
517
+			[$this, 'initialize_db_if_no_migrations_required']
518
+		);
519
+	}
520
+
521
+
522
+	/**
523
+	 * set_db_update_option_name
524
+	 * Until we do something better, we'll just check for migration scripts upon
525
+	 * plugin activation only. In the future, we'll want to do it on plugin updates too
526
+	 *
527
+	 * @return bool
528
+	 */
529
+	public function set_db_update_option_name(): bool
530
+	{
531
+		EE_Error::doing_it_wrong(
532
+			__FUNCTION__,
533
+			esc_html__(
534
+				'EE_Addon::set_db_update_option_name was renamed to EE_Addon::set_activation_indicator_option',
535
+				'event_espresso'
536
+			),
537
+			'4.3.0.alpha.016'
538
+		);
539
+		// let's just handle this on the next request, ok? right now we're just not really ready
540
+		return $this->set_activation_indicator_option();
541
+	}
542
+
543
+
544
+	/**
545
+	 * Returns the name of the activation indicator option
546
+	 * (an option which is set temporarily to indicate that this addon was just activated)
547
+	 *
548
+	 * @return string
549
+	 * @deprecated since version 4.3.0.alpha.016
550
+	 */
551
+	public function get_db_update_option_name(): string
552
+	{
553
+		EE_Error::doing_it_wrong(
554
+			__FUNCTION__,
555
+			esc_html__(
556
+				'EE_Addon::get_db_update_option was renamed to EE_Addon::get_activation_indicator_option_name',
557
+				'event_espresso'
558
+			),
559
+			'4.3.0.alpha.016'
560
+		);
561
+		return $this->get_activation_indicator_option_name();
562
+	}
563
+
564
+
565
+	/**
566
+	 * When the addon is activated, this should be called to set a wordpress option that
567
+	 * indicates it was activated. This is especially useful for detecting reactivations.
568
+	 *
569
+	 * @return bool
570
+	 */
571
+	public function set_activation_indicator_option(): bool
572
+	{
573
+		// let's just handle this on the next request, ok? right now we're just not really ready
574
+		return update_option($this->get_activation_indicator_option_name(), true);
575
+	}
576
+
577
+
578
+	/**
579
+	 * Gets the name of the wp option which is used to temporarily indicate that this addon was activated
580
+	 *
581
+	 * @return string
582
+	 */
583
+	public function get_activation_indicator_option_name(): string
584
+	{
585
+		return 'ee_activation_' . $this->name();
586
+	}
587
+
588
+
589
+	/**
590
+	 * Used by EE_System to set the request type of this addon. Should not be used by addon developers
591
+	 *
592
+	 * @param int $req_type
593
+	 */
594
+	public function set_req_type(int $req_type)
595
+	{
596
+		$this->_req_type = $req_type;
597
+	}
598
+
599
+
600
+	/**
601
+	 * Returns the request type of this addon (ie, EE_System::req_type_normal, EE_System::req_type_new_activation,
602
+	 * EE_System::req_type_reactivation, EE_System::req_type_upgrade, or EE_System::req_type_downgrade). This is set by
603
+	 * EE_System when it is checking for new install or upgrades of addons
604
+	 */
605
+	public function detect_req_type($redetect = false): int
606
+	{
607
+		if ($this->_req_type === null || $redetect) {
608
+			$this->detect_activation_or_upgrade();
609
+		}
610
+		return $this->_req_type;
611
+	}
612
+
613
+
614
+	/**
615
+	 * Detects the request type for this addon (whether it was just activated, upgrades, a normal request, etc.)
616
+	 * Should only be called once per request
617
+	 *
618
+	 * @return void
619
+	 * @throws EE_Error
620
+	 */
621
+	public function detect_activation_or_upgrade()
622
+	{
623
+		$activation_history_for_addon = $this->get_activation_history();
624
+		$request_type                 = EE_System::detect_req_type_given_activation_history(
625
+			$activation_history_for_addon,
626
+			$this->get_activation_indicator_option_name(),
627
+			$this->version()
628
+		);
629
+		$this->set_req_type($request_type);
630
+		$classname = get_class($this);
631
+		switch ($request_type) {
632
+			case EE_System::req_type_new_activation:
633
+				do_action("AHEE__{$classname}__detect_activations_or_upgrades__new_activation");
634
+				do_action('AHEE__EE_Addon__detect_activations_or_upgrades__new_activation', $this);
635
+				$this->new_install();
636
+				$this->update_list_of_installed_versions($activation_history_for_addon);
637
+				break;
638
+			case EE_System::req_type_reactivation:
639
+				do_action("AHEE__{$classname}__detect_activations_or_upgrades__reactivation");
640
+				do_action('AHEE__EE_Addon__detect_activations_or_upgrades__reactivation', $this);
641
+				$this->reactivation();
642
+				$this->update_list_of_installed_versions($activation_history_for_addon);
643
+				break;
644
+			case EE_System::req_type_upgrade:
645
+				do_action("AHEE__{$classname}__detect_activations_or_upgrades__upgrade");
646
+				do_action('AHEE__EE_Addon__detect_activations_or_upgrades__upgrade', $this);
647
+				$this->upgrade();
648
+				$this->update_list_of_installed_versions($activation_history_for_addon);
649
+				break;
650
+			case EE_System::req_type_downgrade:
651
+				do_action("AHEE__{$classname}__detect_activations_or_upgrades__downgrade");
652
+				do_action('AHEE__EE_Addon__detect_activations_or_upgrades__downgrade', $this);
653
+				$this->downgrade();
654
+				$this->update_list_of_installed_versions($activation_history_for_addon);
655
+				break;
656
+			case EE_System::req_type_normal:
657
+			default:
658
+				break;
659
+		}
660
+
661
+		do_action("AHEE__{$classname}__detect_if_activation_or_upgrade__complete");
662
+	}
663
+
664
+
665
+	/**
666
+	 * Updates the version history for this addon
667
+	 *
668
+	 * @param array  $version_history
669
+	 * @param string $current_version_to_add
670
+	 * @return bool success
671
+	 */
672
+	public function update_list_of_installed_versions($version_history = null, $current_version_to_add = null): bool
673
+	{
674
+		if (! $version_history) {
675
+			$version_history = $this->get_activation_history();
676
+		}
677
+		if ($current_version_to_add === null) {
678
+			$current_version_to_add = $this->version();
679
+		}
680
+		$version_history[ $current_version_to_add ][] = date('Y-m-d H:i:s', time());
681
+		return update_option($this->get_activation_history_option_name(), $version_history);
682
+	}
683
+
684
+
685
+	/**
686
+	 * Gets the name of the wp option that stores the activation history
687
+	 * of this addon
688
+	 *
689
+	 * @return string
690
+	 */
691
+	public function get_activation_history_option_name(): string
692
+	{
693
+		return self::ee_addon_version_history_option_prefix . $this->name();
694
+	}
695
+
696
+
697
+	/**
698
+	 * Gets the wp option which stores the activation history for this addon
699
+	 *
700
+	 * @return array
701
+	 */
702
+	public function get_activation_history(): array
703
+	{
704
+		return get_option($this->get_activation_history_option_name(), []);
705
+	}
706
+
707
+
708
+	/**
709
+	 * @param string $config_section
710
+	 */
711
+	public function set_config_section($config_section = '')
712
+	{
713
+		$this->_config_section = ! empty($config_section) ? $config_section : 'addons';
714
+	}
715
+
716
+
717
+	/**
718
+	 * Sets the filepath to the main plugin file
719
+	 *
720
+	 * @param string $filepath
721
+	 */
722
+	public function set_main_plugin_file(string $filepath)
723
+	{
724
+		$this->_main_plugin_file = $filepath;
725
+	}
726
+
727
+
728
+	/**
729
+	 * gets the filepath to teh main file
730
+	 *
731
+	 * @return string
732
+	 */
733
+	public function get_main_plugin_file(): string
734
+	{
735
+		return $this->_main_plugin_file;
736
+	}
737
+
738
+
739
+	/**
740
+	 * Gets the filename (no path) of the main file (the main file loaded
741
+	 * by WP)
742
+	 *
743
+	 * @return string
744
+	 */
745
+	public function get_main_plugin_file_basename(): string
746
+	{
747
+		return plugin_basename($this->get_main_plugin_file());
748
+	}
749
+
750
+
751
+	/**
752
+	 * Gets the folder name which contains the main plugin file
753
+	 *
754
+	 * @return string
755
+	 */
756
+	public function get_main_plugin_file_dirname(): string
757
+	{
758
+		return dirname($this->get_main_plugin_file());
759
+	}
760
+
761
+
762
+	/**
763
+	 * sets hooks used in the admin
764
+	 *
765
+	 * @return void
766
+	 */
767
+	public function admin_init()
768
+	{
769
+		// is admin and not in M-Mode ?
770
+		if (is_admin() && ! EE_Maintenance_Mode::instance()->level()) {
771
+			add_filter('plugin_action_links', [$this, 'plugin_action_links'], 10, 2);
772
+			add_filter('after_plugin_row_' . $this->_plugin_basename, [$this, 'after_plugin_row'], 10, 3);
773
+		}
774
+	}
775
+
776
+
777
+	/**
778
+	 * plugin_actions
779
+	 * Add a settings link to the Plugins page, so people can go straight from the plugin page to the settings page.
780
+	 *
781
+	 * @param array  $links
782
+	 * @param string $file
783
+	 * @return array
784
+	 */
785
+	public function plugin_action_links(array $links, string $file): array
786
+	{
787
+		if ($file === $this->plugin_basename() && $this->plugin_action_slug() !== '') {
788
+			// before other links
789
+			array_unshift(
790
+				$links,
791
+				'<a href="admin.php?page=' . $this->plugin_action_slug() . '">'
792
+				. esc_html__('Settings', 'event_espresso')
793
+				. '</a>'
794
+			);
795
+		}
796
+		return $links;
797
+	}
798
+
799
+
800
+	/**
801
+	 * after_plugin_row
802
+	 * Add additional content to the plugins page plugin row
803
+	 * Inserts another row
804
+	 *
805
+	 * @param string $plugin_file
806
+	 * @param array  $plugin_data
807
+	 * @param string $status
808
+	 * @return void
809
+	 */
810
+	public function after_plugin_row(string $plugin_file, array $plugin_data, string $status)
811
+	{
812
+		$after_plugin_row = '';
813
+		$plugins_page_row = $this->get_plugins_page_row();
814
+		if (! empty($plugins_page_row) && $plugin_file === $this->plugin_basename()) {
815
+			$class       = $status ? 'active' : 'inactive';
816
+			$link_text   = isset($plugins_page_row['link_text']) ? $plugins_page_row['link_text'] : '';
817
+			$link_url    = isset($plugins_page_row['link_url']) ? $plugins_page_row['link_url'] : '';
818
+			$description = isset($plugins_page_row['description'])
819
+				? $plugins_page_row['description']
820
+				: '';
821
+			if (! empty($link_text) && ! empty($link_url) && ! empty($description)) {
822
+				$after_plugin_row .= '<tr id="' . sanitize_title($plugin_file) . '-ee-addon" class="' . $class . '">';
823
+				$after_plugin_row .= '<th class="check-column" scope="row"></th>';
824
+				$after_plugin_row .= '<td class="ee-addon-upsell-info-title-td plugin-title column-primary">';
825
+				$after_plugin_row .= '<p class="ee-addon-upsell-info-dv">
826 826
 	                <a class="button button--primary" href="' . esc_url_raw($link_url) . '">'
827
-                    . $link_text
828
-                    . ' &nbsp;<span class="dashicons dashicons-arrow-right-alt2" style="margin:0;"></span>'
829
-                    . '</a>
827
+					. $link_text
828
+					. ' &nbsp;<span class="dashicons dashicons-arrow-right-alt2" style="margin:0;"></span>'
829
+					. '</a>
830 830
                 </p>';
831
-                $after_plugin_row .= '</td>';
832
-                $after_plugin_row .= '<td class="ee-addon-upsell-info-desc-td column-description desc">';
833
-                $after_plugin_row .= $description;
834
-                $after_plugin_row .= '</td>';
835
-                $after_plugin_row .= '</tr>';
836
-            } else {
837
-                $after_plugin_row .= $description;
838
-            }
839
-        }
840
-
841
-        echo wp_kses($after_plugin_row, AllowedTags::getAllowedTags());
842
-    }
843
-
844
-
845
-    /**
846
-     * A safe space for addons to add additional logic like setting hooks that need to be set early in the request.
847
-     * Child classes that have logic like that to run can override this method declaration.  This was not made abstract
848
-     * for back compat reasons.
849
-     *
850
-     * This will fire on the `AHEE__EE_System__load_espresso_addons__complete` hook at priority 999.
851
-     *
852
-     * It is recommended, if client code is `de-registering` an add-on, then do it on the
853
-     * `AHEE__EE_System__load_espresso_addons__complete` hook before priority 999 so as to ensure any code logic in this
854
-     * callback does not get run/set in that request.
855
-     *
856
-     * Also, keep in mind that if a registered add-on happens to be deactivated via
857
-     * EE_System::_deactivate_incompatible_addons() because its incompatible, any code executed in this method
858
-     * (including setting hooks etc) will have executed before the plugin was deactivated.  If you use
859
-     * `after_registration` to set any filter and/or action hooks and want to ensure they are removed on this add-on's
860
-     * deactivation, you can override `EE_Addon::deactivation` and unset your hooks and filters there.  Just remember
861
-     * to call `parent::deactivation`.
862
-     *
863
-     * @since 4.9.26
864
-     */
865
-    public function after_registration()
866
-    {
867
-        // cricket chirp... cricket chirp...
868
-    }
869
-
870
-
871
-    /**
872
-     * @return string
873
-     */
874
-    public function getPueSlug(): string
875
-    {
876
-        return $this->pue_slug;
877
-    }
878
-
879
-
880
-    /**
881
-     * @param string $pue_slug
882
-     */
883
-    public function setPueSlug(string $pue_slug)
884
-    {
885
-        $this->pue_slug = $pue_slug;
886
-    }
831
+				$after_plugin_row .= '</td>';
832
+				$after_plugin_row .= '<td class="ee-addon-upsell-info-desc-td column-description desc">';
833
+				$after_plugin_row .= $description;
834
+				$after_plugin_row .= '</td>';
835
+				$after_plugin_row .= '</tr>';
836
+			} else {
837
+				$after_plugin_row .= $description;
838
+			}
839
+		}
840
+
841
+		echo wp_kses($after_plugin_row, AllowedTags::getAllowedTags());
842
+	}
843
+
844
+
845
+	/**
846
+	 * A safe space for addons to add additional logic like setting hooks that need to be set early in the request.
847
+	 * Child classes that have logic like that to run can override this method declaration.  This was not made abstract
848
+	 * for back compat reasons.
849
+	 *
850
+	 * This will fire on the `AHEE__EE_System__load_espresso_addons__complete` hook at priority 999.
851
+	 *
852
+	 * It is recommended, if client code is `de-registering` an add-on, then do it on the
853
+	 * `AHEE__EE_System__load_espresso_addons__complete` hook before priority 999 so as to ensure any code logic in this
854
+	 * callback does not get run/set in that request.
855
+	 *
856
+	 * Also, keep in mind that if a registered add-on happens to be deactivated via
857
+	 * EE_System::_deactivate_incompatible_addons() because its incompatible, any code executed in this method
858
+	 * (including setting hooks etc) will have executed before the plugin was deactivated.  If you use
859
+	 * `after_registration` to set any filter and/or action hooks and want to ensure they are removed on this add-on's
860
+	 * deactivation, you can override `EE_Addon::deactivation` and unset your hooks and filters there.  Just remember
861
+	 * to call `parent::deactivation`.
862
+	 *
863
+	 * @since 4.9.26
864
+	 */
865
+	public function after_registration()
866
+	{
867
+		// cricket chirp... cricket chirp...
868
+	}
869
+
870
+
871
+	/**
872
+	 * @return string
873
+	 */
874
+	public function getPueSlug(): string
875
+	{
876
+		return $this->pue_slug;
877
+	}
878
+
879
+
880
+	/**
881
+	 * @param string $pue_slug
882
+	 */
883
+	public function setPueSlug(string $pue_slug)
884
+	{
885
+		$this->pue_slug = $pue_slug;
886
+	}
887 887
 }
Please login to merge, or discard this patch.
Spacing   +10 added lines, -10 removed lines patch added patch discarded remove patch
@@ -582,7 +582,7 @@  discard block
 block discarded – undo
582 582
      */
583 583
     public function get_activation_indicator_option_name(): string
584 584
     {
585
-        return 'ee_activation_' . $this->name();
585
+        return 'ee_activation_'.$this->name();
586 586
     }
587 587
 
588 588
 
@@ -671,13 +671,13 @@  discard block
 block discarded – undo
671 671
      */
672 672
     public function update_list_of_installed_versions($version_history = null, $current_version_to_add = null): bool
673 673
     {
674
-        if (! $version_history) {
674
+        if ( ! $version_history) {
675 675
             $version_history = $this->get_activation_history();
676 676
         }
677 677
         if ($current_version_to_add === null) {
678 678
             $current_version_to_add = $this->version();
679 679
         }
680
-        $version_history[ $current_version_to_add ][] = date('Y-m-d H:i:s', time());
680
+        $version_history[$current_version_to_add][] = date('Y-m-d H:i:s', time());
681 681
         return update_option($this->get_activation_history_option_name(), $version_history);
682 682
     }
683 683
 
@@ -690,7 +690,7 @@  discard block
 block discarded – undo
690 690
      */
691 691
     public function get_activation_history_option_name(): string
692 692
     {
693
-        return self::ee_addon_version_history_option_prefix . $this->name();
693
+        return self::ee_addon_version_history_option_prefix.$this->name();
694 694
     }
695 695
 
696 696
 
@@ -769,7 +769,7 @@  discard block
 block discarded – undo
769 769
         // is admin and not in M-Mode ?
770 770
         if (is_admin() && ! EE_Maintenance_Mode::instance()->level()) {
771 771
             add_filter('plugin_action_links', [$this, 'plugin_action_links'], 10, 2);
772
-            add_filter('after_plugin_row_' . $this->_plugin_basename, [$this, 'after_plugin_row'], 10, 3);
772
+            add_filter('after_plugin_row_'.$this->_plugin_basename, [$this, 'after_plugin_row'], 10, 3);
773 773
         }
774 774
     }
775 775
 
@@ -788,7 +788,7 @@  discard block
 block discarded – undo
788 788
             // before other links
789 789
             array_unshift(
790 790
                 $links,
791
-                '<a href="admin.php?page=' . $this->plugin_action_slug() . '">'
791
+                '<a href="admin.php?page='.$this->plugin_action_slug().'">'
792 792
                 . esc_html__('Settings', 'event_espresso')
793 793
                 . '</a>'
794 794
             );
@@ -811,19 +811,19 @@  discard block
 block discarded – undo
811 811
     {
812 812
         $after_plugin_row = '';
813 813
         $plugins_page_row = $this->get_plugins_page_row();
814
-        if (! empty($plugins_page_row) && $plugin_file === $this->plugin_basename()) {
814
+        if ( ! empty($plugins_page_row) && $plugin_file === $this->plugin_basename()) {
815 815
             $class       = $status ? 'active' : 'inactive';
816 816
             $link_text   = isset($plugins_page_row['link_text']) ? $plugins_page_row['link_text'] : '';
817 817
             $link_url    = isset($plugins_page_row['link_url']) ? $plugins_page_row['link_url'] : '';
818 818
             $description = isset($plugins_page_row['description'])
819 819
                 ? $plugins_page_row['description']
820 820
                 : '';
821
-            if (! empty($link_text) && ! empty($link_url) && ! empty($description)) {
822
-                $after_plugin_row .= '<tr id="' . sanitize_title($plugin_file) . '-ee-addon" class="' . $class . '">';
821
+            if ( ! empty($link_text) && ! empty($link_url) && ! empty($description)) {
822
+                $after_plugin_row .= '<tr id="'.sanitize_title($plugin_file).'-ee-addon" class="'.$class.'">';
823 823
                 $after_plugin_row .= '<th class="check-column" scope="row"></th>';
824 824
                 $after_plugin_row .= '<td class="ee-addon-upsell-info-title-td plugin-title column-primary">';
825 825
                 $after_plugin_row .= '<p class="ee-addon-upsell-info-dv">
826
-	                <a class="button button--primary" href="' . esc_url_raw($link_url) . '">'
826
+	                <a class="button button--primary" href="' . esc_url_raw($link_url).'">'
827 827
                     . $link_text
828 828
                     . ' &nbsp;<span class="dashicons dashicons-arrow-right-alt2" style="margin:0;"></span>'
829 829
                     . '</a>
Please login to merge, or discard this patch.
core/libraries/iframe_display/iframe_wrapper.template.php 2 patches
Spacing   +2 added lines, -2 removed lines patch added patch discarded remove patch
@@ -37,7 +37,7 @@  discard block
 block discarded – undo
37 37
             <?php echo $eei18n; ?>
38 38
         </script>
39 39
         <?php foreach ($header_js as $key => $url) :?>
40
-            <?php $header_attributes = $header_js_attributes[ $key ] ?? ''; ?>
40
+            <?php $header_attributes = $header_js_attributes[$key] ?? ''; ?>
41 41
             <script type="text/javascript" src="<?php echo esc_url_raw($url); ?>" <?php echo AttributesSanitizer::clean($header_attributes, AllowedTags::getAllowedTags(), 'script'); ?>></script>
42 42
         <?php endforeach; ?>
43 43
     <?php endif; ?>
@@ -48,7 +48,7 @@  discard block
 block discarded – undo
48 48
         <?php echo wp_kses($content, AllowedTags::getWithFullTags()); ?>
49 49
     </div>
50 50
     <?php foreach ($footer_js as $key => $url) : ?>
51
-        <?php $footer_attributes = $footer_js_attributes[ $key ] ?? ''; ?>
51
+        <?php $footer_attributes = $footer_js_attributes[$key] ?? ''; ?>
52 52
         <script type="text/javascript" src="<?php echo esc_url_raw($url); ?>" <?php echo AttributesSanitizer::clean($footer_attributes, AllowedTags::getAllowedTags(), 'script'); ?>></script>
53 53
     <?php endforeach; ?>
54 54
     <?php if ($enqueue_wp_assets) : ?>
Please login to merge, or discard this patch.
Braces   +5 added lines, -2 removed lines patch added patch discarded remove patch
@@ -29,9 +29,12 @@
 block discarded – undo
29 29
     <meta name="robots" content="noindex,nofollow">
30 30
     <?php if ($enqueue_wp_assets) : ?>
31 31
         <?php wp_head(); ?>
32
-    <?php else : ?>
32
+    <?php else {
33
+	: ?>
33 34
         <?php foreach ($css as $url) :?>
34
-            <link rel="stylesheet" type="text/css" href="<?php echo esc_url_raw($url); ?>">
35
+            <link rel="stylesheet" type="text/css" href="<?php echo esc_url_raw($url);
36
+}
37
+?>">
35 38
         <?php endforeach; ?>
36 39
         <script type="text/javascript">
37 40
             <?php echo $eei18n; ?>
Please login to merge, or discard this patch.
core/libraries/payment_methods/EE_Gateway.lib.php 1 patch
Indentation   +531 added lines, -531 removed lines patch added patch discarded remove patch
@@ -23,535 +23,535 @@
 block discarded – undo
23 23
  */
24 24
 abstract class EE_Gateway
25 25
 {
26
-    /**
27
-     * a constant used as a possible value for $_currencies_supported to indicate
28
-     * that ALL currencies are supported by this gateway
29
-     */
30
-    const all_currencies_supported = 'all_currencies_supported';
31
-
32
-    /**
33
-     * Where values are 3-letter currency codes
34
-     *
35
-     * @var array|string
36
-     */
37
-    protected $_currencies_supported = [];
38
-
39
-    /**
40
-     * Whether this gateway can support SENDING a refund request (ie, initiated by
41
-     * admin in EE's wp-admin page)
42
-     *
43
-     * @var boolean
44
-     */
45
-    protected $_supports_sending_refunds = false;
46
-
47
-    /**
48
-     * Whether this gateway can support RECEIVING a refund request from the payment
49
-     * provider (ie, initiated by admin on the payment prover's website who sends an IPN to EE)
50
-     *
51
-     * @var boolean
52
-     */
53
-    protected $_supports_receiving_refunds = false;
54
-
55
-    /**
56
-     * Model for querying for existing payments
57
-     *
58
-     * @var EEMI_Payment
59
-     */
60
-    protected $_pay_model;
61
-
62
-    /**
63
-     * Model used for adding to the payments log
64
-     *
65
-     * @var EEM_Change_Log
66
-     */
67
-    protected $_pay_log;
68
-
69
-    /**
70
-     * Used for formatting some input to gateways
71
-     *
72
-     * @var EEH_Template
73
-     */
74
-    protected $_template;
75
-
76
-    /**
77
-     * Concrete class that implements EEHI_Money, used by most gateways
78
-     *
79
-     * @var EEH_Money
80
-     */
81
-    protected $_money;
82
-
83
-    /**
84
-     * Concrete class that implements EEH_Line_Item, used for manipulating the line item tree
85
-     *
86
-     * @var EEH_Line_Item
87
-     */
88
-    protected $_line_item;
89
-
90
-    /**
91
-     * @var GatewayDataFormatterInterface
92
-     */
93
-    protected $_gateway_data_formatter;
94
-
95
-    /**
96
-     * @var FormatterInterface
97
-     */
98
-    protected $_unsupported_character_remover;
99
-
100
-    /**
101
-     * The ID of the payment method using this gateway
102
-     *
103
-     * @var int
104
-     */
105
-    protected $_ID;
106
-
107
-    /**
108
-     * @var $_debug_mode boolean whether to send requests to teh sandbox site or not
109
-     */
110
-    protected $_debug_mode;
111
-
112
-    /**
113
-     * @var string $_name name to show for this payment method
114
-     */
115
-    protected $_name;
116
-
117
-    /**
118
-     * @var string name to show for this payment method to admin-type users
119
-     */
120
-    protected $_admin_name;
121
-
122
-
123
-    /**
124
-     * EE_Gateway constructor
125
-     */
126
-    public function __construct()
127
-    {
128
-    }
129
-
130
-
131
-    /**
132
-     * We don't want to serialize models as they often have circular structures
133
-     * (eg a payment model has a reference to each payment model object; and most
134
-     * payments have a transaction, most transactions have a payment method;
135
-     * most payment methods have a payment method type; most payment method types
136
-     * have a gateway. And if a gateway serializes its models, we start at the
137
-     * beginning again)
138
-     *
139
-     * @return array
140
-     */
141
-    public function __sleep()
142
-    {
143
-        $properties = get_object_vars($this);
144
-        unset($properties['_pay_model'], $properties['_pay_log']);
145
-        return array_keys($properties);
146
-    }
147
-
148
-
149
-    /**
150
-     * Returns whether this gateway should support SENDING refunds
151
-     * see $_supports_sending_refunds
152
-     *
153
-     * @return bool
154
-     */
155
-    public function supports_sending_refunds(): bool
156
-    {
157
-        return $this->_supports_sending_refunds;
158
-    }
159
-
160
-
161
-    /**
162
-     * Returns whether this gateway should support RECEIVING refunds
163
-     * see $_supports_receiving_refunds
164
-     *
165
-     * @return bool
166
-     */
167
-    public function supports_receiving_refunds(): bool
168
-    {
169
-        return $this->_supports_receiving_refunds;
170
-    }
171
-
172
-
173
-    /**
174
-     * Tries to refund the payment specified, taking into account the extra
175
-     * refund info. Note that if the gateway's _supports_sending_refunds is false,
176
-     * this should just throw an exception.
177
-     *
178
-     * @param EE_Payment|null $payment
179
-     * @param array|null      $refund_info
180
-     * @return EE_Payment|null
181
-     */
182
-    public function do_direct_refund(?EE_Payment $payment, ?array $refund_info = null): ?EE_Payment
183
-    {
184
-        return null;
185
-    }
186
-
187
-
188
-    /**
189
-     * Sets the payment method's settings so the gateway knows where to send the request
190
-     * etc
191
-     *
192
-     * @param array $settings_array
193
-     */
194
-    public function set_settings(array $settings_array)
195
-    {
196
-        foreach ($settings_array as $name => $value) {
197
-            $property_name          = "_" . $name;
198
-            $this->{$property_name} = $value;
199
-        }
200
-    }
201
-
202
-
203
-    /**
204
-     * See this class description
205
-     *
206
-     * @param EEM_Payment $payment_model
207
-     */
208
-    public function set_payment_model(EEM_Payment $payment_model)
209
-    {
210
-        $this->_pay_model = $payment_model;
211
-    }
212
-
213
-
214
-    /**
215
-     * See this class description
216
-     *
217
-     * @param EEM_Change_Log $payment_log_model
218
-     */
219
-    public function set_payment_log(EEM_Change_Log $payment_log_model)
220
-    {
221
-        $this->_pay_log = $payment_log_model;
222
-    }
223
-
224
-
225
-    /**
226
-     * See this class description
227
-     *
228
-     * @param EEH_Template $template_helper
229
-     */
230
-    public function set_template_helper(EEH_Template $template_helper)
231
-    {
232
-        $this->_template = $template_helper;
233
-    }
234
-
235
-
236
-    /**
237
-     * See this class description
238
-     *
239
-     * @param EEH_Line_Item $line_item_helper
240
-     */
241
-    public function set_line_item_helper(EEH_Line_Item $line_item_helper)
242
-    {
243
-        $this->_line_item = $line_item_helper;
244
-    }
245
-
246
-
247
-    /**
248
-     * See this class description
249
-     *
250
-     * @param EEH_Money $money_helper
251
-     */
252
-    public function set_money_helper(EEH_Money $money_helper)
253
-    {
254
-        $this->_money = $money_helper;
255
-    }
256
-
257
-
258
-    /**
259
-     * Sets the gateway data formatter helper
260
-     *
261
-     * @param GatewayDataFormatterInterface|null $gateway_data_formatter
262
-     * @throws InvalidEntityException if it's not set properly
263
-     */
264
-    public function set_gateway_data_formatter(?GatewayDataFormatterInterface $gateway_data_formatter)
265
-    {
266
-        if (! $gateway_data_formatter instanceof GatewayDataFormatterInterface) {
267
-            throw new InvalidEntityException(
268
-                is_object($gateway_data_formatter)
269
-                    ? get_class($gateway_data_formatter)
270
-                    : esc_html__('Not an object', 'event_espresso'),
271
-                '\\EventEspresso\\core\\services\\payment_methods\\gateways\\GatewayDataFormatterInterface'
272
-            );
273
-        }
274
-        $this->_gateway_data_formatter = $gateway_data_formatter;
275
-    }
276
-
277
-
278
-    /**
279
-     * Gets the gateway data formatter
280
-     *
281
-     * @return GatewayDataFormatterInterface
282
-     * @throws InvalidEntityException if it's not set properly
283
-     */
284
-    protected function _get_gateway_formatter(): GatewayDataFormatterInterface
285
-    {
286
-        if (! $this->_gateway_data_formatter instanceof GatewayDataFormatterInterface) {
287
-            throw new InvalidEntityException(
288
-                is_object($this->_gateway_data_formatter)
289
-                    ? get_class($this->_gateway_data_formatter)
290
-                    : esc_html__('Not an object', 'event_espresso'),
291
-                '\\EventEspresso\\core\\services\\payment_methods\\gateways\\GatewayDataFormatterInterface'
292
-            );
293
-        }
294
-        return $this->_gateway_data_formatter;
295
-    }
296
-
297
-
298
-    /**
299
-     * Sets the helper which will remove unsupported characters for most gateways
300
-     *
301
-     * @param FormatterInterface|null $formatter
302
-     * @return void
303
-     * @throws InvalidEntityException
304
-     */
305
-    public function set_unsupported_character_remover(?FormatterInterface $formatter)
306
-    {
307
-        if (! $formatter instanceof FormatterInterface) {
308
-            throw new InvalidEntityException(
309
-                is_object($formatter)
310
-                    ? get_class($formatter)
311
-                    : esc_html__('Not an object', 'event_espresso'),
312
-                '\\EventEspresso\\core\\services\\formatters\\FormatterInterface'
313
-            );
314
-        }
315
-        $this->_unsupported_character_remover = $formatter;
316
-    }
317
-
318
-
319
-    /**
320
-     * Gets the helper which removes characters which gateways might not support, like emojis etc.
321
-     *
322
-     * @return FormatterInterface
323
-     * @throws InvalidEntityException
324
-     */
325
-    protected function _get_unsupported_character_remover(): FormatterInterface
326
-    {
327
-        if (! $this->_unsupported_character_remover instanceof FormatterInterface) {
328
-            throw new InvalidEntityException(
329
-                is_object($this->_unsupported_character_remover)
330
-                    ? get_class($this->_unsupported_character_remover)
331
-                    : esc_html__('Not an object', 'event_espresso'),
332
-                '\\EventEspresso\\core\\services\\formatters\\FormatterInterface'
333
-            );
334
-        }
335
-        return $this->_unsupported_character_remover;
336
-    }
337
-
338
-
339
-    /**
340
-     * @param array|string      $message
341
-     * @param mixed             $object_logged
342
-     * @throws EE_Error
343
-     * @throws ReflectionException
344
-     */
345
-    public function log($message, $object_logged)
346
-    {
347
-        if ($object_logged instanceof EE_Payment) {
348
-            $type = 'Payment';
349
-            $id   = $object_logged->ID();
350
-        } elseif ($object_logged instanceof EE_Transaction) {
351
-            $type = 'Transaction';
352
-            $id   = $object_logged->ID();
353
-        } else {
354
-            $type = 'Payment_Method';
355
-            $id   = $this->_ID;
356
-        }
357
-        // only log if we're going to store it for longer than the minimum time
358
-        $reg_config = LoaderFactory::getLoader()->load('EE_Registration_Config');
359
-        if ($reg_config->gateway_log_lifespan !== '1 second') {
360
-            $this->_pay_log->gateway_log($message, $id, $type);
361
-        }
362
-    }
363
-
364
-
365
-    /**
366
-     * Formats the amount so it can generally be sent to gateways
367
-     *
368
-     * @param float $amount
369
-     * @return string
370
-     * @deprecated since 4.9.31 insetad use
371
-     *             EventEspresso\core\services\payment_methods\gateways\GatewayDataFormatter::format_currency()
372
-     */
373
-    public function format_currency(float $amount): string
374
-    {
375
-        return $this->_get_gateway_formatter()->formatCurrency($amount);
376
-    }
377
-
378
-
379
-    /**
380
-     * Returns either an array of all the currency codes supported,
381
-     * or a string indicating they are all supported (EE_gateway::all_currencies_supported)
382
-     *
383
-     * @return array|string
384
-     */
385
-    public function currencies_supported()
386
-    {
387
-        return $this->_currencies_supported;
388
-    }
389
-
390
-
391
-    /**
392
-     * Returns what a simple summing of items and taxes for this transaction. This
393
-     * can be used to determine if some more complex line items, like promotions,
394
-     * surcharges, or cancellations occurred (in which case we might want to forget
395
-     * about creating an itemized list of purchases and instead only send the total due)
396
-     *
397
-     * @param EE_Transaction $transaction
398
-     * @return float
399
-     * @throws EE_Error
400
-     * @throws ReflectionException
401
-     */
402
-    protected function _sum_items_and_taxes(EE_Transaction $transaction)
403
-    {
404
-        $total_line_item = $transaction->total_line_item();
405
-        $total           = 0;
406
-        foreach ($total_line_item->get_items() as $item_line_item) {
407
-            $total += max($item_line_item->total(), 0);
408
-        }
409
-        foreach ($total_line_item->tax_descendants() as $tax_line_item) {
410
-            $total += max($tax_line_item->total(), 0);
411
-        }
412
-        return $total;
413
-    }
414
-
415
-
416
-    /**
417
-     * Determines whether we can easily itemize the transaction using only
418
-     * items and taxes (ie, no promotions or surcharges or cancellations needed)
419
-     *
420
-     * @param EE_Payment $payment
421
-     * @return boolean
422
-     * @throws EE_Error
423
-     * @throws ReflectionException
424
-     */
425
-    protected function _can_easily_itemize_transaction_for(EE_Payment $payment): bool
426
-    {
427
-        return $this->_money->compare_floats(
428
-            $this->_sum_items_and_taxes($payment->transaction()),
429
-            $payment->transaction()->total()
430
-        )
431
-               && $this->_money->compare_floats(
432
-                   $payment->amount(),
433
-                   $payment->transaction()->total()
434
-               );
435
-    }
436
-
437
-
438
-    /**
439
-     * Handles updating the transaction and any other related data based on the payment.
440
-     * You may be tempted to do this as part of do_direct_payment or handle_payment_update,
441
-     * but doing so on those functions might be too early. It's possible that the changes
442
-     * you make to teh transaction or registration or line items may just get overwritten
443
-     * at that point. Instead, you should store any info you need on the payment during those
444
-     * functions, and use that information at this step, which client code will decide
445
-     * for you when it should be called.
446
-     *
447
-     * @param EE_Payment $payment
448
-     * @return void
449
-     */
450
-    public function update_txn_based_on_payment($payment)
451
-    {
452
-        // maybe update the transaction or line items or registrations
453
-        // but most gateways don't need to do this, because they only update the payment
454
-    }
455
-
456
-
457
-    /**
458
-     * Gets the first event for this payment (it's possible that it could be for multiple)
459
-     *
460
-     * @param EE_Payment $payment
461
-     * @return EE_Event|null
462
-     * @deprecated since 4.9.31 instead use EE_Payment::get_first_event()
463
-     */
464
-    protected function _get_first_event_for_payment(EE_Payment $payment): ?EE_Event
465
-    {
466
-        return $payment->get_first_event();
467
-    }
468
-
469
-
470
-    /**
471
-     * Gets the name of the first event for which is being paid
472
-     *
473
-     * @param EE_Payment $payment
474
-     * @return string
475
-     * @deprecated since 4.9.31 instead use EE_Payment::get_first_event_name()
476
-     */
477
-    protected function _get_first_event_name_for_payment(EE_Payment $payment): string
478
-    {
479
-        return $payment->get_first_event_name();
480
-    }
481
-
482
-
483
-    /**
484
-     * Gets the text to use for a gateway's line item name when this is a partial payment
485
-     *
486
-     * @param EE_Payment $payment
487
-     * @return string
488
-     * @deprecated since 4.9.31 instead use $this->_get_gateway_formatter()->formatPartialPaymentLineItemName($payment)
489
-     */
490
-    protected function _format_partial_payment_line_item_name(EE_Payment $payment): string
491
-    {
492
-        return $this->_get_gateway_formatter()->formatPartialPaymentLineItemName($payment);
493
-    }
494
-
495
-
496
-    /**
497
-     * Gets the text to use for a gateway's line item description when this is a partial payment
498
-     *
499
-     * @param EE_Payment $payment
500
-     * @return string
501
-     * @deprecated since 4.9.31 instead use $this->_get_gateway_formatter()->formatPartialPaymentLineItemDesc()
502
-     */
503
-    protected function _format_partial_payment_line_item_desc(EE_Payment $payment): string
504
-    {
505
-        return $this->_get_gateway_formatter()->formatPartialPaymentLineItemDesc($payment);
506
-    }
507
-
508
-
509
-    /**
510
-     * Gets the name to use for a line item when sending line items to the gateway
511
-     *
512
-     * @param EE_Line_Item $line_item
513
-     * @param EE_Payment   $payment
514
-     * @return string
515
-     * @deprecated since 4.9.31 instead use $this->_get_gateway_formatter()->formatLineItemName($line_item,$payment)
516
-     */
517
-    protected function _format_line_item_name(EE_Line_Item $line_item, EE_Payment $payment): string
518
-    {
519
-        return $this->_get_gateway_formatter()->formatLineItemName($line_item, $payment);
520
-    }
521
-
522
-
523
-    /**
524
-     * Gets the description to use for a line item when sending line items to the gateway
525
-     *
526
-     * @param EE_Line_Item $line_item
527
-     * @param EE_Payment   $payment
528
-     * @return string
529
-     * @deprecated since 4.9.31 instead use $this->_get_gateway_formatter()->formatLineItemDesc($line_item, $payment))
530
-     */
531
-    protected function _format_line_item_desc(EE_Line_Item $line_item, EE_Payment $payment): string
532
-    {
533
-        return $this->_get_gateway_formatter()->formatLineItemDesc($line_item, $payment);
534
-    }
535
-
536
-
537
-    /**
538
-     * Gets the order description that should generlly be sent to gateways
539
-     *
540
-     * @param EE_Payment $payment
541
-     * @return string
542
-     * @deprecated since 4.9.31 instead use $this->_get_gateway_formatter()->formatOrderDescription($payment)
543
-     */
544
-    protected function _format_order_description(EE_Payment $payment): string
545
-    {
546
-        return $this->_get_gateway_formatter()->formatOrderDescription($payment);
547
-    }
548
-
549
-
550
-    /**
551
-     * @return bool
552
-     */
553
-    public function isInSandboxMode(): bool
554
-    {
555
-        return $this->_debug_mode;
556
-    }
26
+	/**
27
+	 * a constant used as a possible value for $_currencies_supported to indicate
28
+	 * that ALL currencies are supported by this gateway
29
+	 */
30
+	const all_currencies_supported = 'all_currencies_supported';
31
+
32
+	/**
33
+	 * Where values are 3-letter currency codes
34
+	 *
35
+	 * @var array|string
36
+	 */
37
+	protected $_currencies_supported = [];
38
+
39
+	/**
40
+	 * Whether this gateway can support SENDING a refund request (ie, initiated by
41
+	 * admin in EE's wp-admin page)
42
+	 *
43
+	 * @var boolean
44
+	 */
45
+	protected $_supports_sending_refunds = false;
46
+
47
+	/**
48
+	 * Whether this gateway can support RECEIVING a refund request from the payment
49
+	 * provider (ie, initiated by admin on the payment prover's website who sends an IPN to EE)
50
+	 *
51
+	 * @var boolean
52
+	 */
53
+	protected $_supports_receiving_refunds = false;
54
+
55
+	/**
56
+	 * Model for querying for existing payments
57
+	 *
58
+	 * @var EEMI_Payment
59
+	 */
60
+	protected $_pay_model;
61
+
62
+	/**
63
+	 * Model used for adding to the payments log
64
+	 *
65
+	 * @var EEM_Change_Log
66
+	 */
67
+	protected $_pay_log;
68
+
69
+	/**
70
+	 * Used for formatting some input to gateways
71
+	 *
72
+	 * @var EEH_Template
73
+	 */
74
+	protected $_template;
75
+
76
+	/**
77
+	 * Concrete class that implements EEHI_Money, used by most gateways
78
+	 *
79
+	 * @var EEH_Money
80
+	 */
81
+	protected $_money;
82
+
83
+	/**
84
+	 * Concrete class that implements EEH_Line_Item, used for manipulating the line item tree
85
+	 *
86
+	 * @var EEH_Line_Item
87
+	 */
88
+	protected $_line_item;
89
+
90
+	/**
91
+	 * @var GatewayDataFormatterInterface
92
+	 */
93
+	protected $_gateway_data_formatter;
94
+
95
+	/**
96
+	 * @var FormatterInterface
97
+	 */
98
+	protected $_unsupported_character_remover;
99
+
100
+	/**
101
+	 * The ID of the payment method using this gateway
102
+	 *
103
+	 * @var int
104
+	 */
105
+	protected $_ID;
106
+
107
+	/**
108
+	 * @var $_debug_mode boolean whether to send requests to teh sandbox site or not
109
+	 */
110
+	protected $_debug_mode;
111
+
112
+	/**
113
+	 * @var string $_name name to show for this payment method
114
+	 */
115
+	protected $_name;
116
+
117
+	/**
118
+	 * @var string name to show for this payment method to admin-type users
119
+	 */
120
+	protected $_admin_name;
121
+
122
+
123
+	/**
124
+	 * EE_Gateway constructor
125
+	 */
126
+	public function __construct()
127
+	{
128
+	}
129
+
130
+
131
+	/**
132
+	 * We don't want to serialize models as they often have circular structures
133
+	 * (eg a payment model has a reference to each payment model object; and most
134
+	 * payments have a transaction, most transactions have a payment method;
135
+	 * most payment methods have a payment method type; most payment method types
136
+	 * have a gateway. And if a gateway serializes its models, we start at the
137
+	 * beginning again)
138
+	 *
139
+	 * @return array
140
+	 */
141
+	public function __sleep()
142
+	{
143
+		$properties = get_object_vars($this);
144
+		unset($properties['_pay_model'], $properties['_pay_log']);
145
+		return array_keys($properties);
146
+	}
147
+
148
+
149
+	/**
150
+	 * Returns whether this gateway should support SENDING refunds
151
+	 * see $_supports_sending_refunds
152
+	 *
153
+	 * @return bool
154
+	 */
155
+	public function supports_sending_refunds(): bool
156
+	{
157
+		return $this->_supports_sending_refunds;
158
+	}
159
+
160
+
161
+	/**
162
+	 * Returns whether this gateway should support RECEIVING refunds
163
+	 * see $_supports_receiving_refunds
164
+	 *
165
+	 * @return bool
166
+	 */
167
+	public function supports_receiving_refunds(): bool
168
+	{
169
+		return $this->_supports_receiving_refunds;
170
+	}
171
+
172
+
173
+	/**
174
+	 * Tries to refund the payment specified, taking into account the extra
175
+	 * refund info. Note that if the gateway's _supports_sending_refunds is false,
176
+	 * this should just throw an exception.
177
+	 *
178
+	 * @param EE_Payment|null $payment
179
+	 * @param array|null      $refund_info
180
+	 * @return EE_Payment|null
181
+	 */
182
+	public function do_direct_refund(?EE_Payment $payment, ?array $refund_info = null): ?EE_Payment
183
+	{
184
+		return null;
185
+	}
186
+
187
+
188
+	/**
189
+	 * Sets the payment method's settings so the gateway knows where to send the request
190
+	 * etc
191
+	 *
192
+	 * @param array $settings_array
193
+	 */
194
+	public function set_settings(array $settings_array)
195
+	{
196
+		foreach ($settings_array as $name => $value) {
197
+			$property_name          = "_" . $name;
198
+			$this->{$property_name} = $value;
199
+		}
200
+	}
201
+
202
+
203
+	/**
204
+	 * See this class description
205
+	 *
206
+	 * @param EEM_Payment $payment_model
207
+	 */
208
+	public function set_payment_model(EEM_Payment $payment_model)
209
+	{
210
+		$this->_pay_model = $payment_model;
211
+	}
212
+
213
+
214
+	/**
215
+	 * See this class description
216
+	 *
217
+	 * @param EEM_Change_Log $payment_log_model
218
+	 */
219
+	public function set_payment_log(EEM_Change_Log $payment_log_model)
220
+	{
221
+		$this->_pay_log = $payment_log_model;
222
+	}
223
+
224
+
225
+	/**
226
+	 * See this class description
227
+	 *
228
+	 * @param EEH_Template $template_helper
229
+	 */
230
+	public function set_template_helper(EEH_Template $template_helper)
231
+	{
232
+		$this->_template = $template_helper;
233
+	}
234
+
235
+
236
+	/**
237
+	 * See this class description
238
+	 *
239
+	 * @param EEH_Line_Item $line_item_helper
240
+	 */
241
+	public function set_line_item_helper(EEH_Line_Item $line_item_helper)
242
+	{
243
+		$this->_line_item = $line_item_helper;
244
+	}
245
+
246
+
247
+	/**
248
+	 * See this class description
249
+	 *
250
+	 * @param EEH_Money $money_helper
251
+	 */
252
+	public function set_money_helper(EEH_Money $money_helper)
253
+	{
254
+		$this->_money = $money_helper;
255
+	}
256
+
257
+
258
+	/**
259
+	 * Sets the gateway data formatter helper
260
+	 *
261
+	 * @param GatewayDataFormatterInterface|null $gateway_data_formatter
262
+	 * @throws InvalidEntityException if it's not set properly
263
+	 */
264
+	public function set_gateway_data_formatter(?GatewayDataFormatterInterface $gateway_data_formatter)
265
+	{
266
+		if (! $gateway_data_formatter instanceof GatewayDataFormatterInterface) {
267
+			throw new InvalidEntityException(
268
+				is_object($gateway_data_formatter)
269
+					? get_class($gateway_data_formatter)
270
+					: esc_html__('Not an object', 'event_espresso'),
271
+				'\\EventEspresso\\core\\services\\payment_methods\\gateways\\GatewayDataFormatterInterface'
272
+			);
273
+		}
274
+		$this->_gateway_data_formatter = $gateway_data_formatter;
275
+	}
276
+
277
+
278
+	/**
279
+	 * Gets the gateway data formatter
280
+	 *
281
+	 * @return GatewayDataFormatterInterface
282
+	 * @throws InvalidEntityException if it's not set properly
283
+	 */
284
+	protected function _get_gateway_formatter(): GatewayDataFormatterInterface
285
+	{
286
+		if (! $this->_gateway_data_formatter instanceof GatewayDataFormatterInterface) {
287
+			throw new InvalidEntityException(
288
+				is_object($this->_gateway_data_formatter)
289
+					? get_class($this->_gateway_data_formatter)
290
+					: esc_html__('Not an object', 'event_espresso'),
291
+				'\\EventEspresso\\core\\services\\payment_methods\\gateways\\GatewayDataFormatterInterface'
292
+			);
293
+		}
294
+		return $this->_gateway_data_formatter;
295
+	}
296
+
297
+
298
+	/**
299
+	 * Sets the helper which will remove unsupported characters for most gateways
300
+	 *
301
+	 * @param FormatterInterface|null $formatter
302
+	 * @return void
303
+	 * @throws InvalidEntityException
304
+	 */
305
+	public function set_unsupported_character_remover(?FormatterInterface $formatter)
306
+	{
307
+		if (! $formatter instanceof FormatterInterface) {
308
+			throw new InvalidEntityException(
309
+				is_object($formatter)
310
+					? get_class($formatter)
311
+					: esc_html__('Not an object', 'event_espresso'),
312
+				'\\EventEspresso\\core\\services\\formatters\\FormatterInterface'
313
+			);
314
+		}
315
+		$this->_unsupported_character_remover = $formatter;
316
+	}
317
+
318
+
319
+	/**
320
+	 * Gets the helper which removes characters which gateways might not support, like emojis etc.
321
+	 *
322
+	 * @return FormatterInterface
323
+	 * @throws InvalidEntityException
324
+	 */
325
+	protected function _get_unsupported_character_remover(): FormatterInterface
326
+	{
327
+		if (! $this->_unsupported_character_remover instanceof FormatterInterface) {
328
+			throw new InvalidEntityException(
329
+				is_object($this->_unsupported_character_remover)
330
+					? get_class($this->_unsupported_character_remover)
331
+					: esc_html__('Not an object', 'event_espresso'),
332
+				'\\EventEspresso\\core\\services\\formatters\\FormatterInterface'
333
+			);
334
+		}
335
+		return $this->_unsupported_character_remover;
336
+	}
337
+
338
+
339
+	/**
340
+	 * @param array|string      $message
341
+	 * @param mixed             $object_logged
342
+	 * @throws EE_Error
343
+	 * @throws ReflectionException
344
+	 */
345
+	public function log($message, $object_logged)
346
+	{
347
+		if ($object_logged instanceof EE_Payment) {
348
+			$type = 'Payment';
349
+			$id   = $object_logged->ID();
350
+		} elseif ($object_logged instanceof EE_Transaction) {
351
+			$type = 'Transaction';
352
+			$id   = $object_logged->ID();
353
+		} else {
354
+			$type = 'Payment_Method';
355
+			$id   = $this->_ID;
356
+		}
357
+		// only log if we're going to store it for longer than the minimum time
358
+		$reg_config = LoaderFactory::getLoader()->load('EE_Registration_Config');
359
+		if ($reg_config->gateway_log_lifespan !== '1 second') {
360
+			$this->_pay_log->gateway_log($message, $id, $type);
361
+		}
362
+	}
363
+
364
+
365
+	/**
366
+	 * Formats the amount so it can generally be sent to gateways
367
+	 *
368
+	 * @param float $amount
369
+	 * @return string
370
+	 * @deprecated since 4.9.31 insetad use
371
+	 *             EventEspresso\core\services\payment_methods\gateways\GatewayDataFormatter::format_currency()
372
+	 */
373
+	public function format_currency(float $amount): string
374
+	{
375
+		return $this->_get_gateway_formatter()->formatCurrency($amount);
376
+	}
377
+
378
+
379
+	/**
380
+	 * Returns either an array of all the currency codes supported,
381
+	 * or a string indicating they are all supported (EE_gateway::all_currencies_supported)
382
+	 *
383
+	 * @return array|string
384
+	 */
385
+	public function currencies_supported()
386
+	{
387
+		return $this->_currencies_supported;
388
+	}
389
+
390
+
391
+	/**
392
+	 * Returns what a simple summing of items and taxes for this transaction. This
393
+	 * can be used to determine if some more complex line items, like promotions,
394
+	 * surcharges, or cancellations occurred (in which case we might want to forget
395
+	 * about creating an itemized list of purchases and instead only send the total due)
396
+	 *
397
+	 * @param EE_Transaction $transaction
398
+	 * @return float
399
+	 * @throws EE_Error
400
+	 * @throws ReflectionException
401
+	 */
402
+	protected function _sum_items_and_taxes(EE_Transaction $transaction)
403
+	{
404
+		$total_line_item = $transaction->total_line_item();
405
+		$total           = 0;
406
+		foreach ($total_line_item->get_items() as $item_line_item) {
407
+			$total += max($item_line_item->total(), 0);
408
+		}
409
+		foreach ($total_line_item->tax_descendants() as $tax_line_item) {
410
+			$total += max($tax_line_item->total(), 0);
411
+		}
412
+		return $total;
413
+	}
414
+
415
+
416
+	/**
417
+	 * Determines whether we can easily itemize the transaction using only
418
+	 * items and taxes (ie, no promotions or surcharges or cancellations needed)
419
+	 *
420
+	 * @param EE_Payment $payment
421
+	 * @return boolean
422
+	 * @throws EE_Error
423
+	 * @throws ReflectionException
424
+	 */
425
+	protected function _can_easily_itemize_transaction_for(EE_Payment $payment): bool
426
+	{
427
+		return $this->_money->compare_floats(
428
+			$this->_sum_items_and_taxes($payment->transaction()),
429
+			$payment->transaction()->total()
430
+		)
431
+			   && $this->_money->compare_floats(
432
+				   $payment->amount(),
433
+				   $payment->transaction()->total()
434
+			   );
435
+	}
436
+
437
+
438
+	/**
439
+	 * Handles updating the transaction and any other related data based on the payment.
440
+	 * You may be tempted to do this as part of do_direct_payment or handle_payment_update,
441
+	 * but doing so on those functions might be too early. It's possible that the changes
442
+	 * you make to teh transaction or registration or line items may just get overwritten
443
+	 * at that point. Instead, you should store any info you need on the payment during those
444
+	 * functions, and use that information at this step, which client code will decide
445
+	 * for you when it should be called.
446
+	 *
447
+	 * @param EE_Payment $payment
448
+	 * @return void
449
+	 */
450
+	public function update_txn_based_on_payment($payment)
451
+	{
452
+		// maybe update the transaction or line items or registrations
453
+		// but most gateways don't need to do this, because they only update the payment
454
+	}
455
+
456
+
457
+	/**
458
+	 * Gets the first event for this payment (it's possible that it could be for multiple)
459
+	 *
460
+	 * @param EE_Payment $payment
461
+	 * @return EE_Event|null
462
+	 * @deprecated since 4.9.31 instead use EE_Payment::get_first_event()
463
+	 */
464
+	protected function _get_first_event_for_payment(EE_Payment $payment): ?EE_Event
465
+	{
466
+		return $payment->get_first_event();
467
+	}
468
+
469
+
470
+	/**
471
+	 * Gets the name of the first event for which is being paid
472
+	 *
473
+	 * @param EE_Payment $payment
474
+	 * @return string
475
+	 * @deprecated since 4.9.31 instead use EE_Payment::get_first_event_name()
476
+	 */
477
+	protected function _get_first_event_name_for_payment(EE_Payment $payment): string
478
+	{
479
+		return $payment->get_first_event_name();
480
+	}
481
+
482
+
483
+	/**
484
+	 * Gets the text to use for a gateway's line item name when this is a partial payment
485
+	 *
486
+	 * @param EE_Payment $payment
487
+	 * @return string
488
+	 * @deprecated since 4.9.31 instead use $this->_get_gateway_formatter()->formatPartialPaymentLineItemName($payment)
489
+	 */
490
+	protected function _format_partial_payment_line_item_name(EE_Payment $payment): string
491
+	{
492
+		return $this->_get_gateway_formatter()->formatPartialPaymentLineItemName($payment);
493
+	}
494
+
495
+
496
+	/**
497
+	 * Gets the text to use for a gateway's line item description when this is a partial payment
498
+	 *
499
+	 * @param EE_Payment $payment
500
+	 * @return string
501
+	 * @deprecated since 4.9.31 instead use $this->_get_gateway_formatter()->formatPartialPaymentLineItemDesc()
502
+	 */
503
+	protected function _format_partial_payment_line_item_desc(EE_Payment $payment): string
504
+	{
505
+		return $this->_get_gateway_formatter()->formatPartialPaymentLineItemDesc($payment);
506
+	}
507
+
508
+
509
+	/**
510
+	 * Gets the name to use for a line item when sending line items to the gateway
511
+	 *
512
+	 * @param EE_Line_Item $line_item
513
+	 * @param EE_Payment   $payment
514
+	 * @return string
515
+	 * @deprecated since 4.9.31 instead use $this->_get_gateway_formatter()->formatLineItemName($line_item,$payment)
516
+	 */
517
+	protected function _format_line_item_name(EE_Line_Item $line_item, EE_Payment $payment): string
518
+	{
519
+		return $this->_get_gateway_formatter()->formatLineItemName($line_item, $payment);
520
+	}
521
+
522
+
523
+	/**
524
+	 * Gets the description to use for a line item when sending line items to the gateway
525
+	 *
526
+	 * @param EE_Line_Item $line_item
527
+	 * @param EE_Payment   $payment
528
+	 * @return string
529
+	 * @deprecated since 4.9.31 instead use $this->_get_gateway_formatter()->formatLineItemDesc($line_item, $payment))
530
+	 */
531
+	protected function _format_line_item_desc(EE_Line_Item $line_item, EE_Payment $payment): string
532
+	{
533
+		return $this->_get_gateway_formatter()->formatLineItemDesc($line_item, $payment);
534
+	}
535
+
536
+
537
+	/**
538
+	 * Gets the order description that should generlly be sent to gateways
539
+	 *
540
+	 * @param EE_Payment $payment
541
+	 * @return string
542
+	 * @deprecated since 4.9.31 instead use $this->_get_gateway_formatter()->formatOrderDescription($payment)
543
+	 */
544
+	protected function _format_order_description(EE_Payment $payment): string
545
+	{
546
+		return $this->_get_gateway_formatter()->formatOrderDescription($payment);
547
+	}
548
+
549
+
550
+	/**
551
+	 * @return bool
552
+	 */
553
+	public function isInSandboxMode(): bool
554
+	{
555
+		return $this->_debug_mode;
556
+	}
557 557
 }
Please login to merge, or discard this patch.
core/libraries/line_item_filters/EE_Non_Zero_Line_Item_Filter.class.php 1 patch
Indentation   +94 added lines, -94 removed lines patch added patch discarded remove patch
@@ -13,104 +13,104 @@
 block discarded – undo
13 13
  */
14 14
 class EE_Non_Zero_Line_Item_Filter extends EE_Line_Item_Filter_Base
15 15
 {
16
-    /**
17
-     * EE_Non_Zero_Line_Item_Filter constructor.
18
-     */
19
-    public function __construct()
20
-    {
21
-    }
16
+	/**
17
+	 * EE_Non_Zero_Line_Item_Filter constructor.
18
+	 */
19
+	public function __construct()
20
+	{
21
+	}
22 22
 
23 23
 
24
-    /**
25
-     * Creates a duplicate of the line item tree, except only includes billable items
26
-     * and the portion of line items attributed to billable things
27
-     *
28
-     * @param EE_Line_Item $line_item
29
-     * @return EE_Line_Item
30
-     * @throws EE_Error
31
-     * @throws ReflectionException
32
-     */
33
-    public function process(EE_Line_Item $line_item): ?EE_Line_Item
34
-    {
35
-        $non_zero_line_item = $this->_filter_zero_line_item($line_item);
36
-        if (! $non_zero_line_item instanceof EE_Line_Item) {
37
-            return null;
38
-        }
39
-        // if this is an event subtotal, we want to only include it if it
40
-        // has a non-zero total and at least one ticket line item child
41
-        if ($line_item->children()) {
42
-            $ticket_or_subtotals_with_tkt_children_count = 0;
43
-            foreach ($line_item->children() as $child_line_item) {
44
-                $code = $child_line_item->code();
45
-                $child_line_item = $this->process($child_line_item);
46
-                if (! $child_line_item instanceof EE_Line_Item) {
47
-                    $line_item->delete_child_line_item($code);
48
-                    continue;
49
-                }
50
-                if (
51
-                    (
52
-                        $child_line_item instanceof EE_Line_Item
53
-                        && $child_line_item->type() === EEM_Line_Item::type_sub_total
54
-                    )
55
-                    || (
56
-                        $child_line_item->type() === EEM_Line_Item::type_line_item
57
-                        && $child_line_item->OBJ_type() === 'Ticket'
58
-                    )
59
-                ) {
60
-                    $ticket_or_subtotals_with_tkt_children_count++;
61
-                }
62
-            }
63
-            // if this is an event subtotal with NO ticket children
64
-            // we basically want to ignore it
65
-            return $this->_filter_zero_subtotal_line_item(
66
-                $non_zero_line_item,
67
-                $ticket_or_subtotals_with_tkt_children_count
68
-            );
69
-        }
70
-        return $non_zero_line_item;
71
-    }
24
+	/**
25
+	 * Creates a duplicate of the line item tree, except only includes billable items
26
+	 * and the portion of line items attributed to billable things
27
+	 *
28
+	 * @param EE_Line_Item $line_item
29
+	 * @return EE_Line_Item
30
+	 * @throws EE_Error
31
+	 * @throws ReflectionException
32
+	 */
33
+	public function process(EE_Line_Item $line_item): ?EE_Line_Item
34
+	{
35
+		$non_zero_line_item = $this->_filter_zero_line_item($line_item);
36
+		if (! $non_zero_line_item instanceof EE_Line_Item) {
37
+			return null;
38
+		}
39
+		// if this is an event subtotal, we want to only include it if it
40
+		// has a non-zero total and at least one ticket line item child
41
+		if ($line_item->children()) {
42
+			$ticket_or_subtotals_with_tkt_children_count = 0;
43
+			foreach ($line_item->children() as $child_line_item) {
44
+				$code = $child_line_item->code();
45
+				$child_line_item = $this->process($child_line_item);
46
+				if (! $child_line_item instanceof EE_Line_Item) {
47
+					$line_item->delete_child_line_item($code);
48
+					continue;
49
+				}
50
+				if (
51
+					(
52
+						$child_line_item instanceof EE_Line_Item
53
+						&& $child_line_item->type() === EEM_Line_Item::type_sub_total
54
+					)
55
+					|| (
56
+						$child_line_item->type() === EEM_Line_Item::type_line_item
57
+						&& $child_line_item->OBJ_type() === 'Ticket'
58
+					)
59
+				) {
60
+					$ticket_or_subtotals_with_tkt_children_count++;
61
+				}
62
+			}
63
+			// if this is an event subtotal with NO ticket children
64
+			// we basically want to ignore it
65
+			return $this->_filter_zero_subtotal_line_item(
66
+				$non_zero_line_item,
67
+				$ticket_or_subtotals_with_tkt_children_count
68
+			);
69
+		}
70
+		return $non_zero_line_item;
71
+	}
72 72
 
73 73
 
74
-    /**
75
-     * Creates a new, unsaved line item, but if it's a ticket line item
76
-     * with a total of 0, or a subtotal of 0, returns null instead
77
-     *
78
-     * @param EE_Line_Item $line_item
79
-     * @return EE_Line_Item
80
-     * @throws EE_Error
81
-     * @throws ReflectionException
82
-     */
83
-    protected function _filter_zero_line_item(EE_Line_Item $line_item): ?EE_Line_Item
84
-    {
85
-        if (
86
-            $line_item->type() === EEM_Line_Item::type_line_item
87
-            && $line_item->OBJ_type() === 'Ticket'
88
-            && $line_item->quantity() === 0
89
-        ) {
90
-            return null;
91
-        }
92
-        return $line_item;
93
-    }
74
+	/**
75
+	 * Creates a new, unsaved line item, but if it's a ticket line item
76
+	 * with a total of 0, or a subtotal of 0, returns null instead
77
+	 *
78
+	 * @param EE_Line_Item $line_item
79
+	 * @return EE_Line_Item
80
+	 * @throws EE_Error
81
+	 * @throws ReflectionException
82
+	 */
83
+	protected function _filter_zero_line_item(EE_Line_Item $line_item): ?EE_Line_Item
84
+	{
85
+		if (
86
+			$line_item->type() === EEM_Line_Item::type_line_item
87
+			&& $line_item->OBJ_type() === 'Ticket'
88
+			&& $line_item->quantity() === 0
89
+		) {
90
+			return null;
91
+		}
92
+		return $line_item;
93
+	}
94 94
 
95 95
 
96
-    /**
97
-     * Creates a new, unsaved line item, but if it's a ticket line item
98
-     * with a total of 0, or a subtotal of 0, returns null instead
99
-     *
100
-     * @param EE_Line_Item $line_item
101
-     * @param int          $ticket_children
102
-     * @return EE_Line_Item
103
-     * @throws EE_Error
104
-     * @throws ReflectionException
105
-     */
106
-    protected function _filter_zero_subtotal_line_item(EE_Line_Item $line_item, int $ticket_children = 0): ?EE_Line_Item
107
-    {
108
-        if (
109
-            (int) $ticket_children === 0
110
-            && $line_item->type() === EEM_Line_Item::type_sub_total
111
-        ) {
112
-            return null;
113
-        }
114
-        return $line_item;
115
-    }
96
+	/**
97
+	 * Creates a new, unsaved line item, but if it's a ticket line item
98
+	 * with a total of 0, or a subtotal of 0, returns null instead
99
+	 *
100
+	 * @param EE_Line_Item $line_item
101
+	 * @param int          $ticket_children
102
+	 * @return EE_Line_Item
103
+	 * @throws EE_Error
104
+	 * @throws ReflectionException
105
+	 */
106
+	protected function _filter_zero_subtotal_line_item(EE_Line_Item $line_item, int $ticket_children = 0): ?EE_Line_Item
107
+	{
108
+		if (
109
+			(int) $ticket_children === 0
110
+			&& $line_item->type() === EEM_Line_Item::type_sub_total
111
+		) {
112
+			return null;
113
+		}
114
+		return $line_item;
115
+	}
116 116
 }
Please login to merge, or discard this patch.
core/libraries/batch/JobHandlers/RegistrationsReport.php 1 patch
Indentation   +581 added lines, -581 removed lines patch added patch discarded remove patch
@@ -42,585 +42,585 @@
 block discarded – undo
42 42
  */
43 43
 class RegistrationsReport extends JobHandlerFile
44 44
 {
45
-    // phpcs:disable PSR1.Methods.CamelCapsMethodName.NotCamelCaps
46
-    // phpcs:disable PSR2.Methods.MethodDeclaration.Underscore
47
-    /**
48
-     * Performs any necessary setup for starting the job. This is also a good
49
-     * place to setup the $job_arguments which will be used for subsequent HTTP requests
50
-     * when continue_job will be called
51
-     *
52
-     * @param JobParameters $job_parameters
53
-     * @return JobStepResponse
54
-     * @throws BatchRequestException
55
-     * @throws EE_Error
56
-     * @throws ReflectionException
57
-     */
58
-    public function create_job(JobParameters $job_parameters): JobStepResponse
59
-    {
60
-        $event_id = absint($job_parameters->request_datum('EVT_ID', '0'));
61
-        $DTT_ID   = absint($job_parameters->request_datum('DTT_ID', '0'));
62
-        if (! EE_Capabilities::instance()->current_user_can('ee_read_registrations', 'generating_report')) {
63
-            throw new BatchRequestException(
64
-                esc_html__('You do not have permission to view registrations', 'event_espresso')
65
-            );
66
-        }
67
-        $filepath = $this->create_file_from_job_with_name(
68
-            $job_parameters->job_id(),
69
-            $this->get_filename()
70
-        );
71
-        $job_parameters->add_extra_data('filepath', $filepath);
72
-
73
-        if ($job_parameters->request_datum('use_filters', false)) {
74
-            $query_params = maybe_unserialize($job_parameters->request_datum('filters', []));
75
-        } else {
76
-            $query_params = [
77
-                [
78
-                    'OR'                 => [
79
-                        // don't include registrations from failed or abandoned transactions...
80
-                        'Transaction.STS_ID' => [
81
-                            'NOT IN',
82
-                            [
83
-                                EEM_Transaction::failed_status_code,
84
-                                EEM_Transaction::abandoned_status_code,
85
-                            ],
86
-                        ],
87
-                        // unless the registration is approved,
88
-                        // in which case include it regardless of transaction status
89
-                        'STS_ID'             => EEM_Registration::status_id_approved,
90
-                    ],
91
-                    'Ticket.TKT_deleted' => ['IN', [true, false]],
92
-                ],
93
-                'order_by'   => ['Transaction.TXN_ID' => 'asc', 'REG_count' => 'asc'],
94
-                'force_join' => ['Transaction', 'Ticket', 'Attendee'],
95
-                'caps'       => EEM_Base::caps_read_admin,
96
-            ];
97
-            if ($event_id) {
98
-                $query_params[0]['EVT_ID'] = $event_id;
99
-            } else {
100
-                $query_params['force_join'][] = 'Event';
101
-            }
102
-        }
103
-
104
-        if (! isset($query_params['force_join'])) {
105
-            $query_params['force_join'] = ['Event', 'Transaction', 'Ticket', 'Attendee'];
106
-        }
107
-
108
-        $query_params = apply_filters(
109
-            'FHEE__EE_Export__report_registration_for_event',
110
-            $query_params,
111
-            $event_id
112
-        );
113
-
114
-        $job_parameters->add_extra_data('query_params', $query_params);
115
-        $question_labels = $this->_get_question_labels($query_params);
116
-        $job_parameters->add_extra_data('question_labels', $question_labels);
117
-        $job_parameters->set_job_size($this->count_units_to_process($query_params));
118
-        // we need to set the header columns
119
-        // but to do that we need to process one row so that we can extract ALL of the column headers
120
-        $csv_data_for_row = $this->get_csv_data_for(
121
-            $event_id,
122
-            0,
123
-            1,
124
-            $question_labels,
125
-            $query_params,
126
-            $DTT_ID
127
-        );
128
-        // but we don't want to write any actual data yet...
129
-        // so let's blank out all of the values for that first row
130
-        array_walk(
131
-            $csv_data_for_row[0],
132
-            function (&$value) {
133
-                $value = null;
134
-            }
135
-        );
136
-
137
-        EEH_Export::write_data_array_to_csv($filepath, $csv_data_for_row, true, true);
138
-        $this->updateTextHeader(
139
-            esc_html__('Registrations report started successfully...', 'event_espresso')
140
-        );
141
-        return new JobStepResponse($job_parameters, $this->feedback);
142
-    }
143
-
144
-
145
-    /**
146
-     * Gets the filename
147
-     *
148
-     * @return string
149
-     */
150
-    protected function get_filename(): string
151
-    {
152
-        return apply_filters(
153
-            'FHEE__EventEspressoBatchRequest__JobHandlers__RegistrationsReport__get_filename',
154
-            sprintf(
155
-                'event-espresso-registrations-%s.csv',
156
-                str_replace([':', ' '], '-', current_time('mysql'))
157
-            )
158
-        );
159
-    }
160
-
161
-
162
-    /**
163
-     * Gets the questions which are to be used for this report,
164
-     * so they can be remembered for later
165
-     *
166
-     * @param array $registration_query_params
167
-     * @return array question admin labels to be used for this report
168
-     * @throws EE_Error
169
-     * @throws ReflectionException
170
-     */
171
-    protected function _get_question_labels(array $registration_query_params): array
172
-    {
173
-        $where                 = $registration_query_params[0] ?? null;
174
-        $question_query_params = [];
175
-        if ($where !== null) {
176
-            $question_query_params = [
177
-                $this->_change_registration_where_params_to_question_where_params($registration_query_params[0]),
178
-            ];
179
-        }
180
-        // Make sure it's not a system question
181
-        $question_query_params[0]['OR*not-system-questions'] = [
182
-            'QST_system'      => '',
183
-            'QST_system*null' => ['IS_NULL']
184
-        ];
185
-        if (
186
-            apply_filters(
187
-                'FHEE__EventEspressoBatchRequest__JobHandlers__RegistrationsReport___get_question_labels__only_include_answered_questions',
188
-                false,
189
-                $registration_query_params
190
-            )
191
-        ) {
192
-            $question_query_params[0]['Answer.ANS_ID'] = ['IS_NOT_NULL'];
193
-        }
194
-        $question_query_params['order_by'] = ['QST_admin_label' => 'ASC'];
195
-        $question_query_params['group_by'] = ['QST_ID'];
196
-        return array_unique(EEM_Question::instance()->get_col($question_query_params, 'QST_admin_label'));
197
-    }
198
-
199
-
200
-    /**
201
-     * Takes where params meant for registrations and changes them to work for questions
202
-     *
203
-     * @param array $reg_where_params
204
-     * @return array
205
-     * @throws EE_Error
206
-     * @throws ReflectionException
207
-     */
208
-    protected function _change_registration_where_params_to_question_where_params(array $reg_where_params): array
209
-    {
210
-        $question_where_params = [];
211
-        foreach ($reg_where_params as $key => $val) {
212
-            if (EEM_Registration::instance()->is_logic_query_param_key($key)) {
213
-                $question_where_params[ $key ] =
214
-                    $this->_change_registration_where_params_to_question_where_params($val);
215
-            } else {
216
-                // it's a normal where condition
217
-                $question_where_params[ 'Question_Group.Event.Registration.' . $key ] = $val;
218
-            }
219
-        }
220
-        return $question_where_params;
221
-    }
222
-
223
-
224
-    /**
225
-     * Performs another step of the job
226
-     *
227
-     * @param JobParameters $job_parameters
228
-     * @param int           $batch_size
229
-     * @return JobStepResponse
230
-     * @throws EE_Error
231
-     * @throws ReflectionException
232
-     */
233
-    public function continue_job(JobParameters $job_parameters, int $batch_size = 50): JobStepResponse
234
-    {
235
-        if ($job_parameters->units_processed() < $job_parameters->job_size()) {
236
-            $csv_data = $this->get_csv_data_for(
237
-                (int) $job_parameters->request_datum('EVT_ID', '0'),
238
-                $job_parameters->units_processed(),
239
-                $batch_size,
240
-                $job_parameters->extra_datum('question_labels'),
241
-                $job_parameters->extra_datum('query_params'),
242
-                (int) $job_parameters->request_datum('DTT_ID', '0')
243
-            );
244
-            EEH_Export::write_data_array_to_csv(
245
-                $job_parameters->extra_datum('filepath'),
246
-                $csv_data,
247
-                false
248
-            );
249
-            $units_processed = count($csv_data);
250
-            if ($units_processed) {
251
-                $job_parameters->mark_processed($units_processed);
252
-                $this->updateText(
253
-                    sprintf(
254
-                        esc_html__('Wrote %1$s rows to report CSV file...', 'event_espresso'),
255
-                        $units_processed
256
-                    )
257
-                );
258
-            }
259
-        }
260
-        $extra_response_data = ['file_url' => ''];
261
-        if ($job_parameters->units_processed() >= $job_parameters->job_size()) {
262
-            $job_parameters->set_status(JobParameters::status_complete);
263
-            $extra_response_data['file_url'] = $this->get_url_to_file($job_parameters->extra_datum('filepath'));
264
-            $this->displayJobFinalResults($job_parameters);
265
-        } else {
266
-            $job_parameters->set_status(JobParameters::status_continue);
267
-        }
268
-        return new JobStepResponse($job_parameters, $this->feedback, $extra_response_data);
269
-    }
270
-
271
-
272
-    /**
273
-     * Gets the csv data for a batch of registrations
274
-     *
275
-     * @param int|null $event_id
276
-     * @param int      $offset
277
-     * @param int      $limit
278
-     * @param array    $question_labels the IDs for all the questions which were answered by someone in this selection
279
-     * @param array    $query_params    for using where querying the model
280
-     * @param int      $DTT_ID
281
-     * @return array top-level keys are numeric, next-level keys are column headers
282
-     * @throws EE_Error
283
-     * @throws ReflectionException
284
-     */
285
-    public function get_csv_data_for(
286
-        ?int $event_id,
287
-        int $offset,
288
-        int $limit,
289
-        array $question_labels,
290
-        array $query_params,
291
-        int $DTT_ID = 0
292
-    ): array {
293
-        $reg_fields_to_include = [
294
-            'TXN_ID',
295
-            'ATT_ID',
296
-            'REG_ID',
297
-            'REG_date',
298
-            'REG_code',
299
-            'REG_count',
300
-            'REG_final_price',
301
-        ];
302
-        $att_fields_to_include = [
303
-            'ATT_fname',
304
-            'ATT_lname',
305
-            'ATT_email',
306
-            'ATT_address',
307
-            'ATT_address2',
308
-            'ATT_city',
309
-            'STA_ID',
310
-            'CNT_ISO',
311
-            'ATT_zip',
312
-            'ATT_phone',
313
-        ];
314
-
315
-        // get models
316
-        $event_model   = EEM_Event::instance();
317
-        $date_model    = EEM_Datetime::instance();
318
-        $ticket_model  = EEM_Ticket::instance();
319
-        $txn_model     = EEM_Transaction::instance();
320
-        $reg_model     = EEM_Registration::instance();
321
-        $pay_model     = EEM_Payment::instance();
322
-        $status_model  = EEM_Status::instance();
323
-
324
-        $registrations_csv_ready_array = [];
325
-        $query_params['limit']         = [$offset, $limit];
326
-        $registration_rows             = $reg_model->get_all_wpdb_results($query_params);
327
-
328
-        foreach ($registration_rows as $reg_row) {
329
-            if (! is_array($reg_row)) {
330
-                continue;
331
-            }
332
-            $reg_csv_array = [];
333
-            // registration ID
334
-            $reg_id_field = $reg_model->field_settings_for('REG_ID');
335
-            $reg_csv_array[ EEH_Export::get_column_name_for_field($reg_id_field) ] =
336
-                EEH_Export::prepare_value_from_db_for_display(
337
-                    $reg_model,
338
-                    'REG_ID',
339
-                    $reg_row[ $reg_id_field->get_qualified_column() ]
340
-                );
341
-            // ALL registrations, or is list filtered to just one?
342
-            if (! $event_id) {
343
-                // ALL registrations, so get each event's name and ID
344
-                $reg_csv_array[ esc_html__('Event', 'event_espresso') ] = sprintf(
345
-                    /* translators: 1: event name, 2: event ID */
346
-                    esc_html__('%1$s (%2$s)', 'event_espresso'),
347
-                    EEH_Export::prepare_value_from_db_for_display(
348
-                        $event_model,
349
-                        'EVT_name',
350
-                        $reg_row['Event_CPT.post_title']
351
-                    ),
352
-                    $reg_row['Event_CPT.ID']
353
-                );
354
-            }
355
-            // add attendee columns
356
-            $reg_csv_array = AttendeeCSV::addAttendeeColumns($att_fields_to_include, $reg_row, $reg_csv_array);
357
-            // add registration columns
358
-            $reg_csv_array = RegistrationCSV::addRegistrationColumns($reg_fields_to_include, $reg_row, $reg_csv_array);
359
-            // get pretty status
360
-            $stati = $status_model->localized_status(
361
-                [
362
-                    $reg_row['Registration.STS_ID']     => esc_html__('unknown', 'event_espresso'),
363
-                    $reg_row['TransactionTable.STS_ID'] => esc_html__('unknown', 'event_espresso'),
364
-                ],
365
-                false,
366
-                'sentence'
367
-            );
368
-            $is_primary_reg = $reg_row['Registration.REG_count'] == '1';
369
-
370
-            $reg_csv_array[ esc_html__('Registration Status', 'event_espresso') ] =
371
-                $stati[ $reg_row['Registration.STS_ID'] ];
372
-            // get pretty transaction status
373
-            $reg_csv_array[ esc_html__('Transaction Status', 'event_espresso') ]     =
374
-                $stati[ $reg_row['TransactionTable.STS_ID'] ];
375
-            $reg_csv_array[ esc_html__('Transaction Amount Due', 'event_espresso') ] = $is_primary_reg
376
-                ? EEH_Export::prepare_value_from_db_for_display(
377
-                    $txn_model,
378
-                    'TXN_total',
379
-                    $reg_row['TransactionTable.TXN_total'],
380
-                    'localized_float'
381
-                )
382
-                : '0.00';
383
-
384
-            $reg_csv_array[ esc_html__('Amount Paid', 'event_espresso') ]            = $is_primary_reg
385
-                ? EEH_Export::prepare_value_from_db_for_display(
386
-                    $txn_model,
387
-                    'TXN_paid',
388
-                    $reg_row['TransactionTable.TXN_paid'],
389
-                    'localized_float'
390
-                )
391
-                : '0.00';
392
-
393
-            $payment_methods                                                                  = [];
394
-            $gateway_txn_ids_etc                                                              = [];
395
-            $payment_times                                                                    = [];
396
-            if ($is_primary_reg && $reg_row['TransactionTable.TXN_ID']) {
397
-                $payments_info = $pay_model->get_all_wpdb_results(
398
-                    [
399
-                        [
400
-                            'TXN_ID' => $reg_row['TransactionTable.TXN_ID'],
401
-                            'STS_ID' => EEM_Payment::status_id_approved,
402
-                        ],
403
-                        'force_join' => ['Payment_Method'],
404
-                    ],
405
-                    ARRAY_A,
406
-                    'Payment_Method.PMD_admin_name as name, Payment.PAY_txn_id_chq_nmbr as gateway_txn_id, Payment.PAY_timestamp as payment_time'
407
-                );
408
-                [$payment_methods, $gateway_txn_ids_etc, $payment_times] = PaymentsInfoCSV::extractPaymentInfo(
409
-                    $payments_info
410
-                );
411
-            }
412
-
413
-            $reg_csv_array[ esc_html__('Payment Date(s)', 'event_espresso') ] = implode(
414
-                ',',
415
-                $payment_times
416
-            );
417
-
418
-            $reg_csv_array[ esc_html__('Payment Method(s)', 'event_espresso') ] = implode(
419
-                ',',
420
-                $payment_methods
421
-            );
422
-
423
-            $reg_csv_array[ esc_html__('Gateway Transaction ID(s)', 'event_espresso') ] = implode(
424
-                ',',
425
-                $gateway_txn_ids_etc
426
-            );
427
-
428
-            $ticket_name      = esc_html__('Unknown', 'event_espresso');
429
-            $datetime_strings = [esc_html__('Unknown', 'event_espresso')];
430
-            if ($reg_row['Ticket.TKT_ID']) {
431
-                $ticket_name       = EEH_Export::prepare_value_from_db_for_display(
432
-                    $ticket_model,
433
-                    'TKT_name',
434
-                    $reg_row['Ticket.TKT_name']
435
-                );
436
-                $datetime_strings = [];
437
-                $datetimes        = $date_model->get_all_wpdb_results(
438
-                    [
439
-                        ['Ticket.TKT_ID' => $reg_row['Ticket.TKT_ID']],
440
-                        'order_by'                 => ['DTT_EVT_start' => 'ASC'],
441
-                        'default_where_conditions' => 'none',
442
-                    ]
443
-                );
444
-                foreach ($datetimes as $datetime) {
445
-                    $datetime_strings[] = EEH_Export::prepare_value_from_db_for_display(
446
-                        $date_model,
447
-                        'DTT_EVT_start',
448
-                        $datetime['Datetime.DTT_EVT_start']
449
-                    );
450
-                }
451
-            }
452
-
453
-            $reg_csv_array[ $ticket_model->field_settings_for('TKT_name')->get_nicename() ] = $ticket_name;
454
-
455
-
456
-            $reg_csv_array[ esc_html__('Ticket Datetimes', 'event_espresso') ] = implode(
457
-                ', ',
458
-                $datetime_strings
459
-            );
460
-
461
-            // add answer columns
462
-            $reg_csv_array = AnswersCSV::addAnswerColumns($reg_row, $reg_csv_array, $question_labels);
463
-            // Include check-in data
464
-            if ($event_id && $DTT_ID) {
465
-                // get whether or not the user has checked in
466
-                $reg_csv_array[ esc_html__('Datetime Check-ins #', 'event_espresso') ] =
467
-                    $reg_model->count_related(
468
-                        $reg_row['Registration.REG_ID'],
469
-                        'Checkin',
470
-                        [
471
-                            [
472
-                                'DTT_ID' => $DTT_ID
473
-                            ]
474
-                        ]
475
-                    );
476
-                /** @var EE_Datetime $datetime */
477
-                $datetime     = $date_model->get_one_by_ID($DTT_ID);
478
-                $checkin_rows = EEM_Checkin::instance()->get_all(
479
-                    [
480
-                        [
481
-                            'REG_ID' => $reg_row['Registration.REG_ID'],
482
-                            'DTT_ID' => $datetime->get('DTT_ID'),
483
-                        ],
484
-                    ]
485
-                );
486
-                $checkins     = [];
487
-                foreach ($checkin_rows as $checkin_row) {
488
-                    /** @var EE_Checkin $checkin_row */
489
-                    $checkin_value = CheckinsCSV::getCheckinValue($checkin_row);
490
-                    if ($checkin_value) {
491
-                        $checkins[] = $checkin_value;
492
-                    }
493
-                }
494
-                $datetime_name                   = CheckinsCSV::getDatetimeLabel($datetime);
495
-                $reg_csv_array[ $datetime_name ] = implode(' --- ', $checkins);
496
-            } elseif ($event_id) {
497
-                // get whether or not the user has checked in
498
-                $reg_csv_array[ esc_html__('Event Check-ins #', 'event_espresso') ] =
499
-                    $reg_model->count_related(
500
-                        $reg_row['Registration.REG_ID'],
501
-                        'Checkin'
502
-                    );
503
-
504
-                $datetimes = $date_model->get_all(
505
-                    [
506
-                        [
507
-                            'Ticket.TKT_ID' => $reg_row['Ticket.TKT_ID'],
508
-                        ],
509
-                        'order_by'                 => ['DTT_EVT_start' => 'ASC'],
510
-                        'default_where_conditions' => 'none',
511
-                    ]
512
-                );
513
-                foreach ($datetimes as $datetime) {
514
-                    if (! $datetime instanceof EE_Datetime) {
515
-                        continue;
516
-                    }
517
-
518
-                    /** @var EE_Checkin $checkin_row */
519
-                    $checkin_row = EEM_Checkin::instance()->get_one(
520
-                        [
521
-                            [
522
-                                'REG_ID' => $reg_row['Registration.REG_ID'],
523
-                                'DTT_ID' => $datetime->get('DTT_ID'),
524
-                            ],
525
-                            'limit'    => 1,
526
-                            'order_by' => [
527
-                                'CHK_ID' => 'DESC'
528
-                            ]
529
-                        ]
530
-                    );
531
-
532
-                    $checkin_value = CheckinsCSV::getCheckinValue($checkin_row);
533
-                    $datetime_name = CheckinsCSV::getDatetimeLabel($datetime);
534
-
535
-                    $reg_csv_array[ $datetime_name ] = $checkin_value;
536
-                }
537
-            }
538
-            /**
539
-             * Filter to change the contents of each row of the registrations report CSV file.
540
-             * This can be used to add or remote columns from the CSV file, or change their values.
541
-             * Note when using: all rows in the CSV should have the same columns.
542
-             *
543
-             * @param array $reg_csv_array keys are the column names, values are their cell values
544
-             * @param array $reg_row       one entry from EEM_Registration::get_all_wpdb_results()
545
-             */
546
-            $registrations_csv_ready_array[] = apply_filters(
547
-                'FHEE__EventEspressoBatchRequest__JobHandlers__RegistrationsReport__reg_csv_array',
548
-                $reg_csv_array,
549
-                $reg_row
550
-            );
551
-        }
552
-        // if we couldn't export anything, we want to at least show the column headers
553
-        if (empty($registrations_csv_ready_array)) {
554
-            $reg_csv_array               = [];
555
-            $model_and_fields_to_include = [
556
-                'Registration' => $reg_fields_to_include,
557
-                'Attendee'     => $att_fields_to_include,
558
-            ];
559
-            foreach ($model_and_fields_to_include as $model_name => $field_list) {
560
-                $model = EE_Registry::instance()->load_model($model_name);
561
-                foreach ($field_list as $field_name) {
562
-                    $field                                                          =
563
-                        $model->field_settings_for($field_name);
564
-                    $reg_csv_array[ EEH_Export::get_column_name_for_field($field) ] = null;
565
-                }
566
-            }
567
-            $registrations_csv_ready_array[] = $reg_csv_array;
568
-        }
569
-        return $registrations_csv_ready_array;
570
-    }
571
-
572
-
573
-    /**
574
-     * Counts total unit to process
575
-     *
576
-     * @param array $query_params
577
-     * @return int
578
-     * @throws EE_Error
579
-     * @throws ReflectionException
580
-     */
581
-    public function count_units_to_process(array $query_params): int
582
-    {
583
-        return EEM_Registration::instance()->count(
584
-            array_diff_key(
585
-                $query_params,
586
-                array_flip(
587
-                    ['limit']
588
-                )
589
-            )
590
-        );
591
-    }
592
-
593
-
594
-    /**
595
-     * Performs any clean-up logic when we know the job is completed.
596
-     * In this case, we delete the temporary file
597
-     *
598
-     * @param JobParameters $job_parameters
599
-     * @return JobStepResponse
600
-     */
601
-    public function cleanup_job(JobParameters $job_parameters): JobStepResponse
602
-    {
603
-        $this->updateText(esc_html__('File Generation complete and downloaded', 'event_espresso'));
604
-
605
-        $this->_file_helper->delete(
606
-            EEH_File::remove_filename_from_filepath($job_parameters->extra_datum('filepath')),
607
-            true,
608
-            'd'
609
-        );
610
-        $this->updateText(esc_html__('Cleaned up temporary file', 'event_espresso'));
611
-        $this->updateText(
612
-            $this->infoWrapper(
613
-                sprintf(
614
-                    esc_html__(
615
-                        'If not automatically redirected in %1$s seconds, click here to return to the %2$sRegistrations List Table%3$s',
616
-                        'event_espresso'
617
-                    ),
618
-                    '<span id="ee-redirect-timer">10</span>',
619
-                    '<a href="' . $job_parameters->request_datum('return_url') . '">',
620
-                    '</a>'
621
-                )
622
-            )
623
-        );
624
-        return new JobStepResponse($job_parameters, $this->feedback);
625
-    }
45
+	// phpcs:disable PSR1.Methods.CamelCapsMethodName.NotCamelCaps
46
+	// phpcs:disable PSR2.Methods.MethodDeclaration.Underscore
47
+	/**
48
+	 * Performs any necessary setup for starting the job. This is also a good
49
+	 * place to setup the $job_arguments which will be used for subsequent HTTP requests
50
+	 * when continue_job will be called
51
+	 *
52
+	 * @param JobParameters $job_parameters
53
+	 * @return JobStepResponse
54
+	 * @throws BatchRequestException
55
+	 * @throws EE_Error
56
+	 * @throws ReflectionException
57
+	 */
58
+	public function create_job(JobParameters $job_parameters): JobStepResponse
59
+	{
60
+		$event_id = absint($job_parameters->request_datum('EVT_ID', '0'));
61
+		$DTT_ID   = absint($job_parameters->request_datum('DTT_ID', '0'));
62
+		if (! EE_Capabilities::instance()->current_user_can('ee_read_registrations', 'generating_report')) {
63
+			throw new BatchRequestException(
64
+				esc_html__('You do not have permission to view registrations', 'event_espresso')
65
+			);
66
+		}
67
+		$filepath = $this->create_file_from_job_with_name(
68
+			$job_parameters->job_id(),
69
+			$this->get_filename()
70
+		);
71
+		$job_parameters->add_extra_data('filepath', $filepath);
72
+
73
+		if ($job_parameters->request_datum('use_filters', false)) {
74
+			$query_params = maybe_unserialize($job_parameters->request_datum('filters', []));
75
+		} else {
76
+			$query_params = [
77
+				[
78
+					'OR'                 => [
79
+						// don't include registrations from failed or abandoned transactions...
80
+						'Transaction.STS_ID' => [
81
+							'NOT IN',
82
+							[
83
+								EEM_Transaction::failed_status_code,
84
+								EEM_Transaction::abandoned_status_code,
85
+							],
86
+						],
87
+						// unless the registration is approved,
88
+						// in which case include it regardless of transaction status
89
+						'STS_ID'             => EEM_Registration::status_id_approved,
90
+					],
91
+					'Ticket.TKT_deleted' => ['IN', [true, false]],
92
+				],
93
+				'order_by'   => ['Transaction.TXN_ID' => 'asc', 'REG_count' => 'asc'],
94
+				'force_join' => ['Transaction', 'Ticket', 'Attendee'],
95
+				'caps'       => EEM_Base::caps_read_admin,
96
+			];
97
+			if ($event_id) {
98
+				$query_params[0]['EVT_ID'] = $event_id;
99
+			} else {
100
+				$query_params['force_join'][] = 'Event';
101
+			}
102
+		}
103
+
104
+		if (! isset($query_params['force_join'])) {
105
+			$query_params['force_join'] = ['Event', 'Transaction', 'Ticket', 'Attendee'];
106
+		}
107
+
108
+		$query_params = apply_filters(
109
+			'FHEE__EE_Export__report_registration_for_event',
110
+			$query_params,
111
+			$event_id
112
+		);
113
+
114
+		$job_parameters->add_extra_data('query_params', $query_params);
115
+		$question_labels = $this->_get_question_labels($query_params);
116
+		$job_parameters->add_extra_data('question_labels', $question_labels);
117
+		$job_parameters->set_job_size($this->count_units_to_process($query_params));
118
+		// we need to set the header columns
119
+		// but to do that we need to process one row so that we can extract ALL of the column headers
120
+		$csv_data_for_row = $this->get_csv_data_for(
121
+			$event_id,
122
+			0,
123
+			1,
124
+			$question_labels,
125
+			$query_params,
126
+			$DTT_ID
127
+		);
128
+		// but we don't want to write any actual data yet...
129
+		// so let's blank out all of the values for that first row
130
+		array_walk(
131
+			$csv_data_for_row[0],
132
+			function (&$value) {
133
+				$value = null;
134
+			}
135
+		);
136
+
137
+		EEH_Export::write_data_array_to_csv($filepath, $csv_data_for_row, true, true);
138
+		$this->updateTextHeader(
139
+			esc_html__('Registrations report started successfully...', 'event_espresso')
140
+		);
141
+		return new JobStepResponse($job_parameters, $this->feedback);
142
+	}
143
+
144
+
145
+	/**
146
+	 * Gets the filename
147
+	 *
148
+	 * @return string
149
+	 */
150
+	protected function get_filename(): string
151
+	{
152
+		return apply_filters(
153
+			'FHEE__EventEspressoBatchRequest__JobHandlers__RegistrationsReport__get_filename',
154
+			sprintf(
155
+				'event-espresso-registrations-%s.csv',
156
+				str_replace([':', ' '], '-', current_time('mysql'))
157
+			)
158
+		);
159
+	}
160
+
161
+
162
+	/**
163
+	 * Gets the questions which are to be used for this report,
164
+	 * so they can be remembered for later
165
+	 *
166
+	 * @param array $registration_query_params
167
+	 * @return array question admin labels to be used for this report
168
+	 * @throws EE_Error
169
+	 * @throws ReflectionException
170
+	 */
171
+	protected function _get_question_labels(array $registration_query_params): array
172
+	{
173
+		$where                 = $registration_query_params[0] ?? null;
174
+		$question_query_params = [];
175
+		if ($where !== null) {
176
+			$question_query_params = [
177
+				$this->_change_registration_where_params_to_question_where_params($registration_query_params[0]),
178
+			];
179
+		}
180
+		// Make sure it's not a system question
181
+		$question_query_params[0]['OR*not-system-questions'] = [
182
+			'QST_system'      => '',
183
+			'QST_system*null' => ['IS_NULL']
184
+		];
185
+		if (
186
+			apply_filters(
187
+				'FHEE__EventEspressoBatchRequest__JobHandlers__RegistrationsReport___get_question_labels__only_include_answered_questions',
188
+				false,
189
+				$registration_query_params
190
+			)
191
+		) {
192
+			$question_query_params[0]['Answer.ANS_ID'] = ['IS_NOT_NULL'];
193
+		}
194
+		$question_query_params['order_by'] = ['QST_admin_label' => 'ASC'];
195
+		$question_query_params['group_by'] = ['QST_ID'];
196
+		return array_unique(EEM_Question::instance()->get_col($question_query_params, 'QST_admin_label'));
197
+	}
198
+
199
+
200
+	/**
201
+	 * Takes where params meant for registrations and changes them to work for questions
202
+	 *
203
+	 * @param array $reg_where_params
204
+	 * @return array
205
+	 * @throws EE_Error
206
+	 * @throws ReflectionException
207
+	 */
208
+	protected function _change_registration_where_params_to_question_where_params(array $reg_where_params): array
209
+	{
210
+		$question_where_params = [];
211
+		foreach ($reg_where_params as $key => $val) {
212
+			if (EEM_Registration::instance()->is_logic_query_param_key($key)) {
213
+				$question_where_params[ $key ] =
214
+					$this->_change_registration_where_params_to_question_where_params($val);
215
+			} else {
216
+				// it's a normal where condition
217
+				$question_where_params[ 'Question_Group.Event.Registration.' . $key ] = $val;
218
+			}
219
+		}
220
+		return $question_where_params;
221
+	}
222
+
223
+
224
+	/**
225
+	 * Performs another step of the job
226
+	 *
227
+	 * @param JobParameters $job_parameters
228
+	 * @param int           $batch_size
229
+	 * @return JobStepResponse
230
+	 * @throws EE_Error
231
+	 * @throws ReflectionException
232
+	 */
233
+	public function continue_job(JobParameters $job_parameters, int $batch_size = 50): JobStepResponse
234
+	{
235
+		if ($job_parameters->units_processed() < $job_parameters->job_size()) {
236
+			$csv_data = $this->get_csv_data_for(
237
+				(int) $job_parameters->request_datum('EVT_ID', '0'),
238
+				$job_parameters->units_processed(),
239
+				$batch_size,
240
+				$job_parameters->extra_datum('question_labels'),
241
+				$job_parameters->extra_datum('query_params'),
242
+				(int) $job_parameters->request_datum('DTT_ID', '0')
243
+			);
244
+			EEH_Export::write_data_array_to_csv(
245
+				$job_parameters->extra_datum('filepath'),
246
+				$csv_data,
247
+				false
248
+			);
249
+			$units_processed = count($csv_data);
250
+			if ($units_processed) {
251
+				$job_parameters->mark_processed($units_processed);
252
+				$this->updateText(
253
+					sprintf(
254
+						esc_html__('Wrote %1$s rows to report CSV file...', 'event_espresso'),
255
+						$units_processed
256
+					)
257
+				);
258
+			}
259
+		}
260
+		$extra_response_data = ['file_url' => ''];
261
+		if ($job_parameters->units_processed() >= $job_parameters->job_size()) {
262
+			$job_parameters->set_status(JobParameters::status_complete);
263
+			$extra_response_data['file_url'] = $this->get_url_to_file($job_parameters->extra_datum('filepath'));
264
+			$this->displayJobFinalResults($job_parameters);
265
+		} else {
266
+			$job_parameters->set_status(JobParameters::status_continue);
267
+		}
268
+		return new JobStepResponse($job_parameters, $this->feedback, $extra_response_data);
269
+	}
270
+
271
+
272
+	/**
273
+	 * Gets the csv data for a batch of registrations
274
+	 *
275
+	 * @param int|null $event_id
276
+	 * @param int      $offset
277
+	 * @param int      $limit
278
+	 * @param array    $question_labels the IDs for all the questions which were answered by someone in this selection
279
+	 * @param array    $query_params    for using where querying the model
280
+	 * @param int      $DTT_ID
281
+	 * @return array top-level keys are numeric, next-level keys are column headers
282
+	 * @throws EE_Error
283
+	 * @throws ReflectionException
284
+	 */
285
+	public function get_csv_data_for(
286
+		?int $event_id,
287
+		int $offset,
288
+		int $limit,
289
+		array $question_labels,
290
+		array $query_params,
291
+		int $DTT_ID = 0
292
+	): array {
293
+		$reg_fields_to_include = [
294
+			'TXN_ID',
295
+			'ATT_ID',
296
+			'REG_ID',
297
+			'REG_date',
298
+			'REG_code',
299
+			'REG_count',
300
+			'REG_final_price',
301
+		];
302
+		$att_fields_to_include = [
303
+			'ATT_fname',
304
+			'ATT_lname',
305
+			'ATT_email',
306
+			'ATT_address',
307
+			'ATT_address2',
308
+			'ATT_city',
309
+			'STA_ID',
310
+			'CNT_ISO',
311
+			'ATT_zip',
312
+			'ATT_phone',
313
+		];
314
+
315
+		// get models
316
+		$event_model   = EEM_Event::instance();
317
+		$date_model    = EEM_Datetime::instance();
318
+		$ticket_model  = EEM_Ticket::instance();
319
+		$txn_model     = EEM_Transaction::instance();
320
+		$reg_model     = EEM_Registration::instance();
321
+		$pay_model     = EEM_Payment::instance();
322
+		$status_model  = EEM_Status::instance();
323
+
324
+		$registrations_csv_ready_array = [];
325
+		$query_params['limit']         = [$offset, $limit];
326
+		$registration_rows             = $reg_model->get_all_wpdb_results($query_params);
327
+
328
+		foreach ($registration_rows as $reg_row) {
329
+			if (! is_array($reg_row)) {
330
+				continue;
331
+			}
332
+			$reg_csv_array = [];
333
+			// registration ID
334
+			$reg_id_field = $reg_model->field_settings_for('REG_ID');
335
+			$reg_csv_array[ EEH_Export::get_column_name_for_field($reg_id_field) ] =
336
+				EEH_Export::prepare_value_from_db_for_display(
337
+					$reg_model,
338
+					'REG_ID',
339
+					$reg_row[ $reg_id_field->get_qualified_column() ]
340
+				);
341
+			// ALL registrations, or is list filtered to just one?
342
+			if (! $event_id) {
343
+				// ALL registrations, so get each event's name and ID
344
+				$reg_csv_array[ esc_html__('Event', 'event_espresso') ] = sprintf(
345
+					/* translators: 1: event name, 2: event ID */
346
+					esc_html__('%1$s (%2$s)', 'event_espresso'),
347
+					EEH_Export::prepare_value_from_db_for_display(
348
+						$event_model,
349
+						'EVT_name',
350
+						$reg_row['Event_CPT.post_title']
351
+					),
352
+					$reg_row['Event_CPT.ID']
353
+				);
354
+			}
355
+			// add attendee columns
356
+			$reg_csv_array = AttendeeCSV::addAttendeeColumns($att_fields_to_include, $reg_row, $reg_csv_array);
357
+			// add registration columns
358
+			$reg_csv_array = RegistrationCSV::addRegistrationColumns($reg_fields_to_include, $reg_row, $reg_csv_array);
359
+			// get pretty status
360
+			$stati = $status_model->localized_status(
361
+				[
362
+					$reg_row['Registration.STS_ID']     => esc_html__('unknown', 'event_espresso'),
363
+					$reg_row['TransactionTable.STS_ID'] => esc_html__('unknown', 'event_espresso'),
364
+				],
365
+				false,
366
+				'sentence'
367
+			);
368
+			$is_primary_reg = $reg_row['Registration.REG_count'] == '1';
369
+
370
+			$reg_csv_array[ esc_html__('Registration Status', 'event_espresso') ] =
371
+				$stati[ $reg_row['Registration.STS_ID'] ];
372
+			// get pretty transaction status
373
+			$reg_csv_array[ esc_html__('Transaction Status', 'event_espresso') ]     =
374
+				$stati[ $reg_row['TransactionTable.STS_ID'] ];
375
+			$reg_csv_array[ esc_html__('Transaction Amount Due', 'event_espresso') ] = $is_primary_reg
376
+				? EEH_Export::prepare_value_from_db_for_display(
377
+					$txn_model,
378
+					'TXN_total',
379
+					$reg_row['TransactionTable.TXN_total'],
380
+					'localized_float'
381
+				)
382
+				: '0.00';
383
+
384
+			$reg_csv_array[ esc_html__('Amount Paid', 'event_espresso') ]            = $is_primary_reg
385
+				? EEH_Export::prepare_value_from_db_for_display(
386
+					$txn_model,
387
+					'TXN_paid',
388
+					$reg_row['TransactionTable.TXN_paid'],
389
+					'localized_float'
390
+				)
391
+				: '0.00';
392
+
393
+			$payment_methods                                                                  = [];
394
+			$gateway_txn_ids_etc                                                              = [];
395
+			$payment_times                                                                    = [];
396
+			if ($is_primary_reg && $reg_row['TransactionTable.TXN_ID']) {
397
+				$payments_info = $pay_model->get_all_wpdb_results(
398
+					[
399
+						[
400
+							'TXN_ID' => $reg_row['TransactionTable.TXN_ID'],
401
+							'STS_ID' => EEM_Payment::status_id_approved,
402
+						],
403
+						'force_join' => ['Payment_Method'],
404
+					],
405
+					ARRAY_A,
406
+					'Payment_Method.PMD_admin_name as name, Payment.PAY_txn_id_chq_nmbr as gateway_txn_id, Payment.PAY_timestamp as payment_time'
407
+				);
408
+				[$payment_methods, $gateway_txn_ids_etc, $payment_times] = PaymentsInfoCSV::extractPaymentInfo(
409
+					$payments_info
410
+				);
411
+			}
412
+
413
+			$reg_csv_array[ esc_html__('Payment Date(s)', 'event_espresso') ] = implode(
414
+				',',
415
+				$payment_times
416
+			);
417
+
418
+			$reg_csv_array[ esc_html__('Payment Method(s)', 'event_espresso') ] = implode(
419
+				',',
420
+				$payment_methods
421
+			);
422
+
423
+			$reg_csv_array[ esc_html__('Gateway Transaction ID(s)', 'event_espresso') ] = implode(
424
+				',',
425
+				$gateway_txn_ids_etc
426
+			);
427
+
428
+			$ticket_name      = esc_html__('Unknown', 'event_espresso');
429
+			$datetime_strings = [esc_html__('Unknown', 'event_espresso')];
430
+			if ($reg_row['Ticket.TKT_ID']) {
431
+				$ticket_name       = EEH_Export::prepare_value_from_db_for_display(
432
+					$ticket_model,
433
+					'TKT_name',
434
+					$reg_row['Ticket.TKT_name']
435
+				);
436
+				$datetime_strings = [];
437
+				$datetimes        = $date_model->get_all_wpdb_results(
438
+					[
439
+						['Ticket.TKT_ID' => $reg_row['Ticket.TKT_ID']],
440
+						'order_by'                 => ['DTT_EVT_start' => 'ASC'],
441
+						'default_where_conditions' => 'none',
442
+					]
443
+				);
444
+				foreach ($datetimes as $datetime) {
445
+					$datetime_strings[] = EEH_Export::prepare_value_from_db_for_display(
446
+						$date_model,
447
+						'DTT_EVT_start',
448
+						$datetime['Datetime.DTT_EVT_start']
449
+					);
450
+				}
451
+			}
452
+
453
+			$reg_csv_array[ $ticket_model->field_settings_for('TKT_name')->get_nicename() ] = $ticket_name;
454
+
455
+
456
+			$reg_csv_array[ esc_html__('Ticket Datetimes', 'event_espresso') ] = implode(
457
+				', ',
458
+				$datetime_strings
459
+			);
460
+
461
+			// add answer columns
462
+			$reg_csv_array = AnswersCSV::addAnswerColumns($reg_row, $reg_csv_array, $question_labels);
463
+			// Include check-in data
464
+			if ($event_id && $DTT_ID) {
465
+				// get whether or not the user has checked in
466
+				$reg_csv_array[ esc_html__('Datetime Check-ins #', 'event_espresso') ] =
467
+					$reg_model->count_related(
468
+						$reg_row['Registration.REG_ID'],
469
+						'Checkin',
470
+						[
471
+							[
472
+								'DTT_ID' => $DTT_ID
473
+							]
474
+						]
475
+					);
476
+				/** @var EE_Datetime $datetime */
477
+				$datetime     = $date_model->get_one_by_ID($DTT_ID);
478
+				$checkin_rows = EEM_Checkin::instance()->get_all(
479
+					[
480
+						[
481
+							'REG_ID' => $reg_row['Registration.REG_ID'],
482
+							'DTT_ID' => $datetime->get('DTT_ID'),
483
+						],
484
+					]
485
+				);
486
+				$checkins     = [];
487
+				foreach ($checkin_rows as $checkin_row) {
488
+					/** @var EE_Checkin $checkin_row */
489
+					$checkin_value = CheckinsCSV::getCheckinValue($checkin_row);
490
+					if ($checkin_value) {
491
+						$checkins[] = $checkin_value;
492
+					}
493
+				}
494
+				$datetime_name                   = CheckinsCSV::getDatetimeLabel($datetime);
495
+				$reg_csv_array[ $datetime_name ] = implode(' --- ', $checkins);
496
+			} elseif ($event_id) {
497
+				// get whether or not the user has checked in
498
+				$reg_csv_array[ esc_html__('Event Check-ins #', 'event_espresso') ] =
499
+					$reg_model->count_related(
500
+						$reg_row['Registration.REG_ID'],
501
+						'Checkin'
502
+					);
503
+
504
+				$datetimes = $date_model->get_all(
505
+					[
506
+						[
507
+							'Ticket.TKT_ID' => $reg_row['Ticket.TKT_ID'],
508
+						],
509
+						'order_by'                 => ['DTT_EVT_start' => 'ASC'],
510
+						'default_where_conditions' => 'none',
511
+					]
512
+				);
513
+				foreach ($datetimes as $datetime) {
514
+					if (! $datetime instanceof EE_Datetime) {
515
+						continue;
516
+					}
517
+
518
+					/** @var EE_Checkin $checkin_row */
519
+					$checkin_row = EEM_Checkin::instance()->get_one(
520
+						[
521
+							[
522
+								'REG_ID' => $reg_row['Registration.REG_ID'],
523
+								'DTT_ID' => $datetime->get('DTT_ID'),
524
+							],
525
+							'limit'    => 1,
526
+							'order_by' => [
527
+								'CHK_ID' => 'DESC'
528
+							]
529
+						]
530
+					);
531
+
532
+					$checkin_value = CheckinsCSV::getCheckinValue($checkin_row);
533
+					$datetime_name = CheckinsCSV::getDatetimeLabel($datetime);
534
+
535
+					$reg_csv_array[ $datetime_name ] = $checkin_value;
536
+				}
537
+			}
538
+			/**
539
+			 * Filter to change the contents of each row of the registrations report CSV file.
540
+			 * This can be used to add or remote columns from the CSV file, or change their values.
541
+			 * Note when using: all rows in the CSV should have the same columns.
542
+			 *
543
+			 * @param array $reg_csv_array keys are the column names, values are their cell values
544
+			 * @param array $reg_row       one entry from EEM_Registration::get_all_wpdb_results()
545
+			 */
546
+			$registrations_csv_ready_array[] = apply_filters(
547
+				'FHEE__EventEspressoBatchRequest__JobHandlers__RegistrationsReport__reg_csv_array',
548
+				$reg_csv_array,
549
+				$reg_row
550
+			);
551
+		}
552
+		// if we couldn't export anything, we want to at least show the column headers
553
+		if (empty($registrations_csv_ready_array)) {
554
+			$reg_csv_array               = [];
555
+			$model_and_fields_to_include = [
556
+				'Registration' => $reg_fields_to_include,
557
+				'Attendee'     => $att_fields_to_include,
558
+			];
559
+			foreach ($model_and_fields_to_include as $model_name => $field_list) {
560
+				$model = EE_Registry::instance()->load_model($model_name);
561
+				foreach ($field_list as $field_name) {
562
+					$field                                                          =
563
+						$model->field_settings_for($field_name);
564
+					$reg_csv_array[ EEH_Export::get_column_name_for_field($field) ] = null;
565
+				}
566
+			}
567
+			$registrations_csv_ready_array[] = $reg_csv_array;
568
+		}
569
+		return $registrations_csv_ready_array;
570
+	}
571
+
572
+
573
+	/**
574
+	 * Counts total unit to process
575
+	 *
576
+	 * @param array $query_params
577
+	 * @return int
578
+	 * @throws EE_Error
579
+	 * @throws ReflectionException
580
+	 */
581
+	public function count_units_to_process(array $query_params): int
582
+	{
583
+		return EEM_Registration::instance()->count(
584
+			array_diff_key(
585
+				$query_params,
586
+				array_flip(
587
+					['limit']
588
+				)
589
+			)
590
+		);
591
+	}
592
+
593
+
594
+	/**
595
+	 * Performs any clean-up logic when we know the job is completed.
596
+	 * In this case, we delete the temporary file
597
+	 *
598
+	 * @param JobParameters $job_parameters
599
+	 * @return JobStepResponse
600
+	 */
601
+	public function cleanup_job(JobParameters $job_parameters): JobStepResponse
602
+	{
603
+		$this->updateText(esc_html__('File Generation complete and downloaded', 'event_espresso'));
604
+
605
+		$this->_file_helper->delete(
606
+			EEH_File::remove_filename_from_filepath($job_parameters->extra_datum('filepath')),
607
+			true,
608
+			'd'
609
+		);
610
+		$this->updateText(esc_html__('Cleaned up temporary file', 'event_espresso'));
611
+		$this->updateText(
612
+			$this->infoWrapper(
613
+				sprintf(
614
+					esc_html__(
615
+						'If not automatically redirected in %1$s seconds, click here to return to the %2$sRegistrations List Table%3$s',
616
+						'event_espresso'
617
+					),
618
+					'<span id="ee-redirect-timer">10</span>',
619
+					'<a href="' . $job_parameters->request_datum('return_url') . '">',
620
+					'</a>'
621
+				)
622
+			)
623
+		);
624
+		return new JobStepResponse($job_parameters, $this->feedback);
625
+	}
626 626
 }
Please login to merge, or discard this patch.
core/domain/values/TimeFormat.php 2 patches
Indentation   +26 added lines, -26 removed lines patch added patch discarded remove patch
@@ -4,32 +4,32 @@
 block discarded – undo
4 4
 
5 5
 class TimeFormat extends BaseFormat
6 6
 {
7
-    protected const WORDPRESS_FORMAT_OPTION_NAME = 'time_format';
7
+	protected const WORDPRESS_FORMAT_OPTION_NAME = 'time_format';
8 8
 
9
-    protected static array   $allowed_chars  = [
10
-        'a',
11
-        'A',
12
-        'B',
13
-        'g',
14
-        'G',
15
-        'h',
16
-        'H',
17
-        'i',
18
-        's',
19
-        'u',
20
-        'v',
21
-        'e',
22
-        'I',
23
-        'O',
24
-        'P',
25
-        'T',
26
-        'Z',
27
-        ':',
28
-        '-',
29
-        ',',
30
-        '.',
31
-        ' ',
32
-    ];
9
+	protected static array   $allowed_chars  = [
10
+		'a',
11
+		'A',
12
+		'B',
13
+		'g',
14
+		'G',
15
+		'h',
16
+		'H',
17
+		'i',
18
+		's',
19
+		'u',
20
+		'v',
21
+		'e',
22
+		'I',
23
+		'O',
24
+		'P',
25
+		'T',
26
+		'Z',
27
+		':',
28
+		'-',
29
+		',',
30
+		'.',
31
+		' ',
32
+	];
33 33
 
34
-    protected static ?string $wordpress_format = null;
34
+	protected static ?string $wordpress_format = null;
35 35
 }
Please login to merge, or discard this patch.
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -6,7 +6,7 @@
 block discarded – undo
6 6
 {
7 7
     protected const WORDPRESS_FORMAT_OPTION_NAME = 'time_format';
8 8
 
9
-    protected static array   $allowed_chars  = [
9
+    protected static array   $allowed_chars = [
10 10
         'a',
11 11
         'A',
12 12
         'B',
Please login to merge, or discard this patch.
core/domain/values/DateFormat.php 1 patch
Indentation   +45 added lines, -45 removed lines patch added patch discarded remove patch
@@ -4,51 +4,51 @@
 block discarded – undo
4 4
 
5 5
 class DateFormat extends BaseFormat
6 6
 {
7
-    protected const WORDPRESS_FORMAT_OPTION_NAME = 'date_format';
7
+	protected const WORDPRESS_FORMAT_OPTION_NAME = 'date_format';
8 8
 
9
-    protected static array $allowed_chars = [
10
-        'd',
11
-        'D',
12
-        'j',
13
-        'l',
14
-        'N',
15
-        'S',
16
-        'w',
17
-        'z',
18
-        'W',
19
-        'F',
20
-        'm',
21
-        'M',
22
-        'n',
23
-        't',
24
-        'L',
25
-        'o',
26
-        'Y',
27
-        'y',
28
-        'a',
29
-        'A',
30
-        'B',
31
-        'g',
32
-        'G',
33
-        'h',
34
-        'H',
35
-        'i',
36
-        's',
37
-        'u',
38
-        'v',
39
-        'e',
40
-        'I',
41
-        'O',
42
-        'P',
43
-        'T',
44
-        'Z',
45
-        '/',
46
-        '-',
47
-        ',',
48
-        '.',
49
-        ' ',
50
-        ':',
51
-    ];
9
+	protected static array $allowed_chars = [
10
+		'd',
11
+		'D',
12
+		'j',
13
+		'l',
14
+		'N',
15
+		'S',
16
+		'w',
17
+		'z',
18
+		'W',
19
+		'F',
20
+		'm',
21
+		'M',
22
+		'n',
23
+		't',
24
+		'L',
25
+		'o',
26
+		'Y',
27
+		'y',
28
+		'a',
29
+		'A',
30
+		'B',
31
+		'g',
32
+		'G',
33
+		'h',
34
+		'H',
35
+		'i',
36
+		's',
37
+		'u',
38
+		'v',
39
+		'e',
40
+		'I',
41
+		'O',
42
+		'P',
43
+		'T',
44
+		'Z',
45
+		'/',
46
+		'-',
47
+		',',
48
+		'.',
49
+		' ',
50
+		':',
51
+	];
52 52
 
53
-    protected static ?string $wordpress_format = null;
53
+	protected static ?string $wordpress_format = null;
54 54
 }
Please login to merge, or discard this patch.